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.
This commit is contained in:
parent
a03eeb0109
commit
aaacf24ca2
10 changed files with 493 additions and 45 deletions
|
@ -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
|
||||
|
|
|
@ -29,6 +29,7 @@ public class EmacsThread extends Thread
|
|||
public
|
||||
EmacsThread (EmacsService service, boolean startDashQ)
|
||||
{
|
||||
super ("Emacs main thread");
|
||||
this.startDashQ = startDashQ;
|
||||
}
|
||||
|
||||
|
|
|
@ -312,6 +312,7 @@
|
|||
|
||||
(if (featurep 'android)
|
||||
(progn
|
||||
(load "ls-lisp")
|
||||
(load "term/common-win")
|
||||
(load "term/android-win")))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
168
src/android.c
168
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
|
||||
|
|
|
@ -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 *);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
13
src/dired.c
13
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;
|
||||
|
|
50
src/fileio.c
50
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);
|
||||
|
|
|
@ -22,8 +22,14 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __aarch64__
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue