diff options
| author | Po Lu <luangruo@yahoo.com> | 2023-01-19 22:19:06 +0800 |
|---|---|---|
| committer | Po Lu <luangruo@yahoo.com> | 2023-01-19 22:19:06 +0800 |
| commit | a496509cedb17109d0e6297a74e2ff8ed526333c (patch) | |
| tree | 46f3db2be263de7074675a5188796e25f21d4888 /src | |
| parent | 6253e7e74249c7cdfa86723f0b91a1d207cb143e (diff) | |
| download | emacs-a496509cedb17109d0e6297a74e2ff8ed526333c.tar.gz | |
Update Android port
* .gitignore: Add new files.
* INSTALL.android: Explain how to build Emacs for ancient
versions of Android.
* admin/merge-gnulib (GNULIB_MODULES): Add getdelim.
* build-aux/config.guess (timestamp, version):
* build-aux/config.sub (timestamp, version): Autoupdate.
* configure.ac (BUILD_DETAILS, ANDROID_MIN_SDK):
(ANDROID_STUBIFY): Allow specifying CFLAGS via ANDROID_CFLAGS.
Add new configure tests for Android API version when not
explicitly specified.
* doc/emacs/android.texi (Android): Add reference to ``Other
Input Devices''.
(Android File System): Remove restrictions on directory-files on
the assets directory.
* doc/emacs/emacs.texi (Top): Add Other Input Devices to menu.
* doc/emacs/input.texi (Other Input Devices): New node.
* doc/lispref/commands.texi (Touchscreen Events): Document
changes to touchscreen input events.
* doc/lispref/frames.texi (Pop-Up Menus): Likewise.
* etc/NEWS: Announce changes.
* java/Makefile.in: Use lib-src/asset-directory-tool to generate
an `directory-tree' file placed in /assets.
* java/debug.sh: Large adjustments to support Android 2.2 and
later.
* java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
* java/org/gnu/emacs/EmacsCopyArea.java (perform):
* java/org/gnu/emacs/EmacsDialog.java (toAlertDialog):
* java/org/gnu/emacs/EmacsDrawLine.java (perform):
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
* java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
* java/org/gnu/emacs/EmacsFillPolygon.java (perform):
* java/org/gnu/emacs/EmacsFillRectangle.java (perform):
* java/org/gnu/emacs/EmacsGC.java (EmacsGC):
* java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
(destroyHandle):
* java/org/gnu/emacs/EmacsSdk7FontDriver.java (draw): Avoid
redundant canvas saves and restores.
* java/org/gnu/emacs/EmacsService.java (run):
* java/org/gnu/emacs/EmacsView.java (EmacsView):
(handleDirtyBitmap):
* java/org/gnu/emacs/EmacsWindow.java (changeWindowBackground)
(EmacsWindow): Make compatible with Android 2.2 and later.
* lib-src/Makefile.in (DONT_INSTALL): Add asset-directory-tool
on Android.:(asset-directory-tool{EXEEXT}): New target.
* lib-src/asset-directory-tool.c (struct directory_tree, xmalloc)
(main_1, main_2, main): New file.
* lib, m4: Merge from gnulib. This will be reverted before
merging to master.
* lisp/button.el (button-map):
(push-button):
* lisp/frame.el (display-popup-menus-p): Improve touchscreen
support.
* lisp/subr.el (event-start):
(event-end): Handle touchscreen events.
* lisp/touch-screen.el (touch-screen-handle-timeout):
(touch-screen-handle-point-update):
(touch-screen-handle-point-up):
(touch-screen-track-tap):
(touch-screen-track-drag):
(touch-screen-drag-mode-line-1):
(touch-screen-drag-mode-line): New functions.
([mode-line touchscreen-begin]):
([bottom-divider touchscreen-begin]): Bind new events.
* lisp/wid-edit.el (widget-event-point):
(widget-keymap):
(widget-event-start):
(widget-button--check-and-call-button):
(widget-button-click): Improve touchscreen support.
* src/alloc.c (make_lisp_symbol): Avoid ICE on Android NDK GCC.
(mark_pinned_symbols): Likewise.
* src/android.c (struct android_emacs_window): New struct.
(window_class): New variable.
(android_run_select_thread): Add workaround for Android platform
bug.
(android_extract_long, android_scan_directory_tree): New
functions.
(android_file_access_p): Use those functions instead.
(android_init_emacs_window): New function.
(android_init_emacs_gc_class): Update signature of `markDirty'.
(android_change_gc, android_set_clip_rectangles): Tell the GC
whether or not clip rects were dirtied.
(android_swap_buffers): Do not look up method every time.
(struct android_dir): Adjust for new directory tree lookup.
(android_opendir, android_readdir, android_closedir): Likewise.
(android_four_corners_bilinear): Fix coding style.
(android_ftruncate): New function.
* src/android.h: Update prototypes. Replace ftruncate with
android_ftruncate when necessary.
* src/androidterm.c (handle_one_android_event): Pacify GCC. Fix
touch screen tool bar bug.
* src/emacs.c (using_utf8): Fix compilation error.
* src/fileio.c (Ffile_system_info): Return Qnil when fsusage.o
is not built.
* src/filelock.c (BOOT_TIME_FILE): Fix definition for Android.
* src/frame.c (Fx_parse_geometry): Fix uninitialized variable
uses.
* src/keyboard.c (lispy_function_keys): Fix `back'.
* src/menu.c (x_popup_menu_1): Handle touch screen events.
(Fx_popup_menu): Document changes.
* src/sfnt.c (main): Improve tests.
* src/sfntfont-android.c (sfntfont_android_put_glyphs): Fix
minor problem.
(init_sfntfont_android): Check for
HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL.
* src/sfntfont.c (struct sfnt_font_desc): New fields `adstyle'
and `languages'.
(sfnt_parse_style): Append tokens to adstyle.
(sfnt_parse_languages): New function.
(sfnt_enum_font_1): Parse supported languages and adstyle.
(sfntfont_list_1): Handle new fields.
(sfntfont_text_extents): Fix uninitialized variable use.
(syms_of_sfntfont, mark_sfntfont): Adjust accordingly.
Diffstat (limited to 'src')
| -rw-r--r-- | src/alloc.c | 34 | ||||
| -rw-r--r-- | src/android.c | 569 | ||||
| -rw-r--r-- | src/android.h | 8 | ||||
| -rw-r--r-- | src/androidterm.c | 5 | ||||
| -rw-r--r-- | src/emacs.c | 8 | ||||
| -rw-r--r-- | src/fileio.c | 15 | ||||
| -rw-r--r-- | src/filelock.c | 12 | ||||
| -rw-r--r-- | src/frame.c | 2 | ||||
| -rw-r--r-- | src/keyboard.c | 6 | ||||
| -rw-r--r-- | src/menu.c | 9 | ||||
| -rw-r--r-- | src/sfnt.c | 10 | ||||
| -rw-r--r-- | src/sfntfont-android.c | 4 | ||||
| -rw-r--r-- | src/sfntfont.c | 111 |
13 files changed, 651 insertions, 142 deletions
diff --git a/src/alloc.c b/src/alloc.c index 86e019b931b..ed55ae32710 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6172,16 +6172,44 @@ mark_pinned_objects (void) mark_object (pobj->object); } +#if defined HAVE_ANDROID && !defined (__clang__) + +/* The Android gcc is broken and needs the following version of + make_lisp_symbol. Otherwise a mysterious ICE pops up. */ + +#define make_lisp_symbol android_make_lisp_symbol + +static Lisp_Object +android_make_lisp_symbol (struct Lisp_Symbol *sym) +{ + intptr_t symoffset; + Lisp_Object a; + + symoffset = (intptr_t) sym; + INT_SUBTRACT_WRAPV (symoffset, (intptr_t) &lispsym, + &symoffset); + + a = TAG_PTR (Lisp_Symbol, symoffset); + return a; +} + +#endif + static void mark_pinned_symbols (void) { struct symbol_block *sblk; - int lim = (symbol_block_pinned == symbol_block - ? symbol_block_index : SYMBOL_BLOCK_SIZE); + int lim; + struct Lisp_Symbol *sym, *end; + + if (symbol_block_pinned == symbol_block) + lim = symbol_block_index; + else + lim = SYMBOL_BLOCK_SIZE; for (sblk = symbol_block_pinned; sblk; sblk = sblk->next) { - struct Lisp_Symbol *sym = sblk->symbols, *end = sym + lim; + sym = sblk->symbols, end = sym + lim; for (; sym < end; ++sym) if (sym->u.s.pinned) mark_object (make_lisp_symbol (sym)); diff --git a/src/android.c b/src/android.c index cfb79045c0b..eb9c404f1a3 100644 --- a/src/android.c +++ b/src/android.c @@ -54,6 +54,9 @@ bool android_init_gui; #include <android/log.h> #include <linux/ashmem.h> +#include <linux/unistd.h> + +#include <sys/syscall.h> #define ANDROID_THROW(env, class, msg) \ ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) @@ -114,6 +117,12 @@ struct android_emacs_drawable jmethodID damage_rect; }; +struct android_emacs_window +{ + jclass class; + jmethodID swap_buffers; +}; + /* The asset manager being used. */ static AAssetManager *asset_manager; @@ -181,6 +190,9 @@ static struct android_graphics_point point_class; /* Various methods associated with the EmacsDrawable class. */ static struct android_emacs_drawable drawable_class; +/* Various methods associated with the EmacsWindow class. */ +static struct android_emacs_window window_class; + /* Event handling functions. Events are stored on a (circular) queue @@ -247,10 +259,21 @@ android_run_select_thread (void *data) sigfillset (&signals); +#if __ANDROID_API__ < 16 + /* sigprocmask must be used instead of pthread_sigmask due to a bug + in Android versions earlier than 16. It only affects the calling + thread on Android anyhow. */ + + if (sigprocmask (SIG_BLOCK, &signals, NULL)) + __android_log_print (ANDROID_LOG_FATAL, __func__, + "sigprocmask: %s", + strerror (errno)); +#else if (pthread_sigmask (SIG_BLOCK, &signals, NULL)) __android_log_print (ANDROID_LOG_FATAL, __func__, "pthread_sigmask: %s", strerror (errno)); +#endif sigdelset (&signals, SIGUSR1); sigemptyset (&waitset); @@ -292,6 +315,8 @@ android_run_select_thread (void *data) still be locked, so this must come before. */ sem_post (&android_pselect_sem); } + + return NULL; } static void @@ -538,6 +563,200 @@ android_run_debug_thread (void *data) +/* Asset directory handling functions. ``directory-tree'' is a file in + the root of the assets directory describing its contents. + + See lib-src/asset-directory-tool for more details. */ + +/* The Android directory tree. */ +static const char *directory_tree; + +/* The size of the directory tree. */ +static size_t directory_tree_size; + +/* Read an unaligned (32-bit) long from the address POINTER. */ + +static unsigned int +android_extract_long (char *pointer) +{ + unsigned int number; + + memcpy (&number, pointer, sizeof number); + return number; +} + +/* Scan to the file FILE in the asset directory tree. Return a + pointer to the end of that file (immediately before any children) + in the directory tree, or NULL if that file does not exist. + + If returning non-NULL, also return the offset to the end of the + last subdirectory or file in *LIMIT_RETURN. LIMIT_RETURN may be + NULL. + + FILE must have less than 11 levels of nesting. If it ends with a + trailing slash, then NULL will be returned if it is not actually a + directory. */ + +static const char * +android_scan_directory_tree (char *file, size_t *limit_return) +{ + char *token, *saveptr, *copy, *copy1, *start, *max, *limit; + size_t token_length, ntokens, i; + char *tokens[10]; + + USE_SAFE_ALLOCA; + + /* Skip past the 5 byte header. */ + start = (char *) directory_tree + 5; + + /* Figure out the current limit. */ + limit = (char *) directory_tree + directory_tree_size; + + /* Now, split `file' into tokens, with the delimiter being the file + name separator. Look for the file and seek past it. */ + + ntokens = 0; + saveptr = NULL; + copy = copy1 = xstrdup (file); + memset (tokens, 0, sizeof tokens); + + while ((token = strtok_r (copy, "/", &saveptr))) + { + copy = NULL; + + /* Make sure ntokens is within bounds. */ + if (ntokens == ARRAYELTS (tokens)) + { + xfree (copy1); + goto fail; + } + + tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1); + memcpy (tokens[ntokens], token, strlen (token) + 1); + ntokens++; + } + + /* Free the copy created for strtok_r. */ + xfree (copy1); + + /* If there are no tokens, just return the start of the directory + tree. */ + if (!ntokens) + { + SAFE_FREE (); + + /* Subtract the initial header bytes. */ + if (limit_return) + *limit_return = directory_tree_size - 5; + + return start; + } + + /* Loop through tokens, indexing the directory tree each time. */ + + for (i = 0; i < ntokens; ++i) + { + token = tokens[i]; + + /* Figure out how many bytes to compare. */ + token_length = strlen (token); + + again: + + /* If this would be past the directory, return NULL. */ + if (start + token_length > limit) + goto fail; + + /* Now compare the file name. */ + if (!memcmp (start, token, token_length)) + { + /* They probably match. Find the NULL byte. It must be + either one byte past start + token_length, with the last + byte a trailing slash (indicating that it is a + directory), or just start + token_length. Return 4 bytes + past the next NULL byte. */ + + max = memchr (start, 0, limit - start); + + if (max != start + token_length + && !(max == start + token_length + 1 + && *(max - 1) == '/')) + goto false_positive; + + /* Return it if it exists and is in range, and this is the + last token. Otherwise, set it as start and the limit as + start + the offset and continue the loop. */ + + if (max && max + 5 <= limit) + { + if (i < ntokens - 1) + { + start = max + 5; + limit = ((char *) directory_tree + + android_extract_long (max + 1)); + + /* Make sure limit is still in range. */ + if (limit > directory_tree + directory_tree_size + || start > directory_tree + directory_tree_size) + goto fail; + + continue; + } + + /* Now see if max is not a directory and file is. If + file is a directory, then return NULL. */ + if (*(max - 1) != '/' && file[strlen (file) - 1] == '/') + max = NULL; + else + { + /* Figure out the limit. */ + if (limit_return) + *limit_return = android_extract_long (max + 1); + + /* Go to the end of this file. */ + max += 5; + } + + SAFE_FREE (); + return max; + } + + /* Return NULL otherwise. */ + __android_log_print (ANDROID_LOG_WARN, __func__, + "could not scan to end of directory tree" + ": %s", file); + goto fail; + } + + false_positive: + + /* No match was found. Set start to the next sibling and try + again. */ + + start = memchr (start, 0, limit - start); + + if (!start || start + 5 > limit) + goto fail; + + start = ((char *) directory_tree + + android_extract_long (start + 1)); + + /* Make sure start is still in bounds. */ + + if (start > limit) + goto fail; + + /* Continue the loop. */ + goto again; + } + + fail: + SAFE_FREE (); + return NULL; +} + + + /* Intercept USER_FULL_NAME and return something that makes sense if pw->pw_gecos is NULL. */ @@ -624,10 +843,6 @@ android_fstatat (int dirfd, const char *restrict pathname, bool android_file_access_p (const char *name, int amode) { - AAsset *asset; - AAssetDir *directory; - int length; - if (!asset_manager) return false; @@ -637,50 +852,11 @@ android_file_access_p (const char *name, int amode) /* /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 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; + /* Check if the file exists by looking in the ``directory tree'' + asset generated during the build process. This is used + instead of the AAsset functions, because the latter are + buggy and treat directories inconsistently. */ + return android_scan_directory_tree ((char *) name, NULL) != NULL; } return false; @@ -908,8 +1084,13 @@ android_get_home_directory (void) /* JNI functions called by Java. */ +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-prototypes" +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#endif JNIEXPORT void JNICALL NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, @@ -923,6 +1104,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, int pipefd[2]; pthread_t thread; const char *java_string; + AAsset *asset; /* This may be called from multiple threads. setEmacsParams should only ever be called once. */ @@ -943,6 +1125,33 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, /* Set the asset manager. */ asset_manager = AAssetManager_fromJava (env, local_asset_manager); + /* Initialize the directory tree. */ + asset = AAssetManager_open (asset_manager, "directory-tree", + AASSET_MODE_BUFFER); + + if (!asset) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "Failed to open directory tree"); + emacs_abort (); + } + + directory_tree = AAsset_getBuffer (asset); + + if (!directory_tree) + emacs_abort (); + + /* Now figure out how big the directory tree is, and compare the + first few bytes. */ + directory_tree_size = AAsset_getLength (asset); + if (directory_tree_size < 5 + || memcmp (directory_tree, "EMACS", 5)) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "Directory tree has bad magic"); + emacs_abort (); + } + /* Hold a VM reference to the asset manager to prevent the native object from being deleted. */ (*env)->NewGlobalRef (env, local_asset_manager); @@ -1199,6 +1408,36 @@ android_init_emacs_drawable (void) #undef FIND_METHOD } +static void +android_init_emacs_window (void) +{ + jclass old; + + window_class.class + = (*android_java_env)->FindClass (android_java_env, + "org/gnu/emacs/EmacsWindow"); + eassert (window_class.class); + + old = window_class.class; + window_class.class + = (jclass) (*android_java_env)->NewGlobalRef (android_java_env, + (jobject) old); + ANDROID_DELETE_LOCAL_REF (old); + + if (!window_class.class) + emacs_abort (); + +#define FIND_METHOD(c_name, name, signature) \ + window_class.c_name \ + = (*android_java_env)->GetMethodID (android_java_env, \ + window_class.class, \ + name, signature); \ + assert (window_class.c_name); + + FIND_METHOD (swap_buffers, "swapBuffers", "()V"); +#undef FIND_METHOD +} + extern JNIEXPORT void JNICALL NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv) { @@ -1232,6 +1471,7 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv) android_init_emacs_pixmap (); android_init_graphics_point (); android_init_emacs_drawable (); + android_init_emacs_window (); /* Set HOME to the app data directory. */ setenv ("HOME", android_files_dir, 1); @@ -1545,7 +1785,11 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, android_write_event (&event); } +#ifdef __clang__ #pragma clang diagnostic pop +#else +#pragma GCC diagnostic pop +#endif @@ -1557,8 +1801,6 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, This means that every local reference must be explicitly destroyed with DeleteLocalRef. A helper macro is provided to do this. */ -#define MAX_HANDLE 65535 - struct android_handle_entry { /* The type. */ @@ -1883,7 +2125,7 @@ android_init_emacs_gc_class (void) emacs_gc_mark_dirty = (*android_java_env)->GetMethodID (android_java_env, emacs_gc_class, - "markDirty", "()V"); + "markDirty", "(Z)V"); assert (emacs_gc_mark_dirty); old = emacs_gc_class; @@ -2011,6 +2253,9 @@ android_change_gc (struct android_gc *gc, struct android_gc_values *values) { jobject what, gcontext; + jboolean clip_changed; + + clip_changed = false; android_init_emacs_gc_class (); gcontext = android_resolve_handle (gc->gcontext, @@ -2041,16 +2286,22 @@ android_change_gc (struct android_gc *gc, values->function); if (mask & ANDROID_GC_CLIP_X_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_clip_x_origin, - values->clip_x_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_clip_x_origin, + values->clip_x_origin); + clip_changed = true; + } if (mask & ANDROID_GC_CLIP_Y_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_clip_y_origin, - values->clip_y_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_clip_y_origin, + values->clip_y_origin); + clip_changed = true; + } if (mask & ANDROID_GC_CLIP_MASK) { @@ -2070,6 +2321,7 @@ android_change_gc (struct android_gc *gc, xfree (gc->clip_rects); gc->clip_rects = NULL; gc->num_clip_rects = -1; + clip_changed = true; } if (mask & ANDROID_GC_STIPPLE) @@ -2103,7 +2355,8 @@ android_change_gc (struct android_gc *gc, if (mask) (*android_java_env)->CallVoidMethod (android_java_env, gcontext, - emacs_gc_mark_dirty); + emacs_gc_mark_dirty, + (jboolean) clip_changed); } void @@ -2174,7 +2427,8 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin, (*android_java_env)->CallVoidMethod (android_java_env, gcontext, - emacs_gc_mark_dirty); + emacs_gc_mark_dirty, + (jboolean) true); /* Cache the clip rectangles on the C side for sfntfont-android.c. */ @@ -2327,18 +2581,15 @@ android_swap_buffers (struct android_swap_info *swap_info, int num_windows) { jobject window; - jmethodID swap_buffers; int i; - swap_buffers = android_lookup_method ("org/gnu/emacs/EmacsWindow", - "swapBuffers", "()V"); - for (i = 0; i < num_windows; ++i) { window = android_resolve_handle (swap_info[i].swap_window, ANDROID_HANDLE_WINDOW); (*android_java_env)->CallVoidMethod (android_java_env, - window, swap_buffers); + window, + window_class.swap_buffers); } } @@ -3555,11 +3806,17 @@ android_sync (void) +#if __ANDROID_API__ >= 17 + #undef faccessat /* Replace the system faccessat with one which understands AT_EACCESS. Android's faccessat simply fails upon using AT_EACCESS, so repalce - it with zero here. This isn't caught during configuration. */ + it with zero here. This isn't caught during configuration. + + This replacement is only done when building for Android 17 or + later, because earlier versions use the gnulib replacement that + lacks these issues. */ int faccessat (int dirfd, const char *pathname, int mode, int flags) @@ -3572,6 +3829,8 @@ faccessat (int dirfd, const char *pathname, int mode, int flags) return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); } +#endif /* __ANDROID_API__ < 16 */ + /* Directory listing emulation. */ @@ -3581,8 +3840,11 @@ struct android_dir /* The real DIR *, if it exists. */ DIR *dir; - /* Otherwise, the AAssetDir. */ - void *asset_dir; + /* Otherwise, the pointer to the directory in directory_tree. */ + char *asset_dir; + + /* And the end of the files in asset_dir. */ + char *asset_limit; }; /* Like opendir. However, return an asset directory if NAME points to @@ -3592,8 +3854,9 @@ struct android_dir * android_opendir (const char *name) { struct android_dir *dir; - AAssetDir *asset_dir; + char *asset_dir; const char *asset_name; + size_t limit; asset_name = android_get_asset_name (name); @@ -3601,8 +3864,9 @@ android_opendir (const char *name) directory. */ if (asset_manager && asset_name) { - asset_dir = AAssetManager_openDir (asset_manager, - asset_name); + asset_dir + = (char *) android_scan_directory_tree ((char *) asset_name, + &limit); if (!asset_dir) { @@ -3613,6 +3877,20 @@ android_opendir (const char *name) dir = xmalloc (sizeof *dir); dir->dir = NULL; dir->asset_dir = asset_dir; + dir->asset_limit = (char *) directory_tree + limit; + + /* Make sure dir->asset_limit is within bounds. It is a limit, + and as such can be exactly one byte past directory_tree. */ + if (dir->asset_limit > directory_tree + directory_tree_size) + { + xfree (dir); + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "Invalid dir tree, limit %zu, size %zu\n", + limit, directory_tree_size); + dir = NULL; + errno = EACCES; + } + return dir; } @@ -3636,23 +3914,55 @@ struct dirent * android_readdir (struct android_dir *dir) { static struct dirent dirent; - const char *filename; + const char *last; if (dir->asset_dir) { - filename = AAssetDir_getNextFileName (dir->asset_dir); - errno = 0; + /* There are no more files to read. */ + if (dir->asset_dir >= dir->asset_limit) + return NULL; + + /* Otherwise, scan forward looking for the next NULL byte. */ + last = memchr (dir->asset_dir, 0, + dir->asset_limit - dir->asset_dir); + + /* No more NULL bytes remain. */ + if (!last) + return NULL; + + /* Forward last past the NULL byte. */ + last++; - if (!filename) + /* Make sure it is still within the directory tree. */ + if (last >= directory_tree + directory_tree_size) return NULL; + /* Now, fill in the dirent with the name. */ 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); + + /* Note that dir->asset_dir is actually a NULL terminated + string. */ + memcpy (dirent.d_name, dir->asset_dir, + MIN (sizeof dirent.d_name, + last - dir->asset_dir)); + dirent.d_name[sizeof dirent.d_name - 1] = '\0'; + + /* Strip off the trailing slash, if any. */ + if (dirent.d_name[MIN (sizeof dirent.d_name, + last - dir->asset_dir) + - 2] == '/') + dirent.d_name[MIN (sizeof dirent.d_name, + last - dir->asset_dir) + - 2] = '\0'; + + /* Finally, forward dir->asset_dir to the file past last. */ + dir->asset_dir = ((char *) directory_tree + + android_extract_long ((char *) last)); + return &dirent; } @@ -3666,8 +3976,9 @@ android_closedir (struct android_dir *dir) { if (dir->dir) closedir (dir->dir); - else - AAssetDir_close (dir->asset_dir); + + /* There is no need to close anything else, as the directory tree + lies in statically allocated memory. */ xfree (dir); } @@ -3795,40 +4106,40 @@ android_four_corners_bilinear (unsigned int tl, unsigned int tr, unsigned int bl, unsigned int br, int distx, int disty) { - int distxy, distxiy, distixy, distixiy; - uint32_t f, r; - - distxy = distx * disty; - distxiy = (distx << 8) - distxy; - distixy = (disty << 8) - distxy; - distixiy = (256 * 256 - (disty << 8) - - (distx << 8) + distxy); - - /* Red */ - r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy - + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); - - /* Green */ - f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy - + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); - r |= f & 0xff000000; - - /* Now do the upper two components. */ - tl >>= 16; - tr >>= 16; - bl >>= 16; - br >>= 16; - r >>= 16; - - /* Blue */ - f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy - + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); - r |= f & 0x00ff0000; - - /* Alpha */ - f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy - + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); - r |= f & 0xff000000; + int distxy, distxiy, distixy, distixiy; + uint32_t f, r; + + distxy = distx * disty; + distxiy = (distx << 8) - distxy; + distixy = (disty << 8) - distxy; + distixiy = (256 * 256 - (disty << 8) + - (distx << 8) + distxy); + + /* Red */ + r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + + /* Green */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + /* Now do the upper two components. */ + tl >>= 16; + tr >>= 16; + bl >>= 16; + br >>= 16; + r >>= 16; + + /* Blue */ + f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + r |= f & 0x00ff0000; + + /* Alpha */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; return r; } @@ -4010,6 +4321,36 @@ android_project_image_nearest (struct android_image *image, } } + + +/* System call wrappers for stuff missing in bionic. */ + +#ifndef HAVE_FTRUNCATE + +/* ftruncate wrapper for Android, for systems without ftruncate in the + C library. + + Such systems are always 32 bit systems, since Android 21 and later + all support ftruncate. In addition, ARM and MIPS require registers + used to store long long parameters to be aligned to an even + register pair. */ + +int +android_ftruncate (int fd, off_t length) +{ + int rc; + +#if defined __arm__ || defined __mips__ + return syscall (SYS_ftruncate64, fd, 0, + (unsigned int) (length & 0xffffffff), + (unsigned int) (length >> 32)); +#else + return syscall (SYS_ftruncate64, fd, length); +#endif +} + +#endif + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 036e6d266fd..240bc90d831 100644 --- a/src/android.h +++ b/src/android.h @@ -102,6 +102,14 @@ extern struct android_dir *android_opendir (const char *); extern struct dirent *android_readdir (struct android_dir *); extern void android_closedir (struct android_dir *); +#ifndef HAVE_FTRUNCATE +extern int android_ftruncate (int, off_t); + +/* Replace calls to ftruncate with android_ftruncate when ftruncate is + not defined. */ +#define ftruncate android_ftruncate +#endif + #endif diff --git a/src/androidterm.c b/src/androidterm.c index f19cee5b11b..3c16b542d91 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -683,7 +683,7 @@ handle_one_android_event (struct android_display_info *dpyinfo, XSETFRAME (inev.ie.frame_or_window, f); } else - /* A new frame must be created. */; + ((void) 0) /* A new frame must be created. */; } case ANDROID_ENTER_NOTIFY: @@ -988,6 +988,9 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!NILP (Vmouse_highlight)) { + /* Clear the pointer invisible flag to always make + note_mouse_highlight do its thing. */ + any->pointer_invisible = false; note_mouse_highlight (any, x, y); /* Always allow future mouse motion to diff --git a/src/emacs.c b/src/emacs.c index f4973c70610..994a4d1db93 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -423,7 +423,15 @@ using_utf8 (void) the result is known in advance anyway... */ #if defined HAVE_WCHAR_H && !defined WINDOWSNT wchar_t wc; +#ifndef HAVE_ANDROID mbstate_t mbs = { 0 }; +#else + mbstate_t mbs; + + /* Not sure how mbstate works on Android, but this seems to be + required. */ + memset (&mbs, 0, sizeof mbs); +#endif return mbrtowc (&wc, "\xc4\x80", 2, &mbs) == 2 && wc == 0x100; #else return false; diff --git a/src/fileio.c b/src/fileio.c index 6fa524b3bb4..b5a79312d88 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -6325,6 +6325,11 @@ effect except for flushing STREAM's data. */) #ifndef DOS_NT +#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE \ + || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1 \ + || defined STAT_STATFS4 || defined STAT_STATVFS \ + || defined STAT_STATVFS64 + /* Yield a Lisp number equal to BLOCKSIZE * BLOCKS, with the result negated if NEGATE. */ static Lisp_Object @@ -6339,6 +6344,8 @@ blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, bool negate) return CALLN (Ftimes, bs, make_uint (blocks)); } +#endif + DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0, doc: /* Return storage information about the file system FILENAME is on. Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total @@ -6360,6 +6367,11 @@ If the underlying system call fails, value is nil. */) error ("Invalid handler in `file-name-handler-alist'"); } + /* Try to detect whether or not fsusage.o is actually built. */ +#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE \ + || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1 \ + || defined STAT_STATFS4 || defined STAT_STATVFS \ + || defined STAT_STATVFS64 struct fs_usage u; if (get_fs_usage (SSDATA (ENCODE_FILE (filename)), NULL, &u) != 0) return errno == ENOSYS ? Qnil : file_attribute_errno (filename, errno); @@ -6367,6 +6379,9 @@ If the underlying system call fails, value is nil. */) blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false), blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail, u.fsu_bavail_top_bit_set)); +#else + return Qnil; +#endif } #endif /* !DOS_NT */ diff --git a/src/filelock.c b/src/filelock.c index 51e1ffca9db..45eac5a19a1 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -65,6 +65,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #define BOOT_TIME_FILE "/var/run/random-seed" #endif +/* Boot time is not available on Android. */ + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#undef BOOT_TIME +#endif + #if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME #define WTMP_FILE "/var/log/wtmp" #endif @@ -120,12 +126,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ * Non-forced locks on non-MS-Windows systems that support neither hard nor symbolic links. */ -/* Boot time is not available on Android. */ - -#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY -#undef BOOT_TIME -#endif - /* Return the time of the last system boot. */ diff --git a/src/frame.c b/src/frame.c index 286c9a2cb71..e98256fe9ed 100644 --- a/src/frame.c +++ b/src/frame.c @@ -5666,6 +5666,8 @@ On Nextstep, this just calls `ns-parse-geometry'. */) int x UNINIT, y UNINIT; unsigned int width, height; + width = height = 0; + CHECK_STRING (string); #ifdef HAVE_NS diff --git a/src/keyboard.c b/src/keyboard.c index 834049b496a..11fa1e62c89 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -62,6 +62,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "syssignal.h" +#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT +#include <setjmp.h> +#endif + #include <sys/types.h> #include <unistd.h> #include <fcntl.h> @@ -4955,7 +4959,7 @@ const char *const lispy_function_keys[] = [278] = "copy", [279] = "paste", [28] = "clear", - [4] = "back", + [4] = "XF86Back", [61] = "tab", [66] = "return", [67] = "backspace", diff --git a/src/menu.c b/src/menu.c index e1f899858d3..e02ee880119 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1152,7 +1152,7 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu) else { menuflags |= MENU_FOR_CLICK; - tem = Fcar (XCDR (position)); /* EVENT_START (position) */ + tem = EVENT_START (position); /* EVENT_START (position) */ window = Fcar (tem); /* POSN_WINDOW (tem) */ tem2 = Fcar (Fcdr (tem)); /* POSN_POSN (tem) */ /* The MENU_KBD_NAVIGATION field is set when the menu @@ -1466,9 +1466,10 @@ cached information about equivalent key sequences. If the user gets rid of the menu without making a valid choice, for instance by clicking the mouse away from a valid choice or by typing keyboard input, then this normally results in a quit and -`x-popup-menu' does not return. But if POSITION is a mouse button -event (indicating that the user invoked the menu with the mouse) then -no quit occurs and `x-popup-menu' returns nil. */) +`x-popup-menu' does not return. But if POSITION is a mouse button or +touch screen event (indicating that the user invoked the menu with the +a pointing device) then no quit occurs and `x-popup-menu' returns +nil. */) (Lisp_Object position, Lisp_Object menu) { init_raw_keybuf_count (); diff --git a/src/sfnt.c b/src/sfnt.c index 6d58798c599..7300915a504 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -4624,8 +4624,14 @@ main (int argc, char **argv) } if (meta) - fprintf (stderr, "meta table with count: %"PRIu32"\n", - meta->num_data_maps); + { + fprintf (stderr, "meta table with count: %"PRIu32"\n", + meta->num_data_maps); + + for (i = 0; i < meta->num_data_maps; ++i) + fprintf (stderr, " meta tag: %"PRIx32"\n", + meta->data_maps[i].tag); + } loca_long = NULL; loca_short = NULL; diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 1b01a4d9be4..37fd81953f6 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -310,7 +310,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, /* Allocate enough to hold text_rectangle.height, aligned to 8 bytes. Then fill it with the background. */ - stride = (text_rectangle.width * sizeof *buffer) + 7 & ~7; + stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7; GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride); memset (buffer, 0, text_rectangle.height * stride); @@ -546,6 +546,7 @@ init_sfntfont_android (void) { /* Make sure to pick the right Sans Serif font depending on what version of Android the device is running. */ +#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL if (android_get_device_api_level () >= 15) Vsfnt_default_family_alist = list3 (Fcons (build_string ("Monospace"), @@ -557,6 +558,7 @@ init_sfntfont_android (void) Fcons (build_string ("Sans Serif"), build_string ("Roboto"))); else +#endif Vsfnt_default_family_alist = list3 (Fcons (build_string ("Monospace"), build_string ("Droid Sans Mono")), diff --git a/src/sfntfont.c b/src/sfntfont.c index e2d18517fcb..87f93473ff3 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -21,6 +21,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <config.h> #include <fcntl.h> +#include <ctype.h> #include "lisp.h" @@ -61,6 +62,12 @@ struct sfnt_font_desc /* Designer (foundry) of the font. */ Lisp_Object designer; + /* Style tokens that could not be parsed. */ + Lisp_Object adstyle; + + /* List of design languages. */ + Lisp_Object languages; + /* Numeric width, weight, slant and spacing. */ int width, weight, slant, spacing; @@ -344,7 +351,9 @@ static struct sfnt_style_desc sfnt_width_descriptions[] = }; /* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing - based on the style name passed as STYLE_NAME. */ + based on the style name passed as STYLE_NAME. + + Also append any unknown tokens to DESC->adstyle. */ static void sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) @@ -419,16 +428,85 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) } } - next: + /* This token is extraneous or was not recognized. Capitalize + the first letter and set it as the adstyle. */ - /* Break early if everything has been found. */ - if (desc->slant != 100 && desc->width != 100 && desc->weight != 80) - break; + if (strlen (single)) + { + if (islower (single[0])) + single[0] = toupper (single[0]); + + if (NILP (desc->adstyle)) + desc->adstyle = build_string (single); + else + desc->adstyle = CALLN (Fconcat, desc->adstyle, + build_string (" "), + build_string (single)); + } + next: continue; } } +/* Parse the list of design languages in META, a font metadata table, + and place the results in DESC->languages. Do nothing if there is + no such metadata. */ + +static void +sfnt_parse_languages (struct sfnt_meta_table *meta, + struct sfnt_font_desc *desc) +{ + char *data, *metadata, *tag; + struct sfnt_meta_data_map map; + char *saveptr; + + /* Look up the ``design languages'' metadata. This is a comma (and + possibly space) separated list of scripts that the font was + designed for. Here is an example of one such tag: + + zh-Hans,Jpan,Kore + + for a font that covers Simplified Chinese, along with Japanese + and Korean text. */ + + saveptr = NULL; + data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_DLNG, + &map); + + if (!data) + return; + + USE_SAFE_ALLOCA; + + /* Now copy metadata and add a trailing NULL byte. */ + + if (map.data_length >= SIZE_MAX) + memory_full (SIZE_MAX); + + metadata = SAFE_ALLOCA ((size_t) map.data_length + 1); + memcpy (metadata, data, map.data_length); + metadata[map.data_length] = '\0'; + + /* Loop through each script-language tag. Note that there may be + extra leading spaces. */ + while ((tag = strtok_r (metadata, ",", &saveptr))) + { + metadata = NULL; + + if (strstr (tag, "Hans") || strstr (tag, "Hant")) + desc->languages = Fcons (Qzh, desc->languages); + + if (strstr (tag, "Japn")) + desc->languages = Fcons (Qja, desc->languages); + + if (strstr (tag, "Kore")) + desc->languages = Fcons (Qko, desc->languages); + } + + SAFE_FREE (); +} + /* Enumerate the offset subtable SUBTABLES in the file FD, whose file name is FILE. OFFSET should be the offset of the subtable within the font file, and is recorded for future use. Value is 1 upon @@ -481,6 +559,10 @@ sfnt_enum_font_1 (int fd, const char *file, /* Parse the style. */ sfnt_parse_style (style, desc); + /* If the meta table exists, parse the list of design languages. */ + if (meta) + sfnt_parse_languages (meta, desc); + /* Figure out the spacing. Some fancy test like what Fontconfig does is probably in order but not really necessary. */ if (!NILP (Fstring_search (Fdowncase (family), @@ -990,11 +1072,10 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) desc->family))) return false; - /* Check that no adstyle has been specified. That's a relic from - the Postscript era. */ + /* Check that the adstyle specified matches. */ tem = AREF (spec, FONT_ADSTYLE_INDEX); - if (!NILP (tem)) + if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle))) return false; /* Check the style. */ @@ -1069,6 +1150,11 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) } } + /* Now check that the language is supported. */ + tem = assq_no_quit (QClang, extra); + if (!NILP (tem) && NILP (Fmemq (tem, desc->languages))) + goto fail; + /* Set desc->subtable if cmap was specified. */ if (cmap) desc->subtable = subtable; @@ -2076,9 +2162,9 @@ sfntfont_text_extents (struct font *font, const unsigned int *code, if (pcm.descent > metrics->descent) metrics->descent = pcm.descent; - } - total_width += pcm.width; + total_width += pcm.width; + } } metrics->width = total_width; @@ -2239,6 +2325,9 @@ syms_of_sfntfont (void) DEFSYM (Qapple_roman, "apple-roman"); DEFSYM (Qjisx0208_1983_0, "jisx0208.1983-0"); DEFSYM (Qksc5601_1987_0, "ksc5601.1987-0"); + DEFSYM (Qzh, "zh"); + DEFSYM (Qja, "ja"); + DEFSYM (Qko, "ko"); /* Char-table purpose. */ DEFSYM (Qfont_lookup_cache, "font-lookup-cache"); @@ -2269,6 +2358,8 @@ mark_sfntfont (void) { mark_object (desc->family); mark_object (desc->style); + mark_object (desc->adstyle); + mark_object (desc->languages); mark_object (desc->char_cache); mark_object (desc->designer); } |
