From a32963e11f9f8e5d22b0d754d34a867f3b178ed2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 2 Jan 2023 21:38:19 +0800 Subject: Update Android port * Makefile.in (java): Depend on info. (MAKEFILE_NAME): (config.status): Remove unneeded changes. * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a C++ compiler on Android. * java/AndroidManifest.xml: : Set launchMode appropriately. : New activity. * java/Makefile.in (CROSS_BINS): Add EmacsClient. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity) (onCreate): Use the window attachment manager. * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea) (paintTo): Implement clip masks correctly. * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo): Fix damage tracking rectangles. * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString): New function. (FontMetrics, EmacsFontDriver): Fix signature of textExtents. * java/org/gnu/emacs/EmacsMultitaskActivity.java (EmacsMultitaskActivity): New file. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New functions sendFocusIn, sendFocusOut, sendWindowAction. * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping handling. * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add constructor for mutable pixmaps. * java/org/gnu/emacs/EmacsSdk23FontDriver.java (EmacsSdk23FontDriver): New file. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject) (checkMatch, hasChar, encodeChar): Implement text display and fix font metrics semantics. * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove availableChildren. (getLibraryDirectory, onCreate): Pass pixel density to Emacs. (clearArea): Fix arguments. Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged) (surfaceCreated): Flip buffers on surface attachment. * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): New argument FORCE. Always swap if it is true. (onKeyMultiple, onFocusChanged): New functions. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle) (run): Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): New file. * lisp/cus-edit.el (custom-button, custom-button-mouse) (custom-button-pressed): * lisp/faces.el (tool-bar): Define faces correctly on Android. * src/android.c (struct android_emacs_pixmap): Add mutable constructor. (struct android_emacs_drawable): New structure. (android_write_event): Check if event queue hasn't yet been initialized. (android_select): Set errno to EINTR if pselect fails. (android_close): Remove unused debugging code. (android_get_home_directory): New function. (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel density and compute game path. (android_init_emacs_drawable): New function. (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument `unicode_char'. Pass it in events. (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise. (Java_org_gnu_emacs_EmacsNative_sendFocusIn) (Java_org_gnu_emacs_EmacsNative_sendFocusOut) (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New functions. (android_resolve_handle): Export function. (android_change_gc): Clear clip rects under the right circumstances. Set right clip mask field. (android_create_pixmap_from_bitmap_data): Use correct alpha channels. (android_create_pixmap): Create mutable pixmap and avoid redundant color array allocation. (android_create_bitmap_from_data, android_create_image) (android_destroy_image, android_put_pixel, android_get_pixel) (android_get_image, android_put_image, faccessat): New functions. * src/android.h: Update prototypes. * src/androidfns.c (android_default_font_parameter): Prefer monospace to Droid Sans Mono. * src/androidfont.c (struct android_emacs_font_driver): New method `draw'. (struct android_emacs_font_spec): New field `dpi'. (struct androidfont_info): Add font metrics cache. (android_init_font_driver, android_init_font_spec): Adjust accordingly. (androidfont_from_lisp, androidfont_from_java): Handle new fields. (androidfont_draw): Implement function. (androidfont_open_font): Set pixel size correctly. (androidfont_close_font): Free metrics cache. (androidfont_cache_text_extents) (androidfont_check_cached_extents): New functions. (androidfont_text_extents): Cache glyph metrics somewhere for future use. (androidfont_list_family): Implement function. * src/androidgui.h (enum android_event_type): New focus and window action events. (enum android_modifier_mask): New masks. (struct android_key_event): New field `unicode_char'. (ANDROID_IS_MODIFIER_KEY): Newmacro. (struct android_focus_event, struct android_window_action_event): New structs. (union android_event): Add new fields. (enum android_image_format, struct android_image): New enums and structs. * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers, android_lower_frame) (android_raise_frame, android_new_focus_frame) (android_focus_changed, android_detect_focus_change): New functions. (handle_one_android_event): Implement focus and key event handling. (android_frame_rehighlight): New function. (android_frame_raise_lower): Implement accordingly. (android_make_frame_invisible): Clear highlight_frame if required. (android_free_frame_resources): Clear x_focus_event_frame if required. (android_draw_fringe_bitmap, android_draw_image_foreground) (android_draw_image_foreground_1) (android_draw_image_glyph_string): Remove unnecessary code. (android_create_terminal, android_term_init): Set the baud rate to something sensible. * src/androidterm.h (struct android_bitmap_record): Make structure the same as on X. (struct android_display_info): New focus tracking fields. (struct android_output): Likewise. * src/dispextern.h (struct image): Add ximg and mask_img on Android. * src/emacs.c (android_emacs_init): Fix argc sorting iteration. * src/fileio.c (user_homedir): (get_homedir): Implement correctly on Android. * src/font.h (PT_PER_INCH): Define correctly on Android. * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe bitmaps correctly on Android. * src/image.c (GET_PIXEL, image_create_bitmap_from_data) (image_create_bitmap_from_file, free_bitmap_record) (image_unget_x_image_or_dc, struct image_type) (prepare_image_for_display, image_clear_image_1) (image_size_in_bytes, x_check_image_size) (x_create_x_image_and_pixmap, x_destroy_x_image) (image_check_image_size, image_create_x_image_and_pixmap_1) (image_destroy_x_image, gui_put_x_image, image_put_x_image) (image_get_x_image, image_unget_x_image) (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross) (MaskForeground, image_types, syms_of_image): Implement all of the above on Android in terms of an API very similar to X. * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys): Define on Android to something sensible. * src/lread.c (build_load_history): Fix problem. --- .../gnu/emacs/EmacsWindowAttachmentManager.java | 166 +++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 java/org/gnu/emacs/EmacsWindowAttachmentManager.java (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java new file mode 100644 index 00000000000..34be2ab8789 --- /dev/null +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -0,0 +1,166 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +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 . */ + +package org.gnu.emacs; + +import java.util.LinkedList; +import java.util.List; + +import android.content.Intent; +import android.util.Log; + +/* Code to paper over the differences in lifecycles between + "activities" and windows. There are four interfaces to an instance + of this class: + + registerWindowConsumer (WindowConsumer) + registerWindow (EmacsWindow) + removeWindowConsumer (WindowConsumer) + removeWindow (EmacsWindow) + + A WindowConsumer is expected to allow an EmacsWindow to be attached + to it, and be created or destroyed. + + Every time a window is created, registerWindow checks the list of + window consumers. If a consumer exists and does not currently have + a window of its own attached, it gets the new window. Otherwise, + the window attachment manager starts a new consumer. + + Every time a consumer is registered, registerWindowConsumer checks + the list of available windows. If a window exists and is not + currently attached to a consumer, then the consumer gets it. + + Finally, every time a window is removed, the consumer is + destroyed. */ + +public class EmacsWindowAttachmentManager +{ + public static EmacsWindowAttachmentManager MANAGER; + private final static String TAG = "EmacsWindowAttachmentManager"; + + static + { + MANAGER = new EmacsWindowAttachmentManager (); + }; + + public interface WindowConsumer + { + public void attachWindow (EmacsWindow window); + public EmacsWindow getAttachedWindow (); + public void detachWindow (); + public void destroy (); + }; + + private List consumers; + private List windows; + + public + EmacsWindowAttachmentManager () + { + consumers = new LinkedList (); + windows = new LinkedList (); + } + + public void + registerWindowConsumer (WindowConsumer consumer) + { + Log.d (TAG, "registerWindowConsumer " + consumer); + + consumers.add (consumer); + + for (EmacsWindow window : windows) + { + if (window.getAttachedConsumer () == null) + { + Log.d (TAG, "registerWindowConsumer: attaching " + window); + consumer.attachWindow (window); + return; + } + } + + Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0"); + EmacsNative.sendWindowAction ((short) 0, 0); + } + + public void + registerWindow (EmacsWindow window) + { + Intent intent; + + Log.d (TAG, "registerWindow " + window); + windows.add (window); + + for (WindowConsumer consumer : consumers) + { + if (consumer.getAttachedWindow () == null) + { + Log.d (TAG, "registerWindow: attaching " + consumer); + consumer.attachWindow (window); + return; + } + } + + intent = new Intent (EmacsService.SERVICE, + EmacsMultitaskActivity.class); + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT + | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + EmacsService.SERVICE.startActivity (intent); + Log.d (TAG, "registerWindow: startActivity"); + } + + public void + removeWindowConsumer (WindowConsumer consumer) + { + EmacsWindow window; + + Log.d (TAG, "removeWindowConsumer " + consumer); + + window = consumer.getAttachedWindow (); + + if (window != null) + { + Log.d (TAG, "removeWindowConsumer: detaching " + window); + + consumer.detachWindow (); + window.onActivityDetached (); + } + + Log.d (TAG, "removeWindowConsumer: removing " + consumer); + consumers.remove (consumer); + } + + public void + detachWindow (EmacsWindow window) + { + WindowConsumer consumer; + + Log.d (TAG, "detachWindow " + window); + + if (window.getAttachedConsumer () != null) + { + consumer = window.getAttachedConsumer (); + + Log.d (TAG, "detachWindow: removing" + consumer); + + consumers.remove (consumer); + consumer.destroy (); + } + } +}; -- cgit v1.3 From f9732131cf3c67e24db74a3d49f256d3c189a7e3 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 13 Jan 2023 15:53:08 +0800 Subject: Update Android port * configure.ac (ANDROID_MIN_SDK): New variable. (DX): Remove and replace with D8. (XCONFIGURE): Check for the minimum version of Android the cross compiler compiles for. Generate java/AndroidManifest.xml from java/AndroidManifest.xml.in. Allow using Zlib on Android. * java/AndroidManifest.xml.in: New file. Use the minimum SDK detected by configure. * java/Makefile.in (top_srcdir, version): New variables. (DX, D8): Replace with D8. (ANDROID_MIN_SDK, APK_NAME): New variables. (.PHONY): (.PRECIOUS): (classes.dex): (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'. * java/debug.sh: New option --attach-existing. Attach to an existing Emacs instance when specified. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New field `isPaused'. (invalidateFocus1): Fix infinite recursion. (detachWindow): Deiconify window. (attachWindow): Iconify the window if the activity is paused. (onCreate): Use the ``no title bar'' theme. (onPause, onResume): New functions. * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown) (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New functions. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface): (list): Remove logging for code that is mostly going to be unused. * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree) (getScreenWidth, getScreenHeight, detectMouse): New functions. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra debug logging. Avoid deadlock in surfaceCreated. * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard to make the SurfaceView respect Z order. It didn't work. (handleDirtyBitmap): Copy over the contents from the old bitmap. (explicitlyDirtyBitmap): New function. (onLayout): Don't dirty bitmap if unnecessary. (damageRect, swapBuffers): Don't synchronize so hard. (onTouchEvent): Call window.onTouchEvent instead. (moveChildToBack, raise, lower): New functions. * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New subclass. (pointerMap, isMapped, isIconified, dontFocusOnMap) (dontAcceptFocus): New fields. (EmacsWindow): Don't immediately register unmapped window. (viewLayout): Send configure event outside the lock. (requestViewLayout): Explicitly dirty the bitmap. (mapWindow): Register the window now. Respect dontFocusOnMap. (unmapWindow): Unregister the window now. (figureChange, onTouchEvent): New functions. (onSomeKindOfMotionEvent): Handle scroll wheel events. (reparentTo, makeInputFocus, raise, lower, getWindowGeometry) (noticeIconified, noticeDeiconified, setDontAcceptFocus) (setDontFocusOnMap, getDontFocusOnMap): New functions. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (registerWindow, detachWindow): Synchronize. (noticeIconified, noticeDeiconified): New functions. (copyWindows): New function. * lisp/frame.el (frame-geometry, frame-edges) (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position) (frame-list-z-order, frame-restack, display-mouse-p) (display-monitor-attributes-list): Implement on Android. * lisp/mwheel.el (mouse-wheel-down-event): (mouse-wheel-up-event): (mouse-wheel-left-event): (mouse-wheel-right-event): Define on Android. * src/android.c (struct android_emacs_service): New methods `ringBell', `queryTree', `getScreenWidth', `getScreenHeight', and `detectMouse'. (struct android_event_queue, android_init_events) (android_next_event, android_write_event): Remove write limit. (android_file_access_p): Handle directories correcty. (android_close): Fix coding style. (android_fclose): New function. (android_init_emacs_service): Initialize new methods. (android_reparent_window): Implement function. (android_bell, android_set_input_focus, android_raise_window) (android_lower_window, android_query_tree, android_get_geometry) (android_get_screen_width, android_get_screen_height) (android_get_mm_width, android_get_mm_height, android_detect_mouse) (android_set_dont_focus_on_map, android_set_dont_accept_focus): New functions. (struct android_dir): New structure. (android_opendir, android_readdir, android_closedir): New functions. (emacs_abort): Implement here on Android and poke debuggerd into generating a tombstone. * src/android.h: Update prototypes. * src/androidfns.c (android_set_parent_frame): New function. (android_default_font_parameter): Use sane font size by default. (Fx_display_pixel_width, Fx_display_pixel_height) (Fx_display_mm_width, Fx_display_mm_height) (Fx_display_monitor_attributes_list): Rename to start with `android-'. Implement. Fiddle with documentation to introduce Android specific nuances. (Fandroid_display_monitor_attributes_list): New function. (Fx_frame_geometry, frame_geometry): New function. (Fandroid_frame_geometry): Implement correctly. (Fx_frame_list_z_order): Rename to start with `android-'. (android_frame_list_z_order, Fandroid_frame_list_z_order): Implement. (Fx_frame_restack): Rename to start with `android-'. (Fandroid_frame_restack): ``Implement''. (Fx_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_mouse_absolute_pixel_position): ``Implement''. (Fx_set_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_set_mouse_absolute_pixel_position): ``Implement''. (Fandroid_detect_mouse): New function. (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when clearing area. (android_set_no_focus_on_map, android_set_no_accept_focus): New functions. (android_frame_parm_handlers): Register new frame parameter handlers. (syms_of_androidfns): Update appropriately. * src/androidfont.c (androidfont_draw): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. * src/androidgui.h (enum android_event_type): New events. (struct android_touch_event, struct android_wheel_event) (struct android_iconify_event): New structures. (union android_event): Add new events. * src/androidterm.c (android_clear_frame): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. (android_flash, android_ring_bell): Implement bell ringing. (android_toggle_invisible_pointer): Don't TODO function that can't be implemented. (show_back_buffer, android_flush_dirty_back_buffer_on): Check if a buffer flip is required before doing the flip. (android_lower_frame, android_raise_frame): Implement functions. (android_update_tools, android_find_tool): New functions. (handle_one_android_event): Handle new iconification, wheel and touch events. (android_read_socket): Implement pending-autoraise-frames. (android_frame_up_to_date): Implement bell ringing. (android_buffer_flipping_unblocked_hook): Check if a buffer flip is required before doing the flip. (android_focus_frame, android_frame_highlight) (android_frame_unhighlight): New function. (android_frame_rehighlight): Implement functions. (android_iconify_frame): Always display error. (android_set_alpha): Update commentary. (android_free_frame_resources): Free frame touch points. (android_scroll_run, android_flip_and_flush) (android_clear_rectangle, android_draw_fringe_bitmap) (android_draw_glyph_string_background, android_fill_triangle) (android_clear_point, android_draw_relief_rect) (android_draw_box_rect, android_draw_glyph_string_bg_rect) (android_draw_image_foreground, android_draw_stretch_glyph_string) (android_draw_underwave, android_draw_glyph_string_foreground) (android_draw_composite_glyph_string_foreground) (android_draw_glyphless_glyph_string_foreground) (android_draw_glyph_string, android_clear_frame_area) (android_clear_under_internal_border, android_draw_hollow_cursor) (android_draw_bar_cursor, android_draw_vertical_window_border) (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW for drawing operations. * src/androidterm.h (struct android_touch_point): New structure. (struct android_output): New fields. (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro. * src/dired.c (emacs_readdir, open_directory) (directory_files_internal_unwind, read_dirent) (directory_files_internal, file_name_completion): Add indirection over readdir and opendir. Use android variants on Android. * src/dispnew.c (Fopen_termscript): * src/fileio.c (fclose_unwind): Use emacs_fclose. (Faccess_file): Call android_file_access_p. (file_accessible_directory_p): Append right suffix to Android assets directory. (do_auto_save_unwind): Use emacs_fclose. * src/keyboard.c (lispy_function_keys): Use right function key for page up and page down. (Fopen_dribble_file): Use emacs_fclose. * src/lisp.h: New prototype emacs_fclose. * src/lread.c (close_infile_unwind): Use emacs_fclose. * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation. (sfnt_prepare_raster): Compute raster width and height consistently with outline building. (sfnt_build_outline_edges): Use the same offsets used to set offy and offx. (main): Adjust debug code. * src/sfntfont-android.c (sfntfont_android_saturate32): Delete function. (sfntfont_android_blend, sfntfont_android_blendrgb): Remove unnecessary debug code. (sfntfont_android_composite_bitmap): Prevent out of bounds write. (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE. (init_sfntfont_android): Initialize Monospace Serif font to something sensible. * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics before summing up pcm. (sfntfont_draw): Use s->font instead of s->face->font. * src/sysdep.c (emacs_fclose): Wrap around android_fclose on android. * src/term.c (Fsuspend_tty): (delete_tty): Use emacs_fclose. * src/verbose.mk.in (AM_V_DX): Replace with D8 version. --- configure.ac | 35 +- java/AndroidManifest.xml.in | 84 +++ java/Makefile.in | 30 +- java/debug.sh | 45 +- java/org/gnu/emacs/EmacsActivity.java | 37 +- java/org/gnu/emacs/EmacsNative.java | 25 +- java/org/gnu/emacs/EmacsSdk7FontDriver.java | 7 - java/org/gnu/emacs/EmacsService.java | 116 ++++ java/org/gnu/emacs/EmacsSurfaceView.java | 22 +- java/org/gnu/emacs/EmacsView.java | 189 +++++-- java/org/gnu/emacs/EmacsWindow.java | 498 +++++++++++++++-- .../gnu/emacs/EmacsWindowAttachmentManager.java | 54 +- lisp/frame.el | 32 +- lisp/mwheel.el | 12 +- src/android.c | 551 ++++++++++++++++++- src/android.h | 23 + src/androidfns.c | 471 ++++++++++++++-- src/androidfont.c | 8 +- src/androidgui.h | 83 +++ src/androidterm.c | 589 ++++++++++++++++++--- src/androidterm.h | 28 + src/dired.c | 50 +- src/dispnew.c | 3 +- src/fileio.c | 42 +- src/keyboard.c | 6 +- src/lisp.h | 2 + src/lread.c | 2 +- src/sfnt.c | 35 +- src/sfntfont-android.c | 41 +- src/sfntfont.c | 5 +- src/sysdep.c | 17 +- src/term.c | 10 +- src/verbose.mk.in | 2 +- 33 files changed, 2809 insertions(+), 345 deletions(-) create mode 100644 java/AndroidManifest.xml.in (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/configure.ac b/configure.ac index 68de5f02b97..333a28cc142 100644 --- a/configure.ac +++ b/configure.ac @@ -816,8 +816,8 @@ a valid path to android.jar. See config.log for more details.]) Please verify that the path to the SDK build tools you specified is correct]) fi - AC_PATH_PROGS([DX], [dx d8], [], "${SDK_BUILD_TOOLS}:$PATH") - if test "DX" = ""; then + AC_PATH_PROGS([D8], [d8], [], "${SDK_BUILD_TOOLS}:$PATH") + if test "D8" = ""; then AC_MSG_ERROR([The Android dexer was not found. Please verify that the path to the SDK build tools you specified is correct]) fi @@ -867,6 +867,25 @@ in the ANDROID_CC variable when you ran configure.]) ANDROID_ABI=$android_abi + dnl Obtain the minimum SDK version of the resulting Emacs binary + dnl built with this NDK. + + ANDROID_MIN_SDK=8 + AC_MSG_CHECKING([for the lowest Android version Emacs can run on]) + [android_sdk=`echo "$cc_target" | grep -oE 'android([0-9][0-9]?)'`] + + if test -n "$android_sdk"; then + android_sdk=`echo "$android_sdk" | sed -n 's/android//p'` + AC_MSG_RESULT([$android_sdk]) + ANDROID_MIN_SDK=$android_sdk + else + AC_MSG_RESULT([unknown ($cc_target); assuming 8]) + AC_MSG_WARN([configure could not determine the versions of Android \ +a binary built with this compiler will run on. The generated application \ +package will likely install on older systems but crash on startup.]) + fi + AC_SUBST([ANDROID_MIN_SDK]) + # Save confdefs.h and config.log for now. mv -f confdefs.h _confdefs.h mv -f config.log _config.log @@ -899,7 +918,7 @@ fi AC_SUBST([ANDROID]) AC_SUBST([JAVAC]) AC_SUBST([AAPT]) -AC_SUBST([DX]) +AC_SUBST([D8]) AC_SUBST([ZIPALIGN]) AC_SUBST([ANDROID_JAR]) AC_SUBST([ANDROID_ABI]) @@ -914,7 +933,7 @@ fi AC_SUBST([XCONFIGURE]) if test "$ANDROID" = "yes"; then - # When --with-android is specified, all build options must be + # When --with-android is specified, almost all build options must be # disabled, both within the recursive invocation of configure and # outside. with_xpm=no @@ -938,11 +957,12 @@ if test "$ANDROID" = "yes"; then with_gpm=no with_dbus=no with_gsettings=no - with_selinx=no + with_selinux=no with_gnutls=no - with_zlib=no with_modules=no with_threads=no + + # zlib is available in android. fi dnl This used to use changequote, but, apart from 'changequote is evil' @@ -7211,6 +7231,9 @@ fi ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile]) ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile]) +# Make java/AndroidManifest.xml +ARCH_INDEPENDENT_CONFIG_FILES([java/AndroidManifest.xml]) + AC_OUTPUT if test ! "$with_mailutils"; then diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in new file mode 100644 index 00000000000..b680137a9d0 --- /dev/null +++ b/java/AndroidManifest.xml.in @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/Makefile.in b/java/Makefile.in index 31bd22b5a2e..05e61dede89 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -18,13 +18,15 @@ # along with GNU Emacs. If not, see . top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +version = @version@ -include ${top_builddir}/src/verbose.mk SHELL = @SHELL@ JAVAC = @JAVAC@ AAPT = @AAPT@ -DX = @DX@ +D8 = @D8@ ZIPALIGN = @ZIPALIGN@ JARSIGNER = @JARSIGNER@ ANDROID_JAR = @ANDROID_JAR@ @@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1 JAVA_FILES = $(shell find . -type f -name *.java) CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class) +# Compute the name for the Emacs application package. This should be: +# emacs---.apk + +ANDROID_MIN_SDK = @ANDROID_MIN_SDK@ +APK_NAME = emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk + # How this stuff works. # emacs.apk depends on emacs.apk-in, which is simply a ZIP archive @@ -55,7 +63,7 @@ CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class) # assets/lisp/ .PHONY: emacs.apk-in all -all: emacs.apk +all: $(APK_NAME) # Binaries to cross-compile. CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \ @@ -118,6 +126,18 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd rm -rf install_temp +# Makefile itself. +.PRECIOUS: ../config.status Makefile +../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 + $(MAKE) -C $(dir $@) $(notdir $@) +Makefile: ../config.status $(top_builddir)/java/Makefile.in + $(MAKE) -C .. java/$@ + +# AndroidManifest.xml: +AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \ + AndroidManifest.xml.in + pushd ..; ./config.status java/AndroidManifest.xml; popd + .SUFFIXES: .java .class .java.class &: $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $< @@ -126,7 +146,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml # nested classes. classes.dex: $(CLASS_FILES) - $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \ + $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \ $(subst $$,\$$,$(shell find . -type f -name *.class)) # When emacs.keystore expires, regenerate it with: @@ -136,7 +156,7 @@ classes.dex: $(CLASS_FILES) .PHONY: clean maintainer-clean -emacs.apk: classes.dex emacs.apk-in emacs.keystore +$(APK_NAME): classes.dex emacs.apk-in emacs.keystore cp -f emacs.apk-in $@.unaligned $(AAPT) add $@.unaligned classes.dex $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore" @@ -144,7 +164,7 @@ emacs.apk: classes.dex emacs.apk-in emacs.keystore rm -f $@.unaligned clean: - rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class + rm -f *.apk emacs.apk-in *.dex *.unaligned *.class rm -rf install-temp find . -name '*.class' -delete diff --git a/java/debug.sh b/java/debug.sh index dd710dc31af..3e3e3d9c281 100755 --- a/java/debug.sh +++ b/java/debug.sh @@ -30,6 +30,7 @@ activity=org.gnu.emacs.EmacsActivity gdb_port=5039 jdb_port=64013 jdb=no +attach_existing=no while [ $# -gt 0 ]; do case "$1" in @@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do echo " --port PORT run the GDB server on a specific port" echo " --jdb-port PORT run the JDB server on a specific port" echo " --jdb run JDB instead of GDB" + echo " --attach-existing attach to an existing process" echo " --help print this message" echo "" echo "Available devices:" @@ -63,6 +65,9 @@ while [ $# -gt 0 ]; do "--port" ) gdb_port=$1 ;; + "--attach-existing" ) + attach_existing=yes + ;; "--" ) shift gdbargs=$@ @@ -120,30 +125,32 @@ package_pids=`awk -- '{ print $1 }' <<< $package_pids` -# Finally, kill each existing process. -for pid in $package_pids; do - echo "Killing existing process $pid..." - adb -s $device shell run-as $package kill -9 $pid &> /dev/null -done - -# Now run the main activity. This must be done as the adb user and -# not as the package user. -echo "Starting activity $activity and attaching debugger" - -# Exit if the activity could not be started. -adb -s $device shell am start -D "$package/$activity" -if [ ! $? ]; then - exit 1; -fi +if [ "$attach_existing" != "yes" ]; then + # Finally, kill each existing process. + for pid in $package_pids; do + echo "Killing existing process $pid..." + adb -s $device shell run-as $package kill -9 $pid &> /dev/null + done + + # Now run the main activity. This must be done as the adb user and + # not as the package user. + echo "Starting activity $activity and attaching debugger" + + # Exit if the activity could not be started. + adb -s $device shell am start -D "$package/$activity" + if [ ! $? ]; then + exit 1; + fi -# Now look for processes matching the package again. -package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD` + # Now look for processes matching the package again. + package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD` -# Next, remove lines matching "ps" itself. -package_pids=`awk -- '{ + # Next, remove lines matching "ps" itself. + package_pids=`awk -- '{ if (!match ($0, /(PID|ps)/)) print $1 }' <<< $package_pids` +fi pid=$package_pids num_pids=`wc -w <<< "$package_pids"` diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 67f411a38c3..2b661024842 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -48,6 +48,9 @@ public class EmacsActivity extends Activity /* The currently focused window. */ public static EmacsWindow focusedWindow; + /* Whether or not this activity is paused. */ + private boolean isPaused; + static { focusedActivities = new ArrayList (); @@ -60,7 +63,7 @@ public class EmacsActivity extends Activity focusedWindow = window; for (EmacsWindow child : window.children) - invalidateFocus1 (window); + invalidateFocus1 (child); } public static void @@ -103,6 +106,9 @@ public class EmacsActivity extends Activity /* Clear the window's pointer to this activity and remove the window's view. */ window.setConsumer (null); + + /* The window can't be iconified any longer. */ + window.noticeDeiconified (); layout.removeView (window.view); window = null; @@ -114,6 +120,8 @@ public class EmacsActivity extends Activity public void attachWindow (EmacsWindow child) { + Log.d (TAG, "attachWindow: " + child); + if (window != null) throw new IllegalStateException ("trying to attach window when one" + " already exists"); @@ -124,6 +132,10 @@ public class EmacsActivity extends Activity layout.addView (window.view); child.setConsumer (this); + /* If the activity is iconified, send that to the window. */ + if (isPaused) + window.noticeIconified (); + /* Invalidate the focus. */ invalidateFocus (); } @@ -148,6 +160,9 @@ public class EmacsActivity extends Activity { FrameLayout.LayoutParams params; + /* Set the theme to one without a title bar. */ + setTheme (android.R.style.Theme_NoTitleBar); + params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); @@ -192,4 +207,24 @@ public class EmacsActivity extends Activity invalidateFocus (); } + + @Override + public void + onPause () + { + isPaused = true; + + EmacsWindowAttachmentManager.MANAGER.noticeIconified (this); + super.onResume (); + } + + @Override + public void + onResume () + { + isPaused = false; + + EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this); + super.onResume (); + } }; diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index ae48ce30408..a11e509cd7f 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -96,11 +96,34 @@ public class EmacsNative long time, int state, int button); - /* Send an ANDROID_BUTTON_RELEASE event. */ + /* Send an ANDROID_BUTTON_RELEASE event. */ public static native void sendButtonRelease (short window, int x, int y, long time, int state, int button); + /* Send an ANDROID_TOUCH_DOWN event. */ + public static native void sendTouchDown (short window, int x, int y, + long time, int pointerID); + + /* Send an ANDROID_TOUCH_UP event. */ + public static native void sendTouchUp (short window, int x, int y, + long time, int pointerID); + + /* Send an ANDROID_TOUCH_MOVE event. */ + public static native void sendTouchMove (short window, int x, int y, + long time, int pointerID); + + /* Send an ANDROID_WHEEL event. */ + public static native void sendWheel (short window, int x, int y, + long time, int state, + float xDelta, float yDelta); + + /* Send an ANDROID_ICONIFIED event. */ + public static native void sendIconified (short window); + + /* Send an ANDROID_DEICONIFIED event. */ + public static native void sendDeiconified (short window); + static { System.loadLibrary ("emacs"); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 8a9426050ae..c0f24c7433a 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -149,8 +149,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver } else familyName = fileName; - - Log.d (TAG, "Initialized new typeface " + familyName); } @Override @@ -321,17 +319,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver list = new LinkedList (); - Log.d (TAG, ("Looking for fonts matching font spec: " - + fontSpec.toString ())); - for (i = 0; i < typefaceList.length; ++i) { if (checkMatch (typefaceList[i], fontSpec)) list.add (new Sdk7FontEntity (typefaceList[i])); } - Log.d (TAG, "Found font entities: " + list.toString ()); - return (FontEntity[]) list.toArray (new FontEntity[0]); } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 4444b7f1c56..01a1695f385 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -28,20 +28,27 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.view.View; +import android.view.InputDevice; import android.annotation.TargetApi; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; + import android.os.Build; import android.os.Looper; import android.os.IBinder; import android.os.Handler; +import android.os.Vibrator; +import android.os.VibratorManager; +import android.os.VibrationEffect; import android.util.Log; import android.util.DisplayMetrics; +import android.hardware.input.InputManager; + class Holder { T thing; @@ -250,4 +257,113 @@ public class EmacsService extends Service { window.clearArea (x, y, width, height); } + + @SuppressWarnings ("deprecation") + public void + ringBell () + { + Vibrator vibrator; + VibrationEffect effect; + VibratorManager vibratorManager; + Object tem; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE); + vibratorManager = (VibratorManager) tem; + vibrator = vibratorManager.getDefaultVibrator (); + } + else + vibrator + = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + effect + = VibrationEffect.createOneShot (50, + VibrationEffect.DEFAULT_AMPLITUDE); + vibrator.vibrate (effect); + } + else + vibrator.vibrate (50); + } + + public short[] + queryTree (EmacsWindow window) + { + short[] array; + List windowList; + int i; + + if (window == null) + /* Just return all the windows without a parent. */ + windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows (); + else + windowList = window.children; + + array = new short[windowList.size () + 1]; + i = 1; + + array[0] = window.parent != null ? 0 : window.parent.handle; + + for (EmacsWindow treeWindow : windowList) + array[i++] = treeWindow.handle; + + return array; + } + + public int + getScreenWidth (boolean mmWise) + { + DisplayMetrics metrics; + + metrics = getResources ().getDisplayMetrics (); + + if (!mmWise) + return metrics.widthPixels; + else + return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0); + } + + public int + getScreenHeight (boolean mmWise) + { + DisplayMetrics metrics; + + metrics = getResources ().getDisplayMetrics (); + + if (!mmWise) + return metrics.heightPixels; + else + return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0); + } + + public boolean + detectMouse () + { + InputManager manager; + InputDevice device; + int[] ids; + int i; + + if (Build.VERSION.SDK_INT + < Build.VERSION_CODES.JELLY_BEAN) + return false; + + manager = (InputManager) getSystemService (Context.INPUT_SERVICE); + ids = manager.getInputDeviceIds (); + + for (i = 0; i < ids.length; ++i) + { + device = manager.getInputDevice (ids[i]); + + if (device == null) + continue; + + if (device.supportsSource (InputDevice.SOURCE_MOUSE)) + return true; + } + + return false; + } }; diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 5efb8882263..f713818d4bc 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -27,8 +27,12 @@ import android.os.Build; import android.graphics.Canvas; import android.graphics.Rect; +import android.util.Log; + public class EmacsSurfaceView extends SurfaceView { + private static final String TAG = "EmacsSurfaceView"; + public Object surfaceChangeLock; private boolean created; @@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView surfaceChanged (SurfaceHolder holder, int format, int width, int height) { + Log.d (TAG, "surfaceChanged: " + view); view.swapBuffers (); } @@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView { synchronized (surfaceChangeLock) { + Log.d (TAG, "surfaceCreated: " + view); created = true; - view.swapBuffers (); } + + /* Drop the lock when doing this, or a deadlock can + result. */ + view.swapBuffers (); } @Override @@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView { synchronized (surfaceChangeLock) { + Log.d (TAG, "surfaceDestroyed: " + view); created = false; } } @@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView return holder.lockCanvas (damage); } + @Override + protected void + onLayout (boolean changed, int left, int top, int right, + int bottom) + { + Log.d (TAG, ("onLayout: " + left + " " + top + " " + right + + " " + bottom + " -- " + changed + " visibility " + + getVisibility ())); + } + /* This method is only used during debugging when it seems damage isn't working correctly. */ diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 169a1e42ee3..41acabab97b 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -87,12 +87,27 @@ public class EmacsView extends ViewGroup /* Create the surface view. */ this.surfaceView = new EmacsSurfaceView (this); + this.surfaceView.setZOrderMediaOverlay (true); addView (this.surfaceView); + + /* Not sure exactly what this does but it makes things magically + work. Why is something as simple as XRaiseWindow so involved + on Android? */ + setChildrenDrawingOrderEnabled (true); + + /* Get rid of the foreground and background tint. */ + setBackgroundTintList (null); + setForegroundTintList (null); } private void handleDirtyBitmap () { + Bitmap oldBitmap; + + /* Save the old bitmap. */ + oldBitmap = bitmap; + /* Recreate the front and back buffer bitmaps. */ bitmap = Bitmap.createBitmap (bitmapDirty.width (), @@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup /* And canvases. */ canvas = new Canvas (bitmap); - /* If Emacs is drawing to the bitmap right now from the - main thread, the image contents are lost until the next - ConfigureNotify and complete garbage. Sorry! */ + /* Copy over the contents of the old bitmap. */ + if (oldBitmap != null) + canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ()); + bitmapDirty = null; } + public synchronized void + explicitlyDirtyBitmap (Rect rect) + { + if (bitmapDirty == null + && (bitmap == null + || rect.width () != bitmap.getWidth () + || rect.height () != bitmap.getHeight ())) + bitmapDirty = rect; + } + public synchronized Bitmap getBitmap () { @@ -168,25 +194,31 @@ public class EmacsView extends ViewGroup View child; Rect windowRect; + count = getChildCount (); + if (changed || mustReportLayout) { mustReportLayout = false; window.viewLayout (left, top, right, bottom); } - if (changed) + if (changed + /* Check that a change has really happened. */ + && (bitmapDirty == null + || bitmapDirty.width () != right - left + || bitmapDirty.height () != bottom - top)) bitmapDirty = new Rect (left, top, right, bottom); - count = getChildCount (); - for (i = 0; i < count; ++i) { child = getChildAt (i); + Log.d (TAG, "onLayout: " + child); + if (child == surfaceView) /* The child is the surface view, so give it the entire view. */ - child.layout (left, top, right, bottom); + child.layout (0, 0, right - left, bottom - top); else if (child.getVisibility () != GONE) { if (!(child instanceof EmacsView)) @@ -201,59 +233,68 @@ public class EmacsView extends ViewGroup } } - public synchronized void + public void damageRect (Rect damageRect) { - damageRegion.union (damageRect); + synchronized (damageRegion) + { + damageRegion.union (damageRect); + } } /* This method is called from both the UI thread and the Emacs thread. */ - public synchronized void + public void swapBuffers (boolean force) { Canvas canvas; Rect damageRect; Bitmap bitmap; - if (damageRegion.isEmpty ()) - return; + /* Code must always take damageRegion, and then surfaceChangeLock, + never the other way around! */ - bitmap = getBitmap (); + synchronized (damageRegion) + { + if (damageRegion.isEmpty ()) + return; - /* Emacs must take the following lock to ensure the access to the - canvas occurs with the surface created. Otherwise, Android - will throttle calls to lockCanvas. */ + bitmap = getBitmap (); - synchronized (surfaceView.surfaceChangeLock) - { - damageRect = damageRegion.getBounds (); + /* Emacs must take the following lock to ensure the access to the + canvas occurs with the surface created. Otherwise, Android + will throttle calls to lockCanvas. */ - if (!surfaceView.isCreated ()) - return; + synchronized (surfaceView.surfaceChangeLock) + { + damageRect = damageRegion.getBounds (); - if (bitmap == null) - return; + if (!surfaceView.isCreated ()) + return; - /* Lock the canvas with the specified damage. */ - canvas = surfaceView.lockCanvas (damageRect); + if (bitmap == null) + return; - /* Return if locking the canvas failed. */ - if (canvas == null) - return; + /* Lock the canvas with the specified damage. */ + canvas = surfaceView.lockCanvas (damageRect); - /* Copy from the back buffer to the canvas. If damageRect was - made empty, then draw the entire back buffer. */ + /* Return if locking the canvas failed. */ + if (canvas == null) + return; - if (damageRect.isEmpty ()) - canvas.drawBitmap (bitmap, 0f, 0f, paint); - else - canvas.drawBitmap (bitmap, damageRect, damageRect, paint); + /* Copy from the back buffer to the canvas. If damageRect was + made empty, then draw the entire back buffer. */ - /* Unlock the canvas and clear the damage. */ - surfaceView.unlockCanvasAndPost (canvas); - damageRegion.setEmpty (); + if (damageRect.isEmpty ()) + canvas.drawBitmap (bitmap, 0f, 0f, paint); + else + canvas.drawBitmap (bitmap, damageRect, damageRect, paint); + + /* Unlock the canvas and clear the damage. */ + surfaceView.unlockCanvasAndPost (canvas); + damageRegion.setEmpty (); + } } } @@ -308,6 +349,78 @@ public class EmacsView extends ViewGroup public boolean onTouchEvent (MotionEvent motion) { - return window.onSomeKindOfMotionEvent (motion); + return window.onTouchEvent (motion); + } + + private void + moveChildToBack (View child) + { + int index; + + index = indexOfChild (child); + + if (index > 0) + { + detachViewFromParent (index); + + /* The view at 0 is the surface view. */ + attachViewToParent (child, 1, + child.getLayoutParams()); + } + } + + + /* The following two functions must not be called if the view has no + parent, or is parented to an activity. */ + + public void + raise () + { + EmacsView parent; + + parent = (EmacsView) getParent (); + + Log.d (TAG, "raise: parent " + parent); + + if (parent.indexOfChild (this) + == parent.getChildCount () - 1) + return; + + parent.bringChildToFront (this); + + /* Yes, all of this is really necessary! */ + parent.requestLayout (); + parent.invalidate (); + requestLayout (); + invalidate (); + + /* The surface view must be destroyed and recreated. */ + removeView (surfaceView); + addView (surfaceView, 0); + } + + public void + lower () + { + EmacsView parent; + + parent = (EmacsView) getParent (); + + Log.d (TAG, "lower: parent " + parent); + + if (parent.indexOfChild (this) == 1) + return; + + parent.moveChildToBack (this); + + /* Yes, all of this is really necessary! */ + parent.requestLayout (); + parent.invalidate (); + requestLayout (); + invalidate (); + + /* The surface view must be removed and attached again. */ + removeView (surfaceView); + addView (surfaceView, 0); } }; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index a9a24b61869..1f8596dba50 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -22,6 +22,7 @@ package org.gnu.emacs; import java.lang.IllegalStateException; import java.util.ArrayList; import java.util.List; +import java.util.HashMap; import android.graphics.Rect; import android.graphics.Canvas; @@ -50,9 +51,29 @@ import android.os.Build; Views are also drawables, meaning they can accept drawing requests. */ +/* Help wanted. What does not work includes `EmacsView.raise', + `EmacsView.lower', reparenting a window onto another window. + + All three are likely undocumented restrictions within + EmacsSurface. */ + public class EmacsWindow extends EmacsHandleObject implements EmacsDrawable { + private static final String TAG = "EmacsWindow"; + + private class Coordinate + { + /* Integral coordinate. */ + int x, y; + + Coordinate (int x, int y) + { + this.x = x; + this.y = y; + } + }; + /* The view associated with the window. */ public EmacsView view; @@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject private Rect rect; /* The parent window, or null if it is the root window. */ - private EmacsWindow parent; + public EmacsWindow parent; /* List of all children in stacking order. This must be kept consistent! */ public ArrayList children; + /* Map between pointer identifiers and last known position. Used to + compute which pointer changed upon a touch event. */ + private HashMap pointerMap; + /* The window consumer currently attached, if it exists. */ private EmacsWindowAttachmentManager.WindowConsumer attached; @@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject last button press or release event. */ private int lastButtonState, lastModifiers; + /* Whether or not the window is mapped, and whether or not it is + deiconified. */ + private boolean isMapped, isIconified; + + /* Whether or not to ask for focus upon being mapped, and whether or + not the window should be focusable. */ + private boolean dontFocusOnMap, dontAcceptFocus; + public EmacsWindow (short handle, final EmacsWindow parent, int x, int y, int width, int height) @@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject super (handle); rect = new Rect (x, y, x + width, y + height); + pointerMap = new HashMap (); /* Create the view from the context's UI thread. The window is unmapped, so the view is GONE. */ @@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject if (parent != null) { parent.children.add (this); - parent.view.post (new Runnable () { + EmacsService.SERVICE.runOnUiThread (new Runnable () { @Override public void run () @@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject } }); } - else - EmacsService.SERVICE.runOnUiThread (new Runnable () { - @Override - public void - run () - { - EmacsWindowAttachmentManager manager; - - manager = EmacsWindowAttachmentManager.MANAGER; - - /* If parent is the root window, notice that there are new - children available for interested activites to pick - up. */ - - manager.registerWindow (EmacsWindow.this); - } - }); scratchGC = new EmacsGC ((short) 0); } @@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject + "children!"); /* Remove the view from its parent and make it invisible. */ - view.post (new Runnable () { + EmacsService.SERVICE.runOnUiThread (new Runnable () { public void run () { @@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject parent = (View) view.getParent (); - if (parent != null && attached == null) + if (parent != null) ((ViewGroup) parent).removeView (view); manager.detachWindow (EmacsWindow.this); @@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject public void viewLayout (int left, int top, int right, int bottom) { + int rectWidth, rectHeight; + synchronized (this) { rect.left = left; rect.top = top; rect.right = right; rect.bottom = bottom; - - EmacsNative.sendConfigureNotify (this.handle, - System.currentTimeMillis (), - left, top, rect.width (), - rect.height ()); } + + rectWidth = right - left; + rectHeight = bottom - top; + + EmacsNative.sendConfigureNotify (this.handle, + System.currentTimeMillis (), + left, top, rectWidth, + rectHeight); } public void requestViewLayout () { - view.post (new Runnable () { + /* This is necessary because otherwise subsequent drawing on the + Emacs thread may be lost. */ + view.explicitlyDirtyBitmap (rect); + + EmacsService.SERVICE.runOnUiThread (new Runnable () { @Override public void run () @@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject public void mapWindow () { - view.post (new Runnable () { - @Override - public void - run () - { + if (isMapped) + return; - view.setVisibility (View.VISIBLE); - /* Eventually this should check no-focus-on-map. */ - view.requestFocus (); - } - }); + isMapped = true; + + if (parent == null) + { + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + EmacsWindowAttachmentManager manager; + + /* Make the view visible, first of all. */ + view.setVisibility (View.VISIBLE); + + manager = EmacsWindowAttachmentManager.MANAGER; + + /* If parent is the root window, notice that there are new + children available for interested activites to pick + up. */ + manager.registerWindow (EmacsWindow.this); + + if (!getDontFocusOnMap ()) + /* Eventually this should check no-focus-on-map. */ + view.requestFocus (); + } + }); + } + else + { + /* Do the same thing as above, but don't register this + window. */ + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.setVisibility (View.VISIBLE); + + if (!getDontFocusOnMap ()) + /* Eventually this should check no-focus-on-map. */ + view.requestFocus (); + } + }); + } } public void unmapWindow () { + if (!isMapped) + return; + + isMapped = false; + view.post (new Runnable () { @Override public void run () { + EmacsWindowAttachmentManager manager; + + manager = EmacsWindowAttachmentManager.MANAGER; + view.setVisibility (View.GONE); + + /* Now that the window is unmapped, unregister it as + well. */ + manager.detachWindow (EmacsWindow.this); } }); } @@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject return 4; } + /* Return the ID of the pointer which changed in EVENT. Value is -1 + if it could not be determined, else the pointer that changed, or + -2 if -1 would have been returned, but there is also a pointer + that is a mouse. */ + + private int + figureChange (MotionEvent event) + { + int pointerID, i, truncatedX, truncatedY, pointerIndex; + Coordinate coordinate; + boolean mouseFlag; + + /* pointerID is always initialized but the Java compiler is too + dumb to know that. */ + pointerID = -1; + mouseFlag = false; + + switch (event.getActionMasked ()) + { + case MotionEvent.ACTION_DOWN: + /* Primary pointer pressed with index 0. */ + + /* Detect mice. If this is a mouse event, give it to + onSomeKindOfMotionEvent. */ + if ((Build.VERSION.SDK_INT + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE) + return -2; + + pointerID = event.getPointerId (0); + pointerMap.put (pointerID, + new Coordinate ((int) event.getX (0), + (int) event.getY (0))); + break; + + case MotionEvent.ACTION_UP: + /* Primary pointer released with index 0. */ + pointerID = event.getPointerId (0); + pointerMap.remove (pointerID); + break; + + case MotionEvent.ACTION_POINTER_DOWN: + /* New pointer. Find the pointer ID from the index and place + it in the map. */ + pointerIndex = event.getActionIndex (); + pointerID = event.getPointerId (pointerIndex); + pointerMap.put (pointerID, + new Coordinate ((int) event.getX (pointerID), + (int) event.getY (pointerID))); + break; + + case MotionEvent.ACTION_POINTER_UP: + /* Pointer removed. Remove it from the map. */ + pointerIndex = event.getActionIndex (); + pointerID = event.getPointerId (pointerIndex); + pointerMap.remove (pointerID); + break; + + default: + + /* Loop through each pointer in the event. */ + for (i = 0; i < event.getPointerCount (); ++i) + { + pointerID = event.getPointerId (i); + + /* Look up that pointer in the map. */ + coordinate = pointerMap.get (pointerID); + + if (coordinate != null) + { + /* See if coordinates have changed. */ + truncatedX = (int) event.getX (i); + truncatedY = (int) event.getY (i); + + if (truncatedX != coordinate.x + || truncatedY != coordinate.y) + { + /* The pointer changed. Update the coordinate and + break out of the loop. */ + coordinate.x = truncatedX; + coordinate.y = truncatedY; + + break; + } + } + + /* See if this is a mouse. If so, set the mouseFlag. */ + if ((Build.VERSION.SDK_INT + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE) + mouseFlag = true; + } + + /* Set the pointer ID to -1 if the loop failed to find any + changed pointer. If a mouse pointer was found, set it to + -2. */ + if (i == event.getPointerCount ()) + pointerID = (mouseFlag ? -2 : -1); + } + + /* Return the pointer ID. */ + return pointerID; + } + + public boolean + onTouchEvent (MotionEvent event) + { + int pointerID, index; + + /* Extract the ``touch ID'' (or in Android, the ``pointer + ID''.) */ + pointerID = figureChange (event); + + if (pointerID < 0) + { + /* If this is a mouse event, give it to + onSomeKindOfMotionEvent. */ + if (pointerID == -2) + return onSomeKindOfMotionEvent (event); + + return false; + } + + /* Find the pointer index corresponding to the event. */ + index = event.findPointerIndex (pointerID); + + switch (event.getActionMasked ()) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + /* Touch down event. */ + EmacsNative.sendTouchDown (this.handle, (int) event.getX (index), + (int) event.getY (index), + event.getEventTime (), pointerID); + return true; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + /* Touch up event. */ + EmacsNative.sendTouchUp (this.handle, (int) event.getX (index), + (int) event.getY (index), + event.getEventTime (), pointerID); + return true; + + case MotionEvent.ACTION_MOVE: + /* Pointer motion event. */ + EmacsNative.sendTouchMove (this.handle, (int) event.getX (index), + (int) event.getY (index), + event.getEventTime (), pointerID); + return true; + } + + return false; + } + public boolean onSomeKindOfMotionEvent (MotionEvent event) { @@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_UP: - /* Emacs must return true even though touch events are not yet - handled, because the value of this function is used by the - system to decide whether or not Emacs gets ACTION_MOVE + /* Emacs must return true even though touch events are not + handled here, because the value of this function is used by + the system to decide whether or not Emacs gets ACTION_MOVE events. */ return true; + + case MotionEvent.ACTION_SCROLL: + /* Send a scroll event with the specified deltas. */ + EmacsNative.sendWheel (this.handle, (int) event.getX (), + (int) event.getY (), + event.getEventTime (), + lastModifiers, + event.getAxisValue (MotionEvent.AXIS_HSCROLL), + event.getAxisValue (MotionEvent.AXIS_VSCROLL)); + return true; } return false; } + + public void + reparentTo (final EmacsWindow otherWindow, int x, int y) + { + int width, height; + + /* Reparent this window to the other window. */ + + if (parent != null) + parent.children.remove (this); + + if (otherWindow != null) + otherWindow.children.add (this); + + parent = otherWindow; + + /* Move this window to the new location. */ + synchronized (this) + { + width = rect.width (); + height = rect.height (); + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + } + + /* Now do the work necessary on the UI thread to reparent the + window. */ + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + EmacsWindowAttachmentManager manager; + View parent; + + /* First, detach this window if necessary. */ + manager = EmacsWindowAttachmentManager.MANAGER; + manager.detachWindow (EmacsWindow.this); + + /* Also unparent this view. */ + parent = (View) view.getParent (); + + if (parent != null) + ((ViewGroup) parent).removeView (view); + + /* Next, either add this window as a child of the new + parent's view, or make it available again. */ + if (otherWindow != null) + otherWindow.view.addView (view); + else if (EmacsWindow.this.isMapped) + manager.registerWindow (EmacsWindow.this); + + /* Request relayout. */ + view.requestLayout (); + } + }); + } + + public void + makeInputFocus (long time) + { + /* TIME is currently ignored. Request the input focus now. */ + + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.requestFocus (); + } + }); + } + + public void + raise () + { + /* This does nothing here. */ + if (parent == null) + return; + + /* Remove and add this view again. */ + parent.children.remove (this); + parent.children.add (this); + + /* Request a relayout. */ + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.raise (); + } + }); + } + + public void + lower () + { + /* This does nothing here. */ + if (parent == null) + return; + + /* Remove and add this view again. */ + parent.children.remove (this); + parent.children.add (this); + + /* Request a relayout. */ + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.lower (); + } + }); + } + + public int[] + getWindowGeometry () + { + int[] array; + Rect rect; + + array = new int[4]; + rect = getGeometry (); + + array[0] = rect.left; + array[1] = rect.top; + array[2] = rect.width (); + array[3] = rect.height (); + + return array; + } + + public void + noticeIconified () + { + isIconified = true; + EmacsNative.sendIconified (this.handle); + } + + public void + noticeDeiconified () + { + isIconified = false; + EmacsNative.sendDeiconified (this.handle); + } + + public synchronized void + setDontAcceptFocus (final boolean dontAcceptFocus) + { + this.dontAcceptFocus = dontAcceptFocus; + + /* Update the view's focus state. */ + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.setFocusable (!dontAcceptFocus); + view.setFocusableInTouchMode (!dontAcceptFocus); + } + }); + } + + public synchronized void + setDontFocusOnMap (final boolean dontFocusOnMap) + { + this.dontFocusOnMap = dontFocusOnMap; + } + + public synchronized boolean + getDontFocusOnMap () + { + return dontFocusOnMap; + } }; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 34be2ab8789..15eb3bb65c2 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -19,6 +19,7 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -68,7 +69,7 @@ public class EmacsWindowAttachmentManager }; private List consumers; - private List windows; + public List windows; public EmacsWindowAttachmentManager () @@ -98,12 +99,19 @@ public class EmacsWindowAttachmentManager EmacsNative.sendWindowAction ((short) 0, 0); } - public void + public synchronized void registerWindow (EmacsWindow window) { Intent intent; - Log.d (TAG, "registerWindow " + window); + Log.d (TAG, "registerWindow (maybe): " + window); + + if (windows.contains (window)) + /* The window is already registered. */ + return; + + Log.d (TAG, "registerWindow: " + window); + windows.add (window); for (WindowConsumer consumer : consumers) @@ -146,7 +154,7 @@ public class EmacsWindowAttachmentManager consumers.remove (consumer); } - public void + public synchronized void detachWindow (EmacsWindow window) { WindowConsumer consumer; @@ -162,5 +170,43 @@ public class EmacsWindowAttachmentManager consumers.remove (consumer); consumer.destroy (); } + + windows.remove (window); + } + + public void + noticeIconified (WindowConsumer consumer) + { + EmacsWindow window; + + Log.d (TAG, "noticeIconified " + consumer); + + /* If a window is attached, send the appropriate iconification + events. */ + window = consumer.getAttachedWindow (); + + if (window != null) + window.noticeIconified (); + } + + public void + noticeDeiconified (WindowConsumer consumer) + { + EmacsWindow window; + + Log.d (TAG, "noticeDeiconified " + consumer); + + /* If a window is attached, send the appropriate iconification + events. */ + window = consumer.getAttachedWindow (); + + if (window != null) + window.noticeDeiconified (); + } + + public synchronized List + copyWindows () + { + return new ArrayList (windows); } }; diff --git a/lisp/frame.el b/lisp/frame.el index 436323f7d58..d35df71a6cc 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -1647,6 +1647,7 @@ live frame and defaults to the selected one." (declare-function ns-frame-geometry "nsfns.m" (&optional frame)) (declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame)) (declare-function haiku-frame-geometry "haikufns.c" (&optional frame)) +(declare-function android-frame-geometry "androidfns.c" (&optional frame)) (defun frame-geometry (&optional frame) "Return geometric attributes of FRAME. @@ -1700,6 +1701,8 @@ and width values are in pixels. (pgtk-frame-geometry frame)) ((eq frame-type 'haiku) (haiku-frame-geometry frame)) + ((eq frame-type 'android) + (android-frame-geometry frame)) (t (list '(outer-position 0 . 0) @@ -1826,6 +1829,7 @@ of frames like calls to map a frame or change its visibility." (declare-function ns-frame-edges "nsfns.m" (&optional frame type)) (declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type)) (declare-function haiku-frame-edges "haikufns.c" (&optional frame type)) +(declare-function android-frame-edges "androidfns.c" (&optional frame type)) (defun frame-edges (&optional frame type) "Return coordinates of FRAME's edges. @@ -1853,6 +1857,8 @@ FRAME." (pgtk-frame-edges frame type)) ((eq frame-type 'haiku) (haiku-frame-edges frame type)) + ((eq frame-type 'android) + (android-frame-edges frame type)) (t (list 0 0 (frame-width frame) (frame-height frame)))))) @@ -1861,6 +1867,7 @@ FRAME." (declare-function ns-mouse-absolute-pixel-position "nsfns.m") (declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c") (declare-function haiku-mouse-absolute-pixel-position "haikufns.c") +(declare-function android-mouse-absolute-pixel-position "androidfns.c") (defun mouse-absolute-pixel-position () "Return absolute position of mouse cursor in pixels. @@ -1879,6 +1886,8 @@ position (0, 0) of the selected frame's terminal." (pgtk-mouse-absolute-pixel-position)) ((eq frame-type 'haiku) (haiku-mouse-absolute-pixel-position)) + ((eq frame-type 'android) + (android-mouse-absolute-pixel-position)) (t (cons 0 0))))) @@ -1887,6 +1896,8 @@ position (0, 0) of the selected frame's terminal." (declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y)) (declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y)) (declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y)) +(declare-function android-set-mouse-absolute-pixel-position + "androidfns.c" (x y)) (defun set-mouse-absolute-pixel-position (x y) "Move mouse pointer to absolute pixel position (X, Y). @@ -1903,7 +1914,9 @@ position (0, 0) of the selected frame's terminal." ((eq frame-type 'w32) (w32-set-mouse-absolute-pixel-position x y)) ((eq frame-type 'haiku) - (haiku-set-mouse-absolute-pixel-position x y))))) + (haiku-set-mouse-absolute-pixel-position x y)) + ((eq frame-type 'android) + (android-set-mouse-absolute-pixel-position x y))))) (defun frame-monitor-attributes (&optional frame) "Return the attributes of the physical monitor dominating FRAME. @@ -1999,6 +2012,7 @@ workarea attribute." ;; TODO: implement this on PGTK. ;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display)) (declare-function haiku-frame-list-z-order "haikufns.c" (&optional display)) +(declare-function android-frame-list-z-order "androidfns.c" (&optional display)) (defun frame-list-z-order (&optional display) "Return list of Emacs' frames, in Z (stacking) order. @@ -2024,13 +2038,17 @@ Return nil if DISPLAY contains no Emacs frame." ;; (pgtk-frame-list-z-order display) nil) ((eq frame-type 'haiku) - (haiku-frame-list-z-order display))))) + (haiku-frame-list-z-order display)) + ((eq frame-type 'android) + (android-frame-list-z-order display))))) (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above)) (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above)) (declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above)) (declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional above)) (declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional above)) +(declare-function android-frame-restack "androidfns.c" (frame1 frame2 + &optional above)) (defun frame-restack (frame1 frame2 &optional above) "Restack FRAME1 below FRAME2. @@ -2064,7 +2082,9 @@ Some window managers may refuse to restack windows." ((eq frame-type 'haiku) (haiku-frame-restack frame1 frame2 above)) ((eq frame-type 'pgtk) - (pgtk-frame-restack frame1 frame2 above)))) + (pgtk-frame-restack frame1 frame2 above)) + ((eq frame-type 'android) + (android-frame-restack frame1 frame2 above)))) (error "Cannot restack frames"))) (defun frame-size-changed-p (&optional frame) @@ -2113,6 +2133,8 @@ frame's display)." (> w32-num-mouse-buttons 0))) ((memq frame-type '(x ns haiku pgtk)) t) ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing device + ((eq frame-type 'android) + (android-detect-mouse)) (t (or (and (featurep 'xt-mouse) xterm-mouse-mode) @@ -2396,6 +2418,8 @@ If DISPLAY is omitted or nil, it defaults to the selected frame's display." (&optional terminal)) (declare-function haiku-display-monitor-attributes-list "haikufns.c" (&optional terminal)) +(declare-function android-display-monitor-attributes-list "androidfns.c" + (&optional terminal)) (defun display-monitor-attributes-list (&optional display) "Return a list of physical monitor attributes on DISPLAY. @@ -2449,6 +2473,8 @@ monitors." (pgtk-display-monitor-attributes-list display)) ((eq frame-type 'haiku) (haiku-display-monitor-attributes-list display)) + ((eq frame-type 'android) + (android-display-monitor-attributes-list display)) (t (let ((geometry (list 0 0 (display-pixel-width display) (display-pixel-height display)))) diff --git a/lisp/mwheel.el b/lisp/mwheel.el index 1be52d24e34..67d93c3558d 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -58,7 +58,8 @@ (defcustom mouse-wheel-down-event (if (or (featurep 'w32-win) (featurep 'ns-win) - (featurep 'haiku-win) (featurep 'pgtk-win)) + (featurep 'haiku-win) (featurep 'pgtk-win) + (featurep 'android-win)) 'wheel-up 'mouse-4) "Event used for scrolling down." @@ -79,7 +80,8 @@ (defcustom mouse-wheel-up-event (if (or (featurep 'w32-win) (featurep 'ns-win) - (featurep 'haiku-win) (featurep 'pgtk-win)) + (featurep 'haiku-win) (featurep 'pgtk-win) + (featurep 'android-win)) 'wheel-down 'mouse-5) "Event used for scrolling up." @@ -254,7 +256,8 @@ Also see `mouse-wheel-tilt-scroll'." (defvar mouse-wheel-left-event (if (or (featurep 'w32-win) (featurep 'ns-win) - (featurep 'haiku-win) (featurep 'pgtk-win)) + (featurep 'haiku-win) (featurep 'pgtk-win) + (featurep 'android-win)) 'wheel-left 'mouse-6) "Event used for scrolling left.") @@ -268,7 +271,8 @@ Also see `mouse-wheel-tilt-scroll'." (defvar mouse-wheel-right-event (if (or (featurep 'w32-win) (featurep 'ns-win) - (featurep 'haiku-win) (featurep 'pgtk-win)) + (featurep 'haiku-win) (featurep 'pgtk-win) + (featurep 'android-win)) 'wheel-right 'mouse-7) "Event used for scrolling right.") diff --git a/src/android.c b/src/android.c index 2f852662001..eab18dc6152 100644 --- a/src/android.c +++ b/src/android.c @@ -82,6 +82,11 @@ struct android_emacs_service jmethodID copy_area; jmethodID clear_window; jmethodID clear_area; + jmethodID ring_bell; + jmethodID query_tree; + jmethodID get_screen_width; + jmethodID get_screen_height; + jmethodID detect_mouse; }; struct android_emacs_pixmap @@ -196,9 +201,6 @@ struct android_event_queue /* The thread used to run select. */ pthread_t select_thread; - /* Condition variable for the writing side. */ - pthread_cond_t write_var; - /* Condition variables for the reading side. */ pthread_cond_t read_var; @@ -314,11 +316,6 @@ android_init_events (void) "pthread_mutex_init: %s", strerror (errno)); - if (pthread_cond_init (&event_queue.write_var, NULL)) - __android_log_print (ANDROID_LOG_FATAL, __func__, - "pthread_cond_init: %s", - strerror (errno)); - if (pthread_cond_init (&event_queue.read_var, NULL)) __android_log_print (ANDROID_LOG_FATAL, __func__, "pthread_cond_init: %s", @@ -387,8 +384,7 @@ android_next_event (union android_event *event_return) /* Free the container. */ free (container); - /* Signal that events can now be written. */ - pthread_cond_signal (&event_queue.write_var); + /* Unlock the queue. */ pthread_mutex_unlock (&event_queue.mutex); } @@ -407,12 +403,6 @@ android_write_event (union android_event *event) return; pthread_mutex_lock (&event_queue.mutex); - - /* The event queue is full, wait for events to be read. */ - if (event_queue.num_events >= 1024) - pthread_cond_wait (&event_queue.write_var, - &event_queue.mutex); - container->next = event_queue.events.next; container->last = &event_queue.events; container->next->last = container; @@ -421,6 +411,10 @@ android_write_event (union android_event *event) event_queue.num_events++; pthread_cond_signal (&event_queue.read_var); pthread_mutex_unlock (&event_queue.mutex); + + /* Now set pending_signals to true. This allows C-g to be handled + immediately even without SIGIO. */ + pending_signals = true; } int @@ -604,31 +598,60 @@ android_file_access_p (const char *name, int amode) { AAsset *asset; AAssetDir *directory; + int length; if (!asset_manager) return false; if (!(amode & W_OK) && (name = android_get_asset_name (name))) { + if (!strcmp (name, "") || !strcmp (name, "/")) + /* /assets always exists. */ + return true; + /* Check if the asset exists by opening it. Suboptimal! */ asset = AAssetManager_open (asset_manager, name, AASSET_MODE_UNKNOWN); if (!asset) { - /* See if it's a directory also. */ + /* See if it's a directory as well. To open a directory + with the asset manager, the trailing slash (if specified) + must be removed. */ directory = AAssetManager_openDir (asset_manager, name); if (directory) { + /* Make sure the directory actually has files in it. */ + + if (!AAssetDir_getNextFileName (directory)) + { + AAssetDir_close (directory); + errno = ENOENT; + return false; + } + AAssetDir_close (directory); return true; } + errno = ENOENT; return false; } AAsset_close (asset); + + /* If NAME is a directory name, but it was a regular file, set + errno to ENOTDIR and return false. This is to behave like + faccessat. */ + + length = strlen (name); + if (name[length - 1] == '/') + { + errno = ENOTDIR; + return false; + } + return true; } @@ -819,12 +842,31 @@ int android_close (int fd) { if (fd < ANDROID_MAX_ASSET_FD - && (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID)) + && (android_table[fd].flags + & ANDROID_FD_TABLE_ENTRY_IS_VALID)) android_table[fd].flags = 0; return close (fd); } +/* Like fclose. However, remove any information associated with + FILE's file descriptor from the asset table as well. */ + +int +android_fclose (FILE *stream) +{ + int fd; + + fd = fileno (stream); + + if (fd != -1 && fd < ANDROID_MAX_ASSET_FD + && (android_table[fd].flags + & ANDROID_FD_TABLE_ENTRY_IS_VALID)) + android_table[fd].flags = 0; + + return fclose (stream); +} + /* Return the current user's ``home'' directory, which is actually the app data directory on Android. */ @@ -1010,7 +1052,12 @@ android_init_emacs_service (void) "(Lorg/gnu/emacs/EmacsWindow;)V"); FIND_METHOD (clear_area, "clearArea", "(Lorg/gnu/emacs/EmacsWindow;IIII)V"); - + FIND_METHOD (ring_bell, "ringBell", "()V"); + FIND_METHOD (query_tree, "queryTree", + "(Lorg/gnu/emacs/EmacsWindow;)[S"); + FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I"); + FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I"); + FIND_METHOD (detect_mouse, "detectMouse", "()Z"); #undef FIND_METHOD } @@ -1342,6 +1389,97 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, android_write_event (&event); } +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, + jshort window, jint x, jint y, + jlong time, jint pointer_id) +{ + union android_event event; + + event.touch.type = ANDROID_TOUCH_DOWN; + event.touch.window = window; + event.touch.x = x; + event.touch.y = y; + event.touch.time = time; + event.touch.pointer_id = pointer_id; + + android_write_event (&event); +} + +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, + jshort window, jint x, jint y, + jlong time, jint pointer_id) +{ + union android_event event; + + event.touch.type = ANDROID_TOUCH_UP; + event.touch.window = window; + event.touch.x = x; + event.touch.y = y; + event.touch.time = time; + event.touch.pointer_id = pointer_id; + + android_write_event (&event); +} + +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, + jshort window, jint x, jint y, + jlong time, jint pointer_id) +{ + union android_event event; + + event.touch.type = ANDROID_TOUCH_MOVE; + event.touch.window = window; + event.touch.x = x; + event.touch.y = y; + event.touch.time = time; + event.touch.pointer_id = pointer_id; + + android_write_event (&event); +} + +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, + jshort window, jint x, jint y, + jlong time, jint state, + jfloat x_delta, jfloat y_delta) +{ + union android_event event; + + event.wheel.type = ANDROID_WHEEL; + event.wheel.window = window; + event.wheel.x = x; + event.wheel.y = y; + event.wheel.time = time; + event.wheel.state = state; + event.wheel.x_delta = x_delta; + event.wheel.y_delta = y_delta; + + android_write_event (&event); +} + +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, + jshort window) +{ + union android_event event; + + event.iconified.type = ANDROID_ICONIFIED; + event.iconified.window = window; +} + +extern JNIEXPORT void JNICALL +NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, + jshort window) +{ + union android_event event; + + event.iconified.type = ANDROID_DEICONIFIED; + event.iconified.window = window; +} + #pragma clang diagnostic pop @@ -1979,10 +2117,22 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin, } void -android_reparent_window (android_window w, android_window parent, +android_reparent_window (android_window w, android_window parent_handle, int x, int y) { - /* TODO */ + jobject window, parent; + jmethodID method; + + window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW); + parent = android_resolve_handle (parent_handle, + ANDROID_HANDLE_WINDOW); + + method = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "reparentTo", + "(Lorg/gnu/emacs/EmacsWindow;II)V"); + (*android_java_env)->CallVoidMethod (android_java_env, window, + method, + parent, (jint) x, (jint) y); } /* Look up the method with SIGNATURE by NAME in CLASS. Abort if it @@ -2904,6 +3054,163 @@ android_put_image (android_pixmap handle, struct android_image *image) ANDROID_DELETE_LOCAL_REF (bitmap); } +void +android_bell (void) +{ + (*android_java_env)->CallVoidMethod (android_java_env, + emacs_service, + service_class.ring_bell); +} + +void +android_set_input_focus (android_window handle, unsigned long time) +{ + jobject window; + jmethodID make_input_focus; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + make_input_focus = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "makeInputFocus", "(J)V"); + + (*android_java_env)->CallVoidMethod (android_java_env, window, + make_input_focus, (jlong) time); +} + +void +android_raise_window (android_window handle) +{ + jobject window; + jmethodID raise; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + raise = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "raise", "()V"); + + (*android_java_env)->CallVoidMethod (android_java_env, window, + raise); +} + +void +android_lower_window (android_window handle) +{ + jobject window; + jmethodID lower; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + lower = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "lower", "()V"); + + (*android_java_env)->CallVoidMethod (android_java_env, window, + lower); +} + +int +android_query_tree (android_window handle, android_window *root_return, + android_window *parent_return, + android_window **children_return, + unsigned int *nchildren_return) +{ + jobject window, array; + jsize nelements, i; + android_window *children; + jshort *shorts; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + + /* window can be NULL, so this is a service method. */ + array + = (*android_java_env)->CallObjectMethod (android_java_env, + emacs_service, + service_class.query_tree, + window); + if (!array) + { + (*android_java_env)->ExceptionClear (android_java_env); + memory_full (0); + } + + /* The first element of the array is the parent window. The rest + are the children. */ + nelements = (*android_java_env)->GetArrayLength (android_java_env, + array); + eassert (nelements); + + /* Now fill in the children. */ + children = xnmalloc (nelements - 1, sizeof *children); + + shorts + = (*android_java_env)->GetShortArrayElements (android_java_env, array, + NULL); + for (i = 1; i < nelements; ++i) + children[i] = shorts[i]; + + /* Finally, return the parent and other values. */ + *root_return = 0; + *parent_return = shorts[0]; + *children_return = children; + + /* Release the array contents. */ + (*android_java_env)->ReleaseShortArrayElements (android_java_env, array, + shorts, JNI_ABORT); + + ANDROID_DELETE_LOCAL_REF (array); + return 1; +} + +void +android_get_geometry (android_window handle, + android_window *root_return, + int *x_return, int *y_return, + unsigned int *width_return, + unsigned int *height_return, + unsigned int *border_width_return) +{ + jobject window; + jarray window_geometry; + jmethodID get_geometry; + jint *ints; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + get_geometry = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "getWindowGeometry", "()[I"); + + window_geometry + = (*android_java_env)->CallObjectMethod (android_java_env, + window, + get_geometry); + if (!window_geometry) + { + (*android_java_env)->ExceptionClear (android_java_env); + memory_full (0); + } + + /* window_geometry is an array containing x, y, width and + height. border_width is always 0 on Android. */ + eassert ((*android_java_env)->GetArrayLength (android_java_env, + window_geometry) + == 4); + + *root_return = 0; + *border_width_return = 0; + + ints + = (*android_java_env)->GetIntArrayElements (android_java_env, + window_geometry, + NULL); + + *x_return = ints[0]; + *y_return = ints[1]; + *width_return = ints[2]; + *height_return = ints[3]; + + (*android_java_env)->ReleaseIntArrayElements (android_java_env, + window_geometry, + ints, JNI_ABORT); + + /* Now free the local reference. */ + ANDROID_DELETE_LOCAL_REF (window_geometry); +} + /* Low level drawing primitives. */ @@ -2998,6 +3305,86 @@ android_damage_window (android_drawable handle, +/* Other misc system routines. */ + +int +android_get_screen_width (void) +{ + return (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.get_screen_width, + (jboolean) false); +} + +int +android_get_screen_height (void) +{ + return (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.get_screen_height, + (jboolean) false); +} + +int +android_get_mm_width (void) +{ + return (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.get_screen_width, + (jboolean) true); +} + +int +android_get_mm_height (void) +{ + return (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.get_screen_height, + (jboolean) true); +} + +bool +android_detect_mouse (void) +{ + return (*android_java_env)->CallBooleanMethod (android_java_env, + emacs_service, + service_class.detect_mouse); +} + +void +android_set_dont_focus_on_map (android_window handle, + bool no_focus_on_map) +{ + jmethodID method; + jobject window; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + method = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "setDontFocusOnMap", "(Z)V"); + + (*android_java_env)->CallVoidMethod (android_java_env, window, + method, + (jboolean) no_focus_on_map); +} + +void +android_set_dont_accept_focus (android_window handle, + bool no_accept_focus) +{ + jmethodID method; + jobject window; + + window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + method = android_lookup_method ("org/gnu/emacs/EmacsWindow", + "setDontAcceptFocus", "(Z)V"); + + (*android_java_env)->CallVoidMethod (android_java_env, window, + method, + (jboolean) no_accept_focus); +} + + + #undef faccessat /* Replace the system faccessat with one which understands AT_EACCESS. @@ -3015,6 +3402,128 @@ faccessat (int dirfd, const char *pathname, int mode, int flags) return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); } + + +/* Directory listing emulation. */ + +struct android_dir +{ + /* The real DIR *, if it exists. */ + DIR *dir; + + /* Otherwise, the AAssetDir. */ + void *asset_dir; +}; + +/* Like opendir. However, return an asset directory if NAME points to + an asset. */ + +struct android_dir * +android_opendir (const char *name) +{ + struct android_dir *dir; + AAssetDir *asset_dir; + const char *asset_name; + + asset_name = android_get_asset_name (name); + + /* If the asset manager exists and NAME is an asset, return an asset + directory. */ + if (asset_manager && asset_name) + { + asset_dir = AAssetManager_openDir (asset_manager, + asset_name); + + if (!asset_dir) + { + errno = ENOENT; + return NULL; + } + + dir = xmalloc (sizeof *dir); + dir->dir = NULL; + dir->asset_dir = asset_dir; + return dir; + } + + /* Otherwise, open the directory normally. */ + dir = xmalloc (sizeof *dir); + dir->asset_dir = NULL; + dir->dir = opendir (name); + + if (!dir->dir) + { + xfree (dir); + return NULL; + } + + return dir; +} + +/* Like readdir, except it understands asset directories. */ + +struct dirent * +android_readdir (struct android_dir *dir) +{ + static struct dirent dirent; + const char *filename; + + if (dir->asset_dir) + { + filename = AAssetDir_getNextFileName (dir->asset_dir); + errno = 0; + + if (!filename) + return NULL; + + memset (&dirent, 0, sizeof dirent); + dirent.d_ino = 0; + dirent.d_off = 0; + dirent.d_reclen = sizeof dirent; + dirent.d_type = DT_UNKNOWN; + strncpy (dirent.d_name, filename, + sizeof dirent.d_name - 1); + return &dirent; + } + + return readdir (dir->dir); +} + +/* Like closedir, but it also closes asset manager directories. */ + +void +android_closedir (struct android_dir *dir) +{ + if (dir->dir) + closedir (dir->dir); + else + AAssetDir_close (dir->asset_dir); + + xfree (dir); +} + + + +/* emacs_abort implementation for Android. This logs a stack + trace. */ + +void +emacs_abort (void) +{ + volatile char *foo; + + __android_log_print (ANDROID_LOG_FATAL, __func__, + "emacs_abort called, please review the ensuing" + " stack trace"); + + /* Cause a NULL pointer dereference to make debuggerd generate a + tombstone. */ + foo = NULL; + *foo = '\0'; + + abort (); +} + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 4cf194f23cf..cddd315b9a3 100644 --- a/src/android.h +++ b/src/android.h @@ -27,7 +27,9 @@ along with GNU Emacs. If not, see . */ #ifndef ANDROID_STUBIFY #include #include + #include +#include #include @@ -53,6 +55,7 @@ extern int android_fstat (int, struct stat *); extern int android_fstatat (int, const char *restrict, struct stat *restrict, int); extern int android_close (int); +extern int android_fclose (FILE *); extern const char *android_get_home_directory (void); extern double android_pixel_density_x, android_pixel_density_y; @@ -71,6 +74,26 @@ extern unsigned char *android_lock_bitmap (android_window, jobject *); extern void android_damage_window (android_window, struct android_rectangle *); +extern int android_get_screen_width (void); +extern int android_get_screen_height (void); +extern int android_get_mm_width (void); +extern int android_get_mm_height (void); +extern bool android_detect_mouse (void); + +extern void android_set_dont_focus_on_map (android_window, bool); +extern void android_set_dont_accept_focus (android_window, bool); + + + +/* Directory listing emulation. */ + +struct android_dir; + +extern struct android_dir *android_opendir (const char *); +extern struct dirent *android_readdir (struct android_dir *); +extern void android_closedir (struct android_dir *); + + #endif diff --git a/src/androidfns.c b/src/androidfns.c index 96c2746a21a..459e407b901 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -21,6 +21,7 @@ along with GNU Emacs. If not, see . */ #include #include "lisp.h" +#include "android.h" #include "androidterm.h" #include "blockinput.h" #include "keyboard.h" @@ -151,6 +152,36 @@ android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color) signal_error ("Undefined color", color_name); } +static void +android_set_parent_frame (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + struct frame *p; + + p = NULL; + + if (!NILP (new_value) + && (!FRAMEP (new_value) + || !FRAME_LIVE_P (p = XFRAME (new_value)) + || !FRAME_ANDROID_P (p))) + { + store_frame_param (f, Qparent_frame, old_value); + error ("Invalid specification of `parent-frame'"); + } + + if (p != FRAME_PARENT_FRAME (f)) + { + block_input (); + android_reparent_window (FRAME_ANDROID_WINDOW (f), + (p ? FRAME_ANDROID_WINDOW (p) + : FRAME_DISPLAY_INFO (f)->root_window), + f->left_pos, f->top_pos); + unblock_input (); + + fset_parent_frame (f, new_value); + } +} + void android_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) @@ -531,9 +562,9 @@ android_default_font_parameter (struct frame *f, Lisp_Object parms) if (! FONTP (font) && ! STRINGP (font)) { const char *names[] = { - "Droid Sans Mono", - "monospace", - "DroidSansMono", + "Droid Sans Mono-12", + "Monospace-12", + "DroidSansMono-12", NULL }; int i; @@ -1119,8 +1150,7 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width, error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + return make_fixnum (android_get_screen_width ()); #endif } @@ -1133,8 +1163,7 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height, error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + return make_fixnum (android_get_screen_height ()); #endif } @@ -1185,8 +1214,7 @@ DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + return make_fixnum (android_get_mm_width ()); #endif } @@ -1198,8 +1226,7 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + return make_fixnum (android_get_mm_height ()); #endif } @@ -1225,77 +1252,373 @@ DEFUN ("x-display-visual-class", Fx_display_visual_class, return Qtrue_color; } -DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list, - Sx_display_monitor_attributes_list, +DEFUN ("android-display-monitor-attributes-list", + Fandroid_display_monitor_attributes_list, + Sandroid_display_monitor_attributes_list, 0, 1, 0, - doc: /* SKIP: real doc in xfns.c. */) + doc: /* Return a list of physical monitor attributes on the X display TERMINAL. + +The optional argument TERMINAL specifies which display to ask about. +TERMINAL should be a terminal object, a frame or a display name (a string). +If omitted or nil, that stands for the selected frame's display. + +Internal use only, use `display-monitor-attributes-list' instead. */) (Lisp_Object terminal) { #ifdef ANDROID_STUBIFY error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + struct MonitorInfo monitor; + + memset (&monitor, 0, sizeof monitor); + monitor.geom.width = android_get_screen_width (); + monitor.geom.height = android_get_screen_height (); + monitor.mm_width = android_get_mm_width (); + monitor.mm_height = android_get_mm_height (); + monitor.work = monitor.geom; + monitor.name = (char *) "Android device monitor"; + + /* What to do about monitor_frames? */ + return make_monitor_attribute_list (&monitor, 1, + 0, Qnil, NULL); #endif } -DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry, - 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) - (Lisp_Object terminal) +#ifndef ANDROID_STUBIFY + +static Lisp_Object +frame_geometry (Lisp_Object frame, Lisp_Object attribute) +{ + struct frame *f = decode_live_frame (frame); + android_window rootw; + unsigned int native_width, native_height, x_border_width = 0; + int x_native = 0, y_native = 0, xptr = 0, yptr = 0; + int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0; + int outer_left, outer_top, outer_right, outer_bottom; + int native_left, native_top, native_right, native_bottom; + int inner_left, inner_top, inner_right, inner_bottom; + int internal_border_width; + bool menu_bar_external = false, tool_bar_external = false; + int menu_bar_height = 0, menu_bar_width = 0; + int tab_bar_height = 0, tab_bar_width = 0; + int tool_bar_height = 0, tool_bar_width = 0; + + if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f) + || !FRAME_ANDROID_WINDOW (f)) + return Qnil; + + block_input (); + android_get_geometry (FRAME_ANDROID_WINDOW (f), + &rootw, &x_native, &y_native, + &native_width, &native_height, &x_border_width); + unblock_input (); + + if (FRAME_PARENT_FRAME (f)) + { + Lisp_Object parent, edges; + + XSETFRAME (parent, FRAME_PARENT_FRAME (f)); + edges = Fandroid_frame_edges (parent, Qnative_edges); + if (!NILP (edges)) + { + x_native += XFIXNUM (Fnth (make_fixnum (0), edges)); + y_native += XFIXNUM (Fnth (make_fixnum (1), edges)); + } + + outer_left = x_native; + outer_top = y_native; + outer_right = outer_left + native_width + 2 * x_border_width; + outer_bottom = outer_top + native_height + 2 * x_border_width; + + native_left = x_native + x_border_width; + native_top = y_native + x_border_width; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } + else + { + outer_left = xptr; + outer_top = yptr; + outer_right = outer_left + left_off + native_width + right_off; + outer_bottom = outer_top + top_off + native_height + bottom_off; + + native_left = outer_left + left_off; + native_top = outer_top + top_off; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } + + internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); + inner_left = native_left + internal_border_width; + inner_top = native_top + internal_border_width; + inner_right = native_right - internal_border_width; + inner_bottom = native_bottom - internal_border_width; + + menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); + inner_top += menu_bar_height; + menu_bar_width = menu_bar_height ? native_width : 0; + + tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); + tab_bar_width = (tab_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tab_bar_height; + + tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); + tool_bar_width = (tool_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tool_bar_height; + + /* Construct list. */ + if (EQ (attribute, Qouter_edges)) + return list4i (outer_left, outer_top, outer_right, outer_bottom); + else if (EQ (attribute, Qnative_edges)) + return list4i (native_left, native_top, native_right, native_bottom); + else if (EQ (attribute, Qinner_edges)) + return list4i (inner_left, inner_top, inner_right, inner_bottom); + else + return + list (Fcons (Qouter_position, + Fcons (make_fixnum (outer_left), + make_fixnum (outer_top))), + Fcons (Qouter_size, + Fcons (make_fixnum (outer_right - outer_left), + make_fixnum (outer_bottom - outer_top))), + /* Approximate. */ + Fcons (Qexternal_border_size, + Fcons (make_fixnum (right_off), + make_fixnum (bottom_off))), + Fcons (Qouter_border_width, make_fixnum (x_border_width)), + /* Approximate. */ + Fcons (Qtitle_bar_size, + Fcons (make_fixnum (0), + make_fixnum (top_off - bottom_off))), + Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil), + Fcons (Qmenu_bar_size, + Fcons (make_fixnum (menu_bar_width), + make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), + Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil), + Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), + Fcons (Qtool_bar_size, + Fcons (make_fixnum (tool_bar_width), + make_fixnum (tool_bar_height))), + Fcons (Qinternal_border_width, + make_fixnum (internal_border_width))); +} + +#endif + +DEFUN ("android-frame-geometry", Fandroid_frame_geometry, + Sandroid_frame_geometry, + 0, 1, 0, + doc: /* Return geometric attributes of FRAME. +FRAME must be a live frame and defaults to the selected one. The return +value is an association list of the attributes listed below. All height +and width values are in pixels. + +`outer-position' is a cons of the outer left and top edges of FRAME + relative to the origin - the position (0, 0) - of FRAME's display. + +`outer-size' is a cons of the outer width and height of FRAME. The + outer size includes the title bar and the external borders as well as + any menu and/or tool bar of frame. + +`external-border-size' is a cons of the horizontal and vertical width of + FRAME's external borders as supplied by the window manager. + +`title-bar-size' is a cons of the width and height of the title bar of + FRAME as supplied by the window manager. If both of them are zero, + FRAME has no title bar. If only the width is zero, Emacs was not + able to retrieve the width information. + +`menu-bar-external', if non-nil, means the menu bar is external (never + included in the inner edges of FRAME). + +`menu-bar-size' is a cons of the width and height of the menu bar of + FRAME. + +`tool-bar-external', if non-nil, means the tool bar is external (never + included in the inner edges of FRAME). + +`tool-bar-position' tells on which side the tool bar on FRAME is and can + be one of `left', `top', `right' or `bottom'. If this is nil, FRAME + has no tool bar. + +`tool-bar-size' is a cons of the width and height of the tool bar of + FRAME. + +`internal-border-width' is the width of the internal border of + FRAME. */) + (Lisp_Object frame) { #ifdef ANDROID_STUBIFY error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); + return frame_geometry (frame, Qnil); +#endif +} + +DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 2, 0, + doc: /* Return edge coordinates of FRAME. +FRAME must be a live frame and defaults to the selected one. The return +value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are +in pixels relative to the origin - the position (0, 0) - of FRAME's +display. + +If optional argument TYPE is the symbol `outer-edges', return the outer +edges of FRAME. The outer edges comprise the decorations of the window +manager (like the title bar or external borders) as well as any external +menu or tool bar of FRAME. If optional argument TYPE is the symbol +`native-edges' or nil, return the native edges of FRAME. The native +edges exclude the decorations of the window manager and any external +menu or tool bar of FRAME. If TYPE is the symbol `inner-edges', return +the inner edges of FRAME. These edges exclude title bar, any borders, +menu bar or tool bar of FRAME. */) + (Lisp_Object frame, Lisp_Object type) +{ +#ifndef ANDROID_STUBIFY + return frame_geometry (frame, ((EQ (type, Qouter_edges) + || EQ (type, Qinner_edges)) + ? type + : Qnative_edges)); +#else return Qnil; #endif } -DEFUN ("x-frame-list-z-order", Fx_frame_list_z_order, - Sx_frame_list_z_order, 0, 1, 0, - doc: /* SKIP: real doc in xfns.c. */) +#ifndef ANDROID_STUBIFY + +static Lisp_Object +android_frame_list_z_order (struct android_display_info *dpyinfo, + android_window window) +{ + android_window root, parent, *children; + unsigned int nchildren; + unsigned long i; + Lisp_Object frames; + + frames = Qnil; + + if (android_query_tree (window, &root, &parent, + &children, &nchildren)) + { + for (i = 0; i < nchildren; i++) + { + Lisp_Object frame, tail; + + FOR_EACH_FRAME (tail, frame) + { + struct frame *cf = XFRAME (frame); + + if (FRAME_ANDROID_P (cf) + && (FRAME_ANDROID_WINDOW (cf) == children[i])) + frames = Fcons (frame, frames); + } + } + + if (children) + xfree (children); + } + + return frames; +} + +#endif + +DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order, + Sandroid_frame_list_z_order, 0, 1, 0, + doc: /* Return list of Emacs' frames, in Z (stacking) order. +The optional argument TERMINAL specifies which display to ask about. +TERMINAL should be either a frame or a display name (a string). If +omitted or nil, that stands for the selected frame's display. Return +nil if TERMINAL contains no Emacs frame. + +As a special case, if TERMINAL is non-nil and specifies a live frame, +return the child frames of that frame in Z (stacking) order. + +Frames are listed from topmost (first) to bottommost (last). + +On Android, the order of the frames returned is undefined unless +TERMINAL is a frame. */) (Lisp_Object terminal) { #ifdef ANDROID_STUBIFY error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); - return Qnil; + struct android_display_info *dpyinfo; + android_window window; + + dpyinfo = check_android_display_info (terminal); + + if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal))) + window = FRAME_ANDROID_WINDOW (XFRAME (terminal)); + else + window = dpyinfo->root_window; + + return android_frame_list_z_order (dpyinfo, window); #endif } -DEFUN ("x-frame-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0, - doc: /* SKIP: real doc in xfns.c. */) +DEFUN ("android-frame-restack", Fandroid_frame_restack, + Sandroid_frame_restack, 2, 3, 0, + doc: /* Restack FRAME1 below FRAME2. +This means that if both frames are visible and the display areas of +these frames overlap, FRAME2 (partially) obscures FRAME1. If optional +third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This +means that if both frames are visible and the display areas of these +frames overlap, FRAME1 (partially) obscures FRAME2. + +This may be thought of as an atomic action performed in two steps: The +first step removes FRAME1's window-step window from the display. The +second step reinserts FRAME1's window below (above if ABOVE is true) +that of FRAME2. Hence the position of FRAME2 in its display's Z +\(stacking) order relative to all other frames excluding FRAME1 remains +unaltered. + +The Android system refuses to restack windows, so this does not +work. */) (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3) { #ifdef ANDROID_STUBIFY error ("Android cross-compilation stub called!"); return Qnil; #else - error ("Not implemented"); + /* This is not supported on Android because of limitations in the + platform that prevent ViewGroups from restacking + SurfaceViews. */ return Qnil; #endif } -DEFUN ("x-mouse-absolute-pixel-position", Fx_mouse_absolute_pixel_position, - Sx_mouse_absolute_pixel_position, 0, 0, 0, - doc: /* SKIP: real doc in xfns.c. */) +DEFUN ("android-mouse-absolute-pixel-position", + Fandroid_mouse_absolute_pixel_position, + Sandroid_mouse_absolute_pixel_position, 0, 0, 0, + doc: /* Return absolute position of mouse cursor in pixels. +The position is returned as a cons cell (X . Y) of the coordinates of +the mouse cursor position in pixels relative to a position (0, 0) of the +selected frame's display. This does not work on Android. */) (void) { - /* TODO: figure out how to implement this. */ + /* This cannot be implemented on Android. */ return Qnil; } -DEFUN ("x-set-mouse-absolute-pixel-position", - Fx_set_mouse_absolute_pixel_position, - Sx_set_mouse_absolute_pixel_position, 2, 2, 0, - doc: /* SKIP: real doc in xfns.c. */) +DEFUN ("android-set-mouse-absolute-pixel-position", + Fandroid_set_mouse_absolute_pixel_position, + Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0, + doc: /* Move mouse pointer to a pixel position at (X, Y). The +coordinates X and Y are interpreted to start from the top-left corner +of the screen. This does not work on Android. */) (Lisp_Object x, Lisp_Object y) { - /* TODO: figure out how to implement this. */ + /* This cannot be implemented on Android. */ return Qnil; } @@ -1364,6 +1687,20 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, #endif } +DEFUN ("android-detect-mouse", Fandroid_detect_mouse, + Sandroid_detect_mouse, 0, 0, 0, + doc: /* Figure out whether or not there is a mouse. +Return non-nil if a mouse is connected to this computer, and nil if +there is no mouse. */) + (void) +{ +#ifndef ANDROID_STUBIFY + return android_detect_mouse () ? Qt : Qnil; +#else + return Qnil; +#endif +} + #ifndef ANDROID_STUBIFY @@ -1579,7 +1916,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object value, y = FRAME_TOP_MARGIN_HEIGHT (f); block_input (); - android_clear_area (FRAME_ANDROID_WINDOW (f), + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y, width, height); unblock_input (); } @@ -1590,7 +1927,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object value, height = nlines * FRAME_LINE_HEIGHT (f) - y; block_input (); - android_clear_area (FRAME_ANDROID_WINDOW (f), 0, y, + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y, width, height); unblock_input (); } @@ -1682,6 +2019,30 @@ android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval) } } +static void +android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f), + new_value); + FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value); + } +} + +static void +android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f), + new_value); + FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); + } +} + frame_parm_handler android_frame_parm_handlers[] = { gui_set_autoraise, @@ -1724,16 +2085,16 @@ frame_parm_handler android_frame_parm_handlers[] = NULL, NULL, NULL, - NULL, /* x_set_undecorated, */ - NULL, /* x_set_parent_frame, */ - NULL, /* x_set_skip_taskbar, */ - NULL, /* x_set_no_focus_on_map, */ - NULL, /* x_set_no_accept_focus, */ - NULL, /* x_set_z_group, */ - NULL, /* x_set_override_redirect, */ + NULL, + android_set_parent_frame, + NULL, + android_set_no_focus_on_map, + android_set_no_accept_focus, + NULL, + NULL, gui_set_no_special_glyphs, - NULL, /* x_set_alpha_background, */ - NULL, /* x_set_use_frame_synchronization, */ + NULL, + NULL, }; #endif @@ -1766,14 +2127,16 @@ syms_of_androidfns (void) defsubr (&Sx_display_mm_height); defsubr (&Sx_display_backing_store); defsubr (&Sx_display_visual_class); - defsubr (&Sx_display_monitor_attributes_list); - defsubr (&Sx_frame_geometry); - defsubr (&Sx_frame_list_z_order); - defsubr (&Sx_frame_restack); - defsubr (&Sx_mouse_absolute_pixel_position); - defsubr (&Sx_set_mouse_absolute_pixel_position); + defsubr (&Sandroid_display_monitor_attributes_list); + defsubr (&Sandroid_frame_geometry); + defsubr (&Sandroid_frame_edges); + defsubr (&Sandroid_frame_list_z_order); + defsubr (&Sandroid_frame_restack); + defsubr (&Sandroid_mouse_absolute_pixel_position); + defsubr (&Sandroid_set_mouse_absolute_pixel_position); defsubr (&Sandroid_get_connection); defsubr (&Sx_display_list); defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); + defsubr (&Sandroid_detect_mouse); } diff --git a/src/androidfont.c b/src/androidfont.c index b2c83201b06..b0a9f994a85 100644 --- a/src/androidfont.c +++ b/src/androidfont.c @@ -1,4 +1,4 @@ -/* Communication module for Android terminals. +/* Android fallback font driver. Copyright (C) 2023 Free Software Foundation, Inc. @@ -17,6 +17,10 @@ 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 . */ +/* Due to the terrible nature of the Android Typeface subsystems, this + font driver is only used as a fallback when sfntfont-android.c + fails to enumerate any fonts at all. */ + #include #include "lisp.h" @@ -636,7 +640,7 @@ androidfont_draw (struct glyph_string *s, int from, int to, gcontext = android_resolve_handle (s->gc->gcontext, ANDROID_HANDLE_GCONTEXT); - drawable = android_resolve_handle (FRAME_ANDROID_WINDOW (s->f), + drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f), ANDROID_HANDLE_WINDOW); chars = (*android_java_env)->NewIntArray (android_java_env, to - from); diff --git a/src/androidgui.h b/src/androidgui.h index 7d045f7b450..422e72408c7 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -222,6 +222,12 @@ enum android_event_type ANDROID_MOTION_NOTIFY, ANDROID_BUTTON_PRESS, ANDROID_BUTTON_RELEASE, + ANDROID_TOUCH_DOWN, + ANDROID_TOUCH_UP, + ANDROID_TOUCH_MOVE, + ANDROID_WHEEL, + ANDROID_ICONIFIED, + ANDROID_DEICONIFIED, }; struct android_any_event @@ -312,6 +318,54 @@ struct android_button_event unsigned int button; }; +struct android_touch_event +{ + /* Type of the event. */ + enum android_event_type type; + + /* Window associated with the event. */ + android_window window; + + /* X and Y coordinates of the event. */ + int x, y; + + /* Time of the event, and the pointer identifier. */ + unsigned long time; + + /* Index of the pointer being tracked. */ + unsigned int pointer_id; +}; + +struct android_wheel_event +{ + /* Type of the event. */ + enum android_event_type type; + + /* Window associated with the event. */ + android_window window; + + /* X and Y coordinates of the event. */ + int x, y; + + /* Time of the event, and the pointer identifier. */ + unsigned long time; + + /* Modifier state at the time of the event. */ + int state; + + /* Motion alongside the X and Y axes. */ + double x_delta, y_delta; +}; + +struct android_iconify_event +{ + /* Type of the event. */ + enum android_event_type type; + + /* Window associated with the event. */ + android_window window; +}; + union android_event { enum android_event_type type; @@ -323,8 +377,26 @@ union android_event struct android_crossing_event xcrossing; struct android_motion_event xmotion; struct android_button_event xbutton; + + /* This has no parallel in X, since the X model of having + monotonically increasing touch IDs can't work on Android. */ + struct android_touch_event touch; + + /* This has no parallel in X outside the X Input Extension, and + emulating the input extension interface would be awfully + complicated. */ + struct android_wheel_event wheel; + + /* This has no parallel in X because Android doesn't have window + properties. */ + struct android_iconify_event iconified; }; +enum + { + ANDROID_CURRENT_TIME = 0L, + }; + extern int android_pending (void); extern void android_next_event (union android_event *); @@ -396,6 +468,17 @@ extern void android_clear_area (android_window, int, int, unsigned int, extern android_pixmap android_create_bitmap_from_data (char *, unsigned int, unsigned int); +extern void android_bell (void); +extern void android_set_input_focus (android_window, unsigned long); +extern void android_raise_window (android_window); +extern void android_lower_window (android_window); +extern int android_query_tree (android_window, android_window *, + android_window *, android_window **, + unsigned int *); +extern void android_get_geometry (android_window, android_window *, + int *, int *, unsigned int *, + unsigned int *, unsigned int *); + #endif diff --git a/src/androidterm.c b/src/androidterm.c index 05fe7f01bf9..220858c0fec 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -19,6 +19,7 @@ along with GNU Emacs. If not, see . */ #include #include +#include #include "lisp.h" #include "androidterm.h" @@ -46,6 +47,11 @@ struct android_display_info *x_display_list; static bool any_help_event_p; +/* Counters for tallying up scroll wheel events if + mwheel_coalesce_scroll_events is true. */ + +static double wheel_event_x, wheel_event_y; + enum { ANDROID_EVENT_NORMAL, @@ -83,19 +89,133 @@ android_clear_frame (struct frame *f) /* Clearing the frame will erase any cursor, so mark them all as no longer visible. */ mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f))); - android_clear_window (FRAME_ANDROID_WINDOW (f)); + android_clear_window (FRAME_ANDROID_DRAWABLE (f)); +} + +static void +android_flash (struct frame *f) +{ + struct android_gc *gc; + struct android_gc_values values; + int rc; + fd_set fds; + + block_input (); + + values.function = ANDROID_GC_XOR; + values.foreground = (FRAME_FOREGROUND_PIXEL (f) + ^ FRAME_BACKGROUND_PIXEL (f)); + + gc = android_create_gc ((ANDROID_GC_FUNCTION + | ANDROID_GC_FOREGROUND), + &values); + + /* Get the height not including a menu bar widget. */ + int height = FRAME_PIXEL_HEIGHT (f); + /* Height of each line to flash. */ + int flash_height = FRAME_LINE_HEIGHT (f); + /* These will be the left and right margins of the rectangles. */ + int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f); + int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f); + int width = flash_right - flash_left; + + /* If window is tall, flash top and bottom line. */ + if (height > 3 * FRAME_LINE_HEIGHT (f)) + { + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, + (FRAME_INTERNAL_BORDER_WIDTH (f) + + FRAME_TOP_MARGIN_HEIGHT (f)), + width, flash_height); + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, + (height - flash_height + - FRAME_INTERNAL_BORDER_WIDTH (f)), + width, flash_height); + + } + else + /* If it is short, flash it all. */ + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, FRAME_INTERNAL_BORDER_WIDTH (f), + width, (height - 2 + * FRAME_INTERNAL_BORDER_WIDTH (f))); + + flush_frame (f); + + struct timespec delay = make_timespec (0, 150 * 1000 * 1000); + struct timespec wakeup = timespec_add (current_timespec (), delay); + + /* Keep waiting until past the time wakeup or any input gets + available. */ + while (! detect_input_pending ()) + { + struct timespec current = current_timespec (); + struct timespec timeout; + + /* Break if result would not be positive. */ + if (timespec_cmp (wakeup, current) <= 0) + break; + + /* How long `select' should wait. */ + timeout = make_timespec (0, 10 * 1000 * 1000); + + /* Wait for some input to become available on the X + connection. */ + FD_ZERO (&fds); + + /* Try to wait that long--but we might wake up sooner. */ + rc = pselect (0, &fds, NULL, NULL, &timeout, NULL); + + /* Some input is available, exit the visible bell. */ + if (rc >= 0) + break; + } + + /* If window is tall, flash top and bottom line. */ + if (height > 3 * FRAME_LINE_HEIGHT (f)) + { + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, + (FRAME_INTERNAL_BORDER_WIDTH (f) + + FRAME_TOP_MARGIN_HEIGHT (f)), + width, flash_height); + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, + (height - flash_height + - FRAME_INTERNAL_BORDER_WIDTH (f)), + width, flash_height); + } + else + /* If it is short, flash it all. */ + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, + flash_left, FRAME_INTERNAL_BORDER_WIDTH (f), + width, (height - 2 + * FRAME_INTERNAL_BORDER_WIDTH (f))); + + android_free_gc (gc); + flush_frame (f); + + unblock_input (); } static void android_ring_bell (struct frame *f) { - /* TODO */ + if (visible_bell) + android_flash (f); + else + { + block_input (); + android_bell (); + unblock_input (); + } } static void android_toggle_invisible_pointer (struct frame *f, bool invisible) { - /* TODO */ + } /* Start an update of frame F. This function is installed as a hook @@ -127,10 +247,17 @@ show_back_buffer (struct frame *f) { struct android_swap_info swap_info; + /* Somehow Android frames can be swapped while garbaged. */ + if (FRAME_GARBAGED_P (f)) + return; + memset (&swap_info, 0, sizeof (swap_info)); swap_info.swap_window = FRAME_ANDROID_WINDOW (f); swap_info.swap_action = ANDROID_COPIED; android_swap_buffers (&swap_info, 1); + + /* Now the back buffer no longer needs to be flipped. */ + FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false; } /* Flip back buffers on F if it has undrawn content. */ @@ -142,7 +269,8 @@ android_flush_dirty_back_buffer_on (struct frame *f) || buffer_flipping_blocked_p () /* If the frame is not already up to date, do not flush buffers on input, as that will result in flicker. */ - || !FRAME_ANDROID_COMPLETE_P (f)) + || !FRAME_ANDROID_COMPLETE_P (f) + && FRAME_ANDROID_NEED_BUFFER_FLIP (f)) return; show_back_buffer (f); @@ -174,13 +302,13 @@ static void android_frame_rehighlight (struct android_display_info *); static void android_lower_frame (struct frame *f) { - /* TODO. */ + android_lower_window (FRAME_ANDROID_WINDOW (f)); } static void android_raise_frame (struct frame *f) { - /* TODO. */ + android_raise_window (FRAME_ANDROID_WINDOW (f)); } static void @@ -354,6 +482,46 @@ android_construct_mouse_click (struct input_event *result, return Qnil; } +/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME. + Return the event in IE. Do not set IE->timestamp, as that is left + to the caller. */ + +static void +android_update_tools (struct frame *f, struct input_event *ie) +{ + struct android_touch_point *touchpoint; + + ie->kind = TOUCHSCREEN_UPDATE_EVENT; + XSETFRAME (ie->frame_or_window, f); + ie->arg = Qnil; + + /* Build the list of active touches. */ + for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points; + touchpoint; touchpoint = touchpoint->next) + ie->arg = Fcons (list3i (touchpoint->x, + touchpoint->y, + touchpoint->tool_id), + ie->arg); +} + +/* Find and return an existing tool pressed against FRAME, identified + by POINTER_ID. Return NULL if no tool by that ID was found. */ + +static struct android_touch_point * +android_find_tool (struct frame *f, int pointer_id) +{ + struct android_touch_point *touchpoint; + + for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points; + touchpoint; touchpoint = touchpoint->next) + { + if (touchpoint->tool_id == pointer_id) + return touchpoint; + } + + return NULL; +} + static int handle_one_android_event (struct android_display_info *dpyinfo, union android_event *event, int *finish, @@ -364,6 +532,10 @@ handle_one_android_event (struct android_display_info *dpyinfo, Mouse_HLInfo *hlinfo; union buffered_input_event inev; int modifiers, count, do_help; + struct android_touch_point *touchpoint, **last; + Lisp_Object window; + int scroll_height; + double scroll_unit; /* It is okay for this to not resemble handle_one_xevent so much. Differences in event handling code are much less nasty than @@ -633,6 +805,28 @@ handle_one_android_event (struct android_display_info *dpyinfo, f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window); + if (f && event->xbutton.type == ANDROID_BUTTON_PRESS + && !popup_activated () + /* && !x_window_to_scroll_bar (event->xbutton.display, */ + /* event->xbutton.window, 2) */ + && !FRAME_NO_ACCEPT_FOCUS (f)) + { + /* When clicking into a child frame or when clicking + into a parent frame with the child frame selected and + `no-accept-focus' is not set, select the clicked + frame. */ + struct frame *hf = dpyinfo->highlight_frame; + + if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf))) + { + android_set_input_focus (FRAME_ANDROID_WINDOW (f), + event->xbutton.time); + + if (FRAME_PARENT_FRAME (f)) + android_raise_window (FRAME_ANDROID_WINDOW (f)); + } + } + if (f) { /* Is this in the tab-bar? */ @@ -715,6 +909,223 @@ handle_one_android_event (struct android_display_info *dpyinfo, goto OTHER; + /* Touch events. The events here don't parallel X so much. */ + case ANDROID_TOUCH_DOWN: + + if (!any) + goto OTHER; + + /* This event is sent when a tool is put on the screen. X and Y + are the location of the finger, and pointer_id identifies the + tool for as long as it is still held down. First, see if the + touch point already exists and can be reused (this shouldn't + happen, but be safe.) */ + + touchpoint = android_find_tool (any, event->touch.pointer_id); + + if (touchpoint) + { + /* Simply update the tool position and send an update. */ + touchpoint->x = event->touch.x; + touchpoint->y = event->touch.x; + android_update_tools (any, &inev.ie); + inev.ie.timestamp = event->touch.time; + + goto OTHER; + } + + /* Otherwise, link a new touchpoint onto the output's list of + pressed tools. */ + + touchpoint = xmalloc (sizeof *touchpoint); + touchpoint->tool_id = event->touch.pointer_id; + touchpoint->x = event->touch.x; + touchpoint->y = event->touch.x; + touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points; + FRAME_OUTPUT_DATA (any)->touch_points = touchpoint; + + /* Now generate the Emacs event. */ + inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT; + inev.ie.timestamp = event->touch.time; + XSETFRAME (inev.ie.frame_or_window, any); + XSETINT (inev.ie.x, event->touch.x); + XSETINT (inev.ie.y, event->touch.y); + XSETINT (inev.ie.arg, event->touch.pointer_id); + + goto OTHER; + + case ANDROID_TOUCH_MOVE: + + if (!any) + goto OTHER; + + /* Look for the tool that moved. */ + + touchpoint = android_find_tool (any, event->touch.pointer_id); + + /* If it doesn't exist, skip processing this event. */ + + if (!touchpoint) + goto OTHER; + + /* Otherwise, update the position and send the update event. */ + + touchpoint->x = event->touch.x; + touchpoint->y = event->touch.y; + android_update_tools (any, &inev.ie); + inev.ie.timestamp = event->touch.time; + + goto OTHER; + + case ANDROID_TOUCH_UP: + + if (!any) + goto OTHER; + + /* Now find and unlink the tool in question. */ + + last = &FRAME_OUTPUT_DATA (any)->touch_points; + while ((touchpoint = *last)) + { + if (touchpoint->tool_id == event->touch.pointer_id) + { + *last = touchpoint->next; + + /* The tool was unlinked. Free it and generate the + appropriate Emacs event. */ + xfree (touchpoint); + inev.ie.kind = TOUCHSCREEN_END_EVENT; + inev.ie.timestamp = event->touch.time; + + XSETFRAME (inev.ie.frame_or_window, any); + XSETINT (inev.ie.x, event->touch.x); + XSETINT (inev.ie.y, event->touch.y); + XSETINT (inev.ie.arg, event->touch.pointer_id); + + /* Break out of the loop. */ + goto OTHER; + } + else + last = &touchpoint->next; + } + + /* No touch point was found. This shouldn't happen. */ + goto OTHER; + + /* Wheel motion. The events here don't parallel X because + Android doesn't have scroll valuators. */ + + case ANDROID_WHEEL: + + if (!any) + goto OTHER; + + if (fabs (event->wheel.x_delta) > 0 + || fabs (event->wheel.y_delta) > 0) + { + if (mwheel_coalesce_scroll_events) + { + if (signbit (event->wheel.x_delta) + != signbit (wheel_event_x)) + wheel_event_x = 0.0; + + if (signbit (event->wheel.y_delta) + != signbit (wheel_event_y)) + wheel_event_y = 0.0; + + /* Tally up deltas until one of them exceeds 1.0. */ + wheel_event_x += event->wheel.x_delta; + wheel_event_y += event->wheel.y_delta; + + if (fabs (wheel_event_x) < 1.0 + && fabs (wheel_event_y) < 1.0) + goto OTHER; + } + else + { + /* Use the deltas in the event. */ + wheel_event_x = event->wheel.x_delta; + wheel_event_y = event->wheel.y_delta; + } + + /* Determine what kind of event to send. */ + inev.ie.kind = ((fabs (wheel_event_y) + >= fabs (wheel_event_x)) + ? WHEEL_EVENT : HORIZ_WHEEL_EVENT); + inev.ie.timestamp = event->wheel.time; + + /* Set the event coordinates. */ + XSETINT (inev.ie.x, event->wheel.x); + XSETINT (inev.ie.y, event->wheel.y); + + /* Set the frame. */ + XSETFRAME (inev.ie.frame_or_window, any); + + /* Figure out the scroll direction. */ + inev.ie.modifiers = (signbit ((fabs (wheel_event_x) + >= fabs (wheel_event_y)) + ? wheel_event_x + : wheel_event_y) + ? down_modifier : up_modifier); + + /* Figure out how much to scale the deltas by. */ + window = window_from_coordinates (any, event->wheel.x, + event->wheel.y, NULL, + false, false); + + if (WINDOWP (window)) + scroll_height = XWINDOW (window)->pixel_height; + else + /* EVENT_X and EVENT_Y can be outside the + frame if F holds the input grab, so fall + back to the height of the frame instead. */ + scroll_height = FRAME_PIXEL_HEIGHT (any); + + scroll_unit = pow (scroll_height, 2.0 / 3.0); + + /* Add the keyboard modifiers. */ + inev.ie.modifiers + |= android_android_to_emacs_modifiers (dpyinfo, + event->wheel.state); + + /* Finally include the scroll deltas. */ + inev.ie.arg = list3 (Qnil, + make_float (wheel_event_x + * scroll_unit), + make_float (wheel_event_y + * scroll_unit)); + + wheel_event_x = 0.0; + wheel_event_y = 0.0; + } + + goto OTHER; + + /* Iconification. This is vastly simpler than on X. */ + case ANDROID_ICONIFIED: + + if (FRAME_ICONIFIED_P (any)) + goto OTHER; + + SET_FRAME_VISIBLE (any, false); + SET_FRAME_ICONIFIED (any, true); + + inev.ie.kind = ICONIFY_EVENT; + XSETFRAME (inev.ie.frame_or_window, any); + goto OTHER; + + case ANDROID_DEICONIFIED: + + if (!FRAME_ICONIFIED_P (any)) + goto OTHER; + + SET_FRAME_VISIBLE (any, true); + SET_FRAME_ICONIFIED (any, false); + + inev.ie.kind = DEICONIFY_EVENT; + XSETFRAME (inev.ie.frame_or_window, any); + goto OTHER; + default: goto OTHER; } @@ -781,8 +1192,7 @@ android_read_socket (struct terminal *terminal, now. */ if (dpyinfo->pending_autoraise_frame) { - /* android_raise_frame (dpyinfo->pending_autoraise_frame); - TODO */ + android_raise_frame (dpyinfo->pending_autoraise_frame); dpyinfo->pending_autoraise_frame = NULL; } @@ -796,7 +1206,8 @@ android_frame_up_to_date (struct frame *f) block_input (); FRAME_MOUSE_UPDATE (f); - if (!buffer_flipping_blocked_p ()) + if (!buffer_flipping_blocked_p () + && FRAME_ANDROID_NEED_BUFFER_FLIP (f)) show_back_buffer (f); /* The frame is now complete, as its contents have been drawn. */ @@ -808,7 +1219,10 @@ static void android_buffer_flipping_unblocked_hook (struct frame *f) { block_input (); - show_back_buffer (f); + + if (FRAME_ANDROID_NEED_BUFFER_FLIP (f)) + show_back_buffer (f); + unblock_input (); } @@ -935,7 +1349,25 @@ android_get_focus_frame (struct frame *f) static void android_focus_frame (struct frame *f, bool noactivate) { - /* TODO */ + /* Set the input focus to the frame's window. The system only lets + this work on child frames. */ + android_set_input_focus (FRAME_ANDROID_WINDOW (f), + ANDROID_CURRENT_TIME); +} + +/* The two procedures below only have to update the cursor on Android, + as there are no window borders there. */ + +static void +android_frame_highlight (struct frame *f) +{ + gui_update_cursor (f, true); +} + +static void +android_frame_unhighlight (struct frame *f) +{ + gui_update_cursor (f, true); } static void @@ -963,12 +1395,10 @@ android_frame_rehighlight (struct android_display_info *dpyinfo) if (dpyinfo->highlight_frame != old_highlight) { /* This is not yet required on Android. */ -#if 0 if (old_highlight) android_frame_unhighlight (old_highlight); if (dpyinfo->highlight_frame) android_frame_highlight (dpyinfo->highlight_frame); -#endif } } @@ -1027,7 +1457,8 @@ android_fullscreen_hook (struct frame *f) void android_iconify_frame (struct frame *f) { - /* TODO */ + /* This really doesn't work on Android. */ + error ("Can't notify window manager of iconification"); } static void @@ -1149,7 +1580,7 @@ android_set_offset (struct frame *f, int xoff, int yoff, static void android_set_alpha (struct frame *f) { - /* TODO */ + /* Not supported on Android. */ } static Lisp_Object @@ -1215,6 +1646,7 @@ android_free_frame_resources (struct frame *f) { struct android_display_info *dpyinfo; Mouse_HLInfo *hlinfo; + struct android_touch_point *last, *next; dpyinfo = FRAME_DISPLAY_INFO (f); hlinfo = &dpyinfo->mouse_highlight; @@ -1256,6 +1688,18 @@ android_free_frame_resources (struct frame *f) if (f == dpyinfo->last_mouse_frame) dpyinfo->last_mouse_frame = NULL; + /* Free all tool presses currently active on this frame. */ + next = FRAME_OUTPUT_DATA (f)->touch_points; + while (next) + { + last = next; + next = next->next; + xfree (last); + } + + /* Clear this in case unblock_input reads events. */ + FRAME_OUTPUT_DATA (f)->touch_points = NULL; + unblock_input (); } @@ -1316,8 +1760,8 @@ android_scroll_run (struct window *w, struct run *run) /* Cursor off. Will be switched on again in gui_update_window_end. */ gui_clear_cursor (w); - android_copy_area (FRAME_ANDROID_WINDOW (f), - FRAME_ANDROID_WINDOW (f), + android_copy_area (FRAME_ANDROID_DRAWABLE (f), + FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x, from_y, width, height, x, to_y); @@ -1337,7 +1781,9 @@ static void android_flip_and_flush (struct frame *f) { block_input (); - show_back_buffer (f); + + if (FRAME_ANDROID_NEED_BUFFER_FLIP (f)) + show_back_buffer (f); /* The frame is complete again as its contents were just flushed. */ @@ -1355,7 +1801,7 @@ android_clear_rectangle (struct frame *f, struct android_gc *gc, int x, | ANDROID_GC_FOREGROUND), &xgcv); android_set_foreground (gc, xgcv.background); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, width, height); android_set_foreground (gc, xgcv.foreground); } @@ -1405,7 +1851,7 @@ android_draw_fringe_bitmap (struct window *w, struct glyph_row *row, if (face->stipple) { android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), face->gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc, p->bx, p->by, p->nx, p->ny); android_set_fill_style (face->gc, ANDROID_FILL_SOLID); @@ -1428,7 +1874,7 @@ android_draw_fringe_bitmap (struct window *w, struct glyph_row *row, unsigned long background, cursor_pixel; int depth; - drawable = FRAME_ANDROID_WINDOW (f); + drawable = FRAME_ANDROID_DRAWABLE (f); clipmask = ANDROID_NONE; background = face->background; cursor_pixel = f->output_data.android->cursor_pixel; @@ -1697,7 +2143,7 @@ android_draw_glyph_string_background (struct glyph_string *s, bool force_p) { /* Fill background with a stipple pattern. */ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, s->x, s->y + box_line_width, s->background_width, s->height - 2 * box_line_width); @@ -1734,7 +2180,7 @@ android_fill_triangle (struct frame *f, struct android_gc *gc, abc[1] = point2; abc[2] = point3; - android_fill_polygon (FRAME_ANDROID_WINDOW (f), + android_fill_polygon (FRAME_ANDROID_DRAWABLE (f), gc, abc, 3, ANDROID_CONVEX, ANDROID_COORD_MODE_ORIGIN); } @@ -1776,7 +2222,7 @@ android_clear_point (struct frame *f, struct android_gc *gc, android_get_gc_values (gc, ANDROID_GC_BACKGROUND | ANDROID_GC_FOREGROUND, &xgcv); android_set_foreground (gc, xgcv.background); - android_draw_point (FRAME_ANDROID_WINDOW (f), gc, x, y); + android_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y); android_set_foreground (gc, xgcv.foreground); } @@ -1798,7 +2244,7 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x, black_gc = f->output_data.android->black_relief.gc; normal_gc = f->output_data.android->normal_gc; - drawable = FRAME_ANDROID_WINDOW (f); + drawable = FRAME_ANDROID_DRAWABLE (f); android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1); android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1); @@ -1811,11 +2257,11 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x, /* Draw lines. */ if (top_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y, right_x - left_x + 1, hwidth); if (left_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y, vwidth, bottom_y - top_y + 1); if (raised_p) @@ -1824,12 +2270,12 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x, gc = white_gc; if (bot_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, bottom_y - hwidth + 1, right_x - left_x + 1, hwidth); if (right_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, right_x - vwidth + 1, top_y, vwidth, bottom_y - top_y + 1); @@ -1853,7 +2299,7 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x, if (top_p && left_p && bot_p && right_p && hwidth > 1 && vwidth > 1) - android_draw_rectangle (FRAME_ANDROID_WINDOW (f), + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), black_gc, left_x, top_y, right_x - left_x, bottom_y - top_y); else @@ -1913,22 +2359,22 @@ android_draw_box_rect (struct glyph_string *s, android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1); /* Top. */ - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x, left_x, right_x - left_x + 1, hwidth); /* Left. */ if (left_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x, top_y, vwidth, bottom_y - top_y + 1); /* Bottom. */ - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x, bottom_y - hwidth + 1, right_x - left_x + 1, hwidth); /* Right. */ if (right_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, right_x - vwidth + 1, top_y, vwidth, bottom_y - top_y + 1); @@ -2153,7 +2599,7 @@ android_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y, { /* Fill background with a stipple pattern. */ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y, w, h); android_set_fill_style (s->gc, ANDROID_FILL_SOLID); } @@ -2294,7 +2740,7 @@ android_draw_image_foreground (struct glyph_string *s) if (gui_intersect_rectangles (&clip_rect, &image_rect, &r)) android_copy_area (s->img->pixmap, - FRAME_ANDROID_WINDOW (s->f), + FRAME_ANDROID_DRAWABLE (s->f), s->gc, s->slice.x + r.x - x, s->slice.y + r.y - y, r.width, r.height, r.x, r.y); @@ -2307,7 +2753,7 @@ android_draw_image_foreground (struct glyph_string *s) if (s->hl == DRAW_CURSOR && !s->img->mask) { int relief = eabs (s->img->relief); - android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x - relief, y - relief, s->slice.width + relief*2 - 1, s->slice.height + relief*2 - 1); @@ -2317,7 +2763,7 @@ android_draw_image_foreground (struct glyph_string *s) } else /* Draw a rectangle if image could not be loaded. */ - android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, y, + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y, s->slice.width - 1, s->slice.height - 1); } @@ -2444,7 +2890,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s) { /* Fill background with a stipple pattern. */ android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), gc, x, y, w, h); android_set_fill_style (gc, ANDROID_FILL_SOLID); @@ -2457,7 +2903,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s) | ANDROID_GC_BACKGROUND), &xgcv); android_set_foreground (gc, xgcv.background); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), gc, x, y, w, h); android_set_foreground (gc, xgcv.foreground); } @@ -2536,7 +2982,7 @@ android_draw_underwave (struct glyph_string *s, int decoration_width) while (x1 <= xmax) { - android_draw_line (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x1, y1, x2, y2); x1 = x2, y1 = y2; x2 += dx, y2 = y0 + odd*dy; @@ -2567,7 +3013,7 @@ android_draw_glyph_string_foreground (struct glyph_string *s) for (i = 0; i < s->nchars; ++i) { struct glyph *g = s->first_glyph + i; - android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, s->y, g->pixel_width - 1, s->height - 1); @@ -2618,7 +3064,7 @@ android_draw_composite_glyph_string_foreground (struct glyph_string *s) if (s->font_not_found_p) { if (s->cmp_from == 0) - android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, s->y, s->width - 1, s->height - 1); } @@ -2754,7 +3200,7 @@ android_draw_glyphless_glyph_string_foreground (struct glyph_string *s) false); } if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE) - android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, s->ybase - glyph->ascent, glyph->pixel_width - 1, glyph->ascent + glyph->descent - 1); @@ -2987,14 +3433,14 @@ android_draw_glyph_string (struct glyph_string *s) s->underline_position = position; y = s->ybase + position; if (s->face->underline_defaulted_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, s->x, y, decoration_width, thickness); else { struct android_gc_values xgcv; android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv); android_set_foreground (s->gc, s->face->underline_color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, s->x, y, decoration_width, thickness); android_set_foreground (s->gc, xgcv.foreground); } @@ -3006,7 +3452,7 @@ android_draw_glyph_string (struct glyph_string *s) unsigned long dy = 0, h = 1; if (s->face->overline_color_defaulted_p) - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, s->x, s->y + dy, decoration_width, h); else @@ -3014,8 +3460,8 @@ android_draw_glyph_string (struct glyph_string *s) struct android_gc_values xgcv; android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv); android_set_foreground (s->gc, s->face->overline_color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, s->x, - s->y + dy, decoration_width, h); + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, + s->x, s->y + dy, decoration_width, h); android_set_foreground (s->gc, xgcv.foreground); } } @@ -3044,8 +3490,9 @@ android_draw_glyph_string (struct glyph_string *s) struct android_gc_values xgcv; android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv); android_set_foreground (s->gc, s->face->strike_through_color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, s->x, - glyph_y + dy, decoration_width, h); + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, + s->x, glyph_y + dy, decoration_width, + h); android_set_foreground (s->gc, xgcv.foreground); } } @@ -3125,7 +3572,7 @@ static void android_clear_frame_area (struct frame *f, int x, int y, int width, int height) { - android_clear_area (FRAME_ANDROID_WINDOW (f), + android_clear_area (FRAME_ANDROID_DRAWABLE (f), x, y, width, height); } @@ -3154,25 +3601,25 @@ android_clear_under_internal_border (struct frame *f) struct android_gc *gc = f->output_data.android->normal_gc; android_set_foreground (gc, color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, margin, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin, width, border); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, 0, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0, border, height); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, width - border, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - border, 0, border, height); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, height - border, width, border); android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f)); } else { - android_clear_area (FRAME_ANDROID_WINDOW (f), 0, 0, + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0, border, height); - android_clear_area (FRAME_ANDROID_WINDOW (f), 0, + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, margin, width, border); - android_clear_area (FRAME_ANDROID_WINDOW (f), width - border, + android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border, 0, border, height); - android_clear_area (FRAME_ANDROID_WINDOW (f), 0, + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, height - border, width, border); } } @@ -3221,7 +3668,7 @@ android_draw_hollow_cursor (struct window *w, struct glyph_row *row) } /* Set clipping, draw the rectangle, and reset clipping again. */ android_clip_to_row (w, row, TEXT_AREA, gc); - android_draw_rectangle (FRAME_ANDROID_WINDOW (f), gc, x, y, wd, h - 1); + android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1); android_reset_clip_rectangles (f, gc); } @@ -3295,7 +3742,7 @@ android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, if ((cursor_glyph->resolved_level & 1) != 0) x += cursor_glyph->pixel_width - width; - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y), width, row->height); } @@ -3318,7 +3765,7 @@ android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, if ((cursor_glyph->resolved_level & 1) != 0 && cursor_glyph->pixel_width > w->phys_cursor_width - 1) x += cursor_glyph->pixel_width - w->phys_cursor_width + 1; - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, x, + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, cursor_start_y, w->phys_cursor_width - 1, width); } @@ -3387,7 +3834,7 @@ android_draw_vertical_window_border (struct window *w, int x, int y0, int y1) android_set_foreground (f->output_data.android->normal_gc, face->foreground); - android_draw_line (FRAME_ANDROID_WINDOW (f), + android_draw_line (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x, y0, x, y1); } @@ -3415,17 +3862,17 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) { android_set_foreground (f->output_data.android->normal_gc, color_first); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0, y0, 1, y1 - y0); android_set_foreground (f->output_data.android->normal_gc, color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0 + 1, y0, x1 - x0 - 2, y1 - y0); android_set_foreground (f->output_data.android->normal_gc, color_last); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x1 - 1, y0, 1, y1 - y0); } @@ -3435,16 +3882,16 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) { android_set_foreground (f->output_data.android->normal_gc, color_first); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0, y0, x1 - x0, 1); android_set_foreground (f->output_data.android->normal_gc, color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0, y0 + 1, x1 - x0, y1 - y0 - 2); android_set_foreground (f->output_data.android->normal_gc, color_last); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0, y1 - 1, x1 - x0, 1); } @@ -3453,7 +3900,7 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) /* In any other case do not draw the first and last pixels differently. */ android_set_foreground (f->output_data.android->normal_gc, color); - android_fill_rectangle (FRAME_ANDROID_WINDOW (f), + android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), f->output_data.android->normal_gc, x0, y0, x1 - x0, y1 - y0); } diff --git a/src/androidterm.h b/src/androidterm.h index 814bcf9f08c..ebde15c40a8 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -134,6 +134,18 @@ struct android_display_info Time last_mouse_movement_time; }; +/* Structure representing a single tool (finger or stylus) pressed + onto a frame. */ + +struct android_touch_point +{ + /* The next tool on this list. */ + struct android_touch_point *next; + + /* The tool ID and the last known X and Y positions. */ + int tool_id, x, y; +}; + struct android_output { /* Graphics contexts for the default font. */ @@ -201,6 +213,10 @@ struct android_output input. */ bool_bf complete : 1; + /* True that indicates whether or not a buffer flip is required + because the frame contents have been dirtied. */ + bool_bf need_buffer_flip : 1; + /* Relief GCs, colors etc. */ struct relief { struct android_gc *gc; @@ -214,6 +230,10 @@ struct android_output /* Focus state. Only present for consistency with X; it is actually a boolean. */ int focus_state; + + /* List of all tools (either styluses or fingers) pressed onto the + frame. */ + struct android_touch_point *touch_points; }; enum @@ -240,6 +260,14 @@ enum #define FRAME_ANDROID_NEED_BUFFER_FLIP(f) \ ((f)->output_data.android->need_buffer_flip) +/* Return the drawable used for rendering to frame F and mark the + frame as needing a buffer flip later. There's no easy way to run + code after any drawing command, but code can be run whenever + someone asks for the handle necessary to draw. */ +#define FRAME_ANDROID_DRAWABLE(f) \ + (((f))->output_data.android->need_buffer_flip = true, \ + FRAME_ANDROID_WINDOW ((f))) + /* Return whether or not the frame F has been completely drawn. Used while handling async input. */ #define FRAME_ANDROID_COMPLETE_P(f) \ diff --git a/src/dired.c b/src/dired.c index 084f3225cb2..57d79b84463 100644 --- a/src/dired.c +++ b/src/dired.c @@ -44,6 +44,21 @@ along with GNU Emacs. If not, see . */ #include "msdos.h" /* for fstatat */ #endif +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) +typedef DIR emacs_dir; +#define emacs_readdir readdir +#define emacs_closedir closedir +#else + +#include "android.h" + +/* The Android emulation of dirent stuff is required to be able to + list the /assets special directory. */ +typedef struct android_dir emacs_dir; +#define emacs_readdir android_readdir +#define emacs_closedir android_closedir +#endif + #ifdef WINDOWSNT extern int is_slow_fs (const char *); #endif @@ -78,19 +93,30 @@ dirent_type (struct dirent *dp) #endif } -static DIR * +static emacs_dir * open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp) { char *name = SSDATA (encoded_dirname); - DIR *d; + emacs_dir *d; int fd, opendir_errno; -#ifdef DOS_NT - /* Directories cannot be opened. The emulation assumes that any - file descriptor other than AT_FDCWD corresponds to the most - recently opened directory. This hack is good enough for Emacs. */ +#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + /* On DOS_NT, directories cannot be opened. The emulation assumes + that any file descriptor other than AT_FDCWD corresponds to the + most recently opened directory. This hack is good enough for + Emacs. + + This code is also used on Android for a different reason: a + special `assets' directory outside the normal file system is used + to open assets inside the Android application package, and must + be listed using the opendir-like interface provided in + android.h. */ fd = 0; +#ifndef HAVE_ANDROID d = opendir (name); +#else + d = android_opendir (name); +#endif opendir_errno = errno; #else fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0); @@ -125,7 +151,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg) static void directory_files_internal_unwind (void *d) { - closedir (d); + emacs_closedir (d); } /* Return the next directory entry from DIR; DIR's name is DIRNAME. @@ -133,12 +159,12 @@ directory_files_internal_unwind (void *d) Signal any unrecoverable errors. */ static struct dirent * -read_dirent (DIR *dir, Lisp_Object dirname) +read_dirent (emacs_dir *dir, Lisp_Object dirname) { while (true) { errno = 0; - struct dirent *dp = readdir (dir); + struct dirent *dp = emacs_readdir (dir); if (dp || errno == 0) return dp; if (! (errno == EAGAIN || errno == EINTR)) @@ -190,7 +216,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename); int fd; - DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd); + emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd); /* Unfortunately, we can now invoke expand-file-name and file-attributes on filenames, both of which can throw, so we must @@ -300,7 +326,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list); } - closedir (d); + emacs_closedir (d); #ifdef WINDOWSNT if (attrs) Vw32_get_true_file_attributes = w32_save; @@ -514,7 +540,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, } } int fd; - DIR *d = open_directory (dirname, encoded_dir, &fd); + emacs_dir *d = open_directory (dirname, encoded_dir, &fd); record_unwind_protect_ptr (directory_files_internal_unwind, d); /* Loop reading directory entries. */ diff --git a/src/dispnew.c b/src/dispnew.c index 12814bc0b75..f31e185a84b 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3175,6 +3175,7 @@ redraw_frame (struct frame *f) its redisplay done. */ mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0); set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true); + f->garbaged = false; } @@ -6053,7 +6054,7 @@ FILE = nil means just close any termscript file currently open. */) if (tty->termscript != 0) { block_input (); - fclose (tty->termscript); + emacs_fclose (tty->termscript); tty->termscript = 0; unblock_input (); } diff --git a/src/fileio.c b/src/fileio.c index 9d81f0ca545..6fa524b3bb4 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -277,7 +277,7 @@ void fclose_unwind (void *arg) { FILE *stream = arg; - fclose (stream); + emacs_fclose (stream); } /* Restore point, having saved it as a marker. */ @@ -2989,6 +2989,12 @@ If there is no error, returns nil. */) encoded_filename = ENCODE_FILE (absname); +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* FILE may be some kind of special Android file. */ + if (android_file_access_p (SSDATA (encoded_filename), R_OK)) + return Qnil; +#endif + if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0) report_file_error (SSDATA (string), filename); @@ -3205,7 +3211,11 @@ file_accessible_directory_p (Lisp_Object file) There are three exceptions: "", "/", and "//". Leave "" alone, as it's invalid. Append only "." to the other two exceptions as "/" and "//" are distinct on some platforms, whereas "/", "///", - "////", etc. are all equivalent. */ + "////", etc. are all equivalent. + + Android has a special directory named "/assets". There is no "." + directory there, but appending a "/" is sufficient to check + whether or not it is a directory. */ if (! len) dir = data; else @@ -3215,11 +3225,27 @@ file_accessible_directory_p (Lisp_Object file) special cases "/" and "//", and it's a safe optimization here. After appending '.', append another '/' to work around a macOS bug (Bug#30350). */ - static char const appended[] = "/./"; - char *buf = SAFE_ALLOCA (len + sizeof appended); - memcpy (buf, data, len); - strcpy (buf + len, &appended[data[len - 1] == '/']); - dir = buf; +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + if (!strncmp ("/assets/", data, + sizeof "/assets" - 1)) + { + static char const appended[] = "/"; + char *buf = SAFE_ALLOCA (len + sizeof appended); + memcpy (buf, data, len); + strcpy (buf + len, &appended[data[len - 1] == '/']); + dir = buf; + } + else + { +#endif + static char const appended[] = "/./"; + char *buf = SAFE_ALLOCA (len + sizeof appended); + memcpy (buf, data, len); + strcpy (buf + len, &appended[data[len - 1] == '/']); + dir = buf; +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + } +#endif } ok = file_access_p (dir, F_OK); @@ -5973,7 +5999,7 @@ do_auto_save_unwind (void *arg) if (stream != NULL) { block_input (); - fclose (stream); + emacs_fclose (stream); unblock_input (); } } diff --git a/src/keyboard.c b/src/keyboard.c index daca964b981..78637ef4f15 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4956,8 +4956,8 @@ const char *const lispy_function_keys[] = [66] = "return", [67] = "backspace", [82] = "menu", - [92] = "page-up", - [93] = "page-down", + [92] = "prior", + [93] = "next", }; #elif defined HAVE_NTGUI @@ -11219,7 +11219,7 @@ This may include sensitive information such as passwords. */) if (dribble) { block_input (); - fclose (dribble); + emacs_fclose (dribble); unblock_input (); dribble = 0; } diff --git a/src/lisp.h b/src/lisp.h index bf8ff5cdc18..f64a27ce113 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -28,6 +28,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #ifdef HAVE_SYS_STAT_H #include @@ -5077,6 +5078,7 @@ extern int emacs_open (const char *, int, int); extern int emacs_open_noquit (const char *, int, int); extern int emacs_pipe (int[2]); extern int emacs_close (int); +extern int emacs_fclose (FILE *); extern ptrdiff_t emacs_read (int, void *, ptrdiff_t); extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t); extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t); diff --git a/src/lread.c b/src/lread.c index 2ac3c85ea81..d585dece392 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1141,7 +1141,7 @@ close_infile_unwind (void *arg) { struct infile *prev_infile = arg; eassert (infile && infile != prev_infile); - fclose (infile->stream); + emacs_fclose (infile->stream); infile = prev_infile; } diff --git a/src/sfnt.c b/src/sfnt.c index 20cf2376d85..9b6c421212a 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -3072,8 +3072,8 @@ sfnt_curve_is_flat (struct sfnt_point control0, /* 2.0 is a constant describing the area covered at which point the curve is considered "flat". */ - return (abs (sfnt_mul_fixed (g.x, h.x) - - sfnt_mul_fixed (g.y, h.y)) + return (abs (sfnt_mul_fixed (g.x, h.y) + - sfnt_mul_fixed (g.y, h.x)) <= 0400000); } @@ -3261,9 +3261,11 @@ sfnt_prepare_raster (struct sfnt_raster *raster, struct sfnt_glyph_outline *outline) { raster->width - = sfnt_ceil_fixed (outline->xmax - outline->xmin) >> 16; + = (sfnt_ceil_fixed (outline->xmax) + - sfnt_floor_fixed (outline->xmin)) >> 16; raster->height - = sfnt_ceil_fixed (outline->ymax - outline->ymin) >> 16; + = (sfnt_ceil_fixed (outline->ymax) + - sfnt_floor_fixed (outline->ymin)) >> 16; raster->refcount = 0; /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */ @@ -3292,10 +3294,10 @@ sfnt_step_edge (struct sfnt_edge *edge) } /* Build a list of edges for each contour in OUTLINE, applying - OUTLINE->xmin and OUTLINE->ymin as the offset to each edge. Call - EDGE_PROC with DCONTEXT and the resulting edges as arguments. It - is OK to modify the edges given to EDGE_PROC. Align all edges to - the sub-pixel grid. */ + OUTLINE->xmin and floor (OUTLINE->ymin) as the offset to each edge. + Call EDGE_PROC with DCONTEXT and the resulting edges as arguments. + It is OK to modify the edges given to EDGE_PROC. Align all edges + to the sub-pixel grid. */ static void sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, @@ -3303,13 +3305,18 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, { struct sfnt_edge *edges; size_t i, edge, next_vertex; - sfnt_fixed dx, dy, bot, step_x; + sfnt_fixed dx, dy, bot, step_x, ymin, xmin; int inc_x; size_t top, bottom, y; edges = alloca (outline->outline_used * sizeof *edges); edge = 0; + /* ymin and xmin must be the same as the offset used to set offy and + offx in rasters. */ + ymin = sfnt_floor_fixed (outline->ymin); + xmin = sfnt_floor_fixed (outline->xmin); + for (i = 0; i < outline->outline_used; ++i) { /* Set NEXT_VERTEX to the next point (vertex) in this contour. @@ -3356,12 +3363,12 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, top = next_vertex; } - bot = (outline->outline[bottom].y - outline->ymin); - edges[edge].top = (outline->outline[top].y - outline->ymin); + bot = (outline->outline[bottom].y - ymin); + edges[edge].top = (outline->outline[top].y - ymin); /* Record the edge. Rasterization happens from bottom to up, so record the X at the bottom. */ - edges[edge].x = (outline->outline[bottom].x - outline->xmin); + edges[edge].x = (outline->outline[bottom].x - xmin); dx = (outline->outline[top].x - outline->outline[bottom].x); dy = abs (outline->outline[top].y - outline->outline[bottom].y); @@ -4585,7 +4592,7 @@ main (int argc, char **argv) /* Time this important bit. */ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); outline = sfnt_build_glyph_outline (glyph, head, - 45, + 12, sfnt_test_get_glyph, sfnt_test_free_glyph, &dcontext); @@ -4652,7 +4659,7 @@ main (int argc, char **argv) if (hmtx && head) { - if (!sfnt_lookup_glyph_metrics (code, 36, + if (!sfnt_lookup_glyph_metrics (code, 12, &metrics, hmtx, hhea, head, maxp)) diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 47aa27dc113..01bfdbaaf58 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -42,19 +42,6 @@ static Lisp_Object font_cache; -static unsigned int -sfntfont_android_saturate32 (unsigned int a, unsigned int b) -{ - unsigned int c; - - c = a + b; - - if (c < a) - c = -1; - - return c; -} - /* Scale each of the four packed bytes in P in the low 16 bits of P by SCALE. Return the result. @@ -107,8 +94,9 @@ sfntfont_android_blend (unsigned int src, unsigned int dst) src = src & ~0x00ff00ff; src |= (src_rb >> 16 | src_rb << 16); - /* Saturating is unnecessary but helps find bugs. */ - return sfntfont_android_saturate32 (both, src); + /* This addition need not be saturating because both has already + been multiplied by 255 - a. */ + return both + src; } #define U255TO256(x) ((unsigned short) (x) + ((x) >> 7)) @@ -128,8 +116,9 @@ sfntfont_android_blendrgb (unsigned int src, unsigned int dst) both = ag_part | rb_part; - /* Saturating is unnecessary but helps find bugs. */ - return sfntfont_android_saturate32 (both, src); + /* This addition need not be saturating because both has already + been multiplied by 255 - a. */ + return both + src; } /* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE @@ -162,6 +151,10 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer, src_y = i + (rect->y - text_rectangle->y); + if (src_y > text_rectangle->height) + /* Huh? */ + return; + src_row = (unsigned int *) ((buffer + src_y * stride)); dst_row = (unsigned int *) (dest + ((i + rect->y) * bitmap_info->stride)); @@ -343,7 +336,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, } /* Lock the bitmap. It must be unlocked later. */ - bitmap_data = android_lock_bitmap (FRAME_ANDROID_WINDOW (s->f), + bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f), &bitmap_info, &bitmap); /* If locking the bitmap fails, just discard the data that was @@ -385,7 +378,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, ANDROID_DELETE_LOCAL_REF (bitmap); /* Damage the window by the text rectangle. */ - android_damage_window (FRAME_ANDROID_WINDOW (s->f), + android_damage_window (FRAME_ANDROID_DRAWABLE (s->f), &text_rectangle); /* Release the temporary scanline buffer. */ @@ -495,13 +488,19 @@ init_sfntfont_android (void) version of Android the device is running. */ if (android_get_device_api_level () >= 15) Vsfnt_default_family_alist - = list2 (Fcons (build_string ("Monospace"), + = list3 (Fcons (build_string ("Monospace"), + build_string ("Droid Sans Mono")), + /* Android doesn't come with a Monospace Serif font, so + this will have to do. */ + Fcons (build_string ("Monospace Serif"), build_string ("Droid Sans Mono")), Fcons (build_string ("Sans Serif"), build_string ("Roboto"))); else Vsfnt_default_family_alist - = list2 (Fcons (build_string ("Monospace"), + = list3 (Fcons (build_string ("Monospace"), + build_string ("Droid Sans Mono")), + Fcons (build_string ("Monospace Serif"), build_string ("Droid Sans Mono")), Fcons (build_string ("Sans Serif"), build_string ("Droid Sans"))); diff --git a/src/sfntfont.c b/src/sfntfont.c index 9206fbc6629..25cea59f6a7 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1982,6 +1982,9 @@ sfntfont_text_extents (struct font *font, const unsigned int *code, total_width = 0; + /* First clear the metrics array. */ + memset (metrics, 0, sizeof *metrics); + /* Get the metrcs one by one, then sum them up. */ for (i = 0; i < nglyphs; ++i) { @@ -2059,7 +2062,7 @@ sfntfont_draw (struct glyph_string *s, int from, int to, struct sfnt_glyph_metrics metrics; length = to - from; - font = s->face->font; + font = s->font; info = (struct sfnt_font_info *) font; rasters = alloca (length * sizeof *rasters); diff --git a/src/sysdep.c b/src/sysdep.c index dd97ae1cb38..4d89d4f25ae 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2335,7 +2335,8 @@ emacs_backtrace (int backtrace_limit) } } -#ifndef HAVE_NTGUI +#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID \ + && !defined ANDROID_STUBIFY) void emacs_abort (void) { @@ -2568,6 +2569,20 @@ emacs_close (int fd) } } +/* Wrapper around fclose. On Android, this calls `android_fclose' to + clear information associated with the FILE's file descriptor if + necessary. */ + +int +emacs_fclose (FILE *stream) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return fclose (stream); +#else + return android_fclose (stream); +#endif +} + /* Maximum number of bytes to read or write in a single system call. This works around a serious bug in Linux kernels before 2.6.16; see . diff --git a/src/term.c b/src/term.c index 1a21b6f5764..05163cce533 100644 --- a/src/term.c +++ b/src/term.c @@ -2354,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on it. */) #ifndef MSDOS if (f != t->display_info.tty->output) - fclose (t->display_info.tty->output); - fclose (f); + emacs_fclose (t->display_info.tty->output); + emacs_fclose (f); #endif t->display_info.tty->input = 0; @@ -4632,12 +4632,12 @@ delete_tty (struct terminal *terminal) { delete_keyboard_wait_descriptor (fileno (tty->input)); if (tty->input != stdin) - fclose (tty->input); + emacs_fclose (tty->input); } if (tty->output && tty->output != stdout && tty->output != tty->input) - fclose (tty->output); + emacs_fclose (tty->output); if (tty->termscript) - fclose (tty->termscript); + emacs_fclose (tty->termscript); xfree (tty->old_tty); xfree (tty->Wcm); diff --git a/src/verbose.mk.in b/src/verbose.mk.in index 25dff990881..f826bf18a0a 100644 --- a/src/verbose.mk.in +++ b/src/verbose.mk.in @@ -83,5 +83,5 @@ AM_V_RC = @$(info $ RC $@) # These are used for the Android port. AM_V_JAVAC = @$(info $ JAVAC $@) -AM_V_DX = @$(info $ DX $@) +AM_V_D8 = @$(info $ D8 $@) endif -- cgit v1.3 From d44b60c2f001d57b010f0e9b82f798fbad9a23d6 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 20 Jan 2023 19:06:32 +0800 Subject: Update Android port * .gitignore: Don't ignore verbose.mk.android. * doc/emacs/Makefile.in (EMACSSOURCES): Add android.texi and input.texi. * doc/emacs/android.texi (Android): Document support for the on-screen keyboard. (Android Startup): Document how to start Emacs with -Q on Android. (Android Environment): Document how Emacs works around the system ``task killer''. Document changes to frame deletion behavior. * doc/emacs/emacs.texi (Top): * doc/emacs/input.texi (Other Input Devices, On-Screen Keyboards): Document how to use Emacs with virtual keyboards. * doc/lispref/commands.texi (Touchscreen Events): Document changes to `touch-screen-track-drag'. * doc/lispref/frames.texi (Frames, On-Screen Keyboards): New node. * java/AndroidManifest.xml.in: Add settings activity and appropriate OSK adjustment mode. * java/org/gnu/emacs/EmacsActivity.java (onCreate): Allow creating Emacs with -Q. (onDestroy): Don't remove if killed by the system. * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems): Fix context menus again. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Make all event sending functions return long. * java/org/gnu/emacs/EmacsPreferencesActivity.java (EmacsPreferencesActivity): New class. * java/org/gnu/emacs/EmacsService.java (EmacsService) (onStartCommand, onCreate, startEmacsService): Start as a foreground service if necessary to bypass system restrictions. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout) (onDetachedFromWindow): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, viewLayout): Implement frame resize synchronization.. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager, removeWindowConsumer): Adjust accordingly for changes to frame deletion behavior. * lisp/frame.el (android-toggle-on-screen-keyboard) (frame-toggle-on-screen-keyboard): New function. * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard) (minibuffer-exit-on-screen-keyboard): New functions. (minibuffer-setup-hook, minibuffer-exit-hook): Add new functions to hooks. * lisp/touch-screen.el (touch-screen-relative-xy): Accept new value of window `frame'. Return frame coordinates in that case. (touch-screen-set-point-commands): New variable. (touch-screen-handle-point-up): Respect that variable. (touch-screen-track-drag): Return `no-drag' where appropriate. (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line): Refactor to use `no-drag'. * src/android.c (struct android_emacs_window): New methods. Make all event sending functions return the event serial. (android_toggle_on_screen_keyboard, android_window_updated): New functions. * src/android.h: Update prototypes. * src/androidfns.c (Fandroid_toggle_on_screen_keyboard) (syms_of_androidfns): New function. * src/androidgui.h (struct android_any_event) (struct android_key_event, struct android_configure_event) (struct android_focus_event, struct android_window_action_event) (struct android_crossing_event, struct android_motion_event) (struct android_button_event, struct android_touch_event) (struct android_wheel_event, struct android_iconify_event) (struct android_menu_event): Add `serial' fields. * src/androidterm.c (handle_one_android_event) (android_frame_up_to_date): * src/androidterm.h (struct android_output): Implement frame resize synchronization. --- .gitignore | 1 + doc/emacs/Makefile.in | 2 + doc/emacs/android.texi | 70 +++++-- doc/emacs/emacs.texi | 1 + doc/emacs/input.texi | 36 ++++ doc/lispref/commands.texi | 8 +- doc/lispref/frames.texi | 21 +++ java/AndroidManifest.xml.in | 12 ++ java/org/gnu/emacs/EmacsActivity.java | 20 +- java/org/gnu/emacs/EmacsContextMenu.java | 1 + java/org/gnu/emacs/EmacsNative.java | 39 ++-- java/org/gnu/emacs/EmacsPreferencesActivity.java | 98 ++++++++++ java/org/gnu/emacs/EmacsService.java | 62 ++++++- java/org/gnu/emacs/EmacsSurfaceView.java | 127 +++++++++---- java/org/gnu/emacs/EmacsThread.java | 12 +- java/org/gnu/emacs/EmacsView.java | 55 +++++- java/org/gnu/emacs/EmacsWindow.java | 58 +++++- .../gnu/emacs/EmacsWindowAttachmentManager.java | 4 +- lisp/frame.el | 17 ++ lisp/minibuffer.el | 23 +++ lisp/touch-screen.el | 203 +++++++++++++-------- src/android.c | 147 ++++++++++++--- src/android.h | 2 + src/androidfns.c | 25 +++ src/androidgui.h | 20 ++ src/androidterm.c | 24 +++ src/androidterm.h | 5 + 27 files changed, 894 insertions(+), 199 deletions(-) create mode 100644 java/org/gnu/emacs/EmacsPreferencesActivity.java (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/.gitignore b/.gitignore index 3bc40c67489..6494e4e8f39 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ src/emacs-module.h # Built by recursive call to `configure'. *.android !INSTALL.android +!verbose.mk.android # Built by `java'. java/install_temp/* diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in index 161bdcb1c59..c7415312753 100644 --- a/doc/emacs/Makefile.in +++ b/doc/emacs/Makefile.in @@ -146,6 +146,8 @@ EMACSSOURCES= \ ${srcdir}/glossary.texi \ ${srcdir}/ack.texi \ ${srcdir}/kmacro.texi \ + ${srcdir}/android.texi \ + ${srcdir}/input.texi \ $(EMACS_XTRA) ## Disable implicit rules. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 01a2e68a97e..ba5d2ca3e09 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -10,8 +10,8 @@ Alliance. This section describes the peculiarities of using Emacs on an Android device running Android 2.2 or later. Android devices commonly rely on user input through a touch screen -or digitizer device. For more information about using them with -Emacs, @pxref{other Input Devices}. +or digitizer device and on-screen keyboard. For more information +about using such devices with Emacs, @pxref{Other Input Devices}. @menu * What is Android?:: Preamble. @@ -82,6 +82,16 @@ command on that other system: $ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)" @end example +@cindex emacs -Q, android +Since Android has no command line, there is normally no way to specify +command-line arguments. However, Emacs can be started with the +equivalent of the @code{--quick} option (@pxref{Initial Options}) +through a special preferences screen, which can be accessed through +the Emacs ``app info'' page in the system settings application. + +Consult the manufacturer of your device for more details, as how to do +this varies by device. + @node Android File System @section What files Emacs can access under Android @cindex /assets directory, android @@ -149,7 +159,7 @@ which is the app data directory (@pxref{Android File System}.) directories, and the app data directories of other applications. In recent versions of Android, the system also prohibits, for security reasons, even Emacs itself from running executables inside the app -data directory! +data directory. Emacs comes with several binaries. While being executable files, they are packaged as libraries in the library directory, because @@ -173,9 +183,28 @@ within itself. Application processes are treated as disposable entities by the system. When all Emacs frames move to the background, Emacs is liable to be killed by the system at any time, for the purpose of saving -resources. There is currently no easy way to bypass these -restrictions, aside from keeping Emacs constantly running in the -foreground. +system resources. + + On Android 7.1 and earlier, Emacs tells the system to treat it as a +``background service''. The system will try to avoid killing Emacs +unless the device is under memory stress. + + Android 8.0 removed the ability for background services to receive +such special treatment. However, Emacs applies a workaround: the +system considers applications that create a permanent notification to +be performing active work, and will avoid killing such applications. +Thus, on those systems, Emacs displays a permanant notification for as +long as it is running. Once the notification is displayed, it can be +safely hidden through the system settings without resulting in Emacs +being killed. + + However, it is not guaranteed that the system will not kill Emacs, +even if the notification is being displayed. While the Open Handset +Alliance's sample implementation of Android behaves correctly, many +manufacturers place additional restrictions on program execution in +the background in their proprietary versions of Android. There is a +list of such troublesome manufacturers and sometimes workarounds, at +@url{https://dontkillmyapp.com/}. @section Android permissions @cindex external storage, android @@ -272,14 +301,14 @@ maximized or full-screen, and only one window can be displayed at a time. On larger devices, the system allows up to four windows to be tiled on the screen at any time. -Windows on Android do not continue to exist indefinitely after they + Windows on Android do not continue to exist indefinitely after they are created. Instead, the system may choose to terminate windows that are not on screen in order to save memory, with the assumption that the program will save its contents to disk and restore them later, when the user asks to open it again. As this is obvious not possible with Emacs, Emacs separates a frame from a system window. -Each system window created (including the initial window created + Each system window created (including the initial window created during Emacs startup) is appended to a list of windows that do not have associated frames. When a frame is created, Emacs looks up any window within that list, and displays the contents of the frame @@ -287,11 +316,28 @@ within; if there is no window at all, then one is created. Likewise, when a new window is created by the system, Emacs places the contents of any frame that is not already displayed within a window inside. When a frame is closed, the corresponding system window is also -closed. +closed. Upon startup, the system creates a window itself (within +which Emacs displays the first window system frame shortly +thereafter.) Emacs differentiates between that window and windows +created on behalf of other frames to determine what to do when the +system window associated with a frame is closed: + +@itemize @bullet +@item +When the system closes the window created during application startup +in order to save memory, Emacs retains the frame for when that window +is created later. -This strategy works as long as one window is in the foreground. -Otherwise, Emacs can only run in the background for a limited amount -of time before the process is killed completely. +@item +When the user closes the window created during application startup, +and the window was not previously closed by the system in order to +save resources, Emacs deletes any frame displayed within that window. + +@item +When the user or the system closes any window created by Emacs on +behalf of a specific frame, Emacs deletes the frame displayed within +that window. +@end itemize @cindex windowing limitations, android @cindex frame parameters, android diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index 9fd169cd5cc..0cb454e5294 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1269,6 +1269,7 @@ Emacs and Android Emacs and unconventional input devices * Touchscreens:: Using Emacs on touchscreens. +* On-Screen Keyboards:: Using Emacs with virtual keyboards. Emacs and Microsoft Windows/MS-DOS diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index 0e029c401e3..1a58d1ca0ac 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -16,6 +16,7 @@ input devices, which is detailed here. @menu * Touchscreens:: Using Emacs on touchscreens. +* On-Screen Keyboards:: Using Emacs with virtual keyboards. @end menu @node Touchscreens @@ -58,3 +59,38 @@ were to be held down. @xref{Mouse Commands}. By default, Emacs considers a tool as having been left on the display for a while after 0.7 seconds, but this can be changed by customizing the variable @code{touch-screen-delay}. + +@node On-Screen Keyboards +@section Using Emacs with virtual keyboards +@cindex virtual keyboards +@cindex on-screen keyboards + + When there is no physical keyboard attached to a system, the +windowing system typically provides an on-screen keyboard, more often +known as a ``virtual keyboard'', containing rows of clickable buttons +that send keyboard input to the application, much like a real keyboard +would. This virtual keyboard is hidden by default, as it uses up +valuable on-screen real estate, and must be opened once the program +being used is ready to accept keyboard input. + + Under the X Window System, the client that provides the on-screen +keyboard typically detects when the application is ready to accept +keyboard input through a set of complex heuristics, and automatically +displays the keyboard when necessary. + + On other systems such as Android, Emacs must tell the system when it +is ready to accept keyboard input. Typically, this is done in +response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or +once to the minibuffer becomes in use (@pxref{Minibuffer}.) + +@vindex touch-screen-set-point-commands + When a ``tap'' gesture results in a command being executed, Emacs +checks to see whether or not the command is supposed to set the point +by looking for it in the list @code{touch-screen-set-point-commands}. +If it is, then Emacs looks up whether or not the text under the point +is read-only; if not, it activates the on-screen keyboard, assuming +that the user is about to enter text in to the current buffer. + + Emacs also provides a set of functions to show or hide the on-screen +keyboard. For more details, @pxref{On-Screen Keyboards,,, elisp, The +Emacs Lisp Reference Manual}. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 59367a2cecc..484c7dc2a06 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2055,9 +2055,11 @@ The caller should not perform any action in that case. @defun touch-screen-track-drag event update &optional data This function is used to track a single ``drag'' gesture originating -from the @code{touchscreen-begin} event @code{event}. Currently, it -behaves identically to @code{touch-screen-track-tap}, but differences -are anticipated in the future. +from the @code{touchscreen-begin} event @code{event}. + +It behaves like @code{touch-screen-track-tap}, except that it returns +@code{no-drag} if the touchpoint in @code{event} did not move far +enough to qualify as an actual drag. @end defun @node Focus Events diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 2ceafab7a6b..497715bdb19 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -104,6 +104,7 @@ window of another Emacs frame. @xref{Child Frames}. * Mouse Tracking:: Getting events that say when the mouse moves. * Mouse Position:: Asking where the mouse is, or moving it. * Pop-Up Menus:: Displaying a menu for the user to select from. +* On-Screen Keyboards:: Displaying the virtual keyboard. * Dialog Boxes:: Displaying a box to ask yes or no. * Pointer Shape:: Specifying the shape of the mouse pointer. * Window System Selections:: Transferring text to and from other X clients. @@ -3812,6 +3813,26 @@ keymap. It won't be called if @code{x-popup-menu} returns for some other reason without displaying a pop-up menu. @end defvar +@node On-Screen Keyboards +@section On-Screen Keyboards + + An on-screen keyboard is a special kind of pop up provided by the +system, with rows of clickable buttons that act as a real keyboard. + + On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs +Manual}), Emacs is supposed to display and hide the on screen keyboard +depending on whether or not the user is about to type something. + +@defun frame-toggle-on-screen-keyboard frame hide +This function displays or hides the on-screen keyboard on behalf of +the frame @var{frame}. If @var{hide} is non-@code{nil}, then the +on-screen keyboard is hidden; otherwise, it is displayed. + +This has no effect if the system automatically detects when to display +the on-screen keyboard, or when it does not provide any on-screen +keyboard. +@end defun + @node Dialog Boxes @section Dialog Boxes @cindex dialog boxes diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index b680137a9d0..74f69d2a8e5 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -62,8 +62,10 @@ along with GNU Emacs. If not, see . --> android:theme="@android:style/Theme" android:debuggable="true" android:extractNativeLibs="true"> + @@ -73,8 +75,18 @@ along with GNU Emacs. If not, see . --> + + + + + + + . */ + +package org.gnu.emacs; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.LinearLayout; +import android.widget.TextView; + +import android.R; + +/* This module provides a ``preferences'' display for Emacs. It is + supposed to be launched from inside the Settings application to + perform various actions, such as starting Emacs with the ``-Q'' + option, which would not be possible otherwise, as there is no + command line on Android. */ + +public class EmacsPreferencesActivity extends Activity +{ + /* The linear layout associated with the activity. */ + private LinearLayout layout; + + /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and + tell the system to EmacsActivity with some parameters later. */ + + private void + startEmacsQ () + { + Intent intent; + + intent = new Intent (this, EmacsActivity.class); + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra ("org.gnu.emacs.START_DASH_Q", true); + startActivity (intent); + System.exit (0); + } + + @Override + public void + onCreate (Bundle savedInstanceState) + { + LinearLayout layout; + TextView textView; + LinearLayout.LayoutParams params; + int resid; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + setTheme (R.style.Theme_DeviceDefault_Settings); + else if (Build.VERSION.SDK_INT + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + setTheme (R.style.Theme_DeviceDefault); + + layout = new LinearLayout (this); + layout.setOrientation (LinearLayout.VERTICAL); + setContentView (layout); + + textView = new TextView (this); + textView.setPadding (8, 20, 20, 8); + + params = new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + textView.setLayoutParams (params); + textView.setText ("(Re)start Emacs with -Q"); + textView.setOnClickListener (new View.OnClickListener () { + @Override + public void + onClick (View view) + { + startEmacsQ (); + } + }); + layout.addView (textView); + + super.onCreate (savedInstanceState); + } +}; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index bcf8d9ff6e8..95f21b211a3 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -32,7 +32,13 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.annotation.TargetApi; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.NotificationChannel; +import android.app.PendingIntent; import android.app.Service; + import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; @@ -63,6 +69,7 @@ public class EmacsService extends Service public static final String TAG = "EmacsService"; public static final int MAX_PENDING_REQUESTS = 256; public static volatile EmacsService SERVICE; + public static boolean needDashQ; private EmacsThread thread; private Handler handler; @@ -74,6 +81,31 @@ public class EmacsService extends Service public int onStartCommand (Intent intent, int flags, int startId) { + Notification notification; + NotificationManager manager; + NotificationChannel channel; + String infoBlurb; + Object tem; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + tem = getSystemService (Context.NOTIFICATION_SERVICE); + manager = (NotificationManager) tem; + infoBlurb = ("See (emacs)Android Environment for more" + + " details about this notification."); + channel + = new NotificationChannel ("emacs", "Emacs persistent notification", + NotificationManager.IMPORTANCE_DEFAULT); + manager.createNotificationChannel (channel); + notification = (new Notification.Builder (this, "emacs") + .setContentTitle ("Emacs") + .setContentText (infoBlurb) + .setSmallIcon (android.R.drawable.sym_def_app_icon) + .build ()); + manager.notify (1, notification); + startForeground (1, notification); + } + return START_NOT_STICKY; } @@ -137,7 +169,7 @@ public class EmacsService extends Service this); /* Start the thread that runs Emacs. */ - thread = new EmacsThread (this); + thread = new EmacsThread (this, needDashQ); thread.start (); } catch (IOException exception) @@ -444,4 +476,32 @@ public class EmacsService extends Service } } } + + + + /* Start the Emacs service if necessary. On Android 26 and up, + start Emacs as a foreground service with a notification, to avoid + it being killed by the system. + + On older systems, simply start it as a normal background + service. */ + + public static void + startEmacsService (Context context) + { + PendingIntent intent; + + if (EmacsService.SERVICE == null) + { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) + /* Start the Emacs service now. */ + context.startService (new Intent (context, + EmacsService.class)); + else + /* Display the permanant notification and start Emacs as a + foreground service. */ + context.startForegroundService (new Intent (context, + EmacsService.class)); + } + } }; diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index f713818d4bc..2fe9e103b2b 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -32,53 +32,106 @@ import android.util.Log; public class EmacsSurfaceView extends SurfaceView { private static final String TAG = "EmacsSurfaceView"; - public Object surfaceChangeLock; private boolean created; + private EmacsView view; - public - EmacsSurfaceView (final EmacsView view) - { - super (view.getContext ()); - - surfaceChangeLock = new Object (); + /* This is the callback used on Android 8 to 25. */ - getHolder ().addCallback (new SurfaceHolder.Callback () { - @Override - public void - surfaceChanged (SurfaceHolder holder, int format, - int width, int height) + private class Callback implements SurfaceHolder.Callback + { + @Override + public void + surfaceChanged (SurfaceHolder holder, int format, + int width, int height) + { + Log.d (TAG, "surfaceChanged: " + view + ", " + view.pendingConfigure); + + /* Make sure not to swap buffers if there is pending + configuration, because otherwise the redraw callback will not + run correctly. */ + + if (view.pendingConfigure == 0) + view.swapBuffers (); + } + + @Override + public void + surfaceCreated (SurfaceHolder holder) + { + synchronized (surfaceChangeLock) { - Log.d (TAG, "surfaceChanged: " + view); - view.swapBuffers (); + Log.d (TAG, "surfaceCreated: " + view); + created = true; } - @Override - public void - surfaceCreated (SurfaceHolder holder) - { - synchronized (surfaceChangeLock) - { - Log.d (TAG, "surfaceCreated: " + view); - created = true; - } - - /* Drop the lock when doing this, or a deadlock can - result. */ - view.swapBuffers (); - } + /* Drop the lock when doing this, or a deadlock can + result. */ + view.swapBuffers (); + } - @Override - public void - surfaceDestroyed (SurfaceHolder holder) + @Override + public void + surfaceDestroyed (SurfaceHolder holder) + { + synchronized (surfaceChangeLock) { - synchronized (surfaceChangeLock) - { - Log.d (TAG, "surfaceDestroyed: " + view); - created = false; - } + Log.d (TAG, "surfaceDestroyed: " + view); + created = false; } - }); + } + } + + /* And this is the callback used on Android 26 and later. It is + used because it can tell the system when drawing completes. */ + + private class Callback2 extends Callback implements SurfaceHolder.Callback2 + { + @Override + public void + surfaceRedrawNeeded (SurfaceHolder holder) + { + /* This version is not supported. */ + return; + } + + @Override + public void + surfaceRedrawNeededAsync (SurfaceHolder holder, + Runnable drawingFinished) + { + Runnable old; + + Log.d (TAG, "surfaceRedrawNeededAsync: " + view.pendingConfigure); + + /* The system calls this function when it wants to know whether + or not Emacs is still configuring itself in response to a + resize. + + If the view did not send an outstanding ConfigureNotify + event, then call drawingFinish immediately. Else, give it to + the view to execute after drawing completes. */ + + if (view.pendingConfigure == 0) + drawingFinished.run (); + else + /* And set this runnable to run once drawing completes. */ + view.drawingFinished = drawingFinished; + } + } + + public + EmacsSurfaceView (final EmacsView view) + { + super (view.getContext ()); + + this.surfaceChangeLock = new Object (); + this.view = view; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) + getHolder ().addCallback (new Callback ()); + else + getHolder ().addCallback (new Callback2 ()); } public boolean diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index 0882753747f..f9bc132f354 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -23,12 +23,13 @@ import java.lang.Thread; public class EmacsThread extends Thread { - EmacsService context; + /* Whether or not Emacs should be started -Q. */ + private boolean startDashQ; public - EmacsThread (EmacsService service) + EmacsThread (EmacsService service, boolean startDashQ) { - context = service; + this.startDashQ = startDashQ; } public void @@ -36,7 +37,10 @@ public class EmacsThread extends Thread { String args[]; - args = new String[] { "libandroid-emacs.so", }; + if (!startDashQ) + args = new String[] { "libandroid-emacs.so", }; + else + args = new String[] { "libandroid-emacs.so", "-Q", }; /* Run the native code now. */ EmacsNative.initEmacs (args); diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 82f44acaebe..74bbb7b3ecc 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -19,6 +19,7 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; +import android.content.Context; import android.content.res.ColorStateList; import android.view.ContextMenu; @@ -27,6 +28,8 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; + import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; @@ -86,11 +89,23 @@ public class EmacsView extends ViewGroup /* The serial of the last clip rectangle change. */ private long lastClipSerial; + /* The InputMethodManager for this view's context. */ + private InputMethodManager imManager; + + /* Runnable that will run once drawing completes. */ + public Runnable drawingFinished; + + /* Serial of the last ConfigureNotify event sent that Emacs has not + yet responded to. 0 if there is no such outstanding event. */ + public long pendingConfigure; + public EmacsView (EmacsWindow window) { super (EmacsService.SERVICE); + Object tem; + this.window = window; this.damageRegion = new Region (); this.paint = new Paint (); @@ -111,6 +126,10 @@ public class EmacsView extends ViewGroup /* Get rid of the default focus highlight. */ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) setDefaultFocusHighlightEnabled (false); + + /* Obtain the input method manager. */ + tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE); + imManager = (InputMethodManager) tem; } private void @@ -259,7 +278,8 @@ public class EmacsView extends ViewGroup if (changed || mustReportLayout) { mustReportLayout = false; - window.viewLayout (left, top, right, bottom); + pendingConfigure + = window.viewLayout (left, top, right, bottom); } measuredWidth = right - left; @@ -538,4 +558,37 @@ public class EmacsView extends ViewGroup Runtime.getRuntime ().gc (); } } + + public void + showOnScreenKeyboard () + { + /* Specifying no flags at all tells the system the user asked for + the input method to be displayed. */ + imManager.showSoftInput (this, 0); + } + + public void + hideOnScreenKeyboard () + { + imManager.hideSoftInputFromWindow (this.getWindowToken (), + 0); + } + + public void + windowUpdated (long serial) + { + Log.d (TAG, "windowUpdated: serial is " + serial); + + if (pendingConfigure <= serial + /* Detect wraparound. */ + || pendingConfigure - serial >= 0x7fffffff) + { + pendingConfigure = 0; + + if (drawingFinished != null) + drawingFinished.run (); + + drawingFinished = null; + } + } }; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index c5b1522086c..8511af9193e 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -233,7 +233,7 @@ public class EmacsWindow extends EmacsHandleObject return attached; } - public void + public long viewLayout (int left, int top, int right, int bottom) { int rectWidth, rectHeight; @@ -249,10 +249,10 @@ public class EmacsWindow extends EmacsHandleObject rectWidth = right - left; rectHeight = bottom - top; - EmacsNative.sendConfigureNotify (this.handle, - System.currentTimeMillis (), - left, top, rectWidth, - rectHeight); + return EmacsNative.sendConfigureNotify (this.handle, + System.currentTimeMillis (), + left, top, rectWidth, + rectHeight); } public void @@ -589,11 +589,20 @@ public class EmacsWindow extends EmacsHandleObject EmacsActivity.invalidateFocus (); } + /* Notice that the activity has been detached or destroyed. + + ISFINISHING is set if the activity is not the main activity, or + if the activity was not destroyed in response to explicit user + action. */ + public void - onActivityDetached () + onActivityDetached (boolean isFinishing) { - /* Destroy the associated frame when the activity is detached. */ - EmacsNative.sendWindowAction (this.handle, 0); + /* Destroy the associated frame when the activity is detached in + response to explicit user action. */ + + if (isFinishing) + EmacsNative.sendWindowAction (this.handle, 0); } /* Look through the button state to determine what button EVENT was @@ -1064,4 +1073,37 @@ public class EmacsWindow extends EmacsHandleObject /* Return the resulting coordinates. */ return array; } + + public void + toggleOnScreenKeyboard (final boolean on) + { + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + if (on) + view.showOnScreenKeyboard (); + else + view.hideOnScreenKeyboard (); + } + }); + } + + /* Notice that outstanding configure events have been processed. + SERIAL is checked in the UI thread to verify that no new + configure events have been generated in the mean time. */ + + public void + windowUpdated (final long serial) + { + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + view.windowUpdated (serial); + } + }); + } }; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 15eb3bb65c2..510300571b8 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -134,7 +134,7 @@ public class EmacsWindowAttachmentManager } public void - removeWindowConsumer (WindowConsumer consumer) + removeWindowConsumer (WindowConsumer consumer, boolean isFinishing) { EmacsWindow window; @@ -147,7 +147,7 @@ public class EmacsWindowAttachmentManager Log.d (TAG, "removeWindowConsumer: detaching " + window); consumer.detachWindow (); - window.onActivityDetached (); + window.onActivityDetached (isFinishing); } Log.d (TAG, "removeWindowConsumer: removing " + consumer); diff --git a/lisp/frame.el b/lisp/frame.el index f21c0c369bd..f8ef17325ec 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -2553,6 +2553,23 @@ symbols." ((string= name "Virtual core keyboard") 'core-keyboard)))))) + +;;;; On-screen keyboard management. + +(declare-function android-toggle-on-screen-keyboard "androidfns.c") + +(defun frame-toggle-on-screen-keyboard (frame hide) + "Display or hide the on-screen keyboard. +On systems with an on-screen keyboard, display the on screen +keyboard on behalf of the frame FRAME if HIDE is nil. Else, hide +the on screen keyboard. + +FRAME must already have the input focus for this to work + reliably." + (let ((frame-type (framep-on-display frame))) + (cond ((eq frame-type 'android) + (android-toggle-on-screen-keyboard frame hide))))) + ;;;; Frame geometry values diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 21d4607e7cf..3c42f29cd1f 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -4576,6 +4576,29 @@ is included in the return value." default))) ": ")) + +;;; On screen keyboard support. +;; Try to display the on screen keyboard whenever entering the +;; mini-buffer, and hide it whenever leaving. + +(defun minibuffer-setup-on-screen-keyboard () + "Maybe display the on-screen keyboard in the current frame. +Display the on-screen keyboard in the current frame if the +last device to have sent an input event is not a keyboard. +This is run upon minibuffer setup." + (when (not (memq (device-class last-event-frame + last-event-device) + '(keyboard core-keyboard))) + (frame-toggle-on-screen-keyboard (selected-frame) nil))) + +(defun minibuffer-exit-on-screen-keyboard () + "Hide the on-screen keyboard if it was displayed. +This is run upon minibuffer exit." + (frame-toggle-on-screen-keyboard (selected-frame) t)) + +(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard) +(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard) + (provide 'minibuffer) ;;; minibuffer.el ends here diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index bcc4f5e9be3..a1c9e0b4afd 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -38,6 +38,12 @@ touch point, and the initial position of the touchpoint. See `touch-screen-handle-point-update' for the meanings of the fourth element.") +(defvar touch-screen-set-point-commands '(mouse-set-point) + "List of commands known to set the point. +This is used to determine whether or not to display the on-screen +keyboard after a mouse command is executed in response to a +`touchscreen-end' event.") + (defvar touch-screen-current-timer nil "Timer used to track long-presses. This is always cleared upon any significant state change.") @@ -54,20 +60,28 @@ However, return the coordinates relative to WINDOW. If (posn-window posn) is the same as window, simply return the coordinates in POSN. Otherwise, convert them to the frame, and -then back again." - (if (eq (posn-window posn) window) +then back again. + +If WINDOW is the symbol `frame', simply convert the coordinates +to the frame that they belong in." + (if (or (eq (posn-window posn) window) + (and (eq window 'frame) + (framep (posn-window posn)))) (posn-x-y posn) (let ((xy (posn-x-y posn)) - (edges (window-inside-pixel-edges window))) + (edges (and (windowp window) + (window-inside-pixel-edges window)))) ;; Make the X and Y positions frame relative. (when (windowp (posn-window posn)) (let ((edges (window-inside-pixel-edges (posn-window posn)))) (setq xy (cons (+ (car xy) (car edges)) (+ (cdr xy) (cadr edges)))))) - ;; Make the X and Y positions window relative again. - (cons (- (car xy) (car edges)) - (- (cdr xy) (cadr edges)))))) + (if (eq window 'frame) + xy + ;; Make the X and Y positions window relative again. + (cons (- (car xy) (car edges)) + (- (cdr xy) (cadr edges))))))) (defun touch-screen-handle-scroll (dx dy) "Scroll the display assuming that a touch point has moved by DX and DY." @@ -252,7 +266,12 @@ If the fourth argument of `touch-screen-current-tool' is nil, move point to the position of POINT, selecting the window under POINT as well, and deactivate the mark; if there is a button or link at POINT, call the command bound to `mouse-2' there. -Otherwise, call the command bound to `mouse-1'." +Otherwise, call the command bound to `mouse-1'. + +If the command being executed is listed in +`touch-screen-set-point-commands' also display the on-screen +keyboard if the current buffer and the character at the new point +is not read-only." (let ((what (nth 3 touch-screen-current-tool))) (cond ((null what) (when (windowp (posn-window (cdr point))) @@ -283,9 +302,17 @@ Otherwise, call the command bound to `mouse-1'." (deactivate-mark) ;; This is necessary for following links. (goto-char (posn-point (cdr point))) + ;; Figure out if the on screen keyboard needs to be + ;; displayed. (when command (call-interactively command nil - (vector event))))))))) + (vector event)) + (when (memq command touch-screen-set-point-commands) + (if (not (or buffer-read-only + (get-text-property (point) 'read-only))) + (frame-toggle-on-screen-keyboard (selected-frame) nil) + ;; Otherwise, hide the on screen keyboard now. + (frame-toggle-on-screen-keyboard (selected-frame) t)))))))))) (defun touch-screen-handle-touch (event) "Handle a single touch EVENT, and perform associated actions. @@ -379,22 +406,34 @@ touch point with the same ID as in EVENT, call UPDATE with the touch point in event and DATA. Return nil immediately if any other kind of event is received; -otherwise, return t once the `touchscreen-end' event arrives." - (catch 'finish - (while t - (let ((new-event (read-event))) - (cond - ((eq (car-safe new-event) 'touchscreen-update) - (let ((tool (assq (caadr event) (nth 1 new-event)))) - (when (and update tool) - (funcall update new-event data)))) - ((eq (car-safe new-event) 'touchscreen-end) - (throw 'finish - ;; Now determine whether or not the `touchscreen-end' - ;; event has the same ID as EVENT. If it doesn't, - ;; then this is another touch, so return nil. - (eq (caadr event) (caadr new-event)))) - (t (throw 'finish nil))))))) +otherwise, return either t or `no-drag' once the +`touchscreen-end' event arrives; return `no-drag' returned if the +touch point in EVENT did not move significantly, and t otherwise." + (let ((return-value 'no-drag) + (start-xy (touch-screen-relative-xy (cdadr event) + 'frame))) + (catch 'finish + (while t + (let ((new-event (read-event))) + (cond + ((eq (car-safe new-event) 'touchscreen-update) + (when-let* ((tool (assq (caadr event) (nth 1 new-event))) + (xy (touch-screen-relative-xy (cdr tool) 'frame))) + (when (or (> (- (car xy) (car start-xy)) 5) + (< (- (car xy) (car start-xy)) -5) + (> (- (cdr xy) (cdr start-xy)) 5) + (< (- (cdr xy) (cdr start-xy)) -5)) + (setq return-value t)) + (when (and update tool) + (funcall update new-event data)))) + ((eq (car-safe new-event) 'touchscreen-end) + (throw 'finish + ;; Now determine whether or not the `touchscreen-end' + ;; event has the same ID as EVENT. If it doesn't, + ;; then this is another touch, so return nil. + (and (eq (caadr event) (caadr new-event)) + return-value))) + (t (throw 'finish nil)))))))) @@ -402,45 +441,8 @@ otherwise, return t once the `touchscreen-end' event arrives." (defun touch-screen-drag-mode-line-1 (event) "Internal helper for `touch-screen-drag-mode-line'. -This is called when that function determines it need not execute -any keymaps on the mode line at that particular spot." - ;; Find the window that should be dragged and the starting position. - (let* ((window (posn-window (cdadr event))) - (relative-xy (touch-screen-relative-xy - (cdadr event) window)) - (last-position (cdr relative-xy))) - (when (window-resizable window 0) - (touch-screen-track-drag - event (lambda (new-event &optional _data) - ;; Find the position of the touchpoint in NEW-EVENT. - (let* ((touchpoint (assq (caadr event) (cadr new-event))) - (new-relative-xy - (touch-screen-relative-xy (cdr touchpoint) - window)) - (position (cdr new-relative-xy)) - growth) - ;; Now set the new height of the window. - ;; If new-relative-y is above relative-xy, then - ;; make the window that much shorter. Otherwise, - ;; make it bigger. - (unless (or (zerop (setq growth (- position last-position))) - (and (> growth 0) - (< position (+ (window-pixel-top window) - (window-pixel-height window)))) - (and (< growth 0) - (> position (+ (window-pixel-top window) - (window-pixel-height window))))) - (adjust-window-trailing-edge window growth nil t)) - (setq last-position position))))))) - -(defun touch-screen-drag-mode-line (event) - "Begin dragging the mode line in response to a touch EVENT. -If EVENT lies on top of text with a mouse command bound, run -that command instead. - -Change the height of the window based on where the touch point -in EVENT moves." - (interactive "e") +This is called when that function determines that no drag really +happened. EVENT is the same as in `touch-screen-drag-mode-line'." ;; If there is an object at EVENT, then look either a keymap bound ;; to [down-mouse-1] or a command bound to [mouse-1]. Then, if a ;; keymap was found, pop it up as a menu. Otherwise, wait for a tap @@ -457,21 +459,66 @@ in EVENT moves." (keymap (lookup-key object-keymap [mode-line down-mouse-1])) (command (or (lookup-key object-keymap [mode-line mouse-1]) keymap))) - (if (or (keymapp keymap) command) - (if (keymapp keymap) - (when (touch-screen-track-tap event) - (when-let* ((command (x-popup-menu event keymap)) - (tem (lookup-key keymap - (if (consp command) - (apply #'vector command) - (vector command)) - t))) - (call-interactively tem))) - (when (and (commandp command) - (touch-screen-track-tap event)) - (call-interactively command nil - (vector (list 'mouse-1 (cdadr event)))))) - (touch-screen-drag-mode-line-1 event)))) + (when (or (keymapp keymap) command) + (if (keymapp keymap) + (when-let* ((command (x-popup-menu event keymap)) + (tem (lookup-key keymap + (if (consp command) + (apply #'vector command) + (vector command)) + t))) + (call-interactively tem)) + (when (commandp command) + (call-interactively command nil + (vector (list 'mouse-1 (cdadr event))))))))) + +(defun touch-screen-drag-mode-line (event) + "Begin dragging the mode line in response to a touch EVENT. +Change the height of the window based on where the touch point in +EVENT moves. + +If it does not actually move anywhere and the touch point is +removed, and EVENT lies on top of text with a mouse command +bound, run that command instead." + (interactive "e") + ;; Find the window that should be dragged and the starting position. + (let* ((window (posn-window (cdadr event))) + (relative-xy (touch-screen-relative-xy + (cdadr event) window)) + (last-position (cdr relative-xy))) + (when (window-resizable window 0) + (when (eq + (touch-screen-track-drag + event (lambda (new-event &optional _data) + ;; Find the position of the touchpoint in + ;; NEW-EVENT. + (let* ((touchpoint (assq (caadr event) + (cadr new-event))) + (new-relative-xy + (touch-screen-relative-xy (cdr touchpoint) + window)) + (position (cdr new-relative-xy)) + growth) + ;; Now set the new height of the window. If + ;; new-relative-y is above relative-xy, then + ;; make the window that much shorter. + ;; Otherwise, make it bigger. + (unless (or (zerop (setq growth + (- position last-position))) + (and (> growth 0) + (< position + (+ (window-pixel-top window) + (window-pixel-height window)))) + (and (< growth 0) + (> position + (+ (window-pixel-top window) + (window-pixel-height window))))) + (adjust-window-trailing-edge window growth nil t)) + (setq last-position position)))) + 'no-drag) + ;; Dragging did not actually happen, so try to run any command + ;; necessary. + (touch-screen-drag-mode-line-1 event))))) (global-set-key [mode-line touchscreen-begin] #'touch-screen-drag-mode-line) diff --git a/src/android.c b/src/android.c index eb9c404f1a3..43ac3b3f754 100644 --- a/src/android.c +++ b/src/android.c @@ -121,6 +121,8 @@ struct android_emacs_window { jclass class; jmethodID swap_buffers; + jmethodID toggle_on_screen_keyboard; + jmethodID window_updated; }; /* The asset manager being used. */ @@ -193,6 +195,10 @@ static struct android_emacs_drawable drawable_class; /* Various methods associated with the EmacsWindow class. */ static struct android_emacs_window window_class; +/* The last event serial used. This is a 32 bit value, but it is + stored in unsigned long to be consistent with X. */ +static unsigned int event_serial; + /* Event handling functions. Events are stored on a (circular) queue @@ -1435,6 +1441,9 @@ android_init_emacs_window (void) assert (window_class.c_name); FIND_METHOD (swap_buffers, "swapBuffers", "()V"); + FIND_METHOD (toggle_on_screen_keyboard, + "toggleOnScreenKeyboard", "(Z)V"); + FIND_METHOD (window_updated, "windowUpdated", "(J)V"); #undef FIND_METHOD } @@ -1497,7 +1506,7 @@ NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object) emacs_abort (); } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, jshort window, jlong time, jint x, jint y, jint width, @@ -1506,6 +1515,7 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, union android_event event; event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY; + event.xconfigure.serial = ++event_serial; event.xconfigure.window = window; event.xconfigure.time = time; event.xconfigure.x = x; @@ -1514,9 +1524,10 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, event.xconfigure.height = height; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, jshort window, jlong time, jint state, jint keycode, @@ -1525,6 +1536,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, union android_event event; event.xkey.type = ANDROID_KEY_PRESS; + event.xkey.serial = ++event_serial; event.xkey.window = window; event.xkey.time = time; event.xkey.state = state; @@ -1532,9 +1544,10 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, event.xkey.unicode_char = unicode_char; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, jshort window, jlong time, jint state, jint keycode, @@ -1543,6 +1556,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, union android_event event; event.xkey.type = ANDROID_KEY_RELEASE; + event.xkey.serial = ++event_serial; event.xkey.window = window; event.xkey.time = time; event.xkey.state = state; @@ -1550,48 +1564,55 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, event.xkey.unicode_char = unicode_char; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object, jshort window, jlong time) { union android_event event; - event.xkey.type = ANDROID_FOCUS_IN; - event.xkey.window = window; - event.xkey.time = time; + event.xfocus.type = ANDROID_FOCUS_IN; + event.xfocus.serial = ++event_serial; + event.xfocus.window = window; + event.xfocus.time = time; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object, jshort window, jlong time) { union android_event event; - event.xkey.type = ANDROID_FOCUS_OUT; - event.xkey.window = window; - event.xkey.time = time; + event.xfocus.type = ANDROID_FOCUS_OUT; + event.xfocus.serial = ++event_serial; + event.xfocus.window = window; + event.xfocus.time = time; android_write_event (&event); + return ++event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object, jshort window, jint action) { union android_event event; event.xaction.type = ANDROID_WINDOW_ACTION; + event.xaction.serial = ++event_serial; event.xaction.window = window; event.xaction.action = action; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time) @@ -1599,15 +1620,17 @@ NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, union android_event event; event.xcrossing.type = ANDROID_ENTER_NOTIFY; + event.xcrossing.serial = ++event_serial; event.xcrossing.window = window; event.xcrossing.x = x; event.xcrossing.y = y; event.xcrossing.time = time; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time) @@ -1615,15 +1638,17 @@ NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, union android_event event; event.xcrossing.type = ANDROID_LEAVE_NOTIFY; + event.xcrossing.serial = ++event_serial; event.xcrossing.window = window; event.xcrossing.x = x; event.xcrossing.y = y; event.xcrossing.time = time; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time) @@ -1631,15 +1656,17 @@ NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, union android_event event; event.xmotion.type = ANDROID_MOTION_NOTIFY; + event.xmotion.serial = ++event_serial; event.xmotion.window = window; event.xmotion.x = x; event.xmotion.y = y; event.xmotion.time = time; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint state, @@ -1648,6 +1675,7 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, union android_event event; event.xbutton.type = ANDROID_BUTTON_PRESS; + event.xbutton.serial = ++event_serial; event.xbutton.window = window; event.xbutton.x = x; event.xbutton.y = y; @@ -1656,9 +1684,10 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, event.xbutton.button = button; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint state, @@ -1667,6 +1696,7 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, union android_event event; event.xbutton.type = ANDROID_BUTTON_RELEASE; + event.xbutton.serial = ++event_serial; event.xbutton.window = window; event.xbutton.x = x; event.xbutton.y = y; @@ -1675,9 +1705,10 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, event.xbutton.button = button; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint pointer_id) @@ -1685,6 +1716,7 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, union android_event event; event.touch.type = ANDROID_TOUCH_DOWN; + event.touch.serial = ++event_serial; event.touch.window = window; event.touch.x = x; event.touch.y = y; @@ -1692,9 +1724,10 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, event.touch.pointer_id = pointer_id; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint pointer_id) @@ -1702,6 +1735,7 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, union android_event event; event.touch.type = ANDROID_TOUCH_UP; + event.touch.serial = ++event_serial; event.touch.window = window; event.touch.x = x; event.touch.y = y; @@ -1709,9 +1743,10 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, event.touch.pointer_id = pointer_id; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint pointer_id) @@ -1719,6 +1754,7 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, union android_event event; event.touch.type = ANDROID_TOUCH_MOVE; + event.touch.serial = ++event_serial; event.touch.window = window; event.touch.x = x; event.touch.y = y; @@ -1726,9 +1762,10 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, event.touch.pointer_id = pointer_id; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, jshort window, jint x, jint y, jlong time, jint state, @@ -1737,6 +1774,7 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, union android_event event; event.wheel.type = ANDROID_WHEEL; + event.wheel.serial = ++event_serial; event.wheel.window = window; event.wheel.x = x; event.wheel.y = y; @@ -1746,43 +1784,50 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, event.wheel.y_delta = y_delta; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, jshort window) { union android_event event; event.iconified.type = ANDROID_ICONIFIED; + event.iconified.serial = ++event_serial; event.iconified.window = window; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, jshort window) { union android_event event; event.iconified.type = ANDROID_DEICONIFIED; + event.iconified.serial = ++event_serial; event.iconified.window = window; android_write_event (&event); + return event_serial; } -extern JNIEXPORT void JNICALL +extern JNIEXPORT jlong JNICALL NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, jshort window, jint menu_event_id) { union android_event event; event.menu.type = ANDROID_CONTEXT_MENU; + event.menu.serial = ++event_serial; event.menu.window = window; event.menu.menu_event_id = menu_event_id; android_write_event (&event); + return event_serial; } #ifdef __clang__ @@ -3599,6 +3644,15 @@ android_translate_coordinates (android_window src, int x, ANDROID_DELETE_LOCAL_REF (coordinates); } +void +android_sync (void) +{ + (*android_java_env)->CallVoidMethod (android_java_env, + emacs_service, + service_class.sync); + android_exception_check (); +} + /* Low level drawing primitives. */ @@ -3795,12 +3849,45 @@ android_get_keysym_name (int keysym, char *name_return, size_t size) ANDROID_DELETE_LOCAL_REF (string); } +/* Display the on screen keyboard on window WINDOW, or hide it if SHOW + is false. Ask the system to bring up or hide the on-screen + keyboard on behalf of WINDOW. The request may be rejected by the + system, especially when the window does not have the input + focus. */ + void -android_sync (void) +android_toggle_on_screen_keyboard (android_window window, bool show) { - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.sync); + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = window_class.toggle_on_screen_keyboard; + + /* Now display the on screen keyboard. */ + (*android_java_env)->CallVoidMethod (android_java_env, object, + method, (jboolean) show); + + /* Check for out of memory errors. */ + android_exception_check (); +} + +/* Tell the window system that all configure events sent to WINDOW + have been fully processed, and that it is now okay to display its + new contents. SERIAL is the serial of the last configure event + processed. */ + +void +android_window_updated (android_window window, unsigned long serial) +{ + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = window_class.window_updated; + + (*android_java_env)->CallVoidMethod (android_java_env, object, + method, (jlong) serial); android_exception_check (); } @@ -3811,7 +3898,7 @@ android_sync (void) #undef faccessat /* Replace the system faccessat with one which understands AT_EACCESS. - Android's faccessat simply fails upon using AT_EACCESS, so repalce + Android's faccessat simply fails upon using AT_EACCESS, so replace it with zero here. This isn't caught during configuration. This replacement is only done when building for Android 17 or @@ -3829,7 +3916,7 @@ faccessat (int dirfd, const char *pathname, int mode, int flags) return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); } -#endif /* __ANDROID_API__ < 16 */ +#endif /* __ANDROID_API__ >= 17 */ diff --git a/src/android.h b/src/android.h index 240bc90d831..97818ab4911 100644 --- a/src/android.h +++ b/src/android.h @@ -91,6 +91,8 @@ extern void android_exception_check (void); extern void android_get_keysym_name (int, char *, size_t); extern void android_wait_event (void); +extern void android_toggle_on_screen_keyboard (android_window, bool); +extern void android_window_updated (android_window, unsigned long); diff --git a/src/androidfns.c b/src/androidfns.c index bb37c415069..77ee2e8de44 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -2332,6 +2332,30 @@ there is no mouse. */) #endif } +DEFUN ("android-toggle-on-screen-keyboard", + Fandroid_toggle_on_screen_keyboard, + Sandroid_toggle_on_screen_keyboard, 2, 2, 0, + doc: /* Display or hide the on-screen keyboard. +If HIDE is non-nil, hide the on screen keyboard if it is currently +being displayed. Else, request that the system display it on behalf +of FRAME. This request may be rejected if FRAME does not have the +input focus. */) + (Lisp_Object frame, Lisp_Object hide) +{ +#ifndef ANDROID_STUBIFY + struct frame *f; + + f = decode_window_system_frame (frame); + + block_input (); + android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f), + NILP (hide)); + unblock_input (); +#endif + + return Qnil; +} + #ifndef ANDROID_STUBIFY @@ -2781,6 +2805,7 @@ syms_of_androidfns (void) defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); defsubr (&Sandroid_detect_mouse); + defsubr (&Sandroid_toggle_on_screen_keyboard); #ifndef ANDROID_STUBIFY tip_timer = Qnil; diff --git a/src/androidgui.h b/src/androidgui.h index 1f28c18ff34..3b9a74dc0b0 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -239,6 +239,7 @@ enum android_event_type struct android_any_event { enum android_event_type type; + unsigned long serial; android_window window; }; @@ -252,6 +253,7 @@ enum android_modifier_mask struct android_key_event { enum android_event_type type; + unsigned long serial; android_window window; android_time time; unsigned int state; @@ -271,6 +273,7 @@ struct android_key_event struct android_configure_event { enum android_event_type type; + unsigned long serial; android_window window; android_time time; int x, y; @@ -280,6 +283,7 @@ struct android_configure_event struct android_focus_event { enum android_event_type type; + unsigned long serial; android_window window; android_time time; }; @@ -287,6 +291,7 @@ struct android_focus_event struct android_window_action_event { enum android_event_type type; + unsigned long serial; /* The window handle. This can be ANDROID_NONE. */ android_window window; @@ -301,6 +306,7 @@ struct android_window_action_event struct android_crossing_event { enum android_event_type type; + unsigned long serial; android_window window; int x, y; unsigned long time; @@ -309,6 +315,7 @@ struct android_crossing_event struct android_motion_event { enum android_event_type type; + unsigned long serial; android_window window; int x, y; unsigned long time; @@ -317,6 +324,7 @@ struct android_motion_event struct android_button_event { enum android_event_type type; + unsigned long serial; android_window window; int x, y; unsigned long time; @@ -329,6 +337,9 @@ struct android_touch_event /* Type of the event. */ enum android_event_type type; + /* Serial identifying the event. */ + unsigned long serial; + /* Window associated with the event. */ android_window window; @@ -347,6 +358,9 @@ struct android_wheel_event /* Type of the event. */ enum android_event_type type; + /* Serial identifying the event. */ + unsigned long serial; + /* Window associated with the event. */ android_window window; @@ -368,6 +382,9 @@ struct android_iconify_event /* Type of the event. */ enum android_event_type type; + /* Serial identifying the event. */ + unsigned long serial; + /* Window associated with the event. */ android_window window; }; @@ -377,6 +394,9 @@ struct android_menu_event /* Type of the event. */ enum android_event_type type; + /* Serial identifying the event. */ + unsigned long serial; + /* Window associated with the event. Always None. */ android_window window; diff --git a/src/androidterm.c b/src/androidterm.c index 3c16b542d91..3024890d789 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -591,7 +591,17 @@ handle_one_android_event (struct android_display_info *dpyinfo, android_clear_under_internal_border (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); + + /* Now stash the serial of this configure event somewhere, + and call android_window_updated with it once the redraw + completes. */ + FRAME_OUTPUT_DATA (f)->last_configure_serial + = configureEvent.xconfigure.serial; } + else + /* Reply to this ConfigureNotify event immediately. */ + android_window_updated (FRAME_ANDROID_WINDOW (f), + configureEvent.xconfigure.serial); goto OTHER; @@ -1194,6 +1204,9 @@ handle_one_android_event (struct android_display_info *dpyinfo, /* Iconification. This is vastly simpler than on X. */ case ANDROID_ICONIFIED: + if (!any) + goto OTHER; + if (FRAME_ICONIFIED_P (any)) goto OTHER; @@ -1206,6 +1219,9 @@ handle_one_android_event (struct android_display_info *dpyinfo, case ANDROID_DEICONIFIED: + if (!any) + goto OTHER; + if (!FRAME_ICONIFIED_P (any)) goto OTHER; @@ -1311,6 +1327,14 @@ android_frame_up_to_date (struct frame *f) /* The frame is now complete, as its contents have been drawn. */ FRAME_ANDROID_COMPLETE_P (f) = true; + /* If there was an outstanding configure event, then tell system + that the update has finished and the new contents can now be + displayed. */ + if (FRAME_OUTPUT_DATA (f)->last_configure_serial) + android_window_updated (FRAME_ANDROID_WINDOW (f), + FRAME_OUTPUT_DATA (f)->last_configure_serial); + FRAME_OUTPUT_DATA (f)->last_configure_serial = 0; + /* Shrink the scanline buffer used by the font backend. */ sfntfont_android_shrink_scanline_buffer (); unblock_input (); diff --git a/src/androidterm.h b/src/androidterm.h index c0f862e35fb..11b24d40b73 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -241,6 +241,11 @@ struct android_output /* List of all tools (either styluses or fingers) pressed onto the frame. */ struct android_touch_point *touch_points; + + /* Event serial of the last ConfigureNotify event received that has + not yet been drawn. This is used to synchronize resize with the + window system. 0 if no such outstanding event exists. */ + unsigned long last_configure_serial; }; enum -- cgit v1.3 From 15bcb446be2f2f5b85a1b9585ec3abaabcbf04d9 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 1 Mar 2023 12:00:46 +0800 Subject: Update Android port * java/Makefile.in (ETAGS, clean): New rules to generate tags. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea): * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog)::(dialog. Then): * java/org/gnu/emacs/EmacsDocumentsProvider.java (EmacsDocumentsProvider): * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): * java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle): * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): * java/org/gnu/emacs/EmacsFillRectangle.java (EmacsFillRectangle): * java/org/gnu/emacs/EmacsGC.java (EmacsGC): * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): * java/org/gnu/emacs/EmacsNative.java (EmacsNative): * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive): * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): * java/org/gnu/emacs/EmacsPreferencesActivity.java (EmacsPreferencesActivity): * java/org/gnu/emacs/EmacsSdk11Clipboard.java (EmacsSdk11Clipboard): * java/org/gnu/emacs/EmacsSdk23FontDriver.java (EmacsSdk23FontDriver): * java/org/gnu/emacs/EmacsSdk8Clipboard.java (EmacsSdk8Clipboard): * java/org/gnu/emacs/EmacsService.java (EmacsService): * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (buffers): * java/org/gnu/emacs/EmacsView.java (EmacsView, ViewGroup): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, drawables): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Make classes final where appropriate. --- java/Makefile.in | 16 +++++++++++++++- java/org/gnu/emacs/EmacsActivity.java | 22 +++++++++++----------- java/org/gnu/emacs/EmacsApplication.java | 2 +- java/org/gnu/emacs/EmacsContextMenu.java | 2 +- java/org/gnu/emacs/EmacsCopyArea.java | 2 +- java/org/gnu/emacs/EmacsDialog.java | 2 +- java/org/gnu/emacs/EmacsDocumentsProvider.java | 2 +- java/org/gnu/emacs/EmacsDrawLine.java | 2 +- java/org/gnu/emacs/EmacsDrawPoint.java | 2 +- java/org/gnu/emacs/EmacsDrawRectangle.java | 2 +- java/org/gnu/emacs/EmacsFillPolygon.java | 2 +- java/org/gnu/emacs/EmacsFillRectangle.java | 2 +- java/org/gnu/emacs/EmacsGC.java | 2 +- java/org/gnu/emacs/EmacsInputConnection.java | 11 ++++++++++- java/org/gnu/emacs/EmacsNative.java | 2 +- java/org/gnu/emacs/EmacsNoninteractive.java | 2 +- java/org/gnu/emacs/EmacsOpenActivity.java | 2 +- java/org/gnu/emacs/EmacsPixmap.java | 2 +- java/org/gnu/emacs/EmacsPreferencesActivity.java | 2 +- java/org/gnu/emacs/EmacsSdk11Clipboard.java | 2 +- java/org/gnu/emacs/EmacsSdk23FontDriver.java | 2 +- java/org/gnu/emacs/EmacsSdk8Clipboard.java | 2 +- java/org/gnu/emacs/EmacsService.java | 2 +- java/org/gnu/emacs/EmacsSurfaceView.java | 2 +- java/org/gnu/emacs/EmacsView.java | 2 +- java/org/gnu/emacs/EmacsWindow.java | 2 +- .../gnu/emacs/EmacsWindowAttachmentManager.java | 2 +- 27 files changed, 60 insertions(+), 37 deletions(-) (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/Makefile.in b/java/Makefile.in index 7ba05f6c9a3..bff021752c7 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -281,9 +281,23 @@ $(APK_NAME): classes.dex emacs.apk-in emacs.keystore $(AM_V_SILENT) $(APKSIGNER) $(SIGN_EMACS_V2) $@ $(AM_V_SILENT) rm -f $@.unaligned *.idsig +# TAGS generation. + +ETAGS = $(top_builddir)/lib-src/etags + +$(ETAGS): FORCE + $(MAKE) -C ../lib-src $(notdir $@) + +tagsfiles = $(JAVA_FILES) $(RESOURCE_FILE) + +.PHONY: tags FORCE +tags: TAGS +TAGS: $(ETAGS) $(tagsfiles) + $(AM_V_GEN) $(ETAGS) $(tagsfiles) + clean: rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig - rm -rf install-temp $(RESOURCE_FILE) + rm -rf install-temp $(RESOURCE_FILE) TAGS find . -name '*.class' -delete maintainer-clean distclean bootstrap-clean: clean diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 7e09e608984..0ee8c239899 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -108,7 +108,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void detachWindow () { syncFullscreenWith (null); @@ -131,7 +131,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void attachWindow (EmacsWindow child) { Log.d (TAG, "attachWindow: " + child); @@ -161,21 +161,21 @@ public class EmacsActivity extends Activity } @Override - public void + public final void destroy () { finish (); } @Override - public EmacsWindow + public final EmacsWindow getAttachedWindow () { return window; } @Override - public void + public final void onCreate (Bundle savedInstanceState) { FrameLayout.LayoutParams params; @@ -238,7 +238,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onWindowFocusChanged (boolean isFocused) { Log.d (TAG, ("onWindowFocusChanged: " @@ -256,7 +256,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onPause () { isPaused = true; @@ -266,7 +266,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onResume () { isPaused = false; @@ -279,7 +279,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onContextMenuClosed (Menu menu) { Log.d (TAG, "onContextMenuClosed: " + menu); @@ -298,7 +298,7 @@ public class EmacsActivity extends Activity } @SuppressWarnings ("deprecation") - public void + public final void syncFullscreenWith (EmacsWindow emacsWindow) { WindowInsetsController controller; @@ -372,7 +372,7 @@ public class EmacsActivity extends Activity } @Override - public void + public final void onAttachedToWindow () { super.onAttachedToWindow (); diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java index 6a065165eb1..10099721744 100644 --- a/java/org/gnu/emacs/EmacsApplication.java +++ b/java/org/gnu/emacs/EmacsApplication.java @@ -27,7 +27,7 @@ import android.content.Context; import android.app.Application; import android.util.Log; -public class EmacsApplication extends Application +public final class EmacsApplication extends Application { private static final String TAG = "EmacsApplication"; diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 6b3ae0c6de9..0de292af21a 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -42,7 +42,7 @@ import android.widget.PopupMenu; Android menu, which can be turned into a popup (or other kind of) menu. */ -public class EmacsContextMenu +public final class EmacsContextMenu { private static final String TAG = "EmacsContextMenu"; diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java index 1daa2190542..f69b0cde866 100644 --- a/java/org/gnu/emacs/EmacsCopyArea.java +++ b/java/org/gnu/emacs/EmacsCopyArea.java @@ -27,7 +27,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Xfermode; -public class EmacsCopyArea +public final class EmacsCopyArea { private static Xfermode overAlu; diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 9f9124ce99c..80a5e5f7369 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -38,7 +38,7 @@ import android.view.ViewGroup; describes a single alert dialog. Then, `inflate' turns it into AlertDialog. */ -public class EmacsDialog implements DialogInterface.OnDismissListener +public final class EmacsDialog implements DialogInterface.OnDismissListener { private static final String TAG = "EmacsDialog"; diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index 3c3c7ead3c5..901c3b909e0 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -48,7 +48,7 @@ import java.io.IOException; This functionality is only available on Android 19 and later. */ -public class EmacsDocumentsProvider extends DocumentsProvider +public final class EmacsDocumentsProvider extends DocumentsProvider { /* Home directory. This is the directory whose contents are initially returned to requesting applications. */ diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index c6e5123bfca..0b23138a36c 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -29,7 +29,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Xfermode; -public class EmacsDrawLine +public final class EmacsDrawLine { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java b/java/org/gnu/emacs/EmacsDrawPoint.java index 3bc7be17961..de8ddf09cc4 100644 --- a/java/org/gnu/emacs/EmacsDrawPoint.java +++ b/java/org/gnu/emacs/EmacsDrawPoint.java @@ -19,7 +19,7 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -public class EmacsDrawPoint +public final class EmacsDrawPoint { public static void perform (EmacsDrawable drawable, diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index 695a8c6ea44..ce5e94e4a76 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -27,7 +27,7 @@ import android.graphics.RectF; import android.util.Log; -public class EmacsDrawRectangle +public final class EmacsDrawRectangle { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java index 22e2dd0d8a9..d55a0b3aca8 100644 --- a/java/org/gnu/emacs/EmacsFillPolygon.java +++ b/java/org/gnu/emacs/EmacsFillPolygon.java @@ -29,7 +29,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -public class EmacsFillPolygon +public final class EmacsFillPolygon { public static void perform (EmacsDrawable drawable, EmacsGC gc, Point points[]) diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java index aed0a540c8f..4a0478b446f 100644 --- a/java/org/gnu/emacs/EmacsFillRectangle.java +++ b/java/org/gnu/emacs/EmacsFillRectangle.java @@ -26,7 +26,7 @@ import android.graphics.Rect; import android.util.Log; -public class EmacsFillRectangle +public final class EmacsFillRectangle { public static void perform (EmacsDrawable drawable, EmacsGC gc, diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java index bdc27a1ca5b..a7467cb9bd0 100644 --- a/java/org/gnu/emacs/EmacsGC.java +++ b/java/org/gnu/emacs/EmacsGC.java @@ -29,7 +29,7 @@ import android.graphics.Xfermode; /* X like graphics context structures. Keep the enums in synch with androidgui.h! */ -public class EmacsGC extends EmacsHandleObject +public final class EmacsGC extends EmacsHandleObject { public static final int GC_COPY = 0; public static final int GC_XOR = 1; diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 834c2226c82..ed64c368857 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -36,7 +36,7 @@ import android.util.Log; See EmacsEditable for more details. */ -public class EmacsInputConnection extends BaseInputConnection +public final class EmacsInputConnection extends BaseInputConnection { private static final String TAG = "EmacsInputConnection"; private EmacsView view; @@ -243,6 +243,15 @@ public class EmacsInputConnection extends BaseInputConnection return super.sendKeyEvent (key); } + @Override + public boolean + deleteSurroundingTextInCodePoints (int beforeLength, int afterLength) + { + /* This can be implemented the same way as + deleteSurroundingText. */ + return this.deleteSurroundingText (beforeLength, afterLength); + } + /* Override functions which are not implemented. */ diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index f0917a68120..b1205353090 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -25,7 +25,7 @@ import android.content.res.AssetManager; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; -public class EmacsNative +public final class EmacsNative { /* List of native libraries that must be loaded during class initialization. */ diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index 30901edb75f..f365037b311 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -44,7 +44,7 @@ import java.lang.reflect.Method; initializes Emacs. */ @SuppressWarnings ("unchecked") -public class EmacsNoninteractive +public final class EmacsNoninteractive { private static String getLibraryDirectory (Context context) diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 87ce454a816..fddd5331d2f 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -68,7 +68,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -public class EmacsOpenActivity extends Activity +public final class EmacsOpenActivity extends Activity implements DialogInterface.OnClickListener { private static final String TAG = "EmacsOpenActivity"; diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java index a83d8f25542..eb011bc5e65 100644 --- a/java/org/gnu/emacs/EmacsPixmap.java +++ b/java/org/gnu/emacs/EmacsPixmap.java @@ -29,7 +29,7 @@ import android.os.Build; /* Drawable backed by bitmap. */ -public class EmacsPixmap extends EmacsHandleObject +public final class EmacsPixmap extends EmacsHandleObject implements EmacsDrawable { /* The depth of the bitmap. This is not actually used, just defined diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java b/java/org/gnu/emacs/EmacsPreferencesActivity.java index 85639fe9236..70934fa4bd4 100644 --- a/java/org/gnu/emacs/EmacsPreferencesActivity.java +++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java @@ -42,7 +42,7 @@ import android.preference.*; Unfortunately, there is no alternative that looks the same way. */ @SuppressWarnings ("deprecation") -public class EmacsPreferencesActivity extends PreferenceActivity +public final class EmacsPreferencesActivity extends PreferenceActivity { /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and tell the system to EmacsActivity with some parameters later. */ diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java b/java/org/gnu/emacs/EmacsSdk11Clipboard.java index ea35a463299..a05184513cd 100644 --- a/java/org/gnu/emacs/EmacsSdk11Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java @@ -32,7 +32,7 @@ import java.io.UnsupportedEncodingException; /* This class implements EmacsClipboard for Android 3.0 and later systems. */ -public class EmacsSdk11Clipboard extends EmacsClipboard +public final class EmacsSdk11Clipboard extends EmacsClipboard implements ClipboardManager.OnPrimaryClipChangedListener { private static final String TAG = "EmacsSdk11Clipboard"; diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java b/java/org/gnu/emacs/EmacsSdk23FontDriver.java index 11e128d5769..aaba8dbd166 100644 --- a/java/org/gnu/emacs/EmacsSdk23FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java @@ -22,7 +22,7 @@ package org.gnu.emacs; import android.graphics.Paint; import android.graphics.Rect; -public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver +public final class EmacsSdk23FontDriver extends EmacsSdk7FontDriver { private void textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics, diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java index 34e66912562..818a722a908 100644 --- a/java/org/gnu/emacs/EmacsSdk8Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java @@ -31,7 +31,7 @@ import java.io.UnsupportedEncodingException; similarly old systems. */ @SuppressWarnings ("deprecation") -public class EmacsSdk8Clipboard extends EmacsClipboard +public final class EmacsSdk8Clipboard extends EmacsClipboard { private static final String TAG = "EmacsSdk8Clipboard"; private ClipboardManager manager; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 7f4f75b5147..e61d9487375 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -82,7 +82,7 @@ class Holder /* EmacsService is the service that starts the thread running Emacs and handles requests by that Emacs instance. */ -public class EmacsService extends Service +public final class EmacsService extends Service { public static final String TAG = "EmacsService"; public static final int MAX_PENDING_REQUESTS = 256; diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 62e927094e4..e0411f7f8b3 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -35,7 +35,7 @@ import java.lang.ref.WeakReference; own back buffers, which use too much memory (up to 96 MB for a single frame.) */ -public class EmacsSurfaceView extends View +public final class EmacsSurfaceView extends View { private static final String TAG = "EmacsSurfaceView"; private EmacsView view; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 89f526853b2..d2330494bc7 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -51,7 +51,7 @@ import android.util.Log; It is also a ViewGroup, as it also lays out children. */ -public class EmacsView extends ViewGroup +public final class EmacsView extends ViewGroup { public static final String TAG = "EmacsView"; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 90fc4c44198..007a2a86e68 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -59,7 +59,7 @@ import android.os.Build; Views are also drawables, meaning they can accept drawing requests. */ -public class EmacsWindow extends EmacsHandleObject +public final class EmacsWindow extends EmacsHandleObject implements EmacsDrawable { private static final String TAG = "EmacsWindow"; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 510300571b8..1548bf28087 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -50,7 +50,7 @@ import android.util.Log; Finally, every time a window is removed, the consumer is destroyed. */ -public class EmacsWindowAttachmentManager +public final class EmacsWindowAttachmentManager { public static EmacsWindowAttachmentManager MANAGER; private final static String TAG = "EmacsWindowAttachmentManager"; -- cgit v1.3 From 1cae464859341bfd5fc7d7ed4b503ef959af81dd Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 5 Mar 2023 19:58:28 +0800 Subject: Update Android port * java/org/gnu/emacs/EmacsActivity.java (onCreate): * java/org/gnu/emacs/EmacsContextMenu.java: * java/org/gnu/emacs/EmacsDocumentsProvider.java (getMimeType): * java/org/gnu/emacs/EmacsDrawLine.java (perform): * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): * java/org/gnu/emacs/EmacsFillPolygon.java: * java/org/gnu/emacs/EmacsFontDriver.java: * java/org/gnu/emacs/EmacsHandleObject.java: * java/org/gnu/emacs/EmacsInputConnection.java: * java/org/gnu/emacs/EmacsMultitaskActivity.java (EmacsMultitaskActivity): * java/org/gnu/emacs/EmacsNative.java: * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive, main): * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (startEmacsClient): * java/org/gnu/emacs/EmacsSdk7FontDriver.java: * java/org/gnu/emacs/EmacsSdk8Clipboard.java: * java/org/gnu/emacs/EmacsService.java (EmacsService, onCreate): * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Remove redundant includes. Reorganize some functions around, remove duplicate `getLibDir' functions, and remove unused local variables. --- java/org/gnu/emacs/EmacsActivity.java | 13 ++++-- java/org/gnu/emacs/EmacsContextMenu.java | 3 -- java/org/gnu/emacs/EmacsDocumentsProvider.java | 4 +- java/org/gnu/emacs/EmacsDrawLine.java | 5 --- java/org/gnu/emacs/EmacsDrawRectangle.java | 1 - java/org/gnu/emacs/EmacsFillPolygon.java | 1 - java/org/gnu/emacs/EmacsFontDriver.java | 2 - java/org/gnu/emacs/EmacsHandleObject.java | 3 -- java/org/gnu/emacs/EmacsInputConnection.java | 9 +--- java/org/gnu/emacs/EmacsMultitaskActivity.java | 6 ++- java/org/gnu/emacs/EmacsNative.java | 2 - java/org/gnu/emacs/EmacsNoninteractive.java | 18 +------- java/org/gnu/emacs/EmacsOpenActivity.java | 20 +-------- java/org/gnu/emacs/EmacsSdk7FontDriver.java | 3 -- java/org/gnu/emacs/EmacsSdk8Clipboard.java | 4 +- java/org/gnu/emacs/EmacsService.java | 49 ++++++++-------------- java/org/gnu/emacs/EmacsView.java | 7 ++-- java/org/gnu/emacs/EmacsWindow.java | 15 +++---- .../gnu/emacs/EmacsWindowAttachmentManager.java | 5 +-- 19 files changed, 53 insertions(+), 117 deletions(-) (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 13002d5d0e5..692d8a14e22 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -24,18 +24,22 @@ import java.util.List; import java.util.ArrayList; import android.app.Activity; + import android.content.Context; import android.content.Intent; + import android.os.Build; import android.os.Bundle; + import android.util.Log; + import android.view.Menu; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowInsetsController; -import android.widget.FrameLayout.LayoutParams; + import android.widget.FrameLayout; public class EmacsActivity extends Activity @@ -187,6 +191,7 @@ public class EmacsActivity extends Activity Intent intent; View decorView; ViewTreeObserver observer; + int matchParent; /* See if Emacs should be started with -Q. */ intent = getIntent (); @@ -194,8 +199,10 @@ public class EmacsActivity extends Activity = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q", false); - params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); + matchParent = FrameLayout.LayoutParams.MATCH_PARENT; + params + = new FrameLayout.LayoutParams (matchParent, + matchParent); /* Make the frame layout. */ layout = new FrameLayout (this); diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index d1a624e68d9..dec5e148a8e 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import android.content.Context; import android.content.Intent; -import android.os.Bundle; import android.os.Build; import android.view.Menu; @@ -35,8 +34,6 @@ import android.view.SubMenu; import android.util.Log; -import android.widget.PopupMenu; - /* Context menu implementation. This object is built from JNI and describes a menu hiearchy. Then, `inflate' can turn it into an Android menu, which can be turned into a popup (or other kind of) diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index 901c3b909e0..f70da6040ff 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -160,6 +160,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider { String name, extension, mime; int extensionSeparator; + MimeTypeMap singleton; if (file.isDirectory ()) return Document.MIME_TYPE_DIR; @@ -170,8 +171,9 @@ public final class EmacsDocumentsProvider extends DocumentsProvider if (extensionSeparator > 0) { + singleton = MimeTypeMap.getSingleton (); extension = name.substring (extensionSeparator + 1); - mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension (extension); + mime = singleton.getMimeTypeFromExtension (extension); if (mime != null) return mime; diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 0b23138a36c..92e03c48e26 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -21,13 +21,9 @@ package org.gnu.emacs; import java.lang.Math; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.Xfermode; public final class EmacsDrawLine { @@ -38,7 +34,6 @@ public final class EmacsDrawLine Rect rect; Canvas canvas; Paint paint; - int i; /* TODO implement stippling. */ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index ce5e94e4a76..3bd5779c54e 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -33,7 +33,6 @@ public final class EmacsDrawRectangle perform (EmacsDrawable drawable, EmacsGC gc, int x, int y, int width, int height) { - int i; Paint maskPaint, paint; Canvas maskCanvas; Bitmap maskBitmap; diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java index d55a0b3aca8..ea8324c543c 100644 --- a/java/org/gnu/emacs/EmacsFillPolygon.java +++ b/java/org/gnu/emacs/EmacsFillPolygon.java @@ -21,7 +21,6 @@ package org.gnu.emacs; import java.lang.Math; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java index e142a3121d3..ff52899a897 100644 --- a/java/org/gnu/emacs/EmacsFontDriver.java +++ b/java/org/gnu/emacs/EmacsFontDriver.java @@ -19,8 +19,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -import java.util.List; - import android.os.Build; /* This code is mostly unused. See sfntfont-android.c for the code diff --git a/java/org/gnu/emacs/EmacsHandleObject.java b/java/org/gnu/emacs/EmacsHandleObject.java index a57a3bbdfa9..5b889895337 100644 --- a/java/org/gnu/emacs/EmacsHandleObject.java +++ b/java/org/gnu/emacs/EmacsHandleObject.java @@ -19,9 +19,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -import java.util.List; -import java.util.ArrayList; -import java.lang.Object; import java.lang.IllegalStateException; /* This defines something that is a so-called ``handle''. Handles diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index ed64c368857..7b40fcff99e 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -23,18 +23,13 @@ import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextSnapshot; import android.view.KeyEvent; -import android.text.Editable; - import android.util.Log; -/* Android input methods, take number six. - - See EmacsEditable for more details. */ +/* Android input methods, take number six. See textconv.c for more + details; this is more-or-less a thin wrapper around that file. */ public final class EmacsInputConnection extends BaseInputConnection { diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java index dbdc99a3559..b1c48f03fba 100644 --- a/java/org/gnu/emacs/EmacsMultitaskActivity.java +++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java @@ -19,7 +19,11 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -public class EmacsMultitaskActivity extends EmacsActivity +/* This class only exists because EmacsActivity is already defined as + an activity, and the system wants a new class in order to define a + new activity. */ + +public final class EmacsMultitaskActivity extends EmacsActivity { } diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index b1205353090..38370d60727 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -19,8 +19,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -import java.lang.System; - import android.content.res.AssetManager; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index f365037b311..aaba74d877c 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -46,21 +46,6 @@ import java.lang.reflect.Method; @SuppressWarnings ("unchecked") public final class EmacsNoninteractive { - private static String - getLibraryDirectory (Context context) - { - int apiLevel; - - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - public static void main (String[] args) { @@ -188,7 +173,7 @@ public final class EmacsNoninteractive /* Now configure Emacs. The class path should already be set. */ filesDir = context.getFilesDir ().getCanonicalPath (); - libDir = getLibraryDirectory (context); + libDir = EmacsService.getLibraryDirectory (context); cacheDir = context.getCacheDir ().getCanonicalPath (); } catch (Exception e) @@ -198,6 +183,7 @@ public final class EmacsNoninteractive System.err.println ("and that Emacs needs adjustments in order to"); System.err.println ("obtain required system internal resources."); System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org."); + e.printStackTrace (); System.exit (1); } diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index ac643ae8a13..51335ddb2dd 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -46,7 +46,6 @@ package org.gnu.emacs; import android.app.AlertDialog; import android.app.Activity; -import android.content.Context; import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; @@ -301,23 +300,6 @@ public final class EmacsOpenActivity extends Activity }); } - public String - getLibraryDirectory () - { - int apiLevel; - Context context; - - context = getApplicationContext (); - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - public void startEmacsClient (String fileName) { @@ -327,7 +309,7 @@ public final class EmacsOpenActivity extends Activity EmacsClientThread thread; File file; - libDir = getLibraryDirectory (); + libDir = EmacsService.getLibraryDirectory (this); builder = new ProcessBuilder (libDir + "/libemacsclient.so", fileName, "--reuse-frame", "--timeout=10", "--no-wait"); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index ae91c299de8..6df102f18a2 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -20,7 +20,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; import java.io.File; -import java.io.IOException; import java.util.LinkedList; import java.util.List; @@ -32,8 +31,6 @@ import android.graphics.Canvas; import android.util.Log; -import android.os.Build; - public class EmacsSdk7FontDriver extends EmacsFontDriver { private static final String TOFU_STRING = "\uDB3F\uDFFD"; diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java index 818a722a908..5a40128b0ac 100644 --- a/java/org/gnu/emacs/EmacsSdk8Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java @@ -19,7 +19,9 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; -/* Importing the entire package avoids the deprecation warning. */ +/* Importing the entire package instead of just the legacy + ClipboardManager class avoids the deprecation warning. */ + import android.text.*; import android.content.Context; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d9cb25f3e9c..d05ebce75dc 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -24,22 +24,15 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; -import java.util.ArrayList; -import android.graphics.Canvas; -import android.graphics.Bitmap; import android.graphics.Point; -import android.view.View; import android.view.InputDevice; import android.view.KeyEvent; -import android.annotation.TargetApi; - import android.app.Notification; import android.app.NotificationManager; import android.app.NotificationChannel; -import android.app.PendingIntent; import android.app.Service; import android.content.ClipboardManager; @@ -51,9 +44,6 @@ import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager; import android.content.res.AssetManager; -import android.database.Cursor; -import android.database.MatrixCursor; - import android.hardware.input.InputManager; import android.net.Uri; @@ -68,9 +58,6 @@ import android.os.Vibrator; import android.os.VibratorManager; import android.os.VibrationEffect; -import android.provider.DocumentsContract; -import android.provider.DocumentsContract.Document; - import android.util.Log; import android.util.DisplayMetrics; @@ -87,7 +74,6 @@ class Holder public final class EmacsService extends Service { public static final String TAG = "EmacsService"; - public static final int MAX_PENDING_REQUESTS = 256; public static volatile EmacsService SERVICE; public static boolean needDashQ; @@ -107,6 +93,22 @@ public final class EmacsService extends Service information. */ public static final boolean DEBUG_IC = false; + /* Return the directory leading to the directory in which native + library files are stored on behalf of CONTEXT. */ + + public static String + getLibraryDirectory (Context context) + { + int apiLevel; + + apiLevel = Build.VERSION.SDK_INT; + + if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) + return context.getApplicationInfo ().nativeLibraryDir; + + return context.getApplicationInfo ().dataDir + "/lib"; + } + @Override public int onStartCommand (Intent intent, int flags, int startId) @@ -178,23 +180,6 @@ public final class EmacsService extends Service } } - private String - getLibraryDirectory () - { - int apiLevel; - Context context; - - context = getApplicationContext (); - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - @Override public void onCreate () @@ -219,7 +204,7 @@ public final class EmacsService extends Service /* Configure Emacs with the asset manager and other necessary parameters. */ filesDir = app_context.getFilesDir ().getCanonicalPath (); - libDir = getLibraryDirectory (); + libDir = getLibraryDirectory (this); cacheDir = app_context.getCacheDir ().getCanonicalPath (); /* Now provide this application's apk file, so a recursive diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 617836d8811..aefc79c4fb7 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -20,7 +20,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; import android.content.Context; -import android.content.res.ColorStateList; import android.text.InputType; @@ -116,6 +115,7 @@ public final class EmacsView extends ViewGroup super (EmacsService.SERVICE); Object tem; + Context context; this.window = window; this.damageRegion = new Region (); @@ -133,7 +133,8 @@ public final class EmacsView extends ViewGroup setDefaultFocusHighlightEnabled (false); /* Obtain the input method manager. */ - tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE); + context = getContext (); + tem = context.getSystemService (Context.INPUT_METHOD_SERVICE); imManager = (InputMethodManager) tem; } @@ -281,7 +282,6 @@ public final class EmacsView extends ViewGroup int count, i; View child; Rect windowRect; - int wantedWidth, wantedHeight; count = getChildCount (); @@ -422,7 +422,6 @@ public final class EmacsView extends ViewGroup } } - /* The following two functions must not be called if the view has no parent, or is parented to an activity. */ diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index ea4cf48090d..ffc7476f010 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -31,19 +31,16 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.Canvas; import android.graphics.Bitmap; -import android.graphics.Point; import android.graphics.PixelFormat; import android.view.View; import android.view.ViewManager; -import android.view.ViewGroup; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.InputDevice; import android.view.WindowManager; -import android.content.Intent; import android.util.Log; import android.os.Build; @@ -87,7 +84,7 @@ public final class EmacsWindow extends EmacsHandleObject public EmacsWindow parent; /* List of all children in stacking order. This must be kept - consistent! */ + consistent with their Z order! */ public ArrayList children; /* Map between pointer identifiers and last known position. Used to @@ -105,9 +102,8 @@ public final class EmacsWindow extends EmacsHandleObject last button press or release event. */ public int lastButtonState, lastModifiers; - /* Whether or not the window is mapped, and whether or not it is - deiconified. */ - private boolean isMapped, isIconified; + /* Whether or not the window is mapped. */ + private boolean isMapped; /* Whether or not to ask for focus upon being mapped, and whether or not the window should be focusable. */ @@ -122,7 +118,8 @@ public final class EmacsWindow extends EmacsHandleObject private WindowManager windowManager; /* The time of the last KEYCODE_VOLUME_DOWN release. This is used - to quit Emacs. */ + to quit Emacs upon two rapid clicks of the volume down + button. */ private long lastVolumeButtonRelease; /* Linked list of character strings which were recently sent as @@ -1103,14 +1100,12 @@ public final class EmacsWindow extends EmacsHandleObject public void noticeIconified () { - isIconified = true; EmacsNative.sendIconified (this.handle); } public void noticeDeiconified () { - isIconified = false; EmacsNative.sendDeiconified (this.handle); } diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 1548bf28087..30f29250970 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -20,7 +20,6 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import android.content.Intent; @@ -74,8 +73,8 @@ public final class EmacsWindowAttachmentManager public EmacsWindowAttachmentManager () { - consumers = new LinkedList (); - windows = new LinkedList (); + consumers = new ArrayList (); + windows = new ArrayList (); } public void -- cgit v1.3 From da660a1ffa3218f8e6ec4dfd5422ca6c1ded38ae Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 17 Mar 2023 10:38:09 +0800 Subject: Update Android port * java/org/gnu/emacs/EmacsView.java (cancelPopupMenu): Dismiss context menu correctly. (isOpaque): New function. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java: Make consumer list public. --- java/org/gnu/emacs/EmacsView.java | 21 +++++++++++++++++++++ .../org/gnu/emacs/EmacsWindowAttachmentManager.java | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 878ef2f3fbf..681da98fa16 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -514,6 +514,17 @@ public final class EmacsView extends ViewGroup contextMenu = null; popupActive = false; + + /* It is not possible to know with 100% certainty which activity + is currently displaying the context menu. Loop through each + activity and call `closeContextMenu' instead. */ + + for (EmacsWindowAttachmentManager.WindowConsumer consumer + : EmacsWindowAttachmentManager.MANAGER.consumers) + { + if (consumer instanceof EmacsActivity) + ((EmacsActivity) consumer).closeContextMenu (); + } } @Override @@ -646,6 +657,16 @@ public final class EmacsView extends ViewGroup return isCurrentlyTextEditor; } + @Override + public boolean + isOpaque () + { + /* Returning true here allows the system to not draw the contents + of windows underneath this view, thereby improving + performance. */ + return true; + } + public synchronized void setICMode (int icMode) { diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 30f29250970..c0197ab802c 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -67,7 +67,7 @@ public final class EmacsWindowAttachmentManager public void destroy (); }; - private List consumers; + public List consumers; public List windows; public -- cgit v1.3 From 634e3fcc20ea9fa5b1af59286f4b846a351f52c8 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 18 Mar 2023 10:54:26 +0800 Subject: Update Android port * java/org/gnu/emacs/EmacsView.java (EmacsView) (prepareForLayout): New function. Call this prior to mapping the view. (onGlobalLayout): New function. Register as global layout listener. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) (notifyContentRectPosition): New function. Use specified xPosition and yPosition when reporting the offsets of children of the root window. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (registerWindow): Specify activity launch bounds if necessary. * src/androidterm.c (handle_one_android_event): Send MOVE_FRAME_EVENT where necessary. --- java/org/gnu/emacs/EmacsView.java | 30 ++++- java/org/gnu/emacs/EmacsWindow.java | 145 ++++++++++++--------- .../gnu/emacs/EmacsWindowAttachmentManager.java | 16 ++- src/androidterm.c | 32 +++++ 4 files changed, 160 insertions(+), 63 deletions(-) (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 681da98fa16..88d17a255a2 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -28,6 +28,7 @@ import android.view.View; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -51,6 +52,7 @@ import android.util.Log; It is also a ViewGroup, as it also lays out children. */ public final class EmacsView extends ViewGroup + implements ViewTreeObserver.OnGlobalLayoutListener { public static final String TAG = "EmacsView"; @@ -136,6 +138,9 @@ public final class EmacsView extends ViewGroup context = getContext (); tem = context.getSystemService (Context.INPUT_METHOD_SERVICE); imManager = (InputMethodManager) tem; + + /* Add this view as its own global layout listener. */ + getViewTreeObserver ().addOnGlobalLayoutListener (this); } private void @@ -238,6 +243,13 @@ public final class EmacsView extends ViewGroup return canvas; } + public void + prepareForLayout (int wantedWidth, int wantedHeight) + { + measuredWidth = wantedWidth; + measuredHeight = wantedWidth; + } + @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) @@ -562,8 +574,7 @@ public final class EmacsView extends ViewGroup bitmapDirty = true; /* Now expose the view contents again. */ - EmacsNative.sendExpose (this.window.handle, 0, 0, - measuredWidth, measuredHeight); + EmacsNative.sendExpose (this.window.handle, 0, 0, 0, 0); super.onAttachedToWindow (); } @@ -678,4 +689,19 @@ public final class EmacsView extends ViewGroup { return icMode; } + + @Override + public void + onGlobalLayout () + { + int[] locations; + + /* Get the absolute offset of this view and specify its left and + top position in subsequent ConfigureNotify events. */ + + locations = new int[2]; + getLocationInWindow (locations); + window.notifyContentRectPosition (locations[0], + locations[1]); + } }; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index a8d1beedef7..c14bf16b96e 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -133,6 +133,9 @@ public final class EmacsWindow extends EmacsHandleObject creating new bitmaps. */ public volatile int background; + /* The position of this window relative to the root window. */ + public int xPosition, yPosition; + public EmacsWindow (short handle, final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -192,18 +195,14 @@ public final class EmacsWindow extends EmacsHandleObject background = pixel; } - public Rect + public synchronized Rect getGeometry () { - synchronized (this) - { - /* Huh, this is it. */ - return rect; - } + return new Rect (rect); } @Override - public void + public synchronized void destroyHandle () throws IllegalStateException { if (parent != null) @@ -258,22 +257,28 @@ public final class EmacsWindow extends EmacsHandleObject return attached; } - public long + public synchronized long viewLayout (int left, int top, int right, int bottom) { int rectWidth, rectHeight; - synchronized (this) - { - rect.left = left; - rect.top = top; - rect.right = right; - rect.bottom = bottom; - } + rect.left = left; + rect.top = top; + rect.right = right; + rect.bottom = bottom; rectWidth = right - left; rectHeight = bottom - top; + /* If parent is null, use xPosition and yPosition instead of the + geometry rectangle positions. */ + + if (parent == null) + { + left = xPosition; + top = yPosition; + } + return EmacsNative.sendConfigureNotify (this.handle, System.currentTimeMillis (), left, top, rectWidth, @@ -283,8 +288,6 @@ public final class EmacsWindow extends EmacsHandleObject public void requestViewLayout () { - /* This is necessary because otherwise subsequent drawing on the - Emacs thread may be lost. */ view.explicitlyDirtyBitmap (); EmacsService.SERVICE.runOnUiThread (new Runnable () { @@ -302,35 +305,29 @@ public final class EmacsWindow extends EmacsHandleObject }); } - public void + public synchronized void resizeWindow (int width, int height) { - synchronized (this) - { - rect.right = rect.left + width; - rect.bottom = rect.top + height; + rect.right = rect.left + width; + rect.bottom = rect.top + height; - requestViewLayout (); - } + requestViewLayout (); } - public void + public synchronized void moveWindow (int x, int y) { int width, height; - synchronized (this) - { - width = rect.width (); - height = rect.height (); + width = rect.width (); + height = rect.height (); - rect.left = x; - rect.top = y; - rect.right = x + width; - rect.bottom = y + height; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; - requestViewLayout (); - } + requestViewLayout (); } private WindowManager.LayoutParams @@ -366,13 +363,17 @@ public final class EmacsWindow extends EmacsHandleObject return EmacsService.SERVICE; } - public void + public synchronized void mapWindow () { + final int width, height; + if (isMapped) return; isMapped = true; + width = rect.width (); + height = rect.height (); if (parent == null) { @@ -424,6 +425,7 @@ public final class EmacsWindow extends EmacsHandleObject /* Attach the view. */ try { + view.prepareForLayout (width, height); windowManager.addView (view, params); /* Record the window manager being used in the @@ -448,10 +450,14 @@ public final class EmacsWindow extends EmacsHandleObject public void run () { + /* Prior to mapping the view, set its measuredWidth and + measuredHeight to some reasonable value, in order to + avoid excessive bitmap dirtying. */ + + view.prepareForLayout (width, height); view.setVisibility (View.VISIBLE); if (!getDontFocusOnMap ()) - /* Eventually this should check no-focus-on-map. */ view.requestFocus (); } }); @@ -512,12 +518,9 @@ public final class EmacsWindow extends EmacsHandleObject public void clearWindow () { - synchronized (this) - { - EmacsService.SERVICE.fillRectangle (this, scratchGC, - 0, 0, rect.width (), - rect.height ()); - } + EmacsService.SERVICE.fillRectangle (this, scratchGC, + 0, 0, rect.width (), + rect.height ()); } public void @@ -1001,7 +1004,7 @@ public final class EmacsWindow extends EmacsHandleObject return false; } - public void + public synchronized void reparentTo (final EmacsWindow otherWindow, int x, int y) { int width, height; @@ -1017,15 +1020,12 @@ public final class EmacsWindow extends EmacsHandleObject parent = otherWindow; /* Move this window to the new location. */ - synchronized (this) - { - width = rect.width (); - height = rect.height (); - rect.left = x; - rect.top = y; - rect.right = x + width; - rect.bottom = y + height; - } + width = rect.width (); + height = rect.height (); + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; /* Now do the work necessary on the UI thread to reparent the window. */ @@ -1081,7 +1081,7 @@ public final class EmacsWindow extends EmacsHandleObject }); } - public void + public synchronized void raise () { /* This does nothing here. */ @@ -1103,7 +1103,7 @@ public final class EmacsWindow extends EmacsHandleObject }); } - public void + public synchronized void lower () { /* This does nothing here. */ @@ -1125,17 +1125,15 @@ public final class EmacsWindow extends EmacsHandleObject }); } - public int[] + public synchronized int[] getWindowGeometry () { int[] array; - Rect rect; array = new int[4]; - rect = getGeometry (); - array[0] = rect.left; - array[1] = rect.top; + array[0] = parent != null ? rect.left : xPosition; + array[1] = parent != null ? rect.top : yPosition; array[2] = rect.width (); array[3] = rect.height (); @@ -1272,4 +1270,31 @@ public final class EmacsWindow extends EmacsHandleObject } }); } + + public synchronized void + notifyContentRectPosition (int xPosition, int yPosition) + { + Rect geometry; + + /* Ignore these notifications if not a child of the root + window. */ + if (parent != null) + return; + + /* xPosition and yPosition are the position of this window + relative to the screen. Set them and request a ConfigureNotify + event. */ + + if (this.xPosition != xPosition + || this.yPosition != yPosition) + { + this.xPosition = xPosition; + this.yPosition = yPosition; + + EmacsNative.sendConfigureNotify (this.handle, + System.currentTimeMillis (), + xPosition, yPosition, + rect.width (), rect.height ()); + } + } }; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index c0197ab802c..4fda48616f0 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -22,7 +22,9 @@ package org.gnu.emacs; import java.util.ArrayList; import java.util.List; +import android.app.ActivityOptions; import android.content.Intent; +import android.os.Build; import android.util.Log; /* Code to paper over the differences in lifecycles between @@ -102,6 +104,7 @@ public final class EmacsWindowAttachmentManager registerWindow (EmacsWindow window) { Intent intent; + ActivityOptions options; Log.d (TAG, "registerWindow (maybe): " + window); @@ -128,7 +131,18 @@ public final class EmacsWindowAttachmentManager intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - EmacsService.SERVICE.startActivity (intent); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + EmacsService.SERVICE.startActivity (intent); + else + { + /* Specify the desired window size. */ + options = ActivityOptions.makeBasic (); + options.setLaunchBounds (window.getGeometry ()); + EmacsService.SERVICE.startActivity (intent, + options.toBundle ()); + } + Log.d (TAG, "registerWindow: startActivity"); } diff --git a/src/androidterm.c b/src/androidterm.c index 1cf9bc4afde..825d6da60fa 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -787,6 +787,33 @@ handle_one_android_event (struct android_display_info *dpyinfo, cancel_mouse_face (f); } + /* Now change the left and top position of this window. */ + + { + int old_left = f->left_pos; + int old_top = f->top_pos; + Lisp_Object frame; + + XSETFRAME (frame, f); + + { + android_window root; + unsigned int dummy_uint; + + android_get_geometry (FRAME_ANDROID_WINDOW (f), + &root, &f->left_pos, &f->top_pos, + &dummy_uint, &dummy_uint, + &dummy_uint); + } + + if (!FRAME_TOOLTIP_P (f) + && (old_left != f->left_pos || old_top != f->top_pos)) + { + inev.ie.kind = MOVE_FRAME_EVENT; + XSETFRAME (inev.ie.frame_or_window, f); + } + } + goto OTHER; case ANDROID_KEY_PRESS: @@ -1100,6 +1127,11 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!FRAME_GARBAGED_P (f)) { + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "expose: %d %d %d %d\n", + event->xexpose.x, event->xexpose.y, + event->xexpose.width, + event->xexpose.height); expose_frame (f, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height); show_back_buffer (f); -- cgit v1.3 From 377a3ebbb55a9b944551394e00d24c445e3ff4a1 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 16 Jun 2023 12:59:44 +0800 Subject: Update Android port * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): * java/org/gnu/emacs/EmacsApplication.java (findDumpFile): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (addSubmenu, display): * java/org/gnu/emacs/EmacsDocumentsProvider.java (getNotificationUri, queryChildDocuments, deleteDocument): * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): * java/org/gnu/emacs/EmacsFillRectangle.java (perform): * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog) (checkReadableOrCopy): * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver): * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): * java/org/gnu/emacs/EmacsView.java (EmacsView): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyUp): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Remove various unused arguments and variables, dead stores, and make minor cleanups and performance improvements. * src/androidmenu.c (FIND_METHOD_STATIC, android_menu_show): Adjust accordingly. --- java/org/gnu/emacs/EmacsActivity.java | 2 +- java/org/gnu/emacs/EmacsApplication.java | 3 ++ java/org/gnu/emacs/EmacsContextMenu.java | 9 +++-- java/org/gnu/emacs/EmacsDocumentsProvider.java | 17 ++++++---- java/org/gnu/emacs/EmacsDrawRectangle.java | 2 -- java/org/gnu/emacs/EmacsFillRectangle.java | 2 +- java/org/gnu/emacs/EmacsOpenActivity.java | 38 +++++++++++++++++----- java/org/gnu/emacs/EmacsSdk7FontDriver.java | 4 +++ java/org/gnu/emacs/EmacsSurfaceView.java | 5 --- java/org/gnu/emacs/EmacsView.java | 4 --- java/org/gnu/emacs/EmacsWindow.java | 24 ++++++-------- .../gnu/emacs/EmacsWindowAttachmentManager.java | 7 +++- src/androidmenu.c | 16 +++------ 13 files changed, 73 insertions(+), 60 deletions(-) (limited to 'java/org/gnu/emacs/EmacsWindowAttachmentManager.java') diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 7ba268ba42d..fa9bff39bb0 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -55,7 +55,7 @@ public class EmacsActivity extends Activity private FrameLayout layout; /* List of activities with focus. */ - public static List focusedActivities; + public static final List focusedActivities; /* The last activity to have been focused. */ public static EmacsActivity lastFocusedActivity; diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java index 10099721744..d8b77acdf9e 100644 --- a/java/org/gnu/emacs/EmacsApplication.java +++ b/java/org/gnu/emacs/EmacsApplication.java @@ -60,6 +60,9 @@ public final class EmacsApplication extends Application } }); + if (allFiles == null) + return; + /* Now try to find the right dump file. */ for (i = 0; i < allFiles.length; ++i) { diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index d69d0263b93..eb83016c849 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -131,20 +131,18 @@ public final class EmacsContextMenu }; public List menuItems; - public String title; private EmacsContextMenu parent; /* Create a context menu with no items inside and the title TITLE, which may be NULL. */ public static EmacsContextMenu - createContextMenu (String title) + createContextMenu () { EmacsContextMenu menu; menu = new EmacsContextMenu (); menu.menuItems = new ArrayList (); - menu.title = title; return menu; } @@ -197,7 +195,7 @@ public final class EmacsContextMenu item name. */ public EmacsContextMenu - addSubmenu (String itemName, String title, String tooltip) + addSubmenu (String itemName, String tooltip) { EmacsContextMenu submenu; Item item; @@ -206,7 +204,7 @@ public final class EmacsContextMenu item.itemID = 0; item.itemName = itemName; item.tooltip = tooltip; - item.subMenu = createContextMenu (title); + item.subMenu = createContextMenu (); item.subMenu.parent = this; menuItems.add (item); @@ -334,6 +332,7 @@ public final class EmacsContextMenu final EmacsHolder rc; rc = new EmacsHolder (); + rc.thing = false; runnable = new Runnable () { @Override diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index b4ac4624829..96dc2bc6e14 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -131,9 +131,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider getNotificationUri (File file) { Uri updatedUri; - Context context; - context = getContext (); updatedUri = buildChildDocumentsUri ("org.gnu.emacs", file.getAbsolutePath ()); @@ -294,6 +292,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider { MatrixCursor result; File directory; + File[] files; Context context; if (projection == null) @@ -305,9 +304,15 @@ public final class EmacsDocumentsProvider extends DocumentsProvider requested. */ directory = new File (parentDocumentId); - /* Now add each child. */ - for (File child : directory.listFiles ()) - queryDocument1 (result, child); + /* Look up each child. */ + files = directory.listFiles (); + + if (files != null) + { + /* Now add each child. */ + for (File child : files) + queryDocument1 (result, child); + } context = getContext (); @@ -406,12 +411,10 @@ public final class EmacsDocumentsProvider extends DocumentsProvider { File file, parent; File[] children; - Context context; /* Java makes recursively deleting a file hard. File name encoding issues also prevent easily calling into C... */ - context = getContext (); file = new File (documentId); parent = file.getParentFile (); diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index 3bd5779c54e..b54b031b56b 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -36,7 +36,6 @@ public final class EmacsDrawRectangle Paint maskPaint, paint; Canvas maskCanvas; Bitmap maskBitmap; - Rect rect; Rect maskRect, dstRect; Canvas canvas; Bitmap clipBitmap; @@ -52,7 +51,6 @@ public final class EmacsDrawRectangle paint = gc.gcPaint; paint.setStyle (Paint.Style.STROKE); - rect = new Rect (x, y, x + width, y + height); if (gc.clip_mask == null) /* Use canvas.drawRect with a RectF. That seems to reliably diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java index 4a0478b446f..461fd3c639c 100644 --- a/java/org/gnu/emacs/EmacsFillRectangle.java +++ b/java/org/gnu/emacs/EmacsFillRectangle.java @@ -61,6 +61,7 @@ public final class EmacsFillRectangle /* Drawing with a clip mask involves calculating the intersection of the clip mask with the dst rect, and extrapolating the corresponding part of the src rect. */ + clipBitmap = gc.clip_mask.bitmap; dstRect = new Rect (x, y, x + width, y + height); maskRect = new Rect (gc.clip_x_origin, @@ -69,7 +70,6 @@ public final class EmacsFillRectangle + clipBitmap.getWidth ()), (gc.clip_y_origin + clipBitmap.getHeight ())); - clipBitmap = gc.clip_mask.bitmap; if (!maskRect.setIntersect (dstRect, maskRect)) /* There is no intersection between the clip mask and the diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 6af2b2d2e94..9411f85d434 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -146,7 +146,7 @@ public final class EmacsOpenActivity extends Activity FileReader reader; char[] buffer; int rc; - String what; + StringBuilder builder; /* Because the ProcessBuilder functions necessary to redirect process output are not implemented on Android 7 and earlier, @@ -160,7 +160,8 @@ public final class EmacsOpenActivity extends Activity cache = getCacheDir (); file = new File (cache, "emacsclient.log"); - what = ""; + builder = new StringBuilder (); + reader = null; try { @@ -168,13 +169,25 @@ public final class EmacsOpenActivity extends Activity buffer = new char[2048]; while ((rc = reader.read (buffer, 0, 2048)) != -1) - what += String.valueOf (buffer, 0, 2048); + builder.append (buffer, 0, rc); reader.close (); - return what; + return builder.toString (); } catch (IOException exception) { + /* Close the reader if it's already been opened. */ + + try + { + if (reader != null) + reader.close (); + } + catch (IOException e) + { + /* Not sure what to do here. */ + } + return ("Couldn't read emacsclient.log: " + exception.toString ()); } @@ -248,11 +261,16 @@ public final class EmacsOpenActivity extends Activity /* inFile is now the file being written to. */ inFile = new File (getCacheDir (), inFile.getName ()); buffer = new byte[4098]; - outStream = new FileOutputStream (inFile); - stream = new FileInputStream (fd.getFileDescriptor ()); + + /* Initialize both streams to NULL. */ + outStream = null; + stream = null; try { + outStream = new FileOutputStream (inFile); + stream = new FileInputStream (fd.getFileDescriptor ()); + while ((read = stream.read (buffer)) >= 0) outStream.write (buffer, 0, read); } @@ -263,8 +281,12 @@ public final class EmacsOpenActivity extends Activity Keep in mind that execution is transferred to ``finally'' even if an exception happens inside the while loop above. */ - stream.close (); - outStream.close (); + + if (stream != null) + stream.close (); + + if (outStream != null) + outStream.close (); } return inFile.getCanonicalPath (); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 6df102f18a2..9122b46458a 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -252,6 +252,10 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver systemFontsDirectory = new File ("/system/fonts"); fontFamilyList = systemFontsDirectory.list (); + + /* If that returned null, replace it with an empty array. */ + fontFamilyList = new String[0]; + typefaceList = new Sdk7Typeface[fontFamilyList.length + 3]; /* It would be nice to avoid opening each and every font upon diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 738b1a99eef..5b3e05eb9f4 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -39,10 +39,6 @@ public final class EmacsSurfaceView extends View { private static final String TAG = "EmacsSurfaceView"; - /* The EmacsView representing the window that this surface is - displaying. */ - private EmacsView view; - /* The complete buffer contents at the time of the last draw. */ private Bitmap frontBuffer; @@ -71,7 +67,6 @@ public final class EmacsSurfaceView extends View { super (view.getContext ()); - this.view = view; this.bitmap = new WeakReference (null); } diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index aba1184b0c2..0cabefdf385 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -68,9 +68,6 @@ public final class EmacsView extends ViewGroup /* The damage region. */ public Region damageRegion; - /* The paint. */ - public Paint paint; - /* The associated surface view. */ private EmacsSurfaceView surfaceView; @@ -128,7 +125,6 @@ public final class EmacsView extends ViewGroup this.window = window; this.damageRegion = new Region (); - this.paint = new Paint (); setFocusable (true); setFocusableInTouchMode (true); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 68a18ec2aa7..739a1f43b7d 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -103,11 +103,10 @@ public final class EmacsWindow extends EmacsHandleObject public int lastButtonState, lastModifiers; /* Whether or not the window is mapped. */ - private boolean isMapped; + private volatile boolean isMapped; - /* Whether or not to ask for focus upon being mapped, and whether or - not the window should be focusable. */ - private boolean dontFocusOnMap, dontAcceptFocus; + /* Whether or not to ask for focus upon being mapped. */ + private boolean dontFocusOnMap; /* Whether or not the window is override-redirect. An override-redirect window always has its own system window. */ @@ -464,7 +463,7 @@ public final class EmacsWindow extends EmacsHandleObject } } - public void + public synchronized void unmapWindow () { if (!isMapped) @@ -618,7 +617,7 @@ public final class EmacsWindow extends EmacsHandleObject onKeyUp (int keyCode, KeyEvent event) { int state, state_1; - long time, serial; + long time; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) state = event.getModifiers (); @@ -645,12 +644,11 @@ public final class EmacsWindow extends EmacsHandleObject state_1 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); - serial - = EmacsNative.sendKeyRelease (this.handle, - event.getEventTime (), - state, keyCode, - getEventUnicodeChar (event, - state_1)); + EmacsNative.sendKeyRelease (this.handle, + event.getEventTime (), + state, keyCode, + getEventUnicodeChar (event, + state_1)); lastModifiers = state; if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) @@ -1155,8 +1153,6 @@ public final class EmacsWindow extends EmacsHandleObject public synchronized void setDontAcceptFocus (final boolean dontAcceptFocus) { - this.dontAcceptFocus = dontAcceptFocus; - /* Update the view's focus state. */ EmacsService.SERVICE.runOnUiThread (new Runnable () { @Override diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 4fda48616f0..bc96de7fe1a 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -53,9 +53,11 @@ import android.util.Log; public final class EmacsWindowAttachmentManager { - public static EmacsWindowAttachmentManager MANAGER; private final static String TAG = "EmacsWindowAttachmentManager"; + /* The single window attachment manager ``object''. */ + public static final EmacsWindowAttachmentManager MANAGER; + static { MANAGER = new EmacsWindowAttachmentManager (); @@ -69,7 +71,10 @@ public final class EmacsWindowAttachmentManager public void destroy (); }; + /* List of currently attached window consumers. */ public List consumers; + + /* List of currently attached windows. */ public List windows; public diff --git a/src/androidmenu.c b/src/androidmenu.c index f74e7ca6d99..75710486c75 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -101,13 +101,12 @@ 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;"); + "()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;)" + "Ljava/lang/String;)" "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;"); @@ -271,18 +270,11 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, 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); + method); /* Push the second local frame for temporaries. */ count1 = SPECPDL_INDEX (); @@ -391,7 +383,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, = (*android_java_env)->CallObjectMethod (android_java_env, current_context_menu, menu_class.add_submenu, - title_string, NULL, + title_string, help_string); android_exception_check (); -- cgit v1.3