From: Po Lu Date: Sat, 21 Jan 2023 12:03:37 +0000 (+0800) Subject: Update Android port X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=aaacf24ca25fc284038ec9f17be358067309a8cf;p=emacs.git Update Android port * doc/emacs/android.texi (Android File System): Document that ls-lisp is now used by default. * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Name the thread something meaningful. * lisp/loadup.el (featurep): Load ls-lisp on Android. * lisp/ls-lisp.el (ls-lisp-use-insert-directory-program): Default to off on Android. * src/android.c (android_is_directory): New fucntion. (android_fstatat): Handle directories created by `android_opendir'. (android_open): Return meaningful file mode. (struct android_dir): New fields `next', `asset_file' and `fd'. (android_opendir): Populate those fields. (android_dirfd): New function. (android_closedir): Close file descriptor if set. (android_lookup_asset_directory_fd): New function. * src/android.h: Update prototypes. * src/androidfont.c (androidfont_check_init): New function. (androidfont_list, androidfont_match, androidfont_draw) (androidfont_open_font, androidfont_close_font) (androidfont_has_char, androidfont_encode_char) (androidfont_text_extents, androidfont_list_family): Initialize font driver if necessary. (init_androidfont): Don't initialize Java font if necessary. * src/dired.c (open_directory): Return android_dirfd if appropriate. (directory_files_internal, file_name_completion_dirp): Implement correctly for Android. * src/fileio.c (check_mutable_filename): New function. (Fcopy_file, Fdelete_directory_internal, Fdelete_file) (Frename_file, Fadd_name_to_file, Fmake_symbolic_link) (Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p) (Fverify_visited_file_modtime, Fset_visited_file_modtime): Check that files being written to do not lie in /assets. * src/sfntfont-android.c (GET_SCANLINE_BUFFER) (sfntfont_android_u255to256, sfntfont_android_over_8888_1) (sfntfont_android_over_8888, sfntfont_android_composite_bitmap): Optimize on 64-bit ARM devices. (sfntfont_android_put_glyphs): Optimize away memset if background need not be filled. --- diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index ba5d2ca3e09..223e9dbf8ef 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -99,21 +99,26 @@ this varies by device. Emacs exposes a special directory on Android systems: the name of the directory is @file{/assets}, and it contains the @file{etc}, @file{lisp} and @file{info} directories which are normally installed -in @file{/usr/share/emacs} directory on GNU and Unix systems. +in @file{/usr/share/emacs} directory on GNU and Unix systems. On +Android systems, the Lisp emulation of @command{ls} (@pxref{ls in +Lisp}) is also enabled by default, as the @command{ls} binary which +comes with the system varies by manufacturer and usually does not +support all of the features required by Emacs. One copy of +@command{ls} shipped with some Android devices is even known to lack +support for the @code{-l} flag. @cindex limitations of the /assets directory -This directory exists because Android does not extract the contents of -application packages on to the file system while unpacking them, but -instead requires programs like Emacs to access its contents using a -special ``asset manager'' interface. Here are the peculiarities that -result from such an implementation: + This directory exists because Android does not extract the contents +of application packages on to the file system while unpacking them, +but instead requires programs like Emacs to access its contents using +a special ``asset manager'' interface. Here are the peculiarities +that result from such an implementation: @itemize @bullet @item Subprocesses (such as @command{ls}) can not run from the -@file{/assets} directory, so Dired, and functions such as -@code{list-directory} do not work. +@file{/assets} directory. @item There are no @file{.} and @file{..} directories inside the diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index f9bc132f354..21d8612703a 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -29,6 +29,7 @@ public class EmacsThread extends Thread public EmacsThread (EmacsService service, boolean startDashQ) { + super ("Emacs main thread"); this.startDashQ = startDashQ; } diff --git a/lisp/loadup.el b/lisp/loadup.el index 3b48d5fe1b5..b9ae8212d2d 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -312,6 +312,7 @@ (if (featurep 'android) (progn + (load "ls-lisp") (load "term/common-win") (load "term/android-win"))) diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el index 81ff14932c6..8f5948cefb9 100644 --- a/lisp/ls-lisp.el +++ b/lisp/ls-lisp.el @@ -184,7 +184,7 @@ if emulation is GNU then default is `(links uid gid)'." :group 'ls-lisp) (defcustom ls-lisp-use-insert-directory-program - (not (memq system-type '(ms-dos windows-nt))) + (not (memq system-type '(ms-dos windows-nt android))) "Non-nil causes ls-lisp to revert back to using `insert-directory-program'. This is useful on platforms where ls-lisp is dumped into Emacs, such as Microsoft Windows, but you would still like to use a program to list diff --git a/src/android.c b/src/android.c index 3f6e6f4576b..a51d0518484 100644 --- a/src/android.c +++ b/src/android.c @@ -852,6 +852,26 @@ android_scan_directory_tree (char *file, size_t *limit_return) return NULL; } +/* Return whether or not the directory tree entry DIR is a + directory. + + DIR should be a value returned by + `android_scan_directory_tree'. */ + +static bool +android_is_directory (const char *dir) +{ + /* If the directory is the directory tree, then it is a + directory. */ + if (dir == directory_tree + 5) + return true; + + /* Otherwise, look 5 bytes behind. If it is `/', then it is a + directory. */ + return (dir - 6 >= directory_tree + && *(dir - 6) == '/'); +} + /* Intercept USER_FULL_NAME and return something that makes sense if @@ -899,8 +919,15 @@ android_fstat (int fd, struct stat *statb) return fstat (fd, statb); } +static int android_lookup_asset_directory_fd (int, + const char *restrict *, + const char *restrict); + /* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an - asset, find the information for the corresponding asset. */ + asset, find the information for the corresponding asset, and if + dirfd is an offset into directory_tree as returned by + `android_dirfd', find the information within the corresponding + directory tree entry. */ int android_fstatat (int dirfd, const char *restrict pathname, @@ -908,11 +935,40 @@ android_fstatat (int dirfd, const char *restrict pathname, { AAsset *asset_desc; const char *asset; + const char *asset_dir; + + /* Look up whether or not DIRFD belongs to an open struct + android_dir. */ + + if (dirfd != AT_FDCWD) + dirfd + = android_lookup_asset_directory_fd (dirfd, &pathname, + pathname); if (dirfd == AT_FDCWD && asset_manager && (asset = android_get_asset_name (pathname))) { + /* Look up whether or not PATHNAME happens to be a + directory. */ + asset_dir = android_scan_directory_tree ((char *) asset, + NULL); + + if (!asset_dir) + { + errno = ENOENT; + return -1; + } + + if (android_is_directory (asset_dir)) + { + memset (statbuf, 0, sizeof *statbuf); + + /* Fill in the stat buffer. */ + statbuf->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; + return 0; + } + /* AASSET_MODE_STREAMING is fastest here. */ asset_desc = AAssetManager_open (asset_manager, asset, AASSET_MODE_STREAMING); @@ -923,7 +979,7 @@ android_fstatat (int dirfd, const char *restrict pathname, memset (statbuf, 0, sizeof *statbuf); /* Fill in the stat buffer. */ - statbuf->st_mode = S_IFREG; + statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; statbuf->st_size = AAsset_getLength (asset_desc); /* Close the asset. */ @@ -1118,7 +1174,8 @@ android_open (const char *filename, int oflag, int mode) /* Fill in some information that will be reported to callers of android_fstat, among others. */ - android_table[fd].statb.st_mode = S_IFREG; + android_table[fd].statb.st_mode + = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;; /* Owned by root. */ android_table[fd].statb.st_uid = 0; @@ -4023,8 +4080,21 @@ struct android_dir /* And the end of the files in asset_dir. */ char *asset_limit; + + /* The next struct android_dir. */ + struct android_dir *next; + + /* Path to the directory relative to /. */ + char *asset_file; + + /* File descriptor used when asset_dir is set. */ + int fd; }; +/* List of all struct android_dir's corresponding to an asset + directory that are currently open. */ +static struct android_dir *android_dirs; + /* Like opendir. However, return an asset directory if NAME points to an asset. */ @@ -4034,7 +4104,7 @@ android_opendir (const char *name) struct android_dir *dir; char *asset_dir; const char *asset_name; - size_t limit; + size_t limit, length; asset_name = android_get_asset_name (name); @@ -4052,10 +4122,19 @@ android_opendir (const char *name) return NULL; } + length = strlen (name); + dir = xmalloc (sizeof *dir); dir->dir = NULL; dir->asset_dir = asset_dir; dir->asset_limit = (char *) directory_tree + limit; + dir->fd = -1; + dir->asset_file = xzalloc (length + 2); + + /* Make sure dir->asset_file is terminated with /. */ + strcpy (dir->asset_file, name); + if (dir->asset_file[length - 1] != '/') + dir->asset_file[length] = '/'; /* Make sure dir->asset_limit is within bounds. It is a limit, and as such can be exactly one byte past directory_tree. */ @@ -4069,6 +4148,9 @@ android_opendir (const char *name) errno = EACCES; } + dir->next = android_dirs; + android_dirs = dir; + return dir; } @@ -4086,6 +4168,26 @@ android_opendir (const char *name) return dir; } +/* Like dirfd. However, value is not a real directory file descriptor + if DIR is an asset directory. */ + +int +android_dirfd (struct android_dir *dirp) +{ + int fd; + + if (dirp->dir) + return dirfd (dirp->dir); + else if (dirp->fd != -1) + return dirp->fd; + + fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + + /* Record this file descriptor in dirp. */ + dirp->fd = fd; + return fd; +} + /* Like readdir, except it understands asset directories. */ struct dirent * @@ -4152,8 +4254,29 @@ android_readdir (struct android_dir *dir) void android_closedir (struct android_dir *dir) { + struct android_dir **next, *tem; + if (dir->dir) closedir (dir->dir); + else + { + if (dir->fd != -1) + close (dir->fd); + + /* Unlink this directory from the list of all asset manager + directories. */ + + for (next = &android_dirs; (tem = *next);) + { + if (tem == dir) + *next = dir->next; + else + next = &(*next)->next; + } + + /* Free the asset file name. */ + xfree (dir->asset_file); + } /* There is no need to close anything else, as the directory tree lies in statically allocated memory. */ @@ -4161,6 +4284,43 @@ android_closedir (struct android_dir *dir) xfree (dir); } +/* Subroutine used by android_fstatat. If DIRFD belongs to an open + asset directory and FILE is a relative file name, then return + AT_FDCWD and the absolute file name of the directory prepended to + FILE in *PATHNAME. Else, return DIRFD. */ + +int +android_lookup_asset_directory_fd (int dirfd, + const char *restrict *pathname, + const char *restrict file) +{ + struct android_dir *dir; + static char *name; + + if (file[0] == '/') + return dirfd; + + for (dir = android_dirs; dir; dir = dir->next) + { + if (dir->fd == dirfd && dirfd != -1) + { + if (name) + xfree (name); + + /* dir->asset_file is always separator terminated. */ + name = xzalloc (strlen (dir->asset_file) + + strlen (file) + 1); + strcpy (name, dir->asset_file); + strcpy (name + strlen (dir->asset_file), + file); + *pathname = name; + return AT_FDCWD; + } + } + + return dirfd; +} + /* emacs_abort implementation for Android. This logs a stack diff --git a/src/android.h b/src/android.h index 52b3412fb0e..eee3a7a6498 100644 --- a/src/android.h +++ b/src/android.h @@ -101,6 +101,7 @@ extern void android_window_updated (android_window, unsigned long); struct android_dir; extern struct android_dir *android_opendir (const char *); +extern int android_dirfd (struct android_dir *); extern struct dirent *android_readdir (struct android_dir *); extern void android_closedir (struct android_dir *); diff --git a/src/androidfont.c b/src/androidfont.c index b0a9f994a85..bec5a59ca77 100644 --- a/src/androidfont.c +++ b/src/androidfont.c @@ -384,6 +384,41 @@ androidfont_get_cache (struct frame *frame) return font_cache; } +/* Initialize the Java side of the font driver if it has not already + been initialized. This is only done whenever necessary because the + font driver otherwise uses a lot of memory, as it has to keep every + typeface open. */ + +static void +androidfont_check_init (void) +{ + jmethodID method; + jobject old; + + if (font_driver) + return; + + /* Log a loud message. This font driver really should not be + used. */ + __android_log_print (ANDROID_LOG_WARN, __func__, + "The Android font driver is being used." + " Please investigate why this is so."); + + method = font_driver_class.create_font_driver; + + /* Initialize the font driver on the Java side. */ + font_driver + = (*android_java_env)->CallStaticObjectMethod (android_java_env, + font_driver_class.class, + method); + android_exception_check (); + + old = font_driver; + font_driver + = (*android_java_env)->NewGlobalRef (android_java_env, font_driver); + ANDROID_DELETE_LOCAL_REF (old); +} + /* Return a local reference to an instance of EmacsFontDriver$FontSpec with the same values as FONT. */ @@ -539,6 +574,9 @@ androidfont_list (struct frame *f, Lisp_Object font_spec) Lisp_Object value, entity; struct androidfont_entity *info; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + spec = androidfont_from_lisp (font_spec); array = (*android_java_env)->CallObjectMethod (android_java_env, font_driver, @@ -595,6 +633,9 @@ androidfont_match (struct frame *f, Lisp_Object font_spec) Lisp_Object entity; struct androidfont_entity *info; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + spec = androidfont_from_lisp (font_spec); result = (*android_java_env)->CallObjectMethod (android_java_env, font_driver, @@ -635,6 +676,9 @@ androidfont_draw (struct glyph_string *s, int from, int to, int rc; jobject gcontext, drawable; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + verify (sizeof (unsigned int) == sizeof (jint)); info = (struct androidfont_info *) s->font; @@ -683,6 +727,9 @@ androidfont_open_font (struct frame *f, Lisp_Object font_entity, jobject old; jint value; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); else if (pixel_size == 0) @@ -778,6 +825,9 @@ androidfont_close_font (struct font *font) struct androidfont_info *info; int i; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + info = (struct androidfont_info *) font; /* Free the font metrics cache if it exists. */ @@ -805,6 +855,9 @@ androidfont_has_char (Lisp_Object font, int c) struct androidfont_info *info; struct androidfont_entity *entity; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + if (FONT_ENTITY_P (font)) { entity = (struct androidfont_entity *) XFONT_ENTITY (font); @@ -830,6 +883,9 @@ androidfont_encode_char (struct font *font, int c) { struct androidfont_info *info; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + info = (struct androidfont_info *) font; return (*android_java_env)->CallIntMethod (android_java_env, @@ -891,6 +947,9 @@ androidfont_text_extents (struct font *font, const unsigned int *code, jobject metrics_object; short value; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + info = (struct androidfont_info *) font; if (nglyphs == 1 @@ -968,6 +1027,9 @@ androidfont_list_family (struct frame *f) jsize i, length; const char *family; + /* Maybe initialize the font driver. */ + androidfont_check_init (); + family_array = (*android_java_env)->CallObjectMethod (android_java_env, font_driver, @@ -1042,33 +1104,14 @@ syms_of_androidfont (void) void init_androidfont (void) { - jmethodID method; - jobject old; - android_init_font_driver (); android_init_font_spec (); android_init_font_metrics (); android_init_font_object (); android_init_integer (); - method = font_driver_class.create_font_driver; - - /* Initialize the font driver on the Java side. */ - font_driver - = (*android_java_env)->CallStaticObjectMethod (android_java_env, - font_driver_class.class, - method); - - if (!font_driver) - memory_full (0); - - old = font_driver; - font_driver - = (*android_java_env)->NewGlobalRef (android_java_env, font_driver); - ANDROID_DELETE_LOCAL_REF (old); - - if (!font_driver) - memory_full (0); + /* The Java font driver is not initialized here because it uses a lot + of memory. */ } void diff --git a/src/dired.c b/src/dired.c index 57d79b84463..ced08a35643 100644 --- a/src/dired.c +++ b/src/dired.c @@ -116,6 +116,9 @@ open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp) d = opendir (name); #else d = android_opendir (name); + + if (d) + fd = android_dirfd (d); #endif opendir_errno = errno; #else @@ -216,6 +219,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename); int fd; + + /* Keep in mind that FD is not always a real file descriptor on + Android. */ emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd); /* Unfortunately, we can now invoke expand-file-name and @@ -881,6 +887,13 @@ file_name_completion_dirp (int fd, struct dirent *dp, ptrdiff_t len) char *subdir_name = SAFE_ALLOCA (len + 2); memcpy (subdir_name, dp->d_name, len); strcpy (subdir_name + len, "/"); + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Check if subdir_name lies in the assets directory. */ + if (android_file_access_p (subdir_name, F_OK)) + return true; +#endif + bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0; SAFE_FREE (); return dirp; diff --git a/src/fileio.c b/src/fileio.c index b5a79312d88..218c2b01cd2 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -144,6 +144,25 @@ static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t, static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t, struct coding_system *); + + +/* Check that ENCODED does not lie on any special directory whose + contents are read only. Signal a `file-error' if it does. */ + +static void +check_mutable_filename (Lisp_Object encoded) +{ +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + if (!strcmp (SSDATA (encoded), "/assets") + || !strncmp (SSDATA (encoded), "/assets/", + sizeof "/assets/" - 1)) + xsignal2 (Qfile_error, + build_string ("File lies on read-" + "only directory"), + encoded); +#endif +} + /* Test whether FILE is accessible for AMODE. Return true if successful, false (setting errno) otherwise. */ @@ -2215,6 +2234,7 @@ permissions. */) encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); + check_mutable_filename (encoded_newname); #ifdef WINDOWSNT if (NILP (ok_if_already_exists) @@ -2474,6 +2494,8 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal, encoded_dir = ENCODE_FILE (directory); dir = SSDATA (encoded_dir); + check_mutable_filename (encoded_dir); + if (rmdir (dir) != 0) report_file_error ("Removing directory", directory); @@ -2513,6 +2535,7 @@ With a prefix argument, TRASH is nil. */) return call1 (Qmove_file_to_trash, filename); encoded_file = ENCODE_FILE (filename); + check_mutable_filename (encoded_file); if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT) report_file_error ("Removing old name", filename); @@ -2670,6 +2693,8 @@ This is what happens in interactive use with M-x. */) encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); + check_mutable_filename (encoded_file); + check_mutable_filename (encoded_newname); bool plain_rename = (case_only_rename || (!NILP (ok_if_already_exists) @@ -2781,6 +2806,8 @@ This is what happens in interactive use with M-x. */) encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); + check_mutable_filename (encoded_file); + check_mutable_filename (encoded_newname); if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0) return Qnil; @@ -2834,6 +2861,8 @@ This happens for interactive use with M-x. */) encoded_target = ENCODE_FILE (target); encoded_linkname = ENCODE_FILE (linkname); + check_mutable_filename (encoded_target); + check_mutable_filename (encoded_linkname); if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0) return Qnil; @@ -3565,6 +3594,8 @@ Interactively, prompt for FILENAME, and read MODE with command from GNU Coreutils. */) (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag) { + Lisp_Object encoded; + CHECK_FIXNUM (mode); int nofollow = symlink_nofollow_flag (flag); Lisp_Object absname = Fexpand_file_name (filename, @@ -3576,7 +3607,9 @@ command from GNU Coreutils. */) if (!NILP (handler)) return call4 (handler, Qset_file_modes, absname, mode, flag); - char *fname = SSDATA (ENCODE_FILE (absname)); + encoded = ENCODE_FILE (absname); + check_mutable_filename (encoded); + char *fname = SSDATA (encoded); mode_t imode = XFIXNUM (mode) & 07777; if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) report_file_error ("Doing chmod", absname); @@ -3648,6 +3681,7 @@ TIMESTAMP is in the format of `current-time'. */) return call4 (handler, Qset_file_times, absname, timestamp, flag); Lisp_Object encoded_absname = ENCODE_FILE (absname); + check_mutable_filename (encoded_absname); if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0) { @@ -3680,6 +3714,7 @@ otherwise, if FILE2 does not exist, the answer is t. */) (Lisp_Object file1, Lisp_Object file2) { struct stat st1, st2; + Lisp_Object encoded; CHECK_STRING (file1); CHECK_STRING (file2); @@ -3696,8 +3731,11 @@ otherwise, if FILE2 does not exist, the answer is t. */) if (!NILP (handler)) return call3 (handler, Qfile_newer_than_file_p, absname1, absname2); + encoded = ENCODE_FILE (absname1); + check_mutable_filename (encoded); + int err1; - if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0) + if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st1, 0) == 0) err1 = 0; else { @@ -5853,6 +5891,7 @@ See Info node `(elisp)Modification Time' for more details. */) return call2 (handler, Qverify_visited_file_modtime, buf); filename = ENCODE_FILE (BVAR (b, filename)); + check_mutable_filename (filename); mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0 ? get_stat_mtime (&st) @@ -5913,7 +5952,7 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */) error ("An indirect buffer does not have a visited file"); else { - register Lisp_Object filename; + register Lisp_Object filename, encoded; struct stat st; Lisp_Object handler; @@ -5926,7 +5965,10 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */) /* The handler can find the file name the same way we did. */ return call2 (handler, Qset_visited_file_modtime, Qnil); - if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (filename)), &st, 0) + encoded = ENCODE_FILE (filename); + check_mutable_filename (encoded); + + if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st, 0) == 0) { current_buffer->modtime = get_stat_mtime (&st); diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 37fd81953f6..5c9835fb13e 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -22,8 +22,14 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include +#include + +#ifdef __aarch64__ +#include +#endif #include +#include #include "androidterm.h" #include "sfntfont.h" @@ -63,6 +69,8 @@ static size_t max_scanline_buffer_size; /* Return a temporary buffer for storing scan lines. Set BUFFER to the buffer upon success. */ +#ifndef __aarch64__ + #define GET_SCANLINE_BUFFER(buffer, height, stride) \ do \ { \ @@ -94,6 +102,39 @@ static size_t max_scanline_buffer_size; } \ } while (false); +#else + +#define GET_SCANLINE_BUFFER(buffer, height, stride) \ + do \ + { \ + size_t _size; \ + void *_temp; \ + \ + if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \ + memory_full (SIZE_MAX); \ + \ + if (_size > scanline_buffer.buffer_size) \ + { \ + if (posix_memalign (&_temp, 16, _size)) \ + memory_full (_size); \ + free (scanline_buffer.buffer_data); \ + (buffer) \ + = scanline_buffer.buffer_data \ + = _temp; \ + scanline_buffer.buffer_size = _size; \ + } \ + else if (_size <= scanline_buffer.buffer_size) \ + (buffer) = scanline_buffer.buffer_data; \ + /* This is unreachable but clang says it is. */ \ + else \ + emacs_abort (); \ + \ + max_scanline_buffer_size \ + = max (_size, max_scanline_buffer_size); \ + } while (false); + +#endif + /* Scale each of the four packed bytes in P in the low 16 bits of P by @@ -149,6 +190,122 @@ sfntfont_android_blend (unsigned int src, unsigned int dst) return both + src; } +#ifdef __aarch64__ + +/* Like U255TO256, but operates on vectors. */ + +static uint16x8_t +sfntfont_android_u255to256 (uint8x8_t in) +{ + return vaddl_u8 (vshr_n_u8 (in, 7), in); +} + +/* Use processor features to efficiently composite four pixels at SRC + to DST. */ + +static void +sfntfont_android_over_8888_1 (unsigned int *src, unsigned int *dst) +{ + uint8x8_t alpha; + uint16x8_t alpha_c16, v1, v3, v4; + uint8x8_t b, g, r, a, v2, v5; + uint8x8x4_t _src, _dst; + + /* Pull in src and dst. + + This loads bytes, not words, so little endian ABGR becomes + RGBA. */ + _src = vld4_u8 ((const uint8_t *) src); + _dst = vld4_u8 ((const uint8_t *) dst); + + /* Load constants. */ + v4 = vdupq_n_u16 (256); + v5 = vdup_n_u8 (0); + + /* Load src alpha. */ + alpha = _src.val[3]; + + /* alpha_c16 = 256 - 255TO256 (alpha). */ + alpha_c16 = sfntfont_android_u255to256 (alpha); + alpha_c16 = vsubq_u16 (v4, alpha_c16); + + /* Cout = Csrc + Cdst * alpha_c. */ + v1 = vaddl_u8 (_dst.val[2], v5); + v2 = _src.val[2]; + v3 = vmulq_u16 (v1, alpha_c16); + b = vqadd_u8 (v2, vshrn_n_u16 (v3, 8)); + + v1 = vaddl_u8 (_dst.val[1], v5); + v2 = _src.val[1]; + v3 = vmulq_u16 (v1, alpha_c16); + g = vqadd_u8 (v2, vshrn_n_u16 (v3, 8)); + + v1 = vaddl_u8 (_dst.val[0], v5); + v2 = _src.val[0]; + v3 = vmulq_u16 (v1, alpha_c16); + r = vqadd_u8 (v2, vshrn_n_u16 (v3, 8)); + +#if 0 + /* Aout = Asrc + Adst * alpha_c. */ + v1 = vaddl_u8 (_dst.val[3], v5); + v2 = _src.val[3]; + v3 = vmulq_u16 (v1, alpha_c16); + a = vqadd_u8 (v2, vshrn_n_u16 (v3, 8)); +#else + /* We know that Adst is always 1, so Asrc + Adst * (1 - Asrc) is + always 1. */ + a = vdup_n_u8 (255); +#endif + + /* Store back in dst. */ + _dst.val[0] = r; + _dst.val[1] = g; + _dst.val[2] = b; + _dst.val[3] = a; + vst4_u8 ((uint8_t *) dst, _dst); +} + +/* Use processor features to efficiently composite the buffer at SRC + to DST. Composite at most MAX - SRC words. + + If either SRC or DST are not yet properly aligned, value is 1. + Otherwise, value is 0, and *X is incremented to the start of any + trailing data which could not be composited due to data alignment + constraints. */ + +static int +sfntfont_android_over_8888 (unsigned int *src, unsigned int *dst, + unsigned int *max, unsigned int *x) +{ + size_t i; + ptrdiff_t how_much; + void *s, *d; + + /* Figure out how much can be composited by this loop. */ + how_much = (max - src) & ~7; + + /* Return if there is not enough to vectorize. */ + if (!how_much) + return 1; + + /* Now increment *X by that much so the containing loop can process + the remaining pixels one-by-one. */ + + *x += how_much; + + for (i = 0; i < how_much; i += 8) + { + s = (src + i); + d = (dst + i); + + sfntfont_android_over_8888_1 (s, d); + } + + return 0; +} + +#endif + /* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE onto the native-endian ABGR8888 bitmap described by DEST and BITMAP_INFO. RECT is the subset of the bitmap to composite. */ @@ -163,7 +320,7 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer, { unsigned int *src_row; unsigned int *dst_row; - unsigned int i, src_y, x, src_x, max_x, dst_x; + unsigned int i, src_y, x, src_x, max_x, dst_x, lim_x; if ((intptr_t) dest & 3 || bitmap_info->stride & 3) /* This shouldn't be possible as Android is supposed to align the @@ -196,9 +353,24 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer, src_x = x + (rect->x - text_rectangle->x); dst_x = x + rect->x; - dst_row[dst_x] - = sfntfont_android_blend (src_row[src_x], - dst_row[dst_x]); + /* This is the largest value of src_x. */ + lim_x = max_x + (rect->x - text_rectangle->x); + +#ifdef __aarch64__ + if (!sfntfont_android_over_8888 (src_row + src_x, + dst_row + dst_x, + src_row + lim_x, + &x)) + { + /* Decrement X by one so the for loop can increment + it again. */ + x--; + continue; + } +#endif + dst_row[dst_x] + = sfntfont_android_blend (src_row[src_x], + dst_row[dst_x]); } } } @@ -308,11 +480,21 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, gui_union_rectangles (&background, &text_rectangle, &text_rectangle); - /* Allocate enough to hold text_rectangle.height, aligned to 8 - bytes. Then fill it with the background. */ + /* Allocate enough to hold text_rectangle.height, aligned to 8 (or + 16) bytes. Then fill it with the background. */ +#ifndef __aarch64__ stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7; +#else + stride = ((text_rectangle.width * sizeof *buffer) + 15) & ~15; +#endif GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride); - memset (buffer, 0, text_rectangle.height * stride); + + /* Try to optimize out this memset if the background rectangle + contains the whole text rectangle. */ + + if (!with_background || memcmp (&background, &text_rectangle, + sizeof text_rectangle)) + memset (buffer, 0, text_rectangle.height * stride); if (with_background) {