summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in72
-rw-r--r--src/alloc.c84
-rw-r--r--src/android-asset.h422
-rw-r--r--src/android-emacs.c169
-rw-r--r--src/android.c6726
-rw-r--r--src/android.h231
-rw-r--r--src/androidfns.c3205
-rw-r--r--src/androidfont.c1108
-rw-r--r--src/androidgui.h724
-rw-r--r--src/androidmenu.c829
-rw-r--r--src/androidselect.c499
-rw-r--r--src/androidterm.c6073
-rw-r--r--src/androidterm.h476
-rw-r--r--src/buffer.c24
-rw-r--r--src/buffer.h11
-rw-r--r--src/callproc.c108
-rw-r--r--src/charset.c5
-rw-r--r--src/coding.c27
-rw-r--r--src/coding.h4
-rw-r--r--src/conf_post.h10
-rw-r--r--src/dired.c65
-rw-r--r--src/dispextern.h63
-rw-r--r--src/dispnew.c51
-rw-r--r--src/editfns.c8
-rw-r--r--src/emacs-module.c162
-rw-r--r--src/emacs.c138
-rw-r--r--src/epaths.in22
-rw-r--r--src/fileio.c343
-rw-r--r--src/filelock.c33
-rw-r--r--src/fns.c4
-rw-r--r--src/font.c26
-rw-r--r--src/font.h14
-rw-r--r--src/fontset.c27
-rw-r--r--src/frame.c58
-rw-r--r--src/frame.h103
-rw-r--r--src/fringe.c23
-rw-r--r--src/image.c731
-rw-r--r--src/inotify.c19
-rw-r--r--src/keyboard.c319
-rw-r--r--src/keyboard.h13
-rw-r--r--src/lisp.h24
-rw-r--r--src/lread.c326
-rw-r--r--src/marker.c26
-rw-r--r--src/menu.c20
-rw-r--r--src/pdumper.c24
-rw-r--r--src/print.c17
-rw-r--r--src/process.c25
-rw-r--r--src/scroll.c7
-rw-r--r--src/sfnt.c19684
-rw-r--r--src/sfnt.h1994
-rw-r--r--src/sfntfont-android.c793
-rw-r--r--src/sfntfont.c3874
-rw-r--r--src/sfntfont.h79
-rw-r--r--src/sound.c2
-rw-r--r--src/sysdep.c168
-rw-r--r--src/term.c147
-rw-r--r--src/termhooks.h22
-rw-r--r--src/terminal.c2
-rw-r--r--src/textconv.c1554
-rw-r--r--src/textconv.h42
-rw-r--r--src/verbose.mk.in11
-rw-r--r--src/w32.c3
-rw-r--r--src/w32proc.c2
-rw-r--r--src/window.c5
-rw-r--r--src/window.h19
-rw-r--r--src/xdisp.c186
-rw-r--r--src/xfaces.c61
-rw-r--r--src/xfns.c7
-rw-r--r--src/xterm.c86
-rw-r--r--src/xterm.h7
70 files changed, 51801 insertions, 445 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index e08e5eead28..9ac7983943e 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -33,6 +33,16 @@ top_builddir = @top_builddir@
# MinGW CPPFLAGS may use this.
abs_top_srcdir=@abs_top_srcdir@
VPATH = $(srcdir)
+
+# This is not empty if this is a Makefile that will be copied to
+# cross/src.
+XCONFIGURE = @XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath %.c := $(srcdir)
+vpath %.h := $(srcdir)
+endif
+
CC = @CC@
CXX = @CXX@
CFLAGS = @CFLAGS@
@@ -48,6 +58,7 @@ LIBOBJS = @LIBOBJS@
lispsource = $(top_srcdir)/lisp
lib = ../lib
+hostlib = $(top_builddir)/lib
libsrc = ../lib-src
etc = ../etc
oldXMenudir = ../oldXMenu
@@ -127,6 +138,10 @@ LIB_PTHREAD=@LIB_PTHREAD@
LIBIMAGE=@LIBTIFF@ @LIBJPEG@ @LIBPNG@ @LIBGIF@ @LIBXPM@ @WEBP_LIBS@
+GIF_CFLAGS=@GIF_CFLAGS@
+JPEG_CFLAGS=@JPEG_CFLAGS@
+TIFF_CFLAGS=@TIFF_CFLAGS@
+
XCB_LIBS=@XCB_LIBS@
XFT_LIBS=@XFT_LIBS@
XRENDER_LIBS=@XRENDER_LIBS@
@@ -241,6 +256,7 @@ LIBXML2_LIBS = @LIBXML2_LIBS@
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
SQLITE3_LIBS = @SQLITE3_LIBS@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@
@@ -327,12 +343,13 @@ W32_RES_LINK=@W32_RES_LINK@
## if HAVE_HARFBUZZ, hbfont.o is added regardless of the rest
FONT_OBJ=@FONT_OBJ@
-## Empty for MinGW, cm.o for the rest.
+## Empty for MinGW and Android, cm.o for the rest.
CM_OBJ=@CM_OBJ@
LIBGPM = @LIBGPM@
LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
+LIBSELINUX_CFLAGS = @LIBSELINUX_CFLAGS@
LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
@@ -371,6 +388,13 @@ HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
HAIKU_LIBS = @HAIKU_LIBS@
HAIKU_CFLAGS = @HAIKU_CFLAGS@
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+
+LIBGMP_CFLAGS = @LIBGMP_CFLAGS@
+
DUMPING=@DUMPING@
CHECK_STRUCTS = @CHECK_STRUCTS@
HAVE_PDUMPER = @HAVE_PDUMPER@
@@ -412,7 +436,9 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \
$(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
- $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS)
+ $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS) \
+ $(ANDROID_BUILD_CFLAGS) $(GIF_CFLAGS) $(JPEG_CFLAGS) $(SQLITE3_CFLAGS) \
+ $(LIBGMP_CFLAGS) $(TIFF_CFLAGS) $(LIBSELINUX_CFLAGS)
ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
$(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@@ -450,7 +476,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
$(if $(HYBRID_MALLOC),sheap.o) \
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
- $(HAIKU_OBJ) $(PGTK_OBJ)
+ $(HAIKU_OBJ) $(PGTK_OBJ) $(ANDROID_OBJ)
doc_obj = $(base_obj) $(NS_OBJC_OBJ)
obj = $(doc_obj) $(HAIKU_CXX_OBJ)
@@ -467,7 +493,8 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
xsettings.o xgselect.o termcap.o hbfont.o \
- haikuterm.o haikufns.o haikumenu.o haikufont.o
+ haikuterm.o haikufns.o haikumenu.o haikufont.o androidterm.o androidfns.o \
+ androidfont.o
## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -570,7 +597,8 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) $(LIBX_BASE) $(LIBIMAGE
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
$(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS) $(HAIKU_LIBS) \
- $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS)
+ $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS) \
+ $(ANDROID_LIBS)
## FORCE it so that admin/unidata can decide whether this file is
## up-to-date. Although since charprop depends on bootstrap-emacs,
@@ -659,7 +687,7 @@ $(etc)/DOC: $(libsrc)/make-docfile$(EXEEXT) $(doc_obj)
$(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC
$(libsrc)/make-docfile$(EXEEXT) $(libsrc)/make-fingerprint$(EXEEXT): \
- $(lib)/libgnu.a
+ $(hostlib)/libgnu.a
$(MAKE) -C $(dir $@) $(notdir $@)
buildobj.h: Makefile
@@ -720,6 +748,37 @@ ifeq ($(DUMPING),unexec)
endif
endif
+ifeq ($(XCONFIGURE),android)
+## The Android package internally links to and communicates with a
+## shared library named `libemacs.so' at startup. This is built
+## almost the same way temacs is. But it is position independent. It
+## is not dumped here. Instead, it dumps itself the first time it
+## starts on the user's device.
+
+# Include ndk-build.mk in order to build Emacs dependencies.
+old_top_builddir := $(top_builddir)
+top_builddir := $(old_top_builddir)/..
+include $(old_top_builddir)/ndk-build/ndk-build.mk
+top_builddir := $(old_top_builddir)
+
+libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
+ $(MAKE_PDUMPER_FINGERPRINT) $(NDK_BUILD_SHARED) \
+ $(NDK_BUILD_STATIC)
+ $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \
+ $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \
+ $(LIBEGNU_ARCHIVE) $(LIBES)
+ $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@
+
+# There is also a binary named `android-emacs' which simply calls
+# emacs.so. It need not link against libemacs because app_process
+# will do that instead.
+
+android-emacs: android-emacs.c
+ $(AM_V_CCLD)$(CC) $(lastword $^) -o $@ \
+ $(ALL_CFLAGS) $(LDFLAGS) \
+ $(LIBEGNU_ARCHIVE)
+endif
+
## The following oldxmenu-related rules are only (possibly) used if
## HAVE_X11 && !USE_GTK, but there is no harm in always defining them.
$(lwlibdir)/liblw.a: $(config_h) globals.h lisp.h FORCE
@@ -748,6 +807,7 @@ ns-app: emacs$(EXEEXT) $(pdmp)
.PHONY: versionclean
mostlyclean:
+ rm -f android-emacs libemacs.so
rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o
rm -f dmpstruct.h
rm -f emacs.pdmp
diff --git a/src/alloc.c b/src/alloc.c
index 17ca5c725d0..82b1c6b0355 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -50,6 +50,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
#ifdef HAVE_TREE_SITTER
#include "treesit.h"
#endif
@@ -3346,6 +3350,15 @@ cleanup_vector (struct Lisp_Vector *vector)
drv->close_font (font);
}
}
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* The Android font driver needs the ability to associate extra
+ information with font entities. */
+ if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
+ == FONT_ENTITY_MAX)
+ && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
+ android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
+#endif
}
else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
@@ -5645,6 +5658,22 @@ find_string_data_in_pure (const char *data, ptrdiff_t nbytes)
if (pure_bytes_used_non_lisp <= nbytes)
return NULL;
+ /* The Android GCC generates code like:
+
+ 0xa539e755 <+52>: lea 0x430(%esp),%esi
+=> 0xa539e75c <+59>: movdqa %xmm0,0x0(%ebp)
+ 0xa539e761 <+64>: add $0x10,%ebp
+
+ but data is not aligned appropriately, so a GP fault results. */
+
+#if defined __i386__ \
+ && defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY \
+ && !defined (__clang__)
+ if ((intptr_t) data & 15)
+ return NULL;
+#endif
+
/* Set up the Boyer-Moore table. */
skip = nbytes + 1;
for (i = 0; i < 256; i++)
@@ -6155,16 +6184,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));
@@ -6463,6 +6520,13 @@ garbage_collect (void)
mark_xselect ();
#endif
+#ifdef HAVE_ANDROID
+ mark_androidterm ();
+#ifndef ANDROID_STUBIFY
+ mark_sfntfont ();
+#endif
+#endif
+
#ifdef HAVE_NS
mark_nsterm ();
#endif
@@ -6871,6 +6935,11 @@ static void
mark_frame (struct Lisp_Vector *ptr)
{
struct frame *f = (struct frame *) ptr;
+#ifdef HAVE_TEXT_CONVERSION
+ struct text_conversion_action *tem;
+#endif
+
+
mark_vectorlike (&ptr->header);
mark_face_cache (f->face_cache);
#ifdef HAVE_WINDOW_SYSTEM
@@ -6882,6 +6951,15 @@ mark_frame (struct Lisp_Vector *ptr)
mark_vectorlike (&font->header);
}
#endif
+
+#ifdef HAVE_TEXT_CONVERSION
+ mark_object (f->conversion.compose_region_start);
+ mark_object (f->conversion.compose_region_end);
+ mark_object (f->conversion.compose_region_overlay);
+
+ for (tem = f->conversion.actions; tem; tem = tem->next)
+ mark_object (tem->data);
+#endif
}
static void
diff --git a/src/android-asset.h b/src/android-asset.h
new file mode 100644
index 00000000000..4fb309f1645
--- /dev/null
+++ b/src/android-asset.h
@@ -0,0 +1,422 @@
+/* Android initialization for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <android/log.h>
+
+/* This file contains an emulation of the Android asset manager API
+ used on builds for Android 2.2. It is included by android.c
+ whenever appropriate.
+
+ The replacements in this file are not thread safe and must only be
+ called from the creating thread. */
+
+struct android_asset_manager
+{
+ /* JNI environment. */
+ JNIEnv *env;
+
+ /* Asset manager class and functions. */
+ jclass class;
+ jmethodID open_fd;
+
+ /* Asset file descriptor class and functions. */
+ jclass fd_class;
+ jmethodID get_length;
+ jmethodID create_input_stream;
+ jmethodID close;
+
+ /* Input stream class and functions. */
+ jclass input_stream_class;
+ jmethodID read;
+ jmethodID stream_close;
+
+ /* Associated asset manager object. */
+ jobject asset_manager;
+};
+
+typedef struct android_asset_manager AAssetManager;
+
+struct android_asset
+{
+ /* The asset manager. */
+ AAssetManager *manager;
+
+ /* The length of the asset, or -1. */
+ jlong length;
+
+ /* The asset file descriptor and input stream. */
+ jobject fd, stream;
+
+ /* The mode. */
+ int mode;
+};
+
+typedef struct android_asset AAsset;
+
+static AAssetManager *
+AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
+{
+ AAssetManager *manager;
+ jclass temp;
+
+ manager = malloc (sizeof *manager);
+
+ if (!manager)
+ return NULL;
+
+ manager->env = env;
+ manager->asset_manager
+ = (*env)->NewGlobalRef (env, java_manager);
+
+ if (!manager->asset_manager)
+ {
+ free (manager);
+ return NULL;
+ }
+
+ manager->class
+ = (*env)->FindClass (env, "android/content/res/AssetManager");
+ assert (manager->class);
+
+ manager->open_fd
+ = (*env)->GetMethodID (env, manager->class, "openFd",
+ "(Ljava/lang/String;)"
+ "Landroid/content/res/AssetFileDescriptor;");
+ assert (manager->open);
+
+ manager->fd_class
+ = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
+ assert (manager->fd_class);
+
+ manager->get_length
+ = (*env)->GetMethodID (env, manager->fd_class, "getLength",
+ "()J");
+ assert (manager->get_length);
+
+ manager->create_input_stream
+ = (*env)->GetMethodID (env, manager->fd_class,
+ "createInputStream",
+ "()Ljava/io/FileInputStream;");
+ assert (manager->create_input_stream);
+
+ manager->close
+ = (*env)->GetMethodID (env, manager->fd_class,
+ "close", "()V");
+ assert (manager->close);
+
+ manager->input_stream_class
+ = (*env)->FindClass (env, "java/io/InputStream");
+ assert (manager->input_stream_class);
+
+ manager->read
+ = (*env)->GetMethodID (env, manager->input_stream_class,
+ "read", "([B)I");
+ assert (manager->read);
+
+ manager->stream_close
+ = (*env)->GetMethodID (env, manager->input_stream_class,
+ "close", "()V");
+ assert (manager->stream_close);
+
+ /* Now convert all the class references to global ones. */
+ temp = manager->class;
+ manager->class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->class);
+ (*env)->DeleteLocalRef (env, temp);
+ temp = manager->fd_class;
+ manager->fd_class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->fd_class);
+ (*env)->DeleteLocalRef (env, temp);
+ temp = manager->input_stream_class;
+ manager->input_stream_class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->input_stream_class);
+ (*env)->DeleteLocalRef (env, temp);
+
+ /* Return the asset manager. */
+ return manager;
+}
+
+enum
+ {
+ AASSET_MODE_STREAMING = 0,
+ AASSET_MODE_BUFFER = 1,
+ };
+
+static AAsset *
+AAssetManager_open (AAssetManager *manager, const char *c_name,
+ int mode)
+{
+ jobject desc;
+ jstring name;
+ AAsset *asset;
+
+ /* Push a local frame. */
+ asset = NULL;
+
+ (*(manager->env))->PushLocalFrame (manager->env, 3);
+
+ if ((*(manager->env))->ExceptionCheck (manager->env))
+ goto fail;
+
+ /* Encoding issues can be ignored for now as there are only ASCII
+ file names in Emacs. */
+ name = (*(manager->env))->NewStringUTF (manager->env, c_name);
+
+ if (!name)
+ goto fail;
+
+ /* Now try to open an ``AssetFileDescriptor''. */
+ desc = (*(manager->env))->CallObjectMethod (manager->env,
+ manager->asset_manager,
+ manager->open_fd,
+ name);
+
+ if (!desc)
+ goto fail;
+
+ /* Allocate the asset. */
+ asset = calloc (1, sizeof *asset);
+
+ if (!asset)
+ {
+ (*(manager->env))->CallVoidMethod (manager->env,
+ desc,
+ manager->close);
+ goto fail;
+ }
+
+ /* Pop the local frame and return desc. */
+ desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
+
+ if (!desc)
+ goto fail;
+
+ (*(manager->env))->PopLocalFrame (manager->env, NULL);
+
+ asset->manager = manager;
+ asset->length = -1;
+ asset->fd = desc;
+ asset->mode = mode;
+
+ return asset;
+
+ fail:
+ (*(manager->env))->ExceptionClear (manager->env);
+ (*(manager->env))->PopLocalFrame (manager->env, NULL);
+ free (asset);
+
+ return NULL;
+}
+
+static AAsset *
+AAsset_close (AAsset *asset)
+{
+ JNIEnv *env;
+
+ env = asset->manager->env;
+
+ (*env)->CallVoidMethod (asset->manager->env,
+ asset->fd,
+ asset->manager->close);
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->fd);
+
+ if (asset->stream)
+ {
+ (*env)->CallVoidMethod (asset->manager->env,
+ asset->stream,
+ asset->manager->stream_close);
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->stream);
+ }
+
+ free (asset);
+}
+
+/* Create an input stream associated with the given ASSET. Set
+ ASSET->stream to its global reference.
+
+ Value is 1 upon failure, else 0. ASSET must not already have an
+ input stream. */
+
+static int
+android_asset_create_stream (AAsset *asset)
+{
+ jobject stream;
+ JNIEnv *env;
+
+ env = asset->manager->env;
+ stream
+ = (*env)->CallObjectMethod (env, asset->fd,
+ asset->manager->create_input_stream);
+
+ if (!stream)
+ {
+ (*env)->ExceptionClear (env);
+ return 1;
+ }
+
+ asset->stream
+ = (*env)->NewGlobalRef (env, stream);
+
+ if (!asset->stream)
+ {
+ (*env)->ExceptionClear (env);
+ (*env)->DeleteLocalRef (env, stream);
+ return 1;
+ }
+
+ (*env)->DeleteLocalRef (env, stream);
+ return 0;
+}
+
+/* Read NBYTES from the specified asset into the given BUFFER;
+
+ Internally, allocate a Java byte array containing 4096 elements and
+ copy the data to and from that array.
+
+ Value is the number of bytes actually read, 0 at EOF, or -1 upon
+ failure, in which case errno is set accordingly. If NBYTES is
+ zero, behavior is undefined. */
+
+static int
+android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
+{
+ jbyteArray stash;
+ JNIEnv *env;
+ jint bytes_read, total;
+
+ /* Allocate a suitable amount of storage. Either nbytes or 4096,
+ whichever is larger. */
+ env = asset->manager->env;
+ stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
+
+ if (!stash)
+ {
+ (*env)->ExceptionClear (env);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Try to create an input stream. */
+
+ if (!asset->stream
+ && android_asset_create_stream (asset))
+ {
+ (*env)->DeleteLocalRef (env, stash);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Start reading. */
+
+ total = 0;
+
+ while (nbytes)
+ {
+ bytes_read = (*env)->CallIntMethod (env, asset->stream,
+ asset->manager->read,
+ stash);
+
+ /* Detect error conditions. */
+
+ if ((*env)->ExceptionCheck (env))
+ goto out;
+
+ /* Detect EOF. */
+
+ if (bytes_read == -1)
+ goto out;
+
+ /* Finally write out the amount that was read. */
+ bytes_read = MIN (bytes_read, nbytes);
+ (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
+
+ buffer += bytes_read;
+ total += bytes_read;
+ nbytes -= bytes_read;
+ }
+
+ /* Make sure the value of nbytes still makes sense. */
+ assert (nbytes >= 0);
+
+ out:
+ (*env)->ExceptionClear (env);
+ (*env)->DeleteLocalRef (env, stash);
+ return total;
+}
+
+static long
+AAsset_getLength (AAsset *asset)
+{
+ JNIEnv *env;
+
+ if (asset->length != -1)
+ return asset->length;
+
+ env = asset->manager->env;
+ asset->length
+ = (*env)->CallLongMethod (env, asset->fd,
+ asset->manager->get_length);
+ return asset->length;
+}
+
+static char *
+AAsset_getBuffer (AAsset *asset)
+{
+ long length;
+ char *buffer;
+
+ length = AAsset_getLength (asset);
+
+ if (!length)
+ return NULL;
+
+ buffer = malloc (length);
+
+ if (!buffer)
+ return NULL;
+
+ if (android_asset_read_internal (asset, length, buffer)
+ != length)
+ {
+ xfree (buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static size_t
+AAsset_read (AAsset *asset, void *buffer, size_t size)
+{
+ return android_asset_read_internal (asset, MIN (size, INT_MAX),
+ buffer);
+}
+
+static off_t
+AAsset_seek (AAsset *asset, off_t offset, int whence)
+{
+ /* Java InputStreams don't support seeking at all. */
+ errno = ESPIPE;
+ return -1;
+}
diff --git a/src/android-emacs.c b/src/android-emacs.c
new file mode 100644
index 00000000000..e64caf9a9d4
--- /dev/null
+++ b/src/android-emacs.c
@@ -0,0 +1,169 @@
+/* Android initialization for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdio.h>
+#include <alloca.h>
+#include <string.h>
+#include <unistd.h>
+
+/* android-emacs is a wrapper around /system/bin/app_process(64).
+ It invokes app_process(64) with the right class path and then
+ starts org.gnu.emacs.EmacsNoninteractive.
+
+ The main function in that class tries to load an activity thread
+ and obtain a context and asset manager before calling
+ android_emacs_init, which is required for Emacs to find required
+ preloaded Lisp. */
+
+int
+main (int argc, char **argv)
+{
+ char **args;
+ int i;
+ char *bootclasspath, *emacs_class_path;
+
+ /* Allocate enough to hold the arguments to app_process. */
+ args = alloca ((10 + argc) * sizeof *args);
+
+ /* Clear args. */
+ memset (args, 0, (10 + argc) * sizeof *args);
+
+ /* First, figure out what program to start. */
+#if defined __x86_64__ || defined __aarch64__
+ args[0] = (char *) "/system/bin/app_process64";
+#else
+ args[0] = (char *) "/system/bin/app_process";
+#endif
+
+ /* Machines with ART require the boot classpath to be manually
+ specified. Machines with Dalvik however refuse to do so, as they
+ open the jars inside the BOOTCLASSPATH environment variable at
+ startup, resulting in the following crash:
+
+ W/dalvikvm( 1608): Refusing to reopen boot DEX
+ '/system/framework/core.jar'
+ W/dalvikvm( 1608): Refusing to reopen boot DEX
+ '/system/framework/bouncycastle.jar'
+ E/dalvikvm( 1608): Too many exceptions during init (failed on
+ 'Ljava/io/IOException;' 'Re-opening BOOTCLASSPATH DEX files is
+ not allowed')
+ E/dalvikvm( 1608): VM aborting */
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ if (android_get_device_api_level () < 21)
+ {
+ bootclasspath = NULL;
+ goto skip_setup;
+ }
+#else
+ if (__ANDROID_API__ < 21)
+ {
+ bootclasspath = NULL;
+ goto skip_setup;
+ }
+#endif
+
+ /* Next, obtain the boot class path. */
+ bootclasspath = getenv ("BOOTCLASSPATH");
+
+ if (!bootclasspath)
+ {
+ fprintf (stderr, "The BOOTCLASSPATH environment variable"
+ " is not set. As a result, Emacs does not know"
+ " how to start app_process.\n"
+ "This is likely a change in the Android platform."
+ " Please report this to bug-gnu-emacs@gnu.org.\n");
+ return 1;
+ }
+
+ skip_setup:
+
+ /* And the Emacs class path. */
+ emacs_class_path = getenv ("EMACS_CLASS_PATH");
+
+ if (!emacs_class_path)
+ {
+ fprintf (stderr, "EMACS_CLASS_PATH not set."
+ " Please make sure Emacs is being started"
+ " from within a running copy of Emacs.\n");
+ return 1;
+ }
+
+ if (bootclasspath)
+ {
+ if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s",
+ bootclasspath, emacs_class_path) < 0)
+ {
+ perror ("asprintf");
+ return 1;
+ }
+ }
+ else
+ {
+ if (asprintf (&bootclasspath, "-Djava.class.path=%s",
+ emacs_class_path) < 0)
+ {
+ perror ("asprintf");
+ return 1;
+ }
+ }
+
+ args[1] = bootclasspath;
+ args[2] = (char *) "/system/bin";
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ /* I don't know exactly when --nice-name was introduced; this is
+ just a guess. */
+ if (android_get_device_api_level () >= 26)
+ {
+ args[3] = (char *) "--nice-name=emacs";
+ args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+ /* Arguments from here on are passed to main in
+ EmacsNoninteractive.java. */
+ args[5] = argv[0];
+
+ /* Now copy the rest of the arguments over. */
+ for (i = 1; i < argc; ++i)
+ args[5 + i] = argv[i];
+ }
+ else
+ {
+#endif
+ args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+ /* Arguments from here on are passed to main in
+ EmacsNoninteractive.java. */
+ args[4] = argv[0];
+
+ /* Now copy the rest of the arguments over. */
+ for (i = 1; i < argc; ++i)
+ args[4 + i] = argv[i];
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ }
+#endif
+
+ /* Finally, try to start the app_process. */
+ execvp (args[0], args);
+
+ /* If exit fails, return an error indication. */
+ perror ("exec");
+ return 1;
+}
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 00000000000..8a41a7cdec5
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,6726 @@
+/* Android initialization for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <limits.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+/* Old NDK versions lack MIN and MAX. */
+#include <minmax.h>
+
+#include <assert.h>
+#include <fingerprint.h>
+
+#include "android.h"
+#include "androidgui.h"
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "epaths.h"
+
+/* Whether or not Emacs is running inside the application process and
+ Android windowing should be enabled. */
+bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+#if __ANDROID_API__ >= 9
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#else
+#include "android-asset.h"
+#endif
+
+#include <android/bitmap.h>
+#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))
+
+#define ANDROID_MAX_ASSET_FD 65535
+
+struct android_fd_table_entry
+{
+ /* Various flags associated with this table. */
+ short flags;
+
+ /* The stat buffer associated with this entry. */
+ struct stat statb;
+};
+
+enum android_fd_table_entry_flags
+ {
+ ANDROID_FD_TABLE_ENTRY_IS_VALID = 1,
+ };
+
+struct android_emacs_service
+{
+ jclass class;
+ jmethodID fill_rectangle;
+ jmethodID fill_polygon;
+ jmethodID draw_rectangle;
+ jmethodID draw_line;
+ jmethodID draw_point;
+ jmethodID copy_area;
+ jmethodID clear_window;
+ jmethodID clear_area;
+ jmethodID ring_bell;
+ jmethodID query_tree;
+ jmethodID get_screen_width;
+ jmethodID get_screen_height;
+ jmethodID detect_mouse;
+ jmethodID name_keysym;
+ jmethodID browse_url;
+ jmethodID restart_emacs;
+ jmethodID update_ic;
+ jmethodID reset_ic;
+ jmethodID open_content_uri;
+ jmethodID check_content_uri;
+ jmethodID query_battery;
+ jmethodID display_toast;
+ jmethodID update_extracted_text;
+ jmethodID update_cursor_anchor_info;
+};
+
+struct android_emacs_pixmap
+{
+ jclass class;
+ jmethodID constructor;
+ jmethodID constructor_mutable;
+};
+
+struct android_graphics_point
+{
+ jclass class;
+ jmethodID constructor;
+};
+
+struct android_emacs_drawable
+{
+ jclass class;
+ jmethodID get_bitmap;
+ jmethodID damage_rect;
+};
+
+struct android_emacs_window
+{
+ jclass class;
+ jmethodID swap_buffers;
+ jmethodID toggle_on_screen_keyboard;
+ jmethodID lookup_string;
+ jmethodID set_fullscreen;
+ jmethodID change_window_background;
+ jmethodID reparent_to;
+ jmethodID map_window;
+ jmethodID unmap_window;
+ jmethodID resize_window;
+ jmethodID move_window;
+ jmethodID make_input_focus;
+ jmethodID raise;
+ jmethodID lower;
+ jmethodID get_window_geometry;
+ jmethodID translate_coordinates;
+ jmethodID set_dont_accept_focus;
+ jmethodID set_dont_focus_on_map;
+ jmethodID define_cursor;
+};
+
+struct android_emacs_cursor
+{
+ jclass class;
+ jmethodID constructor;
+};
+
+/* The API level of the current device. */
+static int android_api_level;
+
+/* The asset manager being used. */
+static AAssetManager *asset_manager;
+
+/* Whether or not Emacs has been initialized. */
+static int emacs_initialized;
+
+/* The directory used to store site-lisp. */
+char *android_site_load_path;
+
+/* The directory used to store native libraries. */
+char *android_lib_dir;
+
+/* The directory used to store game files. */
+char *android_game_path;
+
+/* The directory used to store temporary files. */
+char *android_cache_dir;
+
+/* The list of archive files within which the Java virtual macine
+ looks for class files. */
+char *android_class_path;
+
+/* The display's pixel densities. */
+double android_pixel_density_x, android_pixel_density_y;
+
+/* The Android application data directory. */
+static char *android_files_dir;
+
+/* Array of structures used to hold asset information corresponding to
+ a file descriptor. This assumes Emacs does not do funny things
+ with dup. It currently does not. */
+static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD];
+
+/* The Java environment being used for the main thread. */
+JNIEnv *android_java_env;
+
+/* The EmacsGC class. */
+static jclass emacs_gc_class;
+
+/* Various fields. */
+static jfieldID emacs_gc_foreground, emacs_gc_background;
+static jfieldID emacs_gc_function, emacs_gc_clip_rects;
+static jfieldID emacs_gc_clip_x_origin, emacs_gc_clip_y_origin;
+static jfieldID emacs_gc_stipple, emacs_gc_clip_mask;
+static jfieldID emacs_gc_fill_style, emacs_gc_ts_origin_x;
+static jfieldID emacs_gc_ts_origin_y;
+
+/* The constructor and one function. */
+static jmethodID emacs_gc_constructor, emacs_gc_mark_dirty;
+
+/* The Rect class. */
+static jclass android_rect_class;
+
+/* Its constructor. */
+static jmethodID android_rect_constructor;
+
+/* The EmacsService object. */
+static jobject emacs_service;
+
+/* Various methods associated with the EmacsService. */
+static struct android_emacs_service service_class;
+
+/* Various methods associated with the EmacsPixmap class. */
+static struct android_emacs_pixmap pixmap_class;
+
+/* Various methods associated with the Point class. */
+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;
+
+/* Various methods associated with the EmacsCursor class. */
+static struct android_emacs_cursor cursor_class;
+
+/* The last event serial used. This is a 32 bit value, but it is
+ stored in unsigned long to be consistent with X. */
+unsigned int event_serial;
+
+#ifdef __i386__
+
+/* Unused pointer used to control compiler optimizations. */
+void *unused_pointer;
+
+#endif /* __i386__ */
+
+
+
+/* Event handling functions. Events are stored on a (circular) queue
+ that is read synchronously. The Android port replaces pselect with
+ a function android_select, which runs pselect in a separate thread,
+ but more importantly also waits for events to be available on the
+ android event queue. */
+
+struct android_event_container
+{
+ /* The next and last events in this queue. */
+ struct android_event_container *volatile next, *last;
+
+ /* The event itself. */
+ union android_event event;
+};
+
+struct android_event_queue
+{
+ /* Mutex protecting the event queue. */
+ pthread_mutex_t mutex;
+
+ /* Mutex protecting the select data. */
+ pthread_mutex_t select_mutex;
+
+ /* The thread used to run select. */
+ pthread_t select_thread;
+
+ /* Condition variables for the reading side. */
+ pthread_cond_t read_var;
+
+ /* The number of events in the queue. If this is greater than 1024,
+ writing will block. */
+ volatile int num_events;
+
+ /* Circular queue of events. */
+ struct android_event_container events;
+};
+
+/* Arguments to pselect used by the select thread. */
+static volatile int android_pselect_nfds;
+static fd_set *volatile android_pselect_readfds;
+static fd_set *volatile android_pselect_writefds;
+static fd_set *volatile android_pselect_exceptfds;
+static struct timespec *volatile android_pselect_timeout;
+
+/* Value of pselect. */
+static int android_pselect_rc;
+
+/* The global event queue. */
+static struct android_event_queue event_queue;
+
+/* Semaphores used to signal select completion and start. */
+static sem_t android_pselect_sem, android_pselect_start_sem;
+
+#if __ANDROID_API__ < 16
+
+/* Select self-pipe. */
+static int select_pipe[2];
+
+#else
+
+/* Whether or not pselect has been interrupted. */
+static volatile sig_atomic_t android_pselect_interrupted;
+
+#endif
+
+static void *
+android_run_select_thread (void *data)
+{
+ /* Apparently this is required too. */
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ int rc;
+#if __ANDROID_API__ < 16
+ int nfds;
+ fd_set readfds, writefds;
+ char byte;
+#else
+ sigset_t signals, waitset;
+ int sig;
+#endif
+
+#if __ANDROID_API__ < 16
+ /* A completely different implementation is used when building for
+ Android versions earlier than 16, because pselect with a signal
+ mask does not work there. Instead of blocking SIGUSR1 and
+ unblocking it inside pselect, a file descriptor is used instead.
+ Something is written to the file descriptor every time select is
+ supposed to return. */
+
+ while (true)
+ {
+ /* Wait for the thread to be released. */
+ while (sem_wait (&android_pselect_start_sem) < 0)
+ ;;
+
+ /* Get the select lock and call pselect. API 8 does not have
+ working pselect in any sense. Instead, pselect wakes up on
+ select_pipe[0]. */
+
+ pthread_mutex_lock (&event_queue.select_mutex);
+ nfds = android_pselect_nfds;
+
+ if (android_pselect_readfds)
+ readfds = *android_pselect_readfds;
+ else
+ FD_ZERO (&readfds);
+
+ if (nfds < select_pipe[0] + 1)
+ nfds = select_pipe[0] + 1;
+ FD_SET (select_pipe[0], &readfds);
+
+ rc = pselect (nfds, &readfds, &writefds,
+ android_pselect_exceptfds,
+ android_pselect_timeout,
+ NULL);
+
+ /* Subtract 1 from rc if writefds contains the select pipe. */
+ if (FD_ISSET (select_pipe[0], &writefds))
+ rc -= 1;
+
+ /* Save the writefds back again. */
+ if (android_pselect_writefds)
+ *android_pselect_writefds = writefds;
+
+ android_pselect_rc = rc;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Signal the main thread that there is now data to read. Hold
+ the event queue lock during this process to make sure this
+ does not happen before the main thread begins to wait for the
+ condition variable. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Read a single byte from the select pipe. */
+ read (select_pipe[0], &byte, 1);
+
+ /* Signal the Emacs thread that pselect is done. If read_var
+ was signaled by android_write_event, event_queue.mutex could
+ still be locked, so this must come before. */
+ sem_post (&android_pselect_sem);
+ }
+#else
+ if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_sigmask: %s",
+ strerror (errno));
+
+ sigfillset (&signals);
+ sigdelset (&signals, SIGUSR1);
+ sigemptyset (&waitset);
+ sigaddset (&waitset, SIGUSR1);
+
+ while (true)
+ {
+ /* Wait for the thread to be released. */
+ while (sem_wait (&android_pselect_start_sem) < 0)
+ ;;
+
+ /* Clear the ``pselect interrupted'' flag. This is safe because
+ right now, SIGUSR1 is blocked. */
+ android_pselect_interrupted = 0;
+
+ /* Get the select lock and call pselect. */
+ pthread_mutex_lock (&event_queue.select_mutex);
+ rc = pselect (android_pselect_nfds,
+ android_pselect_readfds,
+ android_pselect_writefds,
+ android_pselect_exceptfds,
+ android_pselect_timeout,
+ &signals);
+ android_pselect_rc = rc;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Signal the main thread that there is now data to read. Hold
+ the event queue lock during this process to make sure this
+ does not happen before the main thread begins to wait for the
+ condition variable. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Check `android_pselect_interrupted' instead of rc and errno.
+
+ This is because `pselect' does not return an rc of -1 upon
+ being interrupted in some versions of Android, but does set
+ signal masks correctly. */
+
+ if (!android_pselect_interrupted)
+ /* Now, wait for SIGUSR1, unless pselect was interrupted and
+ the signal was already delivered. The Emacs thread will
+ always send this signal after read_var is triggered or the
+ UI thread has sent an event. */
+ sigwait (&waitset, &sig);
+
+ /* Signal the Emacs thread that pselect is done. If read_var
+ was signaled by android_write_event, event_queue.mutex could
+ still be locked, so this must come before. */
+ sem_post (&android_pselect_sem);
+ }
+#endif
+
+ return NULL;
+}
+
+#if __ANDROID_API__ >= 16
+
+static void
+android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
+{
+ /* Notice that pselect has been interrupted. */
+ android_pselect_interrupted = 1;
+}
+
+#endif
+
+/* Semaphore used to indicate completion of a query.
+ This should ideally be defined further down. */
+static sem_t android_query_sem;
+
+/* Set up the global event queue by initializing the mutex and two
+ condition variables, and the linked list of events. This must be
+ called before starting the Emacs thread. Also, initialize the
+ thread used to run pselect.
+
+ These functions must also use the C library malloc and free,
+ because xmalloc is not thread safe. */
+
+static void
+android_init_events (void)
+{
+ struct sigaction sa;
+
+ if (pthread_mutex_init (&event_queue.mutex, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_mutex_init: %s",
+ strerror (errno));
+
+ if (pthread_mutex_init (&event_queue.select_mutex, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_mutex_init: %s",
+ strerror (errno));
+
+ if (pthread_cond_init (&event_queue.read_var, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_cond_init: %s",
+ strerror (errno));
+
+ sem_init (&android_pselect_sem, 0, 0);
+ sem_init (&android_pselect_start_sem, 0, 0);
+ sem_init (&android_query_sem, 0, 0);
+
+ event_queue.events.next = &event_queue.events;
+ event_queue.events.last = &event_queue.events;
+
+#if __ANDROID_API__ >= 16
+
+ /* Before starting the select thread, make sure the disposition for
+ SIGUSR1 is correct. */
+ sigfillset (&sa.sa_mask);
+ sa.sa_sigaction = android_handle_sigusr1;
+ sa.sa_flags = SA_SIGINFO;
+
+#else
+
+ /* Set up the file descriptor used to wake up pselect. */
+ if (pipe2 (select_pipe, O_CLOEXEC) < 0)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pipe2: %s", strerror (errno));
+
+ /* Make sure the read end will fit in fd_set. */
+ if (select_pipe[0] >= FD_SETSIZE)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "read end of select pipe"
+ " lies outside FD_SETSIZE!");
+
+#endif
+
+ if (sigaction (SIGUSR1, &sa, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "sigaction: %s",
+ strerror (errno));
+
+ /* Start the select thread. */
+ if (pthread_create (&event_queue.select_thread, NULL,
+ android_run_select_thread, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_create: %s",
+ strerror (errno));
+}
+
+int
+android_pending (void)
+{
+ int i;
+
+ pthread_mutex_lock (&event_queue.mutex);
+ i = event_queue.num_events;
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ return i;
+}
+
+/* Wait for events to become available synchronously. Return once an
+ event arrives. */
+
+void
+android_wait_event (void)
+{
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Wait for events to appear if there are none available to
+ read. */
+ if (!event_queue.num_events)
+ pthread_cond_wait (&event_queue.read_var,
+ &event_queue.mutex);
+
+ pthread_mutex_unlock (&event_queue.mutex);
+}
+
+void
+android_next_event (union android_event *event_return)
+{
+ struct android_event_container *container;
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Wait for events to appear if there are none available to
+ read. */
+ if (!event_queue.num_events)
+ pthread_cond_wait (&event_queue.read_var,
+ &event_queue.mutex);
+
+ /* Obtain the event from the end of the queue. */
+ container = event_queue.events.last;
+ eassert (container != &event_queue.events);
+
+ /* Remove the event from the queue and copy it to the caller
+ supplied buffer. */
+ container->last->next = container->next;
+ container->next->last = container->last;
+ *event_return = container->event;
+ event_queue.num_events--;
+
+ /* Free the container. */
+ free (container);
+
+ /* Unlock the queue. */
+ pthread_mutex_unlock (&event_queue.mutex);
+}
+
+bool
+android_check_if_event (union android_event *event_return,
+ bool (*predicate) (union android_event *,
+ void *),
+ void *arg)
+{
+ struct android_event_container *container;
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Loop over each event. */
+ container = event_queue.events.last;
+ for (; container != &event_queue.events; container = container->last)
+ {
+ /* See if the predicate matches. */
+ if ((*predicate) (&container->event, arg))
+ {
+ /* Copy out the event and return true. */
+ *event_return = container->event;
+ --event_queue.num_events;
+
+ /* Unlink container. */
+ container->last->next = container->next;
+ container->next->last = container->last;
+ free (container);
+ pthread_mutex_unlock (&event_queue.mutex);
+ return true;
+ }
+ }
+
+ pthread_mutex_unlock (&event_queue.mutex);
+ return false;
+}
+
+void
+android_write_event (union android_event *event)
+{
+ struct android_event_container *container;
+
+ container = malloc (sizeof *container);
+
+ if (!container)
+ return;
+
+ /* If the event queue hasn't been initialized yet, return false. */
+ if (!event_queue.events.next)
+ return;
+
+ pthread_mutex_lock (&event_queue.mutex);
+ container->next = event_queue.events.next;
+ container->last = &event_queue.events;
+ container->next->last = container;
+ container->last->next = container;
+ container->event = *event;
+ event_queue.num_events++;
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Now set pending_signals to true, and raise SIGIO to interrupt any
+ ongoing reads if the event is important. */
+ pending_signals = true;
+
+ switch (event->type)
+ {
+ /* Key press and window action events are considered important,
+ as they either end up quitting or asking for responses to the
+ IME. */
+ case ANDROID_KEY_PRESS:
+ case ANDROID_WINDOW_ACTION:
+ raise (SIGIO);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int
+android_select (int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timespec *timeout)
+{
+ int nfds_return;
+#if __ANDROID_API__ < 16
+ static char byte;
+#endif
+
+ /* Check for and run anything the UI thread wants to run on the main
+ thread. */
+ android_check_query ();
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ if (event_queue.num_events)
+ {
+ pthread_mutex_unlock (&event_queue.mutex);
+ return 1;
+ }
+
+ nfds_return = 0;
+
+ pthread_mutex_lock (&event_queue.select_mutex);
+ android_pselect_nfds = nfds;
+ android_pselect_readfds = readfds;
+ android_pselect_writefds = writefds;
+ android_pselect_exceptfds = exceptfds;
+ android_pselect_timeout = timeout;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Release the select thread. */
+ sem_post (&android_pselect_start_sem);
+
+ /* Start waiting for the event queue condition to be set. */
+ pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
+
+#if __ANDROID_API__ >= 16
+ /* Interrupt the select thread now, in case it's still in
+ pselect. */
+ pthread_kill (event_queue.select_thread, SIGUSR1);
+#else
+ /* Interrupt the select thread by writing to the select pipe. */
+ if (write (select_pipe[1], &byte, 1) != 1)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "write: %s", strerror (errno));
+#endif
+
+ /* Unlock the event queue mutex. */
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Wait for pselect to return in any case. This must be done with
+ the event queue mutex unlocked. Otherwise, the pselect thread
+ can hang if it tries to lock the event queue mutex to signal
+ read_var after the UI thread has already done so. */
+ while (sem_wait (&android_pselect_sem) < 0)
+ ;;
+
+ /* If there are now events in the queue, return 1. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ if (event_queue.num_events)
+ nfds_return = 1;
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Add the return value of pselect. */
+ if (android_pselect_rc >= 0)
+ nfds_return += android_pselect_rc;
+
+ if (!nfds_return && android_pselect_rc < 0)
+ nfds_return = android_pselect_rc;
+
+ /* This is to shut up process.c when pselect gets EINTR. */
+ if (nfds_return < 0)
+ errno = EINTR;
+
+ /* Now check for and run anything the UI thread wants to run in the
+ main thread. */
+ android_check_query ();
+
+ return nfds_return;
+}
+
+
+
+static void *
+android_run_debug_thread (void *data)
+{
+ FILE *file;
+ int fd;
+ char *line;
+ size_t n;
+
+ fd = (int) (intptr_t) data;
+ file = fdopen (fd, "r");
+
+ if (!file)
+ return NULL;
+
+ line = NULL;
+
+ while (true)
+ {
+ if (getline (&line, &n, file) < 0)
+ {
+ free (line);
+ break;
+ }
+
+ __android_log_print (ANDROID_LOG_INFO, __func__, "%s", line);
+ }
+
+ fclose (file);
+ return NULL;
+}
+
+
+
+/* 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 ();
+
+ /* Return the size of the directory tree as the limit.
+ Do not subtract the initial header bytes, as the limit
+ is an offset from the start of the file. */
+
+ if (limit_return)
+ *limit_return = directory_tree_size;
+
+ 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;
+}
+
+/* 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
+ pw->pw_gecos is NULL. */
+
+char *
+android_user_full_name (struct passwd *pw)
+{
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+ if (!pw->pw_gecos)
+ return (char *) "Android user";
+
+ return pw->pw_gecos;
+#else
+ return "Android user";
+#endif
+}
+
+/* Given a real file name, return the part that describes its asset
+ path, or NULL if it is not an asset. */
+
+static const char *
+android_get_asset_name (const char *filename)
+{
+ if (!strcmp (filename, "/assets") || !strcmp (filename, "/assets/"))
+ return "/";
+
+ if (!strncmp (filename, "/assets/", sizeof "/assets/" - 1))
+ return filename + (sizeof "/assets/" - 1);
+
+ return NULL;
+}
+
+/* Return whether or not the specified FILENAME actually resolves to a
+ content resolver URI. */
+
+static bool
+android_content_name_p (const char *filename)
+{
+ /* Content URIs aren't supported before Android 4.4, so return
+ false. */
+
+ if (android_api_level < 19)
+ return false;
+
+ return (!strcmp (filename, "/content")
+ || !strncmp (filename, "/content/",
+ sizeof "/content/" - 1));
+}
+
+/* Return the content URI corresponding to a `/content' file name,
+ or NULL if it is not a content URI.
+
+ This function is not reentrant. */
+
+static const char *
+android_get_content_name (const char *filename)
+{
+ static char buffer[PATH_MAX + 1];
+ char *head, *token, *saveptr, *copy;
+ size_t n;
+
+ n = PATH_MAX;
+
+ /* First handle content ``URIs'' without a provider. */
+
+ if (!strcmp (filename, "/content")
+ || !strcmp (filename, "/content/"))
+ return "content://";
+
+ /* Next handle ordinary file names. */
+
+ if (strncmp (filename, "/content/", sizeof "/content/" - 1))
+ return NULL;
+
+ /* Forward past the first directory specifying the schema. */
+
+ copy = xstrdup (filename + sizeof "/content");
+ token = saveptr = NULL;
+ head = stpcpy (buffer, "content:/");
+
+ /* Split FILENAME by slashes. */
+
+ while ((token = strtok_r (!token ? copy : NULL,
+ "/", &saveptr)))
+ {
+ head = stpncpy (head, "/", n--);
+ head = stpncpy (head, token, n);
+
+ /* Check that head has not overflown the buffer. */
+ eassert ((head - buffer) <= PATH_MAX);
+
+ n = PATH_MAX - (head - buffer);
+ }
+
+ /* Make sure the given buffer ends up NULL terminated. */
+ buffer[PATH_MAX] = '\0';
+ xfree (copy);
+
+ return buffer;
+}
+
+/* Return whether or not the specified FILENAME is an accessible
+ content URI. MODE specifies what to check. */
+
+static bool
+android_check_content_access (const char *filename, int mode)
+{
+ const char *name;
+ jobject string;
+ size_t length;
+ jboolean rc;
+
+ name = android_get_content_name (filename);
+ length = strlen (name);
+
+ string = (*android_java_env)->NewByteArray (android_java_env,
+ length);
+ android_exception_check ();
+
+ (*android_java_env)->SetByteArrayRegion (android_java_env,
+ string, 0, length,
+ (jbyte *) name);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ emacs_service,
+ service_class.check_content_uri,
+ string,
+ (jboolean) ((mode & R_OK)
+ != 0),
+ (jboolean) ((mode & W_OK)
+ != 0));
+ android_exception_check_1 (string);
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ return rc;
+}
+
+/* Like fstat. However, look up the asset corresponding to the file
+ descriptor. If it exists, return the right information. */
+
+int
+android_fstat (int fd, struct stat *statb)
+{
+ if (fd < ANDROID_MAX_ASSET_FD
+ && (android_table[fd].flags
+ & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+ {
+ memcpy (statb, &android_table[fd].statb,
+ sizeof *statb);
+ return 0;
+ }
+
+ 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, 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,
+ struct stat *restrict statbuf, int flags)
+{
+ AAsset *asset_desc;
+ const char *asset;
+ const char *asset_dir;
+ int fd, rc;
+
+ /* 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);
+
+ if (!asset_desc)
+ return ENOENT;
+
+ memset (statbuf, 0, sizeof *statbuf);
+
+ /* Fill in the stat buffer. */
+ statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ statbuf->st_size = AAsset_getLength (asset_desc);
+
+ /* Close the asset. */
+ AAsset_close (asset_desc);
+ return 0;
+ }
+
+ if (dirfd == AT_FDCWD
+ && android_init_gui
+ && android_content_name_p (pathname))
+ {
+ /* This is actually a content:// URI. Open that file and call
+ stat on it. */
+
+ fd = android_open (pathname, O_RDONLY, 0);
+
+ if (fd < 0)
+ return -1;
+
+ rc = fstat (fd, statbuf);
+ android_close (fd);
+ return rc;
+ }
+
+ return fstatat (dirfd, pathname, statbuf, flags);
+}
+
+/* Return if NAME, a file name relative to the /assets directory, is
+ accessible, as long as !(amode & W_OK). */
+
+static bool
+android_file_access_p (const char *name, int amode)
+{
+ if (!asset_manager)
+ return false;
+
+ if (!(amode & W_OK))
+ {
+ if (!strcmp (name, "") || !strcmp (name, "/"))
+ /* /assets always exists. */
+ 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;
+}
+
+/* Do the same as android_hack_asset_fd, but use an unlinked temporary
+ file to cater to old Android kernels where ashmem files are not
+ readable. */
+
+static int
+android_hack_asset_fd_fallback (AAsset *asset)
+{
+ int fd;
+ char filename[PATH_MAX];
+ size_t size;
+ void *mem;
+
+ /* Assets must be small enough to fit in size_t, if off_t is
+ larger. */
+ size = AAsset_getLength (asset);
+
+ /* Get an unlinked file descriptor from a file in the cache
+ directory, which is guaranteed to only be written to by Emacs.
+ Creating an ashmem file descriptor and reading from it doesn't
+ work on these old Android versions. */
+
+ snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d",
+ android_cache_dir, getpid ());
+ fd = open (filename, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+
+ if (fd < 0)
+ return -1;
+
+ if (unlink (filename))
+ goto fail;
+
+ if (ftruncate (fd, size))
+ goto fail;
+
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ goto fail;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ goto fail;
+ }
+
+ munmap (mem, size);
+ return fd;
+
+ fail:
+ close (fd);
+ return -1;
+}
+
+/* Pointer to the `ASharedMemory_create' function which is loaded
+ dynamically. */
+static int (*asharedmemory_create) (const char *, size_t);
+
+/* Return whether or not shared memory file descriptors can also be
+ read from, and are thus suitable for creating asset files.
+
+ This does not work on some ancient Android systems running old
+ versions of the kernel. */
+
+static bool
+android_detect_ashmem (void)
+{
+ int fd, rc;
+ void *mem;
+ char test_buffer[10];
+
+ memcpy (test_buffer, "abcdefghi", 10);
+
+ /* Create the file descriptor to be used for the test. */
+
+ /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+ prefer that over using ASharedMemory. */
+
+ if (android_api_level <= 28)
+ {
+ fd = open ("/dev/ashmem", O_RDWR);
+
+ if (fd < 0)
+ return false;
+
+ /* An empty name means the memory area will exist until the file
+ descriptor is closed, because no other process can
+ attach. */
+ rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+ if (rc < 0)
+ {
+ close (fd);
+ return false;
+ }
+
+ rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer);
+
+ if (rc < 0)
+ {
+ close (fd);
+ return false;
+ }
+ }
+ else
+ {
+ /* On the other hand, SELinux restrictions on Android 29 and
+ later require that Emacs use a system service to obtain
+ shared memory. Load this dynamically, as this service is not
+ available on all versions of the NDK. */
+
+ if (!asharedmemory_create)
+ {
+ *(void **) (&asharedmemory_create)
+ = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+ if (!asharedmemory_create)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "dlsym: %s\n",
+ strerror (errno));
+ emacs_abort ();
+ }
+ }
+
+ fd = asharedmemory_create ("", sizeof test_buffer);
+
+ if (fd < 0)
+ return false;
+ }
+
+ /* Now map the resource and write the test contents. */
+
+ mem = mmap (NULL, sizeof test_buffer, PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ close (fd);
+ return false;
+ }
+
+ /* Copy over the test contents. */
+ memcpy (mem, test_buffer, sizeof test_buffer);
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, sizeof test_buffer);
+
+ /* Try to read the content back into test_buffer. If this does not
+ compare equal to the original string, or the read fails, then
+ ashmem descriptors are not readable on this system. */
+
+ if ((read (fd, test_buffer, sizeof test_buffer)
+ != sizeof test_buffer)
+ || memcmp (test_buffer, "abcdefghi", sizeof test_buffer))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "/dev/ashmem does not produce real"
+ " temporary files on this system, so"
+ " Emacs will fall back to creating"
+ " unlinked temporary files.");
+ close (fd);
+ return false;
+ }
+
+ close (fd);
+ return true;
+}
+
+/* Get a file descriptor backed by a temporary in-memory file for the
+ given asset. */
+
+static int
+android_hack_asset_fd (AAsset *asset)
+{
+ static bool ashmem_readable_p;
+ static bool ashmem_initialized;
+ int fd, rc;
+ unsigned char *mem;
+ size_t size;
+
+ /* The first time this function is called, try to determine whether
+ or not ashmem file descriptors can be read from. */
+
+ if (!ashmem_initialized)
+ ashmem_readable_p
+ = android_detect_ashmem ();
+ ashmem_initialized = true;
+
+ /* If it isn't, fall back. */
+
+ if (!ashmem_readable_p)
+ return android_hack_asset_fd_fallback (asset);
+
+ /* Assets must be small enough to fit in size_t, if off_t is
+ larger. */
+ size = AAsset_getLength (asset);
+
+ /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+ prefer that over using ASharedMemory. */
+
+ if (android_api_level <= 28)
+ {
+ fd = open ("/dev/ashmem", O_RDWR);
+
+ if (fd < 0)
+ return -1;
+
+ /* An empty name means the memory area will exist until the file
+ descriptor is closed, because no other process can
+ attach. */
+ rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ioctl ASHMEM_SET_NAME: %s",
+ strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ rc = ioctl (fd, ASHMEM_SET_SIZE, size);
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ioctl ASHMEM_SET_SIZE: %s",
+ strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (!size)
+ return fd;
+
+ /* Now map the resource. */
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, size);
+ return fd;
+ }
+
+ /* On the other hand, SELinux restrictions on Android 29 and later
+ require that Emacs use a system service to obtain shared memory.
+ Load this dynamically, as this service is not available on all
+ versions of the NDK. */
+
+ if (!asharedmemory_create)
+ {
+ *(void **) (&asharedmemory_create)
+ = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+ if (!asharedmemory_create)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "dlsym: %s\n",
+ strerror (errno));
+ emacs_abort ();
+ }
+ }
+
+ fd = asharedmemory_create ("", size);
+
+ if (fd < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ASharedMemory_create: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ /* Now map the resource. */
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, size);
+ return fd;
+}
+
+/* Make FD close-on-exec. If any system call fails, do not abort, but
+ log a warning to the system log. */
+
+static void
+android_close_on_exec (int fd)
+{
+ int flags, rc;
+
+ flags = fcntl (fd, F_GETFD);
+
+ if (flags < 0)
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "fcntl: %s", strerror (errno));
+ return;
+ }
+
+ rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC);
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "fcntl: %s", strerror (errno));
+ return;
+ }
+}
+
+/* `open' and such are modified even though they exist on Android,
+ because Emacs treats "/assets/" as a special directory that must
+ contain all assets in the application package. */
+
+int
+android_open (const char *filename, int oflag, mode_t mode)
+{
+ const char *name;
+ AAsset *asset;
+ int fd;
+ size_t length;
+ jobject string;
+
+ if (asset_manager && (name = android_get_asset_name (filename)))
+ {
+ /* If Emacs is trying to write to the file, return NULL. */
+
+ if (oflag & O_WRONLY || oflag & O_RDWR)
+ {
+ errno = EROFS;
+ return -1;
+ }
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* If given AASSET_MODE_BUFFER (which is what Emacs probably
+ does, given that a file descriptor is not always available),
+ the framework fails to uncompress the data before it returns
+ a file descriptor. */
+ asset = AAssetManager_open (asset_manager, name,
+ AASSET_MODE_STREAMING);
+
+ if (!asset)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Create a shared memory file descriptor containing the asset
+ contents.
+
+ The documentation misleads people into thinking that
+ AAsset_openFileDescriptor does precisely this. However, it
+ instead returns an offset into any uncompressed assets in the
+ ZIP archive. This cannot be found in its documentation. */
+
+ fd = android_hack_asset_fd (asset);
+
+ if (fd == -1)
+ {
+ AAsset_close (asset);
+ errno = ENXIO;
+ return -1;
+ }
+
+ /* If O_CLOEXEC is specified, make the file descriptor close on
+ exec too. */
+ if (oflag & O_CLOEXEC)
+ android_close_on_exec (fd);
+
+ if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
+ {
+ /* Too bad. Pretend this is an out of memory error. */
+ errno = ENOMEM;
+
+ if (fd >= 0)
+ close (fd);
+
+ fd = -1;
+ }
+ else
+ {
+ assert (!(android_table[fd].flags
+ & ANDROID_FD_TABLE_ENTRY_IS_VALID));
+ android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID;
+ memset (&android_table[fd].statb, 0,
+ sizeof android_table[fd].statb);
+
+ /* Fill in some information that will be reported to
+ callers of android_fstat, among others. */
+ android_table[fd].statb.st_mode
+ = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ /* Owned by root. */
+ android_table[fd].statb.st_uid = 0;
+ android_table[fd].statb.st_gid = 0;
+
+ /* Size of the file. */
+ android_table[fd].statb.st_size
+ = AAsset_getLength (asset);
+ }
+
+ AAsset_close (asset);
+ return fd;
+ }
+
+ if (android_init_gui && android_content_name_p (filename))
+ {
+ /* This is a content:// URI. Ask the system for a descriptor to
+ that file. */
+
+ name = android_get_content_name (filename);
+ length = strlen (name);
+
+ /* Check if the mode is valid. */
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* Allocate a buffer to hold the file name. */
+ string = (*android_java_env)->NewByteArray (android_java_env,
+ length);
+ if (!string)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ errno = ENOMEM;
+ return -1;
+ }
+ (*android_java_env)->SetByteArrayRegion (android_java_env,
+ string, 0, length,
+ (jbyte *) name);
+
+ /* Try to open the file descriptor. */
+
+ fd
+ = (*android_java_env)->CallIntMethod (android_java_env,
+ emacs_service,
+ service_class.open_content_uri,
+ string,
+ (jboolean) ((mode & O_WRONLY
+ || mode & O_RDWR)
+ != 0),
+ (jboolean) !(mode & O_WRONLY),
+ (jboolean) ((mode & O_TRUNC)
+ != 0));
+
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ errno = ENOMEM;
+ ANDROID_DELETE_LOCAL_REF (string);
+ return -1;
+ }
+
+ /* If fd is -1, just assume that the file does not exist,
+ and return -1 with errno set to ENOENT. */
+
+ if (fd == -1)
+ {
+ errno = ENOENT;
+ goto skip;
+ }
+
+ if (mode & O_CLOEXEC)
+ android_close_on_exec (fd);
+
+ skip:
+ ANDROID_DELETE_LOCAL_REF (string);
+ return fd;
+ }
+
+ return open (filename, oflag, mode);
+}
+
+/* Like close. However, remove the file descriptor from the asset
+ table as well. */
+
+int
+android_close (int fd)
+{
+ if (fd < ANDROID_MAX_ASSET_FD)
+ android_table[fd].flags = 0;
+
+ return close (fd);
+}
+
+/* Like fclose. However, remove any information associated with
+ FILE's file descriptor from the asset table as well. */
+
+int
+android_fclose (FILE *stream)
+{
+ int fd;
+
+ fd = fileno (stream);
+
+ if (fd != -1 && fd < ANDROID_MAX_ASSET_FD)
+ android_table[fd].flags = 0;
+
+ return fclose (stream);
+}
+
+/* Return the current user's ``home'' directory, which is actually the
+ app data directory on Android. */
+
+const char *
+android_get_home_directory (void)
+{
+ return android_files_dir;
+}
+
+/* Return the name of the file behind a file descriptor FD by reading
+ /proc/self/fd/. Place the name in BUFFER, which should be able to
+ hold size bytes. Value is 0 upon success, and 1 upon failure. */
+
+static int
+android_proc_name (int fd, char *buffer, size_t size)
+{
+ char format[sizeof "/proc/self/fd/"
+ + INT_STRLEN_BOUND (int)];
+ ssize_t read;
+
+ sprintf (format, "/proc/self/fd/%d", fd);
+ read = readlink (format, buffer, size - 1);
+
+ if (read == -1)
+ return 1;
+
+ buffer[read] = '\0';
+ return 0;
+}
+
+
+
+/* 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 jint JNICALL
+NATIVE_NAME (dup) (JNIEnv *env, jobject object, jint fd)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ return dup (fd);
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char buffer[sizeof fingerprint * 2 + 1];
+
+ memset (buffer, 0, sizeof buffer);
+ hexbuf_digest (buffer, (char *) fingerprint,
+ sizeof fingerprint);
+
+ return (*env)->NewStringUTF (env, buffer);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
+ jobject local_asset_manager,
+ jobject files_dir, jobject libs_dir,
+ jobject cache_dir,
+ jfloat pixel_density_x,
+ jfloat pixel_density_y,
+ jobject class_path,
+ jobject emacs_service_object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ 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. */
+ if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_SEQ_CST))
+ {
+ ANDROID_THROW (env, "java/lang/IllegalArgumentException",
+ "Emacs was already initialized!");
+ return;
+ }
+
+ android_pixel_density_x = pixel_density_x;
+ android_pixel_density_y = pixel_density_y;
+
+ __android_log_print (ANDROID_LOG_INFO, __func__,
+ "Initializing "PACKAGE_STRING"...\nPlease report bugs to "
+ PACKAGE_BUGREPORT". Thanks.\n");
+
+ /* 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);
+
+ if (emacs_service_object)
+ {
+ /* Create a pipe and duplicate it to stdout and stderr. Next,
+ make a thread that prints stderr to the system log.
+
+ Notice that this function is called in one of two ways. The
+ first is when Emacs is being started as a GUI application by
+ the system, and the second is when Emacs is being started by
+ libandroid-emacs.so as an ordinary noninteractive Emacs.
+
+ In the second case, stderr is usually connected to a PTY, so
+ this is unnecessary. */
+
+ if (pipe2 (pipefd, O_CLOEXEC) < 0)
+ emacs_abort ();
+
+ if (dup2 (pipefd[1], 2) < 0)
+ emacs_abort ();
+ close (pipefd[1]);
+
+ if (pthread_create (&thread, NULL, android_run_debug_thread,
+ (void *) (intptr_t) pipefd[0]))
+ emacs_abort ();
+ }
+
+ /* Now set the path to the site load directory. */
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) files_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_files_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) files_dir,
+ java_string);
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) libs_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_lib_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) libs_dir,
+ java_string);
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) cache_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_cache_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) cache_dir,
+ java_string);
+
+ if (class_path)
+ {
+ java_string = (*env)->GetStringUTFChars (env, (jstring) class_path,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_class_path = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) class_path,
+ java_string);
+ }
+
+ /* Calculate the site-lisp path. */
+
+ android_site_load_path = malloc (PATH_MAX + 1);
+
+ if (!android_site_load_path)
+ emacs_abort ();
+
+ android_game_path = malloc (PATH_MAX + 1);
+
+ if (!android_game_path)
+ emacs_abort ();
+
+ snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
+ android_files_dir);
+ snprintf (android_game_path, PATH_MAX, "%s/scores", android_files_dir);
+
+ __android_log_print (ANDROID_LOG_INFO, __func__,
+ "Site-lisp directory: %s\n"
+ "Files directory: %s\n"
+ "Native code directory: %s\n"
+ "Game score path: %s\n"
+ "Class path: %s\n",
+ android_site_load_path,
+ android_files_dir,
+ android_lib_dir, android_game_path,
+ (android_class_path
+ ? android_class_path
+ : "None"));
+
+ if (android_class_path)
+ /* Set EMACS_CLASS_PATH to the class path where
+ EmacsNoninteractive can be found. */
+ setenv ("EMACS_CLASS_PATH", android_class_path, 1);
+
+ /* Set LD_LIBRARY_PATH to an appropriate value. */
+ setenv ("LD_LIBRARY_PATH", android_lib_dir, 1);
+
+ /* Make a reference to the Emacs service. */
+
+ if (emacs_service_object)
+ {
+ emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
+
+ if (!emacs_service)
+ emacs_abort ();
+ }
+
+ /* Set up events. */
+ android_init_events ();
+
+ /* OK, setup is now complete. The caller may start the Emacs thread
+ now. */
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getProcName) (JNIEnv *env, jobject object, jint fd)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char buffer[PATH_MAX + 1];
+ size_t length;
+ jbyteArray array;
+
+ if (android_proc_name (fd, buffer, PATH_MAX + 1))
+ return NULL;
+
+ /* Return a byte array, as Java strings cannot always encode file
+ names. */
+ length = strlen (buffer);
+ array = (*env)->NewByteArray (env, length);
+ if (!array)
+ return NULL;
+
+ (*env)->SetByteArrayRegion (env, array, 0, length,
+ (jbyte *) buffer);
+
+ return array;
+}
+
+/* Initialize service_class, aborting if something goes wrong. */
+
+static void
+android_init_emacs_service (void)
+{
+ jclass old;
+
+ service_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsService");
+ eassert (service_class.class);
+
+ old = service_class.class;
+ service_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!service_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ service_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ service_class.class, \
+ name, signature); \
+ assert (service_class.c_name);
+
+ FIND_METHOD (fill_rectangle, "fillRectangle",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (fill_polygon, "fillPolygon",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;"
+ "[Landroid/graphics/Point;)V");
+ FIND_METHOD (draw_rectangle, "drawRectangle",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (draw_line, "drawLine",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (draw_point, "drawPoint",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;II)V");
+ FIND_METHOD (copy_area, "copyArea",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIIIII)V");
+ FIND_METHOD (clear_window, "clearWindow",
+ "(Lorg/gnu/emacs/EmacsWindow;)V");
+ FIND_METHOD (clear_area, "clearArea",
+ "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+ FIND_METHOD (ring_bell, "ringBell", "()V");
+ FIND_METHOD (query_tree, "queryTree",
+ "(Lorg/gnu/emacs/EmacsWindow;)[S");
+ FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
+ FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
+ FIND_METHOD (detect_mouse, "detectMouse", "()Z");
+ FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;");
+ FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)"
+ "Ljava/lang/String;");
+ FIND_METHOD (restart_emacs, "restartEmacs", "()V");
+ FIND_METHOD (update_ic, "updateIC",
+ "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+ FIND_METHOD (reset_ic, "resetIC",
+ "(Lorg/gnu/emacs/EmacsWindow;I)V");
+ FIND_METHOD (open_content_uri, "openContentUri",
+ "([BZZZ)I");
+ FIND_METHOD (check_content_uri, "checkContentUri",
+ "([BZZ)Z");
+ FIND_METHOD (query_battery, "queryBattery", "()[J");
+ FIND_METHOD (display_toast, "displayToast",
+ "(Ljava/lang/String;)V");
+ FIND_METHOD (update_extracted_text, "updateExtractedText",
+ "(Lorg/gnu/emacs/EmacsWindow;"
+ "Landroid/view/inputmethod/ExtractedText;I)V");
+ FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo",
+ "(Lorg/gnu/emacs/EmacsWindow;FFFF)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_pixmap (void)
+{
+ jclass old;
+
+ pixmap_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsPixmap");
+ eassert (pixmap_class.class);
+
+ old = pixmap_class.class;
+ pixmap_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!pixmap_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ pixmap_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ pixmap_class.class, \
+ name, signature); \
+ assert (pixmap_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+ FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
+
+#undef FIND_METHOD
+}
+
+static void
+android_init_graphics_point (void)
+{
+ jclass old;
+
+ point_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "android/graphics/Point");
+ eassert (point_class.class);
+
+ old = point_class.class;
+ point_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!point_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ point_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ point_class.class, \
+ name, signature); \
+ assert (point_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(II)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_drawable (void)
+{
+ jclass old;
+
+ drawable_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDrawable");
+ eassert (drawable_class.class);
+
+ old = drawable_class.class;
+ drawable_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!drawable_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ drawable_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ drawable_class.class, \
+ name, signature); \
+ assert (drawable_class.c_name);
+
+ FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
+ FIND_METHOD (damage_rect, "damageRect", "(Landroid/graphics/Rect;)V");
+#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");
+ FIND_METHOD (toggle_on_screen_keyboard,
+ "toggleOnScreenKeyboard", "(Z)V");
+ FIND_METHOD (lookup_string, "lookupString", "(I)Ljava/lang/String;");
+ FIND_METHOD (set_fullscreen, "setFullscreen", "(Z)V");
+ FIND_METHOD (change_window_background, "changeWindowBackground",
+ "(I)V");
+ FIND_METHOD (reparent_to, "reparentTo",
+ "(Lorg/gnu/emacs/EmacsWindow;II)V");
+ FIND_METHOD (map_window, "mapWindow", "()V");
+ FIND_METHOD (unmap_window, "unmapWindow", "()V");
+ FIND_METHOD (resize_window, "resizeWindow", "(II)V");
+ FIND_METHOD (move_window, "moveWindow", "(II)V");
+ FIND_METHOD (make_input_focus, "makeInputFocus", "(J)V");
+ FIND_METHOD (raise, "raise", "()V");
+ FIND_METHOD (lower, "lower", "()V");
+ FIND_METHOD (get_window_geometry, "getWindowGeometry",
+ "()[I");
+ FIND_METHOD (translate_coordinates, "translateCoordinates",
+ "(II)[I");
+ FIND_METHOD (set_dont_focus_on_map, "setDontFocusOnMap", "(Z)V");
+ FIND_METHOD (set_dont_accept_focus, "setDontAcceptFocus", "(Z)V");
+ FIND_METHOD (define_cursor, "defineCursor",
+ "(Lorg/gnu/emacs/EmacsCursor;)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_cursor (void)
+{
+ jclass old;
+
+ cursor_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsCursor");
+ eassert (cursor_class.class);
+
+ old = cursor_class.class;
+ cursor_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!cursor_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ cursor_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ cursor_class.class, \
+ name, signature); \
+ assert (cursor_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(SI)V");
+#undef FIND_METHOD
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv,
+ jobject dump_file_object, jint api_level)
+{
+ /* android_emacs_init is not main, so GCC is not nice enough to add
+ the stack alignment prologue.
+
+ Unfortunately for us, dalvik on Android 4.0.x calls native code
+ with a 4 byte aligned stack, so this prologue must be inserted
+ before each function exported via JNI. */
+
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char **c_argv;
+ jsize nelements, i;
+ jobject argument;
+ const char *c_argument;
+ char *dump_file;
+
+ /* Set the Android API level. */
+ android_api_level = api_level;
+
+ android_java_env = env;
+
+ nelements = (*env)->GetArrayLength (env, argv);
+ c_argv = alloca (sizeof *c_argv * nelements);
+
+ for (i = 0; i < nelements; ++i)
+ {
+ argument = (*env)->GetObjectArrayElement (env, argv, i);
+ c_argument = (*env)->GetStringUTFChars (env, (jstring) argument,
+ NULL);
+
+ if (!c_argument)
+ emacs_abort ();
+
+ /* Note that c_argument is in ``modified UTF-8 encoding'', but
+ we don't care as NUL bytes are not being specified inside. */
+ c_argv[i] = alloca (strlen (c_argument) + 1);
+ strcpy (c_argv[i], c_argument);
+ (*env)->ReleaseStringUTFChars (env, (jstring) argument, c_argument);
+ }
+
+ android_init_emacs_service ();
+ android_init_emacs_pixmap ();
+ android_init_graphics_point ();
+ android_init_emacs_drawable ();
+ android_init_emacs_window ();
+ android_init_emacs_cursor ();
+
+ /* Set HOME to the app data directory. */
+ setenv ("HOME", android_files_dir, 1);
+
+ /* Set TMPDIR to the temporary files directory. */
+ setenv ("TMPDIR", android_cache_dir, 1);
+
+ /* Set the cwd to that directory as well. */
+ if (chdir (android_files_dir))
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "chdir: %s", strerror (errno));
+
+ /* Initialize the Android GUI as long as the service object was
+ set. */
+
+ if (emacs_service)
+ android_init_gui = true;
+
+ /* Now see if a dump file has been specified and should be used. */
+ dump_file = NULL;
+
+ if (dump_file_object)
+ {
+ c_argument
+ = (*env)->GetStringUTFChars (env, (jstring) dump_file_object,
+ NULL);
+
+ /* Copy the Java string data once. */
+ dump_file = strdup (c_argument);
+
+ /* Release the Java string data. */
+ (*env)->ReleaseStringUTFChars (env, (jstring) dump_file_object,
+ c_argument);
+ }
+
+ /* Delete local references to objects that are no longer needed. */
+ ANDROID_DELETE_LOCAL_REF (argv);
+ ANDROID_DELETE_LOCAL_REF (dump_file_object);
+
+ android_emacs_init (nelements, c_argv, dump_file);
+ /* android_emacs_init should never return. */
+ emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (quit) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ /* Raise sigio to interrupt anything that could be reading
+ input. */
+ Vquit_flag = Qt;
+ raise (SIGIO);
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint x, jint y, jint width,
+ jint height)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
+ event.xconfigure.serial = ++event_serial;
+ event.xconfigure.window = window;
+ event.xconfigure.time = time;
+ event.xconfigure.x = x;
+ event.xconfigure.y = y;
+ event.xconfigure.width = width;
+ event.xconfigure.height = height;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint state, jint keycode,
+ jint unicode_char)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xkey.type = ANDROID_KEY_PRESS;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = time;
+ event.xkey.state = state;
+ event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint state, jint keycode,
+ jint unicode_char)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xkey.type = ANDROID_KEY_RELEASE;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = time;
+ event.xkey.state = state;
+ event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xfocus.type = ANDROID_FOCUS_IN;
+ event.xfocus.serial = ++event_serial;
+ event.xfocus.window = window;
+ event.xfocus.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xfocus.type = ANDROID_FOCUS_OUT;
+ event.xfocus.serial = ++event_serial;
+ event.xfocus.window = window;
+ event.xfocus.time = time;
+
+ android_write_event (&event);
+ return ++event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
+ jshort window, jint action)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xaction.type = ANDROID_WINDOW_ACTION;
+ event.xaction.serial = ++event_serial;
+ event.xaction.window = window;
+ event.xaction.action = action;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xcrossing.type = ANDROID_ENTER_NOTIFY;
+ event.xcrossing.serial = ++event_serial;
+ event.xcrossing.window = window;
+ event.xcrossing.x = x;
+ event.xcrossing.y = y;
+ event.xcrossing.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xcrossing.type = ANDROID_LEAVE_NOTIFY;
+ event.xcrossing.serial = ++event_serial;
+ event.xcrossing.window = window;
+ event.xcrossing.x = x;
+ event.xcrossing.y = y;
+ event.xcrossing.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xmotion.type = ANDROID_MOTION_NOTIFY;
+ event.xmotion.serial = ++event_serial;
+ event.xmotion.window = window;
+ event.xmotion.x = x;
+ event.xmotion.y = y;
+ event.xmotion.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jint button)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xbutton.type = ANDROID_BUTTON_PRESS;
+ event.xbutton.serial = ++event_serial;
+ event.xbutton.window = window;
+ event.xbutton.x = x;
+ event.xbutton.y = y;
+ event.xbutton.time = time;
+ event.xbutton.state = state;
+ event.xbutton.button = button;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jint button)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xbutton.type = ANDROID_BUTTON_RELEASE;
+ event.xbutton.serial = ++event_serial;
+ event.xbutton.window = window;
+ event.xbutton.x = x;
+ event.xbutton.y = y;
+ event.xbutton.time = time;
+ event.xbutton.state = state;
+ event.xbutton.button = button;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_DOWN;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_UP;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_MOVE;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jfloat x_delta, jfloat y_delta)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.wheel.type = ANDROID_WHEEL;
+ event.wheel.serial = ++event_serial;
+ event.wheel.window = window;
+ event.wheel.x = x;
+ event.wheel.y = y;
+ event.wheel.time = time;
+ event.wheel.state = state;
+ event.wheel.x_delta = x_delta;
+ event.wheel.y_delta = y_delta;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.iconified.type = ANDROID_ICONIFIED;
+ event.iconified.serial = ++event_serial;
+ event.iconified.window = window;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.iconified.type = ANDROID_DEICONIFIED;
+ event.iconified.serial = ++event_serial;
+ event.iconified.window = window;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object,
+ jshort window, jint menu_event_id,
+ jint menu_event_serial)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.menu.type = ANDROID_CONTEXT_MENU;
+ event.menu.serial = ++event_serial;
+ event.menu.window = window;
+ event.menu.menu_event_id = menu_event_id;
+ event.menu.menu_event_serial = menu_event_serial;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jint width, jint height)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xexpose.type = ANDROID_EXPOSE;
+ event.xexpose.serial = ++event_serial;
+ event.xexpose.window = window;
+ event.xexpose.x = x;
+ event.xexpose.y = y;
+ event.xexpose.width = width;
+ event.xexpose.height = height;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jboolean JNICALL
+NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env,
+ jobject object)
+{
+ /* Yes, android_pass_multimedia_buttons_to_system is being
+ read from the UI thread. */
+ return !android_pass_multimedia_buttons_to_system;
+}
+
+/* Forward declarations of deadlock prevention functions. */
+
+static void android_begin_query (void);
+static void android_end_query (void);
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ android_begin_query ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endSynchronous) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ android_end_query ();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Java functions called by C.
+
+ Because all C code runs in the native function initEmacs, ALL LOCAL
+ REFERENCES WILL PERSIST!
+
+ This means that every local reference must be explicitly destroyed
+ with DeleteLocalRef. A helper macro is provided to do this. */
+
+struct android_handle_entry
+{
+ /* The type. */
+ enum android_handle_type type;
+
+ /* The handle. */
+ jobject handle;
+};
+
+/* Table of handles MAX_HANDLE long. */
+struct android_handle_entry android_handles[USHRT_MAX];
+
+/* The largest handle ID currently known, but subject to
+ wraparound. */
+static android_handle max_handle;
+
+/* Allocate a new, unused, handle identifier. If Emacs is out of
+ identifiers, return 0. */
+
+static android_handle
+android_alloc_id (void)
+{
+ android_handle handle;
+
+ /* 0 is never a valid handle ID. */
+
+ if (!max_handle)
+ max_handle++;
+
+ /* See if the handle is already occupied. */
+
+ if (android_handles[max_handle].handle)
+ {
+ /* Look for a fresh unoccupied handle. */
+
+ handle = max_handle;
+ max_handle++;
+
+ while (handle != max_handle)
+ {
+ ++max_handle;
+
+ /* Make sure the handle is valid. */
+ if (!max_handle)
+ ++max_handle;
+
+ if (!android_handles[max_handle].handle)
+ return max_handle++;
+ }
+
+ return ANDROID_NONE;
+ }
+
+ return max_handle++;
+}
+
+/* Destroy the specified handle and mark it as free on the Java side
+ as well. */
+
+static void
+android_destroy_handle (android_handle handle)
+{
+ static jclass old, class;
+ static jmethodID method;
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy free handle!");
+ emacs_abort ();
+ }
+
+ if (!class)
+ {
+ class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsHandleObject");
+ assert (class != NULL);
+
+ method
+ = (*android_java_env)->GetMethodID (android_java_env, class,
+ "destroyHandle", "()V");
+ assert (method != NULL);
+
+ old = class;
+ class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+ }
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ android_handles[handle].handle,
+ method);
+
+ /* Just clear any exception thrown. If destroying the handle
+ fails from an out-of-memory error, then Emacs loses some
+ resources, but that is not as big deal as signalling. */
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ /* Delete the global reference regardless of any error. */
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ android_handles[handle].handle);
+ android_handles[handle].handle = NULL;
+}
+
+jobject
+android_resolve_handle (android_handle handle,
+ enum android_handle_type type)
+{
+ if (!handle)
+ /* ANDROID_NONE. */
+ return NULL;
+
+ /* CheckJNI will normally ensure that the handle exists and is
+ the right type, but with a less informative error message.
+ Don't waste cycles doing our own checking here. */
+
+#ifdef ENABLE_CHECKING
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to resolve free handle!");
+ emacs_abort ();
+ }
+
+ if (android_handles[handle].type != type)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Handle has wrong type!");
+ emacs_abort ();
+ }
+
+#endif /* ENABLE_CHECKING */
+
+ return android_handles[handle].handle;
+}
+
+static jobject
+android_resolve_handle2 (android_handle handle,
+ enum android_handle_type type,
+ enum android_handle_type type2)
+{
+ if (!handle)
+ return NULL;
+
+ /* CheckJNI will normally ensure that the handle exists and is
+ the right type, but with a less informative error message.
+ Don't waste cycles doing our own checking here. */
+
+#ifdef ENABLE_CHECKING
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to resolve free handle!");
+ emacs_abort ();
+ }
+
+ if (android_handles[handle].type != type
+ && android_handles[handle].type != type2)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Handle has wrong type!");
+ emacs_abort ();
+ }
+
+#endif /* ENABLE_CHECKING */
+
+ return android_handles[handle].handle;
+}
+
+void
+android_change_window_attributes (android_window handle,
+ enum android_window_value_mask value_mask,
+ struct android_set_window_attributes *attrs)
+{
+ jmethodID method;
+ jobject window;
+ jint pixel;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ if (value_mask & ANDROID_CW_BACK_PIXEL)
+ {
+ method = window_class.change_window_background;
+ pixel = (jint) attrs->background_pixel;
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ method, pixel);
+ android_exception_check ();
+ }
+}
+
+/* Create a new window with the given width, height and
+ attributes. */
+
+android_window
+android_create_window (android_window parent, int x, int y,
+ int width, int height,
+ enum android_window_value_mask value_mask,
+ struct android_set_window_attributes *attrs)
+{
+ static jclass class;
+ static jmethodID constructor;
+ jobject object, parent_object, old;
+ android_window window;
+ android_handle prev_max_handle;
+ bool override_redirect;
+
+ parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
+
+ prev_max_handle = max_handle;
+ window = android_alloc_id ();
+
+ if (!window)
+ error ("Out of window handles!");
+
+ if (!class)
+ {
+ class = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsWindow");
+ assert (class != NULL);
+
+ constructor
+ = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
+ "(SLorg/gnu/emacs/EmacsWindow;"
+ "IIIIZ)V");
+ assert (constructor != NULL);
+
+ old = class;
+ class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+ }
+
+ /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+ creation time. */
+ override_redirect = ((value_mask
+ & ANDROID_CW_OVERRIDE_REDIRECT)
+ && attrs->override_redirect);
+
+ object = (*android_java_env)->NewObject (android_java_env, class,
+ constructor, (jshort) window,
+ parent_object, (jint) x, (jint) y,
+ (jint) width, (jint) height,
+ (jboolean) override_redirect);
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[window].type = ANDROID_HANDLE_WINDOW;
+ android_handles[window].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env,
+ object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[window].handle)
+ memory_full (0);
+
+ android_change_window_attributes (window, value_mask, attrs);
+ return window;
+}
+
+void
+android_set_window_background (android_window window, unsigned long pixel)
+{
+ struct android_set_window_attributes attrs;
+
+ attrs.background_pixel = pixel;
+ android_change_window_attributes (window, ANDROID_CW_BACK_PIXEL,
+ &attrs);
+}
+
+void
+android_destroy_window (android_window window)
+{
+ if (android_handles[window].type != ANDROID_HANDLE_WINDOW)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy something not a window!");
+ emacs_abort ();
+ }
+
+ android_destroy_handle (window);
+}
+
+static void
+android_init_android_rect_class (void)
+{
+ jclass old;
+
+ if (android_rect_class)
+ /* Already initialized. */
+ return;
+
+ android_rect_class
+ = (*android_java_env)->FindClass (android_java_env,
+ "android/graphics/Rect");
+ assert (android_rect_class);
+
+ android_rect_constructor
+ = (*android_java_env)->GetMethodID (android_java_env, android_rect_class,
+ "<init>", "(IIII)V");
+ assert (emacs_gc_constructor);
+
+ old = android_rect_class;
+ android_rect_class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) android_rect_class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+}
+
+static void
+android_init_emacs_gc_class (void)
+{
+ jclass old;
+
+ if (emacs_gc_class)
+ /* Already initialized. */
+ return;
+
+ emacs_gc_class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsGC");
+ assert (emacs_gc_class);
+
+ emacs_gc_constructor
+ = (*android_java_env)->GetMethodID (android_java_env,
+ emacs_gc_class,
+ "<init>", "(S)V");
+ assert (emacs_gc_constructor);
+
+ emacs_gc_mark_dirty
+ = (*android_java_env)->GetMethodID (android_java_env,
+ emacs_gc_class,
+ "markDirty", "(Z)V");
+ assert (emacs_gc_mark_dirty);
+
+ old = emacs_gc_class;
+ emacs_gc_class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) emacs_gc_class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ emacs_gc_foreground
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "foreground", "I");
+ emacs_gc_background
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "background", "I");
+ emacs_gc_function
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "function", "I");
+ emacs_gc_clip_rects
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_rects",
+ "[Landroid/graphics/Rect;");
+ emacs_gc_clip_x_origin
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_x_origin", "I");
+ emacs_gc_clip_y_origin
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_y_origin", "I");
+ emacs_gc_stipple
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "stipple",
+ "Lorg/gnu/emacs/EmacsPixmap;");
+ emacs_gc_clip_mask
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_mask",
+ "Lorg/gnu/emacs/EmacsPixmap;");
+ emacs_gc_fill_style
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "fill_style", "I");
+ emacs_gc_ts_origin_x
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "ts_origin_x", "I");
+ emacs_gc_ts_origin_y
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "ts_origin_y", "I");
+}
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ struct android_gc *gc;
+ android_handle prev_max_handle;
+ jobject object;
+
+ android_init_emacs_gc_class ();
+
+ gc = xmalloc (sizeof *gc);
+ prev_max_handle = max_handle;
+ gc->gcontext = android_alloc_id ();
+ gc->foreground = 0;
+ gc->background = 0xffffff;
+ gc->clip_rects = NULL;
+
+ /* This means to not apply any clipping. */
+ gc->num_clip_rects = -1;
+
+ if (!gc->gcontext)
+ {
+ xfree (gc);
+ error ("Out of GContext handles!");
+ }
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ emacs_gc_class,
+ emacs_gc_constructor,
+ (jshort) gc->gcontext);
+
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[gc->gcontext].type = ANDROID_HANDLE_GCONTEXT;
+ android_handles[gc->gcontext].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[gc->gcontext].handle)
+ memory_full (0);
+
+ android_change_gc (gc, mask, values);
+ return gc;
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+ android_destroy_handle (gc->gcontext);
+
+ xfree (gc->clip_rects);
+ xfree (gc);
+}
+
+void
+android_change_gc (struct android_gc *gc,
+ enum android_gc_value_mask mask,
+ 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,
+ ANDROID_HANDLE_GCONTEXT);
+
+ if (mask & ANDROID_GC_FOREGROUND)
+ {
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_foreground,
+ values->foreground);
+ gc->foreground = values->foreground;
+ }
+
+ if (mask & ANDROID_GC_BACKGROUND)
+ {
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_background,
+ values->background);
+ gc->background = values->background;
+ }
+
+ if (mask & ANDROID_GC_FUNCTION)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_function,
+ 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);
+ 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);
+ clip_changed = true;
+ }
+
+ if (mask & ANDROID_GC_CLIP_MASK)
+ {
+ what = android_resolve_handle (values->clip_mask,
+ ANDROID_HANDLE_PIXMAP);
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_mask,
+ what);
+
+ /* Changing GCClipMask also clears the clip rectangles. */
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_rects,
+ NULL);
+
+ xfree (gc->clip_rects);
+ gc->clip_rects = NULL;
+ gc->num_clip_rects = -1;
+ clip_changed = true;
+ }
+
+ if (mask & ANDROID_GC_STIPPLE)
+ {
+ what = android_resolve_handle (values->stipple,
+ ANDROID_HANDLE_PIXMAP);
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_stipple,
+ what);
+ }
+
+ if (mask & ANDROID_GC_FILL_STYLE)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_fill_style,
+ values->fill_style);
+
+ if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_x,
+ values->ts_x_origin);
+
+ if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_y,
+ values->ts_y_origin);
+
+ if (mask)
+ {
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ gcontext,
+ emacs_gc_class,
+ emacs_gc_mark_dirty,
+ (jboolean) clip_changed);
+ android_exception_check ();
+ }
+}
+
+void
+android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
+ int clip_y_origin,
+ struct android_rectangle *clip_rects,
+ int n_clip_rects)
+{
+ jobjectArray array;
+ jobject rect, gcontext;
+ int i;
+
+ android_init_android_rect_class ();
+ android_init_emacs_gc_class ();
+
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ array = (*android_java_env)->NewObjectArray (android_java_env,
+ n_clip_rects,
+ android_rect_class,
+ NULL);
+ android_exception_check ();
+
+ for (i = 0; i < n_clip_rects; ++i)
+ {
+ rect = (*android_java_env)->NewObject (android_java_env,
+ android_rect_class,
+ android_rect_constructor,
+ (jint) clip_rects[i].x,
+ (jint) clip_rects[i].y,
+ (jint) (clip_rects[i].x
+ + clip_rects[i].width),
+ (jint) (clip_rects[i].y
+ + clip_rects[i].height));
+
+ /* The meaning of this call is to check whether or not an
+ allocation error happened, and to delete ARRAY and signal an
+ out-of-memory error if that is the case. */
+ android_exception_check_1 (array);
+
+ (*android_java_env)->SetObjectArrayElement (android_java_env,
+ array, i, rect);
+ ANDROID_DELETE_LOCAL_REF (rect);
+ }
+
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_rects,
+ (jobject) array);
+ ANDROID_DELETE_LOCAL_REF (array);
+
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_x_origin,
+ clip_x_origin);
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_y_origin,
+ clip_y_origin);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ gcontext,
+ emacs_gc_class,
+ emacs_gc_mark_dirty,
+ (jboolean) true);
+ android_exception_check ();
+
+ /* Cache the clip rectangles on the C side for
+ sfntfont-android.c. */
+ if (gc->clip_rects)
+ xfree (gc->clip_rects);
+
+ /* If gc->num_clip_rects is 0, then no drawing will be performed at
+ all. */
+ gc->clip_rects = xmalloc (sizeof *gc->clip_rects
+ * n_clip_rects);
+ gc->num_clip_rects = n_clip_rects;
+ memcpy (gc->clip_rects, clip_rects,
+ n_clip_rects * sizeof *gc->clip_rects);
+}
+
+void
+android_reparent_window (android_window w, android_window parent_handle,
+ int x, int y)
+{
+ jobject window, parent;
+ jmethodID method;
+
+ window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW);
+ parent = android_resolve_handle (parent_handle,
+ ANDROID_HANDLE_WINDOW);
+
+ method = window_class.reparent_to;
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class, method,
+ parent, (jint) x, (jint) y);
+ android_exception_check ();
+}
+
+void
+android_clear_window (android_window handle)
+{
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.clear_window,
+ window);
+ android_exception_check ();
+}
+
+void
+android_map_window (android_window handle)
+{
+ jobject window;
+ jmethodID map_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ map_window = window_class.map_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ map_window);
+ android_exception_check ();
+}
+
+void
+android_unmap_window (android_window handle)
+{
+ jobject window;
+ jmethodID unmap_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ unmap_window = window_class.unmap_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ unmap_window);
+ android_exception_check ();
+}
+
+void
+android_resize_window (android_window handle, unsigned int width,
+ unsigned int height)
+{
+ jobject window;
+ jmethodID resize_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ resize_window = window_class.resize_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ resize_window,
+ (jint) width,
+ (jint) height);
+ android_exception_check ();
+}
+
+void
+android_move_window (android_window handle, int x, int y)
+{
+ jobject window;
+ jmethodID move_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ move_window = window_class.move_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ move_window,
+ (jint) x, (jint) y);
+ android_exception_check ();
+}
+
+void
+android_swap_buffers (struct android_swap_info *swap_info,
+ int num_windows)
+{
+ jobject window;
+ int i;
+
+ for (i = 0; i < num_windows; ++i)
+ {
+ window = android_resolve_handle (swap_info[i].swap_window,
+ ANDROID_HANDLE_WINDOW);
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ window_class.swap_buffers);
+ android_exception_check ();
+ }
+}
+
+void
+android_get_gc_values (struct android_gc *gc,
+ enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ jobject gcontext;
+
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ if (mask & ANDROID_GC_FOREGROUND)
+ /* GCs never have 32 bit colors, so we don't have to worry about
+ sign extension here. */
+ values->foreground = gc->foreground;
+
+ if (mask & ANDROID_GC_BACKGROUND)
+ values->background = gc->background;
+
+ if (mask & ANDROID_GC_FUNCTION)
+ values->function
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_function);
+
+ if (mask & ANDROID_GC_CLIP_X_ORIGIN)
+ values->clip_x_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_x_origin);
+
+ if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+ values->clip_y_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_y_origin);
+
+ if (mask & ANDROID_GC_FILL_STYLE)
+ values->fill_style
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_fill_style);
+
+ if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+ values->ts_x_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_x);
+
+ if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+ values->ts_y_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_y);
+
+ /* Fields involving handles are not used by Emacs, and thus not
+ implemented */
+}
+
+void
+android_set_foreground (struct android_gc *gc, unsigned long foreground)
+{
+ struct android_gc_values gcv;
+
+ gcv.foreground = foreground;
+ android_change_gc (gc, ANDROID_GC_FOREGROUND, &gcv);
+}
+
+void
+android_fill_rectangle (android_drawable handle, struct android_gc *gc,
+ int x, int y, unsigned int width,
+ unsigned int height)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.fill_rectangle,
+ drawable,
+ gcontext,
+ (jint) x, (jint) y,
+ (jint) width,
+ (jint) height);
+}
+
+android_pixmap
+android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
+ unsigned int height,
+ unsigned long foreground,
+ unsigned long background,
+ unsigned int depth)
+{
+ android_handle prev_max_handle;
+ jobject object;
+ jintArray colors;
+ android_pixmap pixmap;
+ unsigned int x, y;
+ jint *region;
+
+ USE_SAFE_ALLOCA;
+
+ /* Create the color array holding the data. */
+ colors = (*android_java_env)->NewIntArray (android_java_env,
+ width * height);
+ android_exception_check ();
+
+ SAFE_NALLOCA (region, sizeof *region, width);
+
+ for (y = 0; y < height; ++y)
+ {
+ for (x = 0; x < width; ++x)
+ {
+ if (depth == 24)
+ {
+ /* The alpha channels must be set, or otherwise, the
+ pixmap will be created entirely transparent. */
+
+ if (data[x / 8] & (1 << (x % 8)))
+ region[x] = foreground | 0xff000000;
+ else
+ region[x] = background | 0xff000000;
+ }
+ else
+ {
+ if (data[x / 8] & (1 << (x % 8)))
+ region[x] = foreground;
+ else
+ region[x] = background;
+ }
+ }
+
+ (*android_java_env)->SetIntArrayRegion (android_java_env,
+ colors,
+ width * y, width,
+ region);
+ data += width / 8;
+ }
+
+ /* First, allocate the pixmap handle. */
+ prev_max_handle = max_handle;
+ pixmap = android_alloc_id ();
+
+ if (!pixmap)
+ {
+ ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+ error ("Out of pixmap handles!");
+ }
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ pixmap_class.class,
+ pixmap_class.constructor,
+ (jshort) pixmap, colors,
+ (jint) width, (jint) height,
+ (jint) depth);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+
+ if (!object)
+ {
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+ android_handles[pixmap].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[pixmap].handle)
+ memory_full (0);
+
+ SAFE_FREE ();
+ return pixmap;
+}
+
+void
+android_set_clip_mask (struct android_gc *gc, android_pixmap pixmap)
+{
+ struct android_gc_values gcv;
+
+ gcv.clip_mask = pixmap;
+ android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+}
+
+void
+android_set_fill_style (struct android_gc *gc,
+ enum android_fill_style fill_style)
+{
+ struct android_gc_values gcv;
+
+ gcv.fill_style = fill_style;
+ android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv);
+}
+
+void
+android_copy_area (android_drawable src, android_drawable dest,
+ struct android_gc *gc, int src_x, int src_y,
+ unsigned int width, unsigned int height,
+ int dest_x, int dest_y)
+{
+ jobject src_object, dest_object, gcontext;
+
+ src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.copy_area,
+ src_object,
+ dest_object,
+ gcontext,
+ (jint) src_x, (jint) src_y,
+ (jint) width, (jint) height,
+ (jint) dest_x, (jint) dest_y);
+ android_exception_check ();
+}
+
+void
+android_free_pixmap (android_pixmap pixmap)
+{
+ android_destroy_handle (pixmap);
+}
+
+void
+android_set_background (struct android_gc *gc, unsigned long background)
+{
+ struct android_gc_values gcv;
+
+ gcv.background = background;
+ android_change_gc (gc, ANDROID_GC_BACKGROUND, &gcv);
+}
+
+void
+android_fill_polygon (android_drawable drawable, struct android_gc *gc,
+ struct android_point *points, int npoints,
+ enum android_shape shape, enum android_coord_mode mode)
+{
+ jobjectArray array;
+ jobject point, drawable_object, gcontext;
+ int i;
+
+ drawable_object = android_resolve_handle2 (drawable,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ array = (*android_java_env)->NewObjectArray (android_java_env,
+ npoints,
+ point_class.class,
+ NULL);
+ android_exception_check ();
+
+ for (i = 0; i < npoints; ++i)
+ {
+ point = (*android_java_env)->NewObject (android_java_env,
+ point_class.class,
+ point_class.constructor,
+ (jint) points[i].x,
+ (jint) points[i].y);
+ android_exception_check_1 (array);
+
+ (*android_java_env)->SetObjectArrayElement (android_java_env,
+ array, i, point);
+ ANDROID_DELETE_LOCAL_REF (point);
+ }
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.fill_polygon,
+ drawable_object,
+ gcontext, array);
+ ANDROID_DELETE_LOCAL_REF (array);
+}
+
+void
+android_draw_rectangle (android_drawable handle, struct android_gc *gc,
+ int x, int y, unsigned int width, unsigned int height)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_rectangle,
+ drawable, gcontext,
+ (jint) x, (jint) y,
+ (jint) width, (jint) height);
+}
+
+void
+android_draw_point (android_drawable handle, struct android_gc *gc,
+ int x, int y)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_point,
+ drawable, gcontext,
+ (jint) x, (jint) y);
+}
+
+void
+android_draw_line (android_drawable handle, struct android_gc *gc,
+ int x, int y, int x2, int y2)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_line,
+ drawable, gcontext,
+ (jint) x, (jint) y,
+ (jint) x2, (jint) y2);
+}
+
+android_pixmap
+android_create_pixmap (unsigned int width, unsigned int height,
+ int depth)
+{
+ android_handle prev_max_handle;
+ jobject object;
+ android_pixmap pixmap;
+
+ /* First, allocate the pixmap handle. */
+ prev_max_handle = max_handle;
+ pixmap = android_alloc_id ();
+
+ if (!pixmap)
+ error ("Out of pixmap handles!");
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ pixmap_class.class,
+ pixmap_class.constructor_mutable,
+ (jshort) pixmap,
+ (jint) width, (jint) height,
+ (jint) depth);
+
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+ android_handles[pixmap].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[pixmap].handle)
+ memory_full (0);
+
+ return pixmap;
+}
+
+void
+android_set_ts_origin (struct android_gc *gc, int x, int y)
+{
+ struct android_gc_values gcv;
+
+ gcv.ts_x_origin = x;
+ gcv.ts_y_origin = y;
+ android_change_gc (gc, (ANDROID_GC_TILE_STIP_X_ORIGIN
+ | ANDROID_GC_TILE_STIP_Y_ORIGIN),
+ &gcv);
+}
+
+void
+android_clear_area (android_window handle, int x, int y,
+ unsigned int width, unsigned int height)
+{
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.clear_area,
+ window, (jint) x, (jint) y,
+ (jint) width, (jint) height);
+}
+
+android_pixmap
+android_create_bitmap_from_data (char *bits, unsigned int width,
+ unsigned int height)
+{
+ return android_create_pixmap_from_bitmap_data (bits, 1, 0,
+ width, height, 1);
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ struct android_image *image;
+
+ image = xmalloc (sizeof *image);
+
+ /* Fill in the fields required by image.c. N.B. that
+ android_destroy_image ostensibly will free data, but image.c
+ mostly sets and frees data itself. */
+ image->width = width;
+ image->height = height;
+ image->data = data;
+ image->depth = depth;
+ image->format = format;
+
+ /* Now fill in the image dimensions. There are only two depths
+ supported by this function. */
+
+ if (depth == 1)
+ {
+ image->bytes_per_line = (width + 7) / 8;
+ image->bits_per_pixel = 1;
+ }
+ else if (depth == 24)
+ {
+ image->bytes_per_line = width * 4;
+ image->bits_per_pixel = 32;
+ }
+ else
+ emacs_abort ();
+
+ return image;
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ /* If XIMG->data is NULL, then it has already been freed by
+ image.c. */
+
+ if (ximg->data)
+ xfree (ximg->data);
+ xfree (ximg);
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ char *byte, *word;
+ unsigned int r, g, b;
+ unsigned int pixel_int;
+
+ /* Ignore out-of-bounds accesses. */
+
+ if (x >= ximg->width || y >= ximg->height || x < 0 || y < 0)
+ return;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+
+ if (pixel)
+ *byte |= (1 << x % 8);
+ else
+ *byte &= ~(1 << x % 8);
+ break;
+
+ case 24:
+ /* Unaligned accesses are problematic on Android devices. */
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+
+ /* Swizzle the pixel into ABGR format. Android uses Skia's
+ ``native color type'', which is ABGR. This is despite the
+ format being named ``ARGB'', and more confusingly
+ `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h. */
+ r = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ b = pixel & 0x000000ff;
+ pixel = (r >> 16) | g | (b << 16) | 0xff000000;
+
+ pixel_int = pixel;
+ memcpy (word, &pixel_int, sizeof pixel_int);
+ break;
+ }
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ char *byte, *word;
+ unsigned int pixel, r, g, b;
+
+ if (x >= ximg->width || y >= ximg->height
+ || x < 0 || y < 0)
+ return 0;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+ return (*byte & (1 << x % 8)) ? 1 : 0;
+
+ case 24:
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+ memcpy (&pixel, word, sizeof pixel);
+
+ /* Convert the pixel back to RGB. */
+ b = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ r = pixel & 0x000000ff;
+ pixel = ((r << 16) | g | (b >> 16)) & ~0xff000000;
+
+ return pixel;
+ }
+
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable handle,
+ enum android_image_format format)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ size_t byte_size;
+ void *data;
+ struct android_image *image;
+ unsigned char *data1, *data2;
+ int i, x;
+
+ drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ android_exception_check ();
+
+ /* Clear the bitmap info structure. */
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Compute how big the image data will be. Fail if it would be too
+ big. */
+
+ if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8)
+ {
+ if (INT_MULTIPLY_WRAPV ((size_t) bitmap_info.stride,
+ (size_t) bitmap_info.height,
+ &byte_size))
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+ }
+ else
+ /* This A8 image will be packed into A1 later on. */
+ byte_size = (bitmap_info.width + 7) / 8;
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Copy the data into a new struct android_image. */
+ image = xmalloc (sizeof *image);
+ image->width = bitmap_info.width;
+ image->height = bitmap_info.height;
+ image->data = malloc (byte_size);
+
+ if (!image->data)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ xfree (image);
+ memory_full (byte_size);
+ }
+
+ /* Use the format of the bitmap to determine the image depth. */
+ switch (bitmap_info.format)
+ {
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
+ image->depth = 24;
+ image->bits_per_pixel = 32;
+ break;
+
+ /* A8 images are used by Emacs to represent bitmaps. They have
+ to be packed manually. */
+ case ANDROID_BITMAP_FORMAT_A_8:
+ image->depth = 1;
+ image->bits_per_pixel = 1;
+ break;
+
+ /* Other formats are currently not supported. */
+ default:
+ emacs_abort ();
+ }
+
+ image->format = format;
+
+ if (image->depth == 24)
+ {
+ image->bytes_per_line = bitmap_info.stride;
+
+ /* Copy the bitmap data over. */
+ memcpy (image->data, data, byte_size);
+ }
+ else
+ {
+ /* Pack the A8 image data into bits manually. */
+ image->bytes_per_line = (image->width + 7) / 8;
+
+ data1 = (unsigned char *) image->data;
+ data2 = data;
+
+ for (i = 0; i < image->height; ++i)
+ {
+ for (x = 0; x < image->width; ++x)
+ /* Some bits in data1 might be initialized at this point,
+ but they will all be set properly later. */
+ data1[x / 8] = (data2[x]
+ ? (data1[x / 8] | (1 << (x % 8)))
+ : (data1[x / 8] & ~(1 << (x % 8))));
+
+ data1 += image->bytes_per_line;
+ data2 += bitmap_info.stride;
+ }
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return image;
+}
+
+void
+android_put_image (android_pixmap handle, struct android_image *image)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ void *data;
+ unsigned char *data_1, *data_2;
+ int i, x;
+
+ drawable = android_resolve_handle (handle, ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ android_exception_check ();
+
+ /* Clear the bitmap info structure. */
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ if (bitmap_info.width != image->width
+ || bitmap_info.height != image->height)
+ /* This is not yet supported. */
+ emacs_abort ();
+
+ /* Make sure the bitmap formats are compatible with each other. */
+
+ if ((image->depth == 24
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
+ || (image->depth == 1
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8))
+ emacs_abort ();
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ data_1 = data;
+ data_2 = (unsigned char *) image->data;
+
+ /* Copy the bitmap data over scanline-by-scanline. */
+ for (i = 0; i < image->height; ++i)
+ {
+ if (image->depth != 1)
+ memcpy (data_1, data_2,
+ image->width * (image->bits_per_pixel / 8));
+ else
+ {
+ /* Android internally uses a 1 byte-per-pixel format for
+ ALPHA_8 images. Expand the image from the 1
+ bit-per-pixel X format correctly. */
+
+ for (x = 0; x < image->width; ++x)
+ data_1[x] = (data_2[x / 8] & (1 << x % 8)) ? 0xff : 0;
+ }
+
+ data_1 += bitmap_info.stride;
+ data_2 += image->bytes_per_line;
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+}
+
+void
+android_bell (void)
+{
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.ring_bell);
+ android_exception_check ();
+}
+
+void
+android_set_input_focus (android_window handle, unsigned long time)
+{
+ jobject window;
+ jmethodID make_input_focus;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ make_input_focus = window_class.make_input_focus;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ make_input_focus,
+ (jlong) time);
+ android_exception_check ();
+}
+
+void
+android_raise_window (android_window handle)
+{
+ jobject window;
+ jmethodID raise;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ raise = window_class.raise;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ raise);
+ android_exception_check ();
+}
+
+void
+android_lower_window (android_window handle)
+{
+ jobject window;
+ jmethodID lower;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ lower = window_class.lower;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ lower);
+ android_exception_check ();
+}
+
+int
+android_query_tree (android_window handle, android_window *root_return,
+ android_window *parent_return,
+ android_window **children_return,
+ unsigned int *nchildren_return)
+{
+ jobject window, array;
+ jsize nelements, i;
+ android_window *children;
+ jshort *shorts;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ /* window can be NULL, so this is a service method. */
+ array
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.query_tree,
+ window);
+ android_exception_check ();
+
+ /* The first element of the array is the parent window. The rest
+ are the children. */
+ nelements = (*android_java_env)->GetArrayLength (android_java_env,
+ array);
+ eassert (nelements);
+
+ /* Now fill in the children. */
+ children = xnmalloc (nelements - 1, sizeof *children);
+
+ shorts
+ = (*android_java_env)->GetShortArrayElements (android_java_env, array,
+ NULL);
+ android_exception_check_nonnull (shorts, array);
+
+ for (i = 1; i < nelements; ++i)
+ children[i] = shorts[i];
+
+ /* Finally, return the parent and other values. */
+ *root_return = 0;
+ *parent_return = shorts[0];
+ *children_return = children;
+ *nchildren_return = nelements - 1;
+
+ /* Release the array contents. */
+ (*android_java_env)->ReleaseShortArrayElements (android_java_env, array,
+ shorts, JNI_ABORT);
+
+ ANDROID_DELETE_LOCAL_REF (array);
+ return 1;
+}
+
+void
+android_get_geometry (android_window handle,
+ android_window *root_return,
+ int *x_return, int *y_return,
+ unsigned int *width_return,
+ unsigned int *height_return,
+ unsigned int *border_width_return)
+{
+ jobject window;
+ jarray window_geometry;
+ jmethodID get_geometry;
+ jint *ints;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ get_geometry = window_class.get_window_geometry;
+
+ window_geometry
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ window,
+ get_geometry);
+ android_exception_check ();
+
+ /* window_geometry is an array containing x, y, width and
+ height. border_width is always 0 on Android. */
+ eassert ((*android_java_env)->GetArrayLength (android_java_env,
+ window_geometry)
+ == 4);
+
+ *root_return = 0;
+ *border_width_return = 0;
+
+ ints
+ = (*android_java_env)->GetIntArrayElements (android_java_env,
+ window_geometry,
+ NULL);
+ android_exception_check_nonnull (ints, window_geometry);
+
+ *x_return = ints[0];
+ *y_return = ints[1];
+ *width_return = ints[2];
+ *height_return = ints[3];
+
+ (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+ window_geometry,
+ ints, JNI_ABORT);
+
+ /* Now free the local reference. */
+ ANDROID_DELETE_LOCAL_REF (window_geometry);
+}
+
+void
+android_move_resize_window (android_window window, int x, int y,
+ unsigned int width, unsigned int height)
+{
+ android_move_window (window, x, y);
+ android_resize_window (window, width, height);
+}
+
+void
+android_map_raised (android_window window)
+{
+ android_raise_window (window);
+ android_map_window (window);
+}
+
+void
+android_translate_coordinates (android_window src, int x,
+ int y, int *root_x, int *root_y)
+{
+ jobject window;
+ jarray coordinates;
+ jmethodID method;
+ jint *ints;
+
+ window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW);
+ method = window_class.translate_coordinates;
+ coordinates
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ window, method,
+ (jint) x, (jint) y);
+ android_exception_check ();
+
+ /* The array must contain two elements: X, Y translated to the root
+ window. */
+ eassert ((*android_java_env)->GetArrayLength (android_java_env,
+ coordinates)
+ == 2);
+
+ /* Obtain the coordinates from the array. */
+ ints = (*android_java_env)->GetIntArrayElements (android_java_env,
+ coordinates, NULL);
+ android_exception_check_nonnull (ints, coordinates);
+
+ *root_x = ints[0];
+ *root_y = ints[1];
+
+ /* Release the coordinates. */
+ (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+ coordinates, ints,
+ JNI_ABORT);
+
+ /* And free the local reference. */
+ ANDROID_DELETE_LOCAL_REF (coordinates);
+}
+
+int
+android_wc_lookup_string (android_key_pressed_event *event,
+ wchar_t *buffer_return, int wchars_buffer,
+ int *keysym_return,
+ enum android_lookup_status *status_return)
+{
+ enum android_lookup_status status;
+ int rc;
+ jobject window, string;
+ const jchar *characters;
+ jsize size;
+ size_t i;
+
+ status = ANDROID_LOOKUP_NONE;
+ rc = 0;
+
+ /* See if an actual lookup has to be made. Note that while
+ BUFFER_RETURN is wchar_t, the returned characters are always in
+ UCS. */
+
+ if (event->unicode_char != (uint32_t) -1)
+ {
+ if (event->unicode_char)
+ {
+ if (wchars_buffer < 1)
+ {
+ *status_return = ANDROID_BUFFER_OVERFLOW;
+ return 0;
+ }
+ else
+ {
+ buffer_return[0] = event->unicode_char;
+ status = ANDROID_LOOKUP_CHARS;
+ rc = 1;
+ }
+ }
+
+ *keysym_return = event->keycode;
+
+ if (status == ANDROID_LOOKUP_CHARS)
+ status = ANDROID_LOOKUP_BOTH;
+ else
+ {
+ status = ANDROID_LOOKUP_KEYSYM;
+ rc = 0;
+ }
+
+ *status_return = status;
+
+ return rc;
+ }
+
+ /* Now look up the window. */
+ rc = 0;
+
+ if (!android_handles[event->window].handle
+ || (android_handles[event->window].type
+ != ANDROID_HANDLE_WINDOW))
+ status = ANDROID_LOOKUP_NONE;
+ else
+ {
+ window = android_handles[event->window].handle;
+ string
+ = (*android_java_env)->CallObjectMethod (android_java_env, window,
+ window_class.lookup_string,
+ (jint) event->serial);
+ android_exception_check ();
+
+ if (!string)
+ status = ANDROID_LOOKUP_NONE;
+ else
+ {
+ /* Now return this input method string. */
+ characters = (*android_java_env)->GetStringChars (android_java_env,
+ string, NULL);
+ android_exception_check_1 (string);
+
+ /* Figure out how big the string is. */
+ size = (*android_java_env)->GetStringLength (android_java_env,
+ string);
+
+ /* Copy over the string data. */
+ for (i = 0; i < MIN ((unsigned int) wchars_buffer, size); ++i)
+ buffer_return[i] = characters[i];
+
+ if (i < size)
+ status = ANDROID_BUFFER_OVERFLOW;
+ else
+ status = ANDROID_LOOKUP_CHARS;
+
+ /* Return the number of characters that should have been
+ written. */
+
+ if (size > INT_MAX)
+ rc = INT_MAX;
+ else
+ rc = size;
+
+ (*android_java_env)->ReleaseStringChars (android_java_env, string,
+ characters);
+ ANDROID_DELETE_LOCAL_REF (string);
+ }
+ }
+
+ *status_return = status;
+ return rc;
+}
+
+
+
+/* Low level drawing primitives. */
+
+/* Lock the bitmap corresponding to the window WINDOW. Return the
+ bitmap data upon success, and store the bitmap object in
+ BITMAP_RETURN. Value is NULL upon failure.
+
+ The caller must take care to unlock the bitmap data afterwards. */
+
+unsigned char *
+android_lock_bitmap (android_window window,
+ AndroidBitmapInfo *bitmap_info,
+ jobject *bitmap_return)
+{
+ jobject drawable, bitmap;
+ void *data;
+
+ drawable = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ if (!bitmap)
+ /* NULL is returned when the bitmap does not currently exist due
+ to ongoing reconfiguration on the main thread. */
+ return NULL;
+
+ memset (bitmap_info, 0, sizeof *bitmap_info);
+
+ /* Get the bitmap info. */
+ AndroidBitmap_getInfo (android_java_env, bitmap, bitmap_info);
+
+ if (!bitmap_info->stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return NULL;
+ }
+
+ /* Now lock the image data. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return NULL;
+ }
+
+ /* Give the bitmap to the caller. */
+ *bitmap_return = bitmap;
+
+ /* The bitmap data is now locked. */
+ return data;
+}
+
+/* Damage the window HANDLE by the given damage rectangle. */
+
+void
+android_damage_window (android_drawable handle,
+ struct android_rectangle *damage)
+{
+ jobject drawable, rect;
+
+ drawable = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ /* Now turn DAMAGE into a Java rectangle. */
+ rect = (*android_java_env)->NewObject (android_java_env,
+ android_rect_class,
+ android_rect_constructor,
+ (jint) damage->x,
+ (jint) damage->y,
+ (jint) (damage->x
+ + damage->width),
+ (jint) (damage->y
+ + damage->height));
+ android_exception_check ();
+
+ /* Post the damage to the drawable. */
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ drawable,
+ drawable_class.damage_rect,
+ rect);
+ android_exception_check_1 (rect);
+ ANDROID_DELETE_LOCAL_REF (rect);
+}
+
+
+
+/* Other misc system routines. */
+
+int
+android_get_screen_width (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_width;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) false);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_screen_height (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_height;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) false);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_mm_width (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_width;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) true);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_mm_height (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_height;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) true);
+ android_exception_check ();
+ return rc;
+}
+
+bool
+android_detect_mouse (void)
+{
+ bool rc;
+ jmethodID method;
+
+ method = service_class.detect_mouse;
+ rc = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method);
+ android_exception_check ();
+ return rc;
+}
+
+void
+android_set_dont_focus_on_map (android_window handle,
+ bool no_focus_on_map)
+{
+ jmethodID method;
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ method = window_class.set_dont_focus_on_map;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class,
+ method,
+ (jboolean) no_focus_on_map);
+ android_exception_check ();
+}
+
+void
+android_set_dont_accept_focus (android_window handle,
+ bool no_accept_focus)
+{
+ jmethodID method;
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ method = window_class.set_dont_accept_focus;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class,
+ method,
+ (jboolean) no_accept_focus);
+ android_exception_check ();
+}
+
+void
+android_get_keysym_name (int keysym, char *name_return, size_t size)
+{
+ jobject string;
+ const char *buffer;
+
+ string = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.name_keysym,
+ (jint) keysym);
+ android_exception_check ();
+
+ buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string,
+ NULL);
+ android_exception_check_nonnull ((void *) buffer, string);
+ strncpy (name_return, buffer, size - 1);
+
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ buffer);
+ ANDROID_DELETE_LOCAL_REF (string);
+}
+
+/* Display the on screen keyboard on window WINDOW, or hide it if SHOW
+ is false. Ask the system to bring up or hide the on-screen
+ keyboard on behalf of WINDOW. The request may be rejected by the
+ system, especially when the window does not have the input
+ focus. */
+
+void
+android_toggle_on_screen_keyboard (android_window window, bool show)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = window_class.toggle_on_screen_keyboard;
+
+ /* Now display the on screen keyboard. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, object,
+ window_class.class,
+ method, (jboolean) show);
+
+ /* Check for out of memory errors. */
+ android_exception_check ();
+}
+
+
+
+/* When calling the system's faccessat, make sure to clear the flag
+ AT_EACCESS.
+
+ Android's faccessat simply fails upon using AT_EACCESS, so replace
+ it with zero here. This isn't caught during configuration as Emacs
+ is being cross compiled.
+
+ This replacement is only done when building for Android 16 or
+ later, because earlier versions use the gnulib replacement that
+ lacks these issues.
+
+ This is unnecessary on earlier API versions, as gnulib's
+ rpl_faccessat will be used instead, which lacks these problems. */
+
+/* Like faccessat, except it also understands DIRFD opened using
+ android_dirfd. */
+
+int
+android_faccessat (int dirfd, const char *pathname, int mode, int flags)
+{
+ const char *asset;
+
+ if (dirfd != AT_FDCWD)
+ dirfd
+ = android_lookup_asset_directory_fd (dirfd, &pathname,
+ pathname);
+
+ /* Check if pathname is actually an asset. If that is the case,
+ simply fall back to android_file_access_p. */
+
+ if (dirfd == AT_FDCWD
+ && asset_manager
+ && (asset = android_get_asset_name (pathname)))
+ {
+ if (android_file_access_p (asset, mode))
+ return 0;
+
+ /* Set errno to an appropriate value. */
+ errno = ENOENT;
+ return 1;
+ }
+
+ /* Check if pathname is actually a content resolver URI. */
+
+ if (dirfd == AT_FDCWD
+ && android_init_gui
+ && android_content_name_p (pathname))
+ {
+ if (android_check_content_access (pathname, mode))
+ return 0;
+
+ /* Set errno to an appropriate value. */
+ errno = ENOENT;
+ return 1;
+ }
+
+#if __ANDROID_API__ >= 16
+ return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
+#else
+ return faccessat (dirfd, pathname, mode, flags);
+#endif
+}
+
+
+
+/* Directory listing emulation. */
+
+struct android_dir
+{
+ /* The real DIR *, if it exists. */
+ DIR *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;
+
+ /* 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. */
+
+struct android_dir *
+android_opendir (const char *name)
+{
+ struct android_dir *dir;
+ char *asset_dir;
+ const char *asset_name;
+ size_t limit, length;
+
+ asset_name = android_get_asset_name (name);
+
+ /* If the asset manager exists and NAME is an asset, return an asset
+ directory. */
+ if (asset_manager && asset_name)
+ {
+ asset_dir
+ = (char *) android_scan_directory_tree ((char *) asset_name,
+ &limit);
+
+ if (!asset_dir)
+ {
+ errno = ENOENT;
+ 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. */
+ 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;
+ }
+
+ dir->next = android_dirs;
+ android_dirs = dir;
+
+ return dir;
+ }
+
+ /* Otherwise, open the directory normally. */
+ dir = xmalloc (sizeof *dir);
+ dir->asset_dir = NULL;
+ dir->dir = opendir (name);
+
+ if (!dir->dir)
+ {
+ xfree (dir);
+ return NULL;
+ }
+
+ return dir;
+}
+
+/* Like 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 *
+android_readdir (struct android_dir *dir)
+{
+ static struct dirent dirent;
+ const char *last;
+
+ if (dir->asset_dir)
+ {
+ /* 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++;
+
+ /* 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;
+
+ /* If this is not a directory, return DT_UNKNOWN. Otherwise,
+ return DT_DIR. */
+
+ if (android_is_directory (dir->asset_dir))
+ dirent.d_type = DT_DIR;
+ else
+ dirent.d_type = DT_UNKNOWN;
+
+ /* 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;
+ }
+
+ return readdir (dir->dir);
+}
+
+/* Like closedir, but it also closes asset manager directories. */
+
+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. */
+
+ xfree (dir);
+}
+
+/* Subroutine used by android_fstatat and android_faccessat. 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
+ trace. */
+
+void
+emacs_abort (void)
+{
+ volatile char *foo;
+
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "emacs_abort called, please review the ensuing"
+ " stack trace");
+
+ /* Cause a NULL pointer dereference to make debuggerd generate a
+ tombstone. */
+ foo = NULL;
+ *foo = '\0';
+
+ abort ();
+}
+
+
+
+/* Return whether or not TEXT, a string without multibyte
+ characters, has no bytes with the 8th bit set. */
+
+static bool
+android_check_string (Lisp_Object text)
+{
+ ptrdiff_t i;
+
+ for (i = 0; i < SBYTES (text); ++i)
+ {
+ if (SREF (text, i) & 128)
+ return false;
+ }
+
+ return true;
+}
+
+/* Given a Lisp string TEXT, return a local reference to an equivalent
+ Java string. */
+
+jstring
+android_build_string (Lisp_Object text)
+{
+ Lisp_Object encoded;
+ jstring string;
+ size_t nchars;
+ jchar *characters;
+ USE_SAFE_ALLOCA;
+
+ /* Directly encode TEXT if it contains no multibyte
+ characters. This is okay because the Java extended UTF
+ format is compatible with ASCII. */
+
+ if (SBYTES (text) == SCHARS (text)
+ && android_check_string (text))
+ {
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ SSDATA (text));
+ android_exception_check ();
+ SAFE_FREE ();
+
+ return string;
+ }
+
+ encoded = code_convert_string_norecord (text, Qutf_16le,
+ true);
+ nchars = (SBYTES (encoded) / sizeof (jchar));
+
+ /* Encode the string as UTF-16 prior to creating the string.
+ Copy the string to a separate buffer in order to preserve
+ alignment. */
+
+ characters = SAFE_ALLOCA (SBYTES (encoded));
+ memcpy (characters, SDATA (encoded), SBYTES (encoded));
+
+ /* Create the string. */
+ string
+ = (*android_java_env)->NewString (android_java_env,
+ characters, nchars);
+ android_exception_check ();
+
+ SAFE_FREE ();
+ return string;
+}
+
+/* Do the same, except TEXT is constant string data in ASCII or
+ UTF-8 containing no characters outside the Basic Multilingual
+ Plane. */
+
+jstring
+android_build_jstring (const char *text)
+{
+ jstring string;
+
+ /* Note that Java expects this string to be in ``modified UTF
+ encoding'', which is actually UTF-8, except with NUL
+ encoded as a two-byte sequence, and surrogate pairs encoded
+ in the three-byte extended encoding. The only consequence
+ of passing an actual UTF-8 string is that NUL bytes and
+ characters requiring surrogate pairs cannot be represented,
+ which is not really of consequence. */
+
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ text);
+ android_exception_check ();
+
+ return string;
+}
+
+
+
+/* Exception checking functions. Most JNI functions which allocate
+ memory return NULL upon failure; they also set the JNI
+ environment's pending exception to an OutOfMemoryError.
+
+ These functions check for such errors and call memory_full wherever
+ appropriate. Three variants are provided: one which releases no
+ local references, one which releases a single local reference
+ before calling memory_full, and one which releases two local
+ references.
+
+ Typically, you use these functions by calling them immediately
+ after a JNI function which allocates memory, passing it any local
+ references that are already valid but are not used after leaving
+ the current scope. For example, to allocate foo and then make
+ global_foo its global reference, and then release foo, you write:
+
+ jobject foo, global_foo;
+
+ foo = (*android_java_env)->New...;
+ android_exception_check ();
+
+ global_foo = (*android_java_env)->NewGlobalRef (..., foo);
+ android_exception_check_1 (foo);
+ ANDROID_DELETE_LOCAL_REF (foo);
+
+ where the first android_exception_check ensures that foo has been
+ allocated correctly, while the call to android_exception_check_1,
+ and the call to ANDROID_DELETE_LOCAL_REF afterwards, together
+ ensure the same of global_foo, and also that foo is released both
+ if global_foo cannot be allocated, and after the global reference
+ is created. */
+
+/* Check for JNI exceptions and call memory_full in that
+ situation. */
+
+void
+android_exception_check (void)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+}
+
+/* Check for JNI exceptions. If there is one such exception, clear
+ it, then delete the local reference to OBJECT and call
+ memory_full. */
+
+void
+android_exception_check_1 (jobject object)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ memory_full (0);
+ }
+}
+
+/* Like android_exception_check_one, except it takes more than one
+ local reference argument. */
+
+void
+android_exception_check_2 (jobject object, jobject object1)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ ANDROID_DELETE_LOCAL_REF (object1);
+ memory_full (0);
+ }
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+ Signal out of memory if OBJECT is NULL. OBJECT1 means the
+ same as in `android_exception_check_1'.
+
+ This function is useful when checking for errors from JNI
+ functions that do not set exceptions on failure, such as
+ `GetIntArrayElements'. */
+
+void
+android_exception_check_nonnull (void *object, jobject object1)
+{
+ if (object)
+ return;
+
+ if (object1)
+ ANDROID_DELETE_LOCAL_REF (object1);
+
+ memory_full (0);
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+ Signal out of memory if OBJECT is NULL. OBJECT1 and OBJECT2 mean
+ the same as in `android_exception_check_2'. */
+
+void
+android_exception_check_nonnull_1 (void *object, jobject object1,
+ jobject object2)
+{
+ if (object)
+ return;
+
+ if (object1)
+ ANDROID_DELETE_LOCAL_REF (object1);
+
+ if (object2)
+ ANDROID_DELETE_LOCAL_REF (object2);
+
+ memory_full (0);
+}
+
+
+
+/* Native image transforms. */
+
+/* Transform the coordinates X and Y by the specified affine
+ transformation MATRIX. Place the result in *XOUT and *YOUT. */
+
+static void
+android_transform_coordinates (int x, int y,
+ struct android_transform *transform,
+ float *xout, float *yout)
+{
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is simply
+ implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See
+ the definition of matrix3x3 in image.c for some more explanations
+ about this.) */
+
+ *xout = transform->m1 * x + transform->m2 * y + transform->m3;
+ *yout = transform->m4 * x + transform->m5 * y + transform->m6;
+}
+
+/* Return the interpolation of the four pixels TL, TR, BL, and BR,
+ according to the weights DISTX and DISTY. */
+
+static unsigned int
+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;
+
+ return r;
+}
+
+/* Return the interpolation of the four pixels closest to at X, Y in
+ IMAGE, according to weights in both axes computed from X and Y.
+ IMAGE must be depth 24, or the behavior is undefined. */
+
+static unsigned int
+android_fetch_pixel_bilinear (struct android_image *image,
+ float x, float y)
+{
+ int x1, y1, x2, y2;
+ float distx, disty;
+ unsigned int top_left, top_right;
+ unsigned int bottom_left, bottom_right;
+ char *word;
+
+ /* Compute the four closest corners to X and Y. */
+ x1 = (int) x;
+ x2 = x1 + 1;
+ y1 = (int) y;
+ y2 = y1 + 1;
+
+ /* Make sure all four corners are within range. */
+ x1 = MAX (0, MIN (image->width - 1, x1));
+ y1 = MAX (0, MIN (image->height - 1, y1));
+ x2 = MAX (0, MIN (image->width - 1, x2));
+ y2 = MAX (0, MIN (image->height - 1, y2));
+
+ /* Compute the X and Y biases. These are numbers between 0f and
+ 1f. */
+ distx = x - x1;
+ disty = y - y1;
+
+ /* Fetch the four closest pixels. */
+ word = image->data + y1 * image->bytes_per_line + x1 * 4;
+ memcpy (&top_left, word, sizeof top_left);
+ word = image->data + y1 * image->bytes_per_line + x2 * 4;
+ memcpy (&top_right, word, sizeof top_right);
+ word = image->data + y2 * image->bytes_per_line + x1 * 4;
+ memcpy (&bottom_left, word, sizeof bottom_left);
+ word = image->data + y2 * image->bytes_per_line + x2 * 4;
+ memcpy (&bottom_right, word, sizeof bottom_right);
+
+ /* Do the interpolation. */
+ return android_four_corners_bilinear (top_left, top_right, bottom_left,
+ bottom_right, distx * 256,
+ disty * 256);
+}
+
+/* Transform the depth 24 image IMAGE by the 3x2 affine transformation
+ matrix MATRIX utilizing a bilinear filter. Place the result in
+ OUT. The matrix maps from the coordinate space of OUT to
+ IMAGE. */
+
+void
+android_project_image_bilinear (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ int x, y;
+ unsigned int pixel;
+ float xout, yout;
+ char *word;
+
+ /* Loop through each pixel in OUT. Transform it by TRANSFORM, then
+ interpolate it to IMAGE, and place the result back in OUT. */
+
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_bilinear (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ word = out->data + y * out->bytes_per_line + x * 4;
+ memcpy (word, &pixel, sizeof pixel);
+ }
+ }
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 24 image. */
+
+static unsigned int
+android_fetch_pixel_nearest_24 (struct android_image *image, float x,
+ float y)
+{
+ int x1, y1;
+ char *word;
+ unsigned int pixel;
+
+ x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+ y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+ word = image->data + y1 * image->bytes_per_line + x1 * 4;
+ memcpy (&pixel, word, sizeof pixel);
+
+ return pixel;
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 1 image. */
+
+static unsigned int
+android_fetch_pixel_nearest_1 (struct android_image *image, float x,
+ float y)
+{
+ int x1, y1;
+ char *byte;
+
+ x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+ y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+ byte = image->data + y1 * image->bytes_per_line;
+ return (byte[x1 / 8] & (1 << x1 % 8)) ? 1 : 0;
+}
+
+/* Transform the depth 24 or 1 image IMAGE by the 3x2 affine
+ transformation matrix MATRIX. Place the result in OUT. The matrix
+ maps from the coordinate space of OUT to IMAGE. Use a
+ nearest-neighbor filter. */
+
+void
+android_project_image_nearest (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ int x, y;
+ unsigned int pixel;
+ float xout, yout;
+ char *word, *byte;
+
+ if (image->depth == 1)
+ {
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_nearest_1 (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ byte = out->data + y * out->bytes_per_line + x / 8;
+
+ if (pixel)
+ *byte |= (1 << x % 8);
+ else
+ *byte &= ~(1 << x % 8);
+ }
+ }
+
+ return;
+ }
+
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_nearest_24 (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ word = out->data + y * out->bytes_per_line + x * 4;
+ memcpy (word, &pixel, sizeof pixel);
+ }
+ }
+}
+
+
+
+/* Other miscellaneous functions. */
+
+/* Ask the system to start browsing the specified encoded URL. Upon
+ failure, return a string describing the error. Else, value is
+ nil. */
+
+Lisp_Object
+android_browse_url (Lisp_Object url)
+{
+ jobject value, string;
+ Lisp_Object tem;
+ const char *buffer;
+
+ string = android_build_string (url);
+ value = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.browse_url,
+ string);
+ android_exception_check ();
+
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ /* If no string was returned, return Qnil. */
+ if (!value)
+ return Qnil;
+
+ buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) value,
+ NULL);
+ android_exception_check_1 (string);
+
+ /* Otherwise, build the string describing the error. */
+ tem = build_string_from_utf8 (buffer);
+
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) value,
+ buffer);
+
+ /* And return it. */
+ ANDROID_DELETE_LOCAL_REF (value);
+ return tem;
+}
+
+/* Tell the system to restart Emacs in a short amount of time, and
+ then kill Emacs. Never return. This is used to implement
+ `restart-emacs'. */
+
+_Noreturn void
+android_restart_emacs (void)
+{
+ /* Try to call the Java side function. Normally, this should call
+ System.exit to terminate this process. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.restart_emacs);
+
+ /* Exit anyway, in case EmacsService did not do so. */
+ exit (0);
+}
+
+/* Return a number from 1 to 33 describing the version of Android
+ Emacs is running on.
+
+ This is different from __ANDROID_API__, as that describes the
+ minimum version of Android this build of Emacs will run on, and in
+ turn which APIs Emacs can safely use. */
+
+int
+android_get_current_api_level (void)
+{
+ return android_api_level;
+}
+
+/* Query the status of the battery, and place it in *STATUS.
+ Value is 1 upon failure, else 0. */
+
+int
+android_query_battery (struct android_battery_state *status)
+{
+ jlongArray array;
+ jlong *longs;
+
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.query_battery);
+ android_exception_check ();
+
+ /* A NULL return with no exception means that battery information
+ could not be obtained. */
+
+ if (!array)
+ return 1;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_nonnull (longs, array);
+
+ status->capacity = longs[0];
+ status->charge_counter = longs[1];
+ status->current_average = longs[2];
+ status->current_now = longs[3];
+ status->remaining = longs[4];
+ status->status = longs[5];
+ status->plugged = longs[6];
+ status->temperature = longs[7];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+
+ return 0;
+}
+
+/* Display a small momentary notification on screen containing
+ TEXT, which must be in the modified UTF encoding used by the
+ JVM. */
+
+void
+android_display_toast (const char *text)
+{
+ jstring string;
+
+ /* Make the string. */
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ text);
+ android_exception_check ();
+
+ /* Display the toast. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.display_toast,
+ string);
+ android_exception_check_1 (string);
+
+ /* Delete the local reference to the string. */
+ ANDROID_DELETE_LOCAL_REF (string);
+}
+
+
+
+/* Whether or not a query is currently being made. */
+static bool android_servicing_query;
+
+/* Function that is waiting to be run in the Emacs thread. */
+static void (*android_query_function) (void *);
+
+/* Context for that function. */
+static void *android_query_context;
+
+/* Deadlock protection. The UI thread and the Emacs thread must
+ sometimes make synchronous queries to each other, which are
+ normally answered inside each thread's respective event loop.
+ Deadlocks can happen when both threads simultaneously make such
+ synchronous queries and block waiting for each others responses.
+
+ The Emacs thread can be interrupted to service any queries made by
+ the UI thread, but is not possible the other way around.
+
+ To avoid such deadlocks, an atomic counter is provided. This
+ counter is incremented every time a query starts, and is set to
+ zerp every time one ends. If the UI thread tries to make a query
+ and sees that the counter is non-zero, it simply returns so that
+ its event loop can proceed to perform and respond to the query. If
+ the Emacs thread sees the same thing, then it stops to service all
+ queries being made by the input method, then proceeds to make its
+ query. */
+
+/* Run any function that the UI thread has asked to run, and then
+ signal its completion. */
+
+void
+android_check_query (void)
+{
+ void (*proc) (void *);
+ void *closure;
+
+ if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST))
+ return;
+
+ /* First, load the procedure and closure. */
+ __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST);
+ __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST);
+
+ if (!proc)
+ return;
+
+ proc (closure);
+
+ /* Finish the query. */
+ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
+ __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST);
+
+ /* Signal completion. */
+ sem_post (&android_query_sem);
+}
+
+/* Notice that the Emacs thread will start blocking waiting for a
+ response from the UI thread. Process any pending queries from the
+ UI thread.
+
+ This function may be called from Java. */
+
+static void
+android_begin_query (void)
+{
+ if (__atomic_test_and_set (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ {
+ /* Answer the query that is currently being made. */
+ assert (android_query_function != NULL);
+ android_check_query ();
+
+ /* Wait for that query to complete. */
+ while (__atomic_load_n (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ ;;
+ }
+}
+
+/* Notice that a query has stopped. This function may be called from
+ Java. */
+
+static void
+android_end_query (void)
+{
+ __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST);
+}
+
+/* Synchronously ask the Emacs thread to run the specified PROC with
+ the given CLOSURE. Return if this fails, or once PROC is run.
+
+ PROC may be run from inside maybe_quit.
+
+ It is not okay to run Lisp code which signals or performs non
+ trivial tasks inside PROC.
+
+ Return 1 if the Emacs thread is currently waiting for the UI thread
+ to respond and PROC could not be run, or 0 otherwise. */
+
+int
+android_run_in_emacs_thread (void (*proc) (void *), void *closure)
+{
+ union android_event event;
+
+ event.xaction.type = ANDROID_WINDOW_ACTION;
+ event.xaction.serial = ++event_serial;
+ event.xaction.window = 0;
+ event.xaction.action = 0;
+
+ /* Set android_query_function and android_query_context. */
+ __atomic_store_n (&android_query_context, closure, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST);
+
+ /* Don't allow deadlocks to happen; make sure the Emacs thread is
+ not waiting for something to be done. */
+
+ if (__atomic_test_and_set (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ {
+ __atomic_store_n (&android_query_context, NULL,
+ __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL,
+ __ATOMIC_SEQ_CST);
+
+ return 1;
+ }
+
+ /* Send a dummy event. `android_check_query' will be called inside
+ wait_reading_process_output after the event arrives.
+
+ Otherwise, android_select will call android_check_thread the next
+ time it is entered. */
+ android_write_event (&event);
+
+ /* Start waiting for the function to be executed. */
+ while (sem_wait (&android_query_sem) < 0)
+ ;;
+
+ return 0;
+}
+
+
+
+/* Input method related functions. */
+
+/* Change WINDOW's active selection to the characters between
+ SELECTION_START and SELECTION_END.
+
+ Also, update the composing region to COMPOSING_REGION_START and
+ COMPOSING_REGION_END.
+
+ If any value cannot fit in jint, then the behavior of the input
+ method is undefined. */
+
+void
+android_update_ic (android_window window, ptrdiff_t selection_start,
+ ptrdiff_t selection_end, ptrdiff_t composing_region_start,
+ ptrdiff_t composing_region_end)
+{
+ jobject object;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.update_ic,
+ object,
+ (jint) selection_start,
+ (jint) selection_end,
+ (jint) composing_region_start,
+ (jint) composing_region_end);
+ android_exception_check ();
+}
+
+/* Reinitialize any ongoing input method connection on WINDOW.
+
+ Any input method that is connected to WINDOW will invalidate its
+ cache of the buffer contents.
+
+ MODE controls certain aspects of the input method's behavior:
+
+ - If MODE is ANDROID_IC_MODE_NULL, the input method will be
+ deactivated, and an ASCII only keyboard will be displayed
+ instead.
+
+ - If MODE is ANDROID_IC_MODE_ACTION, the input method will
+ edit text normally, but send ``return'' as a key event.
+ This is useful inside the mini buffer.
+
+ - If MODE is ANDROID_IC_MODE_TEXT, the input method is free
+ to behave however it wants. */
+
+void
+android_reset_ic (android_window window, enum android_ic_mode mode)
+{
+ jobject object;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.reset_ic,
+ object, (jint) mode);
+ android_exception_check ();
+}
+
+/* Make updates to extracted text known to the input method on
+ WINDOW. TEXT should be a local reference to the new
+ extracted text. TOKEN should be the token specified by the
+ input method. */
+
+void
+android_update_extracted_text (android_window window, void *text,
+ int token)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = service_class.update_extracted_text;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method, object,
+ /* N.B. that
+ text is not
+ jobject,
+ because that
+ type is not
+ available in
+ androidgui.h. */
+ (jobject) text,
+ (jint) token);
+ android_exception_check_1 (text);
+}
+
+/* Report the position of the cursor to the input method connection on
+ WINDOW.
+
+ X is the horizontal position of the end of the insertion marker. Y
+ is the top of the insertion marker. Y_BASELINE is the baseline of
+ the row containing the insertion marker, and Y_BOTTOM is the bottom
+ of the insertion marker. */
+
+void
+android_update_cursor_anchor_info (android_window window, float x,
+ float y, float y_baseline,
+ float y_bottom)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = service_class.update_cursor_anchor_info;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ object,
+ (jfloat) x,
+ (jfloat) y,
+ (jfloat) y_baseline,
+ (jfloat) y_bottom);
+ android_exception_check ();
+}
+
+
+
+/* Window decoration management functions. */
+
+/* Make the specified WINDOW fullscreen, i.e. obscure all of the
+ system navigation and status bars. If not FULLSCREEN, make it
+ maximized instead.
+
+ Value is 1 if the system does not support this, else 0. */
+
+int
+android_set_fullscreen (android_window window, bool fullscreen)
+{
+ jobject object;
+
+ /* Android 4.0 and earlier don't support fullscreen windows. */
+
+ if (android_api_level < 16)
+ return 1;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ object,
+ window_class.class,
+ window_class.set_fullscreen,
+ (jboolean) fullscreen);
+ android_exception_check ();
+ return 0;
+}
+
+
+
+/* External asset management interface. By using functions here
+ to read and write from files, Emacs can avoid opening a
+ shared memory file descriptor for each ``asset'' file. */
+
+/* Like android_open. However, return a structure that can
+ either directly hold an AAsset or a file descriptor.
+
+ Value is the structure upon success. Upon failure, value
+ consists of an uninitialized file descriptor, but its asset
+ field is set to -1, and errno is set accordingly. */
+
+struct android_fd_or_asset
+android_open_asset (const char *filename, int oflag, mode_t mode)
+{
+ const char *name;
+ struct android_fd_or_asset fd;
+ AAsset *asset;
+
+ /* Initialize FD by setting its asset to an invalid
+ pointer. */
+ fd.asset = (void *) -1;
+
+ /* See if this is an asset. */
+
+ if (asset_manager && (name = android_get_asset_name (filename)))
+ {
+ /* Return failure for unsupported flags. */
+
+ if (oflag & O_WRONLY || oflag & O_RDWR)
+ {
+ errno = EROFS;
+ return fd;
+ }
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return fd;
+ }
+
+ /* Now try to open the asset. */
+ asset = AAssetManager_open (asset_manager, name,
+ AASSET_MODE_STREAMING);
+
+ if (!asset)
+ {
+ errno = ENOENT;
+ return fd;
+ }
+
+ /* Return the asset. */
+ fd.asset = asset;
+ return fd;
+ }
+
+ /* If the file is not an asset, fall back to android_open and
+ get a regular file descriptor. */
+
+ fd.fd = android_open (filename, oflag, mode);
+ if (fd.fd < 0)
+ return fd;
+
+ /* Set fd.asset to NULL, signifying that it is a file
+ descriptor. */
+ fd.asset = NULL;
+ return fd;
+}
+
+/* Like android_close. However, it takes a ``file descriptor''
+ opened using android_open_asset. */
+
+int
+android_close_asset (struct android_fd_or_asset asset)
+{
+ if (!asset.asset)
+ return android_close (asset.fd);
+
+ AAsset_close (asset.asset);
+ return 0;
+}
+
+/* Like `emacs_read_quit'. However, it handles file descriptors
+ opened using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read_quit (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return emacs_read_quit (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `read'. However, it handles file descriptors opened
+ using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return read (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `lseek', but it handles ``file descriptors'' opened with
+ android_open_asset. */
+
+off_t
+android_asset_lseek (struct android_fd_or_asset asset, off_t off,
+ int whence)
+{
+ if (!asset.asset)
+ return lseek (asset.fd, off, whence);
+
+ return AAsset_seek (asset.asset, off, whence);
+}
+
+/* Like `fstat'. */
+
+int
+android_asset_fstat (struct android_fd_or_asset asset,
+ struct stat *statb)
+{
+ if (!asset.asset)
+ return fstat (asset.fd, statb);
+
+ /* Clear statb. */
+ memset (statb, 0, sizeof *statb);
+
+ /* Set the mode. */
+ statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ /* Owned by root. */
+ statb->st_uid = 0;
+ statb->st_gid = 0;
+
+ /* Size of the file. */
+ statb->st_size = AAsset_getLength (asset.asset);
+ return 0;
+}
+
+
+
+/* Window cursor support. */
+
+android_cursor
+android_create_font_cursor (enum android_cursor_shape shape)
+{
+ android_cursor id;
+ short prev_max_handle;
+ jobject object;
+
+ /* First, allocate the cursor handle. */
+ prev_max_handle = max_handle;
+ id = android_alloc_id ();
+
+ if (!id)
+ error ("Out of cursor handles!");
+
+ /* Next, create the cursor. */
+ object = (*android_java_env)->NewObject (android_java_env,
+ cursor_class.class,
+ cursor_class.constructor,
+ (jshort) id,
+ (jint) shape);
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[id].type = ANDROID_HANDLE_CURSOR;
+ android_handles[id].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[id].handle)
+ memory_full (0);
+
+ return id;
+}
+
+void
+android_define_cursor (android_window window, android_cursor cursor)
+{
+ jobject window1, cursor1;
+ jmethodID method;
+
+ window1 = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ cursor1 = android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR);
+ method = window_class.define_cursor;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window1,
+ window_class.class,
+ method, cursor1);
+ android_exception_check ();
+}
+
+void
+android_free_cursor (android_cursor cursor)
+{
+ if (android_handles[cursor].type != ANDROID_HANDLE_CURSOR)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy something not a CURSOR!");
+ emacs_abort ();
+ }
+
+ android_destroy_handle (cursor);
+}
+
+
+
+/* Process execution.
+
+ Newer Android systems use SELinux to restrict user programs from
+ executing programs installed in the application data directory for
+ security reasons. Emacs uses a `loader' binary installed in the
+ application data directory to manually load executables and replace
+ the `execve' system call. */
+
+enum
+ {
+ /* Maximum number of arguments available. */
+ MAXARGS = 1024,
+ };
+
+/* Rewrite the command line given in *ARGV to utilize the `exec1'
+ bootstrap binary if necessary.
+
+ Value is 0 upon success, else 1. Set errno upon failure.
+
+ ARGV holds a pointer to a NULL-terminated array of arguments given
+ to `emacs_spawn'. */
+
+int
+android_rewrite_spawn_argv (const char ***argv)
+{
+ static const char *new_args[MAXARGS];
+ static char exec1_name[PATH_MAX], loader_name[PATH_MAX];
+ size_t i, nargs;
+
+ /* This isn't required on Android 9 or earlier. */
+
+ if (android_api_level < 29 || !android_use_exec_loader)
+ return 0;
+
+ /* Get argv[0]; this should never be NULL.
+ Then, verify that it exists and is executable. */
+
+ eassert (**argv);
+ if (access (**argv, R_OK | X_OK))
+ return 1;
+
+ /* Count the number of arguments in *argv. */
+
+ nargs = 0;
+ while ((*argv)[nargs])
+ ++nargs;
+
+ /* nargs now holds the number of arguments in argv. If it's larger
+ than MAXARGS, return failure. */
+
+ if (nargs + 2 > MAXARGS)
+ {
+ errno = E2BIG;
+ return 1;
+ }
+
+ /* Fill in the name of `libexec1.so'. */
+ snprintf (exec1_name, PATH_MAX, "%s/libexec1.so",
+ android_lib_dir);
+
+ /* And libloader.so. */
+ snprintf (loader_name, PATH_MAX, "%s/libloader.so",
+ android_lib_dir);
+
+ /* Now fill in the first two arguments. */
+ new_args[0] = exec1_name;
+ new_args[1] = loader_name;
+
+ /* And insert the rest, including the trailing NULL. */
+ for (i = 0; i < nargs + 1; ++i)
+ new_args[i + 2] = (*argv)[i];
+
+ /* Replace argv. */
+ *argv = new_args;
+
+ /* Return success. */
+ return 0;
+}
+
+
+
+#else /* ANDROID_STUBIFY */
+
+/* X emulation functions for Android. */
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ emacs_abort ();
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ emacs_abort ();
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ emacs_abort ();
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable drawable,
+ enum android_image_format format)
+{
+ emacs_abort ();
+}
+
+void
+android_put_image (android_pixmap pixmap,
+ struct android_image *image)
+{
+ emacs_abort ();
+}
+
+void
+android_project_image_bilinear (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ emacs_abort ();
+}
+
+void
+android_project_image_nearest (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ emacs_abort ();
+}
+
+#endif
diff --git a/src/android.h b/src/android.h
new file mode 100644
index 00000000000..62d420d4cce
--- /dev/null
+++ b/src/android.h
@@ -0,0 +1,231 @@
+/* Android initialization for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/* On Android, Emacs is built as a shared library loaded from Java
+ using the Java Native Interface. Emacs's `main' function is
+ renamed `android_emacs_init', and runs with some modifications
+ inside a separate thread, communicating with the Java code through
+ a table of function pointers. */
+
+#ifndef _ANDROID_H_
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+#include <pwd.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include <android/bitmap.h>
+
+#include "androidgui.h"
+#include "lisp.h"
+#endif
+
+extern bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+extern int android_emacs_init (int, char **, char *);
+extern int android_select (int, fd_set *, fd_set *, fd_set *,
+ struct timespec *);
+
+extern int android_open (const char *, int, mode_t);
+extern char *android_user_full_name (struct passwd *);
+extern int android_fstat (int, struct stat *);
+extern int android_fstatat (int, const char *restrict,
+ struct stat *restrict, int);
+extern int android_faccessat (int, const char *, int, int);
+extern int android_close (int);
+extern int android_fclose (FILE *);
+extern const char *android_get_home_directory (void);
+
+extern double android_pixel_density_x, android_pixel_density_y;
+
+enum android_handle_type
+ {
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_GCONTEXT,
+ ANDROID_HANDLE_PIXMAP,
+ ANDROID_HANDLE_CURSOR,
+ };
+
+extern jobject android_resolve_handle (android_handle,
+ enum android_handle_type);
+extern unsigned char *android_lock_bitmap (android_window,
+ AndroidBitmapInfo *,
+ jobject *);
+extern void android_damage_window (android_window,
+ struct android_rectangle *);
+extern int android_get_screen_width (void);
+extern int android_get_screen_height (void);
+extern int android_get_mm_width (void);
+extern int android_get_mm_height (void);
+extern bool android_detect_mouse (void);
+
+extern void android_set_dont_focus_on_map (android_window, bool);
+extern void android_set_dont_accept_focus (android_window, bool);
+
+extern jstring android_build_string (Lisp_Object);
+extern jstring android_build_jstring (const char *);
+extern void android_exception_check (void);
+extern void android_exception_check_1 (jobject);
+extern void android_exception_check_2 (jobject, jobject);
+extern void android_exception_check_nonnull (void *, jobject);
+extern void android_exception_check_nonnull_1 (void *, jobject, jobject);
+
+extern void android_get_keysym_name (int, char *, size_t);
+extern void android_wait_event (void);
+extern void android_toggle_on_screen_keyboard (android_window, bool);
+extern _Noreturn void android_restart_emacs (void);
+extern int android_get_current_api_level (void);
+
+
+
+/* Directory listing emulation. */
+
+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 *);
+
+
+
+/* External asset manager interface. */
+
+struct android_fd_or_asset
+{
+ /* The file descriptor. */
+ int fd;
+
+ /* The asset. If set, FD is not a real file descriptor. */
+ void *asset;
+};
+
+extern struct android_fd_or_asset android_open_asset (const char *,
+ int, mode_t);
+extern int android_close_asset (struct android_fd_or_asset);
+extern ssize_t android_asset_read_quit (struct android_fd_or_asset,
+ void *, size_t);
+extern ssize_t android_asset_read (struct android_fd_or_asset,
+ void *, size_t);
+extern off_t android_asset_lseek (struct android_fd_or_asset, off_t, int);
+extern int android_asset_fstat (struct android_fd_or_asset,
+ struct stat *);
+
+
+
+/* Very miscellaneous functions. */
+
+struct android_battery_state
+{
+ /* Battery charge level in integer percentage. */
+ intmax_t capacity;
+
+ /* Battery charge level in microampere-hours. */
+ intmax_t charge_counter;
+
+ /* Battery current in microampere-hours. */
+ intmax_t current_average;
+
+ /* Instantaneous battery current in microampere-hours. */
+ intmax_t current_now;
+
+ /* Estimate as to the amount of time remaining until the battery is
+ charged, in milliseconds. */
+ intmax_t remaining;
+
+ /* Battery status. The value is either:
+
+ 2, if the battery is charging.
+ 3, if the battery is discharging.
+ 5, if the battery is full.
+ 4, if the battery is not full or discharging,
+ but is not charging either.
+ 1, if the battery state is unknown. */
+ int status;
+
+ /* The power source of the battery. Value is:
+
+ 0, if on battery power.
+ 1, for line power.
+ 8, for dock power.
+ 2, for USB power.
+ 4, for wireless power. */
+ int plugged;
+
+ /* The temperature of the battery in 10 * degrees centigrade. */
+ int temperature;
+};
+
+extern Lisp_Object android_browse_url (Lisp_Object);
+extern int android_query_battery (struct android_battery_state *);
+extern void android_display_toast (const char *);
+
+
+
+/* Event loop functions. */
+
+extern void android_check_query (void);
+extern int android_run_in_emacs_thread (void (*) (void *), void *);
+extern void android_write_event (union android_event *);
+
+extern unsigned int event_serial;
+
+
+
+/* Process related functions. */
+extern int android_rewrite_spawn_argv (const char ***);
+
+#endif
+
+/* JNI functions should not be built when Emacs is stubbed out for the
+ build. These should be documented in EmacsNative.java. */
+
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+
+extern JNIEnv *android_java_env;
+
+#define ANDROID_DELETE_LOCAL_REF(ref) \
+ ((*android_java_env)->DeleteLocalRef (android_java_env, \
+ (ref)))
+
+#define NATIVE_NAME(name) Java_org_gnu_emacs_EmacsNative_##name
+
+/* Prologue which must be inserted before each JNI function.
+ See initEmacs for why. */
+
+#if defined __i386__
+extern void *unused_pointer;
+
+#define JNI_STACK_ALIGNMENT_PROLOGUE \
+ __attribute__ ((aligned (32))) char stack_align_buffer[32]; \
+ \
+ /* Trick GCC into not optimizing this variable away. */ \
+ unused_pointer = stack_align_buffer;
+
+#else /* !__i386__ */
+#define JNI_STACK_ALIGNMENT_PROLOGUE ((void) 0)
+#endif /* __i386__ */
+
+#endif
+#endif /* _ANDROID_H_ */
diff --git a/src/androidfns.c b/src/androidfns.c
new file mode 100644
index 00000000000..60b0549e7d1
--- /dev/null
+++ b/src/androidfns.c
@@ -0,0 +1,3205 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <math.h>
+
+#include "lisp.h"
+#include "android.h"
+#include "androidterm.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "buffer.h"
+#include "androidgui.h"
+
+#ifndef ANDROID_STUBIFY
+
+/* Some kind of reference count for the image cache. */
+static ptrdiff_t image_cache_refcount;
+
+/* The frame of the currently visible tooltip, or nil if none. */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+ currently visible tooltip. */
+static android_window tip_window;
+
+/* The X and Y deltas of the last call to `x-show-tip'. */
+static Lisp_Object tip_dx, tip_dy;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+ fires. */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_parms;
+
+#endif
+
+static struct android_display_info *
+android_display_info_for_name (Lisp_Object name)
+{
+ struct android_display_info *dpyinfo;
+
+ CHECK_STRING (name);
+
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ {
+ if (!NILP (Fstring_equal (XCAR (dpyinfo->name_list_element),
+ name)))
+ return dpyinfo;
+ }
+
+ error ("Cannot connect to Android if it was not initialized"
+ " at startup");
+}
+
+static struct android_display_info *
+check_android_display_info (Lisp_Object object)
+{
+ struct android_display_info *dpyinfo;
+ struct frame *sf, *f;
+ struct terminal *t;
+
+ if (NILP (object))
+ {
+ sf = XFRAME (selected_frame);
+
+ if (FRAME_ANDROID_P (sf) && FRAME_LIVE_P (sf))
+ dpyinfo = FRAME_DISPLAY_INFO (sf);
+ else if (x_display_list)
+ dpyinfo = x_display_list;
+ else
+ error ("Android windows are not in use or not initialized");
+ }
+ else if (TERMINALP (object))
+ {
+ t = decode_live_terminal (object);
+
+ if (t->type != output_android)
+ error ("Terminal %d is not an Android display", t->id);
+
+ dpyinfo = t->display_info.android;
+ }
+ else if (STRINGP (object))
+ dpyinfo = android_display_info_for_name (object);
+ else
+ {
+ f = decode_window_system_frame (object);
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ }
+
+ return dpyinfo;
+}
+
+Display_Info *
+check_x_display_info (Lisp_Object object)
+{
+ return check_android_display_info (object);
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+void
+gamma_correct (struct frame *f, Emacs_Color *color)
+{
+ if (f->gamma)
+ {
+ color->red = pow (color->red / 65535.0, f->gamma) * 65535.0 + 0.5;
+ color->green = pow (color->green / 65535.0, f->gamma) * 65535.0 + 0.5;
+ color->blue = pow (color->blue / 65535.0, f->gamma) * 65535.0 + 0.5;
+ }
+}
+
+/* Decide if color named COLOR_NAME is valid for use on frame F. If
+ so, return the RGB values in COLOR. If ALLOC_P, allocate the
+ color. Value is false if COLOR_NAME is invalid, or no color could
+ be allocated. MAKE_INDEX is some mysterious argument used on
+ NS. */
+
+bool
+android_defined_color (struct frame *f, const char *color_name,
+ Emacs_Color *color, bool alloc_p,
+ bool make_index)
+{
+ bool success_p;
+
+ success_p = false;
+
+ block_input ();
+ success_p = android_parse_color (f, color_name, color);
+ if (success_p && alloc_p)
+ success_p = android_alloc_nearest_color (f, color);
+ unblock_input ();
+
+ return success_p;
+}
+
+/* Return the pixel color value for color COLOR_NAME on frame F. If F
+ is a monochrome frame, return MONO_COLOR regardless of what ARG
+ says. Signal an error if color can't be allocated. */
+
+static unsigned long
+android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
+{
+ Emacs_Color cdef;
+
+ CHECK_STRING (color_name);
+
+ if (android_defined_color (f, SSDATA (color_name), &cdef,
+ true, false))
+ return cdef.pixel;
+
+ signal_error ("Undefined color", color_name);
+}
+
+static void
+android_set_parent_frame (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ struct frame *p;
+
+ p = NULL;
+
+ if (!NILP (new_value)
+ && (!FRAMEP (new_value)
+ || !FRAME_LIVE_P (p = XFRAME (new_value))
+ || !FRAME_ANDROID_P (p)))
+ {
+ store_frame_param (f, Qparent_frame, old_value);
+ error ("Invalid specification of `parent-frame'");
+ }
+
+ if (p != FRAME_PARENT_FRAME (f))
+ {
+ block_input ();
+ android_reparent_window (FRAME_ANDROID_WINDOW (f),
+ (p ? FRAME_ANDROID_WINDOW (p)
+ : FRAME_DISPLAY_INFO (f)->root_window),
+ f->left_pos, f->top_pos);
+ unblock_input ();
+
+ fset_parent_frame (f, new_value);
+ }
+
+ /* Update the fullscreen frame parameter as well. */
+ FRAME_TERMINAL (f)->fullscreen_hook (f);
+}
+
+void
+android_implicitly_set_name (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+
+}
+
+void
+android_explicitly_set_name (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+
+}
+
+/* Set the number of lines used for the tool bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tool bar lines. This function changes the
+ height of all windows on frame F to match the new tool bar height.
+ The frame's height doesn't change. */
+
+static void
+android_set_tool_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int nlines;
+
+ /* Treat tool bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ android_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+void
+android_change_tool_bar_height (struct frame *f, int height)
+{
+ int unit = FRAME_LINE_HEIGHT (f);
+ int old_height = FRAME_TOOL_BAR_HEIGHT (f);
+ int lines = (height + unit - 1) / unit;
+ Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ FRAME_TOOL_BAR_HEIGHT (f) = height;
+ FRAME_TOOL_BAR_LINES (f) = lines;
+ store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
+
+ if (FRAME_ANDROID_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tool_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+
+ if (!f->tool_bar_resized)
+ {
+ /* As long as tool_bar_resized is false, effectively try to change
+ F's native height. */
+ if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 1, false, Qtool_bar_lines);
+ else
+ adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+ f->tool_bar_resized = f->tool_bar_redisplayed;
+ }
+ else
+ /* Any other change may leave the native size of F alone. */
+ adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+}
+
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tab bar lines. This function may change the
+ height of all windows on frame F to match the new tab bar height.
+ The frame's height may change if frame_inhibit_implied_resize was
+ set accordingly. */
+
+static void
+android_set_tab_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int olines;
+ int nlines;
+
+ olines = FRAME_TAB_BAR_LINES (f);
+
+ /* Treat tab bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ if (nlines != olines && (olines == 0 || nlines == 0))
+ android_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+void
+android_change_tab_bar_height (struct frame *f, int height)
+{
+ int unit, old_height, lines;
+ Lisp_Object fullscreen;
+
+ unit = FRAME_LINE_HEIGHT (f);
+ old_height = FRAME_TAB_BAR_HEIGHT (f);
+ fullscreen = get_frame_param (f, Qfullscreen);
+
+ /* This differs from the tool bar code in that the tab bar height is
+ not rounded up. Otherwise, if redisplay_tab_bar decides to grow
+ the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+ leading to the tab bar height being incorrectly set upon the next
+ call to android_set_font. (bug#59285) */
+ lines = height / unit;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ /* Recalculate tab bar and frame text sizes. */
+ FRAME_TAB_BAR_HEIGHT (f) = height;
+ FRAME_TAB_BAR_LINES (f) = lines;
+ store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+
+ if (FRAME_ANDROID_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+ if (!f->tab_bar_resized)
+ {
+ /* As long as tab_bar_resized is false, effectively try to change
+ F's native height. */
+ if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 1, false, Qtab_bar_lines);
+ else
+ adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+ f->tab_bar_resized = f->tab_bar_redisplayed;
+ }
+ else
+ /* Any other change may leave the native size of F alone. */
+ adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+}
+
+void
+android_set_scroll_bar_default_height (struct frame *f)
+{
+ int height;
+
+ height = FRAME_LINE_HEIGHT (f);
+
+ /* The height of a non-toolkit scrollbar is 14 pixels. */
+ FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
+
+ /* Use all of that space (aside from required margins) for the
+ scroll bar. */
+ FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = 14;
+}
+
+void
+android_set_scroll_bar_default_width (struct frame *f)
+{
+ int unit;
+
+ unit = FRAME_COLUMN_WIDTH (f);
+
+ FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+ FRAME_CONFIG_SCROLL_BAR_WIDTH (f)
+ = FRAME_CONFIG_SCROLL_BAR_COLS (f) * unit;
+}
+
+
+/* Verify that the icon position args for this window are valid. */
+
+static void
+android_icon_verify (struct frame *f, Lisp_Object parms)
+{
+ Lisp_Object icon_x, icon_y;
+
+ /* Set the position of the icon. Note that twm groups all
+ icons in an icon window. */
+ icon_x = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+ RES_TYPE_NUMBER);
+ icon_y = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+ RES_TYPE_NUMBER);
+
+ if (!BASE_EQ (icon_x, Qunbound) && !BASE_EQ (icon_y, Qunbound))
+ {
+ CHECK_FIXNUM (icon_x);
+ CHECK_FIXNUM (icon_y);
+ }
+ else if (!BASE_EQ (icon_x, Qunbound) || !BASE_EQ (icon_y, Qunbound))
+ error ("Both left and top icon corners of icon must be specified");
+}
+
+/* Handle the icon stuff for this window. Perhaps later we might
+ want an x_set_icon_position which can be called interactively as
+ well. */
+
+static void
+android_icon (struct frame *f, Lisp_Object parms)
+{
+ /* Set the position of the icon. Note that twm groups all
+ icons in an icon window. */
+ Lisp_Object icon_x
+ = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+ RES_TYPE_NUMBER);
+ Lisp_Object icon_y
+ = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+ RES_TYPE_NUMBER);
+
+ bool xgiven = !BASE_EQ (icon_x, Qunbound);
+ bool ygiven = !BASE_EQ (icon_y, Qunbound);
+
+ if (xgiven != ygiven)
+ error ("Both left and top icon corners of icon must be specified");
+
+ if (xgiven)
+ {
+ check_integer_range (icon_x, INT_MIN, INT_MAX);
+ check_integer_range (icon_y, INT_MIN, INT_MAX);
+ }
+
+ /* Now return as this is not supported on Android. */
+}
+
+/* Make the GCs needed for this window, setting the background
+ color. */
+
+static void
+android_make_gc (struct frame *f)
+{
+ struct android_gc_values gc_values;
+
+ block_input ();
+
+ /* Create the GCs of this frame.
+ Note that many default values are used. */
+
+ gc_values.foreground = FRAME_FOREGROUND_PIXEL (f);
+ gc_values.background = FRAME_BACKGROUND_PIXEL (f);
+ f->output_data.android->normal_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+
+ /* Reverse video style. */
+ gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+ gc_values.background = FRAME_FOREGROUND_PIXEL (f);
+ f->output_data.android->reverse_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+
+ /* Cursor has cursor-color background, background-color foreground. */
+ gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+ gc_values.background = f->output_data.android->cursor_pixel;
+ f->output_data.android->cursor_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+ unblock_input ();
+}
+
+
+/* Free what was allocated in android_make_gc. */
+
+void
+android_free_gcs (struct frame *f)
+{
+ block_input ();
+
+ if (f->output_data.android->normal_gc)
+ {
+ android_free_gc (f->output_data.android->normal_gc);
+ f->output_data.android->normal_gc = 0;
+ }
+
+ if (f->output_data.android->reverse_gc)
+ {
+ android_free_gc (f->output_data.android->reverse_gc);
+ f->output_data.android->reverse_gc = 0;
+ }
+
+ if (f->output_data.android->cursor_gc)
+ {
+ android_free_gc (f->output_data.android->cursor_gc);
+ f->output_data.android->cursor_gc = 0;
+ }
+
+ unblock_input ();
+}
+
+/* Handler for signals raised during x_create_frame and
+ Fx_create_tip_frame. FRAME is the frame which is partially
+ constructed. */
+
+static Lisp_Object
+unwind_create_frame (Lisp_Object frame)
+{
+ struct frame *f = XFRAME (frame);
+
+ /* If frame is already dead, nothing to do. This can happen if the
+ display is disconnected after the frame has become official, but
+ before Fx_create_frame removes the unwind protect. */
+ if (!FRAME_LIVE_P (f))
+ return Qnil;
+
+ /* If frame is ``official'', nothing to do. */
+ if (NILP (Fmemq (frame, Vframe_list)))
+ {
+ /* If the frame's image cache refcount is still the same as our
+ private shadow variable, it means we are unwinding a frame
+ for which we didn't yet call init_frame_faces, where the
+ refcount is incremented. Therefore, we increment it here, so
+ that free_frame_faces, called in x_free_frame_resources
+ below, will not mistakenly decrement the counter that was not
+ incremented yet to account for this new frame. */
+ if (FRAME_IMAGE_CACHE (f) != NULL
+ && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+ FRAME_IMAGE_CACHE (f)->refcount++;
+
+ android_free_frame_resources (f);
+ free_glyphs (f);
+ return Qt;
+ }
+
+ return Qnil;
+}
+
+static void
+do_unwind_create_frame (Lisp_Object frame)
+{
+ unwind_create_frame (frame);
+}
+
+void
+android_default_font_parameter (struct frame *f, Lisp_Object parms)
+{
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, NULL,
+ RES_TYPE_STRING);
+ Lisp_Object font = Qnil;
+ if (BASE_EQ (font_param, Qunbound))
+ font_param = Qnil;
+
+ if (NILP (font))
+ font = (!NILP (font_param)
+ ? font_param
+ : gui_display_get_arg (dpyinfo, parms,
+ Qfont, "font", "Font",
+ RES_TYPE_STRING));
+
+ if (! FONTP (font) && ! STRINGP (font))
+ {
+ const char *names[] = {
+ "Droid Sans Mono-12",
+ "Monospace-12",
+ "DroidSansMono-12",
+ NULL
+ };
+ int i;
+
+ for (i = 0; names[i]; i++)
+ {
+ font = font_open_by_name (f, build_unibyte_string (names[i]));
+ if (! NILP (font))
+ break;
+ }
+
+ if (NILP (font))
+ error ("No suitable font was found");
+ }
+
+ gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING);
+}
+
+static void
+android_create_frame_window (struct frame *f)
+{
+ struct android_set_window_attributes attributes;
+ enum android_window_value_mask attribute_mask;
+
+ attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+ attribute_mask = ANDROID_CW_BACK_PIXEL;
+
+ block_input ();
+ FRAME_ANDROID_WINDOW (f)
+ = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+ f->left_pos,
+ f->top_pos,
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f),
+ attribute_mask, &attributes);
+ unblock_input ();
+}
+
+#endif /* ANDROID_STUBIFY */
+
+
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+ 1, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object parms)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct frame *f;
+ Lisp_Object frame, tem;
+ Lisp_Object name;
+ bool minibuffer_only;
+ bool undecorated, override_redirect;
+ long window_prompting;
+ specpdl_ref count;
+ Lisp_Object display;
+ struct android_display_info *dpyinfo;
+ Lisp_Object parent, parent_frame;
+ struct kboard *kb;
+
+ minibuffer_only = false;
+ undecorated = false;
+ override_redirect = false;
+ window_prompting = 0;
+ count = SPECPDL_INDEX ();
+ dpyinfo = NULL;
+
+ /* Not actually used, but be consistent with X. */
+ ((void) window_prompting);
+
+ parms = Fcopy_alist (parms);
+
+ /* Use this general default value to start with
+ until we know if this frame has a specified name. */
+ Vx_resource_name = Vinvocation_name;
+
+ display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
+ RES_TYPE_NUMBER);
+ if (BASE_EQ (display, Qunbound))
+ display = gui_display_get_arg (dpyinfo, parms, Qdisplay, 0, 0,
+ RES_TYPE_STRING);
+ if (BASE_EQ (display, Qunbound))
+ display = Qnil;
+ dpyinfo = check_android_display_info (display);
+ kb = dpyinfo->terminal->kboard;
+
+ if (!dpyinfo->terminal->name)
+ error ("Terminal is not live, can't create new frames on it");
+
+ name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+ RES_TYPE_STRING);
+ if (!STRINGP (name)
+ && ! BASE_EQ (name, Qunbound)
+ && ! NILP (name))
+ error ("Invalid frame name--not a string or nil");
+
+ if (STRINGP (name))
+ Vx_resource_name = name;
+
+ /* See if parent window is specified. */
+ parent = gui_display_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (BASE_EQ (parent, Qunbound))
+ parent = Qnil;
+ if (! NILP (parent))
+ CHECK_FIXNUM (parent);
+
+ frame = Qnil;
+ tem = gui_display_get_arg (dpyinfo,
+ parms, Qminibuffer, "minibuffer", "Minibuffer",
+ RES_TYPE_SYMBOL);
+ if (EQ (tem, Qnone) || NILP (tem))
+ f = make_frame_without_minibuffer (Qnil, kb, display);
+ else if (EQ (tem, Qonly))
+ {
+ f = make_minibuffer_frame ();
+ minibuffer_only = true;
+ }
+ else if (WINDOWP (tem))
+ f = make_frame_without_minibuffer (tem, kb, display);
+ else
+ f = make_frame (true);
+
+ parent_frame = gui_display_get_arg (dpyinfo,
+ parms,
+ Qparent_frame,
+ NULL,
+ NULL,
+ RES_TYPE_SYMBOL);
+ /* Accept parent-frame iff parent-id was not specified. */
+ if (!NILP (parent)
+ || BASE_EQ (parent_frame, Qunbound)
+ || NILP (parent_frame)
+ || !FRAMEP (parent_frame)
+ || !FRAME_LIVE_P (XFRAME (parent_frame))
+ || !FRAME_ANDROID_P (XFRAME (parent_frame)))
+ parent_frame = Qnil;
+
+ fset_parent_frame (f, parent_frame);
+ store_frame_param (f, Qparent_frame, parent_frame);
+
+ if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+ parms,
+ Qundecorated,
+ NULL,
+ NULL,
+ RES_TYPE_BOOLEAN)))
+ && !(BASE_EQ (tem, Qunbound)))
+ undecorated = true;
+
+ FRAME_UNDECORATED (f) = undecorated;
+ store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil);
+
+ if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+ parms,
+ Qoverride_redirect,
+ NULL,
+ NULL,
+ RES_TYPE_BOOLEAN)))
+ && !(BASE_EQ (tem, Qunbound)))
+ override_redirect = true;
+
+ FRAME_OVERRIDE_REDIRECT (f) = override_redirect;
+ store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil);
+
+ XSETFRAME (frame, f);
+
+ f->terminal = dpyinfo->terminal;
+
+ f->output_method = output_android;
+ f->output_data.android = xzalloc (sizeof *f->output_data.android);
+ FRAME_FONTSET (f) = -1;
+ f->output_data.android->scroll_bar_foreground_pixel = -1;
+ f->output_data.android->scroll_bar_background_pixel = -1;
+ f->output_data.android->white_relief.pixel = -1;
+ f->output_data.android->black_relief.pixel = -1;
+
+ fset_icon_name (f, gui_display_get_arg (dpyinfo,
+ parms,
+ Qicon_name,
+ "iconName",
+ "Title",
+ RES_TYPE_STRING));
+ if (! STRINGP (f->icon_name))
+ fset_icon_name (f, Qnil);
+
+ FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+ /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe. */
+ record_unwind_protect (do_unwind_create_frame, frame);
+
+ /* These colors will be set anyway later, but it's important
+ to get the color reference counts right, so initialize them!
+
+ (Not really on Android, but it's best to be consistent with
+ X.) */
+ {
+ Lisp_Object black;
+
+ /* Function x_decode_color can signal an error. Make
+ sure to initialize color slots so that we won't try
+ to free colors we haven't allocated. */
+ FRAME_FOREGROUND_PIXEL (f) = -1;
+ FRAME_BACKGROUND_PIXEL (f) = -1;
+ f->output_data.android->cursor_pixel = -1;
+ f->output_data.android->cursor_foreground_pixel = -1;
+ f->output_data.android->mouse_pixel = -1;
+
+ black = build_string ("black");
+ FRAME_FOREGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_foreground_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->mouse_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ }
+
+ /* Set the name; the functions to which we pass f expect the name to
+ be set. */
+ if (BASE_EQ (name, Qunbound) || NILP (name))
+ {
+ fset_name (f, build_string ("GNU Emacs"));
+ f->explicit_name = false;
+ }
+ else
+ {
+ fset_name (f, name);
+ f->explicit_name = true;
+ /* Use the frame's title when getting resources for this frame. */
+ specbind (Qx_resource_name, name);
+ }
+
+ register_font_driver (&androidfont_driver, f);
+ register_font_driver (&android_sfntfont_driver, f);
+
+ image_cache_refcount = (FRAME_IMAGE_CACHE (f)
+ ? FRAME_IMAGE_CACHE (f)->refcount
+ : 0);
+
+ gui_default_parameter (f, parms, Qfont_backend, Qnil,
+ "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+ /* Extract the window parameters from the supplied values
+ that are needed to determine window geometry. */
+ android_default_font_parameter (f, parms);
+ if (!FRAME_FONT (f))
+ {
+ delete_frame (frame, Qnoelisp);
+ error ("Invalid frame font");
+ }
+
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+ "internalBorder", "internalBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qinternal_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qinternal_border_width,
+ make_fixnum (0),
+ "internalBorderWidth", "internalBorderWidth",
+ RES_TYPE_NUMBER);
+
+ /* Same for child frames. */
+ if (NILP (Fassq (Qchild_frame_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width,
+ "childFrameBorder", "childFrameBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qchild_frame_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil,
+ "childFrameBorderWidth", "childFrameBorderWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ gui_default_parameter (f, parms, Qvertical_scroll_bars,
+ Qleft,
+ "verticalScrollBars", "ScrollBars",
+ RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
+ "horizontalScrollBars", "ScrollBars",
+ RES_TYPE_SYMBOL);
+
+ /* Also do the stuff which must be set before the window exists. */
+ gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+ "foreground", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+ "background", "Background", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+ "pointerColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+ "borderColor", "BorderColor", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qscreen_gamma, Qnil,
+ "screenGamma", "ScreenGamma", RES_TYPE_FLOAT);
+ gui_default_parameter (f, parms, Qline_spacing, Qnil,
+ "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qleft_fringe, Qnil,
+ "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_fringe, Qnil,
+ "rightFringe", "RightFringe", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+#if 0
+ android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground,
+ "scrollBarForeground",
+ "ScrollBarForeground", true);
+ android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background,
+ "scrollBarBackground",
+ "ScrollBarBackground", false);
+#endif
+
+ /* Init faces before gui_default_parameter is called for the
+ scroll-bar-width parameter because otherwise we end up in
+ init_iterator with a null face cache, which should not
+ happen. */
+
+ init_frame_faces (f);
+
+ tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (FIXNUMP (tem))
+ store_frame_param (f, Qmin_width, tem);
+ tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (FIXNUMP (tem))
+ store_frame_param (f, Qmin_height, tem);
+
+ adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+ FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true,
+ Qx_create_frame_1);
+
+ /* Set the menu-bar-lines and tool-bar-lines parameters. We don't
+ look up the X resources controlling the menu-bar and tool-bar
+ here; they are processed specially at startup, and reflected in
+ the values of the mode variables. */
+
+ gui_default_parameter (f, parms, Qmenu_bar_lines,
+ NILP (Vmenu_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qtab_bar_lines,
+ NILP (Vtab_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qtool_bar_lines,
+ NILP (Vtool_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ gui_default_parameter (f, parms, Qbuffer_predicate, Qnil,
+ "bufferPredicate", "BufferPredicate",
+ RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qtitle, Qnil,
+ "title", "Title", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qwait_for_wm, Qt,
+ "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qtool_bar_position,
+ FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+ "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+ RES_TYPE_BOOLEAN);
+
+ /* Compute the size of the X window. */
+ window_prompting = gui_figure_window_size (f, parms, true, true);
+
+ tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
+ RES_TYPE_BOOLEAN);
+ f->no_split = minibuffer_only || EQ (tem, Qt);
+
+ android_icon_verify (f, parms);
+ android_create_frame_window (f);
+ android_icon (f, parms);
+ android_make_gc (f);
+
+ /* Now consider the frame official. */
+ f->terminal->reference_count++;
+ Vframe_list = Fcons (frame, Vframe_list);
+
+ /* We need to do this after creating the window, so that the
+ icon-creation functions can say whose icon they're
+ describing. */
+ gui_default_parameter (f, parms, Qicon_type, Qt,
+ "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN);
+
+ gui_default_parameter (f, parms, Qauto_raise, Qnil,
+ "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qauto_lower, Qnil,
+ "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qcursor_type, Qbox,
+ "cursorType", "CursorType", RES_TYPE_SYMBOL);
+ /* Scroll bars are not supported on Android, as they are near
+ useless. */
+#if 0
+ gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+ "scrollBarWidth", "ScrollBarWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+ "scrollBarHeight", "ScrollBarHeight",
+ RES_TYPE_NUMBER);
+#endif
+ gui_default_parameter (f, parms, Qalpha, Qnil,
+ "alpha", "Alpha", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qalpha_background, Qnil,
+ "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER);
+
+ if (!NILP (parent_frame))
+ {
+ struct frame *p = XFRAME (parent_frame);
+
+ block_input ();
+ android_reparent_window (FRAME_ANDROID_WINDOW (f),
+ FRAME_ANDROID_WINDOW (p),
+ f->left_pos, f->top_pos);
+ unblock_input ();
+ }
+
+ gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ /* Consider frame official, now. */
+ f->can_set_window_size = true;
+
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 0, true, Qx_create_frame_2);
+
+ /* Process fullscreen parameter here in the hope that normalizing a
+ fullheight/fullwidth frame will produce the size set by the last
+ adjust_frame_size call. Note that Android only supports the
+ `maximized' state. */
+ gui_default_parameter (f, parms, Qfullscreen, Qmaximized,
+ "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+
+ /* When called from `x-create-frame-with-faces' visibility is
+ always explicitly nil. */
+ Lisp_Object visibility
+ = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
+ RES_TYPE_SYMBOL);
+ Lisp_Object height
+ = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, RES_TYPE_NUMBER);
+ Lisp_Object width
+ = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER);
+
+ if (EQ (visibility, Qicon))
+ {
+ f->was_invisible = true;
+ android_iconify_frame (f);
+ }
+ else
+ {
+ if (BASE_EQ (visibility, Qunbound))
+ visibility = Qt;
+
+ if (!NILP (visibility))
+ android_make_frame_visible (f);
+ else
+ f->was_invisible = true;
+ }
+
+ /* Leave f->was_invisible true only if height or width were
+ specified too. This takes effect only when we are not called
+ from `x-create-frame-with-faces' (see above comment). */
+ f->was_invisible
+ = (f->was_invisible
+ && (!BASE_EQ (height, Qunbound) || !BASE_EQ (width, Qunbound)));
+
+ store_frame_param (f, Qvisibility, visibility);
+
+ /* Set whether or not frame synchronization is enabled. */
+ gui_default_parameter (f, parms, Quse_frame_synchronization, Qt,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ /* Works iff frame has been already mapped. */
+ gui_default_parameter (f, parms, Qskip_taskbar, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+ /* The `z-group' parameter works only for visible frames. */
+ gui_default_parameter (f, parms, Qz_group, Qnil,
+ NULL, NULL, RES_TYPE_SYMBOL);
+
+ /* Initialize `default-minibuffer-frame' in case this is the first
+ frame on this terminal. */
+ if (FRAME_HAS_MINIBUF_P (f)
+ && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+ || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+ kset_default_minibuffer_frame (kb, frame);
+
+ /* All remaining specified parameters, which have not been "used" by
+ gui_display_get_arg and friends, now go in the misc. alist of the
+ frame. */
+ for (tem = parms; CONSP (tem); tem = XCDR (tem))
+ if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+ fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+ /* Make sure windows on this frame appear in calls to next-window
+ and similar functions. */
+ Vwindow_list = Qnil;
+
+ return unbind_to (count, frame);
+#endif
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p,
+ 1, 2, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Emacs_Color foo;
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ CHECK_STRING (color);
+
+ if (android_defined_color (f, SSDATA (color), &foo, false, false))
+ return Qt;
+ else
+ return Qnil;
+#endif
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2,
+ 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Emacs_Color foo;
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ CHECK_STRING (color);
+
+ if (android_defined_color (f, SSDATA (color), &foo, false, false))
+ return list3i (foo.red, foo.green, foo.blue);
+ else
+ return Qnil;
+#endif
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p,
+ Sxw_display_color_p, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ return Qt;
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p,
+ Sx_display_grayscale_p, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width,
+ Sx_display_pixel_width, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_screen_width ());
+#endif
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
+ Sx_display_pixel_height, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_screen_height ());
+#endif
+}
+
+DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes,
+ 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = check_android_display_info (terminal);
+
+ return make_fixnum (dpyinfo->n_planes);
+}
+
+DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells,
+ 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ struct android_display_info *dpyinfo;
+ int nr_planes;
+
+ dpyinfo = check_android_display_info (terminal);
+ nr_planes = dpyinfo->n_planes;
+
+ /* Truncate nr_planes to 24 to avoid integer overflow. */
+
+ if (nr_planes > 24)
+ nr_planes = 24;
+
+ return make_fixnum (1 << nr_planes);
+}
+
+DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ check_android_display_info (terminal);
+ return Vandroid_build_manufacturer;
+#endif
+}
+
+DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ check_android_display_info (terminal);
+ return list3i (android_get_current_api_level (), 0, 0);
+#endif
+}
+
+DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+ return make_fixnum (1);
+}
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_mm_width ());
+#endif
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_mm_height ());
+#endif
+}
+
+DEFUN ("x-display-backing-store", Fx_display_backing_store,
+ Sx_display_backing_store, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+
+ /* The Java part is implemented in a way that it always does the
+ equivalent of backing store. */
+ return Qalways;
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+ Sx_display_visual_class, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+
+ return Qtrue_color;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_make_monitor_attribute_list (struct MonitorInfo *monitors,
+ int n_monitors,
+ int primary_monitor)
+{
+ Lisp_Object monitor_frames;
+ Lisp_Object frame, rest;
+ struct frame *f;
+
+ monitor_frames = make_nil_vector (n_monitors);
+
+ FOR_EACH_FRAME (rest, frame)
+ {
+ f = XFRAME (frame);
+
+ /* Associate all frames with the primary monitor. */
+
+ if (FRAME_WINDOW_P (f)
+ && !FRAME_TOOLTIP_P (f))
+ ASET (monitor_frames, primary_monitor,
+ Fcons (frame, AREF (monitor_frames,
+ primary_monitor)));
+ }
+
+ return make_monitor_attribute_list (monitors, n_monitors,
+ primary_monitor,
+ monitor_frames, NULL);
+}
+
+#endif
+
+DEFUN ("android-display-monitor-attributes-list",
+ Fandroid_display_monitor_attributes_list,
+ Sandroid_display_monitor_attributes_list,
+ 0, 1, 0,
+ doc: /* Return a list of physical monitor attributes on the X display TERMINAL.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Internal use only, use `display-monitor-attributes-list' instead. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct MonitorInfo monitor;
+
+ memset (&monitor, 0, sizeof monitor);
+ monitor.geom.width = android_get_screen_width ();
+ monitor.geom.height = android_get_screen_height ();
+ monitor.mm_width = android_get_mm_width ();
+ monitor.mm_height = android_get_mm_height ();
+ monitor.work = monitor.geom;
+ monitor.name = (char *) "Android device monitor";
+
+ return android_make_monitor_attribute_list (&monitor, 1, 0);
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+ struct frame *f = decode_live_frame (frame);
+ android_window rootw;
+ unsigned int native_width, native_height, x_border_width = 0;
+ int x_native = 0, y_native = 0, xptr = 0, yptr = 0;
+ int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0;
+ int outer_left, outer_top, outer_right, outer_bottom;
+ int native_left, native_top, native_right, native_bottom;
+ int inner_left, inner_top, inner_right, inner_bottom;
+ int internal_border_width;
+ bool menu_bar_external = false, tool_bar_external = false;
+ int menu_bar_height = 0, menu_bar_width = 0;
+ int tab_bar_height = 0, tab_bar_width = 0;
+ int tool_bar_height = 0, tool_bar_width = 0;
+
+ if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f)
+ || !FRAME_ANDROID_WINDOW (f))
+ return Qnil;
+
+ block_input ();
+ android_get_geometry (FRAME_ANDROID_WINDOW (f),
+ &rootw, &x_native, &y_native,
+ &native_width, &native_height, &x_border_width);
+ unblock_input ();
+
+ if (FRAME_PARENT_FRAME (f))
+ {
+ Lisp_Object parent, edges;
+
+ XSETFRAME (parent, FRAME_PARENT_FRAME (f));
+ edges = Fandroid_frame_edges (parent, Qnative_edges);
+ if (!NILP (edges))
+ {
+ x_native += XFIXNUM (Fnth (make_fixnum (0), edges));
+ y_native += XFIXNUM (Fnth (make_fixnum (1), edges));
+ }
+
+ outer_left = x_native;
+ outer_top = y_native;
+ outer_right = outer_left + native_width + 2 * x_border_width;
+ outer_bottom = outer_top + native_height + 2 * x_border_width;
+
+ native_left = x_native + x_border_width;
+ native_top = y_native + x_border_width;
+ native_right = native_left + native_width;
+ native_bottom = native_top + native_height;
+ }
+ else
+ {
+ outer_left = xptr;
+ outer_top = yptr;
+ outer_right = outer_left + left_off + native_width + right_off;
+ outer_bottom = outer_top + top_off + native_height + bottom_off;
+
+ native_left = outer_left + left_off;
+ native_top = outer_top + top_off;
+ native_right = native_left + native_width;
+ native_bottom = native_top + native_height;
+ }
+
+ internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+ inner_left = native_left + internal_border_width;
+ inner_top = native_top + internal_border_width;
+ inner_right = native_right - internal_border_width;
+ inner_bottom = native_bottom - internal_border_width;
+
+ menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+ inner_top += menu_bar_height;
+ menu_bar_width = menu_bar_height ? native_width : 0;
+
+ tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+ tab_bar_width = (tab_bar_height
+ ? native_width - 2 * internal_border_width
+ : 0);
+ inner_top += tab_bar_height;
+
+ tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
+ tool_bar_width = (tool_bar_height
+ ? native_width - 2 * internal_border_width
+ : 0);
+ inner_top += tool_bar_height;
+
+ /* Construct list. */
+ if (EQ (attribute, Qouter_edges))
+ return list4i (outer_left, outer_top, outer_right, outer_bottom);
+ else if (EQ (attribute, Qnative_edges))
+ return list4i (native_left, native_top, native_right, native_bottom);
+ else if (EQ (attribute, Qinner_edges))
+ return list4i (inner_left, inner_top, inner_right, inner_bottom);
+ else
+ return
+ list (Fcons (Qouter_position,
+ Fcons (make_fixnum (outer_left),
+ make_fixnum (outer_top))),
+ Fcons (Qouter_size,
+ Fcons (make_fixnum (outer_right - outer_left),
+ make_fixnum (outer_bottom - outer_top))),
+ /* Approximate. */
+ Fcons (Qexternal_border_size,
+ Fcons (make_fixnum (right_off),
+ make_fixnum (bottom_off))),
+ Fcons (Qouter_border_width, make_fixnum (x_border_width)),
+ /* Approximate. */
+ Fcons (Qtitle_bar_size,
+ Fcons (make_fixnum (0),
+ make_fixnum (top_off - bottom_off))),
+ Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil),
+ Fcons (Qmenu_bar_size,
+ Fcons (make_fixnum (menu_bar_width),
+ make_fixnum (menu_bar_height))),
+ Fcons (Qtab_bar_size,
+ Fcons (make_fixnum (tab_bar_width),
+ make_fixnum (tab_bar_height))),
+ Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
+ Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+ Fcons (Qtool_bar_size,
+ Fcons (make_fixnum (tool_bar_width),
+ make_fixnum (tool_bar_height))),
+ Fcons (Qinternal_border_width,
+ make_fixnum (internal_border_width)));
+}
+
+#endif
+
+DEFUN ("android-frame-geometry", Fandroid_frame_geometry,
+ Sandroid_frame_geometry,
+ 0, 1, 0,
+ doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one. The return
+value is an association list of the attributes listed below. All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+ relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME. The
+ outer size includes the title bar and the external borders as well as
+ any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+ FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+ FRAME as supplied by the window manager. If both of them are zero,
+ FRAME has no title bar. If only the width is zero, Emacs was not
+ able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+ included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+ FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+ included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+ be one of `left', `top', `right' or `bottom'. If this is nil, FRAME
+ has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+ FRAME.
+
+`internal-border-width' is the width of the internal border of
+ FRAME. */)
+ (Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return frame_geometry (frame, Qnil);
+#endif
+}
+
+DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 2, 0,
+ doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one. The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME. The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME. If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME. The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME. If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME. These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME. */)
+ (Lisp_Object frame, Lisp_Object type)
+{
+#ifndef ANDROID_STUBIFY
+ return frame_geometry (frame, ((EQ (type, Qouter_edges)
+ || EQ (type, Qinner_edges))
+ ? type
+ : Qnative_edges));
+#else
+ return Qnil;
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_frame_list_z_order (struct android_display_info *dpyinfo,
+ android_window window)
+{
+ android_window root, parent, *children;
+ unsigned int nchildren;
+ unsigned long i;
+ Lisp_Object frames;
+
+ frames = Qnil;
+
+ if (android_query_tree (window, &root, &parent,
+ &children, &nchildren))
+ {
+ for (i = 0; i < nchildren; i++)
+ {
+ Lisp_Object frame, tail;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ struct frame *cf = XFRAME (frame);
+
+ if (FRAME_ANDROID_P (cf)
+ && (FRAME_ANDROID_WINDOW (cf) == children[i]))
+ frames = Fcons (frame, frames);
+ }
+ }
+
+ if (children)
+ xfree (children);
+ }
+
+ return frames;
+}
+
+#endif
+
+DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order,
+ Sandroid_frame_list_z_order, 0, 1, 0,
+ doc: /* Return list of Emacs' frames, in Z (stacking) order.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be either a frame or a display name (a string). If
+omitted or nil, that stands for the selected frame's display. Return
+nil if TERMINAL contains no Emacs frame.
+
+As a special case, if TERMINAL is non-nil and specifies a live frame,
+return the child frames of that frame in Z (stacking) order.
+
+Frames are listed from topmost (first) to bottommost (last).
+
+On Android, the order of the frames returned is undefined unless
+TERMINAL is a frame. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct android_display_info *dpyinfo;
+ android_window window;
+
+ dpyinfo = check_android_display_info (terminal);
+
+ if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
+ window = FRAME_ANDROID_WINDOW (XFRAME (terminal));
+ else
+ window = dpyinfo->root_window;
+
+ return android_frame_list_z_order (dpyinfo, window);
+#endif
+}
+
+DEFUN ("android-frame-restack", Fandroid_frame_restack,
+ Sandroid_frame_restack, 2, 3, 0,
+ doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1. If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+This may be thought of as an atomic action performed in two steps: The
+first step removes FRAME1's window-step window from the display. The
+second step reinserts FRAME1's window below (above if ABOVE is true)
+that of FRAME2. Hence the position of FRAME2 in its display's Z
+\(stacking) order relative to all other frames excluding FRAME1 remains
+unaltered.
+
+The Android system refuses to restack windows, so this does not
+work. */)
+ (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ /* This is not supported on Android because of limitations in the
+ platform that prevent ViewGroups from restacking
+ SurfaceViews. */
+ return Qnil;
+#endif
+}
+
+DEFUN ("android-mouse-absolute-pixel-position",
+ Fandroid_mouse_absolute_pixel_position,
+ Sandroid_mouse_absolute_pixel_position, 0, 0, 0,
+ doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display. This does not work on Android. */)
+ (void)
+{
+ /* This cannot be implemented on Android. */
+ return Qnil;
+}
+
+DEFUN ("android-set-mouse-absolute-pixel-position",
+ Fandroid_set_mouse_absolute_pixel_position,
+ Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0,
+ doc: /* Move mouse pointer to a pixel position at (X, Y). The
+coordinates X and Y are interpreted to start from the top-left corner
+of the screen. This does not work on Android. */)
+ (Lisp_Object x, Lisp_Object y)
+{
+ /* This cannot be implemented on Android. */
+ return Qnil;
+}
+
+DEFUN ("android-get-connection", Fandroid_get_connection,
+ Sandroid_get_connection, 0, 0, 0,
+ doc: /* Get the connection to the display server.
+Return the terminal if it exists, else nil.
+
+Emacs cannot open a connection to the display server itself under
+Android, so there is no equivalent of `x-open-connection'. */)
+ (void)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Lisp_Object terminal;
+
+ terminal = Qnil;
+
+ if (x_display_list)
+ XSETTERMINAL (terminal, x_display_list->terminal);
+
+ return terminal;
+#endif
+}
+
+DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+ Lisp_Object result;
+
+ result = Qnil;
+
+ if (x_display_list)
+ result = Fcons (XCAR (x_display_list->name_list_element),
+ result);
+
+ return result;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static void
+unwind_create_tip_frame (Lisp_Object frame)
+{
+ Lisp_Object deleted;
+
+ deleted = unwind_create_frame (frame);
+ if (EQ (deleted, Qt))
+ {
+ tip_window = ANDROID_NONE;
+ tip_frame = Qnil;
+ }
+}
+
+static Lisp_Object
+android_create_tip_frame (struct android_display_info *dpyinfo,
+ Lisp_Object parms)
+{
+ struct frame *f;
+ Lisp_Object frame;
+ Lisp_Object name;
+ specpdl_ref count = SPECPDL_INDEX ();
+ bool face_change_before = face_change;
+
+ if (!dpyinfo->terminal->name)
+ error ("Terminal is not live, can't create new frames on it");
+
+ parms = Fcopy_alist (parms);
+
+ /* Get the name of the frame to use for resource lookup. */
+ name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+ RES_TYPE_STRING);
+ if (!STRINGP (name)
+ && !BASE_EQ (name, Qunbound)
+ && !NILP (name))
+ error ("Invalid frame name--not a string or nil");
+
+ frame = Qnil;
+ f = make_frame (false);
+ f->wants_modeline = false;
+ XSETFRAME (frame, f);
+ record_unwind_protect (unwind_create_tip_frame, frame);
+
+ f->terminal = dpyinfo->terminal;
+
+ /* By setting the output method, we're essentially saying that
+ the frame is live, as per FRAME_LIVE_P. If we get a signal
+ from this point on, x_destroy_window might screw up reference
+ counts etc. */
+ f->output_method = output_android;
+ f->output_data.android = xzalloc (sizeof *f->output_data.android);
+ FRAME_FONTSET (f) = -1;
+ f->output_data.android->white_relief.pixel = -1;
+ f->output_data.android->black_relief.pixel = -1;
+
+ f->tooltip = true;
+ fset_icon_name (f, Qnil);
+ FRAME_DISPLAY_INFO (f) = dpyinfo;
+ f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+ /* These colors will be set anyway later, but it's important
+ to get the color reference counts right, so initialize them! */
+ {
+ Lisp_Object black;
+
+ /* Function android_decode_color can signal an error. Make sure
+ to initialize color slots so that we won't try to free colors
+ we haven't allocated. */
+ FRAME_FOREGROUND_PIXEL (f) = -1;
+ FRAME_BACKGROUND_PIXEL (f) = -1;
+ f->output_data.android->cursor_pixel = -1;
+ f->output_data.android->cursor_foreground_pixel = -1;
+ f->output_data.android->mouse_pixel = -1;
+
+ black = build_string ("black");
+ FRAME_FOREGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_foreground_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->mouse_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ }
+
+ /* Set the name; the functions to which we pass f expect the name to
+ be set. */
+ if (BASE_EQ (name, Qunbound) || NILP (name))
+ f->explicit_name = false;
+ else
+ {
+ fset_name (f, name);
+ f->explicit_name = true;
+ /* use the frame's title when getting resources for this frame. */
+ specbind (Qx_resource_name, name);
+ }
+
+ register_font_driver (&androidfont_driver, f);
+ register_font_driver (&android_sfntfont_driver, f);
+
+ image_cache_refcount
+ = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+#ifdef GLYPH_DEBUG
+ dpyinfo_refcount = dpyinfo->reference_count;
+#endif /* GLYPH_DEBUG */
+
+ gui_default_parameter (f, parms, Qfont_backend, Qnil,
+ "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+ /* Extract the window parameters from the supplied values that are
+ needed to determine window geometry. */
+ android_default_font_parameter (f, parms);
+
+ gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+ "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+ /* This defaults to 1 in order to match xterm. We recognize either
+ internalBorderWidth or internalBorder (which is what xterm calls
+ it). */
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+ "internalBorder", "internalBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qinternal_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
+ "internalBorderWidth", "internalBorderWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ /* Also do the stuff which must be set before the window exists. */
+ gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+ "foreground", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+ "background", "Background", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+ "pointerColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
+ "cursorColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+ "borderColor", "BorderColor", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ {
+ struct android_set_window_attributes attrs;
+ unsigned long mask;
+
+ block_input ();
+ mask = ANDROID_CW_OVERRIDE_REDIRECT | ANDROID_CW_BACK_PIXEL;
+
+ attrs.override_redirect = true;
+ attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+ tip_window
+ = FRAME_ANDROID_WINDOW (f)
+ = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+ /* x, y, width, height, value-mask,
+ attrs. */
+ 0, 0, 1, 1, mask, &attrs);
+ unblock_input ();
+ }
+
+ /* Init faces before gui_default_parameter is called for the
+ scroll-bar-width parameter because otherwise we end up in
+ init_iterator with a null face cache, which should not happen. */
+ init_frame_faces (f);
+
+ gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+ "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+ RES_TYPE_BOOLEAN);
+
+ gui_figure_window_size (f, parms, false, false);
+
+ f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+ android_make_gc (f);
+
+ gui_default_parameter (f, parms, Qauto_raise, Qnil,
+ "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qauto_lower, Qnil,
+ "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qcursor_type, Qbox,
+ "cursorType", "CursorType", RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qalpha, Qnil,
+ "alpha", "Alpha", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qalpha_background, Qnil,
+ "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER);
+
+ /* Add `tooltip' frame parameter's default value. */
+ if (NILP (Fframe_parameter (frame, Qtooltip)))
+ {
+ AUTO_FRAME_ARG (arg, Qtooltip, Qt);
+ Fmodify_frame_parameters (frame, arg);
+ }
+
+ /* FIXME - can this be done in a similar way to normal frames?
+ https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */
+
+ /* Set the `display-type' frame parameter before setting up faces. */
+ {
+ Lisp_Object disptype;
+
+ disptype = Qcolor;
+
+ if (NILP (Fframe_parameter (frame, Qdisplay_type)))
+ {
+ AUTO_FRAME_ARG (arg, Qdisplay_type, disptype);
+ Fmodify_frame_parameters (frame, arg);
+ }
+ }
+
+ /* Set up faces after all frame parameters are known. This call
+ also merges in face attributes specified for new frames. */
+ {
+ Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
+
+ call2 (Qface_set_after_frame_default, frame, Qnil);
+
+ if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
+ {
+ AUTO_FRAME_ARG (arg, Qbackground_color, bg);
+ Fmodify_frame_parameters (frame, arg);
+ }
+ }
+
+ f->no_split = true;
+
+ /* Now that the frame will be official, it counts as a reference to
+ its display and terminal. */
+ f->terminal->reference_count++;
+
+ /* It is now ok to make the frame official even if we get an error
+ below. And the frame needs to be on Vframe_list or making it
+ visible won't work. */
+ Vframe_list = Fcons (frame, Vframe_list);
+ f->can_set_window_size = true;
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 0, true, Qtip_frame);
+
+ /* Setting attributes of faces of the tooltip frame from resources
+ and similar will set face_change, which leads to the clearing of
+ all current matrices. Since this isn't necessary here, avoid it
+ by resetting face_change to the value it had before we created
+ the tip frame. */
+ face_change = face_change_before;
+
+ /* Discard the unwind_protect. */
+ return unbind_to (count, frame);
+}
+
+static Lisp_Object
+android_hide_tip (bool delete)
+{
+ if (!NILP (tip_timer))
+ {
+ call1 (Qcancel_timer, tip_timer);
+ tip_timer = Qnil;
+ }
+
+ if (NILP (tip_frame)
+ || (!delete
+ && !NILP (tip_frame)
+ && FRAME_LIVE_P (XFRAME (tip_frame))
+ && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+ return Qnil;
+ else
+ {
+ Lisp_Object was_open = Qnil;
+
+ specpdl_ref count = SPECPDL_INDEX ();
+ specbind (Qinhibit_redisplay, Qt);
+ specbind (Qinhibit_quit, Qt);
+
+ if (!NILP (tip_frame))
+ {
+ struct frame *f = XFRAME (tip_frame);
+
+ if (FRAME_LIVE_P (f))
+ {
+ if (delete)
+ {
+ delete_frame (tip_frame, Qnil);
+ tip_frame = Qnil;
+ }
+ else
+ android_make_frame_invisible (XFRAME (tip_frame));
+
+ was_open = Qt;
+ }
+ else
+ tip_frame = Qnil;
+ }
+ else
+ tip_frame = Qnil;
+
+ return unbind_to (count, was_open);
+ }
+}
+
+static void
+compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx,
+ Lisp_Object dy, int width, int height, int *root_x,
+ int *root_y)
+{
+ Lisp_Object left, top, right, bottom;
+ int min_x, min_y, max_x, max_y = -1;
+ android_window window;
+ struct frame *mouse_frame;
+
+ /* Initialize these values in case there is no mouse frame. */
+ *root_x = 0;
+ *root_y = 0;
+
+ /* User-specified position? */
+ left = CDR (Fassq (Qleft, parms));
+ top = CDR (Fassq (Qtop, parms));
+ right = CDR (Fassq (Qright, parms));
+ bottom = CDR (Fassq (Qbottom, parms));
+
+ /* Move the tooltip window where the mouse pointer was last seen.
+ Resize and show it. */
+ if ((!FIXNUMP (left) && !FIXNUMP (right))
+ || (!FIXNUMP (top) && !FIXNUMP (bottom)))
+ {
+ if (x_display_list->last_mouse_motion_frame)
+ {
+ *root_x = x_display_list->last_mouse_motion_x;
+ *root_y = x_display_list->last_mouse_motion_y;
+ mouse_frame = x_display_list->last_mouse_motion_frame;
+ window = FRAME_ANDROID_WINDOW (mouse_frame);
+
+ /* Translate the coordinates to the screen. */
+ android_translate_coordinates (window, *root_x, *root_y,
+ root_x, root_y);
+ }
+ }
+
+ min_x = 0;
+ min_y = 0;
+ max_x = android_get_screen_width ();
+ max_y = android_get_screen_height ();
+
+ if (FIXNUMP (top))
+ *root_y = XFIXNUM (top);
+ else if (FIXNUMP (bottom))
+ *root_y = XFIXNUM (bottom) - height;
+ else if (*root_y + XFIXNUM (dy) <= min_y)
+ *root_y = min_y; /* Can happen for negative dy */
+ else if (*root_y + XFIXNUM (dy) + height <= max_y)
+ /* It fits below the pointer */
+ *root_y += XFIXNUM (dy);
+ else if (height + XFIXNUM (dy) + min_y <= *root_y)
+ /* It fits above the pointer. */
+ *root_y -= height + XFIXNUM (dy);
+ else
+ /* Put it on the top. */
+ *root_y = min_y;
+
+ if (FIXNUMP (left))
+ *root_x = XFIXNUM (left);
+ else if (FIXNUMP (right))
+ *root_x = XFIXNUM (right) - width;
+ else if (*root_x + XFIXNUM (dx) <= min_x)
+ *root_x = 0; /* Can happen for negative dx */
+ else if (*root_x + XFIXNUM (dx) + width <= max_x)
+ /* It fits to the right of the pointer. */
+ *root_x += XFIXNUM (dx);
+ else if (width + XFIXNUM (dx) + min_x <= *root_x)
+ /* It fits to the left of the pointer. */
+ *root_x -= width + XFIXNUM (dx);
+ else
+ /* Put it left justified on the screen -- it ought to fit that way. */
+ *root_x = min_x;
+}
+
+#endif
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
+ Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct frame *f, *tip_f;
+ struct window *w;
+ int root_x, root_y;
+ struct buffer *old_buffer;
+ struct text_pos pos;
+ int width, height;
+ int old_windows_or_buffers_changed = windows_or_buffers_changed;
+ specpdl_ref count = SPECPDL_INDEX ();
+ Lisp_Object window, size, tip_buf;
+ bool displayed;
+#ifdef ENABLE_CHECKING
+ struct glyph_row *row, *end;
+#endif
+ AUTO_STRING (tip, " *tip*");
+
+ specbind (Qinhibit_redisplay, Qt);
+
+ CHECK_STRING (string);
+ if (SCHARS (string) == 0)
+ string = make_unibyte_string (" ", 1);
+
+ if (NILP (frame))
+ frame = selected_frame;
+ f = decode_window_system_frame (frame);
+
+ if (NILP (timeout))
+ timeout = Vx_show_tooltip_timeout;
+ CHECK_FIXNAT (timeout);
+
+ if (NILP (dx))
+ dx = make_fixnum (5);
+ else
+ CHECK_FIXNUM (dx);
+
+ if (NILP (dy))
+ dy = make_fixnum (-10);
+ else
+ CHECK_FIXNUM (dy);
+
+ tip_dx = dx;
+ tip_dy = dy;
+
+ if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+ && !NILP (Fequal_including_properties (tip_last_string,
+ string))
+ && !NILP (Fequal (tip_last_parms, parms)))
+ {
+ /* Only DX and DY have changed. */
+ tip_f = XFRAME (tip_frame);
+ if (!NILP (tip_timer))
+ {
+ call1 (Qcancel_timer, tip_timer);
+ tip_timer = Qnil;
+ }
+
+ block_input ();
+ compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+ FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+ android_move_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y);
+ unblock_input ();
+
+ goto start_timer;
+ }
+ else
+ android_hide_tip (true);
+ }
+ else
+ android_hide_tip (true);
+
+ tip_last_frame = frame;
+ tip_last_string = string;
+ tip_last_parms = parms;
+
+ if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ /* Add default values to frame parameters. */
+ if (NILP (Fassq (Qname, parms)))
+ parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)),
+ parms);
+ if (NILP (Fassq (Qborder_width, parms)))
+ parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+ if (NILP (Fassq (Qborder_color, parms)))
+ parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+ parms);
+ if (NILP (Fassq (Qbackground_color, parms)))
+ parms = Fcons (Fcons (Qbackground_color,
+ build_string ("lightyellow")),
+ parms);
+
+ /* Create a frame for the tooltip, and record it in the global
+ variable tip_frame. */
+ if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f),
+ parms)))
+ /* Creating the tip frame failed. */
+ return unbind_to (count, Qnil);
+ }
+
+ tip_f = XFRAME (tip_frame);
+ window = FRAME_ROOT_WINDOW (tip_f);
+ tip_buf = Fget_buffer_create (tip, Qnil);
+ /* We will mark the tip window a "pseudo-window" below, and such
+ windows cannot have display margins. */
+ bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ set_window_buffer (window, tip_buf, false, false);
+ w = XWINDOW (window);
+ w->pseudo_window_p = true;
+ /* Try to avoid that `other-window' select us (Bug#47207). */
+ Fset_window_parameter (window, Qno_other_window, Qt);
+
+ /* Set up the frame's root window. Note: The following code does not
+ try to size the window or its frame correctly. Its only purpose is
+ to make the subsequent text size calculations work. The right
+ sizes should get installed when the toolkit gets back to us. */
+ w->left_col = 0;
+ w->top_line = 0;
+ w->pixel_left = 0;
+ w->pixel_top = 0;
+
+ if (CONSP (Vx_max_tooltip_size)
+ && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+ && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+ {
+ w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+ w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+ }
+ else
+ {
+ w->total_cols = 80;
+ w->total_lines = 40;
+ }
+
+ w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+ w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+ FRAME_TOTAL_COLS (tip_f) = w->total_cols;
+ adjust_frame_glyphs (tip_f);
+
+ /* Insert STRING into root window's buffer and fit the frame to the
+ buffer. */
+ specpdl_ref count_1 = SPECPDL_INDEX ();
+ old_buffer = current_buffer;
+ set_buffer_internal_1 (XBUFFER (w->contents));
+ bset_truncate_lines (current_buffer, Qnil);
+ specbind (Qinhibit_read_only, Qt);
+ specbind (Qinhibit_modification_hooks, Qt);
+ specbind (Qinhibit_point_motion_hooks, Qt);
+ Ferase_buffer ();
+ Finsert (1, &string);
+ clear_glyph_matrix (w->desired_matrix);
+ clear_glyph_matrix (w->current_matrix);
+ SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+ displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+
+ if (!displayed && NILP (Vx_max_tooltip_size))
+ {
+#ifdef ENABLE_CHECKING
+ row = w->desired_matrix->rows;
+ end = w->desired_matrix->rows + w->desired_matrix->nrows;
+
+ while (row < end)
+ {
+ if (!row->displays_text_p
+ || row->ends_at_zv_p)
+ break;
+ ++row;
+ }
+
+ eassert (row < end && row->ends_at_zv_p);
+#endif
+ }
+
+ /* Calculate size of tooltip window. */
+ size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+ make_fixnum (w->pixel_height), Qnil,
+ Qnil);
+ /* Add the frame's internal border to calculated size. */
+ width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+ height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+
+ /* Calculate position of tooltip frame. */
+ compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+
+ /* Show tooltip frame. */
+ block_input ();
+ android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y, width,
+ height);
+ android_map_raised (FRAME_ANDROID_WINDOW (tip_f));
+ unblock_input ();
+
+ /* Garbage the tip frame too. */
+ SET_FRAME_GARBAGED (tip_f);
+
+ w->must_be_updated_p = true;
+ update_single_window (w);
+ flush_frame (tip_f);
+ set_buffer_internal_1 (old_buffer);
+ unbind_to (count_1, Qnil);
+ windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+ /* MapNotify events are not sent on Android, so make the frame
+ visible. */
+
+ SET_FRAME_VISIBLE (tip_f, true);
+
+ start_timer:
+ /* Let the tip disappear after timeout seconds. */
+ tip_timer = call3 (Qrun_at_time, timeout, Qnil,
+ Qx_hide_tip);
+
+ return unbind_to (count, Qnil);
+#endif
+}
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return android_hide_tip (true);
+#endif
+}
+
+DEFUN ("android-detect-mouse", Fandroid_detect_mouse,
+ Sandroid_detect_mouse, 0, 0, 0,
+ doc: /* Figure out whether or not there is a mouse.
+Return non-nil if a mouse is connected to this computer, and nil if
+there is no mouse. */)
+ (void)
+{
+#ifndef ANDROID_STUBIFY
+ /* If no display connection is present, just return nil. */
+
+ if (!android_init_gui)
+ return Qnil;
+
+ return android_detect_mouse () ? Qt : Qnil;
+#else
+ return Qnil;
+#endif
+}
+
+DEFUN ("android-toggle-on-screen-keyboard",
+ Fandroid_toggle_on_screen_keyboard,
+ Sandroid_toggle_on_screen_keyboard, 2, 2, 0,
+ doc: /* Display or hide the on-screen keyboard.
+If HIDE is non-nil, hide the on screen keyboard if it is currently
+being displayed. Else, request that the system display it on behalf
+of FRAME. This request may be rejected if FRAME does not have the
+input focus. */)
+ (Lisp_Object frame, Lisp_Object hide)
+{
+#ifndef ANDROID_STUBIFY
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ block_input ();
+ android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f),
+ NILP (hide));
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+static void
+android_set_background_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x;
+ unsigned long bg;
+
+ x = f->output_data.android;
+ bg = android_decode_color (f, arg, WHITE_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f) = bg;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_background (x->normal_gc, bg);
+ android_set_foreground (x->reverse_gc, bg);
+ android_set_window_background (FRAME_ANDROID_WINDOW (f), bg);
+ android_set_foreground (x->cursor_gc, bg);
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qbackground_color, arg);
+
+ if (FRAME_VISIBLE_P (f))
+ redraw_frame (f);
+ }
+}
+
+static void
+android_set_border_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ /* Left unimplemented because Android has no window borders. */
+ CHECK_STRING (arg);
+ android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ update_face_from_frame_parameter (f, Qborder_color, arg);
+}
+
+static void
+android_set_cursor_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ unsigned long fore_pixel, pixel;
+ struct android_output *x;
+
+ x = f->output_data.android;
+
+ if (!NILP (Vx_cursor_fore_pixel))
+ fore_pixel = android_decode_color (f, Vx_cursor_fore_pixel,
+ WHITE_PIX_DEFAULT (f));
+ else
+ fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+
+ pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+
+ /* Make sure that the cursor color differs from the background color. */
+ if (pixel == FRAME_BACKGROUND_PIXEL (f))
+ {
+ pixel = FRAME_FOREGROUND_PIXEL (f);
+ if (pixel == fore_pixel)
+ fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+ }
+
+ x->cursor_foreground_pixel = fore_pixel;
+ x->cursor_pixel = pixel;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_background (x->cursor_gc, x->cursor_pixel);
+ android_set_foreground (x->cursor_gc, fore_pixel);
+ unblock_input ();
+
+ if (FRAME_VISIBLE_P (f))
+ {
+ gui_update_cursor (f, false);
+ gui_update_cursor (f, true);
+ }
+ }
+
+ update_face_from_frame_parameter (f, Qcursor_color, arg);
+}
+
+static void
+android_set_cursor_type (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ set_frame_cursor_types (f, arg);
+}
+
+static void
+android_set_foreground_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x;
+ unsigned long fg, old_fg;
+
+ x = f->output_data.android;
+
+ fg = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ old_fg = FRAME_FOREGROUND_PIXEL (f);
+ FRAME_FOREGROUND_PIXEL (f) = fg;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_foreground (x->normal_gc, fg);
+ android_set_background (x->reverse_gc, fg);
+
+ if (x->cursor_pixel == old_fg)
+ {
+ x->cursor_pixel = fg;
+ android_set_background (x->cursor_gc, x->cursor_pixel);
+ }
+
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qforeground_color, arg);
+
+ if (FRAME_VISIBLE_P (f))
+ redraw_frame (f);
+ }
+}
+
+static void
+android_set_child_frame_border_width (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ int border;
+
+ if (NILP (arg))
+ border = -1;
+ else if (RANGED_FIXNUMP (0, arg, INT_MAX))
+ border = XFIXNAT (arg);
+ else
+ signal_error ("Invalid child frame border width", arg);
+
+ if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f))
+ {
+ f->child_frame_border_width = border;
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width);
+ android_clear_under_internal_border (f);
+ }
+ }
+}
+
+static void
+android_set_internal_border_width (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ int border = check_int_nonnegative (arg);
+
+ if (border != FRAME_INTERNAL_BORDER_WIDTH (f))
+ {
+ f->internal_border_width = border;
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width);
+ android_clear_under_internal_border (f);
+ }
+ }
+}
+
+static void
+android_set_menu_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int nlines;
+ int olines = FRAME_MENU_BAR_LINES (f);
+
+ /* Right now, menu bars don't work properly in minibuf-only frames;
+ most of the commands try to apply themselves to the minibuffer
+ frame itself, and get an error because you can't switch buffers
+ in or split the minibuffer window. */
+ if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f))
+ return;
+
+ if (TYPE_RANGED_FIXNUMP (int, value))
+ nlines = XFIXNUM (value);
+ else
+ nlines = 0;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ FRAME_MENU_BAR_LINES (f) = nlines;
+ FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
+ if (FRAME_ANDROID_WINDOW (f))
+ android_clear_under_internal_border (f);
+
+ /* If the menu bar height gets changed, the internal border below
+ the top margin has to be cleared. Also, if the menu bar gets
+ larger, the area for the added lines has to be cleared except for
+ the first menu bar line that is to be drawn later. */
+ if (nlines != olines)
+ {
+ int height = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = FRAME_PIXEL_WIDTH (f);
+ int y;
+
+ adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
+
+ /* height can be zero here. */
+ if (FRAME_ANDROID_WINDOW (f) && height > 0 && width > 0)
+ {
+ y = FRAME_TOP_MARGIN_HEIGHT (f);
+
+ block_input ();
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+ 0, y, width, height);
+ unblock_input ();
+ }
+
+ if (nlines > 1 && nlines > olines)
+ {
+ y = (olines == 0 ? 1 : olines) * FRAME_LINE_HEIGHT (f);
+ height = nlines * FRAME_LINE_HEIGHT (f) - y;
+
+ block_input ();
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y,
+ width, height);
+ unblock_input ();
+ }
+
+ if (nlines == 0 && WINDOWP (f->menu_bar_window))
+ clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix);
+ }
+
+ adjust_frame_glyphs (f);
+}
+
+
+
+/* These enums must stay in sync with the mouse_cursor_types array
+ below! */
+
+enum mouse_cursor
+ {
+ mouse_cursor_text,
+ mouse_cursor_nontext,
+ mouse_cursor_hourglass,
+ mouse_cursor_mode,
+ mouse_cursor_hand,
+ mouse_cursor_horizontal_drag,
+ mouse_cursor_vertical_drag,
+ mouse_cursor_left_edge,
+ mouse_cursor_top_left_corner,
+ mouse_cursor_top_edge,
+ mouse_cursor_top_right_corner,
+ mouse_cursor_right_edge,
+ mouse_cursor_bottom_right_corner,
+ mouse_cursor_bottom_edge,
+ mouse_cursor_bottom_left_corner,
+ mouse_cursor_max
+ };
+
+struct mouse_cursor_types
+{
+ /* Printable name for error messages (optional). */
+ const char *name;
+
+ /* Lisp variable controlling the cursor shape. */
+ /* FIXME: A couple of these variables are defined in the C code but
+ are not actually accessible from Lisp. They should probably be
+ made accessible or removed. */
+ Lisp_Object *shape_var_ptr;
+
+ /* The default shape. */
+ int default_shape;
+};
+
+/* This array must stay in sync with enum mouse_cursor above! */
+
+static const struct mouse_cursor_types mouse_cursor_types[] =
+ {
+ {"text", &Vx_pointer_shape, ANDROID_XC_XTERM, },
+ {"nontext", &Vx_nontext_pointer_shape, ANDROID_XC_LEFT_PTR, },
+ {"hourglass", &Vx_hourglass_pointer_shape, ANDROID_XC_WATCH, },
+ {"modeline", &Vx_mode_pointer_shape, ANDROID_XC_XTERM, },
+ {NULL, &Vx_sensitive_text_pointer_shape, ANDROID_XC_HAND2, },
+ {NULL, &Vx_window_horizontal_drag_shape, ANDROID_XC_SB_H_DOUBLE_ARROW, },
+ {NULL, &Vx_window_vertical_drag_shape, ANDROID_XC_SB_V_DOUBLE_ARROW, },
+ {NULL, &Vx_window_left_edge_shape, ANDROID_XC_LEFT_SIDE, },
+ {NULL, &Vx_window_top_left_corner_shape, ANDROID_XC_TOP_LEFT_CORNER, },
+ {NULL, &Vx_window_top_edge_shape, ANDROID_XC_TOP_SIDE, },
+ {NULL, &Vx_window_top_right_corner_shape, ANDROID_XC_TOP_RIGHT_CORNER, },
+ {NULL, &Vx_window_right_edge_shape, ANDROID_XC_RIGHT_SIDE, },
+ {NULL, &Vx_window_bottom_right_corner_shape,
+ ANDROID_XC_BOTTOM_RIGHT_CORNER, },
+ {NULL, &Vx_window_bottom_edge_shape, ANDROID_XC_BOTTOM_SIDE, },
+ {NULL, &Vx_window_bottom_left_corner_shape,
+ ANDROID_XC_BOTTOM_LEFT_CORNER, },
+ };
+
+struct mouse_cursor_data
+{
+ /* Cursor numbers chosen. */
+ unsigned int cursor_num[mouse_cursor_max];
+
+ /* Allocated Cursor values, or zero for failed attempts. */
+ android_cursor cursor[mouse_cursor_max];
+};
+
+
+
+static void
+android_set_mouse_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x = f->output_data.android;
+ struct mouse_cursor_data cursor_data = { -1, -1 };
+ unsigned long pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f);
+ int i;
+
+ /* Don't let pointers be invisible. */
+ if (mask_color == pixel)
+ pixel = FRAME_FOREGROUND_PIXEL (f);
+
+ x->mouse_pixel = pixel;
+
+ for (i = 0; i < mouse_cursor_max; i++)
+ {
+ Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr;
+ cursor_data.cursor_num[i]
+ = (!NILP (shape_var)
+ ? check_uinteger_max (shape_var, UINT_MAX)
+ : mouse_cursor_types[i].default_shape);
+ }
+
+ block_input ();
+
+ for (i = 0; i < mouse_cursor_max; i++)
+ cursor_data.cursor[i]
+ = android_create_font_cursor (cursor_data.cursor_num[i]);
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ f->output_data.android->current_cursor
+ = cursor_data.cursor[mouse_cursor_text];
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ f->output_data.android->current_cursor);
+ }
+
+#define INSTALL_CURSOR(FIELD, SHORT_INDEX) \
+ eassert (x->FIELD \
+ != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]); \
+ if (x->FIELD != 0) \
+ android_free_cursor (x->FIELD); \
+ x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX];
+
+ INSTALL_CURSOR (text_cursor, text);
+ INSTALL_CURSOR (nontext_cursor, nontext);
+ INSTALL_CURSOR (hourglass_cursor, hourglass);
+ INSTALL_CURSOR (modeline_cursor, mode);
+ INSTALL_CURSOR (hand_cursor, hand);
+ INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag);
+ INSTALL_CURSOR (vertical_drag_cursor, vertical_drag);
+ INSTALL_CURSOR (left_edge_cursor, left_edge);
+ INSTALL_CURSOR (top_left_corner_cursor, top_left_corner);
+ INSTALL_CURSOR (top_edge_cursor, top_edge);
+ INSTALL_CURSOR (top_right_corner_cursor, top_right_corner);
+ INSTALL_CURSOR (right_edge_cursor, right_edge);
+ INSTALL_CURSOR (bottom_right_corner_cursor, bottom_right_corner);
+ INSTALL_CURSOR (bottom_edge_cursor, bottom_edge);
+ INSTALL_CURSOR (bottom_left_corner_cursor, bottom_left_corner);
+
+#undef INSTALL_CURSOR
+
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qmouse_color, arg);
+}
+
+static void
+android_set_title (struct frame *f, Lisp_Object name,
+ Lisp_Object old_name)
+{
+ /* Don't change the title if it's already NAME. */
+ if (EQ (name, f->title))
+ return;
+
+ update_mode_lines = 38;
+
+ fset_title (f, name);
+
+ if (NILP (name))
+ name = f->name;
+ else
+ CHECK_STRING (name);
+}
+
+static void
+android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+ double alpha = 1.0;
+ double newval[2];
+ int i;
+ Lisp_Object item;
+
+ /* N.B. that setting the window alpha is actually unsupported under
+ Android. */
+
+ for (i = 0; i < 2; i++)
+ {
+ newval[i] = 1.0;
+ if (CONSP (arg))
+ {
+ item = CAR (arg);
+ arg = CDR (arg);
+ }
+ else
+ item = arg;
+
+ if (NILP (item))
+ alpha = - 1.0;
+ else if (FLOATP (item))
+ {
+ alpha = XFLOAT_DATA (item);
+ if (! (0 <= alpha && alpha <= 1.0))
+ args_out_of_range (make_float (0.0), make_float (1.0));
+ }
+ else if (FIXNUMP (item))
+ {
+ EMACS_INT ialpha = XFIXNUM (item);
+ if (! (0 <= ialpha && ialpha <= 100))
+ args_out_of_range (make_fixnum (0), make_fixnum (100));
+ alpha = ialpha / 100.0;
+ }
+ else
+ wrong_type_argument (Qnumberp, item);
+ newval[i] = alpha;
+ }
+
+ for (i = 0; i < 2; i++)
+ f->alpha[i] = newval[i];
+
+ if (FRAME_TERMINAL (f)->set_frame_alpha_hook)
+ {
+ block_input ();
+ FRAME_TERMINAL (f)->set_frame_alpha_hook (f);
+ unblock_input ();
+ }
+}
+
+static void
+android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ if (!EQ (new_value, old_value))
+ {
+ android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f),
+ new_value);
+ FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
+ }
+}
+
+static void
+android_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ if (!EQ (new_value, old_value))
+ {
+ android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f),
+ new_value);
+ FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+ }
+}
+
+frame_parm_handler android_frame_parm_handlers[] =
+{
+ gui_set_autoraise,
+ gui_set_autolower,
+ android_set_background_color,
+ android_set_border_color,
+ gui_set_border_width,
+ android_set_cursor_color,
+ android_set_cursor_type,
+ gui_set_font,
+ android_set_foreground_color,
+ NULL,
+ NULL,
+ android_set_child_frame_border_width,
+ android_set_internal_border_width,
+ gui_set_right_divider_width,
+ gui_set_bottom_divider_width,
+ android_set_menu_bar_lines,
+ android_set_mouse_color,
+ android_explicitly_set_name,
+ gui_set_scroll_bar_width,
+ gui_set_scroll_bar_height,
+ android_set_title,
+ gui_set_unsplittable,
+ gui_set_vertical_scroll_bars,
+ gui_set_horizontal_scroll_bars,
+ gui_set_visibility,
+ android_set_tab_bar_lines,
+ android_set_tool_bar_lines,
+ NULL,
+ NULL,
+ gui_set_screen_gamma,
+ gui_set_line_spacing,
+ gui_set_left_fringe,
+ gui_set_right_fringe,
+ NULL,
+ gui_set_fullscreen,
+ gui_set_font_backend,
+ android_set_alpha,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ android_set_parent_frame,
+ NULL,
+ android_set_no_focus_on_map,
+ android_set_no_accept_focus,
+ NULL,
+ NULL,
+ gui_set_no_special_glyphs,
+ NULL,
+ NULL,
+};
+
+
+
+/* Battery information support. */
+
+DEFUN ("android-query-battery", Fandroid_query_battery,
+ Sandroid_query_battery, 0, 0, 0,
+ doc: /* Perform a query for battery information.
+Value is nil upon failure, or a list of the form:
+
+ (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS
+ REMAINING PLUGGED TEMP)
+
+where REMAINING, CURRENT-AVERAGE, and CURRENT-NOW are undefined prior
+to Android 5.0.
+
+See the documentation at
+
+ https://developer.android.com/reference/android/os/BatteryManager
+
+for more details about these values. */)
+ (void)
+{
+ struct android_battery_state state;
+
+ /* Make sure the Android libraries have been initialized. */
+
+ if (!android_init_gui)
+ return Qnil;
+
+ /* Perform the query. */
+
+ if (android_query_battery (&state))
+ return Qnil;
+
+ return listn (8, make_int (state.capacity),
+ make_fixnum (state.charge_counter),
+ make_int (state.current_average),
+ make_int (state.current_now),
+ make_fixnum (state.status),
+ make_int (state.remaining),
+ make_fixnum (state.plugged),
+ make_fixnum (state.temperature));
+}
+
+
+
+/* Miscellaneous input method related stuff. */
+
+/* Report X, Y, by the phys cursor width and height as the cursor
+ anchor rectangle for W's frame. */
+
+void
+android_set_preeditarea (struct window *w, int x, int y)
+{
+ struct frame *f;
+
+ f = WINDOW_XFRAME (w);
+
+ /* Convert the window coordinates to the frame's coordinate
+ space. */
+ x = (WINDOW_TO_FRAME_PIXEL_X (w, x)
+ + WINDOW_LEFT_FRINGE_WIDTH (w)
+ + WINDOW_LEFT_MARGIN_WIDTH (w));
+ y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
+
+ /* Note that calculating the baseline is too hard, so the bottom of
+ the cursor is used instead. */
+ android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x,
+ y, y + w->phys_cursor_height,
+ y + w->phys_cursor_height);
+}
+
+#endif
+
+
+
+void
+syms_of_androidfns (void)
+{
+ /* Miscellaneous symbols used by some functions here. */
+ DEFSYM (Qtrue_color, "true-color");
+ DEFSYM (Qalways, "always");
+
+ DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_pointer_shape = Qnil;
+
+#if false /* This doesn't really do anything. */
+ DEFVAR_LISP ("x-nontext-pointer-shape", Vx_nontext_pointer_shape,
+ doc: /* SKIP: real doc in xfns.c. */);
+#endif
+ Vx_nontext_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-hourglass-pointer-shape", Vx_hourglass_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_hourglass_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-sensitive-text-pointer-shape",
+ Vx_sensitive_text_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_sensitive_text_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-horizontal-drag-cursor",
+ Vx_window_horizontal_drag_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_horizontal_drag_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-vertical-drag-cursor",
+ Vx_window_vertical_drag_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_vertical_drag_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-left-edge-cursor",
+ Vx_window_left_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_left_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-left-corner-cursor",
+ Vx_window_top_left_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_left_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-edge-cursor",
+ Vx_window_top_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-right-corner-cursor",
+ Vx_window_top_right_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_right_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-right-edge-cursor",
+ Vx_window_right_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_right_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-right-corner-cursor",
+ Vx_window_bottom_right_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_right_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-edge-cursor",
+ Vx_window_bottom_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_edge_shape = Qnil;
+
+#if false /* This doesn't really do anything. */
+ DEFVAR_LISP ("x-mode-pointer-shape", Vx_mode_pointer_shape,
+ doc: /* SKIP: real doc in xfns.c. */);
+#endif
+ Vx_mode_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-left-corner-cursor",
+ Vx_window_bottom_left_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_left_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel,
+ doc: /* SKIP: real doc in xfns.c. */);
+ Vx_cursor_fore_pixel = Qnil;
+
+ /* Used by Fx_show_tip. */
+ DEFSYM (Qrun_at_time, "run-at-time");
+ DEFSYM (Qx_hide_tip, "x-hide-tip");
+ DEFSYM (Qcancel_timer, "cancel-timer");
+ DEFSYM (Qassq_delete_all, "assq-delete-all");
+ DEFSYM (Qcolor, "color");
+
+ DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
+ doc: /* SKIP: real doc in xfns.c. */);
+ Vx_max_tooltip_size = Qnil;
+
+ DEFVAR_BOOL ("android-pass-multimedia-buttons-to-system",
+ android_pass_multimedia_buttons_to_system,
+ doc: /* Whether or not to pass volume control buttons to the system.
+Generally, the `volume-up', `volume-down' and `volume-mute' keys are
+processed by Emacs, but setting this to non-nil they are passed to the
+operating system instead of being intercepted by Emacs.
+
+Note that if you set this, you will no longer be able to quit Emacs
+using the volume down button. */);
+ android_pass_multimedia_buttons_to_system = false;
+
+ DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader,
+ doc: /* Whether or not to bypass system restrictions on program execution.
+
+Android 10 and later prevent programs from executing files installed
+in writable directories, such as the application data directory.
+
+When non-nil, Emacs will bypass this restriction by running such
+executables under system call tracing, and replacing the `execve'
+system call with a version which ignores the system's security
+restrictions.
+
+This option has no effect on Android 9 and earlier. */);
+ android_use_exec_loader = true;
+
+ /* Functions defined. */
+ defsubr (&Sx_create_frame);
+ defsubr (&Sxw_color_defined_p);
+ defsubr (&Sxw_color_values);
+ defsubr (&Sxw_display_color_p);
+ defsubr (&Sx_display_grayscale_p);
+ defsubr (&Sx_display_pixel_width);
+ defsubr (&Sx_display_pixel_height);
+ defsubr (&Sx_display_planes);
+ defsubr (&Sx_display_color_cells);
+ defsubr (&Sx_display_screens);
+ defsubr (&Sx_display_mm_width);
+ defsubr (&Sx_display_mm_height);
+ defsubr (&Sx_display_backing_store);
+ defsubr (&Sx_display_visual_class);
+ defsubr (&Sandroid_display_monitor_attributes_list);
+ defsubr (&Sandroid_frame_geometry);
+ defsubr (&Sandroid_frame_edges);
+ defsubr (&Sandroid_frame_list_z_order);
+ defsubr (&Sandroid_frame_restack);
+ defsubr (&Sandroid_mouse_absolute_pixel_position);
+ defsubr (&Sandroid_set_mouse_absolute_pixel_position);
+ defsubr (&Sandroid_get_connection);
+ defsubr (&Sx_display_list);
+ defsubr (&Sx_show_tip);
+ defsubr (&Sx_hide_tip);
+ defsubr (&Sandroid_detect_mouse);
+ defsubr (&Sandroid_toggle_on_screen_keyboard);
+ defsubr (&Sx_server_vendor);
+ defsubr (&Sx_server_version);
+#ifndef ANDROID_STUBIFY
+ defsubr (&Sandroid_query_battery);
+
+ tip_timer = Qnil;
+ staticpro (&tip_timer);
+ tip_frame = Qnil;
+ staticpro (&tip_frame);
+ tip_last_frame = Qnil;
+ staticpro (&tip_last_frame);
+ tip_last_string = Qnil;
+ staticpro (&tip_last_string);
+ tip_last_parms = Qnil;
+ staticpro (&tip_last_parms);
+ tip_dx = Qnil;
+ staticpro (&tip_dx);
+ tip_dy = Qnil;
+ staticpro (&tip_dy);
+#endif
+}
diff --git a/src/androidfont.c b/src/androidfont.c
new file mode 100644
index 00000000000..1a09027bca7
--- /dev/null
+++ b/src/androidfont.c
@@ -0,0 +1,1108 @@
+/* Android fallback font driver.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Due to the terrible nature of the Android Typeface subsystems, this
+ font driver is only used as a fallback when sfntfont-android.c
+ fails to enumerate any fonts at all. */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "dispextern.h"
+#include "composite.h"
+#include "blockinput.h"
+#include "charset.h"
+#include "frame.h"
+#include "window.h"
+#include "fontset.h"
+#include "androidterm.h"
+#include "character.h"
+#include "coding.h"
+#include "font.h"
+#include "termchar.h"
+#include "pdumper.h"
+#include "android.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+struct android_emacs_font_driver
+{
+ jclass class;
+ jmethodID list;
+ jmethodID match;
+ jmethodID list_families;
+ jmethodID open_font;
+ jmethodID has_char;
+ jmethodID text_extents;
+ jmethodID encode_char;
+ jmethodID draw;
+
+ /* Static methods. */
+ jmethodID create_font_driver;
+};
+
+struct android_emacs_font_spec
+{
+ jclass class;
+ jfieldID foundry;
+ jfieldID family;
+ jfieldID adstyle;
+ jfieldID registry;
+ jfieldID width;
+ jfieldID weight;
+ jfieldID slant;
+ jfieldID size;
+ jfieldID spacing;
+ jfieldID avgwidth;
+ jfieldID dpi;
+};
+
+struct android_emacs_font_metrics
+{
+ jclass class;
+ jfieldID lbearing;
+ jfieldID rbearing;
+ jfieldID width;
+ jfieldID ascent;
+ jfieldID descent;
+};
+
+struct android_emacs_font_object
+{
+ jclass class;
+ jfieldID min_width;
+ jfieldID max_width;
+ jfieldID pixel_size;
+ jfieldID height;
+ jfieldID space_width;
+ jfieldID average_width;
+ jfieldID ascent;
+ jfieldID descent;
+ jfieldID underline_thickness;
+ jfieldID underline_position;
+ jfieldID baseline_offset;
+ jfieldID relative_compose;
+ jfieldID default_ascent;
+ jfieldID encoding_charset;
+ jfieldID repertory_charset;
+};
+
+struct android_integer
+{
+ jclass class;
+ jmethodID constructor;
+ jmethodID int_value;
+};
+
+struct androidfont_info
+{
+ /* The font pseudo-vector object. */
+ struct font font;
+
+ /* The Java-side font. */
+ jobject object;
+
+ /* Cached glyph metrics arranged in a two dimensional array. */
+ struct font_metrics **metrics;
+};
+
+struct androidfont_entity
+{
+ /* The font entity pvec. */
+ struct font_entity font;
+
+ /* The Java-side font entity. */
+ jobject object;
+};
+
+/* Method and class identifiers associated with the EmacsFontDriver
+ class. */
+
+struct android_emacs_font_driver font_driver_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontSpec class. */
+
+struct android_emacs_font_spec font_spec_class;
+
+/* Method and class identifiers associated with the Integer class. */
+
+struct android_integer integer_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontMetrics class. */
+
+struct android_emacs_font_metrics font_metrics_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontObject class. */
+
+struct android_emacs_font_object font_object_class;
+
+/* The font cache. */
+
+static Lisp_Object font_cache;
+
+/* The Java-side font driver. */
+
+static jobject font_driver;
+
+
+
+/* Initialize the class and method identifiers for functions in the
+ EmacsFontDriver class, and place them in `font_driver_class'. */
+
+static void
+android_init_font_driver (void)
+{
+ jclass old;
+
+ font_driver_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver");
+ eassert (font_driver_class.class);
+
+ old = font_driver_class.class;
+ font_driver_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_driver_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ font_driver_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ font_driver_class.class, \
+ name, signature); \
+ eassert (font_driver_class.c_name);
+
+ FIND_METHOD (list, "list", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+ "[Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+ FIND_METHOD (match, "match", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+ "Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+ FIND_METHOD (list_families, "listFamilies", "()[Ljava/lang/String;");
+ FIND_METHOD (open_font, "openFont", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+ "Entity;I)Lorg/gnu/emacs/EmacsFontDriver$FontObject;");
+ FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+ "Spec;C)I");
+ FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
+ "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+ FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
+ "$FontObject;C)I");
+ FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
+ "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
+
+ font_driver_class.create_font_driver
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ font_driver_class.class,
+ "createFontDriver",
+ "()Lorg/gnu/emacs/"
+ "EmacsFontDriver;");
+ eassert (font_driver_class.create_font_driver);
+#undef FIND_METHOD
+}
+
+/* Initialize the class and field identifiers for functions in the
+ EmacsFontDriver$FontSpec class, and place them in
+ `font_spec_class'. */
+
+static void
+android_init_font_spec (void)
+{
+ jclass old;
+
+ font_spec_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontSpec");
+ eassert (font_spec_class.class);
+
+ old = font_spec_class.class;
+ font_spec_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_spec_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_spec_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_spec_class.class, \
+ name, signature); \
+ eassert (font_spec_class.c_name);
+
+ FIND_FIELD (foundry, "foundry", "Ljava/lang/String;");
+ FIND_FIELD (family, "family", "Ljava/lang/String;");
+ FIND_FIELD (adstyle, "adstyle", "Ljava/lang/String;");
+ FIND_FIELD (registry, "registry", "Ljava/lang/String;");
+ FIND_FIELD (width, "width", "Ljava/lang/Integer;");
+ FIND_FIELD (weight, "weight", "Ljava/lang/Integer;");
+ FIND_FIELD (slant, "slant", "Ljava/lang/Integer;");
+ FIND_FIELD (size, "size", "Ljava/lang/Integer;");
+ FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
+ FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
+ FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
+#undef FIND_FIELD
+}
+
+static void
+android_init_font_metrics (void)
+{
+ jclass old;
+
+ font_metrics_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontMetrics");
+ eassert (font_metrics_class.class);
+
+ old = font_metrics_class.class;
+ font_metrics_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_metrics_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_metrics_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_metrics_class.class, \
+ name, signature); \
+ eassert (font_metrics_class.c_name);
+
+ FIND_FIELD (lbearing, "lbearing", "S");
+ FIND_FIELD (rbearing, "rbearing", "S");
+ FIND_FIELD (width, "width", "S");
+ FIND_FIELD (ascent, "ascent", "S");
+ FIND_FIELD (descent, "descent", "S");
+#undef FIND_FIELD
+}
+
+static void
+android_init_integer (void)
+{
+ jclass old;
+
+ integer_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "java/lang/Integer");
+ eassert (integer_class.class);
+
+ old = integer_class.class;
+ integer_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!integer_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ integer_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ integer_class.class, \
+ name, signature); \
+ eassert (integer_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(I)V");
+ FIND_METHOD (int_value, "intValue", "()I");
+#undef FIND_METHOD
+}
+
+static void
+android_init_font_object (void)
+{
+ jclass old;
+
+ font_object_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontObject");
+ eassert (font_object_class.class);
+
+ old = font_object_class.class;
+ font_object_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_object_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_object_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_object_class.class, \
+ name, signature); \
+ eassert (font_object_class.c_name);
+
+ FIND_FIELD (min_width, "minWidth", "I");
+ FIND_FIELD (max_width, "maxWidth", "I");
+ FIND_FIELD (pixel_size, "pixelSize", "I");
+ FIND_FIELD (height, "height", "I");
+ FIND_FIELD (space_width, "spaceWidth", "I");
+ FIND_FIELD (average_width, "averageWidth", "I");
+ FIND_FIELD (ascent, "ascent", "I");
+ FIND_FIELD (descent, "descent", "I");
+ FIND_FIELD (underline_thickness, "underlineThickness", "I");
+ FIND_FIELD (underline_position, "underlinePosition", "I");
+ FIND_FIELD (baseline_offset, "baselineOffset", "I");
+ FIND_FIELD (relative_compose, "relativeCompose", "I");
+ FIND_FIELD (default_ascent, "defaultAscent", "I");
+ FIND_FIELD (encoding_charset, "encodingCharset", "I");
+ FIND_FIELD (repertory_charset, "repertoryCharset", "I");
+#undef FIND_FIELD
+}
+
+static Lisp_Object
+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. */
+
+static jobject
+androidfont_from_lisp (Lisp_Object font)
+{
+ jobject spec, integer;
+ jstring string;
+ Lisp_Object tem;
+
+ spec = (*android_java_env)->AllocObject (android_java_env,
+ font_spec_class.class);
+ android_exception_check ();
+
+#define DO_SYMBOL_FIELD(field, index) \
+ tem = AREF (font, index); \
+ if (SYMBOLP (tem)) \
+ { \
+ /* Java seems to DTRT with the Emacs string encoding, so this does \
+ not matter at all. */ \
+ string = (*android_java_env)->NewStringUTF (android_java_env, \
+ SSDATA (SYMBOL_NAME (tem))); \
+ android_exception_check_1 (spec); \
+ \
+ (*android_java_env)->SetObjectField (android_java_env, spec, \
+ font_spec_class.field, \
+ string); \
+ ANDROID_DELETE_LOCAL_REF (string); \
+ } \
+
+ DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+ DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+ DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+ DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+
+#define DO_CARDINAL_FIELD(field, value) \
+ if (value != -1) \
+ { \
+ integer = (*android_java_env)->NewObject (android_java_env, \
+ integer_class.class, \
+ integer_class.constructor, \
+ (jint) value); \
+ android_exception_check_1 (spec); \
+ \
+ (*android_java_env)->SetObjectField (android_java_env, spec, \
+ font_spec_class.field, \
+ integer); \
+ ANDROID_DELETE_LOCAL_REF (integer); \
+ }
+
+ DO_CARDINAL_FIELD (width, FONT_WIDTH_NUMERIC (font));
+ DO_CARDINAL_FIELD (weight, FONT_WEIGHT_NUMERIC (font));
+ DO_CARDINAL_FIELD (slant, FONT_SLANT_NUMERIC (font));
+ DO_CARDINAL_FIELD (size, (FIXNUMP (AREF (font, FONT_SIZE_INDEX))
+ ? XFIXNUM (AREF (font, FONT_SIZE_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (spacing, (FIXNUMP (AREF (font, FONT_SPACING_INDEX))
+ ? XFIXNUM (AREF (font, FONT_SPACING_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
+ ? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
+ ? XFIXNUM (AREF (font, FONT_DPI_INDEX))
+ : -1));
+
+#undef DO_CARDINAL_FIELD
+
+ return spec;
+}
+
+static void
+androidfont_from_java (jobject spec, Lisp_Object entity)
+{
+ jobject tem;
+ jint value;
+ const char *string;
+
+#define DO_SYMBOL_FIELD(field, index) \
+ tem = (*android_java_env)->GetObjectField (android_java_env, \
+ spec, \
+ font_spec_class.field); \
+ if (tem) \
+ { \
+ string = (*android_java_env)->GetStringUTFChars (android_java_env, \
+ tem, NULL); \
+ if (!string) \
+ memory_full (0); \
+ ASET (entity, index, intern (string)); \
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env, \
+ tem, string); \
+ ANDROID_DELETE_LOCAL_REF (tem); \
+ }
+
+ DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+ DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+ DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+ DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+#define DO_CARDINAL_FIELD(field, index, is_style) \
+ tem = (*android_java_env)->GetObjectField (android_java_env, \
+ spec, \
+ font_spec_class.field); \
+ if (tem) \
+ { \
+ value \
+ = (*android_java_env)->CallIntMethod (android_java_env, \
+ tem, \
+ integer_class.int_value); \
+ if (!is_style) \
+ ASET (entity, index, make_fixnum (value)); \
+ else \
+ FONT_SET_STYLE (entity, index, make_fixnum (value)); \
+ ANDROID_DELETE_LOCAL_REF (tem); \
+ }
+
+ DO_CARDINAL_FIELD (width, FONT_WIDTH_INDEX, true);
+ DO_CARDINAL_FIELD (weight, FONT_WEIGHT_INDEX, true);
+ DO_CARDINAL_FIELD (slant, FONT_SLANT_INDEX, true);
+ DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
+ DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
+ DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
+ DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
+
+#undef DO_CARDINAL_FIELD
+}
+
+/* Transfer the values from FONT, which must be some kind of font
+ entity, */
+
+static Lisp_Object
+androidfont_list (struct frame *f, Lisp_Object font_spec)
+{
+ jobject spec, array, tem;
+ jarray entities;
+ jsize i, size;
+ 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,
+ font_driver_class.list,
+ spec);
+ android_exception_check_1 (spec);
+ ANDROID_DELETE_LOCAL_REF (spec);
+
+ entities = (jarray) array;
+ size = (*android_java_env)->GetArrayLength (android_java_env,
+ entities);
+ value = Qnil;
+
+ for (i = 0; i < size; ++i)
+ {
+ entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+ info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+ /* The type must be set correctly, or font_open_entity won't be
+ able to find the right font driver. */
+ ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+ /* Clear this now in case GC happens without it set, which can
+ happen if androidfont_from_java runs out of memory. */
+ info->object = NULL;
+
+ tem = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ entities, i);
+ androidfont_from_java (tem, entity);
+
+ /* Now, make a global reference to the Java font entity. */
+ info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) tem);
+ android_exception_check_2 (tem, entities);
+ ANDROID_DELETE_LOCAL_REF (tem);
+
+ value = Fcons (entity, value);
+ }
+
+ ANDROID_DELETE_LOCAL_REF (entities);
+ return Fnreverse (value);
+}
+
+static Lisp_Object
+androidfont_match (struct frame *f, Lisp_Object font_spec)
+{
+ jobject spec, result;
+ 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,
+ font_driver_class.match,
+ spec);
+ android_exception_check_1 (spec);
+ ANDROID_DELETE_LOCAL_REF (spec);
+
+ entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+ info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+ /* The type must be set correctly, or font_open_entity won't be able
+ to find the right font driver. */
+ ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+ info->object = NULL;
+ androidfont_from_java (result, entity);
+ info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) result);
+ android_exception_check_2 (entity, result);
+ ANDROID_DELETE_LOCAL_REF (result);
+
+ return entity;
+}
+
+static int
+androidfont_draw (struct glyph_string *s, int from, int to,
+ int x, int y, bool with_background)
+{
+ struct androidfont_info *info;
+ jarray chars;
+ 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;
+
+ gcontext = android_resolve_handle (s->gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+ drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f),
+ ANDROID_HANDLE_WINDOW);
+ chars = (*android_java_env)->NewIntArray (android_java_env,
+ to - from);
+ android_exception_check ();
+
+ (*android_java_env)->SetIntArrayRegion (android_java_env, chars,
+ 0, to - from,
+ (jint *) s->char2b + from);
+
+ info = (struct androidfont_info *) s->font;
+ prepare_face_for_display (s->f, s->face);
+
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.draw,
+ info->object,
+ gcontext, drawable,
+ chars, (jint) x, (jint) y,
+ (jint) s->width,
+ (jboolean) with_background);
+ android_exception_check_1 (chars);
+ ANDROID_DELETE_LOCAL_REF (chars);
+
+ return rc;
+}
+
+static Lisp_Object
+androidfont_open_font (struct frame *f, Lisp_Object font_entity,
+ int pixel_size)
+{
+ struct androidfont_info *font_info;
+ struct androidfont_entity *entity;
+ struct font *font;
+ Lisp_Object font_object;
+ 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)
+ {
+ /* This bit was copied from xfont.c. The values might need
+ adjustment. */
+
+ if (FRAME_FONT (f))
+ pixel_size = FRAME_FONT (f)->pixel_size;
+ else
+ pixel_size = 12;
+ }
+
+ __android_log_print (ANDROID_LOG_DEBUG, __func__,
+ "opening font entity %"pI"x:%d",
+ (EMACS_INT) font_entity, pixel_size);
+
+ entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
+
+ block_input ();
+ font_object = font_make_object (VECSIZE (struct androidfont_info),
+ font_entity, pixel_size);
+ ASET (font_object, FONT_TYPE_INDEX, Qandroid);
+ font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
+ font = &font_info->font;
+ font->driver = &androidfont_driver;
+
+ /* Clear font_info->object and font_info->metrics early in case GC
+ happens later on! */
+ font_info->object = NULL;
+ font_info->metrics = NULL;
+ unblock_input ();
+
+ font_info->object
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.open_font,
+ entity->object,
+ (jint) pixel_size);
+ android_exception_check ();
+
+ old = font_info->object;
+ font_info->object
+ = (*android_java_env)->NewGlobalRef (android_java_env, old);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_info->object)
+ return Qnil;
+
+ /* Copy the font attributes from the Java object. */
+ androidfont_from_java (font_info->object, font_object);
+
+ /* Copy font attributes inside EmacsFontDriver$FontObject. */
+#define DO_CARDINAL_FIELD(field) \
+ value \
+ = (*android_java_env)->GetIntField (android_java_env, \
+ font_info->object, \
+ font_object_class.field); \
+ font->field = value;
+
+ DO_CARDINAL_FIELD (min_width);
+ DO_CARDINAL_FIELD (max_width);
+ DO_CARDINAL_FIELD (pixel_size);
+ DO_CARDINAL_FIELD (height);
+ DO_CARDINAL_FIELD (space_width);
+ DO_CARDINAL_FIELD (average_width);
+ DO_CARDINAL_FIELD (ascent);
+ DO_CARDINAL_FIELD (descent);
+ DO_CARDINAL_FIELD (underline_thickness);
+ DO_CARDINAL_FIELD (underline_position);
+ DO_CARDINAL_FIELD (baseline_offset);
+ DO_CARDINAL_FIELD (relative_compose);
+ DO_CARDINAL_FIELD (default_ascent);
+ DO_CARDINAL_FIELD (encoding_charset);
+ DO_CARDINAL_FIELD (repertory_charset);
+
+#undef DO_CARDINAL_FIELD
+
+ /* This should eventually become unnecessary. */
+ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+ return font_object;
+}
+
+static void
+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. */
+
+ if (info->metrics)
+ {
+ for (i = 0; i < 256; ++i)
+ xfree (info->metrics[i]);
+ xfree (info->metrics);
+ }
+
+ info->metrics = NULL;
+
+ /* If info->object is NULL, then FONT was unsuccessfully created,
+ and there is no global reference that has to be deleted.
+
+ Alternatively, FONT may have been closed by font_close_object,
+ with this function called from GC. */
+
+ if (!info->object)
+ return;
+
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ info->object);
+ info->object = NULL;
+}
+
+static int
+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);
+
+ return (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.has_char,
+ entity->object, (jint) c);
+ }
+ else
+ {
+ info = (struct androidfont_info *) XFONT_OBJECT (font);
+
+ return (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.has_char,
+ info->object, (jint) c);
+ }
+}
+
+static unsigned
+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,
+ font_driver,
+ font_driver_class.encode_char,
+ info->object, (jchar) c);
+}
+
+static void
+androidfont_cache_text_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ int i;
+
+ /* Glyphs larger than 65535 can't be cached. */
+ if (glyph >= 256 * 256)
+ return;
+
+ if (!info->metrics)
+ info->metrics = xzalloc (256 * sizeof *info->metrics);
+
+ if (!info->metrics[glyph / 256])
+ {
+ info->metrics[glyph / 256]
+ = xnmalloc (256, sizeof **info->metrics);
+
+ /* Now, all the metrics in that array as invalid by setting
+ lbearing to SHRT_MAX. */
+ for (i = 0; i < 256; ++i)
+ info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
+ }
+
+ /* Finally, cache the glyph. */
+ info->metrics[glyph / 256][glyph % 256] = *metrics;
+}
+
+static bool
+androidfont_check_cached_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ if (info->metrics && info->metrics[glyph / 256]
+ && info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
+ {
+ *metrics = info->metrics[glyph / 256][glyph % 256];
+ return true;
+ }
+
+ return false;
+}
+
+static void
+androidfont_text_extents (struct font *font, const unsigned int *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ struct androidfont_info *info;
+ jarray codepoint_array;
+ jobject metrics_object;
+ short value;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ info = (struct androidfont_info *) font;
+
+ if (nglyphs == 1
+ && androidfont_check_cached_extents (info, *code, metrics))
+ return;
+
+ /* Allocate the arrays of code points and font metrics. */
+ codepoint_array
+ = (*android_java_env)->NewIntArray (android_java_env,
+ nglyphs);
+ if (!codepoint_array)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ verify (sizeof (unsigned int) == sizeof (jint));
+
+ /* Always true on every Android device. */
+ (*android_java_env)->SetIntArrayRegion (android_java_env,
+ codepoint_array,
+ 0, nglyphs,
+ (jint *) code);
+
+ metrics_object
+ = (*android_java_env)->AllocObject (android_java_env,
+ font_metrics_class.class);
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ font_driver,
+ font_driver_class.text_extents,
+ info->object, codepoint_array,
+ metrics_object);
+
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
+ ANDROID_DELETE_LOCAL_REF (codepoint_array);
+ memory_full (0);
+ }
+
+#define DO_CARDINAL_FIELD(field) \
+ value \
+ = (*android_java_env)->GetShortField (android_java_env, \
+ metrics_object, \
+ font_metrics_class.field); \
+ metrics->field = value;
+
+ DO_CARDINAL_FIELD (lbearing);
+ DO_CARDINAL_FIELD (rbearing);
+ DO_CARDINAL_FIELD (width);
+ DO_CARDINAL_FIELD (ascent);
+ DO_CARDINAL_FIELD (descent);
+
+#undef DO_CARDINAL_FIELD
+
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
+ ANDROID_DELETE_LOCAL_REF (codepoint_array);
+
+ /* Emacs spends a lot of time in androidfont_text_extents, which
+ makes calling JNI too slow. Cache the metrics for this single
+ glyph. */
+
+ if (nglyphs == 1)
+ androidfont_cache_text_extents (info, *code, metrics);
+}
+
+static Lisp_Object
+androidfont_list_family (struct frame *f)
+{
+ Lisp_Object families;
+ jarray family_array;
+ jobject string;
+ jsize i, length;
+ const char *family;
+
+ /* Return if the Android font driver is not initialized. Loading
+ every font under Android takes a non trivial amount of memory,
+ and is not something that should be done when the user tries to
+ list all of the font families. */
+
+ if (!font_driver)
+ return Qnil;
+
+ family_array
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.list_families);
+ android_exception_check ();
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ family_array);
+ families = Qnil;
+
+ for (i = 0; i < length; ++i)
+ {
+ string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ family_array, i);
+ family = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string, NULL);
+
+ if (!family)
+ {
+ ANDROID_DELETE_LOCAL_REF (string);
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ }
+
+ families = Fcons (build_string_from_utf8 (string), families);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ family);
+ ANDROID_DELETE_LOCAL_REF (string);
+ }
+
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ return Fnreverse (families);
+}
+
+struct font_driver androidfont_driver =
+ {
+ .type = LISPSYM_INITIALLY (Qandroid),
+ .case_sensitive = true,
+ .get_cache = androidfont_get_cache,
+ .list = androidfont_list,
+ .match = androidfont_match,
+ .draw = androidfont_draw,
+ .open_font = androidfont_open_font,
+ .close_font = androidfont_close_font,
+ .has_char = androidfont_has_char,
+ .encode_char = androidfont_encode_char,
+ .text_extents = androidfont_text_extents,
+ .list_family = androidfont_list_family,
+ };
+
+static void
+syms_of_androidfont_for_pdumper (void)
+{
+ register_font_driver (&androidfont_driver, NULL);
+}
+
+void
+syms_of_androidfont (void)
+{
+ DEFSYM (Qfontsize, "fontsize");
+
+ pdumper_do_now_and_after_load (syms_of_androidfont_for_pdumper);
+
+ font_cache = list (Qnil);
+ staticpro (&font_cache);
+}
+
+void
+init_androidfont (void)
+{
+ if (!android_init_gui)
+ return;
+
+ android_init_font_driver ();
+ android_init_font_spec ();
+ android_init_font_metrics ();
+ android_init_font_object ();
+ android_init_integer ();
+
+ /* The Java font driver is not initialized here because it uses a lot
+ of memory. */
+}
+
+void
+android_finalize_font_entity (struct font_entity *entity)
+{
+ struct androidfont_entity *info;
+
+ info = (struct androidfont_entity *) entity;
+
+ if (info->object)
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ info->object);
+
+ /* Not sure if this can be called twice. */
+ info->object = NULL;
+}
+
+#endif
diff --git a/src/androidgui.h b/src/androidgui.h
new file mode 100644
index 00000000000..6db25098398
--- /dev/null
+++ b/src/androidgui.h
@@ -0,0 +1,724 @@
+/* Android window system support.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _ANDROID_GUI_H_
+#define _ANDROID_GUI_H_
+
+struct android_char_struct
+{
+ int rbearing;
+ int lbearing;
+ int width;
+ int ascent;
+ int descent;
+};
+
+typedef struct android_char_struct XCharStruct;
+
+typedef unsigned short android_handle;
+
+typedef android_handle android_pixmap, Emacs_Pixmap;
+typedef android_handle android_window, Emacs_Window;
+typedef android_handle android_gcontext, GContext;
+typedef android_handle android_drawable, Drawable;
+typedef android_handle android_cursor, Emacs_Cursor;
+
+typedef unsigned int android_time;
+
+struct android_rectangle
+{
+ int x, y;
+ unsigned width, height;
+};
+
+struct android_point
+{
+ int x, y;
+};
+
+/* Keep this in sync with EmacsGC.java! */
+
+enum android_gc_function
+ {
+ ANDROID_GC_COPY = 0,
+ ANDROID_GC_XOR = 1,
+ };
+
+enum android_gc_value_mask
+ {
+ ANDROID_GC_FOREGROUND = (1 << 0),
+ ANDROID_GC_BACKGROUND = (1 << 1),
+ ANDROID_GC_FUNCTION = (1 << 2),
+ ANDROID_GC_CLIP_X_ORIGIN = (1 << 3),
+ ANDROID_GC_CLIP_Y_ORIGIN = (1 << 4),
+ ANDROID_GC_CLIP_MASK = (1 << 5),
+ ANDROID_GC_STIPPLE = (1 << 6),
+ ANDROID_GC_FILL_STYLE = (1 << 7),
+ ANDROID_GC_TILE_STIP_X_ORIGIN = (1 << 8),
+ ANDROID_GC_TILE_STIP_Y_ORIGIN = (1 << 9),
+ };
+
+enum android_fill_style
+ {
+ ANDROID_FILL_SOLID = 0,
+ ANDROID_FILL_OPAQUE_STIPPLED = 1,
+ };
+
+enum android_window_value_mask
+ {
+ ANDROID_CW_BACK_PIXEL = (1 << 1),
+ ANDROID_CW_OVERRIDE_REDIRECT = (1 << 2),
+ };
+
+struct android_set_window_attributes
+{
+ /* The background pixel. */
+ unsigned long background_pixel;
+
+ /* Whether or not the window is override redirect. This cannot be
+ set after creation on Android. */
+ bool override_redirect;
+};
+
+struct android_gc_values
+{
+ /* The foreground and background. */
+ unsigned long foreground, background;
+
+ /* The function. */
+ enum android_gc_function function;
+
+ /* The fill style. */
+ enum android_fill_style fill_style;
+
+ /* The clip X and Y origin. */
+ int clip_x_origin, clip_y_origin;
+
+ /* The clip mask image and stipple. */
+ android_pixmap clip_mask, stipple;
+
+ /* The tile-stipple X and Y origins. */
+ int ts_x_origin, ts_y_origin;
+};
+
+/* X-like graphics context structure. This is implemented in
+ EmacsGC.java, but a copy is kept here to avoid sending changes all
+ the time. */
+
+struct android_gc
+{
+ /* Array of clip rectangles. */
+ struct android_rectangle *clip_rects;
+
+ /* Number of clip rectangles. When -1, it means clipping should not
+ be applied. */
+ int num_clip_rects;
+
+ /* The Java-side handle. */
+ android_gcontext gcontext;
+
+ /* Current foreground color. */
+ unsigned long foreground;
+
+ /* Current background color. */
+ unsigned long background;
+};
+
+enum android_swap_action
+ {
+ ANDROID_COPIED,
+ };
+
+enum android_shape
+ {
+ ANDROID_CONVEX,
+ };
+
+enum android_coord_mode
+ {
+ ANDROID_COORD_MODE_ORIGIN,
+ };
+
+struct android_swap_info
+{
+ /* The window to swap. */
+ android_window swap_window;
+
+ /* Unused field present only for consistency with X. */
+ enum android_swap_action swap_action;
+};
+
+#define NativeRectangle Emacs_Rectangle
+#define CONVERT_TO_NATIVE_RECT(xr, nr) ((xr) = (nr))
+#define CONVERT_FROM_EMACS_RECT(xr, nr) ((nr) = (xr))
+
+#define STORE_NATIVE_RECT(nr, rx, ry, rwidth, rheight) \
+ ((nr).x = (rx), (nr).y = (ry), \
+ (nr).width = (rwidth), (nr).height = (rheight)) \
+
+#define ForgetGravity 0
+#define NorthWestGravity 1
+#define NorthGravity 2
+#define NorthEastGravity 3
+#define WestGravity 4
+#define CenterGravity 5
+#define EastGravity 6
+#define SouthWestGravity 7
+#define SouthGravity 8
+#define SouthEastGravity 9
+#define StaticGravity 10
+
+#define NoValue 0x0000
+#define XValue 0x0001
+#define YValue 0x0002
+#define WidthValue 0x0004
+#define HeightValue 0x0008
+#define AllValues 0x000F
+#define XNegative 0x0010
+#define YNegative 0x0020
+
+#define USPosition (1L << 0) /* user specified x, y */
+#define USSize (1L << 1) /* user specified width, height */
+#define PPosition (1L << 2) /* program specified position */
+#define PSize (1L << 3) /* program specified size */
+#define PMinSize (1L << 4) /* program specified minimum size */
+#define PMaxSize (1L << 5) /* program specified maximum size */
+#define PResizeInc (1L << 6) /* program specified resize increments */
+#define PAspect (1L << 7) /* program specified min, max aspect ratios */
+#define PBaseSize (1L << 8) /* program specified base for incrementing */
+#define PWinGravity (1L << 9) /* program specified window gravity */
+
+#ifndef ANDROID_STUBIFY
+
+/* Universal NULL handle. */
+static const int ANDROID_NONE, ANDROID_NO_SYMBOL;
+
+/* Keep these as conceptually close to X as possible: that makes
+ synchronizing code between the ports much easier. */
+
+enum android_event_type
+ {
+ ANDROID_KEY_PRESS,
+ ANDROID_KEY_RELEASE,
+ ANDROID_CONFIGURE_NOTIFY,
+ ANDROID_FOCUS_IN,
+ ANDROID_FOCUS_OUT,
+ ANDROID_WINDOW_ACTION,
+ ANDROID_ENTER_NOTIFY,
+ ANDROID_LEAVE_NOTIFY,
+ ANDROID_MOTION_NOTIFY,
+ ANDROID_BUTTON_PRESS,
+ ANDROID_BUTTON_RELEASE,
+ ANDROID_TOUCH_DOWN,
+ ANDROID_TOUCH_UP,
+ ANDROID_TOUCH_MOVE,
+ ANDROID_WHEEL,
+ ANDROID_ICONIFIED,
+ ANDROID_DEICONIFIED,
+ ANDROID_CONTEXT_MENU,
+ ANDROID_EXPOSE,
+ ANDROID_INPUT_METHOD,
+ };
+
+struct android_any_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+};
+
+enum android_modifier_mask
+ {
+ ANDROID_SHIFT_MASK = 193,
+ ANDROID_CONTROL_MASK = 4096,
+ ANDROID_ALT_MASK = 2,
+ ANDROID_SUPER_MASK = 4,
+ };
+
+struct android_key_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+ unsigned int state;
+ unsigned int keycode;
+
+ /* If this field is -1, then android_lookup_string should be called
+ to retrieve the associated individual characters. */
+ unsigned int unicode_char;
+};
+
+typedef struct android_key_event android_key_pressed_event;
+
+/* These hard coded values are Android modifier keycodes derived
+ through experimentation. */
+
+#define ANDROID_IS_MODIFIER_KEY(key) \
+ ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \
+ || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \
+ || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \
+ || (key) == 63 || (key) == 115)
+
+struct android_configure_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+ int x, y;
+ int width, height;
+};
+
+struct android_focus_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+};
+
+struct android_window_action_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+
+ /* The window handle. This can be ANDROID_NONE. */
+ android_window window;
+
+ /* Numerical identifier for this action. If 0 and WINDOW is set,
+ then it means the frame associated with that window has been
+ destroyed. Otherwise, it means Emacs should create a new
+ frame. */
+ unsigned int action;
+};
+
+struct android_crossing_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+};
+
+struct android_motion_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+};
+
+struct android_button_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+ unsigned int state;
+ unsigned int button;
+};
+
+struct android_expose_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ int width, height;
+};
+
+struct android_touch_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+
+ /* X and Y coordinates of the event. */
+ int x, y;
+
+ /* Time of the event, and the pointer identifier. */
+ unsigned long time;
+
+ /* Index of the pointer being tracked. */
+ unsigned int pointer_id;
+};
+
+struct android_wheel_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+
+ /* X and Y coordinates of the event. */
+ int x, y;
+
+ /* Time of the event, and the pointer identifier. */
+ unsigned long time;
+
+ /* Modifier state at the time of the event. */
+ int state;
+
+ /* Motion alongside the X and Y axes. */
+ double x_delta, y_delta;
+};
+
+struct android_iconify_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+};
+
+struct android_menu_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. Always None. */
+ android_window window;
+
+ /* Menu event ID. */
+ int menu_event_id;
+
+ /* Menu event serial; this counter identifies the context menu. */
+ int menu_event_serial;
+};
+
+enum android_ime_operation
+ {
+ ANDROID_IME_COMMIT_TEXT,
+ ANDROID_IME_DELETE_SURROUNDING_TEXT,
+ ANDROID_IME_FINISH_COMPOSING_TEXT,
+ ANDROID_IME_SET_COMPOSING_TEXT,
+ ANDROID_IME_SET_COMPOSING_REGION,
+ ANDROID_IME_SET_POINT,
+ ANDROID_IME_START_BATCH_EDIT,
+ ANDROID_IME_END_BATCH_EDIT,
+ ANDROID_IME_REQUEST_SELECTION_UPDATE,
+ ANDROID_IME_REQUEST_CURSOR_UPDATES,
+ };
+
+enum
+ {
+ ANDROID_CURSOR_UPDATE_IMMEDIATE = 1,
+ ANDROID_CURSOR_UPDATE_MONITOR = (1 << 1),
+ };
+
+struct android_ime_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* The event serial. */
+ unsigned long serial;
+
+ /* The associated window. */
+ android_window window;
+
+ /* What operation is being performed. */
+ enum android_ime_operation operation;
+
+ /* The details of the operation. START and END provide buffer
+ indices, and may actually mean ``left'' and ``right''. */
+ ptrdiff_t start, end, position;
+
+ /* The number of characters in TEXT.
+
+ If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is
+ actually the cursor update mode associated with that
+ operation. */
+ size_t length;
+
+ /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced
+ UTF-16 encoded text that must be decoded by Emacs.
+
+ POSITION is where point should end up after the text is
+ committed, relative to TEXT. If POSITION is less than 0, it is
+ relative to TEXT's start; otherwise, it is relative to its
+ end. */
+ unsigned short *text;
+
+ /* Value to set the counter to after the operation completes. */
+ unsigned long counter;
+};
+
+union android_event
+{
+ enum android_event_type type;
+ struct android_any_event xany;
+ struct android_key_event xkey;
+ struct android_configure_event xconfigure;
+ struct android_focus_event xfocus;
+ struct android_window_action_event xaction;
+ struct android_crossing_event xcrossing;
+ struct android_motion_event xmotion;
+ struct android_button_event xbutton;
+ struct android_expose_event xexpose;
+
+ /* This has no parallel in X, since the X model of having
+ monotonically increasing touch IDs can't work on Android. */
+ struct android_touch_event touch;
+
+ /* This has no parallel in X outside the X Input Extension, and
+ emulating the input extension interface would be awfully
+ complicated. */
+ struct android_wheel_event wheel;
+
+ /* This has no parallel in X because Android doesn't have window
+ properties. */
+ struct android_iconify_event iconified;
+
+ /* This is only used to transmit selected menu items. */
+ struct android_menu_event menu;
+
+ /* This is used to dispatch input method editing requests. */
+ struct android_ime_event ime;
+};
+
+enum
+ {
+ ANDROID_CURRENT_TIME = 0L,
+ };
+
+enum android_lookup_status
+ {
+ ANDROID_BUFFER_OVERFLOW,
+ ANDROID_LOOKUP_NONE,
+ ANDROID_LOOKUP_CHARS,
+ ANDROID_LOOKUP_KEYSYM,
+ ANDROID_LOOKUP_BOTH,
+ };
+
+enum android_ic_mode
+ {
+ ANDROID_IC_MODE_NULL = 0,
+ ANDROID_IC_MODE_ACTION = 1,
+ ANDROID_IC_MODE_TEXT = 2,
+ };
+
+extern int android_pending (void);
+extern void android_next_event (union android_event *);
+extern bool android_check_if_event (union android_event *,
+ bool (*) (union android_event *,
+ void *),
+ void *);
+
+extern android_window android_create_window (android_window, int,
+ int, int, int,
+ enum android_window_value_mask,
+ struct
+ android_set_window_attributes *);
+extern void android_change_window_attributes (android_window,
+ enum android_window_value_mask,
+ struct
+ android_set_window_attributes *);
+extern void android_set_window_background (android_window, unsigned long);
+extern void android_destroy_window (android_window);
+extern void android_reparent_window (android_window, android_window,
+ int, int);
+extern void android_set_clip_rectangles (struct android_gc *,
+ int, int,
+ struct android_rectangle *,
+ int);
+extern void android_change_gc (struct android_gc *,
+ enum android_gc_value_mask,
+ struct android_gc_values *);
+
+extern void android_clear_window (android_window);
+extern void android_map_window (android_window);
+extern void android_unmap_window (android_window);
+extern void android_resize_window (android_window, unsigned int,
+ unsigned int);
+extern void android_move_window (android_window, int, int);
+extern void android_swap_buffers (struct android_swap_info *, int);
+extern void android_get_gc_values (struct android_gc *,
+ enum android_gc_value_mask,
+ struct android_gc_values *);
+extern void android_set_foreground (struct android_gc *,
+ unsigned long);
+extern void android_fill_rectangle (android_drawable, struct android_gc *,
+ int, int, unsigned int, unsigned int);
+extern android_pixmap android_create_pixmap_from_bitmap_data (char *,
+ unsigned int,
+ unsigned int,
+ unsigned long,
+ unsigned long,
+ unsigned int);
+extern void android_set_clip_mask (struct android_gc *, android_pixmap);
+extern void android_set_fill_style (struct android_gc *,
+ enum android_fill_style);
+extern void android_copy_area (android_drawable, android_drawable,
+ struct android_gc *, int, int,
+ unsigned int, unsigned int, int, int);
+extern void android_free_pixmap (android_drawable);
+
+extern void android_set_background (struct android_gc *, unsigned long);
+extern void android_fill_polygon (android_drawable, struct android_gc *,
+ struct android_point *, int,
+ enum android_shape,
+ enum android_coord_mode);
+extern void android_draw_rectangle (android_drawable, struct android_gc *,
+ int, int, unsigned int, unsigned int);
+extern void android_draw_point (android_window, struct android_gc *,
+ int, int);
+extern void android_draw_line (android_window, struct android_gc *,
+ int, int, int, int);
+extern android_pixmap android_create_pixmap (unsigned int, unsigned int,
+ int);
+extern void android_set_ts_origin (struct android_gc *, int, int);
+extern void android_clear_area (android_window, int, int, unsigned int,
+ unsigned int);
+extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
+ unsigned int);
+
+extern void android_bell (void);
+extern void android_set_input_focus (android_window, unsigned long);
+extern void android_raise_window (android_window);
+extern void android_lower_window (android_window);
+extern int android_query_tree (android_window, android_window *,
+ android_window *, android_window **,
+ unsigned int *);
+extern void android_get_geometry (android_window, android_window *,
+ int *, int *, unsigned int *,
+ unsigned int *, unsigned int *);
+extern void android_move_resize_window (android_window, int, int,
+ unsigned int, unsigned int);
+extern void android_map_raised (android_window);
+extern void android_translate_coordinates (android_window, int,
+ int, int *, int *);
+extern int android_wc_lookup_string (android_key_pressed_event *,
+ wchar_t *, int, int *,
+ enum android_lookup_status *);
+extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
+ ptrdiff_t, ptrdiff_t);
+extern void android_reset_ic (android_window, enum android_ic_mode);
+extern void android_update_extracted_text (android_window, void *,
+ int);
+extern void android_update_cursor_anchor_info (android_window, float,
+ float, float, float);
+extern int android_set_fullscreen (android_window, bool);
+
+enum android_cursor_shape
+ {
+ ANDROID_XC_XTERM = 1008,
+ ANDROID_XC_LEFT_PTR = 1000,
+ ANDROID_XC_WATCH = 1004,
+ ANDROID_XC_HAND2 = 1002,
+ ANDROID_XC_SB_H_DOUBLE_ARROW = 1014,
+ ANDROID_XC_SB_V_DOUBLE_ARROW = 1015,
+ ANDROID_XC_LEFT_SIDE = 1020,
+ ANDROID_XC_TOP_LEFT_CORNER = 1020,
+ ANDROID_XC_TOP_SIDE = 1020,
+ ANDROID_XC_TOP_RIGHT_CORNER = 1020,
+ ANDROID_XC_RIGHT_SIDE = 1020,
+ ANDROID_XC_BOTTOM_RIGHT_CORNER = 1020,
+ ANDROID_XC_BOTTOM_SIDE = 1020,
+ ANDROID_XC_BOTTOM_LEFT_CORNER = 1020,
+ ANDROID_XC_NULL = 0,
+ };
+
+extern android_cursor android_create_font_cursor (enum android_cursor_shape);
+extern void android_define_cursor (android_window, android_cursor);
+extern void android_free_cursor (android_cursor);
+
+#endif
+
+
+
+/* Image support. Keep the API as similar to XImage as possible. To
+ avoid leaving a huge mess of "#ifndef ANDROID_STUBIFY" in image.c,
+ stubs should be defined for all functions. */
+
+enum android_image_format
+ {
+ ANDROID_Z_PIXMAP,
+ };
+
+struct android_image
+{
+ int width, height;
+ enum android_image_format format;
+ char *data;
+ int depth;
+ int bytes_per_line;
+ int bits_per_pixel;
+};
+
+extern struct android_image *android_create_image (unsigned int,
+ enum android_image_format,
+ char *, unsigned int,
+ unsigned int);
+extern void android_destroy_image (struct android_image *);
+
+extern void android_put_pixel (struct android_image *, int, int,
+ unsigned long);
+extern unsigned long android_get_pixel (struct android_image *, int, int);
+extern struct android_image *android_get_image (android_drawable,
+ enum android_image_format);
+extern void android_put_image (android_pixmap, struct android_image *);
+
+
+/* Native image transforms. */
+
+/* 3x2 matrix describing a projective transform. See
+ android_transform_coordinates for details. */
+
+struct android_transform
+{
+ float m1, m2, m3;
+ float m4, m5, m6;
+};
+
+extern void android_project_image_bilinear (struct android_image *,
+ struct android_image *,
+ struct android_transform *);
+extern void android_project_image_nearest (struct android_image *,
+ struct android_image *,
+ struct android_transform *);
+
+
+
+/* X emulation stuff also needed while building stubs. */
+
+extern struct android_gc *android_create_gc (enum android_gc_value_mask,
+ struct android_gc_values *);
+extern void android_free_gc (struct android_gc *);
+
+#endif /* _ANDROID_GUI_H_ */
diff --git a/src/androidmenu.c b/src/androidmenu.c
new file mode 100644
index 00000000000..f74e7ca6d99
--- /dev/null
+++ b/src/androidmenu.c
@@ -0,0 +1,829 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "androidterm.h"
+#include "android.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "menu.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Flag indicating whether or not a popup menu has been posted and not
+ yet popped down. */
+
+static int popup_activated_flag;
+
+/* Serial number used to identify which context menu events are
+ associated with the context menu currently being displayed. */
+
+unsigned int current_menu_serial;
+
+int
+popup_activated (void)
+{
+ return popup_activated_flag;
+}
+
+
+
+/* Toolkit menu implementation. */
+
+/* Structure describing the EmacsContextMenu class. */
+
+struct android_emacs_context_menu
+{
+ jclass class;
+ jmethodID create_context_menu;
+ jmethodID add_item;
+ jmethodID add_submenu;
+ jmethodID add_pane;
+ jmethodID parent;
+ jmethodID display;
+ jmethodID dismiss;
+};
+
+/* Identifiers associated with the EmacsContextMenu class. */
+static struct android_emacs_context_menu menu_class;
+
+static void
+android_init_emacs_context_menu (void)
+{
+ jclass old;
+
+ menu_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsContextMenu");
+ eassert (menu_class.class);
+
+ old = menu_class.class;
+ menu_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!menu_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+ FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
+ "(Ljava/lang/String;)"
+ "Lorg/gnu/emacs/EmacsContextMenu;");
+
+ FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ"
+ "Ljava/lang/String;Z)V");
+ FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
+ "Ljava/lang/String;Ljava/lang/String;)"
+ "Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V");
+ FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z");
+ FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static void
+android_unwind_local_frame (void)
+{
+ (*android_java_env)->PopLocalFrame (android_java_env, NULL);
+}
+
+/* Push a local reference frame to the JVM stack and record it on the
+ specpdl. Release local references created within that frame when
+ the specpdl is unwound past where it is after returning. */
+
+static void
+android_push_local_frame (void)
+{
+ int rc;
+
+ rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
+
+ /* This means the JVM ran out of memory. */
+ if (rc < 1)
+ android_exception_check ();
+
+ record_unwind_protect_void (android_unwind_local_frame);
+}
+
+/* Data for android_dismiss_menu. */
+
+struct android_dismiss_menu_data
+{
+ /* The menu object. */
+ jobject menu;
+
+ /* The window object. */
+ jobject window;
+};
+
+/* Cancel the context menu passed in POINTER. Also, clear
+ popup_activated_flag. */
+
+static void
+android_dismiss_menu (void *pointer)
+{
+ struct android_dismiss_menu_data *data;
+
+ data = pointer;
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ data->menu,
+ menu_class.dismiss,
+ data->window);
+ popup_activated_flag = 0;
+}
+
+/* Recursively process events until a ANDROID_CONTEXT_MENU event
+ arrives. Then, return the item ID specified in the event in
+ *ID. */
+
+static void
+android_process_events_for_menu (int *id)
+{
+ int blocked;
+
+ /* Set menu_event_id to -1; handle_one_android_event will set it to
+ the event ID upon receiving a context menu event. This can cause
+ a non-local exit. */
+ x_display_list->menu_event_id = -1;
+
+ /* Unblock input completely. */
+ blocked = interrupt_input_blocked;
+ totally_unblock_input ();
+
+ /* Now wait for the menu event ID to change. */
+ while (x_display_list->menu_event_id == -1)
+ {
+ /* Wait for events to become available. */
+ android_wait_event ();
+
+ /* Process pending signals. */
+ process_pending_signals ();
+
+ /* Maybe quit. This is important because the framework (on
+ Android 4.0.3) can sometimes fail to deliver context menu
+ closed events if a submenu was opened, and the user still
+ needs to be able to quit. */
+ maybe_quit ();
+ }
+
+ /* Restore the input block. */
+ interrupt_input_blocked = blocked;
+
+ /* Return the ID. */
+ *id = x_display_list->menu_event_id;
+}
+
+/* Structure describing a ``subprefix'' in the menu. */
+
+struct android_menu_subprefix
+{
+ /* The subprefix above. */
+ struct android_menu_subprefix *last;
+
+ /* The subprefix itself. */
+ Lisp_Object subprefix;
+};
+
+/* Free the subprefixes starting from *DATA. */
+
+static void
+android_free_subprefixes (void *data)
+{
+ struct android_menu_subprefix **head, *subprefix;
+
+ head = data;
+
+ while (*head)
+ {
+ subprefix = *head;
+ *head = subprefix->last;
+
+ xfree (subprefix);
+ }
+}
+
+Lisp_Object
+android_menu_show (struct frame *f, int x, int y, int menuflags,
+ Lisp_Object title, const char **error_name)
+{
+ jobject context_menu, current_context_menu;
+ jobject title_string, help_string, temp;
+ size_t i;
+ Lisp_Object pane_name, prefix;
+ const char *pane_string;
+ specpdl_ref count, count1;
+ Lisp_Object item_name, enable, def, tem, entry, type, selected;
+ Lisp_Object help;
+ jmethodID method;
+ jobject store;
+ bool rc;
+ jobject window;
+ int id, item_id, submenu_depth;
+ struct android_dismiss_menu_data data;
+ struct android_menu_subprefix *subprefix, *temp_subprefix;
+ struct android_menu_subprefix *subprefix_1;
+ bool checkmark;
+ unsigned int serial;
+
+ count = SPECPDL_INDEX ();
+ serial = ++current_menu_serial;
+
+ block_input ();
+
+ /* Push the first local frame. */
+ android_push_local_frame ();
+
+ /* Push the first local frame for the context menu. */
+ title_string = (!NILP (title)
+ ? (jobject) android_build_string (title)
+ : NULL);
+ method = menu_class.create_context_menu;
+ current_context_menu = context_menu
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ menu_class.class,
+ method,
+ title_string);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ /* Push the second local frame for temporaries. */
+ count1 = SPECPDL_INDEX ();
+ android_push_local_frame ();
+
+ /* Iterate over the menu. */
+ i = 0, submenu_depth = 0;
+
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ /* This is the start of a new submenu. However, it can be
+ ignored here. */
+ i += 1;
+ submenu_depth += 1;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ /* This is the end of a submenu. Go back to the previous
+ context menu. */
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.parent);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+ i += 1;
+ submenu_depth -= 1;
+
+ if (!current_context_menu || submenu_depth < 0)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "unbalanced submenu pop in menu_items");
+ emacs_abort ();
+ }
+ }
+ else if (EQ (AREF (menu_items, i), Qt)
+ && submenu_depth != 0)
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ /* This is a new pane. Switch back to the topmost context
+ menu. */
+ if (current_context_menu != context_menu)
+ ANDROID_DELETE_LOCAL_REF (current_context_menu);
+ current_context_menu = context_menu;
+
+ /* Now figure out the title of this pane. */
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ pane_string = (NILP (pane_name)
+ ? "" : SSDATA (pane_name));
+ if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+ pane_string++;
+
+ /* Add the pane. */
+ temp = (*android_java_env)->NewStringUTF (android_java_env,
+ pane_string);
+ android_exception_check ();
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_pane,
+ temp);
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+ type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+ selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+ help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+ /* This is an actual menu item (or submenu). Add it to the
+ menu. */
+
+ if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used
+ && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+ {
+ /* This is a submenu. Add it. */
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_submenu,
+ title_string, NULL,
+ help_string);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+ else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+ /* Ignore this separator item. */
+ ;
+ else
+ {
+ /* Compute the item ID. This is the index of value.
+ Make sure it doesn't overflow. */
+
+ if (!INT_ADD_OK (0, i + MENU_ITEMS_ITEM_VALUE, &item_id))
+ memory_full (i + MENU_ITEMS_ITEM_VALUE * sizeof (Lisp_Object));
+
+ /* Add this menu item with the appropriate state. */
+
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ /* Determine whether or not to display a check box. */
+
+ checkmark = (EQ (type, QCtoggle)
+ || EQ (type, QCradio));
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_item,
+ (jint) item_id,
+ title_string,
+ (jboolean) !NILP (enable),
+ (jboolean) checkmark,
+ (jboolean) !NILP (selected),
+ help_string,
+ (jboolean) (EQ (type,
+ QCradio)));
+ android_exception_check ();
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The menu has now been built. Pop the second local frame. */
+ unbind_to (count1, Qnil);
+
+ /* Now, display the context menu. */
+ window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
+ ANDROID_HANDLE_WINDOW);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ context_menu,
+ menu_class.display,
+ window, (jint) x,
+ (jint) y,
+ (jint) serial);
+ android_exception_check ();
+
+ if (!rc)
+ /* This means displaying the menu failed. */
+ goto finish;
+
+ /* Make sure the context menu is always dismissed. */
+ data.menu = context_menu;
+ data.window = window;
+ record_unwind_protect_ptr (android_dismiss_menu, &data);
+
+ /* Next, process events waiting for something to be selected. */
+ popup_activated_flag = 1;
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ /* This means no menu item was selected. */
+ goto finish;
+
+ /* This means the id is invalid. */
+ if (id >= ASIZE (menu_items))
+ goto finish;
+
+ /* Now return the menu item at that location. */
+ tem = Qnil;
+ subprefix = NULL;
+ record_unwind_protect_ptr (android_free_subprefixes, &subprefix);
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+
+ prefix = entry = Qnil;
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ temp_subprefix = xmalloc (sizeof *temp_subprefix);
+ temp_subprefix->last = subprefix;
+ subprefix = temp_subprefix;
+ subprefix->subprefix = prefix;
+
+ prefix = entry;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ prefix = subprefix->subprefix;
+ temp_subprefix = subprefix->last;
+ xfree (subprefix);
+ subprefix = temp_subprefix;
+
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ prefix
+ = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (i + MENU_ITEMS_ITEM_VALUE == id)
+ {
+ if (menuflags & MENU_KEYMAPS)
+ {
+ entry = list1 (entry);
+
+ if (!NILP (prefix))
+ entry = Fcons (prefix, entry);
+
+ for (subprefix_1 = subprefix; subprefix_1;
+ subprefix_1 = subprefix_1->last)
+ if (!NILP (subprefix_1->subprefix))
+ entry = Fcons (subprefix_1->subprefix, entry);
+ }
+
+ tem = entry;
+ }
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ unblock_input ();
+ return unbind_to (count, tem);
+
+ finish:
+ unblock_input ();
+ return unbind_to (count, Qnil);
+}
+
+
+
+/* Toolkit dialog implementation. */
+
+/* Structure describing the EmacsDialog class. */
+
+struct android_emacs_dialog
+{
+ jclass class;
+ jmethodID create_dialog;
+ jmethodID add_button;
+ jmethodID display;
+};
+
+/* Identifiers associated with the EmacsDialog class. */
+static struct android_emacs_dialog dialog_class;
+
+static void
+android_init_emacs_dialog (void)
+{
+ jclass old;
+
+ dialog_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDialog");
+ eassert (dialog_class.class);
+
+ old = dialog_class.class;
+ dialog_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!dialog_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+ eassert (dialog_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+
+ FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
+ "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;");
+ FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
+ FIND_METHOD (display, "display", "()Z");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static Lisp_Object
+android_dialog_show (struct frame *f, Lisp_Object title,
+ Lisp_Object header, const char **error_name)
+{
+ specpdl_ref count;
+ jobject dialog, java_header, java_title, temp;
+ size_t i;
+ Lisp_Object item_name, enable, entry;
+ bool rc;
+ int id;
+ jmethodID method;
+ unsigned int serial;
+
+ /* Generate a unique ID for events from this dialog box. */
+ serial = ++current_menu_serial;
+
+ if (menu_items_n_panes > 1)
+ {
+ *error_name = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
+ /* Do the initial setup. */
+ count = SPECPDL_INDEX ();
+ *error_name = NULL;
+
+ android_push_local_frame ();
+
+ /* Figure out what header to use. */
+ java_header = (!NILP (header)
+ ? android_build_jstring ("Information")
+ : android_build_jstring ("Question"));
+
+ /* And the title. */
+ java_title = android_build_string (title);
+
+ /* Now create the dialog. */
+ method = dialog_class.create_dialog;
+ dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ dialog_class.class,
+ method, java_header,
+ java_title,
+ (jint) serial);
+ android_exception_check ();
+
+ /* Delete now unused local references. */
+ if (java_header)
+ ANDROID_DELETE_LOCAL_REF (java_header);
+ ANDROID_DELETE_LOCAL_REF (java_title);
+
+ /* Create the buttons. */
+ i = MENU_ITEMS_PANE_LENGTH;
+ while (i < menu_items_used)
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+
+ /* Verify that there is no submenu here. */
+
+ if (NILP (item_name))
+ {
+ *error_name = "Submenu in dialog items";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Skip past boundaries between buttons on different sides. The
+ Android toolkit is too silly to understand this
+ distinction. */
+
+ if (EQ (item_name, Qquote))
+ ++i;
+ else
+ {
+ /* Make sure i is within bounds. */
+ if (i > TYPE_MAXIMUM (jint))
+ {
+ *error_name = "Dialog box too big";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Add the button. */
+ temp = android_build_string (item_name);
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ dialog,
+ dialog_class.add_button,
+ temp, (jint) i,
+ (jboolean) NILP (enable));
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The dialog is now built. Run it. */
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ dialog,
+ dialog_class.display);
+ android_exception_check ();
+
+ if (!rc)
+ quit ();
+
+ /* Wait for the menu ID to arrive. */
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ quit ();
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (EQ (AREF (menu_items, i), Qt))
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ /* This is the boundary between left-side elts and right-side
+ elts. */
+ ++i;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (id == i)
+ return entry;
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ return Qnil;
+}
+
+Lisp_Object
+android_popup_dialog (struct frame *f, Lisp_Object header,
+ Lisp_Object contents)
+{
+ Lisp_Object title;
+ const char *error_name;
+ Lisp_Object selection;
+ specpdl_ref specpdl_count = SPECPDL_INDEX ();
+
+ check_window_system (f);
+
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title);
+ record_unwind_protect_void (unuse_menu_items);
+
+ if (NILP (Fcar (Fcdr (contents))))
+ /* No buttons specified, add an "Ok" button so users can pop down
+ the dialog. */
+ contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+ list_of_panes (list1 (contents));
+
+ /* Display them in a dialog box. */
+ block_input ();
+ selection = android_dialog_show (f, title, header, &error_name);
+ unblock_input ();
+
+ unbind_to (specpdl_count, Qnil);
+ discard_menu_items ();
+
+ if (error_name)
+ error ("%s", error_name);
+
+ return selection;
+}
+
+#else
+
+int
+popup_activated (void)
+{
+ return 0;
+}
+
+#endif
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p,
+ Smenu_or_popup_active_p, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+ return (popup_activated ()) ? Qt : Qnil;
+}
+
+void
+init_androidmenu (void)
+{
+#ifndef ANDROID_STUBIFY
+ android_init_emacs_context_menu ();
+ android_init_emacs_dialog ();
+#endif
+}
+
+void
+syms_of_androidmenu (void)
+{
+ defsubr (&Smenu_or_popup_active_p);
+}
diff --git a/src/androidselect.c b/src/androidselect.c
new file mode 100644
index 00000000000..54c712ca93b
--- /dev/null
+++ b/src/androidselect.c
@@ -0,0 +1,499 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <minmax.h>
+#include <unistd.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "android.h"
+#include "androidterm.h"
+
+/* Selection support on Android is confined to copying and pasting of
+ plain text and MIME data from the clipboard. There is no primary
+ selection.
+
+ While newer versions of Android are supposed to have the necessary
+ interfaces for transferring other kinds of selection data, doing so
+ is too complicated, and involves registering ``content providers''
+ and all kinds of other stuff; for this reason, Emacs does not
+ support setting the clipboard contents to anything other than plain
+ text. */
+
+
+
+/* Structure describing the EmacsClipboard class. */
+
+struct android_emacs_clipboard
+{
+ jclass class;
+ jmethodID set_clipboard;
+ jmethodID owns_clipboard;
+ jmethodID clipboard_exists;
+ jmethodID get_clipboard;
+ jmethodID make_clipboard;
+ jmethodID get_clipboard_targets;
+ jmethodID get_clipboard_data;
+};
+
+/* Methods associated with the EmacsClipboard class. */
+static struct android_emacs_clipboard clipboard_class;
+
+/* Reference to the EmacsClipboard object. */
+static jobject clipboard;
+
+
+
+static void
+android_init_emacs_clipboard (void)
+{
+ jclass old;
+
+ clipboard_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsClipboard");
+ eassert (clipboard_class.class);
+
+ old = clipboard_class.class;
+ clipboard_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!clipboard_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ clipboard_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ clipboard_class.class, \
+ name, signature); \
+ assert (clipboard_class.c_name);
+
+ FIND_METHOD (set_clipboard, "setClipboard", "([B)V");
+ FIND_METHOD (owns_clipboard, "ownsClipboard", "()I");
+ FIND_METHOD (clipboard_exists, "clipboardExists", "()Z");
+ FIND_METHOD (get_clipboard, "getClipboard", "()[B");
+ FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
+ "()[[B");
+ FIND_METHOD (get_clipboard_data, "getClipboardData",
+ "([B)[J");
+
+ clipboard_class.make_clipboard
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ clipboard_class.class,
+ "makeClipboard",
+ "()Lorg/gnu/emacs/"
+ "EmacsClipboard;");
+ assert (clipboard_class.make_clipboard);
+
+#undef FIND_METHOD
+}
+
+
+
+
+DEFUN ("android-clipboard-owner-p", Fandroid_clipboard_owner_p,
+ Sandroid_clipboard_owner_p, 0, 0, 0,
+ doc: /* Return whether or not Emacs owns the clipboard.
+Alternatively, return the symbol `lambda' if that could not be
+determined. */)
+ (void)
+{
+ jint rc;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ block_input ();
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ clipboard,
+ clipboard_class.owns_clipboard);
+ android_exception_check ();
+ unblock_input ();
+
+ /* If rc is 0 or 1, then Emacs knows whether or not it owns the
+ clipboard. If rc is -1, then Emacs does not. */
+
+ if (rc < 0)
+ return Qlambda;
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
+ Sandroid_set_clipboard, 1, 1, 0,
+ doc: /* Set the clipboard text to STRING. */)
+ (Lisp_Object string)
+{
+ jarray bytes;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ CHECK_STRING (string);
+ string = ENCODE_UTF_8 (string);
+
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (string));
+ android_exception_check ();
+
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (string),
+ (jbyte *) SDATA (string));
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ clipboard,
+ clipboard_class.set_clipboard,
+ bytes);
+ android_exception_check_1 (bytes);
+
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ return Qnil;
+}
+
+DEFUN ("android-get-clipboard", Fandroid_get_clipboard,
+ Sandroid_get_clipboard, 0, 0, 0,
+ doc: /* Return the current contents of the clipboard.
+Value is a multibyte string containing decoded clipboard
+text.
+Alternatively, return nil if the clipboard is empty. */)
+ (void)
+{
+ Lisp_Object string;
+ jarray bytes;
+ jmethodID method;
+ size_t length;
+ jbyte *data;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ method = clipboard_class.get_clipboard;
+ bytes
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull (data, bytes);
+
+ string = make_unibyte_string ((char *) data, length);
+
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ /* Now decode the resulting string. */
+ return code_convert_string_norecord (string, Qutf_8, Qnil);
+}
+
+DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p,
+ Sandroid_clipboard_exists_p, 0, 0, 0,
+ doc: /* Return whether or not clipboard contents exist. */)
+ (void)
+{
+ jboolean rc;
+ jmethodID method;
+
+ if (!android_init_gui)
+ error ("No Android display connection");
+
+ method = clipboard_class.clipboard_exists;
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-browse-url", Fandroid_browse_url,
+ Sandroid_browse_url, 1, 1, 0,
+ doc: /* Start the system web browser.
+Then, point the web browser to URL, which should be a URL-encoded
+URL with a scheme specified. Signal an error upon failure. */)
+ (Lisp_Object url)
+{
+ Lisp_Object value;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ CHECK_STRING (url);
+ value = android_browse_url (url);
+
+ /* Signal an error upon failure. */
+ if (!NILP (value))
+ signal_error ("Error browsing URL", value);
+
+ return Qnil;
+}
+
+
+
+/* MIME clipboard support. This provides support for reading MIME
+ data (but not text) from the clipboard. */
+
+DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
+ Sandroid_get_clipboard_targets, 0, 0, 0,
+ doc: /* Return a list of data types in the clipboard.
+Value is a list of MIME types as strings, each defining a single extra
+data type available from the clipboard. */)
+ (void)
+{
+ jarray bytes_array;
+ jbyteArray bytes;
+ jmethodID method;
+ size_t length, length1, i;
+ jbyte *data;
+ Lisp_Object targets, tem;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ targets = Qnil;
+ block_input ();
+ method = clipboard_class.get_clipboard_targets;
+ bytes_array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method);
+ android_exception_check ();
+
+ if (!bytes_array)
+ goto fail;
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes_array);
+ for (i = 0; i < length; ++i)
+ {
+ /* Retireve the MIME type. */
+ bytes
+ = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ bytes_array, i);
+ android_exception_check_nonnull (bytes, bytes_array);
+
+ /* Cons it onto the list of targets. */
+ length1 = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull_1 (data, bytes, bytes_array);
+
+ /* Decode the string. */
+ tem = make_unibyte_string ((char *) data, length1);
+ tem = code_convert_string_norecord (tem, Qutf_8, Qnil);
+ targets = Fcons (tem, targets);
+
+ /* Delete the retrieved data. */
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ }
+ unblock_input ();
+
+ ANDROID_DELETE_LOCAL_REF (bytes_array);
+ return Fnreverse (targets);
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+/* Free the memory inside PTR, a pointer to a char pointer. */
+
+static void
+android_xfree_inside (void *ptr)
+{
+ xfree (*(char **) ptr);
+}
+
+DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
+ Sandroid_get_clipboard_data, 1, 1, 0,
+ doc: /* Return the clipboard data of the given MIME TYPE.
+Value is a unibyte string containing the entire contents of the
+clipboard, after its owner has converted the data to the given
+MIME type. Value is nil if the conversion fails, or if the data
+is not present.
+
+Value is also nil if the clipboard data consists of a single URL which
+does not have any corresponding data. In that case, use
+`android-get-clipboard' instead. */)
+ (Lisp_Object type)
+{
+ jlongArray array;
+ jbyteArray bytes;
+ jmethodID method;
+ int fd;
+ ptrdiff_t rc;
+ jlong offset, length, *longs;
+ specpdl_ref ref;
+ char *buffer, *start;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ /* Encode the string as UTF-8. */
+ CHECK_STRING (type);
+ type = ENCODE_UTF_8 (type);
+
+ /* Then give it to the selection code. */
+ block_input ();
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (type));
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (type),
+ (jbyte *) SDATA (type));
+ android_exception_check ();
+
+ method = clipboard_class.get_clipboard_data;
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method,
+ bytes);
+ android_exception_check_1 (bytes);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ if (!array)
+ goto fail;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_nonnull (longs, array);
+
+ /* longs[0] is the file descriptor.
+ longs[1] is an offset to apply to the file.
+ longs[2] is either -1, or the number of bytes to read from the
+ file. */
+ fd = longs[0];
+ offset = longs[1];
+ length = longs[2];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+ unblock_input ();
+
+ /* Now begin reading from longs[0]. */
+ ref = SPECPDL_INDEX ();
+ record_unwind_protect_int (close_file_unwind, fd);
+
+ if (length != -1)
+ {
+ buffer = xmalloc (MIN (length, PTRDIFF_MAX));
+ record_unwind_protect_ptr (xfree, buffer);
+
+ rc = emacs_read_quit (fd, buffer,
+ MIN (length, PTRDIFF_MAX));
+
+ /* Return nil upon an IO problem. */
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ /* Return the data as a unibyte string. */
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+ }
+
+ /* Otherwise, read BUFSIZ bytes at a time. */
+ buffer = xmalloc (BUFSIZ);
+ length = 0;
+ start = buffer;
+
+ record_unwind_protect_ptr (android_xfree_inside, &buffer);
+
+ /* Seek to the start of the data. */
+
+ if (offset)
+ {
+ if (lseek (fd, offset, SEEK_SET) < 0)
+ return unbind_to (ref, Qnil);
+ }
+
+ while (true)
+ {
+ rc = emacs_read_quit (fd, start, BUFSIZ);
+
+ if (!INT_ADD_OK (rc, length, &length)
+ || PTRDIFF_MAX - length < BUFSIZ)
+ memory_full (PTRDIFF_MAX);
+
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ if (rc < BUFSIZ)
+ break;
+
+ buffer = xrealloc (buffer, length + BUFSIZ);
+ start = buffer + length;
+ }
+
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+
+
+void
+init_androidselect (void)
+{
+ jobject tem;
+ jmethodID make_clipboard;
+
+ if (!android_init_gui)
+ return;
+
+ android_init_emacs_clipboard ();
+
+ make_clipboard = clipboard_class.make_clipboard;
+ tem
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ clipboard_class.class,
+ make_clipboard);
+ if (!tem)
+ emacs_abort ();
+
+ clipboard = (*android_java_env)->NewGlobalRef (android_java_env, tem);
+
+ if (!clipboard)
+ emacs_abort ();
+
+ ANDROID_DELETE_LOCAL_REF (tem);
+}
+
+void
+syms_of_androidselect (void)
+{
+ defsubr (&Sandroid_clipboard_owner_p);
+ defsubr (&Sandroid_set_clipboard);
+ defsubr (&Sandroid_get_clipboard);
+ defsubr (&Sandroid_clipboard_exists_p);
+ defsubr (&Sandroid_browse_url);
+ defsubr (&Sandroid_get_clipboard_targets);
+ defsubr (&Sandroid_get_clipboard_data);
+}
diff --git a/src/androidterm.c b/src/androidterm.c
new file mode 100644
index 00000000000..6f7c06875ca
--- /dev/null
+++ b/src/androidterm.c
@@ -0,0 +1,6073 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <semaphore.h>
+
+#include "lisp.h"
+#include "androidterm.h"
+#include "keyboard.h"
+#include "blockinput.h"
+#include "android.h"
+#include "buffer.h"
+#include "window.h"
+#include "textconv.h"
+#include "coding.h"
+#include "pdumper.h"
+
+/* This is a chain of structures for all the X displays currently in
+ use. */
+
+struct android_display_info *x_display_list;
+
+
+
+/* Android terminal interface functions. */
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Non-zero means that a HELP_EVENT has been generated since Emacs
+ start. */
+
+static bool any_help_event_p;
+
+/* Counters for tallying up scroll wheel events if
+ mwheel_coalesce_scroll_events is true. */
+
+static double wheel_event_x, wheel_event_y;
+
+enum
+ {
+ ANDROID_EVENT_NORMAL,
+ ANDROID_EVENT_GOTO_OUT,
+ ANDROID_EVENT_DROP,
+ };
+
+/* Find the frame whose window has the identifier WDESC.
+
+ This is like x_window_to_frame in xterm.c, except that DPYINFO may
+ be NULL, as there is only at most one Android display, and is only
+ specified in order to stay consistent with X. */
+
+static struct frame *
+android_window_to_frame (struct android_display_info *dpyinfo,
+ android_window wdesc)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ if (wdesc == ANDROID_NONE)
+ return NULL;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ if (!FRAME_ANDROID_P (f))
+ continue;
+
+ if (FRAME_ANDROID_WINDOW (f) == wdesc)
+ return f;
+ }
+
+ return NULL;
+}
+
+static void
+android_clear_frame (struct frame *f)
+{
+ /* Clearing the frame will erase any cursor, so mark them all as no
+ longer visible. */
+ mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
+ android_clear_window (FRAME_ANDROID_DRAWABLE (f));
+}
+
+static void
+android_show_hourglass (struct frame *f)
+{
+ struct android_output *x;
+
+ /* This isn't implemented like X because a window brings alongside
+ too many unneeded resources. */
+
+ x = FRAME_ANDROID_OUTPUT (f);
+
+ /* If the hourglass window is mapped inside a popup menu, input
+ could be lost if the menu is popped down and the grab is
+ relinquished, but the hourglass window is still up. Just
+ avoid displaying the hourglass at all while popups are
+ active. */
+
+ if (popup_activated ())
+ return;
+
+ x->hourglass = true;
+
+ if (!f->pointer_invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ x->hourglass_cursor);
+}
+
+static void
+android_hide_hourglass (struct frame *f)
+{
+ struct android_output *x;
+
+ x = FRAME_ANDROID_OUTPUT (f);
+ x->hourglass = false;
+
+ if (!f->pointer_invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ x->current_cursor);
+}
+
+static void
+android_flash (struct frame *f)
+{
+ struct android_gc *gc;
+ struct android_gc_values values;
+ int rc;
+ fd_set fds;
+
+ block_input ();
+
+ values.function = ANDROID_GC_XOR;
+ values.foreground = (FRAME_FOREGROUND_PIXEL (f)
+ ^ FRAME_BACKGROUND_PIXEL (f));
+
+ gc = android_create_gc ((ANDROID_GC_FUNCTION
+ | ANDROID_GC_FOREGROUND),
+ &values);
+
+ /* Get the height not including a menu bar widget. */
+ int height = FRAME_PIXEL_HEIGHT (f);
+ /* Height of each line to flash. */
+ int flash_height = FRAME_LINE_HEIGHT (f);
+ /* These will be the left and right margins of the rectangles. */
+ int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = flash_right - flash_left;
+
+ /* If window is tall, flash top and bottom line. */
+ if (height > 3 * FRAME_LINE_HEIGHT (f))
+ {
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (FRAME_INTERNAL_BORDER_WIDTH (f)
+ + FRAME_TOP_MARGIN_HEIGHT (f)),
+ width, flash_height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (height - flash_height
+ - FRAME_INTERNAL_BORDER_WIDTH (f)),
+ width, flash_height);
+
+ }
+ else
+ /* If it is short, flash it all. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+ width, (height - 2
+ * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+ flush_frame (f);
+
+ struct timespec delay = make_timespec (0, 150 * 1000 * 1000);
+ struct timespec wakeup = timespec_add (current_timespec (), delay);
+
+ /* Keep waiting until past the time wakeup or any input gets
+ available. */
+ while (! detect_input_pending ())
+ {
+ struct timespec current = current_timespec ();
+ struct timespec timeout;
+
+ /* Break if result would not be positive. */
+ if (timespec_cmp (wakeup, current) <= 0)
+ break;
+
+ /* How long `select' should wait. */
+ timeout = make_timespec (0, 10 * 1000 * 1000);
+
+ /* Wait for some input to become available on the X
+ connection. */
+ FD_ZERO (&fds);
+
+ /* Try to wait that long--but we might wake up sooner. */
+ rc = pselect (0, &fds, NULL, NULL, &timeout, NULL);
+
+ /* Some input is available, exit the visible bell. */
+ if (rc >= 0)
+ break;
+ }
+
+ /* If window is tall, flash top and bottom line. */
+ if (height > 3 * FRAME_LINE_HEIGHT (f))
+ {
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (FRAME_INTERNAL_BORDER_WIDTH (f)
+ + FRAME_TOP_MARGIN_HEIGHT (f)),
+ width, flash_height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (height - flash_height
+ - FRAME_INTERNAL_BORDER_WIDTH (f)),
+ width, flash_height);
+ }
+ else
+ /* If it is short, flash it all. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+ width, (height - 2
+ * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+ android_free_gc (gc);
+ flush_frame (f);
+
+ unblock_input ();
+}
+
+static void
+android_ring_bell (struct frame *f)
+{
+ if (visible_bell)
+ android_flash (f);
+ else
+ {
+ block_input ();
+ android_bell ();
+ unblock_input ();
+ }
+}
+
+static android_cursor
+make_invisible_cursor (struct android_display_info *dpyinfo)
+{
+ return android_create_font_cursor (ANDROID_XC_NULL);
+}
+
+static void
+android_toggle_visible_pointer (struct frame *f, bool invisible)
+{
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ if (!dpyinfo->invisible_cursor)
+ dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo);
+
+ if (invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ dpyinfo->invisible_cursor);
+ else
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ (FRAME_ANDROID_OUTPUT (f)->hourglass
+ ? f->output_data.android->hourglass_cursor
+ : f->output_data.android->current_cursor));
+
+ f->pointer_invisible = invisible;
+}
+
+static void
+android_toggle_invisible_pointer (struct frame *f, bool invisible)
+{
+ block_input ();
+ android_toggle_visible_pointer (f, invisible);
+ unblock_input ();
+}
+
+/* Start an update of frame F. This function is installed as a hook
+ for update_begin, i.e. it is called when update_begin is called.
+ This function is called prior to calls to gui_update_window_begin
+ for each window being updated. Currently, there is nothing to do
+ here because all interesting stuff is done on a window basis. */
+
+static void
+android_update_begin (struct frame *f)
+{
+ /* The frame is no longer complete, as it is in the midst of an
+ update. */
+ FRAME_ANDROID_COMPLETE_P (f) = false;
+}
+
+/* End update of frame F. This function is installed as a hook in
+ update_end. */
+
+static void
+android_update_end (struct frame *f)
+{
+ /* Mouse highlight may be displayed again. */
+ MOUSE_HL_INFO (f)->mouse_face_defer = false;
+}
+
+static void
+show_back_buffer (struct frame *f)
+{
+ struct android_swap_info swap_info;
+
+ memset (&swap_info, 0, sizeof (swap_info));
+ swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
+ swap_info.swap_action = ANDROID_COPIED;
+ android_swap_buffers (&swap_info, 1);
+
+ /* Now the back buffer no longer needs to be flipped. */
+ FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false;
+}
+
+/* Flip back buffers on F if it has undrawn content. */
+
+static void
+android_flush_dirty_back_buffer_on (struct frame *f)
+{
+ if (FRAME_GARBAGED_P (f)
+ || buffer_flipping_blocked_p ()
+ /* If the frame is not already up to date, do not flush buffers
+ on input, as that will result in flicker. */
+ || !FRAME_ANDROID_COMPLETE_P (f)
+ || !FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ return;
+
+ show_back_buffer (f);
+}
+
+/* Convert between the modifier bits Android uses and the modifier
+ bits Emacs uses. */
+
+static int
+android_android_to_emacs_modifiers (struct android_display_info *dpyinfo,
+ int state)
+{
+ return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0
+ | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0
+ | (state & ANDROID_ALT_MASK) ? meta_modifier : 0
+ | (state & ANDROID_SUPER_MASK) ? super_modifier : 0);
+}
+
+static int
+android_emacs_to_android_modifiers (struct android_display_info *dpyinfo,
+ intmax_t state)
+{
+ return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0
+ | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0
+ | (state & meta_modifier) ? ANDROID_ALT_MASK : 0
+ | (state & super_modifier) ? ANDROID_SUPER_MASK : 0);
+}
+
+static void android_frame_rehighlight (struct android_display_info *);
+
+static void
+android_lower_frame (struct frame *f)
+{
+ android_lower_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_raise_frame (struct frame *f)
+{
+ android_raise_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_new_focus_frame (struct android_display_info *dpyinfo,
+ struct frame *frame)
+{
+ struct frame *old_focus;
+
+ old_focus = dpyinfo->focus_frame;
+
+ if (frame != dpyinfo->focus_frame)
+ {
+ /* Set this before calling other routines, so that they see
+ the correct value of x_focus_frame. */
+ dpyinfo->focus_frame = frame;
+
+ if (old_focus && old_focus->auto_lower)
+ android_lower_frame (old_focus);
+
+ if (dpyinfo->focus_frame && dpyinfo->focus_frame->auto_raise)
+ dpyinfo->pending_autoraise_frame = dpyinfo->focus_frame;
+ else
+ dpyinfo->pending_autoraise_frame = NULL;
+ }
+
+ android_frame_rehighlight (dpyinfo);
+}
+
+static void
+android_focus_changed (int type, int state,
+ struct android_display_info *dpyinfo,
+ struct frame *frame, struct input_event *bufp)
+{
+ if (type == ANDROID_FOCUS_IN)
+ {
+ if (dpyinfo->x_focus_event_frame != frame)
+ {
+ android_new_focus_frame (dpyinfo, frame);
+ dpyinfo->x_focus_event_frame = frame;
+ bufp->kind = FOCUS_IN_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ frame->output_data.android->focus_state |= state;
+ }
+ else if (type == ANDROID_FOCUS_OUT)
+ {
+ frame->output_data.android->focus_state &= ~state;
+
+ if (dpyinfo->x_focus_event_frame == frame)
+ {
+ dpyinfo->x_focus_event_frame = 0;
+ android_new_focus_frame (dpyinfo, 0);
+
+ bufp->kind = FOCUS_OUT_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ if (frame->pointer_invisible)
+ android_toggle_invisible_pointer (frame, false);
+ }
+}
+
+static void
+android_detect_focus_change (struct android_display_info *dpyinfo,
+ struct frame *frame,
+ union android_event *event,
+ struct input_event *bufp)
+{
+ if (!frame)
+ return;
+
+ switch (event->type)
+ {
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_focus_changed (event->type, FOCUS_EXPLICIT,
+ dpyinfo, frame, bufp);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static bool
+android_note_mouse_movement (struct frame *frame,
+ struct android_motion_event *event)
+{
+ struct android_display_info *dpyinfo;
+ Emacs_Rectangle *r;
+
+ if (!FRAME_ANDROID_OUTPUT (frame))
+ return false;
+
+ dpyinfo = FRAME_DISPLAY_INFO (frame);
+ dpyinfo->last_mouse_motion_frame = frame;
+ dpyinfo->last_mouse_motion_x = event->x;
+ dpyinfo->last_mouse_motion_y = event->y;
+ dpyinfo->last_mouse_movement_time = event->time;
+
+ /* Has the mouse moved off the glyph it was on at the last sighting? */
+ r = &dpyinfo->last_mouse_glyph;
+ if (frame != dpyinfo->last_mouse_glyph_frame
+ || event->x < r->x || event->x >= r->x + r->width
+ || event->y < r->y || event->y >= r->y + r->height)
+ {
+ frame->mouse_moved = true;
+ note_mouse_highlight (frame, event->x, event->y);
+ /* Remember which glyph we're now on. */
+ remember_mouse_glyph (frame, event->x, event->y, r);
+ dpyinfo->last_mouse_glyph_frame = frame;
+ return true;
+ }
+
+ return false;
+}
+
+static struct frame *
+mouse_or_wdesc_frame (struct android_display_info *dpyinfo, int wdesc)
+{
+ struct frame *lm_f = (gui_mouse_grabbed (dpyinfo)
+ ? dpyinfo->last_mouse_frame
+ : NULL);
+
+ if (lm_f && !EQ (track_mouse, Qdropping)
+ && !EQ (track_mouse, Qdrag_source))
+ return lm_f;
+ else
+ {
+ struct frame *w_f = android_window_to_frame (dpyinfo, wdesc);
+
+ /* Do not return a tooltip frame. */
+ if (!w_f || FRAME_TOOLTIP_P (w_f))
+ return EQ (track_mouse, Qdropping) ? lm_f : NULL;
+ else
+ /* When dropping it would be probably nice to raise w_f
+ here. */
+ return w_f;
+ }
+}
+
+static Lisp_Object
+android_construct_mouse_click (struct input_event *result,
+ struct android_button_event *event,
+ struct frame *f)
+{
+ struct android_display_info *dpyinfo;
+ int x, y;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ x = event->x;
+ y = event->y;
+
+ /* Make the event type NO_EVENT; we'll change that when we decide
+ otherwise. */
+ result->kind = MOUSE_CLICK_EVENT;
+ result->code = event->button - 1;
+ result->timestamp = event->time;
+ result->modifiers = (android_android_to_emacs_modifiers (dpyinfo,
+ event->state)
+ | (event->type == ANDROID_BUTTON_RELEASE
+ ? up_modifier : down_modifier));
+
+ XSETINT (result->x, x);
+ XSETINT (result->y, y);
+ XSETFRAME (result->frame_or_window, f);
+ result->arg = Qnil;
+ return Qnil;
+}
+
+/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME.
+ Return the event in IE. Do not set IE->timestamp, as that is left
+ to the caller. */
+
+static void
+android_update_tools (struct frame *f, struct input_event *ie)
+{
+ struct android_touch_point *touchpoint;
+
+ ie->kind = TOUCHSCREEN_UPDATE_EVENT;
+ XSETFRAME (ie->frame_or_window, f);
+ ie->arg = Qnil;
+
+ /* Build the list of active touches. */
+ for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+ touchpoint; touchpoint = touchpoint->next)
+ {
+ /* Skip touch points which originated on the tool bar. */
+
+ if (touchpoint->tool_bar_p)
+ continue;
+
+ ie->arg = Fcons (list3i (touchpoint->x,
+ touchpoint->y,
+ touchpoint->tool_id),
+ ie->arg);
+ }
+}
+
+/* Find and return an existing tool pressed against FRAME, identified
+ by POINTER_ID. Return NULL if no tool by that ID was found. */
+
+static struct android_touch_point *
+android_find_tool (struct frame *f, int pointer_id)
+{
+ struct android_touch_point *touchpoint;
+
+ for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+ touchpoint; touchpoint = touchpoint->next)
+ {
+ if (touchpoint->tool_id == pointer_id)
+ return touchpoint;
+ }
+
+ return NULL;
+}
+
+/* Decode STRING, an array of N little endian UTF-16 characters, into
+ a Lisp string. Return Qnil if the string is too large, and the
+ encoded string otherwise. */
+
+static Lisp_Object
+android_decode_utf16 (unsigned short *utf16, size_t n)
+{
+ struct coding_system coding;
+ ptrdiff_t size;
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return Qnil;
+
+ /* Set up the coding system. Decoding a UTF-16 string (with no BOM)
+ should not signal. */
+
+ memset (&coding, 0, sizeof coding);
+
+ setup_coding_system (Qutf_16le, &coding);
+ coding.source = (const unsigned char *) utf16;
+ decode_coding_object (&coding, Qnil, 0, 0, size,
+ size, Qt);
+
+ return coding.dst_object;
+}
+
+/* Handle a cursor update request for F from the input method.
+ MODE specifies whether or not an update should be sent immediately,
+ and whether or not they are needed in the future.
+
+ If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of
+ F's old selected window's phys cursor now.
+
+ If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set
+ `need_cursor_updates'. */
+
+static void
+android_request_cursor_updates (struct frame *f, int mode)
+{
+ struct window *w;
+
+ if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE
+ && WINDOWP (WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window))
+ {
+ /* Prefer the old selected window, as its selection is what was
+ reported to the IME previously. */
+
+ w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+
+ /* Now say whether or not updates are needed in the future. */
+ FRAME_OUTPUT_DATA (f)->need_cursor_updates
+ = (mode & ANDROID_CURSOR_UPDATE_MONITOR);
+}
+
+/* Handle a single input method event EVENT, delivered to the frame
+ F.
+
+ Perform the text conversion action specified inside. */
+
+static void
+android_handle_ime_event (union android_event *event, struct frame *f)
+{
+ Lisp_Object text UNINIT;
+
+ /* First, decode the text if necessary. */
+
+ switch (event->ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ text = android_decode_utf16 (event->ime.text,
+ event->ime.length);
+ xfree (event->ime.text);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Finally, perform the appropriate conversion action. */
+
+ switch (event->ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ commit_text (f, text, event->ime.position,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_DELETE_SURROUNDING_TEXT:
+ delete_surrounding_text (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ finish_composing_text (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ set_composing_text (f, text, event->ime.position,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_COMPOSING_REGION:
+ set_composing_region (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_POINT:
+ textconv_set_point_and_mark (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_START_BATCH_EDIT:
+ start_batch_edit (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_END_BATCH_EDIT:
+ end_batch_edit (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_REQUEST_SELECTION_UPDATE:
+ request_point_update (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_REQUEST_CURSOR_UPDATES:
+ android_request_cursor_updates (f, event->ime.length);
+ break;
+ }
+}
+
+static int
+handle_one_android_event (struct android_display_info *dpyinfo,
+ union android_event *event, int *finish,
+ struct input_event *hold_quit)
+{
+ union android_event configureEvent;
+ struct frame *f, *any, *mouse_frame;
+ Mouse_HLInfo *hlinfo;
+ union buffered_input_event inev;
+ int modifiers, count, do_help;
+ struct android_touch_point *touchpoint, **last;
+ Lisp_Object window;
+ int scroll_height;
+ double scroll_unit;
+ int keysym;
+ ptrdiff_t nchars, i;
+ struct window *w;
+
+ /* It is okay for this to not resemble handle_one_xevent so much.
+ Differences in event handling code are much less nasty than
+ stuble differences in the graphics code. */
+
+ do_help = count = 0;
+ hlinfo = &dpyinfo->mouse_highlight;
+ *finish = ANDROID_EVENT_NORMAL;
+ any = android_window_to_frame (dpyinfo, event->xany.window);
+ nchars = 0;
+
+ if (any && any->wait_event_type == event->type)
+ any->wait_event_type = 0; /* Indicates we got it. */
+
+ EVENT_INIT (inev.ie);
+
+ switch (event->type)
+ {
+ case ANDROID_CONFIGURE_NOTIFY:
+ configureEvent = *event;
+
+ f = android_window_to_frame (dpyinfo,
+ configureEvent.xconfigure.window);
+
+ if (!f)
+ goto OTHER;
+
+ if (FRAME_TOOLTIP_P (f))
+ {
+ if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
+ || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width)
+ SET_FRAME_GARBAGED (f);
+
+ FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
+ FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
+ }
+
+ int width = configureEvent.xconfigure.width;
+ int height = configureEvent.xconfigure.height;
+
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("ConfigureNotify"),
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width,
+ f->new_height);
+
+ /* Even if the number of character rows and columns has
+ not changed, the font size may have changed, so we need
+ to check the pixel dimensions as well. */
+
+ if (width != FRAME_PIXEL_WIDTH (f)
+ || height != FRAME_PIXEL_HEIGHT (f)
+ || (f->new_size_p
+ && ((f->new_width >= 0 && width != f->new_width)
+ || (f->new_height >= 0 && height != f->new_height))))
+ {
+ change_frame_size (f, width, height, false, true, false);
+ android_clear_under_internal_border (f);
+ SET_FRAME_GARBAGED (f);
+ cancel_mouse_face (f);
+ }
+
+ /* Now change the left and top position of this window. */
+
+ {
+ int old_left = f->left_pos;
+ int old_top = f->top_pos;
+ Lisp_Object frame;
+
+ XSETFRAME (frame, f);
+
+ {
+ android_window root;
+ unsigned int dummy_uint;
+
+ android_get_geometry (FRAME_ANDROID_WINDOW (f),
+ &root, &f->left_pos, &f->top_pos,
+ &dummy_uint, &dummy_uint,
+ &dummy_uint);
+ }
+
+ if (!FRAME_TOOLTIP_P (f)
+ && (old_left != f->left_pos || old_top != f->top_pos))
+ {
+ inev.ie.kind = MOVE_FRAME_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ }
+
+ if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+ {
+ w = XWINDOW (f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+ }
+
+ goto OTHER;
+
+ case ANDROID_KEY_PRESS:
+
+ /* Set f to any. There are no ``outer windows'' on Android. */
+ f = any;
+
+ /* If mouse-highlight is an integer, input clears out
+ mouse highlighting. */
+ if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+ && (any == 0
+ || !EQ (any->tool_bar_window, hlinfo->mouse_face_window)
+ || !EQ (any->tab_bar_window, hlinfo->mouse_face_window)))
+ {
+ mouse_frame = hlinfo->mouse_face_mouse_frame;
+
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_hidden = true;
+
+ if (mouse_frame)
+ android_flush_dirty_back_buffer_on (mouse_frame);
+ }
+
+ if (!f)
+ goto OTHER;
+
+ wchar_t copy_buffer[129];
+ wchar_t *copy_bufptr = copy_buffer;
+ int copy_bufsiz = 128 * sizeof (wchar_t);
+
+ event->xkey.state
+ |= android_emacs_to_android_modifiers (dpyinfo,
+ extra_keyboard_modifiers);
+ modifiers = event->xkey.state;
+
+ /* Common for all keysym input events. */
+ XSETFRAME (inev.ie.frame_or_window, any);
+ inev.ie.modifiers
+ = android_android_to_emacs_modifiers (dpyinfo, modifiers);
+ inev.ie.timestamp = event->xkey.time;
+
+ keysym = event->xkey.keycode;
+
+ {
+ enum android_lookup_status status_return;
+
+ nchars = android_wc_lookup_string (&event->xkey, copy_bufptr,
+ copy_bufsiz, &keysym,
+ &status_return);
+
+ /* android_lookup_string can't be called twice, so there's no
+ way to recover from buffer overflow. */
+ if (status_return == ANDROID_BUFFER_OVERFLOW)
+ goto done_keysym;
+ else if (status_return == ANDROID_LOOKUP_NONE)
+ {
+ /* Don't skip preedit text events. */
+ if (event->xkey.keycode != (uint32_t) -1)
+ goto done_keysym;
+ }
+ else if (status_return == ANDROID_LOOKUP_CHARS)
+ keysym = ANDROID_NO_SYMBOL;
+ else if (status_return != ANDROID_LOOKUP_KEYSYM
+ && status_return != ANDROID_LOOKUP_BOTH)
+ emacs_abort ();
+
+ /* Deal with pre-edit text events. On Android, these are
+ simply encoded as events with associated strings and a
+ keycode set to ``-1''. */
+
+ if (event->xkey.keycode == (uint32_t) -1)
+ {
+ inev.ie.kind = PREEDIT_TEXT_EVENT;
+ inev.ie.arg = Qnil;
+
+ /* If text was looked up, decode it and make it the
+ preedit text. */
+
+ if (status_return == ANDROID_LOOKUP_CHARS && nchars)
+ {
+ copy_bufptr[nchars] = 0;
+ inev.ie.arg = from_unicode_buffer (copy_bufptr);
+ }
+
+ goto done_keysym;
+ }
+ }
+
+ if (nchars == 1 && copy_bufptr[0] >= 32)
+ {
+ /* Deal with characters. */
+
+ if (copy_bufptr[0] < 128)
+ inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+ else
+ inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+
+ inev.ie.code = copy_bufptr[0];
+ }
+ else if (nchars < 2 && keysym)
+ {
+ /* If the key is a modifier key, just return. */
+ if (ANDROID_IS_MODIFIER_KEY (keysym))
+ goto done_keysym;
+
+ /* Next, deal with special ``characters'' by giving the
+ keycode to keyboard.c. */
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = keysym;
+ }
+ else
+ {
+ /* Finally, deal with strings. */
+
+ for (i = 0; i < nchars; ++i)
+ {
+ inev.ie.kind = (SINGLE_BYTE_CHAR_P (copy_bufptr[i])
+ ? ASCII_KEYSTROKE_EVENT
+ : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+ inev.ie.code = copy_bufptr[i];
+
+ /* If the character is actually '\n', then change this
+ to RET. */
+
+ if (copy_bufptr[i] == '\n')
+ {
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = 66;
+ }
+
+ kbd_buffer_store_buffered_event (&inev, hold_quit);
+ }
+
+ count += nchars;
+ inev.ie.kind = NO_EVENT; /* Already stored above. */
+ }
+
+ goto done_keysym;
+
+ done_keysym:
+
+ /* Now proceed to tell the input method the current position of
+ the cursor, if required. */
+
+ if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+ {
+ w = XWINDOW (f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+
+ goto OTHER;
+
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_detect_focus_change (dpyinfo, any, event, &inev.ie);
+ goto OTHER;
+
+ case ANDROID_WINDOW_ACTION:
+
+ /* This is a special event sent by android_run_in_emacs_thread
+ used to make Android run stuff. */
+
+ if (!event->xaction.window && !event->xaction.action)
+ {
+ /* Check for and run anything the UI thread wants to run on the main
+ thread. */
+ android_check_query ();
+ goto OTHER;
+ }
+
+ f = any;
+
+ if (event->xaction.action == 0)
+ {
+ /* Action 0 either means that a window has been destroyed
+ and its associated frame should be as well. */
+
+ if (event->xaction.window)
+ {
+ if (!f)
+ goto OTHER;
+
+ inev.ie.kind = DELETE_WINDOW_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ }
+ }
+
+ case ANDROID_ENTER_NOTIFY:
+ f = any;
+
+ if (f)
+ android_note_mouse_movement (f, &event->xmotion);
+ goto OTHER;
+
+ case ANDROID_MOTION_NOTIFY:
+
+ previous_help_echo_string = help_echo_string;
+ help_echo_string = Qnil;
+
+ if (hlinfo->mouse_face_hidden)
+ {
+ hlinfo->mouse_face_hidden = false;
+ clear_mouse_face (hlinfo);
+ }
+
+ f = any;
+
+ if (f)
+ {
+ /* Maybe generate a SELECT_WINDOW_EVENT for
+ `mouse-autoselect-window' but don't let popup menus
+ interfere with this (Bug#1261). */
+ if (!NILP (Vmouse_autoselect_window)
+ && !popup_activated ()
+ /* Don't switch if we're currently in the minibuffer.
+ This tries to work around problems where the
+ minibuffer gets unselected unexpectedly, and where
+ you then have to move your mouse all the way down to
+ the minibuffer to select it. */
+ && !MINI_WINDOW_P (XWINDOW (selected_window))
+ /* With `focus-follows-mouse' non-nil create an event
+ also when the target window is on another frame. */
+ && (f == XFRAME (selected_frame)
+ || !NILP (focus_follows_mouse)))
+ {
+ static Lisp_Object last_mouse_window;
+ Lisp_Object window
+ = window_from_coordinates (f, event->xmotion.x,
+ event->xmotion.y, 0,
+ false, false);
+
+ /* A window will be autoselected only when it is not
+ selected now and the last mouse movement event was
+ not in it. The remainder of the code is a bit vague
+ wrt what a "window" is. For immediate autoselection,
+ the window is usually the entire window but for GTK
+ where the scroll bars don't count. For delayed
+ autoselection the window is usually the window's text
+ area including the margins. */
+ if (WINDOWP (window)
+ && !EQ (window, last_mouse_window)
+ && !EQ (window, selected_window))
+ {
+ inev.ie.kind = SELECT_WINDOW_EVENT;
+ inev.ie.frame_or_window = window;
+ }
+
+ /* Remember the last window where we saw the mouse. */
+ last_mouse_window = window;
+ }
+
+ if (!android_note_mouse_movement (f, &event->xmotion))
+ help_echo_string = previous_help_echo_string;
+ }
+
+ /* If the contents of the global variable help_echo_string
+ has changed, generate a HELP_EVENT. */
+ if (!NILP (help_echo_string)
+ || !NILP (previous_help_echo_string))
+ do_help = 1;
+
+ if (f)
+ android_flush_dirty_back_buffer_on (f);
+
+ goto OTHER;
+
+ case ANDROID_LEAVE_NOTIFY:
+ f = any;
+
+ if (f)
+ {
+ /* Now clear dpyinfo->last_mouse_motion_frame, or
+ gui_redo_mouse_highlight will end up highlighting the
+ last known position of the mouse if a tooltip frame is
+ later unmapped. */
+
+ if (f == dpyinfo->last_mouse_motion_frame)
+ dpyinfo->last_mouse_motion_frame = NULL;
+
+ /* Something similar applies to
+ dpyinfo->last_mouse_glyph_frame. */
+ if (f == dpyinfo->last_mouse_glyph_frame)
+ dpyinfo->last_mouse_glyph_frame = NULL;
+
+ if (f == hlinfo->mouse_face_mouse_frame)
+ {
+ /* If we move outside the frame, then we're
+ certainly no longer on any text in the frame. */
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_mouse_frame = 0;
+ android_flush_dirty_back_buffer_on (f);
+ }
+
+ /* Generate a nil HELP_EVENT to cancel a help-echo.
+ Do it only if there's something to cancel.
+ Otherwise, the startup message is cleared when
+ the mouse leaves the frame. */
+ if (any_help_event_p
+ /* But never if `mouse-drag-and-drop-region' is in
+ progress, since that results in the tooltip being
+ dismissed when the mouse moves on top. */
+ && !((EQ (track_mouse, Qdrag_source)
+ || EQ (track_mouse, Qdropping))
+ && gui_mouse_grabbed (dpyinfo)))
+ do_help = -1;
+ }
+
+ goto OTHER;
+
+ case ANDROID_EXPOSE:
+
+ f = any;
+
+ if (f)
+ {
+ if (!FRAME_VISIBLE_P (f))
+ {
+ f->output_data.android->has_been_visible = true;
+ SET_FRAME_GARBAGED (f);
+ }
+
+ if (!FRAME_GARBAGED_P (f))
+ {
+ expose_frame (f, event->xexpose.x, event->xexpose.y,
+ event->xexpose.width, event->xexpose.height);
+ show_back_buffer (f);
+ }
+ }
+
+ goto OTHER;
+
+ case ANDROID_BUTTON_PRESS:
+ case ANDROID_BUTTON_RELEASE:
+ /* If we decide we want to generate an event to be seen
+ by the rest of Emacs, we put it here. */
+
+ f = any;
+
+ Lisp_Object tab_bar_arg = Qnil;
+ bool tab_bar_p = false;
+ bool tool_bar_p = false;
+
+ dpyinfo->last_mouse_glyph_frame = NULL;
+
+ f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
+
+ if (f && event->xbutton.type == ANDROID_BUTTON_PRESS
+ && !popup_activated ()
+ /* && !x_window_to_scroll_bar (event->xbutton.display, */
+ /* event->xbutton.window, 2) */
+ && !FRAME_NO_ACCEPT_FOCUS (f))
+ {
+ /* When clicking into a child frame or when clicking
+ into a parent frame with the child frame selected and
+ `no-accept-focus' is not set, select the clicked
+ frame. */
+ struct frame *hf = dpyinfo->highlight_frame;
+
+ if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
+ {
+ android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+ event->xbutton.time);
+
+ if (FRAME_PARENT_FRAME (f))
+ android_raise_window (FRAME_ANDROID_WINDOW (f));
+ }
+ }
+
+ if (f)
+ {
+ /* Is this in the tab-bar? */
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, true, true);
+ tab_bar_p = EQ (window, f->tab_bar_window);
+
+ if (tab_bar_p)
+ {
+ tab_bar_arg = handle_tab_bar_click
+ (f, x, y, (event->xbutton.type
+ == ANDROID_BUTTON_PRESS),
+ android_android_to_emacs_modifiers (dpyinfo,
+ event->xbutton.state));
+ android_flush_dirty_back_buffer_on (f);
+ }
+ }
+
+ /* Is this in the tool-bar? */
+ if (WINDOWP (f->tool_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, true, true);
+ tool_bar_p = (EQ (window, f->tool_bar_window)
+ && ((event->xbutton.type
+ != ANDROID_BUTTON_RELEASE)
+ || f->last_tool_bar_item != -1));
+
+ if (tool_bar_p && event->xbutton.button < 4)
+ {
+ handle_tool_bar_click
+ (f, x, y, (event->xbutton.type
+ == ANDROID_BUTTON_PRESS),
+ android_android_to_emacs_modifiers (dpyinfo,
+ event->xbutton.state));
+ android_flush_dirty_back_buffer_on (f);
+ }
+ }
+
+ if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p)
+ {
+ android_construct_mouse_click (&inev.ie, &event->xbutton, f);
+
+ if (!NILP (tab_bar_arg))
+ inev.ie.arg = tab_bar_arg;
+ }
+ }
+
+ if (event->type == ANDROID_BUTTON_PRESS)
+ {
+ dpyinfo->grabbed |= (1 << event->xbutton.button);
+ dpyinfo->last_mouse_frame = f;
+ if (f && !tab_bar_p)
+ f->last_tab_bar_item = -1;
+ if (f && !tool_bar_p)
+ f->last_tool_bar_item = -1;
+ }
+ else
+ dpyinfo->grabbed &= ~(1 << event->xbutton.button);
+
+ /* Ignore any mouse motion that happened before this event;
+ any subsequent mouse-movement Emacs events should reflect
+ only motion after the ButtonPress/Release. */
+ if (f != 0)
+ f->mouse_moved = false;
+
+ goto OTHER;
+
+ /* Touch events. The events here don't parallel X so much. */
+ case ANDROID_TOUCH_DOWN:
+
+ if (!any)
+ goto OTHER;
+
+ /* This event is sent when a tool is put on the screen. X and Y
+ are the location of the finger, and pointer_id identifies the
+ tool for as long as it is still held down. First, see if the
+ touch point already exists and can be reused (this shouldn't
+ happen, but be safe.) */
+
+ touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+ if (touchpoint)
+ {
+ /* Simply update the tool position and send an update. */
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.x;
+ android_update_tools (any, &inev.ie);
+ inev.ie.timestamp = event->touch.time;
+
+ goto OTHER;
+ }
+
+ /* Otherwise, link a new touchpoint onto the output's list of
+ pressed tools. */
+
+ touchpoint = xmalloc (sizeof *touchpoint);
+ touchpoint->tool_id = event->touch.pointer_id;
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.x;
+ touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
+ touchpoint->tool_bar_p = false;
+ FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
+
+ /* Figure out whether or not the tool was pressed on the tool
+ bar. Note that the code which runs when it was is more or
+ less an abuse of the mouse highlight machinery, but it works
+ well enough in practice. */
+
+ if (WINDOWP (any->tool_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (any->tool_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->touch.x;
+ int y = event->touch.y;
+
+ window = window_from_coordinates (any, x, y, 0, true,
+ true);
+
+ /* If this touch has started in the tool bar, do not
+ send it to Lisp. Instead, simulate a tool bar
+ click, releasing it once it goes away. */
+
+ if (EQ (window, any->tool_bar_window))
+ {
+ /* Call note_mouse_highlight on the tool bar
+ item. Otherwise, get_tool_bar_item will
+ return 1.
+
+ This is not necessary when mouse-highlight is
+ nil. */
+
+ 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
+ update the mouse highlight, no matter
+ where it is. */
+ memset (&dpyinfo->last_mouse_glyph, 0,
+ sizeof dpyinfo->last_mouse_glyph);
+ dpyinfo->last_mouse_glyph_frame = any;
+ }
+
+ handle_tool_bar_click (any, x, y, true, 0);
+
+ /* Flush any changes made by that to the front
+ buffer. */
+ android_flush_dirty_back_buffer_on (any);
+
+ /* Mark the touch point as being grabbed by the tool
+ bar. */
+ touchpoint->tool_bar_p = true;
+ goto OTHER;
+ }
+ }
+
+ /* Now generate the Emacs event. */
+ inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
+ inev.ie.timestamp = event->touch.time;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ XSETINT (inev.ie.x, event->touch.x);
+ XSETINT (inev.ie.y, event->touch.y);
+ XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+ goto OTHER;
+
+ case ANDROID_TOUCH_MOVE:
+
+ if (!any)
+ goto OTHER;
+
+ /* Look for the tool that moved. */
+
+ touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+ /* If it doesn't exist or has been grabbed by the tool bar, skip
+ processing this event. */
+
+ if (!touchpoint || touchpoint->tool_bar_p)
+ goto OTHER;
+
+ /* Otherwise, update the position and send the update event. */
+
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.y;
+ android_update_tools (any, &inev.ie);
+ inev.ie.timestamp = event->touch.time;
+
+ goto OTHER;
+
+ case ANDROID_TOUCH_UP:
+
+ if (!any)
+ goto OTHER;
+
+ /* Now find and unlink the tool in question. */
+
+ last = &FRAME_OUTPUT_DATA (any)->touch_points;
+ while ((touchpoint = *last))
+ {
+ if (touchpoint->tool_id == event->touch.pointer_id)
+ {
+ *last = touchpoint->next;
+
+ if (touchpoint->tool_bar_p)
+ {
+ xfree (touchpoint);
+
+ /* Do what is necessary to release the tool bar and
+ possibly trigger a click. */
+
+ if (any->last_tool_bar_item != -1)
+ handle_tool_bar_click (any, event->touch.x,
+ event->touch.y, false,
+ 0);
+
+ /* Cancel any outstanding mouse highlight. */
+ note_mouse_highlight (any, -1, -1);
+ android_flush_dirty_back_buffer_on (any);
+
+ goto OTHER;
+ }
+
+ /* The tool was unlinked. Free it and generate the
+ appropriate Emacs event (assuming that it was not
+ grabbed by the tool bar). */
+ xfree (touchpoint);
+
+ inev.ie.kind = TOUCHSCREEN_END_EVENT;
+ inev.ie.timestamp = event->touch.time;
+
+ XSETFRAME (inev.ie.frame_or_window, any);
+ XSETINT (inev.ie.x, event->touch.x);
+ XSETINT (inev.ie.y, event->touch.y);
+ XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+ /* Break out of the loop. */
+ goto OTHER;
+ }
+ else
+ last = &touchpoint->next;
+ }
+
+ /* No touch point was found. This shouldn't happen. */
+ goto OTHER;
+
+ /* Wheel motion. The events here don't parallel X because
+ Android doesn't have scroll valuators. */
+
+ case ANDROID_WHEEL:
+
+ if (!any)
+ goto OTHER;
+
+ if (fabs (event->wheel.x_delta) > 0
+ || fabs (event->wheel.y_delta) > 0)
+ {
+ if (mwheel_coalesce_scroll_events)
+ {
+ if (signbit (event->wheel.x_delta)
+ != signbit (wheel_event_x))
+ wheel_event_x = 0.0;
+
+ if (signbit (event->wheel.y_delta)
+ != signbit (wheel_event_y))
+ wheel_event_y = 0.0;
+
+ /* Tally up deltas until one of them exceeds 1.0. */
+ wheel_event_x += event->wheel.x_delta;
+ wheel_event_y += event->wheel.y_delta;
+
+ if (fabs (wheel_event_x) < 1.0
+ && fabs (wheel_event_y) < 1.0)
+ goto OTHER;
+ }
+ else
+ {
+ /* Use the deltas in the event. */
+ wheel_event_x = event->wheel.x_delta;
+ wheel_event_y = event->wheel.y_delta;
+ }
+
+ /* Determine what kind of event to send. */
+ inev.ie.kind = ((fabs (wheel_event_y)
+ >= fabs (wheel_event_x))
+ ? WHEEL_EVENT : HORIZ_WHEEL_EVENT);
+ inev.ie.timestamp = event->wheel.time;
+
+ /* Set the event coordinates. */
+ XSETINT (inev.ie.x, event->wheel.x);
+ XSETINT (inev.ie.y, event->wheel.y);
+
+ /* Set the frame. */
+ XSETFRAME (inev.ie.frame_or_window, any);
+
+ /* Figure out the scroll direction. */
+ inev.ie.modifiers = (signbit ((fabs (wheel_event_x)
+ >= fabs (wheel_event_y))
+ ? wheel_event_x
+ : wheel_event_y)
+ ? down_modifier : up_modifier);
+
+ /* Figure out how much to scale the deltas by. */
+ window = window_from_coordinates (any, event->wheel.x,
+ event->wheel.y, NULL,
+ false, false);
+
+ if (WINDOWP (window))
+ scroll_height = XWINDOW (window)->pixel_height;
+ else
+ /* EVENT_X and EVENT_Y can be outside the
+ frame if F holds the input grab, so fall
+ back to the height of the frame instead. */
+ scroll_height = FRAME_PIXEL_HEIGHT (any);
+
+ scroll_unit = pow (scroll_height, 2.0 / 3.0);
+
+ /* Add the keyboard modifiers. */
+ inev.ie.modifiers
+ |= android_android_to_emacs_modifiers (dpyinfo,
+ event->wheel.state);
+
+ /* Finally include the scroll deltas. */
+ inev.ie.arg = list3 (Qnil,
+ make_float (wheel_event_x
+ * scroll_unit),
+ make_float (wheel_event_y
+ * scroll_unit));
+
+ wheel_event_x = 0.0;
+ wheel_event_y = 0.0;
+ }
+
+ goto OTHER;
+
+ /* Iconification. This is vastly simpler than on X. */
+ case ANDROID_ICONIFIED:
+
+ if (!any)
+ goto OTHER;
+
+ if (FRAME_ICONIFIED_P (any))
+ goto OTHER;
+
+ SET_FRAME_VISIBLE (any, false);
+ SET_FRAME_ICONIFIED (any, true);
+
+ inev.ie.kind = ICONIFY_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ goto OTHER;
+
+ case ANDROID_DEICONIFIED:
+
+ if (!any)
+ goto OTHER;
+
+ if (!FRAME_ICONIFIED_P (any))
+ goto OTHER;
+
+ SET_FRAME_VISIBLE (any, true);
+ SET_FRAME_ICONIFIED (any, false);
+
+ inev.ie.kind = DEICONIFY_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ goto OTHER;
+
+ /* Context menu handling. */
+ case ANDROID_CONTEXT_MENU:
+
+ if (dpyinfo->menu_event_id == -1
+ /* Previously displayed popup menus might generate events
+ after dismissal, which might interfere.
+ `current_menu_serial' is always set to an identifier
+ identifying the last context menu to be displayed. */
+ && event->menu.menu_event_serial == current_menu_serial)
+ dpyinfo->menu_event_id = event->menu.menu_event_id;
+
+ goto OTHER;
+
+ /* Input method events. textconv.c functions are called here to
+ queue events, which are then executed in a safe context
+ inside keyboard.c. */
+ case ANDROID_INPUT_METHOD:
+
+ if (!any)
+ /* Free any text allocated for this event. */
+ xfree (event->ime.text);
+ else
+ android_handle_ime_event (event, any);
+
+ goto OTHER;
+
+ default:
+ goto OTHER;
+ }
+
+ OTHER:
+ if (inev.ie.kind != NO_EVENT)
+ {
+ kbd_buffer_store_buffered_event (&inev, hold_quit);
+ count++;
+ }
+
+ if (do_help
+ && !(hold_quit && hold_quit->kind != NO_EVENT))
+ {
+ Lisp_Object frame;
+
+ if (f)
+ XSETFRAME (frame, f);
+ else
+ frame = Qnil;
+
+ if (do_help > 0)
+ {
+ any_help_event_p = true;
+ gen_help_event (help_echo_string, frame, help_echo_window,
+ help_echo_object, help_echo_pos);
+ }
+ else
+ {
+ help_echo_string = Qnil;
+ gen_help_event (Qnil, frame, Qnil, Qnil, 0);
+ }
+ count++;
+ }
+
+ return count;
+}
+
+static int
+android_read_socket (struct terminal *terminal,
+ struct input_event *hold_quit)
+{
+ int count = 0;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = terminal->display_info.android;
+
+ block_input ();
+ while (android_pending ())
+ {
+ int finish;
+ union android_event event;
+
+ android_next_event (&event);
+ count += handle_one_android_event (dpyinfo, &event, &finish,
+ hold_quit);
+
+ if (finish == ANDROID_EVENT_GOTO_OUT)
+ break;
+ }
+ unblock_input ();
+
+ /* If the focus was just given to an auto-raising frame, raise it
+ now. */
+ if (dpyinfo->pending_autoraise_frame)
+ {
+ android_raise_frame (dpyinfo->pending_autoraise_frame);
+ dpyinfo->pending_autoraise_frame = NULL;
+ }
+
+ return count;
+}
+
+static void
+android_frame_up_to_date (struct frame *f)
+{
+ eassert (FRAME_ANDROID_P (f));
+ block_input ();
+ FRAME_MOUSE_UPDATE (f);
+
+ if (!buffer_flipping_blocked_p ()
+ && FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ /* The frame is now complete, as its contents have been drawn. */
+ FRAME_ANDROID_COMPLETE_P (f) = true;
+
+ /* Shrink the scanline buffer used by the font backend. */
+ sfntfont_android_shrink_scanline_buffer ();
+ unblock_input ();
+}
+
+static void
+android_buffer_flipping_unblocked_hook (struct frame *f)
+{
+ block_input ();
+
+ if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ unblock_input ();
+}
+
+static void
+android_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
+{
+ unsigned long background;
+
+ background = FRAME_BACKGROUND_PIXEL (f);
+ bgcolor->pixel = background;
+
+ android_query_colors (f, bgcolor, 1);
+}
+
+int
+android_parse_color (struct frame *f, const char *color_name,
+ Emacs_Color *color)
+{
+ unsigned short r, g, b;
+ Lisp_Object tem, tem1;
+ unsigned long lisp_color;
+
+ if (parse_color_spec (color_name, &r, &g, &b))
+ {
+ color->red = r;
+ color->green = g;
+ color->blue = b;
+
+ return 1;
+ }
+
+ tem = x_display_list->color_map;
+ for (; CONSP (tem); tem = XCDR (tem))
+ {
+ tem1 = XCAR (tem);
+
+ if (CONSP (tem1)
+ && !xstrcasecmp (SSDATA (XCAR (tem1)), color_name))
+ {
+ lisp_color = XFIXNUM (XCDR (tem1));
+ color->red = RED_FROM_ULONG (lisp_color) * 257;
+ color->green = GREEN_FROM_ULONG (lisp_color) * 257;
+ color->blue = BLUE_FROM_ULONG (lisp_color) * 257;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+bool
+android_alloc_nearest_color (struct frame *f, Emacs_Color *color)
+{
+ gamma_correct (f, color);
+ color->pixel = RGB_TO_ULONG (color->red / 256,
+ color->green / 256,
+ color->blue / 256);
+
+ return true;
+}
+
+void
+android_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i)
+ {
+ colors[i].red = RED_FROM_ULONG (colors[i].pixel) * 257;
+ colors[i].green = RED_FROM_ULONG (colors[i].pixel) * 257;
+ colors[i].blue = RED_FROM_ULONG (colors[i].pixel) * 257;
+ }
+}
+
+static void
+android_mouse_position (struct frame **fp, int insist,
+ Lisp_Object *bar_window,
+ enum scroll_bar_part *part, Lisp_Object *x,
+ Lisp_Object *y, Time *timestamp)
+{
+ Lisp_Object tail, frame;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = FRAME_DISPLAY_INFO (*fp);
+
+ /* This is the best implementation possible on Android, where the
+ system doesn't let Emacs obtain any information about the mouse
+ pointer at all. */
+
+ if (dpyinfo->last_mouse_motion_frame)
+ {
+ *fp = dpyinfo->last_mouse_motion_frame;
+ *timestamp = dpyinfo->last_mouse_movement_time;
+ *x = make_fixnum (dpyinfo->last_mouse_motion_x);
+ *y = make_fixnum (dpyinfo->last_mouse_motion_y);
+ *bar_window = Qnil;
+ *part = scroll_bar_nowhere;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ if (FRAME_ANDROID_P (XFRAME (frame)))
+ XFRAME (frame)->mouse_moved = false;
+ }
+
+ dpyinfo->last_mouse_motion_frame->mouse_moved = false;
+ }
+}
+
+static Lisp_Object
+android_get_focus_frame (struct frame *f)
+{
+ Lisp_Object lisp_focus;
+ struct frame *focus;
+
+ focus = FRAME_DISPLAY_INFO (f)->focus_frame;
+
+ if (!focus)
+ return Qnil;
+
+ XSETFRAME (lisp_focus, focus);
+ return lisp_focus;
+}
+
+static void
+android_focus_frame (struct frame *f, bool noactivate)
+{
+ /* Set the input focus to the frame's window. The system only lets
+ this work on child frames. */
+ android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+ ANDROID_CURRENT_TIME);
+}
+
+/* The two procedures below only have to update the cursor on Android,
+ as there are no window borders there. */
+
+static void
+android_frame_highlight (struct frame *f)
+{
+ gui_update_cursor (f, true);
+}
+
+static void
+android_frame_unhighlight (struct frame *f)
+{
+ gui_update_cursor (f, true);
+}
+
+static void
+android_frame_rehighlight (struct android_display_info *dpyinfo)
+{
+ struct frame *old_highlight;
+
+ old_highlight = dpyinfo->highlight_frame;
+
+ if (dpyinfo->focus_frame)
+ {
+ dpyinfo->highlight_frame
+ = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->focus_frame)))
+ ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->focus_frame))
+ : dpyinfo->focus_frame);
+ if (!FRAME_LIVE_P (dpyinfo->highlight_frame))
+ {
+ fset_focus_frame (dpyinfo->focus_frame, Qnil);
+ dpyinfo->highlight_frame = dpyinfo->focus_frame;
+ }
+ }
+ else
+ dpyinfo->highlight_frame = 0;
+
+ if (dpyinfo->highlight_frame != old_highlight)
+ {
+ /* This is not yet required on Android. */
+ if (old_highlight)
+ android_frame_unhighlight (old_highlight);
+ if (dpyinfo->highlight_frame)
+ android_frame_highlight (dpyinfo->highlight_frame);
+ }
+}
+
+static void
+android_frame_rehighlight_hook (struct frame *f)
+{
+ android_frame_rehighlight (FRAME_DISPLAY_INFO (f));
+}
+
+static void
+android_frame_raise_lower (struct frame *f, bool raise_flag)
+{
+ if (raise_flag)
+ android_raise_frame (f);
+ else
+ android_lower_frame (f);
+}
+
+void
+android_make_frame_visible (struct frame *f)
+{
+ android_map_window (FRAME_ANDROID_WINDOW (f));
+
+ SET_FRAME_VISIBLE (f, true);
+ SET_FRAME_ICONIFIED (f, false);
+}
+
+void
+android_make_frame_invisible (struct frame *f)
+{
+ /* Don't keep the highlight on an invisible frame. */
+ if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
+ FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+
+ android_unmap_window (FRAME_ANDROID_WINDOW (f));
+
+ SET_FRAME_VISIBLE (f, false);
+ SET_FRAME_ICONIFIED (f, false);
+}
+
+static void
+android_make_frame_visible_invisible (struct frame *f, bool visible)
+{
+ if (visible)
+ android_make_frame_visible (f);
+ else
+ android_make_frame_invisible (f);
+}
+
+static void
+android_fullscreen_hook (struct frame *f)
+{
+ Lisp_Object wanted;
+
+ if (!FRAME_PARENT_FRAME (f))
+ {
+ /* Explicitly setting fullscreen is not supported on older
+ Android versions. */
+
+ wanted = (f->want_fullscreen == FULLSCREEN_BOTH
+ ? Qfullscreen : Qmaximized);
+
+ if (android_set_fullscreen (FRAME_ANDROID_WINDOW (f),
+ EQ (wanted, Qfullscreen)))
+ store_frame_param (f, Qfullscreen, Qmaximized);
+ else
+ store_frame_param (f, Qfullscreen, wanted);
+ }
+ else
+ {
+ store_frame_param (f, Qfullscreen, Qnil);
+
+ /* If this is a child frame, don't keep it fullscreen
+ anymore. */
+ android_set_fullscreen (FRAME_ANDROID_WINDOW (f), false);
+ }
+}
+
+void
+android_iconify_frame (struct frame *f)
+{
+ /* This really doesn't work on Android. */
+ error ("Can't notify window manager of iconification");
+}
+
+static void
+android_wait_for_event (struct frame *f, int eventtype)
+{
+ if (!FLOATP (Vandroid_wait_for_event_timeout))
+ return;
+
+ int level = interrupt_input_blocked;
+ struct timespec tmo, tmo_at, time_now;
+
+ f->wait_event_type = eventtype;
+
+ /* Default timeout is 0.1 second. Hopefully not noticeable. */
+ double timeout = XFLOAT_DATA (Vandroid_wait_for_event_timeout);
+ time_t timeout_seconds = (time_t) timeout;
+ tmo = make_timespec (timeout_seconds,
+ (long int) ((timeout - timeout_seconds)
+ * 1000 * 1000 * 1000));
+ tmo_at = timespec_add (current_timespec (), tmo);
+
+ while (f->wait_event_type)
+ {
+ pending_signals = true;
+ totally_unblock_input ();
+ /* XTread_socket is called after unblock. */
+ block_input ();
+ interrupt_input_blocked = level;
+
+ time_now = current_timespec ();
+ if (timespec_cmp (tmo_at, time_now) < 0)
+ break;
+
+ tmo = timespec_sub (tmo_at, time_now);
+ if (android_select (0, NULL, NULL, NULL, &tmo) == 0)
+ break; /* Timeout */
+ }
+
+ f->wait_event_type = 0;
+}
+
+static void
+android_set_window_size_1 (struct frame *f, bool change_gravity,
+ int width, int height)
+{
+ if (change_gravity)
+ f->win_gravity = NorthWestGravity;
+
+ android_resize_window (FRAME_ANDROID_WINDOW (f), width,
+ height);
+
+ SET_FRAME_GARBAGED (f);
+
+ if (FRAME_VISIBLE_P (f))
+ {
+ android_wait_for_event (f, ANDROID_CONFIGURE_NOTIFY);
+
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("set_window_size_1 visible"),
+ FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width, f->new_height);
+ }
+ else
+ {
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("set_window_size_1 "
+ "invisible"),
+ FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width, f->new_height);
+
+ adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+ FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
+ 5, 0, Qx_set_window_size_1);
+ }
+}
+
+void
+android_set_window_size (struct frame *f, bool change_gravity,
+ int width, int height)
+{
+ block_input ();
+
+ android_set_window_size_1 (f, change_gravity, width, height);
+ android_clear_under_internal_border (f);
+
+ /* If cursor was outside the new size, mark it as off. */
+ mark_window_cursors_off (XWINDOW (f->root_window));
+
+ /* Clear out any recollection of where the mouse highlighting was,
+ since it might be in a place that's outside the new frame size.
+ Actually checking whether it is outside is a pain in the neck,
+ so don't try--just let the highlighting be done afresh with new size. */
+ cancel_mouse_face (f);
+
+ unblock_input ();
+
+ do_pending_window_change (false);
+}
+
+static void
+android_set_offset (struct frame *f, int xoff, int yoff,
+ int change_gravity)
+{
+ if (change_gravity > 0)
+ {
+ f->top_pos = yoff;
+ f->left_pos = xoff;
+ f->size_hint_flags &= ~ (XNegative | YNegative);
+ if (xoff < 0)
+ f->size_hint_flags |= XNegative;
+ if (yoff < 0)
+ f->size_hint_flags |= YNegative;
+ f->win_gravity = NorthWestGravity;
+ }
+
+ android_move_window (FRAME_ANDROID_WINDOW (f), xoff, yoff);
+}
+
+static void
+android_set_alpha (struct frame *f)
+{
+ /* Not supported on Android. */
+}
+
+static Lisp_Object
+android_new_font (struct frame *f, Lisp_Object font_object, int fontset)
+{
+ struct font *font = XFONT_OBJECT (font_object);
+ int unit, font_ascent, font_descent;
+
+ if (fontset < 0)
+ fontset = fontset_from_font (font_object);
+ FRAME_FONTSET (f) = fontset;
+ if (FRAME_FONT (f) == font)
+ /* This font is already set in frame F. There's nothing more to
+ do. */
+ return font_object;
+
+ FRAME_FONT (f) = font;
+ FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
+ FRAME_COLUMN_WIDTH (f) = font->average_width;
+ get_font_ascent_descent (font, &font_ascent, &font_descent);
+ FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
+
+ /* We could use a more elaborate calculation here. */
+ FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+
+ /* Compute character columns occupied by scrollbar.
+
+ Don't do things differently for non-toolkit scrollbars
+ (Bug#17163). */
+ unit = FRAME_COLUMN_WIDTH (f);
+ if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
+ FRAME_CONFIG_SCROLL_BAR_COLS (f)
+ = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + unit - 1) / unit;
+ else
+ FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+
+
+ /* Don't change the size of a tip frame; there's no point in doing it
+ because it's done in Fx_show_tip, and it leads to problems because
+ the tip frame has no widget. */
+ if (FRAME_ANDROID_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
+ adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+ FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
+ false, Qfont);
+
+ return font_object;
+}
+
+static bool
+android_bitmap_icon (struct frame *f, Lisp_Object file)
+{
+ return false;
+}
+
+static void
+android_free_pixmap_hook (struct frame *f, Emacs_Pixmap pixmap)
+{
+ android_free_pixmap (pixmap);
+}
+
+void
+android_free_frame_resources (struct frame *f)
+{
+ struct android_display_info *dpyinfo;
+ Mouse_HLInfo *hlinfo;
+ struct android_touch_point *last, *next;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ hlinfo = &dpyinfo->mouse_highlight;
+
+ block_input ();
+ free_frame_faces (f);
+
+ /* FRAME_ANDROID_WINDOW can be 0 if frame creation failed. */
+ if (FRAME_ANDROID_WINDOW (f))
+ android_destroy_window (FRAME_ANDROID_WINDOW (f));
+
+ android_free_gcs (f);
+
+ /* Free cursors. */
+ if (f->output_data.android->text_cursor)
+ android_free_cursor (f->output_data.android->text_cursor);
+ if (f->output_data.android->nontext_cursor)
+ android_free_cursor (f->output_data.android->nontext_cursor);
+ if (f->output_data.android->modeline_cursor)
+ android_free_cursor (f->output_data.android->modeline_cursor);
+ if (f->output_data.android->hand_cursor)
+ android_free_cursor (f->output_data.android->hand_cursor);
+ if (f->output_data.android->hourglass_cursor)
+ android_free_cursor (f->output_data.android->hourglass_cursor);
+ if (f->output_data.android->horizontal_drag_cursor)
+ android_free_cursor (f->output_data.android->horizontal_drag_cursor);
+ if (f->output_data.android->vertical_drag_cursor)
+ android_free_cursor (f->output_data.android->vertical_drag_cursor);
+ if (f->output_data.android->left_edge_cursor)
+ android_free_cursor (f->output_data.android->left_edge_cursor);
+ if (f->output_data.android->top_left_corner_cursor)
+ android_free_cursor (f->output_data.android->top_left_corner_cursor);
+ if (f->output_data.android->top_edge_cursor)
+ android_free_cursor (f->output_data.android->top_edge_cursor);
+ if (f->output_data.android->top_right_corner_cursor)
+ android_free_cursor (f->output_data.android->top_right_corner_cursor);
+ if (f->output_data.android->right_edge_cursor)
+ android_free_cursor (f->output_data.android->right_edge_cursor);
+ if (f->output_data.android->bottom_right_corner_cursor)
+ android_free_cursor (f->output_data.android->bottom_right_corner_cursor);
+ if (f->output_data.android->bottom_edge_cursor)
+ android_free_cursor (f->output_data.android->bottom_edge_cursor);
+ if (f->output_data.android->bottom_left_corner_cursor)
+ android_free_cursor (f->output_data.android->bottom_left_corner_cursor);
+
+ /* Free extra GCs allocated by android_setup_relief_colors. */
+ if (f->output_data.android->white_relief.gc)
+ {
+ android_free_gc (f->output_data.android->white_relief.gc);
+ f->output_data.android->white_relief.gc = 0;
+ }
+ if (f->output_data.android->black_relief.gc)
+ {
+ android_free_gc (f->output_data.android->black_relief.gc);
+ f->output_data.android->black_relief.gc = 0;
+ }
+
+ if (f == dpyinfo->focus_frame)
+ dpyinfo->focus_frame = 0;
+ if (f == dpyinfo->x_focus_event_frame)
+ dpyinfo->x_focus_event_frame = 0;
+ if (f == dpyinfo->highlight_frame)
+ dpyinfo->highlight_frame = 0;
+ if (f == hlinfo->mouse_face_mouse_frame)
+ reset_mouse_highlight (hlinfo);
+
+ /* These two need to be freed now that they are used to compute the
+ mouse position, I think. */
+ if (f == dpyinfo->last_mouse_motion_frame)
+ dpyinfo->last_mouse_motion_frame = NULL;
+ if (f == dpyinfo->last_mouse_frame)
+ dpyinfo->last_mouse_frame = NULL;
+
+ /* Free all tool presses currently active on this frame. */
+ next = FRAME_OUTPUT_DATA (f)->touch_points;
+ while (next)
+ {
+ last = next;
+ next = next->next;
+ xfree (last);
+ }
+
+ /* Clear this in case unblock_input reads events. */
+ FRAME_OUTPUT_DATA (f)->touch_points = NULL;
+
+ unblock_input ();
+}
+
+static void
+android_delete_frame (struct frame *f)
+{
+ android_free_frame_resources (f);
+ xfree (f->output_data.android);
+ f->output_data.android = NULL;
+}
+
+static void
+android_delete_terminal (struct terminal *terminal)
+{
+ error ("Cannot terminate connection to Android display server");
+}
+
+
+
+/* RIF functions. */
+
+static void
+android_scroll_run (struct window *w, struct run *run)
+{
+ struct frame *f = XFRAME (w->frame);
+ int x, y, width, height, from_y, to_y, bottom_y;
+
+ /* Get frame-relative bounding box of the text display area of W,
+ without mode lines. Include in this box the left and right
+ fringe of W. */
+ window_box (w, ANY_AREA, &x, &y, &width, &height);
+
+ from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
+ to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
+ bottom_y = y + height;
+
+ if (to_y < from_y)
+ {
+ /* Scrolling up. Make sure we don't copy part of the mode
+ line at the bottom. */
+ if (from_y + run->height > bottom_y)
+ height = bottom_y - from_y;
+ else
+ height = run->height;
+ }
+ else
+ {
+ /* Scrolling down. Make sure we don't copy over the mode line.
+ at the bottom. */
+ if (to_y + run->height > bottom_y)
+ height = bottom_y - to_y;
+ else
+ height = run->height;
+ }
+
+ block_input ();
+
+ /* Cursor off. Will be switched on again in gui_update_window_end. */
+ gui_clear_cursor (w);
+
+ /* To avoid sequence point problems, make sure to only call
+ FRAME_ANDROID_DRAWABLE once. */
+ android_copy_area (FRAME_ANDROID_DRAWABLE (f),
+ FRAME_ANDROID_WINDOW (f),
+ f->output_data.android->normal_gc,
+ x, from_y, width, height, x, to_y);
+
+ unblock_input ();
+}
+
+static void
+android_after_update_window_line (struct window *w, struct glyph_row *desired_row)
+{
+ eassert (w);
+
+ if (!desired_row->mode_line_p && !w->pseudo_window_p)
+ desired_row->redraw_fringe_bitmaps_p = true;
+}
+
+static void
+android_flip_and_flush (struct frame *f)
+{
+ block_input ();
+
+ if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ /* The frame is complete again as its contents were just
+ flushed. */
+ FRAME_ANDROID_COMPLETE_P (f) = true;
+ unblock_input ();
+}
+
+static void
+android_clear_rectangle (struct frame *f, struct android_gc *gc, int x,
+ int y, int width, int height)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (gc, (ANDROID_GC_BACKGROUND
+ | ANDROID_GC_FOREGROUND),
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ x, y, width, height);
+ android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_reset_clip_rectangles (struct frame *f, struct android_gc *gc)
+{
+ android_set_clip_mask (gc, ANDROID_NONE);
+}
+
+static void
+android_clip_to_row (struct window *w, struct glyph_row *row,
+ enum glyph_row_area area, struct android_gc *gc)
+{
+ struct android_rectangle clip_rect;
+ int window_x, window_y, window_width;
+
+ window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+ clip_rect.x = window_x;
+ clip_rect.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+ clip_rect.y = max (clip_rect.y, window_y);
+ clip_rect.width = window_width;
+ clip_rect.height = row->visible_height;
+
+ android_set_clip_rectangles (gc, 0, 0, &clip_rect, 1);
+}
+
+static void
+android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
+ struct draw_fringe_bitmap_params *p)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct android_gc *gc = f->output_data.android->normal_gc;
+ struct face *face = p->face;
+
+ /* Must clip because of partially visible lines. */
+ android_clip_to_row (w, row, ANY_AREA, gc);
+
+ if (p->bx >= 0 && !p->overlay_p)
+ {
+ /* In case the same realized face is used for fringes and for
+ something displayed in the text (e.g. face `region' on
+ mono-displays, the fill style may have been changed to
+ ANDROID_FILL_SOLID in
+ android_draw_glyph_string_background. */
+ if (face->stipple)
+ {
+ android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc,
+ p->bx, p->by, p->nx, p->ny);
+ android_set_fill_style (face->gc, ANDROID_FILL_SOLID);
+
+ row->stipple_p = true;
+ }
+ else
+ {
+ android_set_background (face->gc, face->background);
+ android_clear_rectangle (f, face->gc, p->bx, p->by, p->nx, p->ny);
+ android_set_foreground (face->gc, face->foreground);
+ }
+ }
+
+ if (p->which)
+ {
+ android_drawable drawable;
+ char *bits;
+ android_pixmap pixmap, clipmask;
+ struct android_gc_values gcv;
+ unsigned long background, cursor_pixel;
+ int depth;
+
+ drawable = FRAME_ANDROID_DRAWABLE (f);
+ clipmask = ANDROID_NONE;
+ background = face->background;
+ cursor_pixel = f->output_data.android->cursor_pixel;
+ depth = FRAME_DISPLAY_INFO (f)->n_planes;
+
+ if (p->wd > 8)
+ bits = (char *) (p->bits + p->dh);
+ else
+ bits = (char *) p->bits + p->dh;
+
+ pixmap = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+ (p->cursor_p
+ ? (p->overlay_p
+ ? face->background
+ : cursor_pixel)
+ : face->foreground),
+ background, depth);
+
+ if (p->overlay_p)
+ {
+ clipmask = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+ 1, 0, 1);
+
+ gcv.clip_mask = clipmask;
+ gcv.clip_x_origin = p->x;
+ gcv.clip_y_origin = p->y;
+ android_change_gc (gc, (ANDROID_GC_CLIP_MASK
+ | ANDROID_GC_CLIP_X_ORIGIN
+ | ANDROID_GC_CLIP_Y_ORIGIN),
+ &gcv);
+ }
+
+ android_copy_area (pixmap, drawable, gc, 0, 0, p->wd, p->h,
+ p->x, p->y);
+ android_free_pixmap (pixmap);
+
+ if (p->overlay_p)
+ {
+ gcv.clip_mask = ANDROID_NONE;
+ android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+ android_free_pixmap (clipmask);
+ }
+ }
+
+ android_reset_clip_rectangles (f, gc);
+}
+
+/* Set S->gc to a suitable GC for drawing glyph string S in cursor
+ face. */
+
+static void
+android_set_cursor_gc (struct glyph_string *s)
+{
+ if (s->font == FRAME_FONT (s->f)
+ && s->face->background == FRAME_BACKGROUND_PIXEL (s->f)
+ && s->face->foreground == FRAME_FOREGROUND_PIXEL (s->f)
+ && !s->cmp)
+ s->gc = s->f->output_data.android->cursor_gc;
+ else
+ {
+ /* Cursor on non-default face: must merge. */
+ struct android_gc_values xgcv;
+ unsigned long mask;
+
+ xgcv.background = s->f->output_data.android->cursor_pixel;
+ xgcv.foreground = s->face->background;
+
+ /* If the glyph would be invisible, try a different foreground. */
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->face->foreground;
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->f->output_data.android->cursor_foreground_pixel;
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->face->foreground;
+
+ /* Make sure the cursor is distinct from text in this face. */
+ if (xgcv.background == s->face->background
+ && xgcv.foreground == s->face->foreground)
+ {
+ xgcv.background = s->face->foreground;
+ xgcv.foreground = s->face->background;
+ }
+
+ mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+ if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+ android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+ mask, &xgcv);
+ else
+ FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+ = android_create_gc (mask, &xgcv);
+
+ s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+ }
+}
+
+
+/* Set up S->gc of glyph string S for drawing text in mouse face. */
+
+static void
+android_set_mouse_face_gc (struct glyph_string *s)
+{
+ if (s->font == s->face->font)
+ s->gc = s->face->gc;
+ else
+ {
+ /* Otherwise construct scratch_cursor_gc with values from FACE
+ except for FONT. */
+ struct android_gc_values xgcv;
+ unsigned long mask;
+
+ xgcv.background = s->face->background;
+ xgcv.foreground = s->face->foreground;
+
+ mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+ if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+ android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+ mask, &xgcv);
+ else
+ FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+ = android_create_gc (mask, &xgcv);
+
+ s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+ }
+
+ eassert (s->gc != 0);
+}
+
+
+/* Set S->gc of glyph string S to a GC suitable for drawing a mode line.
+ Faces to use in the mode line have already been computed when the
+ matrix was built, so there isn't much to do, here. */
+
+static void
+android_set_mode_line_face_gc (struct glyph_string *s)
+{
+ s->gc = s->face->gc;
+}
+
+/* Set S->gc of glyph string S for drawing that glyph string. Set
+ S->stippled_p to a non-zero value if the face of S has a stipple
+ pattern. */
+
+static void
+android_set_glyph_string_gc (struct glyph_string *s)
+{
+ prepare_face_for_display (s->f, s->face);
+
+ if (s->hl == DRAW_NORMAL_TEXT)
+ {
+ s->gc = s->face->gc;
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_INVERSE_VIDEO)
+ {
+ android_set_mode_line_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_CURSOR)
+ {
+ android_set_cursor_gc (s);
+ s->stippled_p = false;
+ }
+ else if (s->hl == DRAW_MOUSE_FACE)
+ {
+ android_set_mouse_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_IMAGE_RAISED
+ || s->hl == DRAW_IMAGE_SUNKEN)
+ {
+ s->gc = s->face->gc;
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else
+ emacs_abort ();
+
+ /* GC must have been set. */
+ eassert (s->gc != 0);
+}
+
+
+/* Set clipping for output of glyph string S. S may be part of a mode
+ line or menu if we don't have X toolkit support. */
+
+static void
+android_set_glyph_string_clipping (struct glyph_string *s)
+{
+ struct android_rectangle *r = s->clip;
+ int n = get_glyph_string_clip_rects (s, r, 2);
+
+ if (n > 0)
+ android_set_clip_rectangles (s->gc, 0, 0, r, n);
+ s->num_clips = n;
+}
+
+
+/* Set SRC's clipping for output of glyph string DST. This is called
+ when we are drawing DST's left_overhang or right_overhang only in
+ the area of SRC. */
+
+static void
+android_set_glyph_string_clipping_exactly (struct glyph_string *src,
+ struct glyph_string *dst)
+{
+ struct android_rectangle r;
+
+ r.x = src->x;
+ r.width = src->width;
+ r.y = src->y;
+ r.height = src->height;
+ dst->clip[0] = r;
+ dst->num_clips = 1;
+ android_set_clip_rectangles (dst->gc, 0, 0, &r, 1);
+}
+
+static void
+android_compute_glyph_string_overhangs (struct glyph_string *s)
+{
+ if (s->cmp == NULL
+ && (s->first_glyph->type == CHAR_GLYPH
+ || s->first_glyph->type == COMPOSITE_GLYPH))
+ {
+ struct font_metrics metrics;
+
+ if (s->first_glyph->type == CHAR_GLYPH)
+ {
+ struct font *font = s->font;
+ font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+ }
+ else
+ {
+ Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+ composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+ }
+ s->right_overhang = (metrics.rbearing > metrics.width
+ ? metrics.rbearing - metrics.width : 0);
+ s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
+ }
+ else if (s->cmp)
+ {
+ s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+ s->left_overhang = - s->cmp->lbearing;
+ }
+}
+
+static void
+android_clear_glyph_string_rect (struct glyph_string *s, int x, int y,
+ int w, int h)
+{
+ android_clear_rectangle (s->f, s->gc, x, y, w, h);
+}
+
+static void
+android_draw_glyph_string_background (struct glyph_string *s, bool force_p)
+{
+ /* Nothing to do if background has already been drawn or if it
+ shouldn't be drawn in the first place. */
+ if (!s->background_filled_p)
+ {
+ int box_line_width = max (s->face->box_horizontal_line_width, 0);
+
+ if (s->stippled_p)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, s->y + box_line_width,
+ s->background_width,
+ s->height - 2 * box_line_width);
+ android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+ s->background_filled_p = true;
+ }
+ else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+ /* When xdisp.c ignores FONT_HEIGHT, we cannot trust
+ font dimensions, since the actual glyphs might be
+ much smaller. So in that case we always clear the
+ rectangle with background color. */
+ || FONT_TOO_HIGH (s->font)
+ || s->font_not_found_p
+ || s->extends_to_end_of_line_p
+ || force_p)
+ {
+ android_clear_glyph_string_rect (s, s->x, s->y + box_line_width,
+ s->background_width,
+ s->height - 2 * box_line_width);
+ s->background_filled_p = true;
+ }
+ }
+}
+
+static void
+android_fill_triangle (struct frame *f, struct android_gc *gc,
+ struct android_point point1,
+ struct android_point point2,
+ struct android_point point3)
+{
+ struct android_point abc[3];
+
+ abc[0] = point1;
+ abc[1] = point2;
+ abc[2] = point3;
+
+ android_fill_polygon (FRAME_ANDROID_DRAWABLE (f),
+ gc, abc, 3, ANDROID_CONVEX,
+ ANDROID_COORD_MODE_ORIGIN);
+}
+
+static struct android_point
+android_make_point (int x, int y)
+{
+ struct android_point pt;
+
+ pt.x = x;
+ pt.y = y;
+
+ return pt;
+}
+
+static bool
+android_inside_rect_p (struct android_rectangle *rects, int nrects, int x,
+ int y)
+{
+ int i;
+
+ for (i = 0; i < nrects; ++i)
+ {
+ if (x >= rects[i].x && y >= rects[i].y
+ && x < rects[i].x + rects[i].width
+ && y < rects[i].y + rects[i].height)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+android_clear_point (struct frame *f, struct android_gc *gc,
+ int x, int y)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (gc, ANDROID_GC_BACKGROUND | ANDROID_GC_FOREGROUND,
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y);
+ android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
+ int bottom_y, int hwidth, int vwidth, bool raised_p,
+ bool top_p, bool bot_p, bool left_p, bool right_p,
+ struct android_rectangle *clip_rect)
+{
+ struct android_gc *gc, *white_gc, *black_gc, *normal_gc;
+ android_drawable drawable;
+
+ /* This code is more complicated than it has to be, because of two
+ minor hacks to make the boxes look nicer: (i) if width > 1, draw
+ the outermost line using the black relief. (ii) Omit the four
+ corner pixels. */
+
+ white_gc = f->output_data.android->white_relief.gc;
+ black_gc = f->output_data.android->black_relief.gc;
+ normal_gc = f->output_data.android->normal_gc;
+
+ drawable = FRAME_ANDROID_DRAWABLE (f);
+
+ android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1);
+ android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1);
+
+ if (raised_p)
+ gc = white_gc;
+ else
+ gc = black_gc;
+
+ /* Draw lines. */
+
+ if (top_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+ right_x - left_x + 1, hwidth);
+
+ if (left_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+ vwidth, bottom_y - top_y + 1);
+
+ if (raised_p)
+ gc = black_gc;
+ else
+ gc = white_gc;
+
+ if (bot_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x,
+ bottom_y - hwidth + 1,
+ right_x - left_x + 1, hwidth);
+
+ if (right_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ right_x - vwidth + 1,
+ top_y, vwidth, bottom_y - top_y + 1);
+
+ /* Draw corners. */
+
+ if (bot_p && left_p)
+ android_fill_triangle (f, raised_p ? white_gc : black_gc,
+ android_make_point (left_x, bottom_y - hwidth),
+ android_make_point (left_x + vwidth,
+ bottom_y - hwidth),
+ android_make_point (left_x, bottom_y));
+
+ if (top_p && right_p)
+ android_fill_triangle (f, raised_p ? white_gc : black_gc,
+ android_make_point (right_x - vwidth, top_y),
+ android_make_point (right_x, top_y),
+ android_make_point (right_x - vwidth,
+ top_y + hwidth));
+
+ /* Draw outer line. */
+
+ if (top_p && left_p && bot_p && right_p
+ && hwidth > 1 && vwidth > 1)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ black_gc, left_x, top_y,
+ right_x - left_x, bottom_y - top_y);
+ else
+ {
+ if (top_p && hwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, top_y,
+ right_x + 1, top_y);
+
+ if (bot_p && hwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, bottom_y,
+ right_x + 1, bottom_y);
+
+ if (left_p && vwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, top_y,
+ left_x, bottom_y + 1);
+
+ if (right_p && vwidth > 1)
+ android_draw_line (drawable, black_gc, right_x, top_y,
+ right_x, bottom_y + 1);
+ }
+
+ /* Erase corners. */
+
+ if (hwidth > 1 && vwidth > 1)
+ {
+ if (left_p && top_p && android_inside_rect_p (clip_rect, 1,
+ left_x, top_y))
+ android_clear_point (f, normal_gc, left_x, top_y);
+
+ if (left_p && bot_p && android_inside_rect_p (clip_rect, 1,
+ left_x, bottom_y))
+ android_clear_point (f, normal_gc, left_x, bottom_y);
+
+ if (right_p && top_p && android_inside_rect_p (clip_rect, 1,
+ right_x, top_y))
+ android_clear_point (f, normal_gc, right_x, top_y);
+
+ if (right_p && bot_p && android_inside_rect_p (clip_rect, 1,
+ right_x, bottom_y))
+ android_clear_point (f, normal_gc, right_x, bottom_y);
+ }
+
+ android_reset_clip_rectangles (f, white_gc);
+ android_reset_clip_rectangles (f, black_gc);
+}
+
+static void
+android_draw_box_rect (struct glyph_string *s,
+ int left_x, int top_y, int right_x, int bottom_y,
+ int hwidth, int vwidth, bool left_p, bool right_p,
+ struct android_rectangle *clip_rect)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->box_color);
+ android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1);
+
+ /* Top. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ top_y, right_x - left_x + 1, hwidth);
+
+ /* Left. */
+ if (left_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ top_y, vwidth, bottom_y - top_y + 1);
+
+ /* Bottom. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ bottom_y - hwidth + 1, right_x - left_x + 1,
+ hwidth);
+
+ /* Right. */
+ if (right_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ right_x - vwidth + 1, top_y, vwidth,
+ bottom_y - top_y + 1);
+
+ android_set_foreground (s->gc, xgcv.foreground);
+ android_reset_clip_rectangles (s->f, s->gc);
+}
+
+#define HIGHLIGHT_COLOR_DARK_BOOST_LIMIT 48000
+
+static bool
+android_alloc_lighter_color (struct frame *f, unsigned long *pixel,
+ double factor, int delta)
+{
+ Emacs_Color color, new;
+ long bright;
+ bool success_p;
+
+ /* Get RGB color values. */
+ color.pixel = *pixel;
+ android_query_colors (f, &color, 1);
+
+ /* Change RGB values by specified FACTOR. Avoid overflow! */
+ eassert (factor >= 0);
+ new.red = min (0xffff, factor * color.red);
+ new.green = min (0xffff, factor * color.green);
+ new.blue = min (0xffff, factor * color.blue);
+
+ /* Calculate brightness of COLOR. */
+ bright = (2 * color.red + 3 * color.green + color.blue) / 6;
+
+ /* We only boost colors that are darker than
+ HIGHLIGHT_COLOR_DARK_BOOST_LIMIT. */
+ if (bright < HIGHLIGHT_COLOR_DARK_BOOST_LIMIT)
+ /* Make an additive adjustment to NEW, because it's dark enough so
+ that scaling by FACTOR alone isn't enough. */
+ {
+ /* How far below the limit this color is (0 - 1, 1 being darker). */
+ double dimness = 1 - (double) bright / HIGHLIGHT_COLOR_DARK_BOOST_LIMIT;
+ /* The additive adjustment. */
+ int min_delta = delta * dimness * factor / 2;
+
+ if (factor < 1)
+ {
+ new.red = max (0, new.red - min_delta);
+ new.green = max (0, new.green - min_delta);
+ new.blue = max (0, new.blue - min_delta);
+ }
+ else
+ {
+ new.red = min (0xffff, min_delta + new.red);
+ new.green = min (0xffff, min_delta + new.green);
+ new.blue = min (0xffff, min_delta + new.blue);
+ }
+ }
+
+ /* Try to allocate the color. */
+ success_p = android_alloc_nearest_color (f, &new);
+
+ if (success_p)
+ {
+ if (new.pixel == *pixel)
+ {
+ /* If we end up with the same color as before, try adding
+ delta to the RGB values. */
+ new.red = min (0xffff, delta + color.red);
+ new.green = min (0xffff, delta + color.green);
+ new.blue = min (0xffff, delta + color.blue);
+ success_p = android_alloc_nearest_color (f, &new);
+ }
+ else
+ success_p = true;
+
+ *pixel = new.pixel;
+ }
+
+ return success_p;
+}
+
+/* Set up the foreground color for drawing relief lines of glyph
+ string S. RELIEF is a pointer to a struct relief containing the GC
+ with which lines will be drawn. Use a color that is FACTOR or
+ DELTA lighter or darker than the relief's background which is found
+ in S->f->output_data.android->relief_background. If such a color
+ cannot be allocated, use DEFAULT_PIXEL, instead. */
+
+static void
+android_setup_relief_color (struct frame *f, struct relief *relief,
+ double factor, int delta,
+ unsigned long default_pixel)
+{
+ struct android_gc_values xgcv;
+ struct android_output *di = f->output_data.android;
+ unsigned long mask = ANDROID_GC_FOREGROUND;
+ unsigned long pixel;
+ unsigned long background = di->relief_background;
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ if (relief->gc && relief->pixel != -1)
+ relief->pixel = -1;
+
+ /* Allocate new color. */
+ xgcv.foreground = default_pixel;
+ pixel = background;
+
+ if (dpyinfo->n_planes != 1
+ && android_alloc_lighter_color (f, &pixel, factor, delta))
+ xgcv.foreground = relief->pixel = pixel;
+
+ if (relief->gc == 0)
+ relief->gc = android_create_gc (mask, &xgcv);
+ else
+ android_change_gc (relief->gc, mask, &xgcv);
+}
+
+/* Set up colors for the relief lines around glyph string S. */
+
+static void
+android_setup_relief_colors (struct glyph_string *s)
+{
+ struct android_output *di;
+ unsigned long color;
+
+ di = s->f->output_data.android;
+
+ if (s->face->use_box_color_for_shadows_p)
+ color = s->face->box_color;
+ else if (s->first_glyph->type == IMAGE_GLYPH
+ && s->img->pixmap
+ && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
+ color = IMAGE_BACKGROUND (s->img, s->f, 0);
+ else
+ {
+ struct android_gc_values xgcv;
+
+ /* Get the background color of the face. */
+ android_get_gc_values (s->gc, ANDROID_GC_BACKGROUND, &xgcv);
+ color = xgcv.background;
+ }
+
+ if (di->white_relief.gc == 0
+ || color != di->relief_background)
+ {
+ di->relief_background = color;
+ android_setup_relief_color (s->f, &di->white_relief, 1.2, 0x8000,
+ WHITE_PIX_DEFAULT (s->f));
+ android_setup_relief_color (s->f, &di->black_relief, 0.6, 0x4000,
+ BLACK_PIX_DEFAULT (s->f));
+ }
+}
+
+static void
+android_draw_glyph_string_box (struct glyph_string *s)
+{
+ int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
+ bool raised_p, left_p, right_p;
+ struct glyph *last_glyph;
+ struct android_rectangle clip_rect;
+
+ last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+ ? WINDOW_RIGHT_EDGE_X (s->w)
+ : window_box_right (s->w, s->area));
+
+ /* The glyph that may have a right box line. For static
+ compositions and images, the right-box flag is on the first glyph
+ of the glyph string; for other types it's on the last glyph. */
+ if (s->cmp || s->img)
+ last_glyph = s->first_glyph;
+ else if (s->first_glyph->type == COMPOSITE_GLYPH
+ && s->first_glyph->u.cmp.automatic)
+ {
+ /* For automatic compositions, we need to look up the last glyph
+ in the composition. */
+ struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
+ struct glyph *g = s->first_glyph;
+ for (last_glyph = g++;
+ g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
+ && g->slice.cmp.to < s->cmp_to;
+ last_glyph = g++)
+ ;
+ }
+ else
+ last_glyph = s->first_glyph + s->nchars - 1;
+
+ vwidth = eabs (s->face->box_vertical_line_width);
+ hwidth = eabs (s->face->box_horizontal_line_width);
+ raised_p = s->face->box == FACE_RAISED_BOX;
+ left_x = s->x;
+ right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+ ? last_x - 1
+ : min (last_x, s->x + s->background_width) - 1);
+ top_y = s->y;
+ bottom_y = top_y + s->height - 1;
+
+ left_p = (s->first_glyph->left_box_line_p
+ || (s->hl == DRAW_MOUSE_FACE
+ && (s->prev == NULL
+ || s->prev->hl != s->hl)));
+ right_p = (last_glyph->right_box_line_p
+ || (s->hl == DRAW_MOUSE_FACE
+ && (s->next == NULL
+ || s->next->hl != s->hl)));
+
+ get_glyph_string_clip_rect (s, &clip_rect);
+
+ if (s->face->box == FACE_SIMPLE_BOX)
+ android_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+ vwidth, left_p, right_p, &clip_rect);
+ else
+ {
+ android_setup_relief_colors (s);
+ android_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y, hwidth,
+ vwidth, raised_p, true, true, left_p, right_p,
+ &clip_rect);
+ }
+}
+
+static void
+android_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y,
+ int w, int h)
+{
+ if (s->stippled_p)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x,
+ y, w, h);
+ android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+ }
+ else
+ android_clear_glyph_string_rect (s, x, y, w, h);
+}
+
+static void
+android_draw_image_relief (struct glyph_string *s)
+{
+ int x1, y1, thick;
+ bool raised_p, top_p, bot_p, left_p, right_p;
+ int extra_x, extra_y;
+ struct android_rectangle r;
+ int x = s->x;
+ int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+ /* If first glyph of S has a left box line, start drawing it to the
+ right of that line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ x += max (s->face->box_vertical_line_width, 0);
+
+ /* If there is a margin around the image, adjust x- and y-position
+ by that margin. */
+ if (s->slice.x == 0)
+ x += s->img->hmargin;
+ if (s->slice.y == 0)
+ y += s->img->vmargin;
+
+ if (s->hl == DRAW_IMAGE_SUNKEN
+ || s->hl == DRAW_IMAGE_RAISED)
+ {
+ if (s->face->id == TAB_BAR_FACE_ID)
+ thick = (tab_bar_button_relief < 0
+ ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+ : min (tab_bar_button_relief, 1000000));
+ else
+ thick = (tool_bar_button_relief < 0
+ ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
+ : min (tool_bar_button_relief, 1000000));
+ raised_p = s->hl == DRAW_IMAGE_RAISED;
+ }
+ else
+ {
+ thick = eabs (s->img->relief);
+ raised_p = s->img->relief > 0;
+ }
+
+ x1 = x + s->slice.width - 1;
+ y1 = y + s->slice.height - 1;
+
+ extra_x = extra_y = 0;
+ if (s->face->id == TAB_BAR_FACE_ID)
+ {
+ if (CONSP (Vtab_bar_button_margin)
+ && FIXNUMP (XCAR (Vtab_bar_button_margin))
+ && FIXNUMP (XCDR (Vtab_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)) - thick;
+ extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
+ }
+ else if (FIXNUMP (Vtab_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
+ }
+
+ if (s->face->id == TOOL_BAR_FACE_ID)
+ {
+ if (CONSP (Vtool_bar_button_margin)
+ && FIXNUMP (XCAR (Vtool_bar_button_margin))
+ && FIXNUMP (XCDR (Vtool_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+ extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+ }
+ else if (FIXNUMP (Vtool_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
+ }
+
+ top_p = bot_p = left_p = right_p = false;
+
+ if (s->slice.x == 0)
+ x -= thick + extra_x, left_p = true;
+ if (s->slice.y == 0)
+ y -= thick + extra_y, top_p = true;
+ if (s->slice.x + s->slice.width == s->img->width)
+ x1 += thick + extra_x, right_p = true;
+ if (s->slice.y + s->slice.height == s->img->height)
+ y1 += thick + extra_y, bot_p = true;
+
+ android_setup_relief_colors (s);
+ get_glyph_string_clip_rect (s, &r);
+ android_draw_relief_rect (s->f, x, y, x1, y1, thick, thick, raised_p,
+ top_p, bot_p, left_p, right_p, &r);
+}
+
+static void
+android_draw_image_foreground (struct glyph_string *s)
+{
+ int x = s->x;
+ int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+ /* If first glyph of S has a left box line, start drawing it to the
+ right of that line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ x += max (s->face->box_vertical_line_width, 0);
+
+ /* If there is a margin around the image, adjust x- and y-position
+ by that margin. */
+ if (s->slice.x == 0)
+ x += s->img->hmargin;
+ if (s->slice.y == 0)
+ y += s->img->vmargin;
+
+ if (s->img->pixmap)
+ {
+ unsigned long mask = (ANDROID_GC_CLIP_MASK
+ | ANDROID_GC_CLIP_X_ORIGIN
+ | ANDROID_GC_CLIP_Y_ORIGIN
+ | ANDROID_GC_FUNCTION);
+ struct android_gc_values xgcv;
+ struct android_rectangle clip_rect, image_rect, r;
+
+ xgcv.clip_mask = s->img->mask;
+ xgcv.clip_x_origin = x - s->slice.x;
+ xgcv.clip_y_origin = y - s->slice.y;
+ xgcv.function = ANDROID_GC_COPY;
+ android_change_gc (s->gc, mask, &xgcv);
+
+ get_glyph_string_clip_rect (s, &clip_rect);
+ image_rect.x = x;
+ image_rect.y = y;
+ image_rect.width = s->slice.width;
+ image_rect.height = s->slice.height;
+
+ if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
+ android_copy_area (s->img->pixmap,
+ FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, s->slice.x + r.x - x,
+ s->slice.y + r.y - y,
+ r.width, r.height, r.x, r.y);
+
+ /* When the image has a mask, we can expect that at least part
+ of a mouse highlight or a block cursor will be visible. If
+ the image doesn't have a mask, make a block cursor visible by
+ drawing a rectangle around the image. I believe it's looking
+ better if we do nothing here for mouse-face. */
+ if (s->hl == DRAW_CURSOR && !s->img->mask)
+ {
+ int relief = eabs (s->img->relief);
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x - relief, y - relief,
+ s->slice.width + relief*2 - 1,
+ s->slice.height + relief*2 - 1);
+ }
+
+ android_set_clip_mask (s->gc, ANDROID_NONE);
+ }
+ else
+ /* Draw a rectangle if image could not be loaded. */
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y,
+ s->slice.width - 1, s->slice.height - 1);
+}
+
+static void
+android_draw_image_glyph_string (struct glyph_string *s)
+{
+ int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+ int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
+ int height;
+
+ height = s->height;
+ if (s->slice.y == 0)
+ height -= box_line_vwidth;
+ if (s->slice.y + s->slice.height >= s->img->height)
+ height -= box_line_vwidth;
+
+ /* Fill background with face under the image. Do it only if row is
+ taller than image or if image has a clip mask to reduce
+ flickering. */
+ s->stippled_p = s->face->stipple != 0;
+ if (height > s->slice.height
+ || s->img->hmargin
+ || s->img->vmargin
+ || s->img->mask
+ || s->img->pixmap == 0
+ || s->width != s->background_width)
+ {
+ if (s->stippled_p)
+ s->row->stipple_p = true;
+
+ int x = s->x;
+ int y = s->y;
+ int width = s->background_width;
+
+ if (s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ {
+ x += box_line_hwidth;
+ width -= box_line_hwidth;
+ }
+
+ if (s->slice.y == 0)
+ y += box_line_vwidth;
+
+ android_draw_glyph_string_bg_rect (s, x, y, width, height);
+
+ s->background_filled_p = true;
+ }
+
+ /* Draw the foreground. */
+ android_draw_image_foreground (s);
+ android_set_glyph_string_clipping (s);
+
+ /* If we must draw a relief around the image, do it. */
+ if (s->img->relief
+ || s->hl == DRAW_IMAGE_RAISED
+ || s->hl == DRAW_IMAGE_SUNKEN)
+ android_draw_image_relief (s);
+}
+
+static void
+android_draw_stretch_glyph_string (struct glyph_string *s)
+{
+ eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+ if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
+ {
+ /* If `x-stretch-cursor' is nil, don't draw a block cursor as
+ wide as the stretch glyph. */
+ int width, background_width = s->background_width;
+ int x = s->x;
+
+ if (!s->row->reversed_p)
+ {
+ int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+ if (x < left_x)
+ {
+ background_width -= left_x - x;
+ x = left_x;
+ }
+ }
+ else
+ {
+ /* In R2L rows, draw the cursor on the right edge of the
+ stretch glyph. */
+ int right_x = window_box_right (s->w, TEXT_AREA);
+
+ if (x + background_width > right_x)
+ background_width -= x - right_x;
+ x += background_width;
+ }
+ width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+ if (s->row->reversed_p)
+ x -= width;
+
+ /* Draw cursor. */
+ android_draw_glyph_string_bg_rect (s, x, s->y, width, s->height);
+
+ /* Clear rest using the GC of the original non-cursor face. */
+ if (width < background_width)
+ {
+ int y = s->y;
+ int w = background_width - width, h = s->height;
+ struct android_rectangle r;
+ struct android_gc *gc;
+
+ if (!s->row->reversed_p)
+ x += width;
+ else
+ x = s->x;
+ if (s->row->mouse_face_p
+ && cursor_in_mouse_face_p (s->w))
+ {
+ android_set_mouse_face_gc (s);
+ gc = s->gc;
+ }
+ else
+ gc = s->face->gc;
+
+ get_glyph_string_clip_rect (s, &r);
+ android_set_clip_rectangles (gc, 0, 0, &r, 1);
+
+ if (s->face->stipple)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ gc, x, y, w, h);
+ android_set_fill_style (gc, ANDROID_FILL_SOLID);
+
+ s->row->stipple_p = true;
+ }
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (gc, (ANDROID_GC_FOREGROUND
+ | ANDROID_GC_BACKGROUND),
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ gc, x, y, w, h);
+ android_set_foreground (gc, xgcv.foreground);
+ }
+
+ android_reset_clip_rectangles (s->f, gc);
+ }
+ }
+ else if (!s->background_filled_p)
+ {
+ int background_width = s->background_width;
+ int x = s->x, text_left_x = window_box_left (s->w, TEXT_AREA);
+
+ /* Don't draw into left fringe or scrollbar area except for
+ header line and mode line. */
+ if (s->area == TEXT_AREA
+ && x < text_left_x && !s->row->mode_line_p)
+ {
+ background_width -= text_left_x - x;
+ x = text_left_x;
+ }
+
+ if (!s->row->stipple_p)
+ s->row->stipple_p = s->stippled_p;
+
+ if (background_width > 0)
+ android_draw_glyph_string_bg_rect (s, x, s->y,
+ background_width,
+ s->height);
+ }
+
+ s->background_filled_p = true;
+}
+
+static void
+android_get_scale_factor (int *scale_x, int *scale_y)
+{
+ /* This is 96 everywhere else, but 160 on Android. */
+ const int base_res = 160;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = x_display_list;
+ *scale_x = *scale_y = 1;
+
+ if (dpyinfo)
+ {
+ if (dpyinfo->resx > base_res)
+ *scale_x = floor (dpyinfo->resx / base_res);
+ if (dpyinfo->resy > base_res)
+ *scale_y = floor (dpyinfo->resy / base_res);
+ }
+}
+
+static void
+android_draw_underwave (struct glyph_string *s, int decoration_width)
+{
+ int scale_x, scale_y;
+
+ android_get_scale_factor (&scale_x, &scale_y);
+
+ int wave_height = 3 * scale_y, wave_length = 2 * scale_x;
+
+ int dx, dy, x0, y0, width, x1, y1, x2, y2, xmax;
+ bool odd;
+ struct android_rectangle wave_clip, string_clip, final_clip;
+
+ dx = wave_length;
+ dy = wave_height - 1;
+ x0 = s->x;
+ y0 = s->ybase + wave_height / 2;
+ width = decoration_width;
+ xmax = x0 + width;
+
+ /* Find and set clipping rectangle */
+
+ wave_clip.x = x0;
+ wave_clip.y = y0;
+ wave_clip.width = width;
+ wave_clip.height = wave_height;
+ get_glyph_string_clip_rect (s, &string_clip);
+
+ if (!gui_intersect_rectangles (&wave_clip, &string_clip, &final_clip))
+ return;
+
+ android_set_clip_rectangles (s->gc, 0, 0, &final_clip, 1);
+
+ /* Draw the waves */
+
+ x1 = x0 - (x0 % dx);
+ x2 = x1 + dx;
+ odd = (x1 / dx) & 1;
+ y1 = y2 = y0;
+
+ if (odd)
+ y1 += dy;
+ else
+ y2 += dy;
+
+ if (INT_MAX - dx < xmax)
+ emacs_abort ();
+
+ while (x1 <= xmax)
+ {
+ android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x1, y1, x2, y2);
+ x1 = x2, y1 = y2;
+ x2 += dx, y2 = y0 + odd*dy;
+ odd = !odd;
+ }
+
+ /* Restore previous clipping rectangle(s) */
+ android_set_clip_rectangles (s->gc, 0, 0, s->clip, s->num_clips);
+}
+
+static void
+android_draw_glyph_string_foreground (struct glyph_string *s)
+{
+ int i, x;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ /* Draw characters of S as rectangles if S's font could not be
+ loaded. */
+ if (s->font_not_found_p)
+ {
+ for (i = 0; i < s->nchars; ++i)
+ {
+ struct glyph *g = s->first_glyph + i;
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, x, s->y,
+ g->pixel_width - 1,
+ s->height - 1);
+ x += g->pixel_width;
+ }
+ }
+ else
+ {
+ struct font *font = s->font;
+ int boff = font->baseline_offset;
+ int y;
+
+ if (font->vertical_centering)
+ boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff;
+
+ y = s->ybase - boff;
+ if (s->for_overlaps
+ || (s->background_filled_p && s->hl != DRAW_CURSOR))
+ font->driver->draw (s, 0, s->nchars, x, y, false);
+ else
+ font->driver->draw (s, 0, s->nchars, x, y, true);
+ if (s->face->overstrike)
+ font->driver->draw (s, 0, s->nchars, x + 1, y, false);
+ }
+}
+
+static void
+android_draw_composite_glyph_string_foreground (struct glyph_string *s)
+{
+ int i, j, x;
+ struct font *font = s->font;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face && s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ /* S is a glyph string for a composition. S->cmp_from is the index
+ of the first character drawn for glyphs of this composition.
+ S->cmp_from == 0 means we are drawing the very first character of
+ this composition. */
+
+ /* Draw a rectangle for the composition if the font for the very
+ first character of the composition could not be loaded. */
+ if (s->font_not_found_p)
+ {
+ if (s->cmp_from == 0)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, x, s->y,
+ s->width - 1, s->height - 1);
+ }
+ else if (! s->first_glyph->u.cmp.automatic)
+ {
+ int y = s->ybase;
+
+ for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
+ /* TAB in a composition means display glyphs with
+ padding space on the left or right. */
+ if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
+ {
+ int xx = x + s->cmp->offsets[j * 2];
+ int yy = y - s->cmp->offsets[j * 2 + 1];
+
+ font->driver->draw (s, j, j + 1, xx, yy, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+ }
+ }
+ else
+ {
+ Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+ Lisp_Object glyph;
+ int y = s->ybase;
+ int width = 0;
+
+ for (i = j = s->cmp_from; i < s->cmp_to; i++)
+ {
+ glyph = LGSTRING_GLYPH (gstring, i);
+ if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+ width += LGLYPH_WIDTH (glyph);
+ else
+ {
+ int xoff, yoff, wadjust;
+
+ if (j < i)
+ {
+ font->driver->draw (s, j, i, x, y, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, i, x + 1, y, false);
+ x += width;
+ }
+ xoff = LGLYPH_XOFF (glyph);
+ yoff = LGLYPH_YOFF (glyph);
+ wadjust = LGLYPH_WADJUST (glyph);
+ font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+ false);
+ x += wadjust;
+ j = i + 1;
+ width = 0;
+ }
+ }
+ if (j < i)
+ {
+ font->driver->draw (s, j, i, x, y, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, i, x + 1, y, false);
+ }
+ }
+}
+
+static void
+android_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
+{
+ struct glyph *glyph = s->first_glyph;
+ unsigned char2b[8];
+ int x, i, j;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face && s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ s->char2b = char2b;
+
+ for (i = 0; i < s->nchars; i++, glyph++)
+ {
+#ifdef GCC_LINT
+ enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+ enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+ char buf[7 + PACIFY_GCC_BUG_81401];
+ char *str = NULL;
+ int len = glyph->u.glyphless.len;
+
+ if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
+ {
+ if (len > 0
+ && CHAR_TABLE_P (Vglyphless_char_display)
+ && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
+ >= 1))
+ {
+ Lisp_Object acronym
+ = (! glyph->u.glyphless.for_no_font
+ ? CHAR_TABLE_REF (Vglyphless_char_display,
+ glyph->u.glyphless.ch)
+ : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
+ if (CONSP (acronym))
+ acronym = XCAR (acronym);
+ if (STRINGP (acronym))
+ str = SSDATA (acronym);
+ }
+ }
+ else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
+ {
+ unsigned int ch = glyph->u.glyphless.ch;
+ eassume (ch <= MAX_CHAR);
+ sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+ str = buf;
+ }
+
+ if (str)
+ {
+ int upper_len = (len + 1) / 2;
+
+ /* It is assured that all LEN characters in STR is ASCII. */
+ for (j = 0; j < len; j++)
+ char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
+ s->font->driver->draw (s, 0, upper_len,
+ x + glyph->slice.glyphless.upper_xoff,
+ s->ybase + glyph->slice.glyphless.upper_yoff,
+ false);
+ s->font->driver->draw (s, upper_len, len,
+ x + glyph->slice.glyphless.lower_xoff,
+ s->ybase + glyph->slice.glyphless.lower_yoff,
+ false);
+ }
+ if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x, s->ybase - glyph->ascent,
+ glyph->pixel_width - 1,
+ glyph->ascent + glyph->descent - 1);
+ x += glyph->pixel_width;
+ }
+
+ /* Defend against hypothetical bad code elsewhere that uses
+ s->char2b after this function returns. */
+ s->char2b = NULL;
+}
+
+static void
+android_draw_glyph_string (struct glyph_string *s)
+{
+ bool relief_drawn_p = false;
+
+ /* If S draws into the background of its successors, draw the
+ background of the successors first so that S can draw into it.
+ This makes S->next use XDrawString instead of XDrawImageString. */
+ if (s->next && s->right_overhang && !s->for_overlaps)
+ {
+ int width;
+ struct glyph_string *next;
+
+ for (width = 0, next = s->next;
+ next && width < s->right_overhang;
+ width += next->width, next = next->next)
+ if (next->first_glyph->type != IMAGE_GLYPH)
+ {
+ android_set_glyph_string_gc (next);
+ android_set_glyph_string_clipping (next);
+ if (next->first_glyph->type == STRETCH_GLYPH)
+ android_draw_stretch_glyph_string (next);
+ else
+ android_draw_glyph_string_background (next, true);
+ next->num_clips = 0;
+ }
+ }
+
+ /* Set up S->gc, set clipping and draw S. */
+ android_set_glyph_string_gc (s);
+
+ /* Draw relief (if any) in advance for char/composition so that the
+ glyph string can be drawn over it. */
+ if (!s->for_overlaps
+ && s->face->box != FACE_NO_BOX
+ && (s->first_glyph->type == CHAR_GLYPH
+ || s->first_glyph->type == COMPOSITE_GLYPH))
+
+ {
+ android_set_glyph_string_clipping (s);
+ android_draw_glyph_string_background (s, true);
+ android_draw_glyph_string_box (s);
+ android_set_glyph_string_clipping (s);
+ relief_drawn_p = true;
+ }
+ else if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+ && !s->clip_tail
+ && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+ || (s->next && s->next->hl != s->hl && s->right_overhang)))
+ /* We must clip just this glyph. left_overhang part has already
+ drawn when s->prev was drawn, and right_overhang part will be
+ drawn later when s->next is drawn. */
+ android_set_glyph_string_clipping_exactly (s, s);
+ else
+ android_set_glyph_string_clipping (s);
+
+ switch (s->first_glyph->type)
+ {
+ case IMAGE_GLYPH:
+ android_draw_image_glyph_string (s);
+ break;
+
+ case XWIDGET_GLYPH:
+ emacs_abort ();
+ break;
+
+ case STRETCH_GLYPH:
+ android_draw_stretch_glyph_string (s);
+ break;
+
+ case CHAR_GLYPH:
+ if (s->for_overlaps)
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, false);
+ android_draw_glyph_string_foreground (s);
+ break;
+
+ case COMPOSITE_GLYPH:
+ if (s->for_overlaps || (s->cmp_from > 0
+ && ! s->first_glyph->u.cmp.automatic))
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, true);
+ android_draw_composite_glyph_string_foreground (s);
+ break;
+
+ case GLYPHLESS_GLYPH:
+ if (s->for_overlaps)
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, true);
+ android_draw_glyphless_glyph_string_foreground (s);
+ break;
+
+ default:
+ emacs_abort ();
+ }
+
+ if (!s->for_overlaps)
+ {
+ int area_x, area_y, area_width, area_height;
+ int area_max_x, decoration_width;
+
+ /* Prevent the underline from overwriting surrounding areas
+ and the fringe. */
+ window_box (s->w, s->area, &area_x, &area_y,
+ &area_width, &area_height);
+ area_max_x = area_x + area_width - 1;
+
+ decoration_width = s->width;
+ if (!s->row->mode_line_p
+ && !s->row->tab_line_p
+ && area_max_x < (s->x + decoration_width - 1))
+ decoration_width -= (s->x + decoration_width - 1) - area_max_x;
+
+ /* Draw relief if not yet drawn. */
+ if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+ android_draw_glyph_string_box (s);
+
+ /* Draw underline. */
+ if (s->face->underline)
+ {
+ if (s->face->underline == FACE_UNDER_WAVE)
+ {
+ if (s->face->underline_defaulted_p)
+ android_draw_underwave (s, decoration_width);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->underline_color);
+ android_draw_underwave (s, decoration_width);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+ else if (s->face->underline == FACE_UNDER_LINE)
+ {
+ unsigned long thickness, position;
+ int y;
+
+ if (s->prev
+ && s->prev->face->underline == FACE_UNDER_LINE
+ && (s->prev->face->underline_at_descent_line_p
+ == s->face->underline_at_descent_line_p)
+ && (s->prev->face->underline_pixels_above_descent_line
+ == s->face->underline_pixels_above_descent_line))
+ {
+ /* We use the same underline style as the previous one. */
+ thickness = s->prev->underline_thickness;
+ position = s->prev->underline_position;
+ }
+ else
+ {
+ struct font *font = font_for_underline_metrics (s);
+ unsigned long minimum_offset;
+ bool underline_at_descent_line;
+ bool use_underline_position_properties;
+ Lisp_Object val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qunderline_minimum_offset, s->w));
+
+ if (FIXNUMP (val))
+ minimum_offset = max (0, XFIXNUM (val));
+ else
+ minimum_offset = 1;
+
+ val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qx_underline_at_descent_line, s->w));
+ underline_at_descent_line
+ = (!(NILP (val) || BASE_EQ (val, Qunbound))
+ || s->face->underline_at_descent_line_p);
+
+ val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qx_use_underline_position_properties, s->w));
+ use_underline_position_properties
+ = !(NILP (val) || BASE_EQ (val, Qunbound));
+
+ /* Get the underline thickness. Default is 1 pixel. */
+ if (font && font->underline_thickness > 0)
+ thickness = font->underline_thickness;
+ else
+ thickness = 1;
+ if (underline_at_descent_line)
+ position = ((s->height - thickness)
+ - (s->ybase - s->y)
+ - s->face->underline_pixels_above_descent_line);
+ else
+ {
+ /* Get the underline position. This is the
+ recommended vertical offset in pixels from
+ the baseline to the top of the underline.
+ This is a signed value according to the
+ specs, and its default is
+
+ ROUND ((maximum descent) / 2), with
+ ROUND(x) = floor (x + 0.5) */
+
+ if (use_underline_position_properties
+ && font && font->underline_position >= 0)
+ position = font->underline_position;
+ else if (font)
+ position = (font->descent + 1) / 2;
+ else
+ position = minimum_offset;
+ }
+
+ /* Ignore minimum_offset if the amount of pixels was
+ explicitly specified. */
+ if (!s->face->underline_pixels_above_descent_line)
+ position = max (position, minimum_offset);
+ }
+ /* Check the sanity of thickness and position. We should
+ avoid drawing underline out of the current line area. */
+ if (s->y + s->height <= s->ybase + position)
+ position = (s->height - 1) - (s->ybase - s->y);
+ if (s->y + s->height < s->ybase + position + thickness)
+ thickness = (s->y + s->height) - (s->ybase + position);
+ s->underline_thickness = thickness;
+ s->underline_position = position;
+ y = s->ybase + position;
+ if (s->face->underline_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, y, decoration_width, thickness);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->underline_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, y, decoration_width, thickness);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+ }
+ /* Draw overline. */
+ if (s->face->overline_p)
+ {
+ unsigned long dy = 0, h = 1;
+
+ if (s->face->overline_color_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, s->x, s->y + dy,
+ decoration_width, h);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->overline_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, s->y + dy, decoration_width, h);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+
+ /* Draw strike-through. */
+ if (s->face->strike_through_p)
+ {
+ /* Y-coordinate and height of the glyph string's first
+ glyph. We cannot use s->y and s->height because those
+ could be larger if there are taller display elements
+ (e.g., characters displayed with a larger font) in the
+ same glyph row. */
+ int glyph_y = s->ybase - s->first_glyph->ascent;
+ int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
+ /* Strike-through width and offset from the glyph string's
+ top edge. */
+ unsigned long h = 1;
+ unsigned long dy = (glyph_height - h) / 2;
+
+ if (s->face->strike_through_color_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+ s->gc, s->x, glyph_y + dy,
+ s->width, h);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->strike_through_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, glyph_y + dy, decoration_width,
+ h);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+
+ if (s->prev)
+ {
+ struct glyph_string *prev;
+
+ for (prev = s->prev; prev; prev = prev->prev)
+ if (prev->hl != s->hl
+ && prev->x + prev->width + prev->right_overhang > s->x)
+ {
+ /* As prev was drawn while clipped to its own area, we
+ must draw the right_overhang part using s->hl now. */
+ enum draw_glyphs_face save = prev->hl;
+
+ prev->hl = s->hl;
+ android_set_glyph_string_gc (prev);
+ android_set_glyph_string_clipping_exactly (s, prev);
+ if (prev->first_glyph->type == CHAR_GLYPH)
+ android_draw_glyph_string_foreground (prev);
+ else
+ android_draw_composite_glyph_string_foreground (prev);
+ android_reset_clip_rectangles (prev->f, prev->gc);
+ prev->hl = save;
+ prev->num_clips = 0;
+ }
+ }
+
+ if (s->next)
+ {
+ struct glyph_string *next;
+
+ for (next = s->next; next; next = next->next)
+ if (next->hl != s->hl
+ && next->x - next->left_overhang < s->x + s->width)
+ {
+ /* As next will be drawn while clipped to its own area,
+ we must draw the left_overhang part using s->hl now. */
+ enum draw_glyphs_face save = next->hl;
+
+ next->hl = s->hl;
+ android_set_glyph_string_gc (next);
+ android_set_glyph_string_clipping_exactly (s, next);
+ if (next->first_glyph->type == CHAR_GLYPH)
+ android_draw_glyph_string_foreground (next);
+ else
+ android_draw_composite_glyph_string_foreground (next);
+ android_reset_clip_rectangles (next->f, next->gc);
+ next->hl = save;
+ next->num_clips = 0;
+ next->clip_head = s->next;
+ }
+ }
+ }
+
+ /* Reset clipping. */
+ android_reset_clip_rectangles (s->f, s->gc);
+ s->num_clips = 0;
+
+ /* Set the stippled flag that tells redisplay whether or not a
+ stipple was actually draw. */
+
+ if (s->first_glyph->type != STRETCH_GLYPH
+ && s->first_glyph->type != IMAGE_GLYPH
+ && !s->row->stipple_p)
+ s->row->stipple_p = s->stippled_p;
+}
+
+static void
+android_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
+{
+ if (!f->pointer_invisible
+ && !FRAME_ANDROID_OUTPUT (f)->hourglass
+ && f->output_data.android->current_cursor != cursor)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f), cursor);
+
+ f->output_data.android->current_cursor = cursor;
+}
+
+static void
+android_clear_frame_area (struct frame *f, int x, int y,
+ int width, int height)
+{
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+ x, y, width, height);
+}
+
+void
+android_clear_under_internal_border (struct frame *f)
+{
+ if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+ {
+ int border = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = FRAME_PIXEL_WIDTH (f);
+ int height = FRAME_PIXEL_HEIGHT (f);
+ int margin = FRAME_TOP_MARGIN_HEIGHT (f);
+ int face_id =
+ (FRAME_PARENT_FRAME (f)
+ ? (!NILP (Vface_remapping_alist)
+ ? lookup_basic_face (NULL, f, CHILD_FRAME_BORDER_FACE_ID)
+ : CHILD_FRAME_BORDER_FACE_ID)
+ : (!NILP (Vface_remapping_alist)
+ ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID)
+ : INTERNAL_BORDER_FACE_ID));
+ struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
+
+ if (face)
+ {
+ unsigned long color = face->background;
+ struct android_gc *gc = f->output_data.android->normal_gc;
+
+ android_set_foreground (gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin,
+ width, border);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0,
+ border, height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - border,
+ 0, border, height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0,
+ height - border, width, border);
+ android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f));
+ }
+ else
+ {
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0,
+ border, height);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+ margin, width, border);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border,
+ 0, border, height);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+ height - border, width, border);
+ }
+ }
+}
+
+static void
+android_draw_hollow_cursor (struct window *w, struct glyph_row *row)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ int x, y, wd, h;
+ struct android_gc_values xgcv;
+ struct glyph *cursor_glyph;
+ struct android_gc *gc;
+
+ /* Get the glyph the cursor is on. If we can't tell because
+ the current matrix is invalid or such, give up. */
+ cursor_glyph = get_phys_cursor_glyph (w);
+ if (cursor_glyph == NULL)
+ return;
+
+ /* Compute frame-relative coordinates for phys cursor. */
+ get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
+ wd = w->phys_cursor_width - 1;
+
+ /* The foreground of cursor_gc is typically the same as the normal
+ background color, which can cause the cursor box to be invisible. */
+ xgcv.foreground = f->output_data.android->cursor_pixel;
+ if (dpyinfo->scratch_cursor_gc)
+ android_change_gc (dpyinfo->scratch_cursor_gc,
+ ANDROID_GC_FOREGROUND, &xgcv);
+ else
+ dpyinfo->scratch_cursor_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND, &xgcv);
+ gc = dpyinfo->scratch_cursor_gc;
+
+ /* When on R2L character, show cursor at the right edge of the
+ glyph, unless the cursor box is as wide as the glyph or wider
+ (the latter happens when x-stretch-cursor is non-nil). */
+ if ((cursor_glyph->resolved_level & 1) != 0
+ && cursor_glyph->pixel_width > wd)
+ {
+ x += cursor_glyph->pixel_width - wd;
+ if (wd > 0)
+ wd -= 1;
+ }
+ /* Set clipping, draw the rectangle, and reset clipping again. */
+ android_clip_to_row (w, row, TEXT_AREA, gc);
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
+ android_reset_clip_rectangles (f, gc);
+}
+
+static void
+android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width,
+ enum text_cursor_kinds kind)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct glyph *cursor_glyph;
+ int cursor_start_y;
+
+ /* If cursor is out of bounds, don't draw garbage. This can happen
+ in mini-buffer windows when switching between echo area glyphs
+ and mini-buffer. */
+ cursor_glyph = get_phys_cursor_glyph (w);
+ if (cursor_glyph == NULL)
+ return;
+
+ /* Experimental avoidance of cursor on xwidget. */
+ if (cursor_glyph->type == XWIDGET_GLYPH)
+ return;
+
+ /* If on an image, draw like a normal cursor. That's usually better
+ visible than drawing a bar, esp. if the image is large so that
+ the bar might not be in the window. */
+ if (cursor_glyph->type == IMAGE_GLYPH)
+ {
+ struct glyph_row *r;
+ r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
+ draw_phys_cursor_glyph (w, r, DRAW_CURSOR);
+ }
+ else
+ {
+ struct android_gc *gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
+ unsigned long mask = ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND;
+ struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
+ struct android_gc_values xgcv;
+
+ /* If the glyph's background equals the color we normally draw
+ the bars cursor in, the bar cursor in its normal color is
+ invisible. Use the glyph's foreground color instead in this
+ case, on the assumption that the glyph's colors are chosen so
+ that the glyph is legible. */
+ if (face->background == f->output_data.android->cursor_pixel)
+ xgcv.background = xgcv.foreground = face->foreground;
+ else
+ xgcv.background = xgcv.foreground = f->output_data.android->cursor_pixel;
+
+ if (gc)
+ android_change_gc (gc, mask, &xgcv);
+ else
+ {
+ gc = android_create_gc (mask, &xgcv);
+ FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
+ }
+
+ android_clip_to_row (w, row, TEXT_AREA, gc);
+
+ if (kind == BAR_CURSOR)
+ {
+ int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+ if (width < 0)
+ width = FRAME_CURSOR_WIDTH (f);
+ width = min (cursor_glyph->pixel_width, width);
+
+ w->phys_cursor_width = width;
+
+ /* If the character under cursor is R2L, draw the bar cursor
+ on the right of its glyph, rather than on the left. */
+ if ((cursor_glyph->resolved_level & 1) != 0)
+ x += cursor_glyph->pixel_width - width;
+
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+ WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
+ width, row->height);
+ }
+ else /* HBAR_CURSOR */
+ {
+ int dummy_x, dummy_y, dummy_h;
+ int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+ if (width < 0)
+ width = row->height;
+
+ width = min (row->height, width);
+
+ get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x,
+ &dummy_y, &dummy_h);
+
+ cursor_start_y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y
+ + row->height - width);
+
+ if ((cursor_glyph->resolved_level & 1) != 0
+ && cursor_glyph->pixel_width > w->phys_cursor_width - 1)
+ x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+ cursor_start_y,
+ w->phys_cursor_width - 1, width);
+ }
+
+ android_reset_clip_rectangles (f, gc);
+ }
+}
+
+static void
+android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
+ int x, int y, enum text_cursor_kinds cursor_type,
+ int cursor_width, bool on_p, bool active_p)
+{
+ struct frame *f;
+
+ f = WINDOW_XFRAME (w);
+
+ if (on_p)
+ {
+ w->phys_cursor_type = cursor_type;
+ w->phys_cursor_on_p = true;
+
+ if (glyph_row->exact_window_width_line_p
+ && (glyph_row->reversed_p
+ ? (w->phys_cursor.hpos < 0)
+ : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
+ {
+ glyph_row->cursor_in_fringe_p = true;
+ draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
+ }
+ else
+ {
+ switch (cursor_type)
+ {
+ case HOLLOW_BOX_CURSOR:
+ android_draw_hollow_cursor (w, glyph_row);
+ break;
+
+ case FILLED_BOX_CURSOR:
+ draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+ break;
+
+ case BAR_CURSOR:
+ android_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
+ break;
+
+ case HBAR_CURSOR:
+ android_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
+ break;
+
+ case NO_CURSOR:
+ w->phys_cursor_width = 0;
+ break;
+
+ default:
+ emacs_abort ();
+ }
+ }
+
+ /* Now proceed to tell the input method the current position of
+ the cursor, if required. */
+
+ if (FRAME_OUTPUT_DATA (f)->need_cursor_updates
+ && w == XWINDOW (f->selected_window))
+ android_set_preeditarea (w, x, y);
+ }
+}
+
+static void
+android_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct face *face;
+
+ face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
+ if (face)
+ android_set_foreground (f->output_data.android->normal_gc,
+ face->foreground);
+
+ android_draw_line (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x, y0, x, y1);
+}
+
+static void
+android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
+ struct face *face_first
+ = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
+ struct face *face_last
+ = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
+ unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
+ unsigned long color_first = (face_first
+ ? face_first->foreground
+ : FRAME_FOREGROUND_PIXEL (f));
+ unsigned long color_last = (face_last
+ ? face_last->foreground
+ : FRAME_FOREGROUND_PIXEL (f));
+
+ if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
+ /* A vertical divider, at least three pixels wide: Draw first and
+ last pixels differently. */
+ {
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_first);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, 1, y1 - y0);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0 + 1, y0, x1 - x0 - 2, y1 - y0);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_last);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x1 - 1, y0, 1, y1 - y0);
+ }
+ else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
+ /* A horizontal divider, at least three pixels high: Draw first
+ and last pixels differently. */
+ {
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_first);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, x1 - x0, 1);
+ android_set_foreground (f->output_data.android->normal_gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0 + 1, x1 - x0, y1 - y0 - 2);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_last);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y1 - 1, x1 - x0, 1);
+ }
+ else
+ {
+ /* In any other case do not draw the first and last pixels
+ differently. */
+ android_set_foreground (f->output_data.android->normal_gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, x1 - x0, y1 - y0);
+ }
+}
+
+
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+/* Input method related functions. Some of these are called from Java
+ within the UI thread. */
+
+/* A counter used to decide when an editing request completes. */
+static unsigned long edit_counter;
+
+/* The last counter known to have completed. */
+static unsigned long last_edit_counter;
+
+/* Semaphore posted every time the counter increases. */
+static sem_t edit_sem;
+
+/* Try to synchronize with the UI thread, waiting a certain amount of
+ time for outstanding editing requests to complete.
+
+ Every time one of the text retrieval functions is called and an
+ editing request is made, Emacs gives the main thread approximately
+ 100 ms to process it, in order to mostly keep the input method in
+ sync with the buffer contents. */
+
+static void
+android_sync_edit (void)
+{
+ struct timespec start, end, rem;
+
+ if (__atomic_load_n (&last_edit_counter,
+ __ATOMIC_SEQ_CST)
+ == edit_counter)
+ return;
+
+ start = current_timespec ();
+ end = timespec_add (start, make_timespec (0, 100000000));
+
+ while (true)
+ {
+ rem = timespec_sub (end, current_timespec ());
+
+ /* Timeout. */
+ if (timespec_sign (rem) < 0)
+ break;
+
+ if (__atomic_load_n (&last_edit_counter,
+ __ATOMIC_SEQ_CST)
+ == edit_counter)
+ break;
+
+ sem_timedwait (&edit_sem, &end);
+ }
+}
+
+/* Return a copy of the specified Java string and its length in
+ *LENGTH. Use the JNI environment ENV. Value is NULL if copying
+ *the string fails. */
+
+static unsigned short *
+android_copy_java_string (JNIEnv *env, jstring string, size_t *length)
+{
+ jsize size, i;
+ const jchar *java;
+ unsigned short *buffer;
+
+ size = (*env)->GetStringLength (env, string);
+ buffer = malloc (size * sizeof *buffer);
+
+ if (!buffer)
+ return NULL;
+
+ java = (*env)->GetStringChars (env, string, NULL);
+
+ if (!java)
+ {
+ free (buffer);
+ return NULL;
+ }
+
+ for (i = 0; i < size; ++i)
+ buffer[i] = java[i];
+
+ *length = size;
+ (*env)->ReleaseStringChars (env, string, java);
+ return buffer;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_START_BATCH_EDIT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_END_BATCH_EDIT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitCompletion) (JNIEnv *env, jobject object, jshort window,
+ jstring completion_text, jint position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, completion_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitText) (JNIEnv *env, jobject object, jshort window,
+ jstring commit_text, jint position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, commit_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (deleteSurroundingText) (JNIEnv *env, jobject object,
+ jshort window, jint left_length,
+ jint right_length)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_DELETE_SURROUNDING_TEXT;
+ event.ime.start = left_length;
+ event.ime.end = right_length;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+/* Structure describing the context used for a text query. */
+
+struct android_conversion_query_context
+{
+ /* The conversion request. */
+ struct textconv_callback_struct query;
+
+ /* The window the request is being made on. */
+ android_window window;
+
+ /* Whether or not the request was successful. */
+ bool success;
+};
+
+/* Obtain the text from the frame whose window is that specified in
+ DATA using the text conversion query specified there.
+
+ Set ((struct android_conversion_query_context *) DATA)->success on
+ success. */
+
+static void
+android_perform_conversion_query (void *data)
+{
+ struct android_conversion_query_context *context;
+ struct frame *f;
+
+ context = data;
+
+ /* Find the frame associated with the window. */
+ f = android_window_to_frame (NULL, context->window);
+
+ if (!f)
+ return;
+
+ textconv_query (f, &context->query, 0);
+
+ /* context->query.text will have been set even if textconv_query
+ returns 1. */
+
+ context->success = true;
+}
+
+/* Convert a string BUFFERS containing N characters in Emacs's
+ internal multibyte encoding to a Java string utilizing the
+ specified JNI environment.
+
+ If N is equal to BYTES, then BUFFER is a single byte buffer.
+ Otherwise, BUFFER is a multibyte buffer.
+
+ Make sure N and BYTES are absolutely correct, or you are asking for
+ trouble.
+
+ Value is the string upon success, NULL otherwise. Any exceptions
+ generated are not cleared. */
+
+static jstring
+android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n,
+ ptrdiff_t bytes)
+{
+ jchar *utf16;
+ size_t size, index;
+ jstring string;
+ int encoded;
+
+ if (n == bytes)
+ {
+ /* This buffer holds no multibyte characters. */
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return NULL;
+
+ utf16 = malloc (size);
+ index = 0;
+
+ if (!utf16)
+ return NULL;
+
+ while (n--)
+ {
+ utf16[index] = buffer[index];
+ index++;
+ }
+
+ string = (*env)->NewString (env, utf16, bytes);
+ free (utf16);
+
+ return string;
+ }
+
+ /* Allocate enough to hold N characters. */
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return NULL;
+
+ utf16 = malloc (size);
+ index = 0;
+
+ if (!utf16)
+ return NULL;
+
+ while (n--)
+ {
+ eassert (CHAR_HEAD_P (*buffer));
+ encoded = STRING_CHAR ((unsigned char *) buffer);
+
+ /* Now figure out how to save ENCODED into the string.
+ Emacs operates on multibyte characters, not UTF-16
+ characters with surrogate pairs as Android does.
+
+ However, character positions in Java are represented in 2
+ byte units, meaning that the text position reported to
+ Android can become out of sync if characters are found in a
+ buffer that require surrogate pairs.
+
+ The hack used by Emacs is to simply replace each multibyte
+ character that doesn't fit in a jchar with the NULL
+ character. */
+
+ if (encoded >= 65536)
+ encoded = 0;
+
+ utf16[index++] = encoded;
+ buffer += BYTES_BY_CHAR_HEAD (*buffer);
+ }
+
+ /* Create the string. */
+ string = (*env)->NewString (env, utf16, index);
+ free (utf16);
+ return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
+ jint length, jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_conversion_query_context context;
+ jstring string;
+
+ /* First, set up the conversion query. */
+ context.query.position = EMACS_INT_MAX;
+ context.query.direction = TEXTCONV_FORWARD_CHAR;
+ context.query.factor = min (length, 65535);
+ context.query.operation = TEXTCONV_RETRIEVAL;
+
+ /* Next, set the rest of the context. */
+ context.window = window;
+ context.success = false;
+
+ /* Now try to perform the query. */
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_perform_conversion_query,
+ &context))
+ return NULL;
+
+ if (!context.success)
+ return NULL;
+
+ /* context->query.text now contains the text in Emacs's internal
+ UTF-8 based encoding.
+
+ Convert it to Java's UTF-16 encoding, which is the same as
+ UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+ This assumes that `free' can free data allocated with xmalloc. */
+
+ string = android_text_to_string (env, context.query.text.text,
+ context.query.text.length,
+ context.query.text.bytes);
+ free (context.query.text.text);
+
+ return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window,
+ jint length, jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_conversion_query_context context;
+ jstring string;
+
+ /* First, set up the conversion query. */
+ context.query.position = TYPE_MINIMUM (EMACS_INT);
+ context.query.direction = TEXTCONV_BACKWARD_CHAR;
+ context.query.factor = min (length, 65535);
+ context.query.operation = TEXTCONV_RETRIEVAL;
+
+ /* Next, set the rest of the context. */
+ context.window = window;
+ context.success = false;
+
+ /* Now try to perform the query. */
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_perform_conversion_query,
+ &context))
+ return NULL;
+
+ if (!context.success)
+ return NULL;
+
+ /* context->query.text now contains the text in Emacs's internal
+ UTF-8 based encoding.
+
+ Convert it to Java's UTF-16 encoding, which is the same as
+ UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+ This assumes that `free' can free data allocated with xmalloc. */
+
+ string = android_text_to_string (env, context.query.text.text,
+ context.query.text.length,
+ context.query.text.bytes);
+ free (context.query.text.text);
+
+ return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingText) (JNIEnv *env, jobject object, jshort window,
+ jstring composing_text,
+ jint new_cursor_position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, composing_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_COMPOSING_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = new_cursor_position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window,
+ jint start, jint end)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION;
+ event.ime.start = start + 1;
+ event.ime.end = end + 1;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window,
+ jint start, jint end)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ /* While IMEs want access to the entire selection, Emacs only
+ supports setting the point. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_POINT;
+ event.ime.start = start + 1;
+ event.ime.end = end + 1;
+ event.ime.length = 0;
+ event.ime.position = start;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+/* Structure describing the context for `getSelection'. */
+
+struct android_get_selection_context
+{
+ /* The window in question. */
+ android_window window;
+
+ /* The position of the window's point when it was last
+ redisplayed, and its last mark if active. */
+ ptrdiff_t point, mark;
+};
+
+/* Function run on the main thread by `getSelection'.
+ Place the character position of point in PT. */
+
+static void
+android_get_selection (void *data)
+{
+ struct android_get_selection_context *context;
+ struct frame *f;
+ struct window *w;
+ struct buffer *b;
+
+ context = data;
+
+ /* Look up the associated frame and its selected window. */
+ f = android_window_to_frame (NULL, context->window);
+
+ if (!f)
+ context->point = -1;
+ else
+ {
+ w = XWINDOW (f->selected_window);
+
+ /* Return W's point at the time of the last redisplay. This is
+ rather important to keep the input method consistent with the
+ contents of the display. */
+ context->point = w->ephemeral_last_point;
+
+ /* Default context->mark to w->last_point too. */
+ context->mark = context->point;
+
+ /* If the mark is active, then set it properly. */
+ b = XBUFFER (w->contents);
+ if (!NILP (BVAR (b, mark_active)) && w->last_mark != -1)
+ context->mark = w->last_mark;
+ }
+}
+
+JNIEXPORT jintArray JNICALL
+NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_selection_context context;
+ jintArray array;
+ jint contents[2];
+
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_selection,
+ &context))
+ return NULL;
+
+ if (context.point == -1)
+ return NULL;
+
+ /* Wraparound actually makes more sense than truncation; at least
+ editing will sort of work. Convert the positions to start from
+ index 0, as that is what Android expects. */
+ contents[0] = (unsigned int) min (context.point,
+ context.mark) - 1;
+ contents[1] = (unsigned int) max (context.point,
+ context.mark) - 1;
+
+ /* Now create the array. */
+ array = (*env)->NewIntArray (env, 2);
+
+ if (!array)
+ return NULL;
+
+ /* Set its contents. */
+ (*env)->SetIntArrayRegion (env, array, 0, 2, contents);
+ return array;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object,
+ jshort window, int action)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ /* Undocumented behavior: performEditorAction is apparently expected
+ to finish composing any text. */
+
+ NATIVE_NAME (finishComposingText) (env, object, window);
+
+ event.xkey.type = ANDROID_KEY_PRESS;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = 0;
+ event.xkey.state = 0;
+ event.xkey.keycode = 66;
+ event.xkey.unicode_char = 0;
+
+ android_write_event (&event);
+}
+
+
+
+/* Text extraction. */
+
+struct android_get_extracted_text_context
+{
+ /* The parameters of the request. */
+ int hint_max_chars;
+
+ /* Token for the request. */
+ int token;
+
+ /* Flags associated with the request. */
+ int flags;
+
+ /* The returned text, or NULL. */
+ char *text;
+
+ /* The size of that text in characters and bytes. */
+ ptrdiff_t length, bytes;
+
+ /* Offsets into that text. */
+ ptrdiff_t start, offset;
+
+ /* The window. */
+ android_window window;
+};
+
+/* Return the extracted text in the extracted text context specified
+ by DATA. */
+
+static void
+android_get_extracted_text (void *data)
+{
+ struct android_get_extracted_text_context *request;
+ struct frame *f;
+
+ request = data;
+
+ /* Find the frame associated with the window. */
+ f = android_window_to_frame (NULL, request->window);
+
+ if (!f)
+ return;
+
+ /* Now get the extracted text. */
+ request->text
+ = get_extracted_text (f, min (request->hint_max_chars, 600),
+ &request->start, &request->offset,
+ &request->length, &request->bytes);
+
+ /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then
+ the input method has asked to monitor changes to the extracted
+ text until the next IM context reset. */
+
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = request->flags;
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_token = request->token;
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_hint = request->hint_max_chars;
+}
+
+/* Structure describing the `ExtractedTextRequest' class.
+ Valid only on the UI thread. */
+
+struct android_extracted_text_request_class
+{
+ bool initialized;
+ jfieldID hint_max_chars;
+ jfieldID token;
+};
+
+/* Structure describing the `ExtractedText' class.
+ Valid only on the UI thread. */
+
+struct android_extracted_text_class
+{
+ jclass class;
+ jmethodID constructor;
+ jfieldID partial_start_offset;
+ jfieldID partial_end_offset;
+ jfieldID selection_start;
+ jfieldID selection_end;
+ jfieldID start_offset;
+ jfieldID text;
+};
+
+/* Fields and methods associated with the `ExtractedTextRequest'
+ class. */
+struct android_extracted_text_request_class request_class;
+
+/* Fields and methods associated with the `ExtractedText' class. */
+struct android_extracted_text_class text_class;
+
+/* Return an ExtractedText object corresponding to the extracted text
+ TEXT. START is a character position describing the offset of the
+ first character in TEXT. OFFSET is the offset of point relative to
+ START.
+
+ Assume that request_class and text_class have already been
+ initialized.
+
+ Value is NULL if an error occurs; the exception is not cleared,
+ else a local reference to the ExtractedText object. */
+
+static jobject
+android_build_extracted_text (jstring text, ptrdiff_t start,
+ ptrdiff_t offset)
+{
+ JNIEnv *env;
+ jobject object;
+
+ env = android_java_env;
+
+ /* Return NULL if the class has not yet been obtained. */
+ if (!text_class.class)
+ return NULL;
+
+ /* Create an ExtractedText object containing this information. */
+ object = (*env)->NewObject (env, text_class.class,
+ text_class.constructor);
+ if (!object)
+ return NULL;
+
+ (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+ (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+ (*env)->SetIntField (env, object, text_class.selection_start,
+ min (offset, TYPE_MAXIMUM (jint)));
+ (*env)->SetIntField (env, object, text_class.selection_end,
+ min (offset, TYPE_MAXIMUM (jint)));
+
+ /* Subtract 1 from start: point indices in Emacs start from 1, but
+ Android expects 0. */
+ (*env)->SetIntField (env, object, text_class.start_offset,
+ min (start - 1, TYPE_MAXIMUM (jint)));
+ (*env)->SetObjectField (env, object, text_class.text, text);
+ return object;
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
+ jshort window, jobject request,
+ jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_extracted_text_context context;
+ jstring string;
+ jclass class;
+ jobject object;
+
+ /* Initialize both classes if necessary. */
+
+ if (!request_class.initialized)
+ {
+ class
+ = (*env)->FindClass (env, ("android/view/inputmethod"
+ "/ExtractedTextRequest"));
+ assert (class);
+
+ request_class.hint_max_chars
+ = (*env)->GetFieldID (env, class, "hintMaxChars", "I");
+ assert (request_class.hint_max_chars);
+
+ request_class.token
+ = (*env)->GetFieldID (env, class, "token", "I");
+ assert (request_class.token);
+
+ request_class.initialized = true;
+ }
+
+ if (!text_class.class)
+ {
+ text_class.class
+ = (*env)->FindClass (env, ("android/view/inputmethod"
+ "/ExtractedText"));
+ assert (text_class.class);
+
+ class
+ = text_class.class
+ = (*env)->NewGlobalRef (env, text_class.class);
+ assert (text_class.class);
+
+ text_class.partial_start_offset
+ = (*env)->GetFieldID (env, class, "partialStartOffset", "I");
+ text_class.partial_end_offset
+ = (*env)->GetFieldID (env, class, "partialEndOffset", "I");
+ text_class.selection_start
+ = (*env)->GetFieldID (env, class, "selectionStart", "I");
+ text_class.selection_end
+ = (*env)->GetFieldID (env, class, "selectionEnd", "I");
+ text_class.start_offset
+ = (*env)->GetFieldID (env, class, "startOffset", "I");
+ text_class.text
+ = (*env)->GetFieldID (env, class, "text", "Ljava/lang/CharSequence;");
+ text_class.constructor
+ = (*env)->GetMethodID (env, class, "<init>", "()V");
+ }
+
+ context.hint_max_chars
+ = (*env)->GetIntField (env, request, request_class.hint_max_chars);
+ context.token
+ = (*env)->GetIntField (env, request, request_class.token);
+ context.flags = flags;
+ context.text = NULL;
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_extracted_text,
+ &context))
+ return NULL;
+
+ if (!context.text)
+ return NULL;
+
+ /* Encode the returned text. */
+ string = android_text_to_string (env, context.text, context.length,
+ context.bytes);
+ free (context.text);
+
+ if (!string)
+ return NULL;
+
+ /* Create an ExtractedText object containing this information. */
+ object = (*env)->NewObject (env, text_class.class,
+ text_class.constructor);
+ if (!object)
+ return NULL;
+
+ (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+ (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+ (*env)->SetIntField (env, object, text_class.selection_start,
+ min (context.offset, TYPE_MAXIMUM (jint)));
+ (*env)->SetIntField (env, object, text_class.selection_end,
+ min (context.offset, TYPE_MAXIMUM (jint)));
+
+ /* Subtract 1 from start: point indices in Emacs start from 1, but
+ Android expects 0. */
+ (*env)->SetIntField (env, object, text_class.start_offset,
+ min (context.start - 1, TYPE_MAXIMUM (jint)));
+ (*env)->SetObjectField (env, object, text_class.text, string);
+ return object;
+}
+
+
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_extracted_text_context context;
+ jstring string;
+
+ context.hint_max_chars = -1;
+ context.token = 0;
+ context.text = NULL;
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_extracted_text,
+ &context))
+ return NULL;
+
+ if (!context.text)
+ return NULL;
+
+ /* Encode the returned text. */
+ string = android_text_to_string (env, context.text, context.length,
+ context.bytes);
+ free (context.text);
+
+ return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_REQUEST_SELECTION_UPDATE;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object,
+ jshort window, jint mode)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = mode;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Tell the input method where the composing region and selection of
+ F's selected window is located. W should be F's selected window;
+ if it is NULL, then F->selected_window is used in its place. */
+
+static void
+android_update_selection (struct frame *f, struct window *w)
+{
+ ptrdiff_t start, end, point, mark, offset, length, bytes;
+ struct buffer *b;
+ int hint, token;
+ char *text;
+ jobject extracted;
+ jstring string;
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ eassert (MARKERP (f->conversion.compose_region_end));
+
+ /* Indexing in android starts from 0 instead of 1. */
+ start = marker_position (f->conversion.compose_region_start) - 1;
+ end = marker_position (f->conversion.compose_region_end) - 1;
+ }
+ else
+ start = -1, end = -1;
+
+ /* Now constrain START and END to the maximium size of a Java
+ integer. */
+ start = min (start, TYPE_MAXIMUM (jint));
+ end = min (end, TYPE_MAXIMUM (jint));
+
+ if (!w)
+ w = XWINDOW (f->selected_window);
+
+ /* Figure out where the point and mark are. If the mark is not
+ active, then point is set to equal mark. */
+ b = XBUFFER (w->contents);
+ point = min (w->ephemeral_last_point,
+ TYPE_MAXIMUM (jint));
+ mark = ((!NILP (BVAR (b, mark_active))
+ && w->last_mark != -1)
+ ? min (w->last_mark, TYPE_MAXIMUM (jint))
+ : point);
+
+ /* Send the update. Android doesn't have a concept of ``point'' and
+ ``mark''; instead, it only has a selection, where the start of
+ the selection is less than or equal to the end. Also, convert
+ the indices from 1-based Emacs indices to 0-based Android
+ ones. */
+ android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark) - 1,
+ max (point, mark) - 1, start, end);
+
+ /* Update the extracted text as well, if the input method has asked
+ for updates. 1 is
+ InputConnection.GET_EXTRACTED_TEXT_MONITOR. */
+
+ if (FRAME_ANDROID_OUTPUT (f)->extracted_text_flags & 1)
+ {
+ hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint;
+ token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token;
+ text = get_extracted_text (f, min (hint, 600), &start,
+ &offset, &length, &bytes);
+
+ if (text)
+ {
+ /* Make a string out of the extracted text. */
+ string = android_text_to_string (android_java_env,
+ text, length, bytes);
+ xfree (text);
+ android_exception_check ();
+
+ /* Make extracted text out of that string. */
+ extracted = android_build_extracted_text (string, start,
+ offset);
+ android_exception_check_1 (string);
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ if (extracted)
+ {
+ /* extracted is now an associated ExtractedText object.
+ Perform the update. */
+ android_update_extracted_text (FRAME_ANDROID_WINDOW (f),
+ extracted, token);
+ ANDROID_DELETE_LOCAL_REF (extracted);
+ }
+ }
+ }
+}
+
+/* Return whether or not EVENT is an input method event destined for
+ the frame (struct frame *) ARG. */
+
+static bool
+android_event_is_for_frame (union android_event *event, void *arg)
+{
+ struct frame *f;
+
+ f = arg;
+ return (event->type == ANDROID_INPUT_METHOD
+ && event->ime.window == FRAME_ANDROID_WINDOW (f));
+}
+
+/* Notice that the input method connection to F should be reset as a
+ result of a change to its contents. */
+
+static void
+android_reset_conversion (struct frame *f)
+{
+ enum android_ic_mode mode;
+ struct window *w;
+ struct buffer *buffer;
+ Lisp_Object style;
+ union android_event event;
+
+ /* Reset the input method.
+
+ Pick an appropriate ``input mode'' based on whether or not the
+ minibuffer window is selected; this controls whether or not
+ ``RET'' inserts a newline or sends an actual key event. */
+
+ w = XWINDOW (f->selected_window);
+ buffer = XBUFFER (WINDOW_BUFFER (w));
+
+ style = (EQ (find_symbol_value (Qoverriding_text_conversion_style),
+ Qlambda)
+ ? BVAR (buffer, text_conversion_style)
+ : find_symbol_value (Qoverriding_text_conversion_style));
+
+ if (NILP (style) || conversion_disabled_p ())
+ mode = ANDROID_IC_MODE_NULL;
+ else if (EQ (style, Qaction) || EQ (f->selected_window,
+ f->minibuffer_window))
+ mode = ANDROID_IC_MODE_ACTION;
+ else
+ mode = ANDROID_IC_MODE_TEXT;
+
+ /* Remove any existing input method events that apply to FRAME from
+ the event queue.
+
+ There's a small window between this and the call to
+ android_reset_ic between which more events can be generated. */
+
+ while (android_check_if_event (&event, android_event_is_for_frame, f))
+ {
+ switch (event.ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ xfree (event.ime.text);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ android_reset_ic (FRAME_ANDROID_WINDOW (f), mode);
+
+ /* Clear extracted text flags. Since the IM has been reinitialised,
+ it should no longer be displaying extracted text. */
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = 0;
+
+ /* Move its selection to the specified position. */
+ android_update_selection (f, NULL);
+}
+
+/* Notice that point has moved in the F's selected window's selected
+ buffer. W is the window, and BUFFER is that buffer. */
+
+static void
+android_set_point (struct frame *f, struct window *w,
+ struct buffer *buffer)
+{
+ android_update_selection (f, w);
+}
+
+/* Notice that the composition region on F's old selected window has
+ changed. */
+
+static void
+android_compose_region_changed (struct frame *f)
+{
+ android_update_selection (f, XWINDOW (f->old_selected_window));
+}
+
+/* Notice that the text conversion has completed. */
+
+static void
+android_notify_conversion (unsigned long counter)
+{
+ int sval;
+
+ if (last_edit_counter < counter)
+ __atomic_store_n (&last_edit_counter, counter,
+ __ATOMIC_SEQ_CST);
+
+ sem_getvalue (&edit_sem, &sval);
+
+ if (sval <= 0)
+ sem_post (&edit_sem);
+}
+
+/* Android text conversion interface. */
+
+static struct textconv_interface text_conversion_interface =
+ {
+ android_reset_conversion,
+ android_set_point,
+ android_compose_region_changed,
+ android_notify_conversion,
+ };
+
+
+
+extern frame_parm_handler android_frame_parm_handlers[];
+
+#endif /* !ANDROID_STUBIFY */
+
+static struct redisplay_interface android_redisplay_interface =
+ {
+#ifndef ANDROID_STUBIFY
+ android_frame_parm_handlers,
+ gui_produce_glyphs,
+ gui_write_glyphs,
+ gui_insert_glyphs,
+ gui_clear_end_of_line,
+ android_scroll_run,
+ android_after_update_window_line,
+ NULL, /* update_window_begin */
+ NULL, /* update_window_end */
+ android_flip_and_flush,
+ gui_clear_window_mouse_face,
+ gui_get_glyph_overhangs,
+ gui_fix_overlapping_area,
+ android_draw_fringe_bitmap,
+ NULL, /* define_fringe_bitmap */
+ NULL, /* destroy_fringe_bitmap */
+ android_compute_glyph_string_overhangs,
+ android_draw_glyph_string,
+ android_define_frame_cursor,
+ android_clear_frame_area,
+ android_clear_under_internal_border,
+ android_draw_window_cursor,
+ android_draw_vertical_window_border,
+ android_draw_window_divider,
+ NULL,
+ android_show_hourglass,
+ android_hide_hourglass,
+ android_default_font_parameter,
+#endif
+ };
+
+
+
+void
+frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
+{
+ /* This cannot be implemented on Android, and as such is left
+ blank. */
+}
+
+char *
+get_keysym_name (int keysym)
+{
+ static char buffer[64];
+
+#ifndef ANDROID_STUBIFY
+ android_get_keysym_name (keysym, buffer, 64);
+#else
+ emacs_abort ();
+#endif
+ return buffer;
+}
+
+
+
+/* Create a struct terminal, initialize it with the Android specific
+ functions and make DISPLAY->TERMINAL point to it. */
+
+static struct terminal *
+android_create_terminal (struct android_display_info *dpyinfo)
+{
+ struct terminal *terminal;
+
+ terminal = create_terminal (output_android,
+ &android_redisplay_interface);
+ terminal->display_info.android = dpyinfo;
+ dpyinfo->terminal = terminal;
+
+ /* kboard is initialized in android_term_init. */
+
+#ifndef ANDROID_STUBIFY
+
+ terminal->clear_frame_hook = android_clear_frame;
+ terminal->ring_bell_hook = android_ring_bell;
+ terminal->toggle_invisible_pointer_hook
+ = android_toggle_invisible_pointer;
+ terminal->update_begin_hook = android_update_begin;
+ terminal->update_end_hook = android_update_end;
+ terminal->read_socket_hook = android_read_socket;
+ terminal->frame_up_to_date_hook = android_frame_up_to_date;
+ terminal->buffer_flipping_unblocked_hook
+ = android_buffer_flipping_unblocked_hook;
+ terminal->defined_color_hook = android_defined_color;
+ terminal->query_frame_background_color
+ = android_query_frame_background_color;
+ terminal->query_colors = android_query_colors;
+ terminal->mouse_position_hook = android_mouse_position;
+ terminal->get_focus_frame = android_get_focus_frame;
+ terminal->focus_frame_hook = android_focus_frame;
+ terminal->frame_rehighlight_hook = android_frame_rehighlight_hook;
+ terminal->frame_raise_lower_hook = android_frame_raise_lower;
+ terminal->frame_visible_invisible_hook
+ = android_make_frame_visible_invisible;
+ terminal->fullscreen_hook = android_fullscreen_hook;
+ terminal->iconify_frame_hook = android_iconify_frame;
+ terminal->set_window_size_hook = android_set_window_size;
+ terminal->set_frame_offset_hook = android_set_offset;
+ terminal->set_frame_alpha_hook = android_set_alpha;
+ terminal->set_new_font_hook = android_new_font;
+ terminal->set_bitmap_icon_hook = android_bitmap_icon;
+ terminal->implicit_set_name_hook = android_implicitly_set_name;
+ terminal->menu_show_hook = android_menu_show;
+ terminal->popup_dialog_hook = android_popup_dialog;
+ terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
+ terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
+ terminal->set_scroll_bar_default_width_hook
+ = android_set_scroll_bar_default_width;
+ terminal->set_scroll_bar_default_height_hook
+ = android_set_scroll_bar_default_height;
+ terminal->free_pixmap = android_free_pixmap_hook;
+ terminal->delete_frame_hook = android_delete_frame;
+ terminal->delete_terminal_hook = android_delete_terminal;
+
+#else
+ emacs_abort ();
+#endif
+
+ return terminal;
+}
+
+/* Initialize the Android terminal interface. The display connection
+ has already been set up by the system at this point. */
+
+void
+android_term_init (void)
+{
+ struct terminal *terminal;
+ struct android_display_info *dpyinfo;
+ Lisp_Object color_file, color_map;
+
+ dpyinfo = xzalloc (sizeof *dpyinfo);
+ terminal = android_create_terminal (dpyinfo);
+ terminal->kboard = allocate_kboard (Qandroid);
+ terminal->kboard->reference_count++;
+
+ dpyinfo->n_planes = 24;
+
+ /* This function should only be called once at startup. */
+ eassert (!x_display_list);
+ x_display_list = dpyinfo;
+
+ dpyinfo->name_list_element
+ = Fcons (build_pure_c_string ("android"), Qnil);
+
+ color_file = Fexpand_file_name (build_string ("rgb.txt"),
+ Vdata_directory);
+ color_map = Fx_load_color_file (color_file);
+
+ if (NILP (color_map))
+ fatal ("Could not read %s.\n", SDATA (color_file));
+
+ dpyinfo->color_map = color_map;
+
+#ifndef ANDROID_STUBIFY
+
+ dpyinfo->resx = android_pixel_density_x;
+ dpyinfo->resy = android_pixel_density_y;
+
+#endif
+
+ /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */
+ dpyinfo->smallest_font_height = 1;
+ dpyinfo->smallest_char_width = 1;
+
+ terminal->name = xstrdup ("android");
+
+ /* The display "connection" is now set up, and it must never go
+ away. */
+ terminal->reference_count = 30000;
+
+ /* Set the baud rate to the same value it gets set to on X. */
+ baud_rate = 19200;
+
+#ifndef ANDROID_STUBIFY
+ sem_init (&edit_sem, false, 0);
+ register_textconv_interface (&text_conversion_interface);
+#endif
+}
+
+
+
+/* Set Vandroid_build_fingerprint to a reasonable value, and also
+ Vandroid_build_manufacturer. */
+
+static void
+android_set_build_fingerprint (void)
+{
+#ifdef ANDROID_STUBIFY
+ Vandroid_build_fingerprint = Qnil;
+#else
+ jclass class;
+ jfieldID field;
+ jobject string;
+ const char *data;
+
+ /* Set class to NULL so freeing an uninitialized local ref can be
+ avoided. */
+ class = NULL;
+
+ /* Likewise for string. */
+ string = NULL;
+
+ if (!android_init_gui)
+ goto fail;
+ else
+ {
+ /* Obtain Build.FINGERPRINT. Clear exceptions after each query;
+ JNI can't find Build.FINGERPRIN on some systems. */
+
+ class = (*android_java_env)->FindClass (android_java_env,
+ "android/os/Build");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!class)
+ goto fail;
+
+ field = (*android_java_env)->GetStaticFieldID (android_java_env,
+ class,
+ "FINGERPRINT",
+ "Ljava/lang/String;");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!field)
+ goto fail;
+
+ string
+ = (*android_java_env)->GetStaticObjectField (android_java_env,
+ class, field);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!string)
+ goto fail;
+
+ data = (*android_java_env)->GetStringUTFChars (android_java_env,
+ string, NULL);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!data)
+ goto fail;
+
+ Vandroid_build_fingerprint = build_string_from_utf8 (data);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ string, data);
+
+ /* Now obtain Build.MANUFACTURER. */
+
+ ANDROID_DELETE_LOCAL_REF (string);
+ string = NULL;
+
+ field = (*android_java_env)->GetStaticFieldID (android_java_env,
+ class,
+ "MANUFACTURER",
+ "Ljava/lang/String;");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!field)
+ goto fail;
+
+ string
+ = (*android_java_env)->GetStaticObjectField (android_java_env,
+ class, field);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!string)
+ goto fail;
+
+ data = (*android_java_env)->GetStringUTFChars (android_java_env,
+ string, NULL);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!data)
+ goto fail;
+
+ Vandroid_build_manufacturer = build_string_from_utf8 (data);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ string, data);
+ }
+
+ if (string)
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ ANDROID_DELETE_LOCAL_REF (class);
+
+ return;
+
+ fail:
+ if (class)
+ ANDROID_DELETE_LOCAL_REF (class);
+
+ Vandroid_build_fingerprint = Qnil;
+ Vandroid_build_manufacturer = Qnil;
+#endif
+}
+
+void
+syms_of_androidterm (void)
+{
+ Fprovide (Qandroid, Qnil);
+
+ DEFVAR_LISP ("android-wait-for-event-timeout",
+ Vandroid_wait_for_event_timeout,
+ doc: /* How long to wait for Android events.
+
+Emacs will wait up to this many seconds to receive events after
+making changes which affect the state of the graphical interface.
+Under some situations this can take an indefinite amount of time,
+so it is important to limit the wait.
+
+If set to a non-float value, there will be no wait at all. */);
+ Vandroid_wait_for_event_timeout = make_float (0.1);
+
+ DEFVAR_BOOL ("x-use-underline-position-properties",
+ x_use_underline_position_properties,
+ doc: /* SKIP: real doc in xterm.c. */);
+ x_use_underline_position_properties = true;
+ DEFSYM (Qx_use_underline_position_properties,
+ "x-use-underline-position-properties");
+
+ DEFVAR_BOOL ("x-underline-at-descent-line",
+ x_underline_at_descent_line,
+ doc: /* SKIP: real doc in xterm.c. */);
+ x_underline_at_descent_line = false;
+
+ DEFVAR_LISP ("android-build-fingerprint", Vandroid_build_fingerprint,
+ doc: /* String identifying the device's OS version.
+This is a string that uniquely identifies the version of Android
+Emacs is running on. */);
+ Vandroid_build_fingerprint = Qnil;
+
+ DEFVAR_LISP ("android-build-manufacturer", Vandroid_build_manufacturer,
+ doc: /* Name of the developer of the running version of Android. */);
+ Vandroid_build_manufacturer = Qnil;
+
+ /* Only defined so loadup.el loads scroll-bar.el. */
+ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+ doc: /* SKIP: real doc in xterm.c. */);
+ Vx_toolkit_scroll_bars = Qnil;
+
+ /* Avoid dumping Vandroid_build_fingerprint. */
+ pdumper_do_now_and_after_load (android_set_build_fingerprint);
+
+ DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
+}
+
+void
+mark_androidterm (void)
+{
+ if (x_display_list)
+ mark_object (x_display_list->color_map);
+}
diff --git a/src/androidterm.h b/src/androidterm.h
new file mode 100644
index 00000000000..e3738fb2192
--- /dev/null
+++ b/src/androidterm.h
@@ -0,0 +1,476 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _ANDROID_TERM_H_
+#define _ANDROID_TERM_H_
+
+#include "androidgui.h"
+#include "frame.h"
+#include "character.h"
+#include "dispextern.h"
+#include "font.h"
+
+struct android_bitmap_record
+{
+ /* The image backing the bitmap and its mask. */
+ android_pixmap pixmap, mask;
+
+ /* The file from which it comes. */
+ char *file;
+
+ /* The number of references to it. */
+ int refcount;
+
+ /* The height and width and the depth. */
+ int height, width, depth;
+
+ /* Whether or not there is a mask. */
+ bool have_mask;
+};
+
+struct android_display_info
+{
+ /* Chain of all struct android_display_info structures. */
+ struct android_display_info *next;
+
+ /* The terminal. */
+ struct terminal *terminal;
+
+ /* The root window. This field is unused. */
+ Emacs_Window root_window;
+
+ /* List possibly used only for the font cache but probably used for
+ something else too. */
+ Lisp_Object name_list_element;
+
+ /* List of predefined X colors. */
+ Lisp_Object color_map;
+
+ /* DPI of the display. */
+ double resx, resy;
+
+ /* Scratch GC for drawing a cursor in a non-default face. */
+ struct android_gc *scratch_cursor_gc;
+
+ /* Mouse highlight information. */
+ Mouse_HLInfo mouse_highlight;
+
+ /* Number of planes on this screen. Always 24. */
+ int n_planes;
+
+ /* Mask of things causing the mouse to be grabbed. */
+ int grabbed;
+
+ /* Minimum width over all characters in all fonts in font_table. */
+ int smallest_char_width;
+
+ /* Minimum font height over all fonts in font_table. */
+ int smallest_font_height;
+
+ /* The number of fonts opened for this display. */
+ int n_fonts;
+
+ /* Pointer to bitmap records. */
+ struct android_bitmap_record *bitmaps;
+
+ /* Allocated size of bitmaps field. */
+ ptrdiff_t bitmaps_size;
+
+ /* Last used bitmap index. */
+ ptrdiff_t bitmaps_last;
+
+ /* The frame currently with the input focus. */
+ struct frame *focus_frame;
+
+ /* The last frame mentioned in a focus event. */
+ struct frame *x_focus_event_frame;
+
+ /* The frame which currently has the visual highlight, and should
+ get keyboard input. It points to the focus frame's selected
+ window's frame, but can differ. */
+ struct frame *highlight_frame;
+
+ /* The frame waiting to be auto-raised in android_read_socket. */
+ struct frame *pending_autoraise_frame;
+
+ /* The frame where the mouse was the last time a button event
+ happened. */
+ struct frame *last_mouse_frame;
+
+ /* The frame where the mouse was the last time the mouse glyph
+ changed. */
+ struct frame *last_mouse_glyph_frame;
+
+ /* The frame where the mouse was the last time mouse motion
+ happened. */
+ struct frame *last_mouse_motion_frame;
+
+ /* Position where the mouse was last time we reported a motion.
+ This is a position on last_mouse_motion_frame. It is used in to
+ report the mouse position as well: see
+ android_mouse_position. */
+ int last_mouse_motion_x, last_mouse_motion_y;
+
+ /* Where the mouse was the last time the mouse moved. */
+ Emacs_Rectangle last_mouse_glyph;
+
+ /* The time of the last mouse movement. */
+ Time last_mouse_movement_time;
+
+ /* ID of the last menu event received. -1 means Emacs is waiting
+ for a context menu event. */
+ int menu_event_id;
+
+ /* The invisible cursor used for pointer blanking. */
+ android_cursor invisible_cursor;
+};
+
+/* Structure representing a single tool (finger or stylus) pressed
+ onto a frame. */
+
+struct android_touch_point
+{
+ /* The next tool on this list. */
+ struct android_touch_point *next;
+
+ /* The tool ID and the last known X and Y positions. */
+ int tool_id, x, y;
+
+ /* Whether or not the tool is pressed on the tool bar. */
+ bool tool_bar_p;
+};
+
+struct android_output
+{
+ /* Graphics contexts for the default font. */
+ struct android_gc *normal_gc, *reverse_gc, *cursor_gc;
+
+ /* The window used for this frame. */
+ Emacs_Window window;
+
+ /* Unused field. */
+ Emacs_Window parent_desc;
+
+ /* Default ASCII font of this frame. */
+ struct font *font;
+
+ /* The baseline offset of the default ASCII font. */
+ int baseline_offset;
+
+ /* If a fontset is specified for this frame instead of font, this
+ value contains an ID of the fontset, else -1. */
+ int fontset;
+
+ /* Various colors. */
+ unsigned long cursor_pixel;
+ unsigned long mouse_pixel;
+ unsigned long cursor_foreground_pixel;
+
+ /* Foreground color for scroll bars. A value of -1 means use the
+ default (black for non-toolkit scroll bars). */
+ unsigned long scroll_bar_foreground_pixel;
+
+ /* Background color for scroll bars. A value of -1 means use the
+ default (background color of the frame for non-toolkit scroll
+ bars). */
+ unsigned long scroll_bar_background_pixel;
+
+ /* Cursors associated with this frame. */
+ Emacs_Cursor text_cursor;
+ Emacs_Cursor nontext_cursor;
+ Emacs_Cursor modeline_cursor;
+ Emacs_Cursor hand_cursor;
+ Emacs_Cursor hourglass_cursor;
+ Emacs_Cursor horizontal_drag_cursor;
+ Emacs_Cursor vertical_drag_cursor;
+ Emacs_Cursor current_cursor;
+ Emacs_Cursor left_edge_cursor;
+ Emacs_Cursor top_left_corner_cursor;
+ Emacs_Cursor top_edge_cursor;
+ Emacs_Cursor top_right_corner_cursor;
+ Emacs_Cursor right_edge_cursor;
+ Emacs_Cursor bottom_right_corner_cursor;
+ Emacs_Cursor bottom_edge_cursor;
+ Emacs_Cursor bottom_left_corner_cursor;
+
+ /* Whether or not the hourglass cursor is being displayed. */
+ bool hourglass;
+
+ /* This is the Emacs structure for the display this frame is on. */
+ struct android_display_info *display_info;
+
+ /* True if this frame was ever previously visible. */
+ bool_bf has_been_visible : 1;
+
+ /* True if this frame's alpha value is the same for both the active
+ and inactive states. */
+ bool_bf alpha_identical_p : 1;
+
+ /* Flag that indicates whether or not the frame contents are
+ complete and can be safely flushed while handling async
+ input. */
+ bool_bf complete : 1;
+
+ /* True that indicates whether or not a buffer flip is required
+ because the frame contents have been dirtied. */
+ bool_bf need_buffer_flip : 1;
+
+ /* Whether or not the input method should be notified every time the
+ position of this frame's selected window changes. */
+ bool_bf need_cursor_updates : 1;
+
+ /* Relief GCs, colors etc. */
+ struct relief {
+ struct android_gc *gc;
+ unsigned long pixel;
+ } black_relief, white_relief;
+
+ /* The background for which the above relief GCs were set up.
+ They are changed only when a different background is involved. */
+ unsigned long relief_background;
+
+ /* Focus state. Only present for consistency with X; it is actually
+ a boolean. */
+ int focus_state;
+
+ /* List of all tools (either styluses or fingers) pressed onto the
+ frame. */
+ struct android_touch_point *touch_points;
+
+ /* Flags associated with the last request to obtain ``extracted
+ text''. */
+ int extracted_text_flags;
+
+ /* Token asssociated with that request. */
+ int extracted_text_token;
+
+ /* The number of characters of extracted text wanted by the IM. */
+ int extracted_text_hint;
+};
+
+enum
+ {
+ /* Values for focus_state, used as bit mask. EXPLICIT means we
+ received a FocusIn for the frame and know it has the focus.
+ IMPLICIT means we received an EnterNotify and the frame may
+ have the focus if no window manager is running. FocusOut and
+ LeaveNotify clears EXPLICIT/IMPLICIT. */
+ FOCUS_NONE = 0,
+ FOCUS_IMPLICIT = 1,
+ FOCUS_EXPLICIT = 2
+ };
+
+/* Return the Android output data for frame F. */
+#define FRAME_ANDROID_OUTPUT(f) ((f)->output_data.android)
+#define FRAME_OUTPUT_DATA(f) ((f)->output_data.android)
+
+/* Return the Android window used for displaying data in frame F. */
+#define FRAME_ANDROID_WINDOW(f) ((f)->output_data.android->window)
+#define FRAME_NATIVE_WINDOW(f) ((f)->output_data.android->window)
+
+/* Return the need-buffer-flip flag for frame F. */
+#define FRAME_ANDROID_NEED_BUFFER_FLIP(f) \
+ ((f)->output_data.android->need_buffer_flip)
+
+/* Return the drawable used for rendering to frame F and mark the
+ frame as needing a buffer flip later. There's no easy way to run
+ code after any drawing command, but code can be run whenever
+ someone asks for the handle necessary to draw. */
+#define FRAME_ANDROID_DRAWABLE(f) \
+ (((f))->output_data.android->need_buffer_flip = true, \
+ FRAME_ANDROID_WINDOW ((f)))
+
+/* Return whether or not the frame F has been completely drawn. Used
+ while handling async input. */
+#define FRAME_ANDROID_COMPLETE_P(f) \
+ ((f)->output_data.android->complete)
+
+#define FRAME_FONT(f) ((f)->output_data.android->font)
+#define FRAME_FONTSET(f) ((f)->output_data.android->fontset)
+
+#define FRAME_BASELINE_OFFSET(f) \
+ ((f)->output_data.android->baseline_offset)
+
+/* This gives the android_display_info structure for the display F is
+ on. */
+#define FRAME_DISPLAY_INFO(f) ((f)->output_data.android->display_info)
+
+/* Some things for X compatibility. */
+#define BLACK_PIX_DEFAULT(f) 0
+#define WHITE_PIX_DEFAULT(f) 0xffffffff
+
+/* Android-specific scroll bar stuff. */
+
+/* We represent scroll bars as lisp vectors. This allows us to place
+ references to them in windows without worrying about whether we'll
+ end up with windows referring to dead scroll bars; the garbage
+ collector will free it when its time comes.
+
+ We use struct scroll_bar as a template for accessing fields of the
+ vector. */
+
+struct scroll_bar
+{
+ /* These fields are shared by all vectors. */
+ union vectorlike_header header;
+
+ /* The window we're a scroll bar for. */
+ Lisp_Object window;
+
+ /* The next and previous in the chain of scroll bars in this frame. */
+ Lisp_Object next, prev;
+
+ /* Fields after 'prev' are not traced by the GC. */
+
+ /* The X window representing this scroll bar. */
+ Emacs_Window x_window;
+
+ /* The position and size of the scroll bar in pixels, relative to the
+ frame. */
+ int top, left, width, height;
+
+ /* The starting and ending positions of the handle, relative to the
+ handle area (i.e. zero is the top position, not
+ SCROLL_BAR_TOP_BORDER). If they're equal, that means the handle
+ hasn't been drawn yet.
+
+ These are not actually the locations where the beginning and end
+ are drawn; in order to keep handles from becoming invisible when
+ editing large files, we establish a minimum height by always
+ drawing handle bottoms VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
+ where they would be normally; the bottom and top are in a
+ different coordinate system. */
+ int start, end;
+
+ /* If the scroll bar handle is currently being dragged by the user,
+ this is the number of pixels from the top of the handle to the
+ place where the user grabbed it. If the handle isn't currently
+ being dragged, this is -1. */
+ int dragging;
+
+ /* True if the scroll bar is horizontal. */
+ bool horizontal;
+};
+
+/* Turning a lisp vector value into a pointer to a struct scroll_bar. */
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+
+
+/* This is a chain of structures for all the Android displays
+ currently in use. There is only ever one, but the rest of Emacs is
+ written with systems on which there can be many in mind. */
+extern struct android_display_info *x_display_list;
+
+
+
+/* Start of function definitions. These should be a neat subset of
+ the same ones in xterm.h, and come in the same order. */
+
+/* From androidfns.c. */
+
+extern void android_free_gcs (struct frame *);
+extern void android_default_font_parameter (struct frame *, Lisp_Object);
+extern void android_set_preeditarea (struct window *, int, int);
+
+/* Defined in androidterm.c. */
+
+extern void android_term_init (void);
+extern void android_set_window_size (struct frame *, bool, int, int);
+extern void android_iconify_frame (struct frame *);
+extern void android_make_frame_visible (struct frame *);
+extern void android_make_frame_invisible (struct frame *);
+extern void android_free_frame_resources (struct frame *);
+
+extern int android_parse_color (struct frame *, const char *,
+ Emacs_Color *);
+extern bool android_alloc_nearest_color (struct frame *, Emacs_Color *);
+extern void android_query_colors (struct frame *, Emacs_Color *, int);
+extern void android_clear_under_internal_border (struct frame *);
+
+extern void syms_of_androidterm (void);
+extern void mark_androidterm (void);
+
+/* Defined in androidfns.c. */
+
+extern void android_change_tab_bar_height (struct frame *, int);
+extern void android_change_tool_bar_height (struct frame *, int);
+extern void android_set_scroll_bar_default_width (struct frame *);
+extern void android_set_scroll_bar_default_height (struct frame *);
+extern bool android_defined_color (struct frame *, const char *,
+ Emacs_Color *, bool, bool);
+extern void android_implicitly_set_name (struct frame *, Lisp_Object,
+ Lisp_Object);
+extern void android_explicitly_set_name (struct frame *, Lisp_Object,
+ Lisp_Object);
+
+extern void syms_of_androidfns (void);
+
+/* Defined in androidfont.c. */
+
+extern struct font_driver androidfont_driver;
+
+extern void init_androidfont (void);
+extern void syms_of_androidfont (void);
+
+extern void android_finalize_font_entity (struct font_entity *);
+
+/* Defined in androidmenu.c. */
+
+#ifndef ANDROID_STUBIFY
+
+extern unsigned int current_menu_serial;
+
+#endif
+
+extern Lisp_Object android_menu_show (struct frame *, int, int, int,
+ Lisp_Object, const char **);
+extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object,
+ Lisp_Object);
+
+extern void init_androidmenu (void);
+extern void syms_of_androidmenu (void);
+
+/* Defined in sfntfont-android.c. */
+
+extern const struct font_driver android_sfntfont_driver;
+
+extern void sfntfont_android_shrink_scanline_buffer (void);
+extern void init_sfntfont_android (void);
+extern void syms_of_sfntfont_android (void);
+
+/* Defined in androidselect.c */
+
+#ifndef ANDROID_STUBIFY
+
+extern void init_androidselect (void);
+extern void syms_of_androidselect (void);
+
+#endif
+
+
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+
+
+
+#endif /* _ANDROID_TERM_H_ */
diff --git a/src/buffer.c b/src/buffer.c
index 0c46b201586..9bcace37e9b 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4719,6 +4719,7 @@ init_buffer_once (void)
#ifdef HAVE_TREE_SITTER
XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
#endif
+ XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
/* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4790,6 +4791,9 @@ init_buffer_once (void)
#ifdef HAVE_TREE_SITTER
bset_ts_parser_list (&buffer_defaults, Qnil);
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ bset_text_conversion_style (&buffer_defaults, Qnil);
+#endif
bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
bset_enable_multibyte_characters (&buffer_defaults, Qt);
@@ -5866,6 +5870,26 @@ If t, displays a cursor related to the usual cursor type
You can also specify the cursor type as in the `cursor-type' variable.
Use Custom to set this variable and update the display. */);
+ /* While this is defined here, each *term.c module must implement
+ the logic itself. */
+
+ DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
+ text_conversion_style),
+ Qnil,
+ doc: /* How the on screen keyboard's input method should insert in this buffer.
+When nil, the input method will be disabled and an ordinary keyboard
+will be displayed in its place.
+When the symbol `action', the input method will insert text directly, but
+will send `return' key events instead of inserting new line characters.
+Any other value means that the input method will insert text directly.
+
+If you need to make non-buffer local changes to this variable, use
+`overriding-text-conversion-style', which see.
+
+This variable does not take immediate effect when set; rather, it
+takes effect upon the next redisplay after the selected window or
+buffer changes. */);
+
DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
doc: /* List of functions called with no args to query before killing a buffer.
The buffer being killed will be current while the functions are running.
diff --git a/src/buffer.h b/src/buffer.h
index e700297a264..e71ffe28045 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -566,6 +566,11 @@ struct buffer
/* A list of tree-sitter parsers for this buffer. */
Lisp_Object ts_parser_list_;
#endif
+
+ /* What type of text conversion the input method should apply to
+ this buffer. */
+ Lisp_Object text_conversion_style_;
+
/* Cursor type to display in non-selected windows.
t means to use hollow box cursor.
See `cursor-type' for other values. */
@@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
b->width_table_ = val;
}
+INLINE void
+bset_text_conversion_style (struct buffer *b, Lisp_Object val)
+{
+ b->text_conversion_style_ = val;
+}
+
/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
the max (resp. min) p such that
diff --git a/src/callproc.c b/src/callproc.c
index 6f3d4fad9be..ee5195385de 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -92,6 +92,10 @@ extern char **environ;
#include "pgtkterm.h"
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
+
/* Pattern used by call-process-region to make temp files. */
static Lisp_Object Vtemp_file_name_pattern;
@@ -144,7 +148,11 @@ static CHILD_SETUP_TYPE child_setup (int, int, int, char **, char **,
directory if it's unreachable. If ENCODE is true, return as a string
suitable for a system call; otherwise, return a string in its
internal representation. Signal an error if the result would not be
- an accessible directory. */
+ an accessible directory.
+
+ If the default directory lies inside a special directory which
+ cannot be made the current working directory, and ENCODE is also
+ set, simply return the home directory. */
Lisp_Object
get_current_directory (bool encode)
@@ -157,6 +165,19 @@ get_current_directory (bool encode)
if (NILP (dir))
dir = build_string ("~");
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+ /* If DIR is an asset directory or a content directory, return
+ the home directory instead. */
+
+ if (encode && (!strcmp (SSDATA (dir), "/assets")
+ || !strncmp (SSDATA (dir), "/assets/", 8)
+ || !strcmp (SSDATA (dir), "/content")
+ || !strncmp (SSDATA (dir), "/content/", 9)))
+ dir = build_string ("~");
+
+#endif /* HAVE_ANDROID && ANDROID_STUBIFY */
+
dir = expand_and_dir_to_file (dir);
Lisp_Object encoded_dir = ENCODE_FILE (remove_slash_colon (dir));
@@ -499,7 +520,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd,
int ok;
ok = openp (Vexec_path, args[0], Vexec_suffixes, &path,
- make_fixnum (X_OK), false, false);
+ make_fixnum (X_OK), false, false, NULL);
if (ok < 0)
report_file_error ("Searching for program", args[0]);
}
@@ -1421,6 +1442,18 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
const char *pty_name, bool pty_in, bool pty_out,
const sigset_t *oldset)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Android 10 and later don't allow directly executing programs
+ installed in the application data directory. Emacs provides a
+ loader binary which replaces the `execve' system call for it and
+ all its children. On these systems, rewrite the command line to
+ call that loader binary instead. */
+
+ if (android_rewrite_spawn_argv ((const char ***) &argv))
+ return 1;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+
#if USABLE_POSIX_SPAWN
/* Prefer the simpler `posix_spawn' if available. `posix_spawn'
doesn't yet support setting up pseudoterminals, so we fall back
@@ -1988,7 +2021,12 @@ init_callproc (void)
dir_warning ("arch-independent data dir", Vdata_directory);
sh = getenv ("SHELL");
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* The Android shell is found under /system/bin, not /bin. */
+ Vshell_file_name = build_string (sh ? sh : "/system/bin/sh");
+#else
Vshell_file_name = build_string (sh ? sh : "/bin/sh");
+#endif
Lisp_Object gamedir = Qnil;
if (PATH_GAME)
@@ -2111,6 +2149,72 @@ use.
See `setenv' and `getenv'. */);
Vprocess_environment = Qnil;
+ DEFVAR_LISP ("ctags-program-name", Vctags_program_name,
+ doc: /* Name of the `ctags' program distributed with Emacs.
+Use this instead of calling `ctags' directly, as `ctags' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vctags_program_name = build_pure_c_string ("ctags");
+#else
+ Vctags_program_name = build_pure_c_string ("libctags.so");
+#endif
+
+ DEFVAR_LISP ("etags-program-name", Vetags_program_name,
+ doc: /* Name of the `etags' program distributed with Emacs.
+Use this instead of calling `etags' directly, as `etags' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vetags_program_name = build_pure_c_string ("etags");
+#else
+ Vetags_program_name = build_pure_c_string ("libetags.so");
+#endif
+
+ DEFVAR_LISP ("hexl-program-name", Vhexl_program_name,
+ doc: /* Name of the `hexl' program distributed with Emacs.
+Use this instead of calling `hexl' directly, as `hexl' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vhexl_program_name = build_pure_c_string ("hexl");
+#else
+ Vhexl_program_name = build_pure_c_string ("libhexl.so");
+#endif
+
+ DEFVAR_LISP ("emacsclient-program-name", Vemacsclient_program_name,
+ doc: /* Name of the `emacsclient' program distributed with Emacs.
+Use this instead of calling `emacsclient' directly, as `emacsclient'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vemacsclient_program_name = build_pure_c_string ("emacsclient");
+#else
+ Vemacsclient_program_name = build_pure_c_string ("libemacsclient.so");
+#endif
+
+ DEFVAR_LISP ("movemail-program-name", Vmovemail_program_name,
+ doc: /* Name of the `movemail' program distributed with Emacs.
+Use this instead of calling `movemail' directly, as `movemail'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+ /* Don't change the name of `movemail' if Emacs is being built to
+ use movemail from another source. */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \
+ || defined HAVE_MAILUTILS
+ Vmovemail_program_name = build_pure_c_string ("movemail");
+#else
+ Vmovemail_program_name = build_pure_c_string ("libmovemail.so");
+#endif
+
+ DEFVAR_LISP ("ebrowse-program-name", Vebrowse_program_name,
+ doc: /* Name of the `ebrowse' program distributed with Emacs.
+Use this instead of calling `ebrowse' directly, as `ebrowse'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vebrowse_program_name = build_pure_c_string ("ebrowse");
+#else
+ Vebrowse_program_name = build_pure_c_string ("libebrowse.so");
+#endif
+
defsubr (&Scall_process);
defsubr (&Sgetenv_internal);
defsubr (&Scall_process_region);
diff --git a/src/charset.c b/src/charset.c
index 7987ffa0c5e..c532f79d282 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -486,7 +486,8 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile,
specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_nothing ();
specbind (Qfile_name_handler_alist, Qnil);
- fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false);
+ fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false,
+ NULL);
fp = fd < 0 ? 0 : fdopen (fd, "r");
if (!fp)
{
@@ -544,7 +545,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile,
entries->entry[idx].c = c;
n_entries++;
}
- fclose (fp);
+ emacs_fclose (fp);
clear_unwind_protect (count);
load_charset_map (charset, head, n_entries, control_flag);
diff --git a/src/coding.c b/src/coding.c
index f014749c4ea..96c3827c326 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -8494,7 +8494,7 @@ preferred_coding_system (void)
return CODING_ID_NAME (id);
}
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
Lisp_Object
from_unicode (Lisp_Object str)
@@ -8512,10 +8512,31 @@ from_unicode (Lisp_Object str)
Lisp_Object
from_unicode_buffer (const wchar_t *wstr)
{
+#if defined WINDOWSNT || defined CYGWIN
/* We get one of the two final null bytes for free. */
ptrdiff_t len = 1 + sizeof (wchar_t) * wcslen (wstr);
AUTO_STRING_WITH_LEN (str, (char *) wstr, len);
return from_unicode (str);
+#else
+ /* This code is used only on Android, where little endian UTF-16
+ strings are extended to 32-bit wchar_t. */
+
+ uint16_t *words;
+ size_t length, i;
+
+ length = wcslen (wstr) + 1;
+
+ USE_SAFE_ALLOCA;
+ SAFE_NALLOCA (words, sizeof *words, length);
+
+ for (i = 0; i < length - 1; ++i)
+ words[i] = wstr[i];
+
+ words[i] = '\0';
+ AUTO_STRING_WITH_LEN (str, (char *) words,
+ (length - 1) * sizeof *words);
+ return unbind_to (sa_count, from_unicode (str));
+#endif
}
wchar_t *
@@ -8535,7 +8556,7 @@ to_unicode (Lisp_Object str, Lisp_Object *buf)
return WCSDATA (*buf);
}
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
/*** 8. Emacs Lisp library functions ***/
@@ -11734,7 +11755,7 @@ syms_of_coding (void)
DEFSYM (Qutf_8_unix, "utf-8-unix");
DEFSYM (Qutf_8_emacs, "utf-8-emacs");
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
/* No, not utf-16-le: that one has a BOM. */
DEFSYM (Qutf_16le, "utf-16le");
#endif
diff --git a/src/coding.h b/src/coding.h
index b8d4f5f27e1..08c29c884a5 100644
--- a/src/coding.h
+++ b/src/coding.h
@@ -709,7 +709,7 @@ extern void encode_coding_object (struct coding_system *,
/* Defined in this file. */
INLINE int surrogates_to_codepoint (int, int);
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
/* These functions use Lisp string objects to store the UTF-16LE
strings that modern versions of Windows expect. These strings are
@@ -732,7 +732,7 @@ extern Lisp_Object from_unicode (Lisp_Object str);
/* Convert WSTR to an Emacs string. */
extern Lisp_Object from_unicode_buffer (const wchar_t *wstr);
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
/* Macros for backward compatibility. */
diff --git a/src/conf_post.h b/src/conf_post.h
index 0d5f90a6910..f31e012dc6e 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -461,3 +461,13 @@ extern int emacs_setenv_TZ (char const *);
#else
# define UNINIT /* empty */
#endif
+
+/* MB_CUR_MAX is often broken on systems which copy-paste LLVM
+ headers, so replace its definition with a working one if
+ necessary. */
+
+#ifdef REPLACEMENT_MB_CUR_MAX
+#include <stdlib.h>
+#undef MB_CUR_MAX
+#define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX
+#endif /* REPLACEMENT_MB_CUR_MAX */
diff --git a/src/dired.c b/src/dired.c
index 3f55c4c3830..93487d552e2 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -44,6 +44,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "msdos.h" /* for fstatat */
#endif
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+typedef DIR emacs_dir;
+#define emacs_readdir readdir
+#define emacs_closedir closedir
+#else
+
+#include "android.h"
+
+/* The Android emulation of dirent stuff is required to be able to
+ list the /assets special directory. */
+typedef struct android_dir emacs_dir;
+#define emacs_readdir android_readdir
+#define emacs_closedir android_closedir
+#endif
+
#ifdef WINDOWSNT
extern int is_slow_fs (const char *);
#endif
@@ -78,19 +93,33 @@ dirent_type (struct dirent *dp)
#endif
}
-static DIR *
+static emacs_dir *
open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
{
char *name = SSDATA (encoded_dirname);
- DIR *d;
+ emacs_dir *d;
int fd, opendir_errno;
-#ifdef DOS_NT
- /* Directories cannot be opened. The emulation assumes that any
- file descriptor other than AT_FDCWD corresponds to the most
- recently opened directory. This hack is good enough for Emacs. */
+#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ /* On DOS_NT, directories cannot be opened. The emulation assumes
+ that any file descriptor other than AT_FDCWD corresponds to the
+ most recently opened directory. This hack is good enough for
+ Emacs.
+
+ This code is also used on Android for a different reason: a
+ special `assets' directory outside the normal file system is used
+ to open assets inside the Android application package, and must
+ be listed using the opendir-like interface provided in
+ android.h. */
fd = 0;
+#ifndef HAVE_ANDROID
d = opendir (name);
+#else
+ d = android_opendir (name);
+
+ if (d)
+ fd = android_dirfd (d);
+#endif
opendir_errno = errno;
#else
fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
@@ -125,7 +154,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
static void
directory_files_internal_unwind (void *d)
{
- closedir (d);
+ emacs_closedir (d);
}
/* Return the next directory entry from DIR; DIR's name is DIRNAME.
@@ -133,12 +162,12 @@ directory_files_internal_unwind (void *d)
Signal any unrecoverable errors. */
static struct dirent *
-read_dirent (DIR *dir, Lisp_Object dirname)
+read_dirent (emacs_dir *dir, Lisp_Object dirname)
{
while (true)
{
errno = 0;
- struct dirent *dp = readdir (dir);
+ struct dirent *dp = emacs_readdir (dir);
if (dp || errno == 0)
return dp;
if (! (errno == EAGAIN || errno == EINTR))
@@ -190,7 +219,10 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
int fd;
- DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
+
+ /* 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
file-attributes on filenames, both of which can throw, so we must
@@ -300,7 +332,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
}
- closedir (d);
+ emacs_closedir (d);
#ifdef WINDOWSNT
if (attrs)
Vw32_get_true_file_attributes = w32_save;
@@ -514,7 +546,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
}
}
int fd;
- DIR *d = open_directory (dirname, encoded_dir, &fd);
+ emacs_dir *d = open_directory (dirname, encoded_dir, &fd);
record_unwind_protect_ptr (directory_files_internal_unwind, d);
/* Loop reading directory entries. */
@@ -855,7 +887,9 @@ 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, "/");
- bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0;
+
+ bool dirp = sys_faccessat (fd, subdir_name,
+ F_OK, AT_EACCESS) == 0;
SAFE_FREE ();
return dirp;
}
@@ -979,14 +1013,15 @@ file_attributes (int fd, char const *name,
int err = EINVAL;
-#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG
+#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG \
+ && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
int namefd = emacs_openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW, 0);
if (namefd < 0)
err = errno;
else
{
record_unwind_protect_int (close_file_unwind, namefd);
- if (fstat (namefd, &s) != 0)
+ if (sys_fstat (namefd, &s) != 0)
{
err = errno;
/* The Linux kernel before version 3.6 does not support
diff --git a/src/dispextern.h b/src/dispextern.h
index ece128949f5..402972d33d9 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -53,8 +53,15 @@ typedef struct
unsigned short red, green, blue;
} Emacs_Color;
+#ifndef HAVE_ANDROID
/* Accommodate X's usage of None as a null resource ID. */
#define No_Cursor (NULL)
+#else
+/* Android doesn't support cursors and also uses handles. */
+#define No_Cursor 0
+#endif
+
+#ifndef HAVE_ANDROID
/* XRectangle-like struct used by non-X GUI code. */
typedef struct
@@ -63,6 +70,12 @@ typedef struct
unsigned width, height;
} Emacs_Rectangle;
+#else
+
+typedef struct android_rectangle Emacs_Rectangle;
+
+#endif
+
/* XGCValues-like struct used by non-X GUI code. */
typedef struct
{
@@ -144,6 +157,13 @@ typedef Emacs_Pixmap Emacs_Pix_Container;
typedef Emacs_Pixmap Emacs_Pix_Context;
#endif
+#ifdef HAVE_ANDROID
+#include "androidgui.h"
+typedef struct android_display_info Display_Info;
+typedef struct android_image *Emacs_Pix_Container;
+typedef struct android_image *Emacs_Pix_Context;
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
# include <time.h>
# include "fontset.h"
@@ -157,6 +177,22 @@ typedef void *Emacs_Cursor;
#define NativeRectangle int
#endif
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* ``box'' structure similar to that found in the X sample server,
+ meaning that X2 and Y2 are not actually the end of the box, but one
+ pixel past the end of the box, which makes checking for overlaps
+ less necessary. This is convenient to use in every GUI port. */
+
+struct gui_box
+{
+ /* Bounds of the box. */
+ int x1, y1;
+ int x2, y2;
+};
+
+#endif
+
/* Text cursor types. */
enum text_cursor_kinds
@@ -1401,6 +1437,8 @@ struct glyph_string
/* The GC to use for drawing this glyph string. */
#if defined (HAVE_X_WINDOWS)
GC gc;
+#elif defined HAVE_ANDROID
+ struct android_gc *gc;
#endif
#if defined (HAVE_NTGUI)
Emacs_GC *gc;
@@ -1681,6 +1719,8 @@ struct face
drawing the characters in this face. */
# ifdef HAVE_X_WINDOWS
GC gc;
+# elif defined HAVE_ANDROID
+ struct android_gc *gc;
# else
Emacs_GC *gc;
# endif
@@ -3057,8 +3097,9 @@ struct redisplay_interface
#ifdef HAVE_WINDOW_SYSTEM
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
- || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
+# if (defined USE_CAIRO || defined HAVE_XRENDER \
+ || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID)
# define HAVE_NATIVE_TRANSFORMS
# endif
@@ -3094,6 +3135,13 @@ struct image
int original_width, original_height;
# endif
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+ /* Android images of the image, corresponding to the above Pixmaps.
+ Non-NULL means it and its Pixmap counterpart may be out of sync
+ and the latter is outdated. NULL means the X image has been
+ synchronized to Pixmap. */
+ struct android_image *ximg, *mask_img;
+#endif /* HAVE_ANDROID */
#ifdef HAVE_NTGUI
XFORM xform;
#endif
@@ -3480,6 +3528,7 @@ extern void get_glyph_string_clip_rect (struct glyph_string *,
NativeRectangle *nr);
extern Lisp_Object find_hot_spot (Lisp_Object, int, int);
+extern int get_tab_bar_item_kbd (struct frame *, int, int, int *, bool *);
extern Lisp_Object handle_tab_bar_click (struct frame *,
int, int, bool, int);
extern void handle_tool_bar_click (struct frame *,
@@ -3491,6 +3540,9 @@ extern void expose_frame (struct frame *, int, int, int, int);
extern bool gui_intersect_rectangles (const Emacs_Rectangle *,
const Emacs_Rectangle *,
Emacs_Rectangle *);
+extern void gui_union_rectangles (const Emacs_Rectangle *,
+ const Emacs_Rectangle *,
+ Emacs_Rectangle *);
extern void gui_consider_frame_title (Lisp_Object);
#endif /* HAVE_WINDOW_SYSTEM */
@@ -3499,9 +3551,11 @@ extern void gui_clear_window_mouse_face (struct window *);
extern void cancel_mouse_face (struct frame *);
extern bool clear_mouse_face (Mouse_HLInfo *);
extern bool cursor_in_mouse_face_p (struct window *w);
+#ifndef HAVE_ANDROID
extern void tty_draw_row_with_mouse_face (struct window *, struct glyph_row *,
int, int, enum draw_glyphs_face);
extern void display_tty_menu_item (const char *, int, int, int, int, bool);
+#endif
extern struct glyph *x_y_to_hpos_vpos (struct window *, int, int, int *, int *,
int *, int *, int *);
/* Flags passed to try_window. */
@@ -3563,7 +3617,7 @@ void prepare_image_for_display (struct frame *, struct image *);
ptrdiff_t lookup_image (struct frame *, Lisp_Object, int);
#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS \
- || defined HAVE_HAIKU
+ || defined HAVE_HAIKU || defined HAVE_ANDROID
#define RGB_PIXEL_COLOR unsigned long
#endif
@@ -3644,6 +3698,9 @@ void gamma_correct (struct frame *, COLORREF *);
#ifdef HAVE_HAIKU
void gamma_correct (struct frame *, Emacs_Color *);
#endif
+#ifdef HAVE_ANDROID
+extern void gamma_correct (struct frame *, Emacs_Color *);
+#endif
#ifdef HAVE_WINDOW_SYSTEM
diff --git a/src/dispnew.c b/src/dispnew.c
index 9133d515ca3..8d039bf9a3d 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -43,6 +43,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "xwidget.h"
#include "pdumper.h"
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@@ -788,7 +792,7 @@ clear_current_matrices (register struct frame *f)
if (f->current_matrix)
clear_glyph_matrix (f->current_matrix);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Clear the matrix of the menu bar window, if such a window exists.
The menu bar window is currently used to display menus on X when
no toolkit support is compiled in. */
@@ -822,7 +826,7 @@ clear_desired_matrices (register struct frame *f)
if (f->desired_matrix)
clear_glyph_matrix (f->desired_matrix);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
if (WINDOWP (f->menu_bar_window))
clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix);
#endif
@@ -1156,6 +1160,7 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p)
}
}
+#ifndef HAVE_ANDROID
/* Return a hash code for glyph row ROW, which may
be from current or desired matrix of frame F. */
@@ -1248,6 +1253,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix *matrix, int vpos)
return len;
}
+#endif
/* Return true if the glyph rows A and B have equal contents.
MOUSE_FACE_P means compare the mouse_face_p flags of A and B, too. */
@@ -2160,7 +2166,7 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
/* Allocate/reallocate window matrices. */
allocate_matrices_for_window_redisplay (XWINDOW (FRAME_ROOT_WINDOW (f)));
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Allocate/ reallocate matrices of the dummy window used to display
the menu bar under X when no X toolkit support is available. */
{
@@ -2302,7 +2308,7 @@ free_glyphs (struct frame *f)
if (!NILP (f->root_window))
free_window_matrices (XWINDOW (f->root_window));
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Free the dummy window for menu bars without X toolkit and its
glyph matrices. */
if (!NILP (f->menu_bar_window))
@@ -3175,6 +3181,7 @@ redraw_frame (struct frame *f)
its redisplay done. */
mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true);
+
f->garbaged = false;
}
@@ -3240,7 +3247,7 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
when pending input is detected. */
update_begin (f);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Update the menu bar on X frames that don't have toolkit
support. */
if (WINDOWP (f->menu_bar_window))
@@ -5077,6 +5084,10 @@ update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p,
static bool
scrolling (struct frame *frame)
{
+ /* In fact this code should never be reached at all under
+ Android. */
+
+#ifndef HAVE_ANDROID
int unchanged_at_top, unchanged_at_bottom;
int window_size;
int changed_lines;
@@ -5167,6 +5178,7 @@ scrolling (struct frame *frame)
free_at_end_vpos - unchanged_at_top);
SAFE_FREE ();
+#endif
return false;
}
@@ -5208,7 +5220,9 @@ count_match (struct glyph *str1, struct glyph *end1, struct glyph *str2, struct
/* Char insertion/deletion cost vector, from term.c */
+#ifndef HAVE_ANDROID
#define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_TOTAL_COLS ((f))])
+#endif
/* Perform a frame-based update on line VPOS in frame FRAME. */
@@ -5413,7 +5427,10 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p)
tem = (nlen - nsp) - (olen - osp);
if (endmatch && tem
&& (!FRAME_CHAR_INS_DEL_OK (f)
- || endmatch <= char_ins_del_cost (f)[tem]))
+#ifndef HAVE_ANDROID
+ || endmatch <= char_ins_del_cost (f)[tem]
+#endif
+ ))
endmatch = 0;
/* nsp - osp is the distance to insert or delete.
@@ -5423,7 +5440,10 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p)
if (nsp != osp
&& (!FRAME_CHAR_INS_DEL_OK (f)
- || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]))
+#ifndef HAVE_ANDROID
+ || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]
+#endif
+ ))
{
begmatch = 0;
endmatch = 0;
@@ -6052,7 +6072,7 @@ FILE = nil means just close any termscript file currently open. */)
if (tty->termscript != 0)
{
block_input ();
- fclose (tty->termscript);
+ emacs_fclose (tty->termscript);
tty->termscript = 0;
unblock_input ();
}
@@ -6546,6 +6566,15 @@ init_display_interactive (void)
}
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+ if (!inhibit_window_system && android_init_gui)
+ {
+ Vinitial_window_system = Qandroid;
+ android_term_init ();
+ return;
+ }
+#endif
+
#ifdef HAVE_NTGUI
if (!inhibit_window_system)
{
@@ -6600,6 +6629,7 @@ init_display_interactive (void)
exit (1);
}
+#ifndef HAVE_ANDROID
{
struct terminal *t;
struct frame *f = XFRAME (selected_frame);
@@ -6642,6 +6672,11 @@ init_display_interactive (void)
: Qnil));
Fmodify_frame_parameters (selected_frame, tty_arg);
}
+#else
+ fatal ("Could not establish a connection to the Android application.\n"
+ "Emacs does not work on text terminals when built to run as"
+ " part of an Android application package.");
+#endif
{
struct frame *sf = SELECTED_FRAME ();
diff --git a/src/editfns.c b/src/editfns.c
index 44e11841faa..e72d86d84d0 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -33,6 +33,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/utsname.h>
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
#include "lisp.h"
#include <float.h>
@@ -1264,7 +1268,11 @@ is in general a comma-separated list. */)
if (!pw)
return Qnil;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ p = android_user_full_name (pw);
+#else
p = USER_FULL_NAME;
+#endif
/* Chop off everything after the first comma, since 'pw_gecos' is a
comma-separated list. */
q = strchr (p, ',');
diff --git a/src/emacs-module.c b/src/emacs-module.c
index 10699ec25d9..86360a0f225 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -206,7 +206,7 @@ static void module_non_local_exit_signal_1 (emacs_env *,
static void module_non_local_exit_throw_1 (emacs_env *,
Lisp_Object, Lisp_Object);
static void module_out_of_memory (emacs_env *);
-static void module_reset_handlerlist (struct handler **);
+static void module_reset_handlerlist (struct handler *);
static bool value_storage_contains_p (const struct emacs_value_storage *,
emacs_value, ptrdiff_t *);
@@ -246,10 +246,6 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
of `internal_condition_case' etc., and to avoid worrying about
passing information to the handler functions. */
-#if !HAS_ATTRIBUTE (cleanup)
- #error "__attribute__ ((cleanup)) not supported by this compiler; try GCC"
-#endif
-
/* Place this macro at the beginning of a function returning a number
or a pointer to handle non-local exits. The function must have an
ENV parameter. The function will return the specified value if a
@@ -257,8 +253,8 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
/* It is very important that pushing the handler doesn't itself raise
a signal. Install the cleanup only after the handler has been
- pushed. Use __attribute__ ((cleanup)) to avoid
- non-local-exit-prone manual cleanup.
+ pushed. All code following this point should use
+ MODULE_INTERNAL_CLEANUP before each return.
The do-while forces uses of the macro to be followed by a semicolon.
This macro cannot enclose its entire body inside a do-while, as the
@@ -278,17 +274,20 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
return retval; \
} \
struct handler *internal_cleanup \
- __attribute__ ((cleanup (module_reset_handlerlist))) \
= internal_handler; \
if (sys_setjmp (internal_cleanup->jmp)) \
{ \
module_handle_nonlocal_exit (env, \
internal_cleanup->nonlocal_exit, \
internal_cleanup->val); \
+ module_reset_handlerlist (internal_cleanup); \
return retval; \
} \
do { } while (false)
+#define MODULE_INTERNAL_CLEANUP() \
+ module_reset_handlerlist (internal_cleanup)
+
/* Implementation of runtime and environment functions.
@@ -315,7 +314,10 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
Emacs functions, by placing the macro
MODULE_HANDLE_NONLOCAL_EXIT right after the above 2 tests.
- 5. Do NOT use 'eassert' for checking validity of user code in the
+ 5. Finally, any code which expands MODULE_HANDLE_NONLOCAL_EXIT
+ should use MODULE_INTERNAL_CLEANUP prior to returning.
+
+ 6. Do NOT use 'eassert' for checking validity of user code in the
module. Instead, make those checks part of the code, and if the
check fails, call 'module_non_local_exit_signal_1' or
'module_non_local_exit_throw_1' to report the error. This is
@@ -438,6 +440,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
bool overflow = ckd_add (&ref->refcount, ref->refcount, 1);
if (overflow)
overflow_error ();
+ MODULE_INTERNAL_CLEANUP ();
return &ref->value;
}
else
@@ -450,6 +453,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
Lisp_Object value;
XSETPSEUDOVECTOR (value, ref, PVEC_OTHER);
hash_put (h, new_obj, value, hashcode);
+ MODULE_INTERNAL_CLEANUP ();
return &ref->value;
}
}
@@ -481,6 +485,8 @@ module_free_global_ref (emacs_env *env, emacs_value global_value)
if (--ref->refcount == 0)
hash_remove_from_table (h, obj);
}
+
+ MODULE_INTERNAL_CLEANUP ();
}
static enum emacs_funcall_exit
@@ -574,6 +580,8 @@ static emacs_value
module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
emacs_function func, const char *docstring, void *data)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= min_arity
@@ -598,7 +606,9 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
XSET_MODULE_FUNCTION (result, function);
eassert (MODULE_FUNCTIONP (result));
- return lisp_to_value (env, result);
+ value = lisp_to_value (env, result);
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static emacs_finalizer
@@ -607,6 +617,7 @@ module_get_function_finalizer (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_MODULE_FUNCTION (lisp);
+ MODULE_INTERNAL_CLEANUP ();
return XMODULE_FUNCTION (lisp)->finalizer;
}
@@ -618,6 +629,7 @@ module_set_function_finalizer (emacs_env *env, emacs_value arg,
Lisp_Object lisp = value_to_lisp (arg);
CHECK_MODULE_FUNCTION (lisp);
XMODULE_FUNCTION (lisp)->finalizer = fin;
+ MODULE_INTERNAL_CLEANUP ();
}
void
@@ -637,6 +649,7 @@ module_make_interactive (emacs_env *env, emacs_value function, emacs_value spec)
/* Normalize (interactive nil) to (interactive). */
XMODULE_FUNCTION (lisp_fun)->interactive_form
= NILP (lisp_spec) ? list1 (Qinteractive) : list2 (Qinteractive, lisp_spec);
+ MODULE_INTERNAL_CLEANUP ();
}
Lisp_Object
@@ -670,21 +683,30 @@ module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs,
newargs[1 + i] = value_to_lisp (args[i]);
emacs_value result = lisp_to_value (env, Ffuncall (nargs1, newargs));
SAFE_FREE ();
+ MODULE_INTERNAL_CLEANUP ();
return result;
}
static emacs_value
module_intern (emacs_env *env, const char *name)
{
+ emacs_value tem;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, intern (name));
+ tem = lisp_to_value (env, intern (name));
+ MODULE_INTERNAL_CLEANUP ();
+ return tem;
}
static emacs_value
module_type_of (emacs_env *env, emacs_value arg)
{
+ emacs_value tem;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+ tem = lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+ MODULE_INTERNAL_CLEANUP ();
+ return tem;
}
static bool
@@ -710,14 +732,20 @@ module_extract_integer (emacs_env *env, emacs_value arg)
intmax_t i;
if (! integer_to_intmax (lisp, &i))
xsignal1 (Qoverflow_error, lisp);
+ MODULE_INTERNAL_CLEANUP ();
return i;
}
static emacs_value
module_make_integer (emacs_env *env, intmax_t n)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_int (n));
+ value = lisp_to_value (env, make_int (n));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static double
@@ -726,14 +754,21 @@ module_extract_float (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (0);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_TYPE (FLOATP (lisp), Qfloatp, lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return XFLOAT_DATA (lisp);
}
static emacs_value
module_make_float (emacs_env *env, double d)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_float (d));
+ value = lisp_to_value (env, make_float (d));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static bool
@@ -765,6 +800,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf,
if (buf == NULL)
{
*len = required_buf_size;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
@@ -780,36 +816,51 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf,
*len = required_buf_size;
memcpy (buf, SDATA (lisp_str_utf8), raw_size + 1);
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
static emacs_value
module_make_string (emacs_env *env, const char *str, ptrdiff_t len)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= len && len <= STRING_BYTES_BOUND))
overflow_error ();
Lisp_Object lstr
= len == 0 ? empty_multibyte_string : module_decode_utf_8 (str, len);
- return lisp_to_value (env, lstr);
+ value = lisp_to_value (env, lstr);
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static emacs_value
module_make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t length)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= length && length <= STRING_BYTES_BOUND))
overflow_error ();
Lisp_Object lstr
= length == 0 ? empty_unibyte_string : make_unibyte_string (str, length);
- return lisp_to_value (env, lstr);
+ value = lisp_to_value (env, lstr);
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static emacs_value
module_make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_user_ptr (fin, ptr));
+ value = lisp_to_value (env, make_user_ptr (fin, ptr));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static void *
@@ -818,6 +869,8 @@ module_get_user_ptr (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return XUSER_PTR (lisp)->p;
}
@@ -828,6 +881,7 @@ module_set_user_ptr (emacs_env *env, emacs_value arg, void *ptr)
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
XUSER_PTR (lisp)->p = ptr;
+ MODULE_INTERNAL_CLEANUP ();
}
static emacs_finalizer
@@ -836,6 +890,7 @@ module_get_user_finalizer (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
return XUSER_PTR (lisp)->finalizer;
}
@@ -847,6 +902,7 @@ module_set_user_finalizer (emacs_env *env, emacs_value arg,
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
XUSER_PTR (lisp)->finalizer = fin;
+ MODULE_INTERNAL_CLEANUP ();
}
static void
@@ -866,15 +922,21 @@ module_vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index,
Lisp_Object lisp = value_to_lisp (vector);
check_vec_index (lisp, index);
ASET (lisp, index, value_to_lisp (value));
+ MODULE_INTERNAL_CLEANUP ();
}
static emacs_value
module_vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (vector);
check_vec_index (lisp, index);
- return lisp_to_value (env, AREF (lisp, index));
+ value = lisp_to_value (env, AREF (lisp, index));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static ptrdiff_t
@@ -883,6 +945,8 @@ module_vec_size (emacs_env *env, emacs_value vector)
MODULE_FUNCTION_BEGIN (0);
Lisp_Object lisp = value_to_lisp (vector);
CHECK_VECTOR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return ASIZE (lisp);
}
@@ -898,23 +962,37 @@ module_should_quit (emacs_env *env)
static enum emacs_process_input_result
module_process_input (emacs_env *env)
{
+ enum emacs_process_input_result rc;
+
MODULE_FUNCTION_BEGIN (emacs_process_input_quit);
maybe_quit ();
- return emacs_process_input_continue;
+ rc = emacs_process_input_continue;
+ MODULE_INTERNAL_CLEANUP ();
+ return rc;
}
static struct timespec
module_extract_time (emacs_env *env, emacs_value arg)
{
+ struct timespec value;
+
MODULE_FUNCTION_BEGIN ((struct timespec) {0});
- return lisp_time_argument (value_to_lisp (arg));
+ value = lisp_time_argument (value_to_lisp (arg));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static emacs_value
module_make_time (emacs_env *env, struct timespec time)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, timespec_to_lisp (time));
+ value = lisp_to_value (env, timespec_to_lisp (time));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
/*
@@ -991,7 +1069,10 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
EMACS_INT x = XFIXNUM (o);
*sign = (0 < x) - (x < 0);
if (x == 0 || count == NULL)
- return true;
+ {
+ MODULE_INTERNAL_CLEANUP ();
+ return true;
+ }
/* As a simplification we don't check how many array elements
are exactly required, but use a reasonable static upper
bound. For most architectures exactly one element should
@@ -1002,6 +1083,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
if (magnitude == NULL)
{
*count = required;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
if (*count < required)
@@ -1020,12 +1102,16 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
verify (required * bits < PTRDIFF_MAX);
for (ptrdiff_t i = 0; i < required; ++i)
magnitude[i] = (emacs_limb_t) (u >> (i * bits));
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
const mpz_t *x = xbignum_val (o);
*sign = mpz_sgn (*x);
if (count == NULL)
- return true;
+ {
+ MODULE_INTERNAL_CLEANUP ();
+ return true;
+ }
size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb;
eassert (required_size <= PTRDIFF_MAX);
ptrdiff_t required = (ptrdiff_t) required_size;
@@ -1033,6 +1119,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
if (magnitude == NULL)
{
*count = required;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
if (*count < required)
@@ -1045,6 +1132,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
size_t written;
mpz_export (magnitude, &written, order, size, endian, nails, *x);
eassert (written == required_size);
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
@@ -1052,21 +1140,34 @@ static emacs_value
module_make_big_integer (emacs_env *env, int sign,
ptrdiff_t count, const emacs_limb_t *magnitude)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (sign == 0)
- return lisp_to_value (env, make_fixed_natnum (0));
+ {
+ value = lisp_to_value (env, make_fixed_natnum (0));
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
+ }
enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 };
mpz_import (mpz[0], count, order, size, endian, nails, magnitude);
if (sign < 0)
mpz_neg (mpz[0], mpz[0]);
- return lisp_to_value (env, make_integer_mpz ());
+ value = lisp_to_value (env, make_integer_mpz ());
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static int
module_open_channel (emacs_env *env, emacs_value pipe_process)
{
+ int rc;
+
MODULE_FUNCTION_BEGIN (-1);
- return open_channel_for_module (value_to_lisp (pipe_process));
+ rc = open_channel_for_module (value_to_lisp (pipe_process));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return rc;
}
@@ -1519,12 +1620,13 @@ finalize_runtime_unwind (void *raw_ert)
/* Must be called after setting up a handler immediately before
returning from the function. See the comments in lisp.h and the
code in eval.c for details. The macros below arrange for this
- function to be called automatically. PHANDLERLIST points to a word
- containing the handler list, for sanity checking. */
+ function to be called automatically. IHANDLERLIST points to the
+ handler list. */
+
static void
-module_reset_handlerlist (struct handler **phandlerlist)
+module_reset_handlerlist (struct handler *ihandlerlist)
{
- eassert (handlerlist == *phandlerlist);
+ eassert (handlerlist == ihandlerlist);
handlerlist = handlerlist->next;
}
diff --git a/src/emacs.c b/src/emacs.c
index 80a013b68df..d75a83ab9d8 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -33,6 +33,14 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "sysstdio.h"
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
#ifdef WINDOWSNT
#include <fcntl.h>
#include <sys/socket.h>
@@ -137,6 +145,10 @@ extern char etext;
#include <sys/resource.h>
#endif
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+#endif
+
/* We don't guard this with HAVE_TREE_SITTER because treesit.o is
always compiled (to provide treesit-available-p). */
#include "treesit.h"
@@ -411,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;
@@ -511,7 +531,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
{
Lisp_Object found;
int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
- &found, make_fixnum (X_OK), false, false);
+ &found, make_fixnum (X_OK), false, false,
+ NULL);
if (yes == 1)
{
/* Add /: to the front of the name
@@ -724,6 +745,8 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr,
}
}
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
/* Find a name (absolute or relative) of the Emacs executable whose
name (as passed into this program) is ARGV0. Called early in
initialization by portable dumper loading code, so avoid Lisp and
@@ -823,6 +846,8 @@ find_emacs_executable (char const *argv0, ptrdiff_t *candidate_size)
#endif /* !WINDOWSNT */
}
+#endif
+
#ifdef HAVE_PDUMPER
static const char *
@@ -851,10 +876,38 @@ dump_error_to_string (int result)
}
}
-/* This function returns the Emacs executable. */
+/* This function returns the Emacs executable. DUMP_FILE is ignored
+ outside of Android. Otherwise, it is the name of the dump file to
+ use, or NULL if Emacs should look for a ``--dump-file'' argument
+ instead. */
+
static char *
-load_pdump (int argc, char **argv)
+load_pdump (int argc, char **argv, char *dump_file)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ int skip_args = 0, result;
+
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-dump-file", "--dump-file",
+ 6, &dump_file, &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL,
+ &skip_args))
+ break;
+ skip_args++;
+ }
+
+ if (!dump_file)
+ return argv[0];
+
+ result = pdumper_load (dump_file, argv[0]);
+
+ if (result != PDUMPER_LOAD_SUCCESS)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+ return argv[0];
+#else
+
const char *const suffix = ".pdmp";
int result;
char *emacs_executable = argv[0];
@@ -885,7 +938,7 @@ load_pdump (int argc, char **argv)
/* Look for an explicitly-specified dump file. */
const char *path_exec = PATH_EXEC;
- char *dump_file = NULL;
+ dump_file = NULL;
int skip_args = 0;
while (skip_args < argc - 1)
{
@@ -1047,6 +1100,7 @@ load_pdump (int argc, char **argv)
xfree (dump_file);
return emacs_executable;
+#endif
}
#endif /* HAVE_PDUMPER */
@@ -1123,7 +1177,7 @@ load_seccomp (const char *file)
goto out;
}
struct stat stat;
- if (fstat (fd, &stat) != 0)
+ if (sys_fstat (fd, &stat) != 0)
{
emacs_perror ("fstat");
goto out;
@@ -1225,12 +1279,24 @@ maybe_load_seccomp (int argc, char **argv)
#endif /* SECCOMP_USABLE */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+int
+android_emacs_init (int argc, char **argv, char *dump_file)
+#else
int
main (int argc, char **argv)
+#endif
{
/* Variable near the bottom of the stack, and aligned appropriately
for pointers. */
void *stack_bottom_variable;
+ int old_argc;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ char *dump_file;
+
+ /* This is just a dummy argument used to avoid extra defines. */
+ dump_file = NULL;
+#endif
/* First, check whether we should apply a seccomp filter. This
should come at the very beginning to allow the filter to protect
@@ -1360,7 +1426,7 @@ main (int argc, char **argv)
#ifdef HAVE_PDUMPER
if (attempt_load_pdump)
- initial_emacs_executable = load_pdump (argc, argv);
+ initial_emacs_executable = load_pdump (argc, argv, dump_file);
#else
ptrdiff_t bufsize;
initial_emacs_executable = find_emacs_executable (argv[0], &bufsize);
@@ -1425,8 +1491,9 @@ main (int argc, char **argv)
bool only_version = false;
sort_args (argc, argv);
- argc = 0;
- while (argv[argc]) argc++;
+ old_argc = argc, argc = 0;
+ /* Don't allow going past argv. */
+ while (argc < old_argc && argv[argc]) argc++;
skip_args = 0;
if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
@@ -1934,6 +2001,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#ifdef HAVE_WINDOW_SYSTEM
init_fringe_once (); /* Swap bitmaps if necessary. */
#endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+ syms_of_textconv ();
+#endif
}
init_alloc ();
@@ -2375,6 +2445,18 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
syms_of_fontset ();
#endif /* HAVE_HAIKU */
+#ifdef HAVE_ANDROID
+ syms_of_androidterm ();
+ syms_of_androidfns ();
+ syms_of_androidmenu ();
+ syms_of_fontset ();
+#if !defined ANDROID_STUBIFY
+ syms_of_androidfont ();
+ syms_of_androidselect ();
+ syms_of_sfntfont ();
+ syms_of_sfntfont_android ();
+#endif /* !ANDROID_STUBIFY */
+#endif /* HAVE_ANDROID */
syms_of_gnutls ();
@@ -2471,6 +2553,17 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_window ();
init_font ();
+#ifdef HAVE_ANDROID
+ init_androidmenu ();
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ init_androidfont ();
+ init_androidselect ();
+ init_sfntfont ();
+ init_sfntfont_android ();
+#endif
+
if (!initialized)
{
char *file;
@@ -2525,6 +2618,16 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
safe_run_hooks (Qafter_pdump_load_hook);
#endif
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && 0
+ /* This comes very late in the startup process because it requires
+ most of lisp/international to be loaded. This approach doesn't
+ work because normal-top-level runs and creates the initial frame
+ before fonts are initialized. So this is done in
+ normal-top-level instead. */
+ Vtop_level = list3 (Qprogn, Vtop_level,
+ list1 (Qandroid_enumerate_fonts));
+#endif
+
/* Enter editor command loop. This never returns. */
set_initial_minibuffer_mode ();
Frecursive_edit ();
@@ -2850,7 +2953,14 @@ killed. */
#ifndef WINDOWSNT
/* Do some checking before shutting down Emacs, because errors
can't be meaningfully reported afterwards. */
- if (!NILP (restart))
+ if (!NILP (restart)
+ /* Don't perform the following checks when Emacs is running as
+ an Android GUI application, because there the system is
+ relied on to restart Emacs. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ && !android_init_gui
+#endif
+ )
{
/* This is very unlikely, but it's possible to execute a binary
(on some systems) with no argv. */
@@ -2912,6 +3022,13 @@ killed. */
if (!NILP (restart))
{
turn_on_atimers (false);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Re-executing the Emacs process created by the system doesn't
+ work. Instead, schedule a restart for a few hundered
+ milliseconds and exit Emacs. */
+ if (android_init_gui)
+ android_restart_emacs ();
+#endif
#ifdef WINDOWSNT
if (w32_reexec_emacs (initial_cmdline, initial_wd) < 0)
#else
@@ -2952,7 +3069,7 @@ shut_down_emacs (int sig, Lisp_Object stuff)
Vinhibit_redisplay = Qt;
/* If we are controlling the terminal, reset terminal modes. */
-#ifndef DOS_NT
+#if !defined DOS_NT && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
pid_t tpgrp = tcgetpgrp (STDIN_FILENO);
if (tpgrp != -1 && tpgrp == getpgrp ())
{
@@ -3471,6 +3588,7 @@ Special values:
`windows-nt' compiled as a native W32 application.
`cygwin' compiled using the Cygwin library.
`haiku' compiled for a Haiku system.
+ `android' compiled for Android.
Anything else (in Emacs 26, the possibilities are: aix, berkeley-unix,
hpux, usg-unix-v) indicates some sort of Unix system. */);
Vsystem_type = intern_c_string (SYSTEM_TYPE);
diff --git a/src/epaths.in b/src/epaths.in
index b290f0243bc..afddd57763a 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -18,6 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
/* Together with PATH_SITELOADSEARCH, this gives the default value of
load-path, which is the search path for the Lisp function "load".
@@ -79,3 +80,24 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* Where Emacs should look for the application default file. */
#define PATH_X_DEFAULTS "/usr/lib/X11/%L/%T/%N%C%S:/usr/lib/X11/%l/%T/%N%C%S:/usr/lib/X11/%T/%N%C%S:/usr/lib/X11/%L/%T/%N%S:/usr/lib/X11/%l/%T/%N%S:/usr/lib/X11/%T/%N%S"
+
+#else
+
+/* Replace the defines above with links to files in the assets
+ pseudo-directory. Preserve the extra spaces, or epaths.in will not
+ be generated correctly. */
+
+ # define PATH_EXEC (android_lib_dir)
+ # define PATH_LOADSEARCH "/assets/lisp/"
+ # define PATH_SITELOADSEARCH (android_site_load_path)
+ # define PATH_DUMPLOADSEARCH "/assets/lisp/"
+ # define PATH_DATA "/assets/etc/"
+ # define PATH_DOC "/assets/etc/"
+ # define PATH_INFO "/assets/info/"
+ # define PATH_GAME ""
+ # define PATH_BITMAPS ""
+
+extern char *android_site_load_path;
+extern char *android_lib_dir;
+
+#endif
diff --git a/src/fileio.c b/src/fileio.c
index 859cf57d249..f2f440d0a3b 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -56,6 +56,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "region-cache.h"
#include "frame.h"
+#if defined HAVE_ANDROID
+#include "android.h"
+#endif
+
#ifdef HAVE_LINUX_FS_H
# include <sys/ioctl.h>
# include <linux/fs.h>
@@ -109,6 +113,42 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "commands.h"
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
+/* Type describing a file descriptor used by functions such as
+ `insert-file-contents'. */
+
+typedef int emacs_fd;
+
+/* Function used to read and open from such a file descriptor. */
+
+#define emacs_fd_open emacs_open
+#define emacs_fd_close emacs_close
+#define emacs_fd_read emacs_read_quit
+#define emacs_fd_lseek lseek
+#define emacs_fd_fstat sys_fstat
+#define emacs_fd_valid_p(fd) ((fd) >= 0)
+
+/* This is not used on MS Windows. */
+
+#ifndef WINDOWSNT
+#define emacs_fd_to_int(fds) (fds)
+#endif /* WINDOWSNT */
+
+#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+typedef struct android_fd_or_asset emacs_fd;
+
+#define emacs_fd_open android_open_asset
+#define emacs_fd_close android_close_asset
+#define emacs_fd_read android_asset_read_quit
+#define emacs_fd_lseek android_asset_lseek
+#define emacs_fd_fstat android_asset_fstat
+#define emacs_fd_valid_p(fd) ((fd).asset != ((void *) -1))
+#define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd)
+
+#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+
/* True during writing of auto-save files. */
static bool auto_saving;
@@ -135,13 +175,44 @@ static dev_t timestamp_file_system;
static Lisp_Object Vwrite_region_annotation_buffers;
static Lisp_Object emacs_readlinkat (int, char const *);
-static Lisp_Object file_name_directory (Lisp_Object);
static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
Lisp_Object *, struct coding_system *);
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.
+
+ If WRITE, then don't check that the file lies on `/content' on
+ Android. This special exception allows writing to content
+ provider-supplied files. */
+
+static void
+check_mutable_filename (Lisp_Object encoded, bool write)
+{
+#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);
+
+ if (write)
+ return;
+
+ if (!strcmp (SSDATA (encoded), "/content")
+ || !strncmp (SSDATA (encoded), "/content/",
+ sizeof "/content/" - 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. */
@@ -160,7 +231,7 @@ file_access_p (char const *file, int amode)
}
#endif
- if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
+ if (sys_faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
return true;
#ifdef CYGWIN
@@ -264,11 +335,20 @@ close_file_unwind (int fd)
emacs_close (fd);
}
+static void
+close_file_unwind_emacs_fd (void *ptr)
+{
+ emacs_fd *fd;
+
+ fd = ptr;
+ emacs_fd_close (*fd);
+}
+
void
fclose_unwind (void *arg)
{
FILE *stream = arg;
- fclose (stream);
+ emacs_fclose (stream);
}
/* Restore point, having saved it as a marker. */
@@ -370,7 +450,7 @@ Given a Unix syntax file name, returns a string ending in slash. */)
/* Return the directory component of FILENAME, or nil if FILENAME does
not contain a directory component. */
-static Lisp_Object
+Lisp_Object
file_name_directory (Lisp_Object filename)
{
char *beg = SSDATA (filename);
@@ -889,6 +969,10 @@ user_homedir (char const *name)
p[length] = 0;
struct passwd *pw = getpwnam (p);
SAFE_FREE ();
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (pw && !pw->pw_dir && pw->pw_uid == getuid ())
+ return (char *) android_get_home_directory ();
+#endif
if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
return NULL;
return pw->pw_dir;
@@ -1879,6 +1963,11 @@ get_homedir (void)
pw = getpwuid (getuid ());
if (pw)
home = pw->pw_dir;
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!home && pw && pw->pw_uid == getuid ())
+ return android_get_home_directory ();
+#endif
if (!home)
return "";
}
@@ -2177,7 +2266,8 @@ permissions. */)
#else
bool already_exists = false;
mode_t new_mask;
- int ifd, ofd;
+ emacs_fd ifd;
+ int ofd;
struct stat st;
#endif
@@ -2197,6 +2287,7 @@ permissions. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_newname, true);
#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
@@ -2220,22 +2311,24 @@ permissions. */)
report_file_error ("Copying permissions to", newname);
}
#else /* not WINDOWSNT */
- ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
+ ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
- if (ifd < 0)
+ if (!emacs_fd_valid_p (ifd))
report_file_error ("Opening input file", file);
- record_unwind_protect_int (close_file_unwind, ifd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd);
- if (fstat (ifd, &st) != 0)
+ if (emacs_fd_fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ())
+ if (is_selinux_enabled ()
+ && emacs_fd_to_int (ifd) != -1)
{
- conlength = fgetfilecon (ifd, &con);
+ conlength = fgetfilecon (emacs_fd_to_int (ifd),
+ &con);
if (conlength == -1)
report_file_error ("Doing fgetfilecon", file);
}
@@ -2273,7 +2366,7 @@ permissions. */)
if (already_exists)
{
struct stat out_st;
- if (fstat (ofd, &out_st) != 0)
+ if (sys_fstat (ofd, &out_st) != 0)
report_file_error ("Output file status", newname);
if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
report_file_errno ("Input and output files are the same",
@@ -2284,7 +2377,8 @@ permissions. */)
maybe_quit ();
- if (clone_file (ofd, ifd))
+ if (emacs_fd_to_int (ifd) != -1
+ && clone_file (ofd, emacs_fd_to_int (ifd)))
newsize = st.st_size;
else
{
@@ -2292,30 +2386,38 @@ permissions. */)
ssize_t copied;
#ifndef MSDOS
- for (newsize = 0; newsize < insize; newsize += copied)
+ newsize = 0;
+
+ if (emacs_fd_to_int (ifd) != -1)
{
- /* Copy at most COPY_MAX bytes at a time; this is min
- (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
- surely aligned well. */
- ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
- ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
- off_t intail = insize - newsize;
- ptrdiff_t len = min (intail, copy_max);
- copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0);
- if (copied <= 0)
- break;
- maybe_quit ();
+ for (; newsize < insize; newsize += copied)
+ {
+ /* Copy at most COPY_MAX bytes at a time; this is min
+ (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
+ surely aligned well. */
+ ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
+ ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
+ off_t intail = insize - newsize;
+ ptrdiff_t len = min (intail, copy_max);
+ copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
+ ofd, NULL, len, 0);
+ if (copied <= 0)
+ break;
+ maybe_quit ();
+ }
}
#endif /* MSDOS */
/* Fall back on read+write if copy_file_range failed, or if the
- input is empty and so could be a /proc file. read+write will
- either succeed, or report an error more precisely than
- copy_file_range would. */
+ input is empty and so could be a /proc file, or if ifd is an
+ invention of android.c. read+write will either succeed, or
+ report an error more precisely than copy_file_range
+ would. */
if (newsize != insize || insize == 0)
{
char buf[MAX_ALLOCA];
- for (; (copied = emacs_read_quit (ifd, buf, sizeof buf));
+
+ for (; (copied = emacs_fd_read (ifd, buf, sizeof buf));
newsize += copied)
{
if (copied < 0)
@@ -2363,8 +2465,10 @@ permissions. */)
}
}
- switch (!NILP (preserve_permissions)
- ? qcopy_acl (SSDATA (encoded_file), ifd,
+ switch ((!NILP (preserve_permissions)
+ && emacs_fd_to_int (ifd) != -1)
+ ? qcopy_acl (SSDATA (encoded_file),
+ emacs_fd_to_int (ifd),
SSDATA (encoded_newname), ofd,
preserved_permissions)
: (already_exists
@@ -2396,7 +2500,17 @@ permissions. */)
struct timespec ts[2];
ts[0] = get_stat_atime (&st);
ts[1] = get_stat_mtime (&st);
- if (futimens (ofd, ts) != 0)
+ if (futimens (ofd, ts) != 0
+ /* Various versions of the Android C library are missing
+ futimens, which leads a gnulib fallback to be installed
+ that uses fdutimens instead. However, fdutimens is not
+ supported on many Android kernels, so just silently fail
+ if errno is ENOTSUP or ENOSYS. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ && errno != ENOTSUP
+ && errno != ENOSYS
+#endif
+ )
xsignal2 (Qfile_date_error,
build_string ("Cannot set file date"), newname);
}
@@ -2404,7 +2518,9 @@ permissions. */)
if (emacs_close (ofd) < 0)
report_file_error ("Write error", newname);
- emacs_close (ifd);
+ /* Note that ifd is not closed twice because unwind_protects are
+ discarded at the end of this function. */
+ emacs_fd_close (ifd);
#ifdef MSDOS
/* In DJGPP v2.0 and later, fstat usually returns true file mode bits,
@@ -2456,6 +2572,8 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal,
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);
+ check_mutable_filename (encoded_dir, false);
+
if (rmdir (dir) != 0)
report_file_error ("Removing directory", directory);
@@ -2495,6 +2613,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, false);
if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT)
report_file_error ("Removing old name", filename);
@@ -2652,6 +2771,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, false);
+ check_mutable_filename (encoded_newname, false);
bool plain_rename = (case_only_rename
|| (!NILP (ok_if_already_exists)
@@ -2763,6 +2884,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, false);
+ check_mutable_filename (encoded_newname, false);
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
return Qnil;
@@ -2816,6 +2939,8 @@ This happens for interactive use with M-x. */)
encoded_target = ENCODE_FILE (target);
encoded_linkname = ENCODE_FILE (linkname);
+ check_mutable_filename (encoded_target, false);
+ check_mutable_filename (encoded_linkname, false);
if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
return Qnil;
@@ -2971,7 +3096,8 @@ If there is no error, returns nil. */)
encoded_filename = ENCODE_FILE (absname);
- if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
+ if (sys_faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK,
+ AT_EACCESS) != 0)
report_file_error (SSDATA (string), filename);
return Qnil;
@@ -3073,12 +3199,13 @@ file_directory_p (Lisp_Object file)
{
#ifdef DOS_NT
/* This is cheaper than 'stat'. */
- bool retval = faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
+ bool retval = sys_faccessat (AT_FDCWD, SSDATA (file),
+ D_OK, AT_EACCESS) == 0;
if (!retval && errno == EACCES)
errno = ENOTDIR; /* like the non-DOS_NT branch below does */
return retval;
#else
-# ifdef O_PATH
+# if defined O_PATH && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
/* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */
int fd = emacs_openat (AT_FDCWD, SSDATA (file),
O_PATH | O_CLOEXEC | O_DIRECTORY, 0);
@@ -3187,7 +3314,11 @@ file_accessible_directory_p (Lisp_Object file)
There are three exceptions: "", "/", and "//". Leave "" alone,
as it's invalid. Append only "." to the other two exceptions as
"/" and "//" are distinct on some platforms, whereas "/", "///",
- "////", etc. are all equivalent. */
+ "////", etc. are all equivalent.
+
+ Android has a special directory named "/assets". There is no "."
+ directory there, but appending a "/" is sufficient to check
+ whether or not it is a directory. */
if (! len)
dir = data;
else
@@ -3197,11 +3328,27 @@ file_accessible_directory_p (Lisp_Object file)
special cases "/" and "//", and it's a safe optimization
here. After appending '.', append another '/' to work around
a macOS bug (Bug#30350). */
- static char const appended[] = "/./";
- char *buf = SAFE_ALLOCA (len + sizeof appended);
- memcpy (buf, data, len);
- strcpy (buf + len, &appended[data[len - 1] == '/']);
- dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!strncmp ("/assets/", data,
+ sizeof "/assets" - 1))
+ {
+ static char const appended[] = "/";
+ char *buf = SAFE_ALLOCA (len + sizeof appended);
+ memcpy (buf, data, len);
+ strcpy (buf + len, &appended[data[len - 1] == '/']);
+ dir = buf;
+ }
+ else
+ {
+#endif
+ static char const appended[] = "/./";
+ char *buf = SAFE_ALLOCA (len + sizeof appended);
+ memcpy (buf, data, len);
+ strcpy (buf + len, &appended[data[len - 1] == '/']);
+ dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ }
+#endif
}
ok = file_access_p (dir, F_OK);
@@ -3521,6 +3668,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,
@@ -3532,7 +3681,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, false);
+ char *fname = SSDATA (encoded);
mode_t imode = XFIXNUM (mode) & 07777;
if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
report_file_error ("Doing chmod", absname);
@@ -3604,6 +3755,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, false);
if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0)
{
@@ -3636,6 +3788,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);
@@ -3652,8 +3805,10 @@ 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);
+
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
{
@@ -3742,7 +3897,7 @@ union read_non_regular
{
struct
{
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted, trytry;
} s;
GCALIGNED_UNION_MEMBER
@@ -3753,10 +3908,10 @@ static Lisp_Object
read_non_regular (Lisp_Object state)
{
union read_non_regular *data = XFIXNUMPTR (state);
- int nbytes = emacs_read_quit (data->s.fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + data->s.inserted),
- data->s.trytry);
+ int nbytes = emacs_fd_read (data->s.fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + data->s.inserted),
+ data->s.trytry);
return make_fixnum (nbytes);
}
@@ -3905,7 +4060,7 @@ by calling `format-decode', which see. */)
{
struct stat st;
struct timespec mtime;
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted = 0;
int unprocessed;
specpdl_ref count = SPECPDL_INDEX ();
@@ -3983,8 +4138,8 @@ by calling `format-decode', which see. */)
orig_filename = filename;
filename = ENCODE_FILE (filename);
- fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
- if (fd < 0)
+ fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
+ if (!emacs_fd_valid_p (fd))
{
save_errno = errno;
if (NILP (visit))
@@ -4002,7 +4157,7 @@ by calling `format-decode', which see. */)
}
specpdl_ref fd_index = SPECPDL_INDEX ();
- record_unwind_protect_int (close_file_unwind, fd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);
/* Replacement should preserve point as it preserves markers. */
if (!NILP (replace))
@@ -4012,7 +4167,7 @@ by calling `format-decode', which see. */)
XCAR (XCAR (window_markers)));
}
- if (fstat (fd, &st) != 0)
+ if (emacs_fd_fstat (fd, &st) != 0)
report_file_error ("Input file status", orig_filename);
mtime = get_stat_mtime (&st);
@@ -4033,7 +4188,7 @@ by calling `format-decode', which see. */)
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
- seekable = lseek (fd, 0, SEEK_CUR) < 0;
+ seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0;
if (!NILP (beg) && !seekable)
xsignal2 (Qfile_error,
build_string ("cannot use a start position in a non-seekable file/device"),
@@ -4112,17 +4267,17 @@ by calling `format-decode', which see. */)
int nread;
if (st.st_size <= (1024 * 4))
- nread = emacs_read_quit (fd, read_buf, 1024 * 4);
+ nread = emacs_fd_read (fd, read_buf, 1024 * 4);
else
{
- nread = emacs_read_quit (fd, read_buf, 1024);
+ nread = emacs_fd_read (fd, read_buf, 1024);
if (nread == 1024)
{
int ntail;
- if (lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
+ if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
report_file_error ("Setting file position",
orig_filename);
- ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3);
+ ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3);
nread = ntail < 0 ? ntail : nread + ntail;
}
}
@@ -4163,7 +4318,7 @@ by calling `format-decode', which see. */)
specpdl_ptr--;
/* Rewind the file for the actual read done later. */
- if (lseek (fd, 0, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
}
@@ -4222,7 +4377,7 @@ by calling `format-decode', which see. */)
if (beg_offset != 0)
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
@@ -4230,7 +4385,7 @@ by calling `format-decode', which see. */)
match the text at the beginning of the buffer. */
while (true)
{
- int nread = emacs_read_quit (fd, read_buf, sizeof read_buf);
+ int nread = emacs_fd_read (fd, read_buf, sizeof read_buf);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
@@ -4265,7 +4420,7 @@ by calling `format-decode', which see. */)
there's no need to replace anything. */
if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
{
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
/* Truncate the buffer to the size of the file. */
@@ -4288,14 +4443,14 @@ by calling `format-decode', which see. */)
break;
/* How much can we scan in the next step? */
trial = min (curpos, sizeof read_buf);
- if (lseek (fd, curpos - trial, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
total_read = nread = 0;
while (total_read < trial)
{
- nread = emacs_read_quit (fd, read_buf + total_read,
- trial - total_read);
+ nread = emacs_fd_read (fd, read_buf + total_read,
+ trial - total_read);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
@@ -4415,7 +4570,7 @@ by calling `format-decode', which see. */)
/* First read the whole file, performing code conversion into
CONVERSION_BUFFER. */
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */
@@ -4426,8 +4581,8 @@ by calling `format-decode', which see. */)
/* Read at most READ_BUF_SIZE bytes at a time, to allow
quitting while reading a huge file. */
- this = emacs_read_quit (fd, read_buf + unprocessed,
- READ_BUF_SIZE - unprocessed);
+ this = emacs_fd_read (fd, read_buf + unprocessed,
+ READ_BUF_SIZE - unprocessed);
if (this <= 0)
break;
@@ -4442,7 +4597,7 @@ by calling `format-decode', which see. */)
if (this < 0)
report_file_error ("Read error", orig_filename);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (unprocessed > 0)
@@ -4589,7 +4744,7 @@ by calling `format-decode', which see. */)
if (beg_offset != 0 || !NILP (replace))
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
@@ -4642,10 +4797,10 @@ by calling `format-decode', which see. */)
/* Allow quitting out of the actual I/O. We don't make text
part of the buffer until all the reading is done, so a C-g
here doesn't do any harm. */
- this = emacs_read_quit (fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + inserted),
- trytry);
+ this = emacs_fd_read (fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + inserted),
+ trytry);
}
if (this <= 0)
@@ -4672,7 +4827,7 @@ by calling `format-decode', which see. */)
else
Fset (Qdeactivate_mark, Qt);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (read_quit < 0)
@@ -4851,8 +5006,10 @@ by calling `format-decode', which see. */)
}
}
- /* Decode file format. */
- if (inserted > 0)
+ /* Decode file format. Don't do this if Qformat_decode is not
+ bound, which can happen when called early during loadup. */
+
+ if (inserted > 0 && !NILP (Ffboundp (Qformat_decode)))
{
/* Don't run point motion or modification hooks when decoding. */
specpdl_ref count1 = SPECPDL_INDEX ();
@@ -5297,6 +5454,8 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
}
encoded_filename = ENCODE_FILE (filename);
+ check_mutable_filename (encoded_filename, false);
+
fn = SSDATA (encoded_filename);
open_flags = O_WRONLY | O_CREAT;
open_flags |= EQ (mustbenew, Qexcl) ? O_EXCL : !NILP (append) ? 0 : O_TRUNC;
@@ -5383,7 +5542,7 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
modtime = invalid_timespec ();
if (visiting)
{
- if (fstat (desc, &st) == 0)
+ if (sys_fstat (desc, &st) == 0)
modtime = get_stat_mtime (&st);
else
ok = 0, save_errno = errno;
@@ -5421,7 +5580,7 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
if (desc1 >= 0)
{
struct stat st1;
- if (fstat (desc1, &st1) == 0
+ if (sys_fstat (desc1, &st1) == 0
&& st.st_dev == st1.st_dev && st.st_ino == st1.st_ino)
{
/* Use the heuristic if it appears to be valid. With neither
@@ -5798,7 +5957,6 @@ See Info node `(elisp)Modification Time' for more details. */)
return call2 (handler, Qverify_visited_file_modtime, buf);
filename = ENCODE_FILE (BVAR (b, filename));
-
mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0
? get_stat_mtime (&st)
: time_error_value (errno));
@@ -5858,7 +6016,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;
@@ -5871,7 +6029,9 @@ 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);
+
+ if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st, 0)
== 0)
{
current_buffer->modtime = get_stat_mtime (&st);
@@ -5944,7 +6104,7 @@ do_auto_save_unwind (void *arg)
if (stream != NULL)
{
block_input ();
- fclose (stream);
+ emacs_fclose (stream);
unblock_input ();
}
}
@@ -6270,6 +6430,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
@@ -6284,6 +6449,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
@@ -6305,6 +6472,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);
@@ -6312,6 +6484,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 be9f8f488d9..be551fc876f 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
@@ -653,8 +659,31 @@ lock_if_free (lock_info_type *clasher, Lisp_Object lfname)
static Lisp_Object
make_lock_file_name (Lisp_Object fn)
{
- Lisp_Object lock_file_name = call1 (Qmake_lock_file_name,
- Fexpand_file_name (fn, Qnil));
+ Lisp_Object lock_file_name;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ char *name;
+#endif
+
+ fn = Fexpand_file_name (fn, Qnil);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Files in /assets and /contents can't have lock files on Android
+ as these directories are fabrications of android.c, and backed by
+ read only data. */
+
+ name = SSDATA (fn);
+
+ if (strcmp (name, "/assets")
+ || strcmp (name, "/assets/")
+ || strcmp (name, "/content")
+ || strcmp (name, "/content/")
+ || strncmp (name, "/assets/", sizeof "/assets")
+ || strncmp (name, "/content/", sizeof "/content"))
+ return Qnil;
+#endif
+
+ lock_file_name = call1 (Qmake_lock_file_name, fn);
+
return !NILP (lock_file_name) ? ENCODE_FILE (lock_file_name) : Qnil;
}
diff --git a/src/fns.c b/src/fns.c
index bfd19e8c8f2..1ad6eb57de4 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3555,6 +3555,10 @@ The data read from the system are decoded using `locale-coding-system'. */)
(Lisp_Object item)
{
char *str = NULL;
+
+ /* STR is apparently unused on Android. */
+ ((void) str);
+
#ifdef HAVE_LANGINFO_CODESET
if (EQ (item, Qcodeset))
{
diff --git a/src/font.c b/src/font.c
index e586277a5d3..9dcafb3bb33 100644
--- a/src/font.c
+++ b/src/font.c
@@ -177,9 +177,35 @@ font_make_entity (void)
allocate_pseudovector (VECSIZE (struct font_entity),
FONT_ENTITY_MAX, FONT_ENTITY_MAX, PVEC_FONT));
XSETFONT (font_entity, entity);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ entity->is_android = false;
+#endif
+
+ return font_entity;
+}
+
+#ifdef HAVE_ANDROID
+
+Lisp_Object
+font_make_entity_android (int size)
+{
+ Lisp_Object font_entity;
+ struct font_entity *entity
+ = ((struct font_entity *)
+ allocate_pseudovector (size, FONT_ENTITY_MAX, FONT_ENTITY_MAX,
+ PVEC_FONT));
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ entity->is_android = true;
+#endif
+
+ XSETFONT (font_entity, entity);
return font_entity;
}
+#endif
+
/* Create a font-object whose structure size is SIZE. If ENTITY is
not nil, copy properties from ENTITY to the font-object. If
PIXELSIZE is positive, set the `size' property to PIXELSIZE. */
diff --git a/src/font.h b/src/font.h
index 492c2e58b09..ed3b17db994 100644
--- a/src/font.h
+++ b/src/font.h
@@ -260,6 +260,11 @@ struct font_entity
{
union vectorlike_header header;
Lisp_Object props[FONT_ENTITY_MAX];
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Whether or not this is an Android font entity. */
+ bool is_android;
+#endif
};
/* A value which may appear in the member `encoding' of struct font
@@ -547,8 +552,14 @@ CHECK_FONT_GET_OBJECT (Lisp_Object x)
return XFONT_OBJECT (x);
}
+#ifndef HAVE_ANDROID
/* Number of pt per inch (from the TeXbook). */
#define PT_PER_INCH 72.27
+#else
+/* Android uses this value instead to compensate for different device
+ dimensions. */
+#define PT_PER_INCH 160.00
+#endif
/* Return a pixel size (integer) corresponding to POINT size (double)
on resolution DPI. */
@@ -823,6 +834,9 @@ extern Lisp_Object copy_font_spec (Lisp_Object);
extern Lisp_Object merge_font_spec (Lisp_Object, Lisp_Object);
extern Lisp_Object font_make_entity (void);
+#ifdef HAVE_ANDROID
+extern Lisp_Object font_make_entity_android (int);
+#endif
extern Lisp_Object font_make_object (int, Lisp_Object, int);
#if defined (HAVE_XFT) || defined (HAVE_FREETYPE) || defined (HAVE_NS)
extern Lisp_Object font_build_object (int, Lisp_Object, Lisp_Object, double);
diff --git a/src/fontset.c b/src/fontset.c
index c0e00cfa346..139dcb6eb89 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -667,8 +667,35 @@ fontset_find_font (Lisp_Object fontset, int c, struct face *face,
}
font_object = font_open_for_lface (f, font_entity, face->lface,
FONT_DEF_SPEC (font_def));
+
+ /* If the font registry is not the same as explicitly
+ specified in the font spec, do not cache the font.
+ TrueType fonts have contrived character map selection
+ semantics which makes determining the repertory at font
+ spec matching time unduly expensive. */
+
+ {
+ Lisp_Object spec;
+
+ spec = FONT_DEF_SPEC (font_def);
+
+ if (!NILP (font_object)
+ && !NILP (AREF (spec, FONT_REGISTRY_INDEX))
+ && !NILP (AREF (font_object, FONT_REGISTRY_INDEX))
+ && !EQ (AREF (spec, FONT_REGISTRY_INDEX),
+ AREF (font_object, FONT_REGISTRY_INDEX))
+ /* See sfntfont_registries_compatible_p in
+ sfntfont.c. */
+ && !(EQ (AREF (spec, FONT_REGISTRY_INDEX),
+ Qiso8859_1)
+ && EQ (AREF (font_object, FONT_REGISTRY_INDEX),
+ Qiso10646_1)))
+ goto strangeness;
+ }
+
if (NILP (font_object))
{
+ strangeness:
/* Something strange happened, perhaps because of a
Font-backend problem. To avoid crashing, record
that this spec is unusable. It may be better to find
diff --git a/src/frame.c b/src/frame.c
index 037914ac9dc..04ffdd5494d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -228,6 +228,7 @@ Value is:
`pc' for a direct-write MS-DOS frame,
`pgtk' for an Emacs frame running on pure GTK.
`haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android.
See also `frame-live-p'. */)
(Lisp_Object object)
{
@@ -250,6 +251,8 @@ See also `frame-live-p'. */)
return Qpgtk;
case output_haiku:
return Qhaiku;
+ case output_android:
+ return Qandroid;
default:
emacs_abort ();
}
@@ -279,6 +282,7 @@ The value is a symbol:
`pc' for a direct-write MS-DOS frame.
`pgtk' for an Emacs frame using pure GTK facilities.
`haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android/
FRAME defaults to the currently selected frame.
@@ -993,6 +997,16 @@ make_frame (bool mini_p)
f->select_mini_window_flag = false;
/* This one should never be zero. */
f->change_stamp = 1;
+
+#ifdef HAVE_TEXT_CONVERSION
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ f->conversion.compose_region_overlay = Qnil;
+ f->conversion.batch_edit_count = 0;
+ f->conversion.batch_edit_flags = 0;
+ f->conversion.actions = NULL;
+#endif
+
root_window = make_window ();
rw = XWINDOW (root_window);
if (mini_p)
@@ -1228,6 +1242,7 @@ make_initial_frame (void)
return f;
}
+#ifndef HAVE_ANDROID
static struct frame *
make_terminal_frame (struct terminal *terminal)
@@ -1317,6 +1332,8 @@ get_future_frame_param (Lisp_Object parameter,
return result;
}
+#endif
+
DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
1, 1, 0,
doc: /* Create an additional terminal frame, possibly on another terminal.
@@ -1336,6 +1353,10 @@ Note that changing the size of one terminal frame automatically
affects all frames on the same terminal device. */)
(Lisp_Object parms)
{
+#ifdef HAVE_ANDROID
+ error ("Text terminals are not supported on this platform");
+ return Qnil;
+#else
struct frame *f;
struct terminal *t = NULL;
Lisp_Object frame;
@@ -1436,6 +1457,7 @@ affects all frames on the same terminal device. */)
f->after_make_frame = true;
return frame;
+#endif
}
@@ -2252,6 +2274,13 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
f->terminal = 0; /* Now the frame is dead. */
unblock_input ();
+ /* Clear markers and overlays set by F on behalf of an input
+ method. */
+#ifdef HAVE_TEXT_CONVERSION
+ if (FRAME_WINDOW_P (f))
+ reset_frame_state (f);
+#endif
+
/* If needed, delete the terminal that this frame was on.
(This must be done after the frame is killed.) */
terminal->reference_count--;
@@ -5303,16 +5332,23 @@ gui_display_get_resource (Display_Info *dpyinfo, Lisp_Object attribute,
*nz++ = '.';
lispstpcpy (nz, attribute);
- const char *value =
- dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
- name_key,
- class_key);
- SAFE_FREE();
+#ifndef HAVE_ANDROID
+ const char *value
+ = dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
+ name_key,
+ class_key);
+
+ SAFE_FREE ();
if (value && *value)
return build_string (value);
else
return Qnil;
+#else
+
+ SAFE_FREE ();
+ return Qnil;
+#endif
}
@@ -5647,6 +5683,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
@@ -6119,8 +6157,11 @@ make_monitor_attribute_list (struct MonitorInfo *monitors,
mi->work.width, mi->work.height);
geometry = list4i (mi->geom.x, mi->geom.y,
mi->geom.width, mi->geom.height);
- attributes = Fcons (Fcons (Qsource, build_string (source)),
- attributes);
+
+ if (source)
+ attributes = Fcons (Fcons (Qsource, build_string (source)),
+ attributes);
+
attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
attributes);
#ifdef HAVE_PGTK
@@ -6218,6 +6259,7 @@ syms_of_frame (void)
DEFSYM (Qns, "ns");
DEFSYM (Qpgtk, "pgtk");
DEFSYM (Qhaiku, "haiku");
+ DEFSYM (Qandroid, "android");
DEFSYM (Qvisible, "visible");
DEFSYM (Qbuffer_predicate, "buffer-predicate");
DEFSYM (Qbuffer_list, "buffer-list");
@@ -6624,7 +6666,7 @@ implicitly when there's no window system support.
Note that when a frame is not large enough to accommodate a change of
any of the parameters listed above, Emacs may try to enlarge the frame
even if this option is non-nil. */);
-#if defined (HAVE_WINDOW_SYSTEM)
+#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_ANDROID)
#if defined (USE_GTK) || defined (HAVE_NS)
frame_inhibit_implied_resize = list1 (Qtab_bar_lines);
#else
diff --git a/src/frame.h b/src/frame.h
index b95b94c7685..e2900d1c15b 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -76,6 +76,64 @@ enum ns_appearance_type
#endif
#endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+
+enum text_conversion_operation
+ {
+ TEXTCONV_START_BATCH_EDIT,
+ TEXTCONV_END_BATCH_EDIT,
+ TEXTCONV_COMMIT_TEXT,
+ TEXTCONV_FINISH_COMPOSING_TEXT,
+ TEXTCONV_SET_COMPOSING_TEXT,
+ TEXTCONV_SET_COMPOSING_REGION,
+ TEXTCONV_SET_POINT_AND_MARK,
+ TEXTCONV_DELETE_SURROUNDING_TEXT,
+ TEXTCONV_REQUEST_POINT_UPDATE,
+ };
+
+/* Structure describing a single edit being performed by the input
+ method that should be executed in the context of
+ kbd_buffer_get_event. */
+
+struct text_conversion_action
+{
+ /* The next text conversion action. */
+ struct text_conversion_action *next;
+
+ /* Any associated data. */
+ Lisp_Object data;
+
+ /* The operation being performed. */
+ enum text_conversion_operation operation;
+
+ /* Counter value. */
+ unsigned long counter;
+};
+
+/* Structure describing the text conversion state associated with a
+ frame. */
+
+struct text_conversion_state
+{
+ /* List of text conversion actions associated with this frame. */
+ struct text_conversion_action *actions;
+
+ /* Markers representing the composing region. */
+ Lisp_Object compose_region_start, compose_region_end;
+
+ /* Overlay representing the composing region. */
+ Lisp_Object compose_region_overlay;
+
+ /* The number of ongoing ``batch edits'' that are causing point
+ reporting to be delayed. */
+ int batch_edit_count;
+
+ /* Mask containing what must be updated after batch edits end. */
+ int batch_edit_flags;
+};
+
+#endif
+
/* The structure representing a frame. */
struct frame
@@ -181,7 +239,7 @@ struct frame
most recently buried buffer is first. For last-buffer. */
Lisp_Object buried_buffer_list;
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* A dummy window used to display menu bars under X when no X
toolkit support is available. */
Lisp_Object menu_bar_window;
@@ -377,7 +435,7 @@ struct frame
/* The output method says how the contents of this frame are
displayed. It could be using termcap, or using an X window.
This must be the same as the terminal->type. */
- ENUM_BF (output_method) output_method : 3;
+ ENUM_BF (output_method) output_method : 4;
#ifdef HAVE_WINDOW_SYSTEM
/* True if this frame is a tooltip frame. */
@@ -586,20 +644,22 @@ struct frame
well. */
union output_data
{
- struct tty_output *tty; /* From termchar.h. */
- struct x_output *x; /* From xterm.h. */
- struct w32_output *w32; /* From w32term.h. */
- struct ns_output *ns; /* From nsterm.h. */
- struct pgtk_output *pgtk; /* From pgtkterm.h. */
- struct haiku_output *haiku; /* From haikuterm.h. */
+ struct tty_output *tty; /* From termchar.h. */
+ struct x_output *x; /* From xterm.h. */
+ struct w32_output *w32; /* From w32term.h. */
+ struct ns_output *ns; /* From nsterm.h. */
+ struct pgtk_output *pgtk; /* From pgtkterm.h. */
+ struct haiku_output *haiku; /* From haikuterm.h. */
+ struct android_output *android; /* From androidterm.h. */
}
output_data;
/* List of font-drivers available on the frame. */
struct font_driver_list *font_driver_list;
-#if defined (HAVE_X_WINDOWS)
- /* Used by x_wait_for_event when watching for an X event on this frame. */
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+ /* Used by x_wait_for_event when watching for an X event on this
+ frame. */
int wait_event_type;
#endif
@@ -662,6 +722,11 @@ struct frame
enum ns_appearance_type ns_appearance;
bool_bf ns_transparent_titlebar;
#endif
+
+#ifdef HAVE_TEXT_CONVERSION
+ /* Text conversion state used by certain input methods. */
+ struct text_conversion_state conversion;
+#endif
} GCALIGNED_STRUCT;
/* Most code should use these functions to set Lisp fields in struct frame. */
@@ -713,7 +778,7 @@ fset_menu_bar_vector (struct frame *f, Lisp_Object val)
{
f->menu_bar_vector = val;
}
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
INLINE void
fset_menu_bar_window (struct frame *f, Lisp_Object val)
{
@@ -872,6 +937,11 @@ default_pixels_per_inch_y (void)
#else
#define FRAME_HAIKU_P(f) ((f)->output_method == output_haiku)
#endif
+#ifndef HAVE_ANDROID
+#define FRAME_ANDROID_P(f) false
+#else
+#define FRAME_ANDROID_P(f) ((f)->output_method == output_android)
+#endif
/* FRAME_WINDOW_P tests whether the frame is a graphical window system
frame. */
@@ -890,6 +960,9 @@ default_pixels_per_inch_y (void)
#ifdef HAVE_HAIKU
#define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
#endif
+#ifdef HAVE_ANDROID
+#define FRAME_WINDOW_P(f) FRAME_ANDROID_P (f)
+#endif
#ifndef FRAME_WINDOW_P
#define FRAME_WINDOW_P(f) ((void) (f), false)
#endif
@@ -917,11 +990,17 @@ default_pixels_per_inch_y (void)
frame F. We need to define two versions because a TTY-only build
does not have FRAME_DISPLAY_INFO. */
#ifdef HAVE_WINDOW_SYSTEM
+#ifndef HAVE_ANDROID
# define MOUSE_HL_INFO(F) \
- (FRAME_WINDOW_P(F) \
+ (FRAME_WINDOW_P (F) \
? &FRAME_DISPLAY_INFO(F)->mouse_highlight \
: &(F)->output_data.tty->display_info->mouse_highlight)
#else
+/* There is no "struct tty_output" on Android at all. */
+# define MOUSE_HL_INFO(F) \
+ (&FRAME_DISPLAY_INFO(F)->mouse_highlight)
+#endif
+#else
# define MOUSE_HL_INFO(F) \
(&(F)->output_data.tty->display_info->mouse_highlight)
#endif
diff --git a/src/fringe.c b/src/fringe.c
index ed257c073b9..3452c8503f0 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -1422,25 +1422,30 @@ If BITMAP overrides a standard fringe bitmap, the original bitmap is restored.
On X, we bit-swap the built-in bitmaps and reduce bitmap
from short to char array if width is <= 8 bits.
+ The Android port tries to follow X as closely as possible, so do
+ that there too.
+
On MAC with big-endian CPU, we need to byte-swap each short.
On W32 and MAC (little endian), there's no need to do this.
*/
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
-static const unsigned char swap_nibble[16] = {
- 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
- 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
- 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
- 0x3, 0xb, 0x7, 0xf}; /* 0011 1011 0111 1111 */
-#endif /* HAVE_X_WINDOWS */
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_ANDROID)
+static const unsigned char swap_nibble[16] =
+ {
+ 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
+ 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
+ 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
+ 0x3, 0xb, 0x7, 0xf, /* 0011 1011 0111 1111 */
+ };
+#endif
static void
init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
{
if (once_p || fb->dynamic)
{
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
unsigned short *bits = fb->bits;
int j;
@@ -1488,7 +1493,7 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
}
}
#endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
unsigned short *bits = fb->bits;
diff --git a/src/image.c b/src/image.c
index 87a0c1ca497..689ff882728 100644
--- a/src/image.c
+++ b/src/image.c
@@ -175,6 +175,31 @@ typedef struct haiku_bitmap_record Bitmap_Record;
#endif
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+
+typedef struct android_bitmap_record Bitmap_Record;
+
+typedef struct android_image XImage;
+typedef android_pixmap Pixmap;
+
+#define GET_PIXEL(ximg, x, y) android_get_pixel (ximg, x, y)
+#define PUT_PIXEL(ximg, x, y, pixel) android_put_pixel (ximg, x, y, pixel)
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN 0
+#define PIX_MASK_DRAW 1
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+#define RED16_FROM_ULONG(color) (RED_FROM_ULONG (color) * 0x101)
+#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG (color) * 0x101)
+#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG (color) * 0x101)
+
+#endif
+
static void image_disable_image (struct frame *, struct image *);
static void image_edge_detection (struct frame *, struct image *, Lisp_Object,
Lisp_Object);
@@ -486,6 +511,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return -1;
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_pixmap bitmap;
+
+ bitmap = android_create_bitmap_from_data (bits, width, height);
+
+ if (!bitmap)
+ return -1;
+#elif defined HAVE_ANDROID
+ ((void) dpyinfo);
+ emacs_abort ();
+#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
#ifdef HAVE_NTGUI
Lisp_Object frame UNINIT; /* The value is not used. */
Emacs_Pixmap bitmap;
@@ -598,14 +635,16 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
dpyinfo->bitmaps[id - 1].width = width;
dpyinfo->bitmaps[id - 1].refcount = 1;
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+#endif /* ANDROID_STUBIFY */
dpyinfo->bitmaps[id - 1].have_mask = false;
dpyinfo->bitmaps[id - 1].depth = 1;
#ifdef USE_CAIRO
dpyinfo->bitmaps[id - 1].stipple = NULL;
#endif /* USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#ifdef HAVE_NTGUI
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
@@ -616,9 +655,19 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return id;
}
-#if defined HAVE_HAIKU || defined HAVE_NS
-static char *slurp_file (int, ptrdiff_t *);
-static Lisp_Object image_find_image_fd (Lisp_Object, int *);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+
+/* This abstraction allows directly loading images from assets without
+ copying them to a file descriptor first. */
+typedef struct android_fd_or_asset image_fd;
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+typedef int image_fd;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_ANDROID
+static char *slurp_file (image_fd, ptrdiff_t *);
+static Lisp_Object image_find_image_fd (Lisp_Object, image_fd *);
static bool xbm_read_bitmap_data (struct frame *, char *, char *,
int *, int *, char **, bool);
#endif
@@ -724,7 +773,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
/* Search bitmap-file-path for the file, if appropriate. */
if (openp (Vx_bitmap_file_path, file, Qnil, &found,
- make_fixnum (R_OK), false, false)
+ make_fixnum (R_OK), false, false, NULL)
< 0)
return -1;
@@ -773,7 +822,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
/* Search bitmap-file-path for the file, if appropriate. */
if (openp (Vx_bitmap_file_path, file, Qnil, &found,
- make_fixnum (R_OK), false, false)
+ make_fixnum (R_OK), false, false, NULL)
< 0)
return -1;
@@ -834,6 +883,73 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
xfree (contents);
return id;
#endif
+
+#ifdef HAVE_ANDROID
+#ifdef ANDROID_STUBIFY
+ ((void) dpyinfo);
+
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+#else
+ ptrdiff_t id, size;
+ int width, height, rc;
+ image_fd fd;
+ char *contents, *data;
+ Lisp_Object found;
+ android_pixmap bitmap;
+
+ /* Look for an existing bitmap with the same name. */
+ for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+ {
+ if (dpyinfo->bitmaps[id].refcount
+ && dpyinfo->bitmaps[id].file
+ && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+ {
+ ++dpyinfo->bitmaps[id].refcount;
+ return id + 1;
+ }
+ }
+
+ /* Search bitmap-file-path for the file, if appropriate. */
+ if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+ make_fixnum (R_OK), false, false, NULL)
+ < 0)
+ return -1;
+
+ if (!STRINGP (image_find_image_fd (file, &fd))
+ && !STRINGP (image_find_image_fd (found, &fd)))
+ return -1;
+
+ contents = slurp_file (fd, &size);
+
+ if (!contents)
+ return -1;
+
+ rc = xbm_read_bitmap_data (f, contents, contents + size,
+ &width, &height, &data, 0);
+
+ if (!rc)
+ {
+ xfree (contents);
+ return -1;
+ }
+
+ xfree (contents);
+ bitmap = android_create_bitmap_from_data (data, width, height);
+ xfree (data);
+
+ id = image_allocate_bitmap_record (f);
+ dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+ dpyinfo->bitmaps[id - 1].have_mask = false;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].height = height;
+ dpyinfo->bitmaps[id - 1].width = width;
+
+ return id;
+#endif
+#endif
}
/* Free bitmap B. */
@@ -851,6 +967,13 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
#endif /* USE_CAIRO */
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_free_pixmap (bm->pixmap);
+
+ if (bm->have_mask)
+ android_free_pixmap (bm->pixmap);
+#endif
+
#ifdef HAVE_NTGUI
DeleteObject (bm->pixmap);
#endif /* HAVE_NTGUI */
@@ -938,7 +1061,7 @@ static void image_unget_x_image (struct image *, bool, Emacs_Pix_Container);
image_unget_x_image (img, mask_p, ximg)
#endif
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
#ifndef USE_CAIRO
static void image_sync_to_pixmaps (struct frame *, struct image *);
@@ -952,6 +1075,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, int, int, int,
XImage **, Pixmap *);
static void x_destroy_x_image (XImage *);
+#if defined HAVE_X_WINDOWS
+
/* Create a mask of a bitmap. Note is this not a perfect mask.
It's nicer with some borders in this context */
@@ -1048,7 +1173,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
x_destroy_x_image (mask_img);
}
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
/***********************************************************************
Image types
@@ -1082,7 +1209,7 @@ struct image_type
#if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \
defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \
defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \
- defined HAVE_WEBP
+ defined HAVE_WEBP || defined HAVE_ANDROID
# ifdef WINDOWSNT
# define IMAGE_TYPE_INIT(f) f
# else
@@ -1584,7 +1711,7 @@ prepare_image_for_display (struct frame *f, struct image *img)
}
unblock_input ();
}
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!img->load_failed_p)
{
block_input ();
@@ -1791,7 +1918,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
/* NOTE (HAVE_NS): background color is NOT an indexed color! */
img->background_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->ximg)
{
image_destroy_x_image (img->ximg);
@@ -1809,7 +1936,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
img->mask = NO_PIXMAP;
img->background_transparent_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->mask_img)
{
image_destroy_x_image (img->mask_img);
@@ -2181,11 +2308,11 @@ image_size_in_bytes (struct image *img)
if (msk)
size += msk->height * msk->bytes_per_line;
-#elif defined HAVE_X_WINDOWS
- /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask,
- to avoid having to query the server. */
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+ /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp
+ for mask, to avoid having to query the server. */
if (img->pixmap != NO_PIXMAP)
- size += img->width * img->height * 3;
+ size += img->width * img->height * 4;
if (img->mask != NO_PIXMAP)
size += img->width * img->height / 8;
@@ -2520,11 +2647,11 @@ compute_image_size (double width, double height,
finally move the origin back to the top left of the image, which
may now be a different corner.
- Note that different GUI backends (X, Cairo, w32, NS, Haiku) want
- the transform matrix defined as transform from the original image
- to the transformed image, while others want the matrix to describe
- the transform of the space, which boils down to inverting the
- matrix.
+ Note that different GUI backends (X, Cairo, w32, NS, Haiku,
+ Android) want the transform matrix defined as transform from the
+ original image to the transformed image, while others want the
+ matrix to describe the transform of the space, which boils down to
+ inverting the matrix.
It's possible to pre-calculate the matrix multiplications and just
generate one transform matrix that will do everything we need in a
@@ -2566,6 +2693,96 @@ compute_image_rotation (struct image *img, double *rotation)
*rotation = XFIXNUM (reduced_angle);
}
+#ifdef HAVE_ANDROID
+
+static void
+matrix_identity (matrix3x3 matrix)
+{
+ memset (matrix, 0, sizeof (matrix3x3));
+
+ matrix[0][0] = 1.0;
+ matrix[1][1] = 1.0;
+ matrix[2][2] = 1.0;
+}
+
+/* Translate the matrix TRANSFORM to X, Y, and then perform clockwise
+ rotation by the given angle THETA in radians and translate back.
+ As the transform is being performed in a coordinate system where Y
+ grows downwards, the given angle describes a clockwise
+ rotation. */
+
+static void
+matrix_rotate (matrix3x3 transform, double theta, double x, double y)
+{
+ matrix3x3 temp, copy;
+
+ /* 1. Translate the matrix so X and Y are in the center. */
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ temp[0][2] = x;
+ temp[1][2] = y;
+
+ matrix3x3_mult (copy, temp, transform);
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* 2. Rotate the matrix counter-clockwise, assuming a coordinate
+ system where Y grows downwards. */
+
+ temp[0][0] = cos (theta);
+ temp[0][1] = -sin (theta);
+ temp[1][0] = sinf (theta);
+ temp[1][1] = cosf (theta);
+
+ matrix3x3_mult (copy, temp, transform);
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* 3. Translate back. */
+
+ temp[0][2] = -x;
+ temp[1][2] = -y;
+
+ matrix3x3_mult (copy, temp, transform);
+}
+
+/* Scale the matrix TRANSFORM by -1, and then apply a TX of width, in
+ effect flipping the image horizontally. */
+
+static void
+matrix_mirror_horizontal (matrix3x3 transform, double width)
+{
+ matrix3x3 temp, copy;
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ temp[0][0] = -1.0f;
+ temp[0][2] = width;
+
+ matrix3x3_mult (copy, temp, transform);
+}
+
+static void
+matrix_translate (matrix3x3 transform, float tx, float ty)
+{
+ matrix3x3 temp, copy;
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* Set the tx and ty. */
+ temp[0][2] = tx;
+ temp[1][2] = ty;
+
+ /* Multiply it with the transform. */
+ matrix3x3_mult (copy, temp, transform);
+}
+
+#endif
+
static void
image_set_transform (struct frame *f, struct image *img)
{
@@ -2585,6 +2802,14 @@ image_set_transform (struct frame *f, struct image *img)
memcpy (&img->transform, identity, sizeof identity);
#endif
+#if defined HAVE_ANDROID
+ matrix3x3 identity = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 },
+ };
+#endif
+
# if (defined HAVE_IMAGEMAGICK \
&& !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE)
/* ImageMagick images already have the correct transform. */
@@ -2622,7 +2847,8 @@ image_set_transform (struct frame *f, struct image *img)
/* Determine flipping. */
flip = !NILP (image_spec_value (img->spec, QCflip, NULL));
-# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
/* We want scale up operations to use a nearest neighbor filter to
show real pixels instead of munging them, but scale down
operations to use a blended filter, to avoid aliasing and the like.
@@ -2644,7 +2870,7 @@ image_set_transform (struct frame *f, struct image *img)
matrix3x3 matrix
= {
-# if defined USE_CAIRO || defined HAVE_XRENDER
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
[0][0] = (!IEEE_FLOATING_POINT && width == 0 ? DBL_MAX
: img->width / (double) width),
[1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX
@@ -2667,7 +2893,7 @@ image_set_transform (struct frame *f, struct image *img)
/* Haiku needs this, since the transformation is done on the basis
of the view, and not the image. */
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
int extra_tx, extra_ty;
extra_tx = 0;
@@ -2678,8 +2904,9 @@ image_set_transform (struct frame *f, struct image *img)
rotate_flag = 0;
else
{
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
- || defined HAVE_NTGUI || defined HAVE_NS \
+#ifndef HAVE_ANDROID
+# if (defined USE_CAIRO || defined HAVE_XRENDER \
+ || defined HAVE_NTGUI || defined HAVE_NS \
|| defined HAVE_HAIKU)
int cos_r, sin_r;
if (rotation == 0)
@@ -2706,7 +2933,7 @@ image_set_transform (struct frame *f, struct image *img)
sin_r = 1;
rotate_flag = 1;
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
if (!flip)
extra_ty = height;
extra_tx = 0;
@@ -2742,7 +2969,7 @@ image_set_transform (struct frame *f, struct image *img)
if (0 < rotate_flag)
{
-# if defined USE_CAIRO || defined HAVE_XRENDER
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
/* 1. Translate so (0, 0) is in the center of the image. */
matrix3x3 t
= { [0][0] = 1,
@@ -2793,6 +3020,93 @@ image_set_transform (struct frame *f, struct image *img)
img->height = height;
}
# endif
+#else
+ /* Calculate the inverse transform from the destination to the
+ source. The matrix is currently identity with scale
+ applied.
+
+ This code makes more sense to me than what lies above. But
+ I'm not touching what works. */
+
+ if (rotation != 0 && rotation != 90
+ && rotation != 180 && rotation != 270)
+ {
+ rotate_flag = 0;
+ goto bail;
+ }
+
+ rotate_flag = 1;
+
+ switch ((int) rotation + (flip ? 1 : 0))
+ {
+ case 0:
+ break;
+
+ case 90:
+ /* Rotate the image 90 degrees clockwise. IOW, rotate the
+ destination by 90 degrees counterclockwise, which is 270
+ degrees clockwise. */
+ matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+ matrix_translate (matrix, -height, 0);
+ break;
+
+ case 180:
+ /* Apply clockwise 180 degree rotation around the
+ center. */
+ matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+ break;
+
+ case 270:
+ /* Apply 270 degree counterclockwise rotation to the
+ destination, which is 90 degrees clockwise. */
+ matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+ matrix_translate (matrix, 0, -width);
+ break;
+
+ case 1:
+ /* Flipped. Apply horizontal flip. */
+ matrix_mirror_horizontal (matrix, width);
+ break;
+
+ case 91:
+ /* Apply a flip but otherwise treat this the same as 90. */
+ matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+ matrix_translate (matrix, -height, 0);
+ matrix_mirror_horizontal (matrix, height);
+ break;
+
+ case 181:
+ /* Flipped 180 degrees. Apply a flip and treat this the
+ same as 180. */
+ matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+ matrix_mirror_horizontal (matrix, width);
+ break;
+
+ case 271:
+ /* Flipped 270 degrees. Apply a flip and treat this the
+ same as 270. */
+ matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+ matrix_translate (matrix, 0, -width);
+ matrix_mirror_horizontal (matrix, height);
+ break;
+ }
+
+ /* Now set img->width and img->height. Flip them if the
+ rotation being applied requires so. */
+
+ if (rotation != 270 && rotation != 90)
+ {
+ img->width = width;
+ img->height = height;
+ }
+ else
+ {
+ img->height = width;
+ img->width = height;
+ }
+ bail:
+ ;
+#endif
}
if (rotate_flag < 0)
@@ -2857,6 +3171,103 @@ image_set_transform (struct frame *f, struct image *img)
img->transform[0][2] = extra_tx;
img->transform[1][2] = extra_ty;
}
+# elif defined HAVE_ANDROID
+ /* Create a new image of the right size, then turn it into a pixmap
+ and set that as img->pixmap. Destroy img->mask for now (this is
+ not right.) */
+
+ struct android_image *transformed_image, *image;
+ struct android_transform transform;
+
+ /* If there is no transform, simply return. */
+ if (!memcmp (&matrix, &identity, sizeof matrix))
+ return;
+
+ /* First, get the source image. */
+ image = image_get_x_image (f, img, false);
+
+ /* Make the transformed image. */
+ transformed_image = android_create_image (image->depth,
+ ANDROID_Z_PIXMAP,
+ NULL, img->width,
+ img->height);
+
+ /* Allocate memory for that image. */
+ transformed_image->data
+ = xmalloc (transformed_image->bytes_per_line
+ * transformed_image->height);
+
+ /* Do the transform. */
+ transform.m1 = matrix[0][0];
+ transform.m2 = matrix[0][1];
+ transform.m3 = matrix[0][2];
+ transform.m4 = matrix[1][0];
+ transform.m5 = matrix[1][1];
+ transform.m6 = matrix[1][2];
+
+ if (image->depth == 24 && smoothing)
+ android_project_image_bilinear (image, transformed_image,
+ &transform);
+ else
+ android_project_image_nearest (image, transformed_image,
+ &transform);
+
+ image_unget_x_image (img, false, image);
+
+ /* Now replace the image. */
+
+ if (img->ximg)
+ image_destroy_x_image (img->ximg);
+
+ img->ximg = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+ /* Then replace the pixmap. */
+ android_free_pixmap (img->pixmap);
+
+ /* In case android_create_pixmap signals. */
+ img->pixmap = ANDROID_NONE;
+ img->pixmap = android_create_pixmap (img->width, img->height,
+ transformed_image->depth);
+ android_put_image (img->pixmap, transformed_image);
+#else
+ emacs_abort ();
+#endif
+
+ /* Now, transform the mask. The mask should be depth 1, and is
+ always transformed using a nearest neighbor filter. */
+
+ if (img->mask_img || img->mask)
+ {
+ image = image_get_x_image (f, img, true);
+ transformed_image = android_create_image (1, ANDROID_Z_PIXMAP,
+ NULL, img->width,
+ img->height);
+ transformed_image->data
+ = xmalloc (transformed_image->bytes_per_line
+ * transformed_image->height);
+ android_project_image_nearest (image, transformed_image,
+ &transform);
+ image_unget_x_image (img, true, image);
+
+ /* Now replace the image. */
+
+ if (img->mask_img)
+ image_destroy_x_image (img->mask_img);
+
+ img->mask_img = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+ if (img->mask)
+ android_free_pixmap (img->mask);
+
+ img->mask = ANDROID_NONE;
+ img->mask = android_create_pixmap (img->width, img->height, 1);
+ android_put_image (img->mask, transformed_image);
+#endif
+ }
+
+ /* Done! */
#endif
}
@@ -3164,9 +3575,12 @@ mark_image_cache (struct image_cache *c)
/***********************************************************************
X / NS / W32 support code
+ Most of this code is shared with Android to make
+ it easier to maintain.
***********************************************************************/
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+
static bool
x_check_image_size (XImage *ximg, int width, int height)
{
@@ -3182,7 +3596,11 @@ x_check_image_size (XImage *ximg, int width, int height)
int bitmap_pad, depth, bytes_per_line;
if (ximg)
{
+#ifndef HAVE_ANDROID
bitmap_pad = ximg->bitmap_pad;
+#else
+ bitmap_pad = (ximg->depth == 1 ? 8 : 32);
+#endif
depth = ximg->depth;
bytes_per_line = ximg->bytes_per_line;
}
@@ -3200,16 +3618,23 @@ static bool
x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
XImage **ximg, Pixmap *pixmap)
{
+#ifndef HAVE_ANDROID
Display *display = FRAME_X_DISPLAY (f);
Drawable drawable = FRAME_X_DRAWABLE (f);
+#endif
eassert (input_blocked_p ());
if (depth <= 0)
depth = FRAME_DISPLAY_INFO (f)->n_planes;
+#ifndef HAVE_ANDROID
*ximg = XCreateImage (display, FRAME_X_VISUAL (f),
depth, ZPixmap, 0, NULL, width, height,
depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+#else
+ *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width,
+ height);
+#endif
if (*ximg == NULL)
{
image_error ("Unable to allocate X image");
@@ -3229,7 +3654,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
(*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
/* Allocate a pixmap of the same size. */
+#ifndef HAVE_ANDROID
*pixmap = XCreatePixmap (display, drawable, width, height, depth);
+#else
+#ifndef ANDROID_STUBIFY
+ *pixmap = android_create_pixmap (width, height, depth);
+#else
+ emacs_abort ();
+#endif
+#endif
if (*pixmap == NO_PIXMAP)
{
x_destroy_x_image (*ximg);
@@ -3250,7 +3683,11 @@ x_destroy_x_image (XImage *ximg)
ximg->data = NULL;
}
+#ifndef HAVE_ANDROID
XDestroyImage (ximg);
+#else
+ android_destroy_image (ximg);
+#endif
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -3317,7 +3754,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth)
static bool
image_check_image_size (Emacs_Pix_Container ximg, int width, int height)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
return x_check_image_size (ximg, width, height);
#else
/* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases.
@@ -3355,7 +3792,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
*pimg = *pixmap;
return 1;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
return 0;
# ifdef HAVE_XRENDER
@@ -3501,7 +3938,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
static void
image_destroy_x_image (Emacs_Pix_Container pimg)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
x_destroy_x_image (pimg);
#else
eassert (input_blocked_p ());
@@ -3540,7 +3977,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0,
pimg->width, pimg->height);
XFreeGC (FRAME_X_DISPLAY (f), gc);
-#endif /* HAVE_X_WINDOWS */
+#elif defined HAVE_ANDROID
+ android_put_image (pixmap, pimg);
+#endif
#ifdef HAVE_NS
eassert (pimg == pixmap);
@@ -3577,7 +4016,7 @@ static void
image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
bool mask_p)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (!mask_p)
{
eassert (img->ximg == NULL);
@@ -3595,7 +4034,7 @@ image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
#endif
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
/* Put the X images recorded in IMG on frame F into pixmaps, then free
the X images and their buffers. */
@@ -3651,7 +4090,7 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p)
{
#if defined USE_CAIRO || defined (HAVE_HAIKU)
return !mask_p ? img->pixmap : img->mask;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
@@ -3661,9 +4100,15 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p)
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->original_width, img->original_height, ~0, ZPixmap);
#endif
+#ifndef HAVE_ANDROID
else
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+ else
+ return android_get_image (!mask_p ? img->pixmap : img->mask,
+ ANDROID_Z_PIXMAP);
+#endif
#elif defined (HAVE_NS)
Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask;
@@ -3676,13 +4121,18 @@ static void
image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
{
#ifdef USE_CAIRO
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
eassert (ximg == ximg_in_img);
+#ifdef HAVE_ANDROID
+ else
+ android_destroy_image (ximg);
+#else
else
XDestroyImage (ximg);
+#endif
#elif defined (HAVE_NS)
ns_release_object (ximg);
#endif
@@ -3701,10 +4151,11 @@ image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
PFD is null, do not open the file. */
static Lisp_Object
-image_find_image_fd (Lisp_Object file, int *pfd)
+image_find_image_fd (Lisp_Object file, image_fd *pfd)
{
Lisp_Object file_found, search_path;
int fd;
+ void *platform;
/* TODO I think this should use something like image-load-path
instead. Unfortunately, that can contain non-string elements. */
@@ -3713,8 +4164,10 @@ image_find_image_fd (Lisp_Object file, int *pfd)
Vx_bitmap_file_path);
/* Try to find FILE in data-directory/images, then x-bitmap-file-path. */
+ platform = NULL;
fd = openp (search_path, file, Qnil, &file_found,
- pfd ? Qt : make_fixnum (R_OK), false, false);
+ pfd ? Qt : make_fixnum (R_OK), false, false,
+ pfd ? &platform : NULL);
if (fd == -2)
{
/* The file exists locally, but has a file name handler.
@@ -3724,10 +4177,23 @@ image_find_image_fd (Lisp_Object file, int *pfd)
Lisp_Object encoded_name = ENCODE_FILE (file_found);
fd = emacs_open (SSDATA (encoded_name), O_RDONLY, 0);
}
- else if (fd < 0)
+ /* FD is -3 if PLATFORM is set to a valid asset file descriptor on
+ Android. */
+ else if (fd < 0 && fd != -3)
return Qnil;
+
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
if (pfd)
*pfd = fd;
+#else
+ /* Construct an asset file descriptor. */
+
+ if (pfd)
+ {
+ pfd->fd = fd;
+ pfd->asset = platform;
+ }
+#endif
return file_found;
}
@@ -3741,14 +4207,25 @@ image_find_image_file (Lisp_Object file)
return image_find_image_fd (file, 0);
}
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+static void
+close_android_fd (void *ptr)
+{
+ android_close_asset (*(struct android_fd_or_asset *) ptr);
+}
+
+#endif
+
/* Read FILE into memory. Value is a pointer to a buffer allocated
with xmalloc holding FILE's contents. Value is null if an error
occurred. FD is a file descriptor open for reading FILE. Set
*SIZE to the size of the file. */
static char *
-slurp_file (int fd, ptrdiff_t *size)
+slurp_file (image_fd fd, ptrdiff_t *size)
{
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
FILE *fp = fdopen (fd, "rb");
char *buf = NULL;
@@ -3759,7 +4236,7 @@ slurp_file (int fd, ptrdiff_t *size)
specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_ptr (fclose_unwind, fp);
- if (fstat (fileno (fp), &st) == 0
+ if (sys_fstat (fileno (fp), &st) == 0
&& 0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))
{
/* Report an error if we read past the purported EOF.
@@ -3777,6 +4254,39 @@ slurp_file (int fd, ptrdiff_t *size)
unbind_to (count, Qnil);
}
+#else
+ char *buf;
+ struct stat st;
+ specpdl_ref count;
+
+ if (!android_asset_fstat (fd, &st)
+ && (0 <= st.st_size
+ && st.st_size < min (PTRDIFF_MAX, SIZE_MAX)))
+ {
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (close_android_fd, &fd);
+ buf = xmalloc (st.st_size + 1);
+
+ /* Read one byte past the end of the file. That allows
+ detecting if the file grows as it is being read. */
+
+ if (android_asset_read (fd, buf,
+ st.st_size + 1) == st.st_size)
+ *size = st.st_size;
+ else
+ {
+ xfree (buf);
+ buf = NULL;
+ }
+
+ unbind_to (count, Qnil);
+ }
+ else
+ {
+ buf = NULL;
+ android_close_asset (fd);
+ }
+#endif
return buf;
}
@@ -4205,6 +4715,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
img->picture = x_create_xrender_picture (f, img->pixmap, 0);
# endif
+#elif defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+ img->pixmap
+ = android_create_pixmap_from_bitmap_data (data, img->width, img->height,
+ fg, bg,
+ FRAME_DISPLAY_INFO (f)->n_planes);
+#else
+ emacs_abort ();
+#endif
#elif defined HAVE_NTGUI
img->pixmap
= w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@@ -4490,7 +5009,7 @@ xbm_load (struct frame *f, struct image *img)
file_name = image_spec_value (img->spec, QCfile, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -4635,7 +5154,8 @@ xbm_load (struct frame *f, struct image *img)
XPM images
***********************************************************************/
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
static bool xpm_image_p (Lisp_Object object);
static bool xpm_load (struct frame *f, struct image *img);
@@ -4665,7 +5185,8 @@ static bool xpm_load (struct frame *f, struct image *img);
#endif /* not HAVE_NTGUI */
#endif /* HAVE_XPM */
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS \
+ || defined HAVE_HAIKU || defined HAVE_ANDROID
/* Indices of image specification fields in xpm_format, below. */
@@ -4685,7 +5206,8 @@ enum xpm_keyword_index
XPM_LAST
};
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Vector of image_keyword structures describing the format
of valid XPM image specifications. */
@@ -4927,7 +5449,8 @@ init_xpm_functions (void)
#endif /* WINDOWSNT */
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Value is true if COLOR_SYMBOLS is a valid color symbols list
for XPM images. Such a list must consist of conses whose car and
cdr are strings. */
@@ -4963,9 +5486,9 @@ xpm_image_p (Lisp_Object object)
&& (! fmt[XPM_COLOR_SYMBOLS].count
|| xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
}
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
#if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
ptrdiff_t
@@ -5338,10 +5861,12 @@ xpm_load (struct frame *f, struct image *img)
#if (defined USE_CAIRO && defined HAVE_XPM) \
|| (defined HAVE_NS && !defined HAVE_XPM) \
|| (defined HAVE_HAIKU && !defined HAVE_XPM) \
- || (defined HAVE_PGTK && !defined HAVE_XPM)
+ || (defined HAVE_PGTK && !defined HAVE_XPM) \
+ || (defined HAVE_ANDROID && !defined HAVE_XPM)
-/* XPM support functions for NS and Haiku where libxpm is not available, and for
- Cairo. Only XPM version 3 (without any extensions) is supported. */
+/* XPM support functions for NS, Haiku and Android where libxpm is not
+ available, and for Cairo. Only XPM version 3 (without any
+ extensions) is supported. */
static void xpm_put_color_table_v (Lisp_Object, const char *,
int, Lisp_Object);
@@ -5780,7 +6305,7 @@ xpm_load (struct frame *f,
file_name = image_spec_value (img->spec, QCfile, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -6077,7 +6602,8 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
{
#ifdef HAVE_NTGUI
return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
#else
xsignal1 (Qfile_error,
@@ -6150,7 +6676,8 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
p = colors;
for (y = 0; y < img->height; ++y)
{
-#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU \
+ && !defined HAVE_ANDROID
Emacs_Color *row = p;
for (x = 0; x < img->width; ++x, ++p)
p->pixel = GET_PIXEL (ximg, x, y);
@@ -6158,7 +6685,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
{
FRAME_TERMINAL (f)->query_colors (f, row, img->width);
}
-#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
for (x = 0; x < img->width; ++x, ++p)
{
p->pixel = GET_PIXEL (ximg, x, y);
@@ -6169,7 +6696,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
p->blue = BLUE16_FROM_ULONG (p->pixel);
}
}
-#endif /* USE_CAIRO || HAVE_NS */
+#endif /* USE_CAIRO || HAVE_NS || HAVE_ANDROID */
}
image_unget_x_image_or_dc (img, 0, ximg, prev);
@@ -6234,7 +6761,11 @@ image_from_emacs_colors (struct frame *f, struct image *img, Emacs_Color *colors
Emacs_Pix_Container ximage;
Emacs_Color *p;
+#ifndef HAVE_ANDROID
ximage = NULL;
+#else
+ ximage = 0;
+#endif
init_color_table ();
@@ -6396,7 +6927,9 @@ image_edge_detection (struct frame *f, struct image *img,
}
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
+
static void
image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
int x, int y, unsigned int width, unsigned int height,
@@ -6432,8 +6965,21 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
XFreeGC (dpy, gc);
#elif HAVE_HAIKU
be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
+#elif HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+ struct android_gc *gc;
+
+ gc = android_create_gc (0, NULL);
+ android_set_foreground (gc, color);
+ android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1);
+ android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y);
+ android_free_gc (gc);
+#else
+ emacs_abort ();
+#endif
#endif
}
+
#endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
/* Transform image IMG on frame F so that it looks disabled. */
@@ -6477,7 +7023,7 @@ image_disable_image (struct frame *f, struct image *img)
#ifndef HAVE_NTGUI
#ifndef HAVE_NS /* TODO: NS support, however this not needed for toolbars */
-#if !defined USE_CAIRO && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
#define CrossForeground(f) BLACK_PIX_DEFAULT (f)
#define MaskForeground(f) WHITE_PIX_DEFAULT (f)
#else /* USE_CAIRO || HAVE_HAIKU */
@@ -6788,7 +7334,7 @@ pbm_load (struct frame *f, struct image *img)
if (STRINGP (specified_file))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (specified_file, &fd);
if (!STRINGP (file))
{
@@ -7456,8 +8002,11 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
if (NILP (specified_data))
{
int fd;
- Lisp_Object file = image_find_image_fd (specified_file, &fd);
- if (!STRINGP (file))
+ Lisp_Object file = image_find_image_file (specified_file);
+
+ if (!STRINGP (file)
+ || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+ O_RDONLY, 0)) < 0)
{
image_error ("Cannot find image file `%s'", specified_file);
return 0;
@@ -7475,7 +8024,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
if (fread (sig, 1, sizeof sig, fp) != sizeof sig
|| png_sig_cmp (sig, 0, sizeof sig))
{
- fclose (fp);
+ emacs_fclose (fp);
image_error ("Not a PNG file: `%s'", file);
return 0;
}
@@ -7529,7 +8078,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
}
if (! png_ptr)
{
- if (fp) fclose (fp);
+ if (fp) emacs_fclose (fp);
return 0;
}
@@ -7543,7 +8092,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
xfree (c->pixels);
xfree (c->rows);
if (c->fp)
- fclose (c->fp);
+ emacs_fclose (c->fp);
return 0;
}
@@ -7668,7 +8217,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
png_read_end (png_ptr, info_ptr);
if (fp)
{
- fclose (fp);
+ emacs_fclose (fp);
c->fp = NULL;
}
@@ -8184,8 +8733,10 @@ jpeg_load_body (struct frame *f, struct image *img,
if (NILP (specified_data))
{
int fd;
- Lisp_Object file = image_find_image_fd (specified_file, &fd);
- if (!STRINGP (file))
+ Lisp_Object file = image_find_image_file (specified_file);
+ if (!STRINGP (file)
+ || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+ O_RDONLY, 0)) < 0)
{
image_error ("Cannot find image file `%s'", specified_file);
return 0;
@@ -8231,7 +8782,7 @@ jpeg_load_body (struct frame *f, struct image *img,
/* Close the input file and destroy the JPEG object. */
if (fp)
- fclose (fp);
+ emacs_fclose (fp);
jpeg_destroy_decompress (&mgr->cinfo);
/* If we already have an XImage, free that. */
@@ -8326,7 +8877,7 @@ jpeg_load_body (struct frame *f, struct image *img,
jpeg_finish_decompress (&mgr->cinfo);
jpeg_destroy_decompress (&mgr->cinfo);
if (fp)
- fclose (fp);
+ emacs_fclose (fp);
/* Maybe fill in the background field while we have ximg handy. */
if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
@@ -9100,11 +9651,16 @@ gif_load (struct frame *f, struct image *img)
/* Get the file size so that we can report it in
`image-cache-size'. */
- struct stat st;
- FILE *fp = fopen (SSDATA (encoded_file), "rb");
- if (fstat (fileno (fp), &st) == 0)
- byte_size = st.st_size;
- fclose (fp);
+ {
+ struct stat st;
+ int fd;
+
+ fd = emacs_open (SSDATA (encoded_file), O_RDONLY,
+ 0);
+ if (!sys_fstat (fd, &st))
+ byte_size = st.st_size;
+ emacs_close (fd);
+ }
}
else
{
@@ -9682,7 +10238,7 @@ webp_load (struct frame *f, struct image *img)
if (NILP (specified_data))
{
- int fd;
+ image_fd fd;
file = image_find_image_fd (specified_file, &fd);
if (!STRINGP (file))
{
@@ -10365,7 +10921,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
return 0;
}
-#ifdef HAVE_MAGICKAUTOORIENTIMAGE
+#if defined HAVE_MAGICKAUTOORIENTIMAGE \
+ || HAVE_DECL_MAGICKAUTOORIENTIMAGE
/* If no :rotation is explicitly specified, apply the automatic
rotation from EXIF. */
if (NILP (image_spec_value (img->spec, QCrotation, NULL)))
@@ -10522,7 +11079,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
{
MagickWand *new_wand;
MagickSetImageBackgroundColor (image_wand, bg_wand);
-#ifdef HAVE_MAGICKMERGEIMAGELAYERS
+#if defined HAVE_MAGICKMERGEIMAGELAYERS \
+ || HAVE_DECL_MAGICKMERGEIMAGELAYERS
new_wand = MagickMergeImageLayers (image_wand, MergeLayer);
#else
new_wand = MagickFlattenImages (image_wand);
@@ -10551,8 +11109,9 @@ imagemagick_load_image (struct frame *f, struct image *img,
init_color_table ();
-#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \
- ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
+#if (defined (HAVE_MAGICKEXPORTIMAGEPIXELS) \
+ || HAVE_DECL_MAGICKEXPORTIMAGEPIXELS) \
+ && ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
if (imagemagick_render_type != 0)
{
/* Magicexportimage is normally faster than pixelpushing. This
@@ -11078,7 +11637,7 @@ svg_load (struct frame *f, struct image *img)
base_uri = image_spec_value (img->spec, QCbase_uri, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -11901,7 +12460,7 @@ The list of capabilities can include one or more of the following:
{
#ifdef HAVE_NATIVE_TRANSFORMS
# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU)
+ || defined (HAVE_HAIKU) | defined HAVE_ANDROID
return list2 (Qscale, Qrotate90);
# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
if (FRAME_DISPLAY_INFO (f)->xrender_supported_p)
@@ -12012,7 +12571,8 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
IMAGE_TYPE_INIT (init_jpeg_functions) },
#endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
{ SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
IMAGE_TYPE_INIT (init_xpm_functions) },
#endif
@@ -12174,7 +12734,8 @@ non-numeric, there is no explicit limit on the size of images. */);
add_image_type (Qxbm);
#if defined (HAVE_XPM) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+ || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
DEFSYM (Qxpm, "xpm");
add_image_type (Qxpm);
#endif
diff --git a/src/inotify.c b/src/inotify.c
index 7562ffb1701..844bf54105c 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -419,6 +419,7 @@ IN_ONESHOT */)
int wd = -1;
uint32_t imask = aspect_to_inotifymask (aspect);
uint32_t mask = imask | IN_MASK_ADD | IN_EXCL_UNLINK;
+ char *name;
CHECK_STRING (filename);
@@ -432,7 +433,23 @@ IN_ONESHOT */)
}
encoded_file_name = ENCODE_FILE (filename);
- wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
+ name = SSDATA (encoded_file_name);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* If FILENAME actually lies in a special directory, return now
+ instead of letting inotify fail. These directories cannot
+ receive file notifications as they are read only. */
+
+ if (strcmp (name, "/assets")
+ || strcmp (name, "/assets/")
+ || strcmp (name, "/content")
+ || strcmp (name, "/content/")
+ || strncmp (name, "/assets/", sizeof "/assets")
+ || strncmp (name, "/content/", sizeof "/content"))
+ return Qnil;
+#endif
+
+ wd = inotify_add_watch (inotifyfd, name, mask);
if (wd < 0)
report_file_notify_error ("Could not add watch for file", filename);
diff --git a/src/keyboard.c b/src/keyboard.c
index 14c55666768..364f26f421d 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -44,6 +44,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "atimer.h"
#include "process.h"
#include "menu.h"
+
+#ifdef HAVE_TEXT_CONVERSION
+#include "textconv.h"
+#endif
+
#include <errno.h>
#ifdef HAVE_PTHREAD
@@ -62,6 +67,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>
@@ -348,6 +357,10 @@ static struct timespec timer_last_idleness_start_time;
static Lisp_Object virtual_core_pointer_name;
static Lisp_Object virtual_core_keyboard_name;
+/* If not nil, ID of the last TOUCHSCREEN_END_EVENT to land on the
+ menu bar. */
+static Lisp_Object menu_bar_touch_id;
+
/* Global variable declarations. */
@@ -3022,6 +3035,10 @@ read_char (int commandflag, Lisp_Object map,
{
struct buffer *prev_buffer = current_buffer;
last_input_event = c;
+
+ /* All a `text-conversion' event does is prevent Emacs from
+ staying idle. It is not useful. */
+
call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt);
if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events))
@@ -3584,6 +3601,11 @@ readable_events (int flags)
return 1;
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ if (detect_conversion_events ())
+ return 1;
+#endif
+
if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ())
return 1;
if (single_kboard)
@@ -3916,6 +3938,11 @@ kbd_buffer_get_event (KBOARD **kbp,
had_pending_selection_requests = false;
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ bool had_pending_conversion_events;
+
+ had_pending_conversion_events = false;
+#endif
#ifdef subprocesses
if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4)
@@ -3980,6 +4007,13 @@ kbd_buffer_get_event (KBOARD **kbp,
break;
}
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ if (detect_conversion_events ())
+ {
+ had_pending_conversion_events = true;
+ break;
+ }
+#endif
if (end_time)
{
struct timespec now = current_timespec ();
@@ -4035,6 +4069,24 @@ kbd_buffer_get_event (KBOARD **kbp,
return first;
}
+#ifdef HAVE_TEXT_CONVERSION
+ /* There are pending text conversion operations. Text conversion
+ events should be generated before processing any other keyboard
+ input. */
+ if (had_pending_conversion_events)
+ {
+ handle_pending_conversion_events ();
+ obj = Qtext_conversion;
+
+ /* See the comment in handle_pending_conversion_events_1.
+ Note that in addition, text conversion events are not
+ generated if no edits were actually made. */
+ if (conversion_disabled_p ()
+ || NILP (Vtext_conversion_edits))
+ obj = Qnil;
+ }
+ else
+#endif
/* At this point, we know that there is a readable event available
somewhere. If the event queue is empty, then there must be a
mouse movement enabled and available. */
@@ -4919,7 +4971,74 @@ static const char *const lispy_accent_keys[] =
"dead-horn",
};
-#ifdef HAVE_NTGUI
+#ifdef HAVE_ANDROID
+#define FUNCTION_KEY_OFFSET 0
+
+const char *const lispy_function_keys[] =
+ {
+ /* All elements in this array default to 0, except for the few
+ function keys that Emacs recognizes. */
+ [111] = "escape",
+ [112] = "delete",
+ [120] = "sysrq",
+ [121] = "break",
+ [122] = "home",
+ [123] = "end",
+ [124] = "insert",
+ [126] = "media-play",
+ [127] = "media-pause",
+ [130] = "media-record",
+ [131] = "f1",
+ [132] = "f2",
+ [133] = "f3",
+ [134] = "f4",
+ [135] = "f5",
+ [136] = "f6",
+ [137] = "f7",
+ [138] = "f8",
+ [139] = "f9",
+ [140] = "f10",
+ [141] = "f11",
+ [142] = "f12",
+ [160] = "kp-ret",
+ [164] = "volume-mute",
+ [19] = "up",
+ [20] = "down",
+ [213] = "muhenkan",
+ [214] = "henkan",
+ [215] = "hiragana-katakana",
+ [218] = "kana",
+ [21] = "left",
+ [22] = "right",
+ [24] = "volume-up",
+ [259] = "help",
+ [25] = "volume-down",
+ [268] = "kp-up-left",
+ [269] = "kp-down-left",
+ [270] = "kp-up-right",
+ [271] = "kp-down-right",
+ [272] = "media-skip-forward",
+ [273] = "media-skip-backward",
+ [277] = "cut",
+ [278] = "copy",
+ [279] = "paste",
+ [28] = "clear",
+ [4] = "XF86Back",
+ [61] = "tab",
+ [66] = "return",
+ [67] = "backspace",
+ [82] = "menu",
+ [84] = "find",
+ [85] = "media-play-pause",
+ [86] = "media-stop",
+ [87] = "media-next",
+ [88] = "media-previous",
+ [89] = "media-rewind",
+ [92] = "prior",
+ [93] = "next",
+ };
+
+#elif defined HAVE_NTGUI
#define FUNCTION_KEY_OFFSET 0x0
const char *const lispy_function_keys[] =
@@ -5756,6 +5875,25 @@ coords_in_menu_bar_window (struct frame *f, int x, int y)
#endif
+/* Return whether or not the coordinates X and Y are inside the
+ tab-bar window of the given frame F. */
+
+static bool
+coords_in_tab_bar_window (struct frame *f, int x, int y)
+{
+ struct window *window;
+
+ if (!WINDOWP (f->tab_bar_window))
+ return false;
+
+ window = XWINDOW (f->tab_bar_window);
+
+ return (y >= WINDOW_TOP_EDGE_Y (window)
+ && x >= WINDOW_LEFT_EDGE_X (window)
+ && y <= WINDOW_BOTTOM_EDGE_Y (window)
+ && x <= WINDOW_RIGHT_EDGE_X (window));
+}
+
/* Given a struct input_event, build the lisp event which represents
it. If EVENT is 0, build a mouse movement event from the mouse
movement buffer, which should have a movement event in it.
@@ -6403,14 +6541,123 @@ make_lispy_event (struct input_event *event)
case TOUCHSCREEN_END_EVENT:
{
Lisp_Object x, y, id, position;
- struct frame *f = XFRAME (event->frame_or_window);
+ struct frame *f;
+ int tab_bar_item;
+ bool close;
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+ int column, row, dummy;
+#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */
+ f = XFRAME (event->frame_or_window);
id = event->arg;
x = event->x;
y = event->y;
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+ if (event->kind == TOUCHSCREEN_BEGIN_EVENT
+ && coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y)))
+ {
+ /* If the tap began in the menu bar window, then save the
+ id. */
+ menu_bar_touch_id = id;
+ return Qnil;
+ }
+ else if (event->kind == TOUCHSCREEN_END_EVENT
+ && EQ (menu_bar_touch_id, id))
+ {
+ /* This touch should activate the menu bar. Generate the
+ menu bar event. */
+ menu_bar_touch_id = Qnil;
+
+ if (f->menu_bar_window)
+ {
+ x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x),
+ XFIXNUM (y), &column, &row, NULL, NULL,
+ &dummy);
+
+ if (row >= 0 && row < FRAME_MENU_BAR_LINES (f))
+ {
+ Lisp_Object items, item;
+
+ /* Find the menu bar item under `column'. */
+ item = Qnil;
+ items = FRAME_MENU_BAR_ITEMS (f);
+ for (i = 0; i < ASIZE (items); i += 4)
+ {
+ Lisp_Object pos, string;
+ string = AREF (items, i + 1);
+ pos = AREF (items, i + 3);
+ if (NILP (string))
+ break;
+ if (column >= XFIXNUM (pos)
+ && column < XFIXNUM (pos) + SCHARS (string))
+ {
+ item = AREF (items, i);
+ break;
+ }
+ }
+
+ /* ELisp manual 2.4b says (x y) are window
+ relative but code says they are
+ frame-relative. */
+ position = list4 (event->frame_or_window,
+ Qmenu_bar,
+ Fcons (event->x, event->y),
+ INT_TO_INTEGER (event->timestamp));
+
+ return list2 (item, position);
+ }
+ }
+
+ return Qnil;
+ }
+#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */
+
position = make_lispy_position (f, x, y, event->timestamp);
+#ifdef HAVE_WINDOW_SYSTEM
+
+ /* Now check if POSITION lies on the tab bar. If so, look up
+ the corresponding tab bar item's propertized string as the
+ OBJECT. */
+
+ if (coords_in_tab_bar_window (f, XFIXNUM (event->x),
+ XFIXNUM (event->y))
+ /* `get_tab_bar_item_kbd' returns 0 if the item was
+ previously highlighted, 1 otherwise, and -1 if there is
+ no tab bar item. */
+ && get_tab_bar_item_kbd (f, XFIXNUM (event->x),
+ XFIXNUM (event->y), &tab_bar_item,
+ &close) >= 0)
+ {
+ /* First, obtain the propertized string. */
+ x = Fcopy_sequence (AREF (f->tab_bar_items,
+ (tab_bar_item
+ + TAB_BAR_ITEM_CAPTION)));
+
+ /* Next, add the key binding. */
+ AUTO_LIST2 (y, Qmenu_item, list3 (AREF (f->tab_bar_items,
+ (tab_bar_item
+ + TAB_BAR_ITEM_KEY)),
+ AREF (f->tab_bar_items,
+ (tab_bar_item
+ + TAB_BAR_ITEM_BINDING)),
+ close ? Qt : Qnil));
+
+ /* And add the new properties to the propertized string. */
+ Fadd_text_properties (make_fixnum (0),
+ make_fixnum (SCHARS (x)),
+ y, x);
+
+ /* Set the position to 0. */
+ x = Fcons (x, make_fixnum (0));
+
+ /* Finally, add the OBJECT. */
+ position = nconc2 (position, Fcons (x, Qnil));
+ }
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
return list2 (((event->kind
== TOUCHSCREEN_BEGIN_EVENT)
? Qtouchscreen_begin
@@ -9896,6 +10143,13 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
/* Gets around Microsoft compiler limitations. */
bool dummyflag = false;
+#ifdef HAVE_TEXT_CONVERSION
+ bool disabled_conversion;
+
+ /* Whether or not text conversion has already been disabled. */
+ disabled_conversion = false;
+#endif
+
struct buffer *starting_buffer;
/* List of events for which a fake prefix key has been generated. */
@@ -10045,6 +10299,43 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
echo_local_start = echo_length ();
keys_local_start = this_command_key_count;
+#ifdef HAVE_TEXT_CONVERSION
+ /* When reading a key sequence while text conversion is in
+ effect, turn it off after the first actual character read.
+ This makes input methods send actual key events instead.
+
+ Make sure only to do this once. Also, disabling text
+ conversion seems to interact badly with menus, so don't
+ disable text conversion if a menu was displayed. */
+
+ if (!disabled_conversion && t && !used_mouse_menu
+ && !disable_inhibit_text_conversion)
+ {
+ int i;
+
+ /* used_mouse_menu isn't set if a menu bar prefix key has
+ just been stored. It appears necessary to look for a
+ prefix key itself. Don't look through too many keys for
+ efficiency reasons. */
+
+ for (i = 0; i < min (t, 10); ++i)
+ {
+ if (NUMBERP (keybuf[i])
+ || (SYMBOLP (keybuf[i])
+ && EQ (Fget (keybuf[i], Qevent_kind),
+ Qfunction_key)))
+ goto disable_text_conversion;
+ }
+
+ goto replay_key;
+
+ disable_text_conversion:
+ disable_text_conversion ();
+ record_unwind_protect_void (resume_text_conversion);
+ disabled_conversion = true;
+ }
+#endif
+
replay_key:
/* These are no-ops, unless we throw away a keystroke below and
jumped back up to replay_key; in that case, these restore the
@@ -10272,7 +10563,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
if (EVENT_HAS_PARAMETERS (key))
{
Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
- if (EQ (kind, Qmouse_click))
+ if (EQ (kind, Qmouse_click) || EQ (kind, Qtouchscreen))
{
Lisp_Object window = POSN_WINDOW (EVENT_START (key));
Lisp_Object posn = POSN_POSN (EVENT_START (key));
@@ -11184,7 +11475,7 @@ This may include sensitive information such as passwords. */)
if (dribble)
{
block_input ();
- fclose (dribble);
+ emacs_fclose (dribble);
unblock_input ();
dribble = 0;
}
@@ -12083,7 +12374,9 @@ static const struct event_head head_table[] = {
{SYMBOL_INDEX (Qmake_frame_visible), SYMBOL_INDEX (Qmake_frame_visible)},
/* `select-window' should be handled just like `switch-frame'
in read_key_sequence. */
- {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)}
+ {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)},
+ /* Touchscreen events should be prefixed by the posn. */
+ {SYMBOL_INDEX (Qtouchscreen_begin), SYMBOL_INDEX (Qtouchscreen)},
};
static Lisp_Object
@@ -12429,6 +12722,9 @@ syms_of_keyboard (void)
virtual_core_keyboard_name = Qnil;
staticpro (&virtual_core_keyboard_name);
+ menu_bar_touch_id = Qnil;
+ staticpro (&menu_bar_touch_id);
+
defsubr (&Scurrent_idle_time);
defsubr (&Sevent_symbol_parse_modifiers);
defsubr (&Sevent_convert_list);
@@ -12788,6 +13084,10 @@ See also `pre-command-hook'. */);
"display-monitors-changed-functions");
DEFSYM (Qcoding, "coding");
+ DEFSYM (Qtouchscreen, "touchscreen");
+#ifdef HAVE_TEXT_CONVERSION
+ DEFSYM (Qtext_conversion, "text-conversion");
+#endif
Fset (Qecho_area_clear_hook, Qnil);
@@ -13162,9 +13462,16 @@ which see. */);
DEFVAR_LISP ("post-select-region-hook", Vpost_select_region_hook,
doc: /* Abnormal hook run after the region is selected.
This usually happens as a result of `select-active-regions'. The hook
-is called with one argument, the string that was selected. */);;
+is called with one argument, the string that was selected. */);
Vpost_select_region_hook = Qnil;
+ DEFVAR_LISP ("disable-inhibit-text-conversion",
+ disable_inhibit_text_conversion,
+ doc: /* Don't disable text conversion inside `read-key-sequence'.
+If non-nil, text conversion will continue to happen after a prefix
+key has been read inside `read-key-sequence'. */);
+ disable_inhibit_text_conversion = false;
+
pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper);
}
diff --git a/src/keyboard.h b/src/keyboard.h
index 3f86a8e03ad..26eecd48b00 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -395,8 +395,17 @@ extern void unuse_menu_items (void);
#define EVENT_HEAD(event) \
(EVENT_HAS_PARAMETERS (event) ? XCAR (event) : (event))
-/* Extract the starting and ending positions from a composite event. */
-#define EVENT_START(event) (CAR_SAFE (CDR_SAFE (event)))
+/* Extract the starting and ending positions from a composite event. */
+
+/* Unlike Lisp `event-start', this also handles touch screen events,
+ which are not actually mouse events in the general sense. */
+#define EVENT_START(event) \
+ ((EQ (EVENT_HEAD (event), Qtouchscreen_begin) \
+ || EQ (EVENT_HEAD (event), Qtouchscreen_end)) \
+ ? CDR_SAFE (CAR_SAFE (CDR_SAFE (event))) \
+ : CAR_SAFE (CDR_SAFE (event)))
+
+/* This does not handle touchscreen events. */
#define EVENT_END(event) (CAR_SAFE (CDR_SAFE (CDR_SAFE (event))))
/* Extract the click count from a multi-click event. */
diff --git a/src/lisp.h b/src/lisp.h
index 2bfcd1a1983..e8cfda1be6e 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -29,6 +29,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <float.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
#include <attribute.h>
#include <count-leading-zeros.h>
@@ -4511,7 +4516,8 @@ extern bool suffix_p (Lisp_Object, const char *);
extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object);
extern int openp (Lisp_Object, Lisp_Object, Lisp_Object,
- Lisp_Object *, Lisp_Object, bool, bool);
+ Lisp_Object *, Lisp_Object, bool, bool,
+ void **);
enum { S2N_IGNORE_TRAILING = 1 };
extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *);
extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object),
@@ -4730,6 +4736,7 @@ extern void syms_of_marker (void);
/* Defined in fileio.c. */
+extern Lisp_Object file_name_directory (Lisp_Object);
extern char *splice_dir_file (char *, char const *, char const *)
ATTRIBUTE_RETURNS_NONNULL;
extern bool file_name_absolute_p (const char *);
@@ -5069,11 +5076,18 @@ extern void init_random (void);
extern void emacs_backtrace (int);
extern AVOID emacs_abort (void) NO_INLINE;
extern int emacs_fstatat (int, char const *, void *, int);
+#ifdef HAVE_SYS_STAT_H
+extern int sys_fstat (int, struct stat *);
+#endif
+extern int sys_faccessat (int, const char *, int, int);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
extern int emacs_openat (int, char const *, int, int);
+#endif
extern int emacs_open (const char *, int, int);
extern int emacs_open_noquit (const char *, int, int);
extern int emacs_pipe (int[2]);
extern int emacs_close (int);
+extern int emacs_fclose (FILE *);
extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);
@@ -5107,7 +5121,9 @@ extern Lisp_Object directory_files_internal (Lisp_Object, Lisp_Object,
bool, Lisp_Object, Lisp_Object);
/* Defined in term.c. */
+#ifndef HAVE_ANDROID
extern int *char_ins_del_vector;
+#endif
extern void syms_of_term (void);
extern AVOID fatal (const char *msgid, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
@@ -5217,7 +5233,13 @@ extern char *emacs_root_dir (void);
#ifdef HAVE_TEXT_CONVERSION
/* Defined in textconv.c. */
+extern void reset_frame_state (struct frame *);
extern void report_selected_window_change (struct frame *);
+extern void report_point_change (struct frame *, struct window *,
+ struct buffer *);
+extern void disable_text_conversion (void);
+extern void resume_text_conversion (void);
+extern void syms_of_textconv (void);
#endif
#ifdef HAVE_NATIVE_COMP
diff --git a/src/lread.c b/src/lread.c
index d2e544afef9..3ac5d43a839 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -62,6 +62,24 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <fcntl.h>
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \
+ || (__ANDROID_API__ < 9)
+
+#define lread_fd int
+#define lread_fd_cmp(n) (fd == (n))
+#define lread_fd_p (fd >= 0)
+#define lread_close emacs_close
+#define lread_fstat fstat
+#define lread_read_quit emacs_read_quit
+#define lread_lseek lseek
+
+#define file_stream FILE *
+#define file_seek fseek
+#define file_stream_valid_p(p) (p)
+#define file_stream_close emacs_fclose
+#define file_stream_invalid NULL
+#define file_get_char getc
+
#ifdef HAVE_FSEEKO
#define file_offset off_t
#define file_tell ftello
@@ -70,6 +88,79 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#define file_tell ftell
#endif
+#else
+
+#include "android.h"
+
+/* Use an Android file descriptor under Android instead, as this
+ allows loading directly from asset files without loading each asset
+ into memory and creating a separate file descriptor every time.
+
+ Note that `struct android_fd_or_asset' as used here is different
+ from that returned from `android_open_asset'; if fd.asset is NULL,
+ then fd.fd is either a valid file descriptor or -1, meaning that
+ the file descriptor is invalid.
+
+ However, lread requires the ability to seek inside asset files,
+ which is not provided under Android 2.2. So when building for that
+ particular system, fall back to the usual file descriptor-based
+ code. */
+
+#define lread_fd struct android_fd_or_asset
+#define lread_fd_cmp(n) (!fd.asset && fd.fd == (n))
+#define lread_fd_p (fd.asset || fd.fd >= 0)
+#define lread_close android_close_asset
+#define lread_fstat android_asset_fstat
+#define lread_read_quit android_asset_read_quit
+#define lread_lseek android_asset_lseek
+
+/* The invalid file stream. */
+
+static struct android_fd_or_asset invalid_file_stream =
+ {
+ -1,
+ NULL,
+ };
+
+#define file_stream struct android_fd_or_asset
+#define file_offset off_t
+#define file_tell(n) (android_asset_lseek ((n), 0, SEEK_CUR))
+#define file_seek android_asset_lseek
+#define file_stream_valid_p(p) ((p).asset || (p).fd >= 0)
+#define file_stream_close android_close_asset
+#define file_stream_invalid invalid_file_stream
+
+/* Return a single character from the file input stream STREAM.
+ Value and errors are the same as getc. */
+
+static int
+file_get_char (file_stream stream)
+{
+ int c;
+ char byte;
+ ssize_t rc;
+
+ retry:
+ rc = android_asset_read (stream, &byte, 1);
+
+ if (rc == 0)
+ c = EOF;
+ else if (rc == -1)
+ {
+ if (errno == EINTR)
+ goto retry;
+ else
+ c = EOF;
+ }
+ else
+ c = (unsigned char) byte;
+
+ return c;
+}
+
+#define USE_ANDROID_ASSETS
+#endif
+
#if IEEE_FLOATING_POINT
# include <ieee754.h>
# ifndef INFINITY
@@ -113,7 +204,7 @@ static Lisp_Object read_objects_completed;
static struct infile
{
/* The input stream. */
- FILE *stream;
+ file_stream stream;
/* Lookahead byte count. */
signed char lookahead;
@@ -375,7 +466,7 @@ skip_dyn_bytes (Lisp_Object readcharfun, ptrdiff_t n)
if (FROM_FILE_P (readcharfun))
{
block_input (); /* FIXME: Not sure if it's needed. */
- fseek (infile->stream, n - infile->lookahead, SEEK_CUR);
+ file_seek (infile->stream, n - infile->lookahead, SEEK_CUR);
unblock_input ();
infile->lookahead = 0;
}
@@ -399,7 +490,7 @@ skip_dyn_eof (Lisp_Object readcharfun)
if (FROM_FILE_P (readcharfun))
{
block_input (); /* FIXME: Not sure if it's needed. */
- fseek (infile->stream, 0, SEEK_END);
+ file_seek (infile->stream, 0, SEEK_END);
unblock_input ();
infile->lookahead = 0;
}
@@ -480,10 +571,12 @@ readbyte_from_stdio (void)
return infile->buf[--infile->lookahead];
int c;
- FILE *instream = infile->stream;
+ file_stream instream = infile->stream;
block_input ();
+#if !defined USE_ANDROID_ASSETS
+
/* Interrupted reads have been observed while reading over the network. */
while ((c = getc (instream)) == EOF && errno == EINTR && ferror (instream))
{
@@ -493,6 +586,35 @@ readbyte_from_stdio (void)
clearerr (instream);
}
+#else
+
+ {
+ char byte;
+ ssize_t rc;
+
+ retry:
+ rc = android_asset_read (instream, &byte, 1);
+
+ if (rc == 0)
+ c = EOF;
+ else if (rc == -1)
+ {
+ if (errno == EINTR)
+ {
+ unblock_input ();
+ maybe_quit ();
+ block_input ();
+ goto retry;
+ }
+ else
+ c = EOF;
+ }
+ else
+ c = (unsigned char) byte;
+ }
+
+#endif
+
unblock_input ();
return (c == EOF ? -1 : c);
@@ -672,7 +794,11 @@ static void substitute_in_interval (INTERVAL, void *);
if the character warrants that.
If SECONDS is a number, wait that many seconds for input, and
- return Qnil if no input arrives within that time. */
+ return Qnil if no input arrives within that time.
+
+ If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII,
+ temporarily disable any input method which wants to perform
+ edits, unless `disable-inhibit-text-conversion'. */
static Lisp_Object
read_filtered_event (bool no_switch_frame, bool ascii_required,
@@ -680,12 +806,29 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
{
Lisp_Object val, delayed_switch_frame;
struct timespec end_time;
+#ifdef HAVE_TEXT_CONVERSION
+ specpdl_ref count;
+#endif
#ifdef HAVE_WINDOW_SYSTEM
if (display_hourglass_p)
cancel_hourglass ();
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ count = SPECPDL_INDEX ();
+
+ /* Don't use text conversion when trying to just read a
+ character. */
+
+ if (ascii_required && error_nonascii
+ && !disable_inhibit_text_conversion)
+ {
+ disable_text_conversion ();
+ record_unwind_protect_void (resume_text_conversion);
+ }
+#endif
+
delayed_switch_frame = Qnil;
/* Compute timeout. */
@@ -761,7 +904,11 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ return unbind_to (count, val);
+#else
return val;
+#endif
}
DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0,
@@ -1038,7 +1185,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun)
safe to load. Only files compiled with Emacs can be loaded. */
static int
-safe_to_load_version (Lisp_Object file, int fd)
+safe_to_load_version (Lisp_Object file, lread_fd fd)
{
struct stat st;
char buf[512];
@@ -1047,12 +1194,12 @@ safe_to_load_version (Lisp_Object file, int fd)
/* If the file is not regular, then we cannot safely seek it.
Assume that it is not safe to load as a compiled file. */
- if (fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
+ if (lread_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
return 0;
/* Read the first few bytes from the file, and look for a line
specifying the byte compiler version used. */
- nbytes = emacs_read_quit (fd, buf, sizeof buf);
+ nbytes = lread_read_quit (fd, buf, sizeof buf);
if (nbytes > 0)
{
/* Skip to the next newline, skipping over the initial `ELC'
@@ -1067,7 +1214,7 @@ safe_to_load_version (Lisp_Object file, int fd)
version = 0;
}
- if (lseek (fd, 0, SEEK_SET) < 0)
+ if (lread_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Seeking to start of file", file);
return version;
@@ -1141,7 +1288,7 @@ close_infile_unwind (void *arg)
{
struct infile *prev_infile = arg;
eassert (infile && infile != prev_infile);
- fclose (infile->stream);
+ file_stream_close (infile->stream);
infile = prev_infile;
}
@@ -1170,6 +1317,22 @@ loadhist_initialize (Lisp_Object filename)
specbind (Qcurrent_load_list, Fcons (filename, Qnil));
}
+#ifdef USE_ANDROID_ASSETS
+
+/* Like `close_file_unwind'. However, PTR is a pointer to an Android
+ file descriptor instead of a system file descriptor. */
+
+static void
+close_file_unwind_android_fd (void *ptr)
+{
+ struct android_fd_or_asset *fd;
+
+ fd = ptr;
+ android_close_asset (*fd);
+}
+
+#endif
+
DEFUN ("load", Fload, Sload, 1, 5, 0,
doc: /* Execute a file of Lisp code named FILE.
First try FILE with `.elc' appended, then try with `.el', then try
@@ -1218,8 +1381,12 @@ Return t if the file exists and loads successfully. */)
(Lisp_Object file, Lisp_Object noerror, Lisp_Object nomessage,
Lisp_Object nosuffix, Lisp_Object must_suffix)
{
- FILE *stream UNINIT;
- int fd;
+ file_stream stream UNINIT;
+ lread_fd fd;
+#ifdef USE_ANDROID_ASSETS
+ int rc;
+ void *asset;
+#endif
specpdl_ref fd_index UNINIT;
specpdl_ref count = SPECPDL_INDEX ();
Lisp_Object found, efound, hist_file_name;
@@ -1260,7 +1427,12 @@ Return t if the file exists and loads successfully. */)
since it would try to load a directory as a Lisp file. */
if (SCHARS (file) == 0)
{
+#if !defined USE_ANDROID_ASSETS
fd = -1;
+#else
+ fd.asset = NULL;
+ fd.fd = -1;
+#endif
errno = ENOENT;
}
else
@@ -1299,12 +1471,22 @@ Return t if the file exists and loads successfully. */)
suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
}
- fd =
- openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer,
- no_native);
+#if !defined USE_ANDROID_ASSETS
+ fd = openp (Vload_path, file, suffixes, &found, Qnil,
+ load_prefer_newer, no_native, NULL);
+#else
+ asset = NULL;
+ rc = openp (Vload_path, file, suffixes, &found, Qnil,
+ load_prefer_newer, no_native, &asset);
+ fd.fd = rc;
+ fd.asset = asset;
+
+ /* fd.asset will be non-NULL if this is actually an asset
+ file. */
+#endif
}
- if (fd == -1)
+ if (lread_fd_cmp (-1))
{
if (NILP (noerror))
report_file_error ("Cannot open load file", file);
@@ -1316,7 +1498,7 @@ Return t if the file exists and loads successfully. */)
Vuser_init_file = found;
/* If FD is -2, that means openp found a magic file. */
- if (fd == -2)
+ if (lread_fd_cmp (-2))
{
if (NILP (Fequal (found, file)))
/* If FOUND is a different file name from FILE,
@@ -1345,11 +1527,21 @@ Return t if the file exists and loads successfully. */)
#endif
}
+#if !defined USE_ANDROID_ASSETS
if (0 <= fd)
{
fd_index = SPECPDL_INDEX ();
record_unwind_protect_int (close_file_unwind, fd);
}
+#else
+ if (fd.asset || fd.fd >= 0)
+ {
+ /* Use a different kind of unwind_protect here. */
+ fd_index = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (close_file_unwind_android_fd,
+ &fd);
+ }
+#endif
#ifdef HAVE_MODULES
bool is_module =
@@ -1415,11 +1607,12 @@ Return t if the file exists and loads successfully. */)
if (is_elc
/* version = 1 means the file is empty, in which case we can
treat it as not byte-compiled. */
- || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1))
+ || (lread_fd_p
+ && (version = safe_to_load_version (file, fd)) > 1))
/* Load .elc files directly, but not when they are
remote and have no handler! */
{
- if (fd != -2)
+ if (!lread_fd_cmp (-2))
{
struct stat s1, s2;
int result;
@@ -1476,9 +1669,9 @@ Return t if the file exists and loads successfully. */)
{
Lisp_Object val;
- if (fd >= 0)
+ if (lread_fd_p)
{
- emacs_close (fd);
+ lread_close (fd);
clear_unwind_protect (fd_index);
}
val = call4 (Vload_source_file_function, found, hist_file_name,
@@ -1488,12 +1681,12 @@ Return t if the file exists and loads successfully. */)
}
}
- if (fd < 0)
+ if (!lread_fd_p)
{
/* We somehow got here with fd == -2, meaning the file is deemed
to be remote. Don't even try to reopen the file locally;
just force a failure. */
- stream = NULL;
+ stream = file_stream_invalid;
errno = EINVAL;
}
else if (!is_module && !is_native_elisp)
@@ -1504,7 +1697,15 @@ Return t if the file exists and loads successfully. */)
efound = ENCODE_FILE (found);
stream = emacs_fopen (SSDATA (efound), fmode);
#else
+#if !defined USE_ANDROID_ASSETS
stream = fdopen (fd, fmode);
+#else
+ /* Android systems use special file descriptors which can point
+ into compressed data and double as file streams. FMODE is
+ unused. */
+ ((void) fmode);
+ stream = fd;
+#endif
#endif
}
@@ -1516,15 +1717,15 @@ Return t if the file exists and loads successfully. */)
{
/* `module-load' uses the file name, so we can close the stream
now. */
- if (fd >= 0)
+ if (lread_fd_p)
{
- emacs_close (fd);
+ lread_close (fd);
clear_unwind_protect (fd_index);
}
}
else
{
- if (! stream)
+ if (!file_stream_valid_p (stream))
report_file_error ("Opening stdio stream", file);
set_unwind_protect_ptr (fd_index, close_infile_unwind, infile);
input.stream = stream;
@@ -1660,7 +1861,8 @@ directories, make sure the PREDICATE function returns `dir-ok' for them. */)
(Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate)
{
Lisp_Object file;
- int fd = openp (path, filename, suffixes, &file, predicate, false, true);
+ int fd = openp (path, filename, suffixes, &file, predicate, false, true,
+ NULL);
if (NILP (predicate) && fd >= 0)
emacs_close (fd);
return file;
@@ -1676,7 +1878,7 @@ maybe_swap_for_eln1 (Lisp_Object src_name, Lisp_Object eln_name,
if (eln_fd > 0)
{
- if (fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
+ if (sys_fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
emacs_close (eln_fd);
else
{
@@ -1801,14 +2003,20 @@ maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd,
If NEWER is true, try all SUFFIXes and return the result for the
newest file that exists. Does not apply to remote files,
- or if a non-nil and non-t PREDICATE is specified.
+ platform-specific files, or if a non-nil and non-t PREDICATE is
+ specified.
+
+ If NO_NATIVE is true do not try to load native code.
- if NO_NATIVE is true do not try to load native code. */
+ If PLATFORM is non-NULL and the file being loaded lies in a special
+ directory, such as the Android `/assets' directory, return a handle
+ to that directory in *PLATFORM instead of a file descriptor; in
+ that case, value is -3. */
int
openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
Lisp_Object *storeptr, Lisp_Object predicate, bool newer,
- bool no_native)
+ bool no_native, void **platform)
{
ptrdiff_t fn_size = 100;
char buf[100];
@@ -1820,6 +2028,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
ptrdiff_t max_suffix_len = 0;
int last_errno = ENOENT;
int save_fd = -1;
+#ifdef USE_ANDROID_ASSETS
+ struct android_fd_or_asset platform_fd;
+#endif
USE_SAFE_ALLOCA;
/* The last-modified time of the newest matching file found.
@@ -1964,8 +2175,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
fd = -1;
if (INT_MAX < XFIXNAT (predicate))
last_errno = EINVAL;
- else if (faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
- AT_EACCESS)
+ else if (sys_faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
+ AT_EACCESS)
== 0)
{
if (file_directory_p (encoded_fn))
@@ -1985,11 +2196,34 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
it. Only open the file when we are sure that it
exists. */
#ifdef WINDOWSNT
- if (faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
+ if (sys_faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
fd = -1;
else
#endif
- fd = emacs_open (pfn, O_RDONLY, 0);
+ {
+#if !defined USE_ANDROID_ASSETS
+ fd = emacs_open (pfn, O_RDONLY, 0);
+#else
+ if (platform)
+ {
+ platform_fd = android_open_asset (pfn, O_RDONLY, 0);
+
+ if (platform_fd.asset
+ && platform_fd.asset != (void *) -1)
+ {
+ *storeptr = string;
+ goto handle_platform_fd;
+ }
+
+ if (platform_fd.asset == (void *) -1)
+ fd = -1;
+ else
+ fd = platform_fd.fd;
+ }
+ else
+ fd = emacs_open (pfn, O_RDONLY, 0);
+#endif
+ }
if (fd < 0)
{
@@ -1998,7 +2232,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
}
else
{
- int err = (fstat (fd, &st) != 0 ? errno
+ int err = (sys_fstat (fd, &st) != 0 ? errno
: S_ISDIR (st.st_mode) ? EISDIR : 0);
if (err)
{
@@ -2057,6 +2291,16 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
SAFE_FREE ();
errno = last_errno;
return -1;
+
+#ifdef USE_ANDROID_ASSETS
+ handle_platform_fd:
+
+ /* Here, openp found a platform specific file descriptor. It can't
+ be a directory under Android, so return it in *PLATFORM and then
+ -3 as the file descriptor. */
+ *platform = platform_fd.asset;
+ return -3;
+#endif
}
@@ -2088,7 +2332,7 @@ build_load_history (Lisp_Object filename, bool entire)
{
foundit = 1;
- /* If we're loading the entire file, remove old data. */
+ /* If we're loading the entire file, remove old data. */
if (entire)
{
if (NILP (prev))
@@ -2096,8 +2340,8 @@ build_load_history (Lisp_Object filename, bool entire)
else
Fsetcdr (prev, XCDR (tail));
}
-
- /* Otherwise, cons on new symbols that are not already members. */
+ /* Otherwise, cons on new symbols that are not already
+ members. */
else
{
tem2 = Vcurrent_load_list;
@@ -3460,7 +3704,7 @@ skip_lazy_string (Lisp_Object readcharfun)
ss->string = xrealloc (ss->string, ss->size);
}
- FILE *instream = infile->stream;
+ file_stream instream = infile->stream;
ss->position = (file_tell (instream) - infile->lookahead);
/* Copy that many bytes into the saved string. */
@@ -3470,7 +3714,7 @@ skip_lazy_string (Lisp_Object readcharfun)
ss->string[i++] = c = infile->buf[--infile->lookahead];
block_input ();
for (; i < nskip && c >= 0; i++)
- ss->string[i] = c = getc (instream);
+ ss->string[i] = c = file_get_char (instream);
unblock_input ();
ss->length = i;
diff --git a/src/marker.c b/src/marker.c
index e42c49a5434..7b15cd62f1e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "character.h"
#include "buffer.h"
+#include "window.h"
/* Record one cached position found recently by
buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
@@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
attach_marker (m, b, charpos, bytepos);
}
+
+#ifdef HAVE_TEXT_CONVERSION
+
+ /* If B is the buffer's mark and there is a window displaying B, and
+ text conversion is enabled while the mark is active, redisplay
+ the buffer.
+
+ propagate_window_redisplay will propagate this redisplay to the
+ window, which will eventually reach
+ mark_window_display_accurate_1. At that point,
+ report_point_change will be told to update the mark as seen by
+ the input method.
+
+ This is done all the way in (the seemingly irrelevant) redisplay
+ because the selection reported to the input method is actually what
+ is visible on screen, namely w->last_point. */
+
+ if (m->buffer
+ && EQ (marker, BVAR (m->buffer, mark))
+ && !NILP (BVAR (m->buffer, mark_active))
+ && buffer_window_count (m->buffer))
+ bset_redisplay (m->buffer);
+
+#endif
+
return marker;
}
diff --git a/src/menu.c b/src/menu.c
index 73d4215b94b..6ab34a16996 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -48,7 +48,7 @@ static bool
have_boxes (void)
{
#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU)
+ || defined (HAVE_HAIKU) || defined (HAVE_ANDROID)
if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
return 1;
#endif
@@ -167,7 +167,7 @@ ensure_menu_items (int items)
}
}
-#ifdef HAVE_EXT_MENU_BAR
+#if defined HAVE_EXT_MENU_BAR || defined HAVE_ANDROID
/* Begin a submenu. */
@@ -191,7 +191,7 @@ push_submenu_end (void)
menu_items_submenu_depth--;
}
-#endif /* HAVE_EXT_MENU_BAR */
+#endif /* HAVE_EXT_MENU_BAR || HAVE_ANDROID */
/* Indicate boundary between left and right. */
@@ -420,8 +420,9 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk
AREF (item_properties, ITEM_PROPERTY_SELECTED),
AREF (item_properties, ITEM_PROPERTY_HELP));
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
- || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
+ || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
/* Display a submenu using the toolkit. */
if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
&& ! (NILP (map) || NILP (enabled)))
@@ -1151,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
@@ -1465,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/pdumper.c b/src/pdumper.c
index 34998549cc8..04e3b66b0c2 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -4071,10 +4071,12 @@ types. */)
{
eassert (initialized);
+#ifndef HAVE_ANDROID
if (! noninteractive)
error ("Dumping Emacs currently works only in batch mode. "
"If you'd like it to work interactively, please consider "
"contributing a patch to Emacs.");
+#endif
if (will_dump_with_unexec_p ())
error ("This Emacs instance was started under the assumption "
@@ -4744,7 +4746,9 @@ dump_discard_mem (void *mem, size_t size)
# ifdef HAVE_POSIX_MADVISE
/* Discard COWed pages. */
(void) posix_madvise (mem, size, POSIX_MADV_DONTNEED);
-# endif
+# elif defined HAVE_MADVISE
+ (void) madvise (mem, size, MADV_DONTNEED);
+#endif
/* Release the commit charge for the mapping. */
(void) mprotect (mem, size, PROT_NONE);
#endif
@@ -5614,7 +5618,7 @@ pdumper_load (const char *dump_filename, char *argv0)
}
err = PDUMPER_LOAD_FILE_NOT_FOUND;
- if (fstat (dump_fd, &stat) < 0)
+ if (sys_fstat (dump_fd, &stat) < 0)
goto out;
err = PDUMPER_LOAD_BAD_FILE_TYPE;
@@ -5836,6 +5840,10 @@ void
syms_of_pdumper (void)
{
#ifdef HAVE_PDUMPER
+ unsigned char desired[sizeof fingerprint];
+ int i;
+ char hexbuf[2 * sizeof fingerprint];
+
defsubr (&Sdump_emacs_portable);
defsubr (&Sdump_emacs_portable__sort_predicate);
defsubr (&Sdump_emacs_portable__sort_predicate_copied);
@@ -5848,5 +5856,17 @@ syms_of_pdumper (void)
DEFSYM (Qdump_file_name, "dump-file-name");
DEFSYM (Qafter_pdump_load_hook, "after-pdump-load-hook");
defsubr (&Spdumper_stats);
+
+ for (i = 0; i < sizeof fingerprint; i++)
+ desired[i] = fingerprint[i];
+
+ hexbuf_digest (hexbuf, desired, sizeof desired);
+
+ DEFVAR_LISP ("pdumper-fingerprint", Vpdumper_fingerprint,
+ doc: /* The fingerprint of this Emacs binary.
+It is a string that is supposed to be unique to each build of
+Emacs. */);
+ Vpdumper_fingerprint = make_unibyte_string ((char *) hexbuf,
+ sizeof hexbuf);
#endif /* HAVE_PDUMPER */
}
diff --git a/src/print.c b/src/print.c
index 5c95aeb9a20..940ec7312bf 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1913,12 +1913,17 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
print_c_string ("#<font-entity", printcharfun);
for (int i = 0; i < FONT_SPEC_MAX; i++)
{
- printchar (' ', printcharfun);
- if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
- print_object (AREF (obj, i), printcharfun, escapeflag);
- else
- print_object (font_style_symbolic (obj, i, 0),
- printcharfun, escapeflag);
+ /* FONT_EXTRA_INDEX can contain private information in
+ font entities which isn't safe to print. */
+ if (i != FONT_EXTRA_INDEX || !FONT_ENTITY_P (obj))
+ {
+ printchar (' ', printcharfun);
+ if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
+ print_object (AREF (obj, i), printcharfun, escapeflag);
+ else
+ print_object (font_style_symbolic (obj, i, 0),
+ printcharfun, escapeflag);
+ }
}
}
else
diff --git a/src/process.c b/src/process.c
index 67d1d3e425f..0eff789e599 100644
--- a/src/process.c
+++ b/src/process.c
@@ -119,6 +119,11 @@ static struct rlimit nofile_limit;
#include "gnutls.h"
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#include "androidterm.h"
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@@ -876,7 +881,8 @@ allocate_pty (char pty_name[PTY_NAME_SIZE])
/* Check to make certain that both sides are available.
This avoids a nasty yet stupid bug in rlogins. */
- if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
+ if (sys_faccessat (AT_FDCWD, pty_name,
+ R_OK | W_OK, AT_EACCESS) != 0)
{
emacs_close (fd);
continue;
@@ -2002,7 +2008,7 @@ usage: (make-process &rest ARGS) */)
{
tem = Qnil;
openp (Vexec_path, program, Vexec_suffixes, &tem,
- make_fixnum (X_OK), false, false);
+ make_fixnum (X_OK), false, false, NULL);
if (NILP (tem))
report_file_error ("Searching for program", program);
tem = Fexpand_file_name (tem, Qnil);
@@ -5679,7 +5685,17 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
timeout = short_timeout;
#endif
- /* Non-macOS HAVE_GLIB builds call thread_select in xgselect.c. */
+ /* Android doesn't support threads and requires using a
+ replacement for pselect in android.c to poll for
+ events. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ nfds = android_select (max_desc + 1,
+ &Available, (check_write ? &Writeok : 0),
+ NULL, &timeout);
+#else
+
+ /* Non-macOS HAVE_GLIB builds call thread_select in
+ xgselect.c. */
#if defined HAVE_GLIB && !defined HAVE_NS
nfds = xg_select (max_desc + 1,
&Available, (check_write ? &Writeok : 0),
@@ -5695,6 +5711,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
(check_write ? &Writeok : 0),
NULL, &timeout, NULL);
#endif /* !HAVE_GLIB */
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
#ifdef HAVE_GNUTLS
/* Merge tls_available into Available. */
@@ -7231,7 +7248,7 @@ process has been transmitted to the serial port. */)
send_process (proc, "\004", 1, Qnil);
else if (EQ (XPROCESS (proc)->type, Qserial))
{
-#ifndef WINDOWSNT
+#if !defined WINDOWSNT && defined HAVE_TCDRAIN
if (tcdrain (XPROCESS (proc)->outfd) != 0)
report_file_error ("Failed tcdrain", Qnil);
#endif /* not WINDOWSNT */
diff --git a/src/scroll.c b/src/scroll.c
index eee1ad80950..1f530dc5c95 100644
--- a/src/scroll.c
+++ b/src/scroll.c
@@ -21,6 +21,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
+/* The entire file is defined out under Android, where there is no
+ text terminal support of any kind. */
+
+#ifndef HAVE_ANDROID
+
#include "lisp.h"
#include "termchar.h"
#include "dispextern.h"
@@ -984,3 +989,5 @@ do_line_insertion_deletion_costs (struct frame *frame,
FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
coefficient);
}
+
+#endif
diff --git a/src/sfnt.c b/src/sfnt.c
new file mode 100644
index 00000000000..800adc19214
--- /dev/null
+++ b/src/sfnt.c
@@ -0,0 +1,19684 @@
+/* TrueType format font support for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+
+#include "sfnt.h"
+
+#include <assert.h>
+#include <attribute.h>
+#include <byteswap.h>
+#include <fcntl.h>
+#include <intprops.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
+#ifdef TEST
+
+#include <time.h>
+#include <timespec.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+static void *
+xmalloc (size_t size)
+{
+ void *ptr;
+
+ ptr = malloc (size);
+
+ if (!ptr)
+ abort ();
+
+ return ptr;
+}
+
+static void *
+xrealloc (void *ptr, size_t size)
+{
+ void *new_ptr;
+
+ new_ptr = realloc (ptr, size);
+
+ if (!new_ptr)
+ abort ();
+
+ return new_ptr;
+}
+
+static void
+xfree (void *ptr)
+{
+ return free (ptr);
+}
+
+/* Use this for functions that are static while building in test mode,
+ but are used outside as well. */
+#define TEST_STATIC static
+
+/* Needed for tests. */
+#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])
+
+#else
+#define TEST_STATIC
+#include "lisp.h"
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/* This file provides generic support for reading most TrueType fonts,
+ and some OpenType fonts with TrueType outlines, along with glyph
+ lookup, outline decomposition, and alpha mask generation from those
+ glyphs. It is intended to be used on any platform where proper
+ libraries such as FreeType are not easily available, and the native
+ font library is too limited for Emacs to support properly.
+
+ Unlike most popular libraries for handling fonts, no ``font'' or
+ ``face'' type is provided. Instead, routines and structure
+ definitions for accessing and making use of individual tables in a
+ font file are exported, which allows for flexibility in the rest of
+ Emacs.
+
+ Try not to keep this file too dependent on Emacs. Everything Lisp
+ related goes in sfntfont.c. The author wants to keep using it for
+ some other (free) software.
+
+ The source of reference is the TrueType Reference Manual, published
+ by Apple Computer, which is currently found at:
+
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/
+
+ Apple's TrueType implementation is notably missing features
+ provided by Microsoft's extended OpenType scaler, such as the two
+ additional phantom points on the Y axis, and also behaves
+ differently, especially when it comes to considering phantom points
+ as anchors in compound glyphs.
+
+ As a result, do not expect this scaler to work well with Microsoft
+ fonts such as Arial. */
+
+
+
+/* Mapping between sfnt table names and their identifiers. */
+
+static uint32_t sfnt_table_names[] =
+ {
+ [SFNT_TABLE_CMAP] = 0x636d6170,
+ [SFNT_TABLE_GLYF] = 0x676c7966,
+ [SFNT_TABLE_HEAD] = 0x68656164,
+ [SFNT_TABLE_HHEA] = 0x68686561,
+ [SFNT_TABLE_HMTX] = 0x686d7478,
+ [SFNT_TABLE_LOCA] = 0x6c6f6361,
+ [SFNT_TABLE_MAXP] = 0x6d617870,
+ [SFNT_TABLE_NAME] = 0x6e616d65,
+ [SFNT_TABLE_META] = 0x6d657461,
+ [SFNT_TABLE_CVT ] = 0x63767420,
+ [SFNT_TABLE_FPGM] = 0x6670676d,
+ [SFNT_TABLE_PREP] = 0x70726570,
+ [SFNT_TABLE_FVAR] = 0x66766172,
+ [SFNT_TABLE_GVAR] = 0x67766172,
+ [SFNT_TABLE_CVAR] = 0x63766172,
+ [SFNT_TABLE_AVAR] = 0x61766172,
+ };
+
+/* Swap values from TrueType to system byte order. */
+
+static void
+sfnt_swap16_1 (uint16_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+ *value = bswap_16 (*value);
+#endif
+}
+
+static void
+sfnt_swap32_1 (uint32_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+ *value = bswap_32 (*value);
+#endif
+}
+
+#define sfnt_swap16(what) (sfnt_swap16_1 ((uint16_t *) (what)))
+#define sfnt_swap32(what) (sfnt_swap32_1 ((uint32_t *) (what)))
+
+/* Read the table directory from the file FD. FD must currently be at
+ the start of the file (or an offset defined in the TTC header, if
+ applicable), and must be seekable. Return the table directory upon
+ success, else NULL.
+
+ Value is NULL upon failure, and the offset subtable upon success.
+ If FD is actually a TrueType collection file, value is -1. */
+
+TEST_STATIC struct sfnt_offset_subtable *
+sfnt_read_table_directory (int fd)
+{
+ struct sfnt_offset_subtable *subtable;
+ ssize_t rc;
+ size_t offset, subtable_size;
+ int i;
+
+ subtable = xmalloc (sizeof *subtable);
+ offset = SFNT_ENDOF (struct sfnt_offset_subtable,
+ range_shift, uint16_t);
+ rc = read (fd, subtable, offset);
+
+ if (rc < offset)
+ {
+ if (rc >= sizeof (uint32_t))
+ {
+ /* Detect a TTC file. In that case, the first long will be
+ ``ttcf''. */
+ sfnt_swap32 (&subtable->scaler_type);
+
+ if (subtable->scaler_type == SFNT_TTC_TTCF)
+ {
+ xfree (subtable);
+ return (struct sfnt_offset_subtable *) -1;
+ }
+ }
+
+ xfree (subtable);
+ return NULL;
+ }
+
+ sfnt_swap32 (&subtable->scaler_type);
+
+ /* Bail out early if this font is actually a TrueType collection
+ file. */
+
+ if (subtable->scaler_type == SFNT_TTC_TTCF)
+ {
+ xfree (subtable);
+ return (struct sfnt_offset_subtable *) -1;
+ }
+
+ sfnt_swap16 (&subtable->num_tables);
+ sfnt_swap16 (&subtable->search_range);
+ sfnt_swap16 (&subtable->entry_selector);
+ sfnt_swap16 (&subtable->range_shift);
+
+ /* Figure out how many more tables have to be read, and read each
+ one of them. */
+ subtable_size = (subtable->num_tables
+ * sizeof (struct sfnt_table_directory));
+ subtable = xrealloc (subtable, sizeof *subtable + subtable_size);
+ subtable->subtables
+ = (struct sfnt_table_directory *) (subtable + 1);
+
+ rc = read (fd, subtable->subtables, subtable_size);
+
+ if (rc < offset)
+ {
+ xfree (subtable);
+ return NULL;
+ }
+
+ /* Swap each of the subtables. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ sfnt_swap32 (&subtable->subtables[i].tag);
+ sfnt_swap32 (&subtable->subtables[i].checksum);
+ sfnt_swap32 (&subtable->subtables[i].offset);
+ sfnt_swap32 (&subtable->subtables[i].length);
+ }
+
+ return subtable;
+}
+
+/* Return a pointer to the table directory entry for TABLE in
+ SUBTABLE, or NULL if it was not found. */
+
+static struct sfnt_table_directory *
+sfnt_find_table (struct sfnt_offset_subtable *subtable,
+ enum sfnt_table table)
+{
+ int i;
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == sfnt_table_names[table])
+ return &subtable->subtables[i];
+ }
+
+ return NULL;
+}
+
+
+
+/* Character mapping routines. */
+
+/* Read a format 0 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_0 *
+sfnt_read_cmap_format_0 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_0 *format0;
+ ssize_t rc;
+ size_t wanted_size;
+
+ format0 = xmalloc (sizeof *format0);
+
+ /* Fill in fields that have already been read. */
+ format0->format = header->format;
+ format0->length = header->length;
+
+ /* Read the rest. */
+ wanted_size = (sizeof *format0
+ - offsetof (struct sfnt_cmap_format_0,
+ language));
+ rc = read (fd, &format0->language, wanted_size);
+
+ if (rc < wanted_size)
+ {
+ xfree (format0);
+ return (struct sfnt_cmap_format_0 *) -1;
+ }
+
+ /* Swap fields and return. */
+ sfnt_swap16 (&format0->language);
+ return format0;
+}
+
+/* Read a format 2 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_2 *
+sfnt_read_cmap_format_2 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_2 *format2;
+ ssize_t rc;
+ size_t min_bytes;
+ int i, nsub;
+
+ /* Reject contents that are too small. */
+ min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_2,
+ sub_header_keys, uint16_t[256]);
+ if (header->length < min_bytes)
+ return NULL;
+
+ /* Add enough bytes at the end to fit the two variable length
+ pointers. */
+ format2 = xmalloc (header->length + sizeof *format2);
+ format2->format = header->format;
+ format2->length = header->length;
+
+ /* Read the part before the variable length data. */
+ min_bytes -= offsetof (struct sfnt_cmap_format_2, language);
+ rc = read (fd, &format2->language, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Swap the fields now. */
+
+ sfnt_swap16 (&format2->language);
+
+ /* At the same time, look for the largest value in sub_header_keys.
+ That will be the number of subheaders and elements in the glyph
+ index array. */
+
+ nsub = 0;
+
+ for (i = 0; i < 256; ++i)
+ {
+ sfnt_swap16 (&format2->sub_header_keys[i]);
+
+ if (format2->sub_header_keys[i] > nsub)
+ nsub = format2->sub_header_keys[i];
+ }
+
+ if (!nsub)
+ /* If there are no subheaders, then things are finished. */
+ return format2;
+
+ /* Otherwise, read the rest of the variable length data to the end
+ of format2. */
+ min_bytes = (format2->length
+ - SFNT_ENDOF (struct sfnt_cmap_format_2,
+ sub_header_keys, uint16_t[256]));
+ rc = read (fd, format2 + 1, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Check whether or not the data is of the correct size. */
+ if (min_bytes < nsub * sizeof *format2->subheaders)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Point the data pointers to the right location, swap everything,
+ and return. */
+
+ format2->subheaders
+ = (struct sfnt_cmap_format_2_subheader *) (format2 + 1);
+ format2->glyph_index_array
+ = (uint16_t *) (format2->subheaders + nsub);
+
+ for (i = 0; i < nsub; ++i)
+ {
+ sfnt_swap16 (&format2->subheaders[i].first_code);
+ sfnt_swap16 (&format2->subheaders[i].entry_count);
+ sfnt_swap16 (&format2->subheaders[i].id_delta);
+ sfnt_swap16 (&format2->subheaders[i].id_range_offset);
+ }
+
+ /* Figure out how big the glyph index array is, and swap everything
+ there. */
+ format2->num_glyphs
+ = (min_bytes - nsub * sizeof *format2->subheaders) / 2;
+
+ for (i = 0; i < format2->num_glyphs; ++i)
+ sfnt_swap16 (&format2->glyph_index_array[i]);
+
+ return format2;
+}
+
+/* Read a format 4 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_4 *
+sfnt_read_cmap_format_4 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_4 *format4;
+ size_t min_bytes, variable_size;
+ ssize_t rc;
+ size_t bytes_minus_format4;
+ int seg_count, i;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4,
+ entry_selector, uint16_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (header->length < min_bytes)
+ return NULL;
+
+ /* Allocate the format4 buffer, making it the size of the buffer
+ itself plus that of the data. */
+ format4 = xmalloc (header->length + sizeof *format4);
+
+ /* Copy over fields that have already been read. */
+ format4->format = header->format;
+ format4->length = header->length;
+
+ /* Read the initial data. */
+ min_bytes -= offsetof (struct sfnt_cmap_format_4, language);
+ rc = read (fd, &format4->language, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format4);
+ return (struct sfnt_cmap_format_4 *) -1;
+ }
+
+ /* Swap fields that have been read. */
+ sfnt_swap16 (&format4->language);
+ sfnt_swap16 (&format4->seg_count_x2);
+ sfnt_swap16 (&format4->search_range);
+ sfnt_swap16 (&format4->entry_selector);
+
+ /* Get the number of segments to read. */
+ seg_count = format4->seg_count_x2 / 2;
+
+ /* Now calculate whether or not the size is sufficiently large. */
+ bytes_minus_format4
+ = format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4,
+ entry_selector, uint16_t);
+ variable_size = (seg_count * sizeof *format4->end_code
+ + sizeof *format4->reserved_pad
+ + seg_count * sizeof *format4->start_code
+ + seg_count * sizeof *format4->id_delta
+ + seg_count * sizeof *format4->id_range_offset);
+
+ if (bytes_minus_format4 < variable_size)
+ {
+ /* Not enough bytes to fit the entire implied table
+ contents. */
+ xfree (format4);
+ return NULL;
+ }
+
+ /* Read the rest of the bytes to the end of format4. */
+ rc = read (fd, format4 + 1, bytes_minus_format4);
+ if (rc < bytes_minus_format4)
+ {
+ xfree (format4);
+ return (struct sfnt_cmap_format_4 *) -1;
+ }
+
+ /* Set data pointers to the right locations. */
+ format4->end_code = (uint16_t *) (format4 + 1);
+ format4->reserved_pad = format4->end_code + seg_count;
+ format4->start_code = format4->reserved_pad + 1;
+ format4->id_delta = (int16_t *) (format4->start_code + seg_count);
+ format4->id_range_offset = format4->id_delta + seg_count;
+ format4->glyph_index_array = (uint16_t *) (format4->id_range_offset
+ + seg_count);
+
+ /* N.B. that the number of elements in glyph_index_array is
+ (bytes_minus_format4 - variable_size) / 2. Swap all the
+ data. */
+
+ sfnt_swap16 (format4->reserved_pad);
+
+ for (i = 0; i < seg_count; ++i)
+ {
+ sfnt_swap16 (&format4->end_code[i]);
+ sfnt_swap16 (&format4->start_code[i]);
+ sfnt_swap16 (&format4->id_delta[i]);
+ sfnt_swap16 (&format4->id_range_offset[i]);
+ }
+
+ format4->glyph_index_size
+ = (bytes_minus_format4 - variable_size) / 2;
+
+ for (i = 0; i < format4->glyph_index_size; ++i)
+ sfnt_swap16 (&format4->glyph_index_array[i]);
+
+ /* Done. Return the format 4 character map. */
+ return format4;
+}
+
+/* Read a format 6 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_6 *
+sfnt_read_cmap_format_6 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_6 *format6;
+ size_t min_size;
+ ssize_t rc;
+ uint16_t i;
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_6, entry_count,
+ uint16_t);
+
+ /* See if header->length is big enough. */
+ if (header->length < min_size)
+ return NULL;
+
+ /* Allocate the buffer to hold header->size and enough for at least
+ the glyph index array pointer. */
+ format6 = xmalloc (header->length + sizeof *format6);
+
+ /* Fill in data that has already been read. */
+ format6->format = header->format;
+ format6->length = header->length;
+
+ /* Read the fixed size data. */
+ min_size -= offsetof (struct sfnt_cmap_format_6, language);
+ rc = read (fd, &format6->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format6);
+ return (struct sfnt_cmap_format_6 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap16 (&format6->language);
+ sfnt_swap16 (&format6->first_code);
+ sfnt_swap16 (&format6->entry_count);
+
+ /* Figure out whether or not header->length is sufficient to hold
+ the variable length data. */
+ if (header->length
+ < format6->entry_count * sizeof *format6->glyph_index_array)
+ {
+ xfree (format6);
+ return NULL;
+ }
+
+ /* Read the variable length data. */
+ rc = read (fd, format6 + 1,
+ (format6->entry_count
+ * sizeof *format6->glyph_index_array));
+ if (rc < format6->entry_count * sizeof *format6->glyph_index_array)
+ {
+ xfree (format6);
+ return (struct sfnt_cmap_format_6 *) -1;
+ }
+
+ /* Set the data pointer and swap everything. */
+ format6->glyph_index_array = (uint16_t *) (format6 + 1);
+ for (i = 0; i < format6->entry_count; ++i)
+ sfnt_swap16 (&format6->glyph_index_array[i]);
+
+ /* All done! */
+ return format6;
+}
+
+/* Read a format 8 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_8 *
+sfnt_read_cmap_format_8 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_8 *format8;
+ size_t min_size, temp;
+ ssize_t rc;
+ uint32_t length, i;
+
+ /* Read the 32-bit length field. */
+ if (read (fd, &length, sizeof (length)) < sizeof (length))
+ return (struct sfnt_cmap_format_8 *) -1;
+
+ /* Swap the 32-bit length field. */
+ sfnt_swap32 (&length);
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+ uint32_t);
+
+ /* Make sure the header is at least as large as min_size. */
+ if (length < min_size)
+ return NULL;
+
+ /* Allocate a buffer of sufficient size. */
+ format8 = xmalloc (length + sizeof *format8);
+ format8->format = header->format;
+ format8->reserved = header->length;
+ format8->length = length;
+
+ /* Read the fixed length data. */
+ min_size -= offsetof (struct sfnt_cmap_format_8, language);
+ rc = read (fd, &format8->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format8);
+ return (struct sfnt_cmap_format_8 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&format8->language);
+ sfnt_swap32 (&format8->num_groups);
+
+ /* See if the size is sufficient to read the variable length
+ data. */
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+ uint32_t);
+
+ if (INT_MULTIPLY_WRAPV (format8->num_groups, sizeof *format8->groups,
+ &temp))
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ if (INT_ADD_WRAPV (min_size, temp, &min_size))
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ if (length < min_size)
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ /* Now read the variable length data. */
+ rc = read (fd, format8 + 1, temp);
+ if (rc < temp)
+ {
+ xfree (format8);
+ return (struct sfnt_cmap_format_8 *) -1;
+ }
+
+ /* Set the pointer to the variable length data. */
+ format8->groups
+ = (struct sfnt_cmap_format_8_or_12_group *) (format8 + 1);
+
+ for (i = 0; i < format8->num_groups; ++i)
+ {
+ sfnt_swap32 (&format8->groups[i].start_char_code);
+ sfnt_swap32 (&format8->groups[i].end_char_code);
+ sfnt_swap32 (&format8->groups[i].start_glyph_code);
+ }
+
+ /* All done. */
+ return format8;
+}
+
+/* Read a format 12 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_12 *
+sfnt_read_cmap_format_12 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_12 *format12;
+ size_t min_size, temp;
+ ssize_t rc;
+ uint32_t length, i;
+
+ /* Read the 32-bit length field. */
+ if (read (fd, &length, sizeof (length)) < sizeof (length))
+ return (struct sfnt_cmap_format_12 *) -1;
+
+ /* Swap the 32-bit length field. */
+ sfnt_swap32 (&length);
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+ uint32_t);
+
+ /* Make sure the header is at least as large as min_size. */
+ if (length < min_size)
+ return NULL;
+
+ /* Allocate a buffer of sufficient size. */
+ format12 = xmalloc (length + sizeof *format12);
+ format12->format = header->format;
+ format12->reserved = header->length;
+ format12->length = length;
+
+ /* Read the fixed length data. */
+ min_size -= offsetof (struct sfnt_cmap_format_12, language);
+ rc = read (fd, &format12->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format12);
+ return (struct sfnt_cmap_format_12 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&format12->language);
+ sfnt_swap32 (&format12->num_groups);
+
+ /* See if the size is sufficient to read the variable length
+ data. */
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+ uint32_t);
+
+ if (INT_MULTIPLY_WRAPV (format12->num_groups, sizeof *format12->groups,
+ &temp))
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ if (INT_ADD_WRAPV (min_size, temp, &min_size))
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ if (length < min_size)
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ /* Now read the variable length data. */
+ rc = read (fd, format12 + 1, temp);
+ if (rc < temp)
+ {
+ xfree (format12);
+ return (struct sfnt_cmap_format_12 *) -1;
+ }
+
+ /* Set the pointer to the variable length data. */
+ format12->groups
+ = (struct sfnt_cmap_format_8_or_12_group *) (format12 + 1);
+
+ for (i = 0; i < format12->num_groups; ++i)
+ {
+ sfnt_swap32 (&format12->groups[i].start_char_code);
+ sfnt_swap32 (&format12->groups[i].end_char_code);
+ sfnt_swap32 (&format12->groups[i].start_glyph_code);
+ }
+
+ /* All done. */
+ return format12;
+}
+
+/* Read a 3-byte big endian number from BYTES. */
+
+static unsigned int
+sfnt_read_24 (unsigned char *bytes)
+{
+ return (bytes[0] << 16u) | (bytes[1] << 8u) | bytes[2];
+}
+
+/* Read a format 14 cmap table from FD. HEADER->format will be 14 and
+ HEADER->length will be 0; the 16-bit length field is not read.
+ OFFSET is the offset of the table's header in the font file.
+
+ Only variation selector records will be read. UVS tables will
+ not. */
+
+static struct sfnt_cmap_format_14 *
+sfnt_read_cmap_format_14 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header,
+ off_t offset)
+{
+ struct sfnt_cmap_format_14 *format14;
+ uint32_t length;
+ uint32_t num_records;
+ uint32_t buffer1[2];
+ size_t size, temp;
+ char buffer[3 + 4 + 4];
+ int i;
+
+ /* Read the length field and number of variation selector
+ records. */
+
+ if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1)
+ return NULL;
+
+ length = buffer1[0];
+ num_records = buffer1[1];
+
+ sfnt_swap32 (&length);
+ sfnt_swap32 (&num_records);
+
+ /* Now, the number of records present is known. Allocate the format
+ 14 cmap table. */
+
+ size = sizeof *format14;
+ if (INT_MULTIPLY_WRAPV (num_records, sizeof *format14->records,
+ &temp)
+ || INT_ADD_WRAPV (size, temp, &size))
+ return NULL;
+
+ format14 = xmalloc (size);
+
+ /* Fill in the data already read. */
+ format14->format = header->format;
+ format14->length = length;
+ format14->num_var_selector_records = num_records;
+ format14->offset = offset;
+
+ /* Set the pointer to the remaining record data. */
+ format14->records
+ = (struct sfnt_variation_selector_record *) (format14 + 1);
+
+ /* Read each variation selector record. */
+
+ for (i = 0; i < num_records; ++i)
+ {
+ if (read (fd, buffer, sizeof buffer) < sizeof buffer)
+ {
+ xfree (format14);
+ return NULL;
+ }
+
+ /* First, read the 24 bit variation selector. */
+ format14->records[i].var_selector
+ = sfnt_read_24 ((unsigned char *) buffer);
+
+ /* Next, read the two unaligned longs. */
+ memcpy (&format14->records[i].default_uvs_offset,
+ buffer + 3,
+ sizeof format14->records[i].default_uvs_offset);
+ memcpy (&format14->records[i].nondefault_uvs_offset,
+ buffer + 7,
+ sizeof format14->records[i].nondefault_uvs_offset);
+
+ /* And swap them. */
+ sfnt_swap32 (&format14->records[i].default_uvs_offset);
+ sfnt_swap32 (&format14->records[i].nondefault_uvs_offset);
+ }
+
+ /* Return the format 14 character mapping table. */
+ return format14;
+}
+
+/* Read the CMAP subtable data from a given file FD at TABLE_OFFSET
+ bytes from DIRECTORY_OFFSET. Return the subtable data if it is
+ supported. Else, value is NULL if the format is unsupported, or -1
+ upon an IO error. */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
+ uint32_t table_offset)
+{
+ off_t offset;
+ struct sfnt_cmap_encoding_subtable_data header;
+
+ if (INT_ADD_WRAPV (directory_offset, table_offset, &offset))
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ if (read (fd, &header.format, sizeof header.format)
+ < sizeof header.format)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ sfnt_swap16 (&header.format);
+
+ /* Format 14 tables are rather special: they do not have a 16-bit
+ `length' field. When these tables are encountered, leave reading
+ the rest of the header to `sfnt_read_cmap_table_14'. */
+
+ if (header.format != 14)
+ {
+ if (read (fd, &header.length, sizeof header.length)
+ < sizeof header.length)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ sfnt_swap16 (&header.length);
+ }
+ else
+ header.length = 0;
+
+ switch (header.format)
+ {
+ case 0:
+ /* If the length changes, then something has changed to the
+ format. */
+ if (header.length != 262)
+ return NULL;
+
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_0 (fd, &header));
+
+ case 2:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_2 (fd, &header));
+
+ case 4:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_4 (fd, &header));
+
+ case 6:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_6 (fd, &header));
+
+ case 8:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_8 (fd, &header));
+
+ case 12:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_12 (fd, &header));
+
+ case 14:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_14 (fd, &header, offset));
+
+ default:
+ return NULL;
+ }
+}
+
+/* Read the CMAP table of a given font from the file FD. Use the
+ table directory specified in SUBTABLE.
+
+ Return the CMAP table and a list of encoding subtables in
+ *SUBTABLES and *DATA upon success, else NULL. If DATA is NULL, do
+ not read the subtable data. */
+
+TEST_STATIC struct sfnt_cmap_table *
+sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_cmap_encoding_subtable **subtables,
+ struct sfnt_cmap_encoding_subtable_data ***data)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_cmap_table *cmap;
+ ssize_t rc;
+ int i, j;
+
+ /* Find the CMAP table in the table directory. */
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CMAP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the start of the CMAP table. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the table header. */
+ cmap = xmalloc (sizeof *cmap);
+ rc = read (fd, cmap, sizeof *cmap);
+
+ if (rc < sizeof *cmap)
+ {
+ xfree (cmap);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap16 (&cmap->version);
+ sfnt_swap16 (&cmap->num_subtables);
+
+ if (cmap->version != 0)
+ {
+ xfree (cmap);
+ return NULL;
+ }
+
+ *subtables = xmalloc (cmap->num_subtables
+ * sizeof **subtables);
+
+
+ /* First, read the common parts of each encoding subtable. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ /* Read the common part of the new subtable. */
+ rc = read (fd, &(*subtables)[i], sizeof (*subtables)[i]);
+
+ if (rc < sizeof (*subtables))
+ {
+ xfree (cmap);
+ xfree (*subtables);
+ return NULL;
+ }
+
+ sfnt_swap16 (&(*subtables)[i].platform_id);
+ sfnt_swap16 (&(*subtables)[i].platform_specific_id);
+ sfnt_swap32 (&(*subtables)[i].offset);
+ }
+
+ /* If data is NULL, the caller only wants the table headers. */
+
+ if (!data)
+ return cmap;
+
+ /* Second, read each encoding subtable itself. */
+ *data = xmalloc (cmap->num_subtables * sizeof *data);
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ (*data)[i] = sfnt_read_cmap_table_1 (fd, directory->offset,
+ (*subtables)[i].offset);
+
+ if ((*data)[i] == (void *) -1)
+ {
+ /* An IO error occurred (as opposed to the subtable format
+ being unsupported.) Return now. */
+
+ for (j = 0; j < i; ++j)
+ xfree ((*data)[j]);
+
+ xfree (*data);
+ xfree (*subtables);
+ xfree (cmap);
+ return NULL;
+ }
+ }
+
+ return cmap;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 0 cmap
+ FORMAT0. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_0 (sfnt_char character,
+ struct sfnt_cmap_format_0 *format0)
+{
+ if (character >= 256)
+ return 0;
+
+ return format0->glyph_index_array[character];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 2 cmap
+ FORMAT2. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_2 (sfnt_char character,
+ struct sfnt_cmap_format_2 *format2)
+{
+ unsigned char i, k, j;
+ struct sfnt_cmap_format_2_subheader *subheader;
+ unsigned char *slice;
+ uint16_t glyph;
+
+ if (character > 65335)
+ return 0;
+
+ i = character >> 16;
+ j = character & 0xff;
+ k = format2->sub_header_keys[i] / 8;
+
+ if (k)
+ {
+ subheader = &format2->subheaders[k];
+
+ if (subheader->first_code <= j
+ && j <= ((int) subheader->first_code
+ + (int) subheader->entry_count))
+ {
+ /* id_range_offset is actually the number of bytes past
+ itself containing the uint16_t ``slice''. It is possibly
+ unaligned. */
+ slice = (unsigned char *) &subheader->id_range_offset;
+ slice += subheader->id_range_offset;
+ slice += (j - subheader->first_code) * sizeof (uint16_t);
+
+ if (slice < (unsigned char *) format2->glyph_index_array
+ || (slice + 1
+ > (unsigned char *) (format2->glyph_index_array
+ + format2->num_glyphs)))
+ /* The character is out of bounds. */
+ return 0;
+
+ memcpy (&glyph, slice, sizeof glyph);
+ return (glyph + subheader->id_delta) % 65536;
+ }
+ else
+ return 0;
+ }
+
+ /* k is 0, so glyph_index_array[i] is the glyph. */
+ return (i < format2->num_glyphs
+ ? format2->glyph_index_array[i]
+ : 0);
+}
+
+/* Like `bsearch'. However, return the highest element above KEY if
+ it could not be found. */
+
+static void *
+sfnt_bsearch_above (const void *key, const void *base,
+ size_t nmemb, size_t size,
+ int (*compar) (const void *,
+ const void *))
+{
+ const unsigned char *bytes, *sample;
+ size_t low, high, mid;
+
+ bytes = base;
+ low = 0;
+ high = nmemb - 1;
+
+ if (!nmemb)
+ return NULL;
+
+ while (low != high)
+ {
+ mid = low + (high - low) / 2;
+ sample = bytes + mid * size;
+
+ if (compar (key, sample) > 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ return (unsigned char *) bytes + low * size;
+}
+
+/* Compare two uint16_t's. Used to bisect through a format 4
+ table. */
+
+static int
+sfnt_compare_uint16 (const void *a, const void *b)
+{
+ return ((int) *((uint16_t *) a)) - ((int) *((uint16_t *) b));
+}
+
+/* Look up the glyph corresponding to CODE in the format 4 cmap
+ FORMAT4, using the table segment SEGMENT. Value is 0 if no glyph
+ was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4_1 (uint16_t code, uint16_t segment,
+ struct sfnt_cmap_format_4 *format4)
+{
+ uint16_t *index;
+
+ if (format4->id_range_offset[segment])
+ {
+ /* id_range_offset is not 0, so the glyph mapping depends on
+ it. */
+ index = (uint16_t *) (&format4->id_range_offset[segment]
+ + format4->id_range_offset[segment] / 2
+ + (code - format4->start_code[segment]));
+
+ /* Check that index is not out of bounds. */
+ if (index >= (format4->glyph_index_array
+ + format4->glyph_index_size)
+ || index < format4->glyph_index_array)
+ return 0;
+
+ /* Return what is in index. */
+ return (*index ? (format4->id_delta[segment]
+ + *index) % 65536 : 0);
+ }
+
+ /* Otherwise, just add id_delta. */
+ return (format4->id_delta[segment] + code) % 65536;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 4 cmap
+ FORMAT4. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4 (sfnt_char character,
+ struct sfnt_cmap_format_4 *format4)
+{
+ uint16_t *segment_address;
+ uint16_t code, segment;
+ sfnt_glyph glyph;
+
+ if (character > 65535)
+ return 0;
+
+ code = character;
+
+ /* Find the segment ending above or at CHARACTER. */
+ segment_address = sfnt_bsearch_above (&code, format4->end_code,
+ format4->seg_count_x2 / 2,
+ sizeof code,
+ sfnt_compare_uint16);
+ segment = segment_address - format4->end_code;
+
+ /* If the segment starts too late, return 0. */
+ if (!segment_address || format4->start_code[segment] > character)
+ return 0;
+
+ glyph = sfnt_lookup_glyph_4_1 (character, segment, format4);
+
+ if (glyph)
+ return glyph;
+
+ /* Droid Sans Mono has overlapping segments in its format 4 cmap
+ subtable where the first segment's end code is 32, while the
+ second segment's start code is also 32. The TrueType Reference
+ Manual says that mapping should begin by searching for the first
+ segment whose end code is greater than or equal to the character
+ being indexed, but that results in the first subtable being
+ found, which doesn't work, while the second table does. Try to
+ detect this situation and use the second table if possible. */
+
+ if (!glyph
+ /* The character being looked up is the current segment's end
+ code. */
+ && code == format4->end_code[segment]
+ /* There is an additional segment. */
+ && segment + 1 < format4->seg_count_x2 / 2
+ /* That segment's start code is the same as this segment's end
+ code. */
+ && format4->start_code[segment + 1] == format4->end_code[segment])
+ /* Try the second segment. */
+ return sfnt_lookup_glyph_4_1 (character, segment + 1, format4);
+
+ /* Fail. */
+ return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 6 cmap
+ FORMAT6. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_6 (sfnt_char character,
+ struct sfnt_cmap_format_6 *format6)
+{
+ if (character < format6->first_code
+ || character >= (format6->first_code
+ + (int) format6->entry_count))
+ return 0;
+
+ return format6->glyph_index_array[character - format6->first_code];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 8 cmap
+ FORMAT8. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_8 (sfnt_char character,
+ struct sfnt_cmap_format_8 *format8)
+{
+ uint32_t i;
+
+ if (character > 0xffffffff)
+ return 0;
+
+ for (i = 0; i < format8->num_groups; ++i)
+ {
+ if (format8->groups[i].start_char_code <= character
+ && format8->groups[i].end_char_code >= character)
+ return (format8->groups[i].start_glyph_code
+ + (character
+ - format8->groups[i].start_char_code));
+ }
+
+ return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 12 cmap
+ FORMAT12. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_12 (sfnt_char character,
+ struct sfnt_cmap_format_12 *format12)
+{
+ uint32_t i;
+
+ if (character > 0xffffffff)
+ return 0;
+
+ for (i = 0; i < format12->num_groups; ++i)
+ {
+ if (format12->groups[i].start_char_code <= character
+ && format12->groups[i].end_char_code >= character)
+ return (format12->groups[i].start_glyph_code
+ + (character
+ - format12->groups[i].start_char_code));
+ }
+
+ return 0;
+}
+
+/* Look up the glyph index corresponding to the character CHARACTER,
+ which must be in the correct encoding for the cmap table pointed to
+ by DATA.
+
+ DATA must be either a format 0, 2, 4, 6, 8 or 12 cmap table, else
+ behavior is undefined. */
+
+TEST_STATIC sfnt_glyph
+sfnt_lookup_glyph (sfnt_char character,
+ struct sfnt_cmap_encoding_subtable_data *data)
+{
+ switch (data->format)
+ {
+ case 0:
+ return sfnt_lookup_glyph_0 (character,
+ (struct sfnt_cmap_format_0 *) data);
+
+ case 2:
+ return sfnt_lookup_glyph_2 (character,
+ (struct sfnt_cmap_format_2 *) data);
+
+ case 4:
+ return sfnt_lookup_glyph_4 (character,
+ (struct sfnt_cmap_format_4 *) data);
+
+ case 6:
+ return sfnt_lookup_glyph_6 (character,
+ (struct sfnt_cmap_format_6 *) data);
+
+ case 8:
+ return sfnt_lookup_glyph_8 (character,
+ (struct sfnt_cmap_format_8 *) data);
+
+ case 12:
+ return sfnt_lookup_glyph_12 (character,
+ (struct sfnt_cmap_format_12 *) data);
+ }
+
+ return 0;
+}
+
+
+
+/* Header reading routines. */
+
+/* Read the head table of a given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the head table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_head_table *
+sfnt_read_head_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_head_table *head;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HEAD);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the entire table. */
+ head = xmalloc (sizeof *head);
+ rc = read (fd, head, sizeof *head);
+
+ if (rc < sizeof *head)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap32 (&head->version);
+ sfnt_swap32 (&head->revision);
+
+ if (head->version != 0x00010000)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ /* Swap the rest of the data. */
+ sfnt_swap32 (&head->checksum_adjustment);
+ sfnt_swap32 (&head->magic);
+
+ if (head->magic != 0x5f0f3cf5)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ sfnt_swap16 (&head->flags);
+ sfnt_swap16 (&head->units_per_em);
+ sfnt_swap32 (&head->created_high);
+ sfnt_swap32 (&head->created_low);
+ sfnt_swap32 (&head->modified_high);
+ sfnt_swap32 (&head->modified_low);
+ sfnt_swap16 (&head->xmin);
+ sfnt_swap16 (&head->xmax);
+ sfnt_swap16 (&head->ymin);
+ sfnt_swap16 (&head->ymax);
+ sfnt_swap16 (&head->mac_style);
+ sfnt_swap16 (&head->lowest_rec_ppem);
+ sfnt_swap16 (&head->font_direction_hint);
+ sfnt_swap16 (&head->index_to_loc_format);
+ sfnt_swap16 (&head->glyph_data_format);
+
+ return head;
+}
+
+/* Read the hhea table of a given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the head table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_hhea_table *
+sfnt_read_hhea_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_hhea_table *hhea;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HHEA);
+
+ if (!directory)
+ return NULL;
+
+ /* Check the length is right. */
+ if (directory->length != sizeof *hhea)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the entire table. */
+ hhea = xmalloc (sizeof *hhea);
+ rc = read (fd, hhea, sizeof *hhea);
+
+ if (rc < sizeof *hhea)
+ {
+ xfree (hhea);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap32 (&hhea->version);
+
+ if (hhea->version != 0x00010000)
+ {
+ xfree (hhea);
+ return NULL;
+ }
+
+ /* Swap the rest of the data. */
+ sfnt_swap16 (&hhea->ascent);
+ sfnt_swap16 (&hhea->descent);
+ sfnt_swap16 (&hhea->line_gap);
+ sfnt_swap16 (&hhea->advance_width_max);
+ sfnt_swap16 (&hhea->min_left_side_bearing);
+ sfnt_swap16 (&hhea->min_right_side_bearing);
+ sfnt_swap16 (&hhea->x_max_extent);
+ sfnt_swap16 (&hhea->caret_slope_rise);
+ sfnt_swap16 (&hhea->caret_slope_run);
+ sfnt_swap16 (&hhea->reserved1);
+ sfnt_swap16 (&hhea->reserved2);
+ sfnt_swap16 (&hhea->reserved3);
+ sfnt_swap16 (&hhea->reserved4);
+ sfnt_swap16 (&hhea->metric_data_format);
+ sfnt_swap16 (&hhea->num_of_long_hor_metrics);
+
+ return hhea;
+}
+
+/* Read a short loca table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the short table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_loca_table_short *
+sfnt_read_loca_table_short (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_loca_table_short *loca;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out how many glyphs there are based on the length. */
+ loca = xmalloc (sizeof *loca + directory->length);
+ loca->offsets = (uint16_t *) (loca + 1);
+ loca->num_offsets = directory->length / 2;
+
+ /* Read the variable-length table data. */
+ rc = read (fd, loca->offsets, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (loca);
+ return NULL;
+ }
+
+ /* Swap each of the offsets. */
+ for (i = 0; i < loca->num_offsets; ++i)
+ sfnt_swap16 (&loca->offsets[i]);
+
+ /* Return the table. */
+ return loca;
+}
+
+/* Read a long loca table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the long table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_loca_table_long *
+sfnt_read_loca_table_long (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_loca_table_long *loca;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out how many glyphs there are based on the length. */
+ loca = xmalloc (sizeof *loca + directory->length);
+ loca->offsets = (uint32_t *) (loca + 1);
+ loca->num_offsets = directory->length / 4;
+
+ /* Read the variable-length table data. */
+ rc = read (fd, loca->offsets, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (loca);
+ return NULL;
+ }
+
+ /* Swap each of the offsets. */
+ for (i = 0; i < loca->num_offsets; ++i)
+ sfnt_swap32 (&loca->offsets[i]);
+
+ /* Return the table. */
+ return loca;
+}
+
+/* Read the maxp table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the maxp table upon success, else NULL. If the version is
+ 0.5, fields past num_glyphs will not be populated. */
+
+TEST_STATIC struct sfnt_maxp_table *
+sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_maxp_table *maxp;
+ size_t size;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_MAXP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* If directory->length is not big enough for version 0.5, punt. */
+ if (directory->length < SFNT_ENDOF (struct sfnt_maxp_table,
+ num_glyphs, uint16_t))
+ return NULL;
+
+ /* Allocate the buffer to hold the data. Then, read
+ directory->length or sizeof *maxp bytes into it, whichever is
+ smaller. */
+
+ maxp = xmalloc (sizeof *maxp);
+ size = MIN (directory->length, sizeof *maxp);
+ rc = read (fd, maxp, size);
+
+ if (rc < size)
+ {
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* Now, swap version and num_glyphs. */
+ sfnt_swap32 (&maxp->version);
+ sfnt_swap16 (&maxp->num_glyphs);
+
+ /* Reject version 1.0 tables that are too small. */
+ if (maxp->version > 0x00005000 && size < sizeof *maxp)
+ {
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* If the table is version 0.5, then this function is done. */
+ if (maxp->version == 0x00005000)
+ return maxp;
+ else if (maxp->version != 0x00010000)
+ {
+ /* Reject invalid versions. */
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* Otherwise, swap the rest of the fields. */
+ sfnt_swap16 (&maxp->max_points);
+ sfnt_swap16 (&maxp->max_contours);
+ sfnt_swap16 (&maxp->max_composite_points);
+ sfnt_swap16 (&maxp->max_composite_contours);
+ sfnt_swap16 (&maxp->max_zones);
+ sfnt_swap16 (&maxp->max_twilight_points);
+ sfnt_swap16 (&maxp->max_storage);
+ sfnt_swap16 (&maxp->max_function_defs);
+ sfnt_swap16 (&maxp->max_instruction_defs);
+ sfnt_swap16 (&maxp->max_stack_elements);
+ sfnt_swap16 (&maxp->max_size_of_instructions);
+ sfnt_swap16 (&maxp->max_component_elements);
+ sfnt_swap16 (&maxp->max_component_depth);
+
+ /* All done. */
+ return maxp;
+}
+
+
+
+/* Glyph outlining generation. */
+
+/* Read a glyf table from the given font FD. Use the table directory
+ specified in SUBTABLE. The glyph data is not swapped.
+
+ Return the glyf table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_glyf_table *
+sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_glyf_table *glyf;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold everything. */
+ glyf = xmalloc (sizeof *glyf + directory->length);
+ glyf->size = directory->length;
+ glyf->glyphs = (unsigned char *) (glyf + 1);
+
+ /* Read the glyph data. */
+ rc = read (fd, glyf->glyphs, glyf->size);
+ if (rc < glyf->size)
+ {
+ xfree (glyf);
+ return NULL;
+ }
+
+ /* Return the table. */
+ return glyf;
+}
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Map a glyph table from the given font FD. Use the table directory
+ specified in SUBTABLE. The glyph data is not byte-swapped.
+
+ Value is the glyf table upon success, else NULL.
+ A mapped glyf table must be unmapped using `sfnt_unmap_glyf_table'.
+ The caller must correctly handle bus errors in between glyf->table
+ and glyf->size. */
+
+struct sfnt_glyf_table *
+sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_glyf_table *glyf;
+ void *glyphs;
+ size_t offset, page, map_offset;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+ if (!directory)
+ return NULL;
+
+ /* Now try to map the glyph data. Make sure offset is a multiple of
+ the page size. */
+
+ page = getpagesize ();
+ offset = directory->offset & ~(page - 1);
+
+ /* Figure out how much larger the mapping should be. */
+ map_offset = directory->offset - offset;
+
+ /* Do the mmap. */
+ glyphs = mmap (NULL, directory->length + map_offset,
+ PROT_READ, MAP_PRIVATE, fd, offset);
+
+ if (glyphs == MAP_FAILED)
+ return NULL;
+
+ /* An observation is that glyphs tend to be accessed in sequential
+ order and immediately after the font's glyph table is loaded. */
+
+#ifdef HAVE_POSIX_MADVISE
+ posix_madvise (glyphs, directory->length,
+ POSIX_MADV_WILLNEED);
+#elif defined HAVE_MADVISE
+ madvise (glyphs, directory->length, MADV_WILLNEED);
+#endif
+
+ /* Allocate the glyf table. */
+ glyf = xmalloc (sizeof *glyf);
+ glyf->size = directory->length;
+ glyf->glyphs = (unsigned char *) glyphs + map_offset;
+ glyf->start = glyphs;
+
+ return glyf;
+}
+
+/* Unmap the mmap'ed glyf table GLYF, then free its associated data.
+ Value is 0 upon success, else 1, in which case GLYF is still freed
+ all the same. */
+
+int
+sfnt_unmap_glyf_table (struct sfnt_glyf_table *glyf)
+{
+ int rc;
+ size_t size;
+
+ /* Calculate the size of the mapping. */
+ size = glyf->size + (glyf->glyphs - glyf->start);
+
+ rc = munmap (glyf->start, size);
+ xfree (glyf);
+
+ return rc != 0;
+}
+
+#endif /* HAVE_MMAP */
+
+/* Read the simple glyph outline from the glyph GLYPH from the
+ specified glyf table at the given offset. Set GLYPH->simple to a
+ non-NULL value upon success, else set it to NULL. */
+
+static void
+sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_glyf_table *glyf,
+ size_t offset)
+{
+ struct sfnt_simple_glyph *simple;
+ ssize_t min_size, min_size_2;
+ int i, number_of_points, repeat_count;
+ unsigned char *instructions_start;
+ unsigned char *flags_start, *flags_end;
+ unsigned char *vec_start;
+ int16_t delta, x, y;
+
+ /* Calculate the minimum size of the glyph data. This is the size
+ of the instruction length field followed by
+ glyph->number_of_contours * sizeof (uint16_t). */
+
+ min_size = (glyph->number_of_contours * sizeof (uint16_t)
+ + sizeof (uint16_t));
+
+ /* Check that the size is big enough. */
+ if (glyf->size < offset + min_size)
+ {
+ glyph->simple = NULL;
+ return;
+ }
+
+ /* Allocate enough to read at least that. */
+ simple = xmalloc (sizeof *simple + min_size);
+ simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+ memcpy (simple->end_pts_of_contours, glyf->glyphs + offset,
+ min_size);
+
+ /* This is not really an index into simple->end_pts_of_contours.
+ Rather, it is reading the first word past it. */
+ simple->instruction_length
+ = simple->end_pts_of_contours[glyph->number_of_contours];
+
+ /* Swap the contour end point indices and the instruction
+ length. */
+
+ for (i = 0; i < glyph->number_of_contours; ++i)
+ sfnt_swap16 (&simple->end_pts_of_contours[i]);
+
+ sfnt_swap16 (&simple->instruction_length);
+
+ /* Based on those values, calculate the maximum size of the
+ following data. This is the instruction length + the last
+ contour point + the last contour point * uint16_t * 2. */
+
+ if (glyph->number_of_contours)
+ number_of_points
+ = simple->end_pts_of_contours[glyph->number_of_contours - 1] + 1;
+ else
+ number_of_points = 0;
+
+ min_size_2 = (simple->instruction_length
+ + number_of_points
+ + (number_of_points
+ * sizeof (uint16_t) * 2));
+
+ /* Set simple->number_of_points. */
+ simple->number_of_points = number_of_points;
+
+ /* Make simple big enough. */
+ simple = xrealloc (simple, sizeof *simple + min_size + min_size_2);
+ simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+
+ /* Set the instruction data pointer and other pointers.
+ simple->instructions comes one word past number_of_contours,
+ because end_pts_of_contours also contains the instruction
+ length. */
+ simple->instructions = (uint8_t *) (simple->end_pts_of_contours
+ + glyph->number_of_contours + 1);
+ simple->flags = simple->instructions + simple->instruction_length;
+
+ /* Read instructions into the glyph. */
+ instructions_start = glyf->glyphs + offset + min_size;
+
+ if (instructions_start >= glyf->glyphs + glyf->size
+ || (instructions_start + simple->instruction_length
+ >= glyf->glyphs + glyf->size))
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ memcpy (simple->instructions, instructions_start,
+ simple->instruction_length);
+
+ /* Start reading flags. */
+ flags_start = (glyf->glyphs + offset
+ + min_size + simple->instruction_length);
+ flags_end = flags_start + number_of_points;
+
+ if (flags_start >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ i = 0;
+
+ while (flags_start < flags_end)
+ {
+ if (i == number_of_points)
+ break;
+
+ if (flags_start >= glyf->glyphs + glyf->size)
+ break;
+
+ simple->flags[i++] = *flags_start;
+
+ if (*flags_start & 010) /* REPEAT_FLAG */
+ {
+ /* The next byte specifies how many times this byte is to be
+ repeated. Check that it is in range. */
+
+ if (flags_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Repeat the current flag until
+ glyph->number_of_points. */
+
+ repeat_count = *(flags_start + 1);
+
+ while (i < number_of_points && repeat_count)
+ {
+ simple->flags[i++] = *flags_start;
+ repeat_count--;
+ }
+
+ /* Skip one byte in flags_start. */
+ flags_start++;
+ }
+
+ flags_start++;
+ }
+
+ /* If an insufficient number of flags have been read, then the
+ outline is invalid. */
+
+ if (i != number_of_points)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Now that the flags have been decoded, start decoding the
+ vectors. */
+ simple->x_coordinates = (int16_t *) (simple->flags + number_of_points);
+ vec_start = flags_start;
+ i = 0;
+ x = 0;
+
+ /* flags_start is now repurposed to act as a pointer to the flags
+ for the current vector! */
+ flags_start = simple->flags;
+
+ while (i < number_of_points)
+ {
+ delta = 0;
+
+ if ((*flags_start) & 02) /* X_SHORT_VECTOR */
+ {
+ /* The next byte is a delta to apply to the previous
+ value. Make sure it is in bounds. */
+
+ if (vec_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ delta = *vec_start++;
+
+ if (!(*flags_start & 020)) /* SAME_X */
+ delta = -delta;
+ }
+ else if (!(*flags_start & 020)) /* SAME_X */
+ {
+ /* The next word is a delta to apply to the previous value.
+ Make sure it is in bounds. */
+
+ if (vec_start + 2 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Read the unaligned word and swap it. */
+ memcpy (&delta, vec_start, sizeof delta);
+ sfnt_swap16 (&delta);
+ vec_start += 2;
+ }
+
+ /* Apply the delta and set the X value. */
+ x += delta;
+ simple->x_coordinates[i++] = x;
+ flags_start++;
+ }
+
+ /* Decode the Y vector. flags_start is again repurposed to act as a
+ pointer to the flags for the current vector. */
+ flags_start = simple->flags;
+ y = 0;
+ simple->y_coordinates = simple->x_coordinates + i;
+ i = 0;
+
+ while (i < number_of_points)
+ {
+ delta = 0;
+
+ if (*flags_start & 04) /* Y_SHORT_VECTOR */
+ {
+ /* The next byte is a delta to apply to the previous
+ value. Make sure it is in bounds. */
+
+ if (vec_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ delta = *vec_start++;
+
+ if (!(*flags_start & 040)) /* SAME_Y */
+ delta = -delta;
+ }
+ else if (!(*flags_start & 040)) /* SAME_Y */
+ {
+ /* The next word is a delta to apply to the previous value.
+ Make sure it is in bounds. */
+
+ if (vec_start + 2 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Read the unaligned word and swap it. */
+ memcpy (&delta, vec_start, sizeof delta);
+ sfnt_swap16 (&delta);
+ vec_start += 2;
+ }
+
+ /* Apply the delta and set the X value. */
+ y += delta;
+ simple->y_coordinates[i++] = y;
+ flags_start++;
+ }
+
+ /* All done. */
+ simple->y_coordinates_end = simple->y_coordinates + i;
+ glyph->simple = simple;
+ return;
+}
+
+/* Read the compound glyph outline from the glyph GLYPH from the
+ specified glyf table at the given offset. Set GLYPH->compound to a
+ non-NULL value upon success, else set it to NULL. */
+
+static void
+sfnt_read_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_glyf_table *glyf,
+ size_t offset)
+{
+ uint16_t flags, instruction_length, words[2], words4[4];
+ size_t required_bytes, num_components, i;
+ unsigned char *data, *instruction_base;
+
+ /* Assume failure for now. Figure out how many bytes have to be
+ allocated by reading the compound data. */
+ glyph->compound = NULL;
+ required_bytes = 0;
+ num_components = 0;
+ data = glyf->glyphs + offset;
+
+ /* Offset could be unaligned. */
+ do
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ return;
+
+ memcpy (&flags, data, sizeof flags);
+ sfnt_swap16 (&flags);
+ data += sizeof flags;
+
+ /* Require at least one structure to hold this data. */
+ required_bytes += sizeof (struct sfnt_compound_glyph_component);
+ num_components++;
+
+ /* Skip past unused data. */
+ data += 2;
+
+ if (flags & 01) /* ARG_1_AND_2_ARE_WORDS */
+ data += sizeof (int16_t) * 2;
+ else
+ data += sizeof (int8_t) * 2;
+
+ if (flags & 010) /* WE_HAVE_A_SCALE */
+ data += sizeof (uint16_t);
+ else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ data += sizeof (uint16_t) * 2;
+ else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ data += sizeof (uint16_t) * 4;
+ }
+ while (flags & 040); /* MORE_COMPONENTS */
+
+ if (flags & 0400) /* WE_HAVE_INSTRUCTIONS */
+ {
+ /* Figure out the instruction length. */
+ if (data + 2 > glyf->glyphs + glyf->size)
+ return;
+
+ /* Now see how much is required to hold the instruction
+ data. */
+ memcpy (&instruction_length, data,
+ sizeof instruction_length);
+ sfnt_swap16 (&instruction_length);
+ required_bytes += instruction_length;
+ data += sizeof data + instruction_length;
+ }
+
+ /* Now allocate the buffer to hold all the glyph data. */
+ glyph->compound = xmalloc (sizeof *glyph->compound
+ + required_bytes);
+ glyph->compound->components
+ = (struct sfnt_compound_glyph_component *) (glyph->compound + 1);
+ glyph->compound->num_components = num_components;
+
+ /* Figure out where instruction data starts. It comes after
+ glyph->compound->components ends. */
+ instruction_base
+ = (unsigned char *) (glyph->compound->components
+ + glyph->compound->num_components);
+
+ /* Start reading. */
+ i = 0;
+ data = glyf->glyphs + offset;
+ do
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ memcpy (&flags, data, sizeof flags);
+ sfnt_swap16 (&flags);
+ data += sizeof flags;
+ glyph->compound->components[i].flags = flags;
+
+ memcpy (&glyph->compound->components[i].glyph_index,
+ data, sizeof glyph->compound->components[i].glyph_index);
+ sfnt_swap16 (&glyph->compound->components[i].glyph_index);
+ data += sizeof glyph->compound->components[i].glyph_index;
+
+ if (flags & 01) /* ARG_1_AND_2_ARE_WORDS. */
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two words into arg1 and arg2. */
+ memcpy (words, data, sizeof words);
+ sfnt_swap16 (&words[0]);
+ sfnt_swap16 (&words[1]);
+
+ glyph->compound->components[i].argument1.c = words[0];
+ glyph->compound->components[i].argument2.c = words[1];
+ data += sizeof words;
+ }
+ else
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two bytes into arg1 and arg2. */
+ glyph->compound->components[i].argument1.a = data[0];
+ glyph->compound->components[i].argument2.a = data[1];
+ data += 2;
+ }
+
+ if (flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read one word into scale. */
+ memcpy (&glyph->compound->components[i].u.scale, data,
+ sizeof glyph->compound->components[i].u.scale);
+ sfnt_swap16 (&glyph->compound->components[i].u.scale);
+ data += sizeof glyph->compound->components[i].u.scale;
+ }
+ else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE. */
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two words into xscale and yscale. */
+ memcpy (words, data, sizeof words);
+ sfnt_swap16 (&words[0]);
+ sfnt_swap16 (&words[1]);
+
+ glyph->compound->components[i].u.a.xscale = words[0];
+ glyph->compound->components[i].u.a.yscale = words[1];
+ data += sizeof words;
+ }
+ else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ if (data + 8 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read 4 words into the transformation matrix. */
+ memcpy (words4, data, sizeof words4);
+ sfnt_swap16 (&words4[0]);
+ sfnt_swap16 (&words4[1]);
+ sfnt_swap16 (&words4[2]);
+ sfnt_swap16 (&words4[3]);
+
+ glyph->compound->components[i].u.b.xscale = words4[0];
+ glyph->compound->components[i].u.b.scale01 = words4[1];
+ glyph->compound->components[i].u.b.scale10 = words4[2];
+ glyph->compound->components[i].u.b.yscale = words4[3];
+ data += sizeof words4;
+ }
+
+ /* Record the component flags. */
+ glyph->compound->components[i].flags = flags;
+
+ i++;
+ }
+ while (flags & 040); /* MORE_COMPONENTS */
+
+ if (flags & 0400) /* WE_HAVE_INSTR */
+ {
+ /* Figure out the instruction length. */
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Now see how much is required to hold the instruction
+ data. */
+ memcpy (&glyph->compound->instruction_length,
+ data,
+ sizeof glyph->compound->instruction_length);
+ sfnt_swap16 (&glyph->compound->instruction_length);
+ data += 2;
+
+ /* Read the instructions. */
+ glyph->compound->instructions = instruction_base;
+
+ if (data + glyph->compound->instruction_length
+ > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ memcpy (instruction_base, data,
+ glyph->compound->instruction_length);
+ }
+ else
+ {
+ glyph->compound->instructions = NULL;
+ glyph->compound->instruction_length = 0;
+ }
+
+ /* Data read successfully. */
+ return;
+}
+
+/* Read the description of the glyph GLYPH_CODE from the specified
+ glyf table, using the offsets of LOCA_SHORT or LOCA_LONG, depending
+ on which is non-NULL. */
+
+TEST_STATIC struct sfnt_glyph *
+sfnt_read_glyph (sfnt_glyph glyph_code,
+ struct sfnt_glyf_table *glyf,
+ struct sfnt_loca_table_short *loca_short,
+ struct sfnt_loca_table_long *loca_long)
+{
+ struct sfnt_glyph glyph, *memory;
+ size_t offset, next_offset;
+
+ /* Check the glyph code is within bounds. */
+ if (glyph_code > 65535)
+ return NULL;
+
+ if (loca_short)
+ {
+ /* Check that the glyph is within bounds. glyph_code + 1 is the
+ entry in the table which defines the length of the glyph. */
+ if (glyph_code + 1 >= loca_short->num_offsets)
+ return NULL;
+
+ offset = loca_short->offsets[glyph_code] * 2;
+ next_offset = loca_short->offsets[glyph_code + 1] * 2;
+ }
+ else if (loca_long)
+ {
+ if (glyph_code + 1 >= loca_long->num_offsets)
+ return NULL;
+
+ offset = loca_long->offsets[glyph_code];
+ next_offset = loca_long->offsets[glyph_code + 1];
+ }
+ else
+ abort ();
+
+ /* If offset - next_offset is 0, then the glyph is empty. Its
+ horizontal advance may still be provided by the hmtx table. */
+
+ if (offset == next_offset)
+ {
+ glyph.number_of_contours = 0;
+ glyph.xmin = 0;
+ glyph.ymin = 0;
+ glyph.xmax = 0;
+ glyph.ymax = 0;
+ glyph.simple = xmalloc (sizeof *glyph.simple);
+ glyph.compound = NULL;
+ memset (glyph.simple, 0, sizeof *glyph.simple);
+ memory = xmalloc (sizeof *memory);
+ *memory = glyph;
+ return memory;
+ }
+
+ /* Verify that GLYF is big enough to hold a glyph at OFFSET. */
+ if (glyf->size < offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword))
+ return NULL;
+
+ /* Copy over the glyph data. */
+ memcpy (&glyph, glyf->glyphs + offset,
+ SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ /* Swap the glyph data. */
+ sfnt_swap16 (&glyph.number_of_contours);
+ sfnt_swap16 (&glyph.xmin);
+ sfnt_swap16 (&glyph.ymin);
+ sfnt_swap16 (&glyph.xmax);
+ sfnt_swap16 (&glyph.ymax);
+
+ /* This is set later on after `sfnt_vary_X_glyph'. */
+ glyph.advance_distortion = 0;
+ glyph.origin_distortion = 0;
+
+ /* Figure out what needs to be read based on
+ glyph.number_of_contours. */
+ if (glyph.number_of_contours >= 0)
+ {
+ /* Read the simple glyph. */
+
+ glyph.compound = NULL;
+ sfnt_read_simple_glyph (&glyph, glyf,
+ offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ if (glyph.simple)
+ {
+ memory = xmalloc (sizeof glyph);
+ *memory = glyph;
+
+ return memory;
+ }
+ }
+ else
+ {
+ /* Read the compound glyph. */
+
+ glyph.simple = NULL;
+ sfnt_read_compound_glyph (&glyph, glyf,
+ offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ if (glyph.compound)
+ {
+ memory = xmalloc (sizeof glyph);
+ *memory = glyph;
+
+ return memory;
+ }
+ }
+
+ return NULL;
+}
+
+/* Free a glyph returned from sfnt_read_glyph. GLYPH may be NULL. */
+
+TEST_STATIC void
+sfnt_free_glyph (struct sfnt_glyph *glyph)
+{
+ if (!glyph)
+ return;
+
+ xfree (glyph->simple);
+ xfree (glyph->compound);
+ xfree (glyph);
+}
+
+
+
+/* Glyph outline decomposition. */
+
+/* Apply the transform in the compound glyph component COMPONENT to
+ the array of points of length NUM_COORDINATES given as X and Y.
+
+ Also, apply the fixed point offsets X_OFF and Y_OFF to each X and Y
+ coordinate.
+
+ See sfnt_decompose_compound_glyph for an explanation of why offsets
+ might be applied here, and not while reading the subglyph
+ itself. */
+
+static void
+sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component,
+ sfnt_fixed *restrict x, sfnt_fixed *restrict y,
+ size_t num_coordinates,
+ sfnt_fixed x_off, sfnt_fixed y_off)
+{
+ double m1, m2, m3;
+ double m4, m5, m6;
+ size_t i;
+
+ if (component->flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.scale / 16384.0;
+ y[i] *= component->u.scale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.a.xscale / 16384.0;
+ y[i] *= component->u.a.yscale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is
+ simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+ 1.0. See the definition of matrix3x3 in image.c for some
+ more explanations about this.) */
+ m1 = component->u.b.xscale / 16384.0;
+ m2 = component->u.b.scale01 / 16384.0;
+ m3 = 0;
+ m4 = component->u.b.scale10 / 16384.0;
+ m5 = component->u.b.yscale / 16384.0;
+ m6 = 0;
+
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+ y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+}
+
+struct sfnt_compound_glyph_context
+{
+ /* Arrays of points. The underlying type is actually sfnt_f26dot6
+ when instructing a compound glyph. */
+ sfnt_fixed *x_coordinates, *y_coordinates;
+
+ /* Array of flags for the points. */
+ unsigned char *flags;
+
+ /* Number of points in that array, and the size of that array. */
+ size_t num_points, points_size;
+
+ /* Array of contour end points. */
+ size_t *contour_end_points;
+
+ /* Number of elements in and the size of that array. */
+ size_t num_end_points, end_points_size;
+};
+
+/* Extend the arrays inside the compound glyph decomposition context
+ CONTEXT. NUMBER_OF_CONTOURS is the number of contours to add.
+ NUMBER_OF_POINTS is the number of points to add.
+
+ Return pointers to the beginning of the extension in *X_BASE,
+ *Y_BASE, *FLAGS_BASE and *CONTOUR_BASE. Value zero upon success,
+ and something else on failure. */
+
+static int
+sfnt_expand_compound_glyph_context (struct sfnt_compound_glyph_context *context,
+ size_t number_of_contours,
+ size_t number_of_points,
+ sfnt_fixed **x_base, sfnt_fixed **y_base,
+ unsigned char **flags_base,
+ size_t **contour_base)
+{
+ size_t size_bytes;
+
+ /* Add each field while checking for overflow. */
+ if (INT_ADD_WRAPV (number_of_contours, context->num_end_points,
+ &context->num_end_points))
+ return 1;
+
+ if (INT_ADD_WRAPV (number_of_points, context->num_points,
+ &context->num_points))
+ return 1;
+
+ /* Reallocate each array to the new size if necessary. */
+ if (context->points_size < context->num_points)
+ {
+ if (INT_MULTIPLY_WRAPV (context->num_points, 2,
+ &context->points_size))
+ context->points_size = context->num_points;
+
+ if (INT_MULTIPLY_WRAPV (context->points_size,
+ sizeof *context->x_coordinates,
+ &size_bytes))
+ return 1;
+
+ context->x_coordinates = xrealloc (context->x_coordinates,
+ size_bytes);
+ context->y_coordinates = xrealloc (context->y_coordinates,
+ size_bytes);
+ context->flags = xrealloc (context->flags,
+ context->points_size);
+ }
+
+ /* Set x_base and y_base. */
+ *x_base = (context->x_coordinates
+ + context->num_points
+ - number_of_points);
+ *y_base = (context->y_coordinates
+ + context->num_points
+ - number_of_points);
+ *flags_base = (context->flags
+ + context->num_points
+ - number_of_points);
+
+ if (context->end_points_size < context->num_end_points)
+ {
+ if (INT_MULTIPLY_WRAPV (context->num_end_points, 2,
+ &context->end_points_size))
+ context->end_points_size = context->num_end_points;
+
+ if (INT_MULTIPLY_WRAPV (context->end_points_size,
+ sizeof *context->contour_end_points,
+ &size_bytes))
+ return 1;
+
+ context->contour_end_points
+ = xrealloc (context->contour_end_points,
+ size_bytes);
+ }
+
+ /* Set contour_base. */
+ *contour_base = (context->contour_end_points
+ + context->num_end_points
+ - number_of_contours);
+ return 0;
+}
+
+/* Round the 16.16 fixed point number NUMBER to the nearest integral
+ value. */
+
+static int32_t
+sfnt_round_fixed (int32_t number)
+{
+ /* Add 0.5... */
+ number += (1 << 15);
+
+ /* Remove the fractional. */
+ return number & ~0xffff;
+}
+
+/* Decompose GLYPH, a compound glyph, into an array of points and
+ contours.
+
+ CONTEXT should be zeroed and put on the stack. OFF_X and OFF_Y
+ should be zero, as should RECURSION_COUNT. GET_GLYPH and
+ FREE_GLYPH, along with DCONTEXT, mean the same as in
+ sfnt_decompose_glyph. */
+
+static int
+sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_compound_glyph_context *context,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ sfnt_fixed off_x, sfnt_fixed off_y,
+ int recursion_count,
+ void *dcontext)
+{
+ struct sfnt_glyph *subglyph;
+ int i, j, rc;
+ bool need_free;
+ struct sfnt_compound_glyph_component *component;
+ sfnt_fixed x, y, xtemp, ytemp;
+ size_t point, point2, index;
+ uint16_t last_point, number_of_contours;
+ sfnt_fixed *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+ size_t base_index, contour_start;
+ bool defer_offsets;
+
+ /* Set up the base index. This is the index from where on point
+ renumbering starts.
+
+ In other words, point 0 in this glyph will be 0 + base_index,
+ point 1 will be 1 + base_index, and so on. */
+ base_index = context->num_points;
+
+ /* Prevent infinite loops. */
+ if (recursion_count > 12)
+ return 1;
+
+ /* Don't defer offsets. */
+ defer_offsets = false;
+
+ for (j = 0; j < glyph->compound->num_components; ++j)
+ {
+ /* Look up the associated subglyph. */
+ component = &glyph->compound->components[j];
+ subglyph = get_glyph (component->glyph_index,
+ dcontext, &need_free);
+
+ if (!subglyph)
+ return -1;
+
+ /* Record the size of the point array before expansion. This
+ will be the base to apply to all points coming from this
+ subglyph. */
+ contour_start = context->num_points;
+
+ /* Compute the offset for the component. */
+ if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+ {
+ /* Component offsets are X/Y values as opposed to points
+ GLYPH. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ /* X and Y are signed bytes. */
+ x = component->argument1.b * 65536;
+ y = component->argument2.b * 65536;
+ }
+ else
+ {
+ /* X and Y are signed words. */
+ x = component->argument1.d * 65536;
+ y = component->argument2.d * 65536;
+ }
+
+ /* If there is some kind of scale and component offsets are
+ scaled, then apply the transform to the offset. */
+ if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+ sfnt_transform_coordinates (component, &x, &y, 1,
+ 0, 0);
+
+ if (component->flags & 04) /* ROUND_XY_TO_GRID */
+ {
+ x = sfnt_round_fixed (x);
+ y = sfnt_round_fixed (y);
+ }
+ }
+ else
+ {
+ /* The offset is determined by matching a point location in
+ a preceeding component with a point location in the
+ current component. The index of the point in the
+ previous component can be determined by adding
+ component->argument1.a or component->argument1.c to
+ point. argument2 contains the index of the point in the
+ current component. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ point = base_index + component->argument1.a;
+ point2 = component->argument2.a;
+ }
+ else
+ {
+ point = base_index + component->argument1.c;
+ point2 = component->argument2.c;
+ }
+
+ /* Now, check that the anchor point specified lies inside
+ the glyph. */
+
+ if (point >= contour_start)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ if (!subglyph->compound)
+ {
+ if (point2 >= subglyph->simple->number_of_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* Get the points and use them to compute the offsets. */
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - subglyph->simple->x_coordinates[point2] * 65536);
+ y = (ytemp - subglyph->simple->y_coordinates[point2] * 65536);
+ }
+ else
+ {
+ /* First, set offsets to 0, because it is not yet
+ possible to determine the position of the anchor
+ point in the child. */
+ x = 0;
+ y = 0;
+
+ /* Set a flag which indicates that offsets must be
+ resolved from the child glyph after it is loaded, but
+ before it is incorporated into the parent glyph. */
+ defer_offsets = true;
+ }
+ }
+
+ if (subglyph->simple)
+ {
+ /* Simple subglyph. Copy over the points and contours, and
+ transform them. */
+ if (subglyph->number_of_contours)
+ {
+ index = subglyph->number_of_contours - 1;
+ last_point
+ = subglyph->simple->end_pts_of_contours[index];
+ number_of_contours = subglyph->number_of_contours;
+
+
+ /* Grow various arrays. */
+ rc = sfnt_expand_compound_glyph_context (context,
+ /* Number of
+ new contours
+ required. */
+ number_of_contours,
+ /* Number of new
+ points
+ required. */
+ last_point + 1,
+ &x_base,
+ &y_base,
+ &flags_base,
+ &contour_base);
+ if (rc)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ for (i = 0; i <= last_point; ++i)
+ {
+ x_base[i] = ((subglyph->simple->x_coordinates[i] * 65536)
+ + off_x + x);
+ y_base[i] = ((subglyph->simple->y_coordinates[i] * 65536)
+ + off_y + y);
+ flags_base[i] = subglyph->simple->flags[i];
+ }
+
+ /* Apply the transform to the points. */
+ sfnt_transform_coordinates (component, x_base, y_base,
+ last_point + 1, 0, 0);
+
+ /* Copy over the contours. */
+ for (i = 0; i < number_of_contours; ++i)
+ contour_base[i] = (contour_start
+ + subglyph->simple->end_pts_of_contours[i]);
+ }
+ }
+ else
+ {
+ /* Compound subglyph. Decompose the glyph recursively, and
+ then apply the transform. */
+ rc = sfnt_decompose_compound_glyph (subglyph,
+ context,
+ get_glyph,
+ free_glyph,
+ off_x + x,
+ off_y + y,
+ recursion_count + 1,
+ dcontext);
+
+ if (rc)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* When an anchor point is being used to translate the
+ glyph, and the subglyph in question is actually a
+ compound glyph, it is impossible to know which offset to
+ use until the compound subglyph has actually been
+ loaded.
+
+ As a result, the offset is calculated here, using the
+ points in the loaded child compound glyph. But first, X
+ and Y must be reset to 0, as otherwise the translation
+ might be applied twice if defer_offsets is not set. */
+
+ x = 0;
+ y = 0;
+
+ if (defer_offsets)
+ {
+ /* Renumber the non renumbered point2 to point into the
+ decomposed component. */
+ point2 += contour_start;
+
+ /* Next, check that the non-renumbered point being
+ anchored lies inside the glyph data that was
+ decomposed. */
+
+ if (point2 >= context->num_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* Get the points and use them to compute the
+ offsets. */
+
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - context->x_coordinates[point2]);
+ y = (ytemp - context->y_coordinates[point2]);
+ }
+
+ sfnt_transform_coordinates (component,
+ context->x_coordinates + contour_start,
+ context->y_coordinates + contour_start,
+ contour_start - context->num_points,
+ x, y);
+ }
+
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+ }
+
+ /* Decomposition is complete. CONTEXT now contains the adjusted
+ outlines of the entire compound glyph. */
+ return 0;
+}
+
+/* Linear-interpolate to a point halfway between the points specified
+ by CONTROL1 and CONTROL2. Put the result in RESULT. */
+
+static void
+sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2,
+ struct sfnt_point *result)
+{
+ result->x = control1->x + ((control2->x - control1->x) >> 1);
+ result->y = control1->y + ((control2->y - control1->y) >> 1);
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+ HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+ with DCONTEXT as an argument. Apply SCALE to each point; SCALE
+ should be the factor necessary to turn points into 16.16 fixed
+ point.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+sfnt_decompose_glyph_1 (size_t here, size_t last,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext,
+ sfnt_fword *x,
+ sfnt_fword *y, unsigned char *flags,
+ int scale)
+{
+ struct sfnt_point control1, control2, start, mid;
+ size_t i;
+
+ /* The contour is empty. */
+
+ if (here == last)
+ return 1;
+
+ /* Move the pen to the start of the contour. Apparently some fonts
+ have off the curve points as the start of a contour, so when that
+ happens lerp between the first and last points. */
+
+ if (flags[here] & 01) /* On Curve */
+ {
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ start = control1;
+ }
+ else if (flags[last] & 01)
+ {
+ /* Start at the last point if it is on the curve. Here, the
+ start really becomes the middle of a spline. */
+ control1.x = x[last] * scale;
+ control1.y = y[last] * scale;
+ start = control1;
+
+ /* Curve back one point early. */
+ last -= 1;
+ here -= 1;
+ }
+ else
+ {
+ /* Lerp between the start and the end. */
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ control2.x = x[last] * scale;
+ control2.y = y[last] * scale;
+ sfnt_lerp_half (&control1, &control2, &start);
+
+ /* In either of these cases, start iterating from just here as
+ opposed to here + 1, since logically the contour now starts
+ from the last curve. */
+ here -= 1;
+ }
+
+ /* Move to the start. */
+ move_to (start, dcontext);
+
+ /* Now handle each point between here + 1 and last. */
+
+ i = here;
+ while (++i <= last)
+ {
+ /* If the point is on the curve, then draw a line here from the
+ last control point. */
+
+ if (flags[i] & 01)
+ {
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ line_to (control1, dcontext);
+
+ /* Move to the next point. */
+ continue;
+ }
+
+ /* Off the curve points are more interesting. They are handled
+ one by one, with points in between being interpolated, until
+ either the last point is reached or an on-curve point is
+ processed. First, load the initial control points. */
+
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ while (++i <= last)
+ {
+ /* Load this point. */
+ control2.x = x[i] * scale;
+ control2.y = y[i] * scale;
+
+ /* If this point is on the curve, curve directly to this
+ point. */
+
+ if (flags[i] & 01)
+ {
+ curve_to (control1, control2, dcontext);
+ goto continue_loop;
+ }
+
+ /* Calculate the point between here and the previous
+ point. */
+ sfnt_lerp_half (&control1, &control2, &mid);
+
+ /* Curve over there. */
+ curve_to (control1, mid, dcontext);
+
+ /* Reload the control point. */
+ control1 = control2;
+ }
+
+ /* Close the contour by curving back to start. */
+ curve_to (control1, start, dcontext);
+
+ /* Don't close the contour twice. */
+ goto exit;
+
+ continue_loop:
+ continue;
+ }
+
+ /* Close the contour with a line back to start. */
+ line_to (start, dcontext);
+
+ exit:
+ return 0;
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+ HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+ with DCONTEXT as an argument. Apply SCALE to each point; SCALE
+ should be the factor necessary to turn points into 16.16 fixed
+ point.
+
+ This is the version of sfnt_decompose_glyph_1 which takes
+ sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+sfnt_decompose_glyph_2 (size_t here, size_t last,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext,
+ sfnt_fixed *x,
+ sfnt_fixed *y, unsigned char *flags,
+ int scale)
+{
+ struct sfnt_point control1, control2, start, mid;
+ size_t i;
+
+ /* The contour is empty. */
+
+ if (here == last)
+ return 1;
+
+ /* Move the pen to the start of the contour. Apparently some fonts
+ have off the curve points as the start of a contour, so when that
+ happens lerp between the first and last points. */
+
+ if (flags[here] & 01) /* On Curve */
+ {
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ start = control1;
+ }
+ else if (flags[last] & 01)
+ {
+ /* Start at the last point if it is on the curve. Here, the
+ start really becomes the middle of a spline. */
+ control1.x = x[last] * scale;
+ control1.y = y[last] * scale;
+ start = control1;
+
+ /* Curve back one point early. */
+ last -= 1;
+ here -= 1;
+ }
+ else
+ {
+ /* Lerp between the start and the end. */
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ control2.x = x[last] * scale;
+ control2.y = y[last] * scale;
+ sfnt_lerp_half (&control1, &control2, &start);
+
+ /* In either of these cases, start iterating from just here as
+ opposed to here + 1, since logically the contour now starts
+ from the last curve. */
+ here -= 1;
+ }
+
+ /* Move to the start. */
+ move_to (start, dcontext);
+
+ /* Now handle each point between here + 1 and last. */
+
+ i = here;
+ while (++i <= last)
+ {
+ /* If the point is on the curve, then draw a line here from the
+ last control point. */
+
+ if (flags[i] & 01)
+ {
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ line_to (control1, dcontext);
+
+ /* Move to the next point. */
+ continue;
+ }
+
+ /* Off the curve points are more interesting. They are handled
+ one by one, with points in between being interpolated, until
+ either the last point is reached or an on-curve point is
+ processed. First, load the initial control points. */
+
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ while (++i <= last)
+ {
+ /* Load this point. */
+ control2.x = x[i] * scale;
+ control2.y = y[i] * scale;
+
+ /* If this point is on the curve, curve directly to this
+ point. */
+
+ if (flags[i] & 01)
+ {
+ curve_to (control1, control2, dcontext);
+ goto continue_loop;
+ }
+
+ /* Calculate the point between here and the previous
+ point. */
+ sfnt_lerp_half (&control1, &control2, &mid);
+
+ /* Curve over there. */
+ curve_to (control1, mid, dcontext);
+
+ /* Reload the control point. */
+ control1 = control2;
+ }
+
+ /* Close the contour by curving back to start. */
+ curve_to (control1, start, dcontext);
+
+ /* Don't close the contour twice. */
+ goto exit;
+
+ continue_loop:
+ continue;
+ }
+
+ /* Close the contour with a line back to start. */
+ line_to (start, dcontext);
+
+ exit:
+ return 0;
+}
+
+/* Decompose GLYPH into its individual components. Call MOVE_TO to
+ move to a specific location. For each line encountered, call
+ LINE_TO to draw a line to that location. For each spline
+ encountered, call CURVE_TO to draw the curves comprising the
+ spline.
+
+ If GLYPH is compound, use GET_GLYPH to obtain subglyphs. PROC must
+ return whether or not FREE_GLYPH will be called with the glyph
+ after sfnt_decompose_glyph is done with it.
+
+ All functions will be called with DCONTEXT as an argument.
+
+ The winding rule used to fill the resulting lines is described in
+ chapter 2 of the TrueType reference manual, under the heading
+ "distinguishing the inside from the outside of a glyph."
+
+ Value is 0 upon success, or some non-zero value upon failure, which
+ can happen if the glyph is invalid. */
+
+static int
+sfnt_decompose_glyph (struct sfnt_glyph *glyph,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ void *dcontext)
+{
+ size_t here, last, n;
+ struct sfnt_compound_glyph_context context;
+
+ if (glyph->simple)
+ {
+ if (!glyph->number_of_contours)
+ /* No contours. Nothing needs to be decomposed. */
+ return 0;
+
+ here = 0;
+
+ for (n = 0; n < glyph->number_of_contours; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = glyph->simple->end_pts_of_contours[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= glyph->simple->number_of_points)
+ return 1;
+
+ /* Now perform the decomposition. */
+ if (sfnt_decompose_glyph_1 (here, last, move_to,
+ line_to, curve_to,
+ dcontext,
+ glyph->simple->x_coordinates,
+ glyph->simple->y_coordinates,
+ glyph->simple->flags,
+ 65536))
+ return 1;
+
+ /* Move forward to the start of the next contour. */
+ here = last + 1;
+ }
+
+ return 0;
+ }
+
+ /* Decompose the specified compound glyph. */
+ memset (&context, 0, sizeof context);
+
+ if (sfnt_decompose_compound_glyph (glyph, &context,
+ get_glyph, free_glyph,
+ 0, 0, 0, dcontext))
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+
+ return 1;
+ }
+
+ /* Now, generate the outlines. */
+
+ if (!context.num_end_points)
+ /* No contours. */
+ goto early;
+
+ here = 0;
+
+ for (n = 0; n < context.num_end_points; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = context.contour_end_points[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= context.num_points)
+ goto fail;
+
+ /* Now perform the decomposition. */
+ if (sfnt_decompose_glyph_2 (here, last, move_to,
+ line_to, curve_to,
+ dcontext,
+ context.x_coordinates,
+ context.y_coordinates,
+ context.flags, 1))
+ goto fail;
+
+ /* Move forward. */
+ here = last + 1;
+ }
+
+ early:
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return 0;
+
+ fail:
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return 1;
+}
+
+struct sfnt_build_glyph_outline_context
+{
+ /* The outline being built. */
+ struct sfnt_glyph_outline *outline;
+
+ /* Factor to multiply positions by to get the pixel width. */
+ sfnt_fixed factor;
+
+ /* The position of the pen in 16.16 fixed point format. */
+ sfnt_fixed x, y;
+};
+
+/* Global state for sfnt_build_glyph_outline and related
+ functions. */
+static struct sfnt_build_glyph_outline_context build_outline_context;
+
+/* Append the given three words FLAGS, X, and Y to the outline
+ currently being built. Value is the new pointer to outline
+ memory. */
+
+static struct sfnt_glyph_outline *
+sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y)
+{
+ struct sfnt_glyph_outline *outline;
+
+ if (x == build_outline_context.x
+ && y == build_outline_context.y)
+ /* Ignore redundant motion. */
+ return build_outline_context.outline;
+
+ outline = build_outline_context.outline;
+ outline->outline_used++;
+
+ /* See if the outline has to be extended. Checking for overflow
+ should not be necessary. */
+
+ if (outline->outline_used > outline->outline_size)
+ {
+ outline->outline_size = outline->outline_used * 2;
+
+ /* Extend the outline to some size past the new size. */
+ outline = xrealloc (outline, (sizeof *outline
+ + (outline->outline_size
+ * sizeof *outline->outline)));
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+ }
+
+ /* Write the outline data. */
+ outline->outline[outline->outline_used - 1].flags = flags;
+ outline->outline[outline->outline_used - 1].x = x;
+ outline->outline[outline->outline_used - 1].y = y;
+
+ /* Extend outline bounding box. */
+
+ if (outline->outline_used == 1)
+ {
+ /* These are the first points in the outline. */
+ outline->xmin = outline->xmax = x;
+ outline->ymin = outline->ymax = y;
+ }
+ else
+ {
+ outline->xmin = MIN ((sfnt_fixed) x, outline->xmin);
+ outline->ymin = MIN ((sfnt_fixed) y, outline->ymin);
+ outline->xmax = MAX ((sfnt_fixed) x, outline->xmax);
+ outline->ymax = MAX ((sfnt_fixed) y, outline->ymax);
+ }
+
+ return outline;
+}
+
+#ifndef INT64_MAX
+
+/* 64 bit integer type. */
+
+struct sfnt_large_integer
+{
+ unsigned int high, low;
+};
+
+/* Calculate (A * B), placing the result in *VALUE. */
+
+static void
+sfnt_multiply_divide_1 (unsigned int a, unsigned int b,
+ struct sfnt_large_integer *value)
+{
+ unsigned int lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+ lo1 = a & 0x0000ffffu;
+ hi1 = a >> 16;
+ lo2 = b & 0x0000ffffu;
+ hi2 = b >> 16;
+
+ lo = lo1 * lo2;
+ i1 = lo1 * hi2;
+ i2 = lo2 * hi1;
+ hi = hi1 * hi2;
+
+ /* Check carry overflow of i1 + i2. */
+ i1 += i2;
+ hi += (unsigned int) (i1 < i2) << 16;
+
+ hi += i1 >> 16;
+ i1 = i1 << 16;
+
+ /* Check carry overflow of i1 + lo. */
+ lo += i1;
+ hi += (lo < i1);
+
+ value->low = lo;
+ value->high = hi;
+}
+
+/* Count the number of most significant zero bits in N. */
+
+static unsigned int
+sfnt_count_leading_zero_bits (unsigned int n)
+{
+ int shift;
+
+ shift = 0;
+
+ if (n & 0xffff0000ul)
+ {
+ n >>= 16;
+ shift += 16;
+ }
+
+ if (n & 0x0000ff00ul)
+ {
+ n >>= 8;
+ shift += 8;
+ }
+
+ if (n & 0x000000f0ul)
+ {
+ n >>= 4;
+ shift += 4;
+ }
+
+ if (n & 0x0000000cul)
+ {
+ n >>= 2;
+ shift += 2;
+ }
+
+ if (n & 0x00000002ul)
+ shift += 1;
+
+ return shift;
+}
+
+/* Calculate AB / C. Value is a 32 bit unsigned integer. */
+
+static unsigned int
+sfnt_multiply_divide_2 (struct sfnt_large_integer *ab,
+ unsigned int c)
+{
+ unsigned int hi, lo;
+ int i;
+ unsigned int r, q; /* Remainder and quotient. */
+
+ hi = ab->high;
+ lo = ab->low;
+
+ i = 31 - sfnt_count_leading_zero_bits (hi);
+ r = (hi << i) | (lo >> (32 - i));
+ lo <<= i;
+ q = r / c;
+ r -= q * c;
+ i = 32 - i;
+
+ do
+ {
+ q <<= 1;
+ r = (r << 1) | (lo >> 31);
+ lo <<= 1;
+
+ if (r >= c)
+ {
+ r -= c;
+ q |= 1;
+ }
+ }
+ while (--i);
+
+ return q;
+}
+
+#endif
+
+/* Calculate (A * B) / C with no rounding and return the result, using
+ a 64 bit integer if necessary. */
+
+static unsigned int
+sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c)
+{
+#ifndef INT64_MAX
+ struct sfnt_large_integer temp;
+
+ sfnt_multiply_divide_1 (a, b, &temp);
+ return sfnt_multiply_divide_2 (&temp, c);
+#else
+ uint64_t temp;
+
+ temp = (uint64_t) a * (uint64_t) b;
+ return temp / c;
+#endif
+}
+
+#ifndef INT64_MAX
+
+/* Add the specified unsigned 32-bit N to the large integer
+ INTEGER. */
+
+static void
+sfnt_large_integer_add (struct sfnt_large_integer *integer,
+ uint32_t n)
+{
+ struct sfnt_large_integer number;
+
+ number.low = integer->low + n;
+ number.high = integer->high + (number.low
+ < integer->low);
+
+ *integer = number;
+}
+
+/* Calculate (A * B) / C, rounding the result with a threshold of N.
+ Use a 64 bit temporary. */
+
+static unsigned int
+sfnt_multiply_divide_round (unsigned int a, unsigned int b,
+ unsigned int n, unsigned int c)
+{
+ struct sfnt_large_integer temp;
+
+ sfnt_multiply_divide_1 (a, b, &temp);
+ sfnt_large_integer_add (&temp, n);
+ return sfnt_multiply_divide_2 (&temp, c);
+}
+
+#endif /* INT64_MAX */
+
+/* The same as sfnt_multiply_divide, but handle signed values
+ instead. */
+
+MAYBE_UNUSED static int
+sfnt_multiply_divide_signed (int a, int b, int c)
+{
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ if (c < 0)
+ sign = -sign;
+
+ return (sfnt_multiply_divide (abs (a), abs (b), abs (c))
+ * sign);
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y. Return the
+ result regardless of overflow. */
+
+static sfnt_fixed
+sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) x * (int64_t) y;
+
+ /* This can be done quickly with int64_t. */
+ return product / (int64_t) 65536;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (x), abs (y),
+ 65536) * sign;
+#endif
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y, with rounding
+ of the result. */
+
+static sfnt_fixed
+sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t product, round;
+
+ product = (int64_t) x * (int64_t) y;
+ round = product < 0 ? -32768 : 32768;
+
+ /* This can be done quickly with int64_t. */
+ return (product + round) / (int64_t) 65536;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide_round (abs (x), abs (y),
+ 32768, 65536) * sign;
+#endif
+}
+
+/* Set the pen size to the specified point and return. POINT will be
+ scaled up to the pixel size. */
+
+static void
+sfnt_move_to_and_build (struct sfnt_point point, void *dcontext)
+{
+ sfnt_fixed x, y;
+
+ x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+ y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+ build_outline_context.outline = sfnt_build_append (0, x, y);
+ build_outline_context.x = x;
+ build_outline_context.y = y;
+}
+
+/* Record a line to the specified point and return. POINT will be
+ scaled up to the pixel size. */
+
+static void
+sfnt_line_to_and_build (struct sfnt_point point, void *dcontext)
+{
+ sfnt_fixed x, y;
+
+ x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+ y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+ build_outline_context.outline
+ = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+ x, y);
+ build_outline_context.x = x;
+ build_outline_context.y = y;
+}
+
+/* Divide the two 16.16 fixed point numbers X and Y. Return the
+ result regardless of overflow. */
+
+static sfnt_fixed
+sfnt_div_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t result;
+
+ result = ((int64_t) x * 65536) / y;
+
+ return result;
+#else
+ int sign;
+ unsigned int a, b;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ a = abs (x);
+ b = abs (y);
+
+ return sfnt_multiply_divide (a, 65536, b) * sign;
+#endif
+}
+
+/* Return the ceiling value of the specified fixed point number X. */
+
+static sfnt_fixed
+sfnt_ceil_fixed (sfnt_fixed x)
+{
+ return (x + 0177777) & 037777600000;
+}
+
+/* Return the floor value of the specified fixed point number X. */
+
+static sfnt_fixed
+sfnt_floor_fixed (sfnt_fixed x)
+{
+ return x & 037777600000;
+}
+
+/* Given a curve consisting of three points CONTROL0, CONTROL1 and
+ ENDPOINT, return whether or not the curve is sufficiently small to
+ be approximated by a line between CONTROL0 and ENDPOINT. */
+
+static bool
+sfnt_curve_is_flat (struct sfnt_point control0,
+ struct sfnt_point control1,
+ struct sfnt_point endpoint)
+{
+ struct sfnt_point g, h;
+
+ g.x = control1.x - control0.x;
+ g.y = control1.y - control0.y;
+ h.x = endpoint.x - control0.x;
+ h.y = endpoint.y - control0.y;
+
+ /* 2.0 is a constant describing the area covered at which point the
+ curve is considered "flat". */
+ return (abs (sfnt_mul_fixed (g.x, h.y)
+ - sfnt_mul_fixed (g.y, h.x))
+ <= 0400000);
+}
+
+/* Recursively split the splines in the bezier curve formed from
+ CONTROL0, CONTROL1 and ENDPOINT until the area between the curve's
+ two ends is small enough to be considered ``flat''. Then, turn
+ those ``flat'' curves into lines. */
+
+static void
+sfnt_curve_to_and_build_1 (struct sfnt_point control0,
+ struct sfnt_point control1,
+ struct sfnt_point endpoint)
+{
+ struct sfnt_point ab, bc, abbc;
+
+ /* control0, control and endpoint make up the spline. Figure out
+ its distance from a line. */
+ if (sfnt_curve_is_flat (control0, control1, endpoint))
+ {
+ /* Draw a line to endpoint. */
+ build_outline_context.outline
+ = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+ endpoint.x, endpoint.y);
+ build_outline_context.x = endpoint.x;
+ build_outline_context.y = endpoint.y;
+ }
+ else
+ {
+ /* Calculate new control points.
+ Maybe apply a recursion limit here? */
+ sfnt_lerp_half (&control0, &control1, &ab);
+ sfnt_lerp_half (&control1, &endpoint, &bc);
+ sfnt_lerp_half (&ab, &bc, &abbc);
+
+ /* Keep splitting until a flat enough spline results. */
+ sfnt_curve_to_and_build_1 (control0, ab, abbc);
+
+ /* Then go on with the spline between control1 and endpoint. */
+ sfnt_curve_to_and_build_1 (abbc, bc, endpoint);
+ }
+}
+
+/* Scale and decompose the specified bezier curve into individual
+ lines. Then, record each of those lines into the outline being
+ built. */
+
+static void
+sfnt_curve_to_and_build (struct sfnt_point control,
+ struct sfnt_point endpoint,
+ void *dcontext)
+{
+ struct sfnt_point control0;
+
+ control0.x = build_outline_context.x;
+ control0.y = build_outline_context.y;
+ control.x = sfnt_mul_fixed (control.x,
+ build_outline_context.factor);
+ control.y = sfnt_mul_fixed (control.y,
+ build_outline_context.factor);
+ endpoint.x = sfnt_mul_fixed (endpoint.x,
+ build_outline_context.factor);
+ endpoint.y = sfnt_mul_fixed (endpoint.y,
+ build_outline_context.factor);
+
+ sfnt_curve_to_and_build_1 (control0, control, endpoint);
+}
+
+/* Non-reentrantly build the outline for the specified GLYPH at the
+ given scale factor. Return the outline data with a refcount of 0
+ upon success, or NULL upon failure.
+
+ SCALE is a scale factor that converts between em space and device
+ space.
+
+ Use the unscaled glyph METRICS to determine the origin point of the
+ outline.
+
+ Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain
+ glyphs for compound glyph subcomponents. */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_glyph_outline (struct sfnt_glyph *glyph,
+ sfnt_fixed scale,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ void *dcontext)
+{
+ struct sfnt_glyph_outline *outline;
+ int rc;
+ sfnt_fword origin;
+
+ memset (&build_outline_context, 0, sizeof build_outline_context);
+
+ /* Allocate the outline now with enough for 44 words at the end. */
+ outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+ outline->outline_size = 40;
+ outline->outline_used = 0;
+ outline->refcount = 0;
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+ /* DCONTEXT will be passed to GET_GLYPH and FREE_GLYPH, so global
+ variables must be used to communicate with the decomposition
+ functions. */
+ build_outline_context.outline = outline;
+
+ /* Clear outline bounding box. */
+ outline->xmin = 0;
+ outline->ymin = 0;
+ outline->xmax = 0;
+ outline->ymax = 0;
+
+ /* Set the scale factor. */
+ build_outline_context.factor = scale;
+
+ /* Decompose the outline. */
+ rc = sfnt_decompose_glyph (glyph, sfnt_move_to_and_build,
+ sfnt_line_to_and_build,
+ sfnt_curve_to_and_build,
+ get_glyph, free_glyph, dcontext);
+
+ /* Synchronize the outline object with what might have changed
+ inside sfnt_decompose_glyph. */
+ outline = build_outline_context.outline;
+
+ if (rc)
+ {
+ xfree (outline);
+ return NULL;
+ }
+
+ /* Compute the origin position. Note that the original glyph xmin
+ is first used to calculate the origin point, and the origin
+ distortion is applied to it to get the distorted origin. */
+
+ origin = glyph->xmin - metrics->lbearing + glyph->origin_distortion;
+ outline->origin = sfnt_mul_fixed (origin, scale);
+
+ return outline;
+}
+
+
+
+/* Glyph rasterization. The algorithm used here is fairly simple.
+ Each contour is decomposed into lines, which turn into a polygon.
+ Then, a bog standard edge filler is used to turn them into
+ spans. */
+
+/* Coverage table. This is a four dimensional array indiced by the Y,
+ then X axis fractional, shifted down to 2 bits. */
+
+static const unsigned char sfnt_poly_coverage[8][9] =
+ {
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 3, 7, 11, 15, 19, 23, 27, 31, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ };
+
+/* Return the nearest coordinate on the sample grid no less than
+ F. */
+
+static sfnt_fixed
+sfnt_poly_grid_ceil (sfnt_fixed f)
+{
+ return (((f + (SFNT_POLY_START - 1))
+ & ~(SFNT_POLY_STEP - 1)) + SFNT_POLY_START);
+}
+
+enum
+ {
+ SFNT_POLY_ALIGNMENT = 4,
+ };
+
+/* Initialize the specified RASTER in preparation for displaying spans
+ for OUTLINE, and set RASTER->refcount to 0. The caller must then
+ set RASTER->cells to a zeroed array of size RASTER->stride *
+ RASTER->height, aligned to RASTER. */
+
+TEST_STATIC void
+sfnt_prepare_raster (struct sfnt_raster *raster,
+ struct sfnt_glyph_outline *outline)
+{
+ raster->width
+ = (sfnt_ceil_fixed (outline->xmax)
+ - sfnt_floor_fixed (outline->xmin)) / 65536;
+ raster->height
+ = (sfnt_ceil_fixed (outline->ymax)
+ - sfnt_floor_fixed (outline->ymin)) / 65536;
+ raster->refcount = 0;
+
+ /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */
+ raster->stride = ((raster->width
+ + (SFNT_POLY_ALIGNMENT - 1))
+ & ~(SFNT_POLY_ALIGNMENT - 1));
+
+ /* Apply outline->origin. This is 0 by convention in most fonts.
+ However, variable fonts typically change this as variations are
+ applied. */
+ raster->offx = sfnt_floor_fixed (outline->xmin
+ - outline->origin) / 65536;
+ raster->offy = sfnt_floor_fixed (outline->ymin) / 65536;
+}
+
+typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t,
+ void *);
+typedef void (*sfnt_span_proc) (struct sfnt_edge *, sfnt_fixed, void *);
+
+/* Move EDGE->x forward, assuming that the scanline has moved upwards
+ by SFNT_POLY_STEP. */
+
+static void
+sfnt_step_edge (struct sfnt_edge *edge)
+{
+ /* Add step. */
+ edge->x += edge->step_x;
+}
+
+/* Build a list of edges for each contour in OUTLINE, applying xmin
+ and ymin as the offset to each edge. Call EDGE_PROC with DCONTEXT
+ and the resulting edges as arguments. It is OK to modify the edges
+ given to EDGE_PROC. Align all edges to the sub-pixel grid. */
+
+static void
+sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
+ sfnt_edge_proc edge_proc, void *dcontext)
+{
+ struct sfnt_edge *edges;
+ size_t i, edge, next_vertex;
+ sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
+ size_t top, bottom, y;
+
+ edges = alloca (outline->outline_used * sizeof *edges);
+ edge = 0;
+
+ /* ymin and xmin must be the same as the offset used to set offy and
+ offx in rasters. */
+ ymin = sfnt_floor_fixed (outline->ymin);
+ xmin = sfnt_floor_fixed (outline->xmin);
+
+ for (i = 0; i < outline->outline_used; ++i)
+ {
+ /* Set NEXT_VERTEX to the next point (vertex) in this contour.
+
+ If i is past the end of the contour, then don't build edges
+ for this point. */
+ next_vertex = i + 1;
+
+ if (next_vertex == outline->outline_used
+ || !(outline->outline[next_vertex].flags
+ & SFNT_GLYPH_OUTLINE_LINETO))
+ continue;
+
+ /* Skip past horizontal vertices. */
+ if (outline->outline[next_vertex].y == outline->outline[i].y)
+ continue;
+
+ /* Figure out the winding direction. */
+ if (outline->outline[next_vertex].y < outline->outline[i].y)
+ /* Vector will cross imaginary ray from its bottom from the
+ left of the ray. Winding is thus 1. */
+ edges[edge].winding = 1;
+ else
+ /* Moving clockwise. Winding is thus -1. */
+ edges[edge].winding = -1;
+
+ /* Figure out the top and bottom values of this edge. If the
+ next edge is below, top is here and bot is the edge below.
+ If the next edge is above, then top is there and this is the
+ bottom. */
+
+ if (outline->outline[next_vertex].y < outline->outline[i].y)
+ {
+ /* End of edge is below this one (keep in mind this is a
+ cartesian coordinate system, so smaller values are below
+ larger ones.) */
+ top = i;
+ bottom = next_vertex;
+ }
+ else
+ {
+ /* End of edge is above this one. */
+ bottom = i;
+ top = next_vertex;
+ }
+
+ bot = (outline->outline[bottom].y - ymin);
+ edges[edge].top = (outline->outline[top].y - ymin);
+
+ /* Record the edge. Rasterization happens from bottom to
+ up, so record the X at the bottom. */
+ edges[edge].x = (outline->outline[bottom].x - xmin);
+ dx = (outline->outline[top].x - outline->outline[bottom].x);
+ dy = abs (outline->outline[top].y
+ - outline->outline[bottom].y);
+
+ /* Step to first grid point. */
+ y = sfnt_poly_grid_ceil (bot);
+
+ /* If rounding would make the edge not cover any area, skip this
+ edge. */
+
+ if (y >= edges[edge].top)
+ continue;
+
+ /* Compute the step X. This is how much X changes for each
+ increase in Y. */
+
+ step_x = sfnt_div_fixed (dx, dy);
+ edges[edge].next = NULL;
+
+ /* Compute the step X scaled to the poly step. */
+ edges[edge].step_x
+ = sfnt_mul_fixed (step_x, SFNT_POLY_STEP);
+
+ /* Step to the grid point. */
+ edges[edge].x += sfnt_mul_fixed (step_x, bot - y);
+
+ /* Set the bottom position. */
+ edges[edge].bottom = y;
+
+ edge++;
+ }
+
+ if (edge)
+ edge_proc (edges, edge, dcontext);
+}
+
+/* Sort an array of SIZE edges to increase by bottom Y position, in
+ preparation for building spans.
+
+ Insertion sort is used because there are usually not very many
+ edges, and anything larger would bloat up the code. */
+
+static void
+sfnt_edge_sort (struct sfnt_edge *edges, size_t size)
+{
+ ssize_t i, j;
+ struct sfnt_edge edge;
+
+ for (i = 1; i < size; ++i)
+ {
+ edge = edges[i];
+ j = i - 1;
+
+ while (j >= 0 && (edges[j].bottom > edge.bottom))
+ {
+ edges[j + 1] = edges[j];
+ j--;
+ }
+
+ edges[j + 1] = edge;
+ }
+}
+
+/* Draw EDGES, an unsorted array of polygon edges of size SIZE. For
+ each scanline, call SPAN_FUNC with a list of active edges and
+ coverage information, and DCONTEXT.
+
+ Sort each edge in ascending order by the bottommost Y coordinate to
+ which it applies. Start a loop on the Y coordinate, which starts
+ out at that of the bottommost edge. For each iteration, add edges
+ that now overlap with Y, keeping them sorted by X. Poly those
+ edges through SPAN_FUNC. Then, move upwards by SFNT_POLY_STEP,
+ remove edges that no longer apply, and interpolate the remaining
+ edges' X coordinates. Repeat until all the edges have been polyed.
+
+ Or alternatively, think of this as such: each edge is actually a
+ vector from its bottom position towards its top most position.
+ Every time Y moves upwards, the position of each edge intersecting
+ with Y is interpolated and added to a list of spans along with
+ winding information that is then given to EDGE_FUNC.
+
+ Anti-aliasing is performed using a coverage map for fractional
+ coordinates, and incrementing the Y axis by SFNT_POLY_STEP instead
+ of 1. SFNT_POLY_STEP is chosen to always keep Y aligned to a grid
+ placed such that there are always 1 << SFNT_POLY_SHIFT positions
+ available for each integral pixel coordinate. */
+
+static void
+sfnt_poly_edges (struct sfnt_edge *edges, size_t size,
+ sfnt_span_proc span_func, void *dcontext)
+{
+ sfnt_fixed y;
+ size_t e;
+ struct sfnt_edge *active, **prev, *a, *n;
+
+ if (!size)
+ return;
+
+ /* Sort edges to ascend by Y-order. Once again, remember: cartesian
+ coordinates. */
+ sfnt_edge_sort (edges, size);
+
+ /* Step down line by line. Find active edges. */
+
+ y = edges[0].bottom;
+ active = 0;
+ active = NULL;
+ e = 0;
+
+ for (;;)
+ {
+ /* Add in new edges keeping them sorted. */
+ for (; e < size && edges[e].bottom <= y; ++e)
+ {
+ /* Find where to place this edge. */
+ for (prev = &active; (a = *prev); prev = &(a->next))
+ {
+ if (a->x > edges[e].x)
+ break;
+ }
+
+ edges[e].next = *prev;
+ *prev = &edges[e];
+ }
+
+ /* Draw this span at the current position. Y axis antialiasing
+ is expected to be handled by SPAN_FUNC. */
+ span_func (active, y, dcontext);
+
+ /* Compute the next Y position. */
+ y += SFNT_POLY_STEP;
+
+ /* Strip out edges that no longer have effect. */
+
+ for (prev = &active; (a = *prev);)
+ {
+ if (a->top <= y)
+ *prev = a->next;
+ else
+ prev = &a->next;
+ }
+
+ /* Break if all is done. */
+ if (!active && e == size)
+ break;
+
+ /* Step all edges. */
+ for (a = active; a; a = a->next)
+ sfnt_step_edge (a);
+
+ /* Resort on X axis. */
+ for (prev = &active; (a = *prev) && (n = a->next);)
+ {
+ if (a->x > n->x)
+ {
+ a->next = n->next;
+ n->next = a;
+ *prev = n;
+ prev = &active;
+ }
+ else
+ prev = &a->next;
+ }
+ }
+}
+
+/* Saturate and convert the given unsigned short value X to an
+ unsigned char. */
+
+static unsigned char
+sfnt_saturate_short (unsigned short x)
+{
+ if (x > 255)
+ return 255;
+
+ return x;
+}
+
+/* Fill a single span of pixels between X0 and X1 at Y, a raster
+ coordinate, onto RASTER. */
+
+static void
+sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y,
+ sfnt_fixed x0, sfnt_fixed x1)
+{
+ unsigned char *start;
+ const unsigned char *coverage;
+ sfnt_fixed left, right, end;
+ unsigned short w, a;
+ int row;
+
+ /* Clip bounds to pixmap. */
+ if (x0 < 0)
+ x0 = 0;
+
+ if (x1 >= raster->width << 16)
+ x1 = (raster->width - 1) << 16;
+
+ /* Check for empty bounds. */
+ if (x1 <= x0)
+ return;
+
+ /* Figure out coverage based on Y axis fractional. */
+ coverage = sfnt_poly_coverage[(y >> (16 - SFNT_POLY_SHIFT))
+ & SFNT_POLY_MASK];
+ row = y >> 16;
+
+ /* Don't fill out of bounds rows. */
+ if (row < 0 || row >= raster->height)
+ return;
+
+ /* Set start, then start filling according to coverage. left and
+ right are now .3. */
+ left = x0 >> (16 - SFNT_POLY_SHIFT);
+ right = x1 >> (16 - SFNT_POLY_SHIFT);
+ start = raster->cells + row * raster->stride;
+ start += left >> SFNT_POLY_SHIFT;
+
+ /* If left and right actually lie in the same pixel, just fill with
+ the coverage of both and return. */
+
+ if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK))
+ {
+ w = coverage[right - left];
+ a = *start + w;
+
+ *start = sfnt_saturate_short (a);
+ return;
+ }
+
+ /* Compute coverage for first pixel, then poly. The code from here
+ onwards assumes that left and right are on two different
+ pixels. */
+
+ if (left & SFNT_POLY_MASK)
+ {
+ /* Compute the coverage for the first pixel, and move left past
+ it. The coverage is a number from 1 to 7 describing how
+ ``partially'' covered this pixel is. */
+
+ end = (left + SFNT_POLY_SAMPLE - 1) & ~SFNT_POLY_MASK;
+ end = MIN (right, end);
+
+ w = coverage[end - left];
+ a = *start + w;
+
+ /* Now move left past. */
+ left = end;
+ *start++ = sfnt_saturate_short (a);
+ }
+
+ /* Clear coverage info for first pixel. Compute coverage for center
+ pixels. Note that SFNT_POLY_SAMPLE is used and not
+ SFNT_POLY_MASK, because coverage has a blank column at the
+ start. */
+ w = coverage[SFNT_POLY_SAMPLE];
+
+ /* Fill pixels between left and right. */
+ while (left + SFNT_POLY_MASK < right)
+ {
+ a = *start + w;
+ *start++ = sfnt_saturate_short (a);
+ left += SFNT_POLY_SAMPLE;
+ }
+
+ /* Fill rightmost pixel with any partial coverage. */
+
+ if (right & SFNT_POLY_MASK)
+ {
+ w = coverage[right - left];
+ a = *start + w;
+ *start = sfnt_saturate_short (a);
+ }
+
+ /* All done. */
+}
+
+/* Poly each span starting from START onto RASTER, at position Y. Y
+ here is still a cartesian coordinate, where the bottom of the
+ raster is 0. But that is no longer true by the time sfnt_span_fill
+ is called. */
+
+static void
+sfnt_poly_span (struct sfnt_edge *start, sfnt_fixed y,
+ struct sfnt_raster *raster)
+{
+ struct sfnt_edge *edge;
+ int winding;
+ sfnt_fixed x0, x1;
+
+ /* Pacify -Wmaybe-uninitialized; x1 and x0 are only used when edge
+ != start, at which point x0 has already been set. */
+ x0 = x1 = 0;
+
+ /* Generate the X axis coverage map. Then poly it onto RASTER.
+ winding on each edge determines the winding direction: when it is
+ positive, winding is 1. When it is negative, winding is -1.
+
+ Fill each consecutive stretch of spans that are inside the glyph;
+ otherwise, coverage will overlap for some spans, but not
+ others.
+
+ The spans must be terminated with an edge that causes an
+ off-transition, or some spans will not be filled. */
+
+ winding = 0;
+
+ for (edge = start; edge; edge = edge->next)
+ {
+ if (!winding)
+ {
+ if (edge != start && x0 != x1)
+ /* Draw this section of spans that are on. */
+ sfnt_fill_span (raster, (raster->height << 16) - y,
+ x0, x1);
+
+ x0 = x1 = edge->x;
+ }
+ else
+ x1 = edge->x;
+
+ winding += edge->winding;
+ }
+
+ /* Draw the last span following the last off-transition. */
+
+ if (!winding && edge != start && x0 != x1)
+ sfnt_fill_span (raster, (raster->height << 16) - y,
+ x0, x1);
+}
+
+
+
+/* Main entry point for outline rasterization. */
+
+/* Raster the spans between START and its end to the raster specified
+ as DCONTEXT. The span's position is Y. */
+
+static void
+sfnt_raster_span (struct sfnt_edge *start, sfnt_fixed y,
+ void *dcontext)
+{
+ sfnt_poly_span (start, y, dcontext);
+}
+
+/* Generate and poly each span in EDGES onto the raster specified as
+ DCONTEXT. */
+
+static void
+sfnt_raster_edge (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+ sfnt_poly_edges (edges, num_edges, sfnt_raster_span,
+ dcontext);
+}
+
+/* Generate an alpha mask for the glyph outline OUTLINE. Value is the
+ alpha mask upon success, NULL upon failure. */
+
+TEST_STATIC struct sfnt_raster *
+sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline)
+{
+ struct sfnt_raster raster, *data;
+
+ /* Get the raster parameters. */
+ sfnt_prepare_raster (&raster, outline);
+
+ /* Allocate the raster data. */
+ data = xmalloc (sizeof *data + raster.stride * raster.height);
+ *data = raster;
+ data->cells = (unsigned char *) (data + 1);
+ memset (data->cells, 0, raster.stride * raster.height);
+
+ /* Generate edges for the outline, polying each array of edges to
+ the raster. */
+ sfnt_build_outline_edges (outline, sfnt_raster_edge, data);
+
+ /* All done. */
+ return data;
+}
+
+
+
+/* Glyph metrics computation. */
+
+/* Read an hmtx table from the font FD, using the table directory
+ specified as SUBTABLE, the maxp table MAXP, and the hhea table
+ HHEA.
+
+ Return NULL upon failure, and the hmtx table otherwise.
+ HHEA->num_of_long_hor_metrics determines the number of horizontal
+ metrics present, and MAXP->num_glyphs -
+ HHEA->num_of_long_hor_metrics determines the number of left-side
+ bearings present. */
+
+TEST_STATIC struct sfnt_hmtx_table *
+sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_hmtx_table *hmtx;
+ size_t size;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HMTX);
+
+ if (!directory)
+ return NULL;
+
+ /* Figure out how many bytes are required. */
+ size = ((hhea->num_of_long_hor_metrics
+ * sizeof (struct sfnt_long_hor_metric))
+ + (MAX (0, ((int) maxp->num_glyphs
+ - hhea->num_of_long_hor_metrics))
+ * sizeof (int16_t)));
+
+ /* Check the length matches exactly. */
+ if (directory->length != size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Now allocate enough to hold all of that along with the table
+ directory structure. */
+
+ hmtx = xmalloc (sizeof *hmtx + size);
+
+ /* Read into hmtx + 1. */
+ rc = read (fd, hmtx + 1, size);
+ if (rc < size)
+ {
+ xfree (hmtx);
+ return NULL;
+ }
+
+ /* Set pointers to data. */
+ hmtx->h_metrics = (struct sfnt_long_hor_metric *) (hmtx + 1);
+ hmtx->left_side_bearing
+ = (int16_t *) (hmtx->h_metrics
+ + hhea->num_of_long_hor_metrics);
+
+ /* Swap what was read. */
+
+ for (i = 0; i < hhea->num_of_long_hor_metrics; ++i)
+ {
+ sfnt_swap16 (&hmtx->h_metrics[i].advance_width);
+ sfnt_swap16 (&hmtx->h_metrics[i].left_side_bearing);
+ }
+
+ for (; i < maxp->num_glyphs; ++i)
+ sfnt_swap16 (&hmtx->left_side_bearing[i - hhea->num_of_long_hor_metrics]);
+
+ /* All done. */
+ return hmtx;
+}
+
+/* Obtain glyph metrics for the glyph indiced by GLYPH at the
+ specified PIXEL_SIZE. Return 0 and the metrics in *METRICS if
+ metrics could be found, else 1.
+
+ If PIXEL_SIZE is -1, do not perform any scaling on the glyph
+ metrics; HEAD need not be specified in that case.
+
+ HMTX, HHEA, HEAD and MAXP should be the hmtx, hhea, head, and maxp
+ tables of the font respectively. */
+
+TEST_STATIC int
+sfnt_lookup_glyph_metrics (sfnt_glyph glyph, int pixel_size,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_head_table *head,
+ struct sfnt_maxp_table *maxp)
+{
+ short lbearing;
+ unsigned short advance;
+ sfnt_fixed factor;
+
+ if (glyph < hhea->num_of_long_hor_metrics)
+ {
+ /* There is a long entry in the hmtx table. */
+ lbearing = hmtx->h_metrics[glyph].left_side_bearing;
+ advance = hmtx->h_metrics[glyph].advance_width;
+ }
+ else if (hhea->num_of_long_hor_metrics
+ && glyph < maxp->num_glyphs)
+ {
+ /* There is a short entry in the hmtx table. */
+ lbearing
+ = hmtx->left_side_bearing[glyph
+ - hhea->num_of_long_hor_metrics];
+ advance
+ = hmtx->h_metrics[hhea->num_of_long_hor_metrics - 1].advance_width;
+ }
+ else
+ /* No entry corresponds to the glyph. */
+ return 1;
+
+ if (pixel_size == -1)
+ {
+ /* Return unscaled metrics in this case. */
+ metrics->lbearing = lbearing;
+ metrics->advance = advance;
+ return 0;
+ }
+
+ /* Now scale lbearing and advance up to the pixel size. */
+ factor = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+ /* Save them. */
+ metrics->lbearing = sfnt_mul_fixed (lbearing * 65536, factor);
+ metrics->advance = sfnt_mul_fixed (advance * 65536, factor);
+
+ /* All done. */
+ return 0;
+}
+
+/* Scale the specified glyph metrics by FACTOR.
+ Set METRICS->lbearing and METRICS->advance to their current
+ values times factor. */
+
+MAYBE_UNUSED TEST_STATIC void
+sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed factor)
+{
+ metrics->lbearing
+ = sfnt_mul_fixed (metrics->lbearing * 65536, factor);
+ metrics->advance
+ = sfnt_mul_fixed (metrics->advance * 65536, factor);
+}
+
+/* Calculate the factor used to convert em space to device space for a
+ font with the specified HEAD table and PPEM value. */
+
+MAYBE_UNUSED TEST_STATIC sfnt_fixed
+sfnt_get_scale (struct sfnt_head_table *head, int ppem)
+{
+ /* Figure out how to convert from font unit-space to pixel space.
+ To turn one unit to its corresponding pixel size given a ppem of
+ 1, the unit must be divided by head->units_per_em. Then, it must
+ be multipled by the ppem. So,
+
+ PIXEL = UNIT / UPEM * PPEM
+
+ which means:
+
+ PIXEL = UNIT * PPEM / UPEM */
+
+ return sfnt_div_fixed (ppem, head->units_per_em);
+}
+
+
+
+/* Font style parsing. */
+
+/* Read the name table from the given font FD, using the table
+ directory specified as SUBTABLE. Perform validation on the offsets
+ in the name records. Return NULL upon failure, else the name
+ table. */
+
+TEST_STATIC struct sfnt_name_table *
+sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_name_table *name;
+ size_t required;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_NAME);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum that has to be read. */
+ required = SFNT_ENDOF (struct sfnt_name_table,
+ string_offset, uint16_t);
+
+ if (directory->length < required)
+ return NULL;
+
+ /* Allocate enough to hold the name table and variable length
+ data. */
+ name = xmalloc (sizeof *name + directory->length);
+
+ /* Read the fixed length data. */
+ rc = read (fd, name, required);
+ if (rc < required)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap16 (&name->format);
+ sfnt_swap16 (&name->count);
+ sfnt_swap16 (&name->string_offset);
+
+ /* Reject unsupported formats. */
+ if (name->format)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Set the pointer to the start of the variable length data. */
+ name->name_records
+ = (struct sfnt_name_record *) (name + 1);
+
+ /* Check there is enough for the name records. */
+ required = directory->length - required;
+ if (required < name->count * sizeof *name->name_records)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Read the variable length data. First, read the name records. */
+ rc = read (fd, name->name_records,
+ (name->count
+ * sizeof *name->name_records));
+ if (rc < (name->count
+ * sizeof *name->name_records))
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Swap each of the name records. */
+ for (i = 0; i < name->count; ++i)
+ {
+ sfnt_swap16 (&name->name_records[i].platform_id);
+ sfnt_swap16 (&name->name_records[i].platform_specific_id);
+ sfnt_swap16 (&name->name_records[i].language_id);
+ sfnt_swap16 (&name->name_records[i].name_id);
+ sfnt_swap16 (&name->name_records[i].length);
+ sfnt_swap16 (&name->name_records[i].offset);
+ }
+
+ /* Now, read the name data. */
+
+ if (name->string_offset > directory->length)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ required = directory->length - name->string_offset;
+
+ /* It can happen that the string offset comes before the name
+ records, and as a result exceeds the number of bytes
+ previously allocated. Extend name if that is the case. */
+
+ if (required > (directory->length
+ - (name->count
+ * sizeof *name->name_records)))
+ {
+ name = xrealloc (name, (sizeof *name
+ + (name->count
+ * sizeof *name->name_records)
+ + required));
+ name->name_records = (struct sfnt_name_record *) (name + 1);
+ }
+
+ /* There is enough space past name->name_records to hold REQUIRED
+ bytes. Seek to the right offset. */
+
+ if (lseek (fd, directory->offset + name->string_offset,
+ SEEK_SET) == (off_t) -1)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Read REQURIED bytes into the string data. */
+ name->data = (unsigned char *) (name->name_records
+ + name->count);
+ rc = read (fd, name->data, required);
+ if (rc < required)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Now validate each of the name records. */
+ for (i = 0; i < name->count; ++i)
+ {
+ if (((int) name->name_records[i].offset
+ + name->name_records[i].length) > required)
+ {
+ /* The name is out of bounds! */
+ xfree (name);
+ return NULL;
+ }
+ }
+
+ /* Return the name table. */
+ return name;
+}
+
+/* Return a pointer to the name data corresponding with CODE under the
+ name table NAME. Return the start of the data and the name record
+ under *RECORD upon success, and NULL otherwise. */
+
+TEST_STATIC unsigned char *
+sfnt_find_name (struct sfnt_name_table *name,
+ enum sfnt_name_identifier_code code,
+ struct sfnt_name_record *record)
+{
+ int i;
+
+ for (i = 0; i < name->count; ++i)
+ {
+ if (name->name_records[i].name_id == code)
+ {
+ /* The offsets within have already been validated. */
+ *record = name->name_records[i];
+ return name->data + record->offset;
+ }
+ }
+
+ return NULL;
+}
+
+/* Read the meta table from the given font FD, using the table
+ directory specified as SUBTABLE. Perform validation on the offsets
+ in each metadata record. Return NULL upon failure, else the meta
+ table. */
+
+TEST_STATIC struct sfnt_meta_table *
+sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_meta_table *meta;
+ size_t required, i, data_size, map_size, offset;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_META);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum that has to be read. */
+ required = SFNT_ENDOF (struct sfnt_meta_table,
+ num_data_maps, uint32_t);
+
+ if (directory->length < required)
+ return NULL;
+
+ /* Allocate enough to hold it. */
+ meta = xmalloc (sizeof *meta);
+
+ /* Read the header. */
+ rc = read (fd, meta, required);
+ if (rc < required)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Swap what has been read so far. */
+ sfnt_swap32 (&meta->version);
+ sfnt_swap32 (&meta->flags);
+ sfnt_swap32 (&meta->data_offset);
+ sfnt_swap32 (&meta->num_data_maps);
+
+ /* Make sure the meta is supported. */
+ if (meta->version != 1)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Reallocate the table to hold sizeof *meta + meta->num_data_maps
+ times sizeof meta->data_maps + directory->length bytes. This is
+ because it is ok for metadata to point into the data map itself,
+ so an unswapped copy of the whole meta contents must be
+ retained. */
+
+ if (INT_MULTIPLY_WRAPV (sizeof *meta->data_maps, meta->num_data_maps,
+ &map_size)
+ /* Do so while checking for overflow from bad sfnt files. */
+ || INT_ADD_WRAPV (map_size, sizeof *meta, &data_size)
+ || INT_ADD_WRAPV (data_size, directory->length, &data_size))
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Do the reallocation. */
+ meta = xrealloc (meta, data_size);
+
+ /* Check that the remaining data is big enough to hold the data
+ maps. */
+ if (directory->length - required < map_size)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Set pointers to data_maps and data. */
+ meta->data_maps = (struct sfnt_meta_data_map *) (meta + 1);
+ meta->data = (unsigned char *) (meta->data_maps
+ + meta->num_data_maps);
+
+ /* Now, seek back. Read the entire table into meta->data. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ rc = read (fd, meta->data, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Copy the data maps into meta->data_maps and swap them one by
+ one. */
+ memcpy (meta->data_maps, meta->data + required,
+ map_size);
+
+ for (i = 0; i < meta->num_data_maps; ++i)
+ {
+ sfnt_swap32 (&meta->data_maps[i].tag);
+ sfnt_swap32 (&meta->data_maps[i].data_offset);
+ sfnt_swap32 (&meta->data_maps[i].data_length);
+
+ /* Verify the data offsets. Overflow checking is particularly
+ important here. */
+
+ if (INT_ADD_WRAPV (meta->data_maps[i].data_offset,
+ meta->data_maps[i].data_length,
+ &offset))
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ if (offset > directory->length)
+ {
+ xfree (meta);
+ return NULL;
+ }
+ }
+
+ /* All done. */
+ return meta;
+}
+
+/* Return a pointer to the metadata corresponding to TAG under the
+ meta table META. Return the start of the data and the metadata map
+ under *MAP upon success, and NULL otherwise. */
+
+MAYBE_UNUSED TEST_STATIC char *
+sfnt_find_metadata (struct sfnt_meta_table *meta,
+ enum sfnt_meta_data_tag tag,
+ struct sfnt_meta_data_map *map)
+{
+ int i;
+
+ for (i = 0; i < meta->num_data_maps; ++i)
+ {
+ if (meta->data_maps[i].tag == tag)
+ {
+ *map = meta->data_maps[i];
+ return (char *) meta->data + map->data_offset;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* TrueType collection format support. */
+
+/* Read a TrueType collection header from the font file FD.
+ FD must currently at the start of the file.
+
+ Value is the header upon success, else NULL. */
+
+TEST_STATIC struct sfnt_ttc_header *
+sfnt_read_ttc_header (int fd)
+{
+ struct sfnt_ttc_header *ttc;
+ size_t size, i;
+ ssize_t rc;
+
+ /* First, allocate only as much as required. */
+
+ ttc = xmalloc (sizeof *ttc);
+
+ /* Read the version 1.0 data. */
+
+ size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
+ uint32_t);
+ rc = read (fd, ttc, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Now swap what was read. */
+ sfnt_swap32 (&ttc->ttctag);
+ sfnt_swap32 (&ttc->version);
+ sfnt_swap32 (&ttc->num_fonts);
+
+ /* Verify that the tag is as expected. */
+ if (ttc->ttctag != SFNT_TTC_TTCF)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Now, read the variable length data. Make sure to check for
+ overflow. */
+
+ if (INT_MULTIPLY_WRAPV (ttc->num_fonts,
+ sizeof *ttc->offset_table,
+ &size))
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ ttc = xrealloc (ttc, sizeof *ttc + size);
+ ttc->offset_table = (uint32_t *) (ttc + 1);
+ rc = read (fd, ttc->offset_table, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Swap each of the offsets read. */
+ for (i = 0; i < ttc->num_fonts; ++i)
+ sfnt_swap32 (&ttc->offset_table[i]);
+
+ /* Now, look at the version. If it is earlier than 2.0, then
+ reading is finished. */
+
+ if (ttc->version < 0x00020000)
+ return ttc;
+
+ /* If it is 2.0 or later, then continue to read ul_dsig_tag to
+ ul_dsig_offset. */
+
+ size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset,
+ uint32_t)
+ - offsetof (struct sfnt_ttc_header, ul_dsig_tag));
+ rc = read (fd, &ttc->ul_dsig_offset, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&ttc->ul_dsig_tag);
+ sfnt_swap32 (&ttc->ul_dsig_length);
+ sfnt_swap32 (&ttc->ul_dsig_offset);
+
+ /* All done. */
+ return ttc;
+}
+
+
+
+/* TrueType hinting support.
+
+ If you do not read the code in this section in conjunction with
+ Apple's TrueType Reference Manual, you will get very confused!
+
+ TrueType fonts don't provide simple hinting meta data, unlike Type
+ 2 or CFF fonts.
+
+ Instead, they come with a ``font program'', a bytecode program
+ which is executed upon loading the font, a ``control value
+ program'', executed upon font metrics changing, and then a ``glyph
+ program'' for each glyph, which is run to fit its glyph after
+ scaling.
+
+ The virtual machine which runs this bytecode is arranged as such:
+
+ Firstly, there is a set of registers known as the ``graphics
+ state''. Each time the point size of a font changes, the ``control
+ value program'' is run to establish the default values of the
+ ``graphics state''. Then, before each glyph program is run, the
+ ``graphics state'' is set back to the default values.
+
+ Secondly, there is an address space which contains all instructions
+ being run for the current program, which is addressed by the
+ interpreter through its program counter and also by the
+ instructions which push data on to the stack.
+
+ Thirdly, there is a single stack, from which most instructions take
+ their operands and store data.
+
+ Then, there is some memory set aside for each font, the ``storage
+ area'', which is addressed through the RS[] and WS[] instructions,
+ and a ``control value table'', which is the `cvt ' table of the
+ font.
+
+ And finally, there is a ``glyph zone'' which holds points from a
+ scaled glyph outline, and a ``twilight zone'', which holds points
+ used by the font program itself. Both are addressed indirectly
+ through one of three ``zone pointer'' registers, and are accessible
+ only when a program is being run on behalf of a glyph. */
+
+
+
+/* Functions for reading tables used by the TrueType interpreter. */
+
+/* Read the cvt table (control value table) from the given font FD,
+ using the table directory specified as SUBTABLE. Swap all values
+ in the control value table. Return NULL upon failure, else the cvt
+ table. */
+
+TEST_STATIC struct sfnt_cvt_table *
+sfnt_read_cvt_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required, i;
+ ssize_t rc;
+ struct sfnt_cvt_table *cvt;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CVT );
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *cvt, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ cvt = xmalloc (required);
+
+ /* Now set cvt->num_elements as appropriate, and make cvt->values
+ point into the values. */
+ cvt->num_elements = directory->length / 2;
+ cvt->values = (sfnt_fword *) (cvt + 1);
+
+ /* Read into cvt. */
+ rc = read (fd, cvt->values, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (cvt);
+ return NULL;
+ }
+
+ /* Swap each element in the control value table. */
+ for (i = 0; i < cvt->num_elements; ++i)
+ sfnt_swap16 (&cvt->values[i]);
+
+ /* All done. */
+ return cvt;
+}
+
+/* Read the fpgm table from the given font FD, using the table
+ directory specified as SUBTABLE. Value is NULL upon failure, else
+ the fpgm table. */
+
+TEST_STATIC struct sfnt_fpgm_table *
+sfnt_read_fpgm_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required;
+ ssize_t rc;
+ struct sfnt_fpgm_table *fpgm;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_FPGM);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *fpgm, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ fpgm = xmalloc (sizeof *fpgm + directory->length);
+
+ /* Now set fpgm->num_instructions as appropriate, and make
+ fpgm->instructions point to the right place. */
+
+ fpgm->num_instructions = directory->length;
+ fpgm->instructions = (unsigned char *) (fpgm + 1);
+
+ /* Read into fpgm. */
+ rc = read (fd, fpgm->instructions, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (fpgm);
+ return NULL;
+ }
+
+ /* All done. */
+ return fpgm;
+}
+
+/* Read the prep table from the given font FD, using the table
+ directory specified as SUBTABLE. Value is NULL upon failure, else
+ the prep table. */
+
+TEST_STATIC struct sfnt_prep_table *
+sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required;
+ ssize_t rc;
+ struct sfnt_prep_table *prep;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_PREP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *prep, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ prep = xmalloc (sizeof *prep + directory->length);
+
+ /* Now set prep->num_instructions as appropriate, and make
+ prep->instructions point to the right place. */
+
+ prep->num_instructions = directory->length;
+ prep->instructions = (unsigned char *) (prep + 1);
+
+ /* Read into prep. */
+ rc = read (fd, prep->instructions, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (prep);
+ return NULL;
+ }
+
+ /* All done. */
+ return prep;
+}
+
+
+
+/* Interpreter execution environment. */
+
+/* Divide the specified two 26.6 fixed point numbers X and Y.
+ Return the result. */
+
+static sfnt_f26dot6
+sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y)
+{
+#ifdef INT64_MAX
+ int64_t result;
+
+ result = ((int64_t) x * 64) / y;
+
+ return result;
+#else
+ int sign;
+ unsigned int a, b;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ a = abs (x);
+ b = abs (y);
+
+ return sfnt_multiply_divide (a, 64, b) * sign;
+#endif
+}
+
+/* Multiply the specified two 26.6 fixed point numbers A and B.
+ Return the result, or an undefined value upon overflow. */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) a * (int64_t) b;
+
+ /* This can be done quickly with int64_t. */
+ return product / (int64_t) 64;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (a), abs (b),
+ 64) * sign;
+#endif
+}
+
+/* Multiply the specified 2.14 number with another signed 32 bit
+ number. Return the result as a signed 32 bit number. */
+
+static int32_t
+sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) a * (int64_t) b;
+
+ return product / (int64_t) 16384;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (a), abs (b),
+ 16384) * sign;
+#endif
+}
+
+/* Multiply the specified 26.6 fixed point number X by the specified
+ 16.16 fixed point number Y with symmetric rounding.
+
+ The 26.6 fixed point number must fit inside -32768 to 32767.ffff.
+ Value is otherwise undefined. */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6_fixed (sfnt_f26dot6 x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ uint64_t product;
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ {
+ x = -x;
+ sign = -sign;
+ }
+
+ if (y < 0)
+ {
+ y = -y;
+ sign = -sign;
+ }
+
+ product = (uint64_t) y * (uint64_t) x;
+
+ /* This can be done quickly with int64_t. */
+ return ((int64_t) (product + 32676) / (int64_t) 65536) * sign;
+#else
+ struct sfnt_large_integer temp;
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ sfnt_multiply_divide_1 (abs (x), abs (y), &temp);
+ sfnt_large_integer_add (&temp, 32676);
+ return sfnt_multiply_divide_2 (&temp, 65536) * sign;
+#endif
+}
+
+/* Return the floor of the specified 26.6 fixed point value X. */
+
+static sfnt_f26dot6
+sfnt_floor_f26dot6 (sfnt_f26dot6 x)
+{
+ return x & 037777777700;
+}
+
+/* Return the ceiling of the specified 26.6 fixed point value X. */
+
+static sfnt_f26dot6
+sfnt_ceil_f26dot6 (sfnt_f26dot6 x)
+{
+ return (x + 077) & ~077;
+}
+
+/* Return the 26.6 fixed point value X rounded to the nearest integer
+ value. */
+
+static sfnt_f26dot6
+sfnt_round_f26dot6 (sfnt_f26dot6 x)
+{
+ /* Add 0.5. */
+ x += 040;
+
+ /* Remove the fractional. */
+ return x & ~077;
+}
+
+/* Needed by sfnt_init_graphics_state. */
+
+static void sfnt_validate_gs (struct sfnt_graphics_state *);
+
+/* Set up default values for the interpreter graphics state. Return
+ them in STATE. */
+
+static void
+sfnt_init_graphics_state (struct sfnt_graphics_state *state)
+{
+ state->auto_flip = true;
+ state->cvt_cut_in = 0104;
+ state->delta_base = 9;
+ state->delta_shift = 3;
+ state->dual_projection_vector.x = 040000; /* 1.0 */
+ state->dual_projection_vector.y = 0;
+ state->freedom_vector.x = 040000; /* 1.0 */
+ state->freedom_vector.y = 0;
+ state->instruct_control = 0;
+ state->loop = 1;
+ state->minimum_distance = 0100;
+ state->projection_vector.x = 040000; /* 1.0 */
+ state->projection_vector.y = 0;
+ state->round_state = 1;
+ state->rp0 = 0;
+ state->rp1 = 0;
+ state->rp2 = 0;
+ state->scan_control = 0;
+ state->sw_cut_in = 0;
+ state->single_width_value = 0;
+ state->zp0 = 1;
+ state->zp1 = 1;
+ state->zp2 = 1;
+
+ /* Validate the graphics state. */
+ sfnt_validate_gs (state);
+}
+
+/* Set up an interpreter to be used with a font. Use the resource
+ limits specified in the MAXP table, the values specified in the
+ CVT, HEAD and FVAR tables, the pixel size PIXEL_SIZE, and the point
+ size POINT_SIZE. CVT may be NULL, in which case the interpreter
+ will not have access to a control value table.
+
+ POINT_SIZE should be PIXEL_SIZE, converted to 1/72ths of an inch.
+
+ Value is the interpreter, with all state initialized to default
+ values, or NULL upon failure. */
+
+TEST_STATIC struct sfnt_interpreter *
+sfnt_make_interpreter (struct sfnt_maxp_table *maxp,
+ struct sfnt_cvt_table *cvt,
+ struct sfnt_head_table *head,
+ struct sfnt_fvar_table *fvar,
+ int pixel_size, int point_size)
+{
+ size_t size, temp, i, storage_size, pad;
+ struct sfnt_interpreter *interpreter;
+
+ /* Detect CFF maxp tables. */
+ if (maxp->version != 0x00010000)
+ return NULL;
+
+ /* Use the contents of the MAXP table to determine the size of the
+ interpreter structure. */
+ size = sizeof (*interpreter);
+
+ /* Add program stack. */
+ if (INT_ADD_WRAPV ((maxp->max_stack_elements
+ * sizeof *interpreter->stack),
+ size, &size))
+ return NULL;
+
+ /* Add twilight zone. */
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_x),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ /* Add the storage area. */
+ storage_size = maxp->max_storage * sizeof *interpreter->storage;
+ if (INT_ADD_WRAPV (storage_size, size, &size))
+ return NULL;
+
+ /* Add padding for the storage area. */
+ pad = alignof (struct sfnt_interpreter_definition);
+ pad -= size & (pad - 1);
+ if (INT_ADD_WRAPV (pad, size, &size))
+ return NULL;
+
+ /* Add function and instruction definitions. */
+ if (INT_ADD_WRAPV ((((int) maxp->max_instruction_defs
+ + maxp->max_function_defs)
+ * sizeof *interpreter->function_defs),
+ size, &size))
+ return NULL;
+
+ /* Add control value table. */
+
+ if (cvt)
+ {
+ if (INT_MULTIPLY_WRAPV (cvt->num_elements,
+ sizeof *interpreter->cvt,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+ }
+
+ /* Allocate the interpreter. */
+ interpreter = xmalloc (size);
+
+#ifdef TEST
+ interpreter->run_hook = NULL;
+ interpreter->push_hook = NULL;
+ interpreter->pop_hook = NULL;
+#endif
+
+ /* Fill in pointers and default values. */
+ interpreter->max_stack_elements = maxp->max_stack_elements;
+ interpreter->num_instructions = 0;
+ interpreter->IP = 0;
+ interpreter->storage_size = maxp->max_storage;
+ interpreter->function_defs_size = maxp->max_function_defs;
+ interpreter->instruction_defs_size = maxp->max_instruction_defs;
+ interpreter->twilight_zone_size = maxp->max_twilight_points;
+ interpreter->scale = 0; /* This should be set later. */
+
+ interpreter->stack = (uint32_t *) (interpreter + 1);
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = NULL;
+ interpreter->twilight_x
+ = (sfnt_f26dot6 *) (interpreter->stack
+ + maxp->max_stack_elements);
+ interpreter->twilight_y = (interpreter->twilight_x
+ + maxp->max_twilight_points);
+ interpreter->twilight_original_x = (interpreter->twilight_y
+ + maxp->max_twilight_points);
+ interpreter->twilight_original_y
+ = (interpreter->twilight_original_x
+ + maxp->max_twilight_points);
+ interpreter->glyph_zone = NULL;
+ interpreter->advance_width = 0;
+ interpreter->storage
+ = (uint32_t *) (interpreter->twilight_original_y
+ + maxp->max_twilight_points);
+ interpreter->function_defs
+ = (struct sfnt_interpreter_definition *) (interpreter->storage
+ + maxp->max_storage);
+ interpreter->function_defs
+ = ((struct sfnt_interpreter_definition *)
+ ((unsigned char *) interpreter->function_defs + pad));
+ interpreter->instruction_defs = (interpreter->function_defs
+ + maxp->max_function_defs);
+ interpreter->cvt
+ = (sfnt_f26dot6 *) (interpreter->instruction_defs
+ + maxp->max_instruction_defs);
+
+ if (cvt)
+ interpreter->cvt_size = cvt->num_elements;
+ else
+ interpreter->cvt_size = 0;
+
+ /* Now compute the scale. Then, scale up the control value table
+ values. */
+ interpreter->scale
+ = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+ /* Set the PPEM. */
+ interpreter->ppem = pixel_size;
+ interpreter->point_size = point_size;
+
+ /* Zero out the interpreter state from the stack to the end of the
+ instruction definitions. */
+ memset (interpreter->stack, 0, size - sizeof *interpreter);
+
+ /* Initialize the interpreter graphics state. */
+ sfnt_init_graphics_state (&interpreter->state);
+
+ /* Load the control value table. */
+ for (i = 0; i < interpreter->cvt_size; ++i)
+ interpreter->cvt[i]
+ = sfnt_mul_f26dot6_fixed (cvt->values[i] * 64,
+ interpreter->scale);
+
+ /* Fill in the default values for phase, period and threshold. */
+ interpreter->period = 64;
+ interpreter->phase = 0;
+ interpreter->threshold = 0;
+
+ /* Fill in the current call depth. */
+ interpreter->call_depth = 0;
+
+ /* Clear variation axes. They will be set upon a call to
+ `sfnt_vary_interpreter'. */
+ interpreter->n_axis = 0;
+ interpreter->norm_coords = NULL;
+
+ /* Set n_axis now if a fvar table was provided. This way, GXAXIS
+ pushes the correct number of values even if no blend is
+ provided. */
+
+ if (fvar)
+ interpreter->n_axis = fvar->axis_count;
+
+ /* Return the interpreter. */
+ return interpreter;
+}
+
+/* These enums are used to determine why the interpreter is being
+ run. They have the following meanings:
+
+ - The interpreter is being run to interpret a font program.
+ - The interpreter is being run to interpret the control
+ value program.
+ - The interpreter is being run to fit a glyph.
+ - The interpreter is being run as part of an automated test. */
+
+enum sfnt_interpreter_run_context
+ {
+ SFNT_RUN_CONTEXT_FONT_PROGRAM,
+ SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM,
+ SFNT_RUN_CONTEXT_GLYPH_PROGRAM,
+#ifdef TEST
+ SFNT_RUN_CONTEXT_TEST,
+#endif
+ };
+
+/* Cancel execution of the program in INTERPRETER with the specified
+ error REASON and reset the loop counter to 1.
+
+ After this is called, it is probably okay to reuse INTERPRETER.
+ However, instructions must always be reloaded. */
+
+_Noreturn static void
+sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
+ const char *reason)
+{
+ interpreter->trap_reason = reason;
+ interpreter->call_depth = 0;
+ interpreter->state.loop = 1;
+ longjmp (interpreter->trap, 1);
+}
+
+#define STACKSIZE() \
+ (interpreter->SP - interpreter->stack)
+
+#define TRAP(why) \
+ sfnt_interpret_trap (interpreter, (why))
+
+#define MOVE(a, b, n) \
+ memmove (a, b, (n) * sizeof (uint32_t))
+
+#define CHECK_STACK_ELEMENTS(n) \
+ { \
+ if ((interpreter->SP \
+ - interpreter->stack) < n) \
+ TRAP ("stack underflow"); \
+ }
+
+#define CHECK_PREP() \
+ if (!is_prep) \
+ TRAP ("instruction executed not valid" \
+ " outside control value program") \
+
+#define sfnt_add(a, b) \
+ ((int) ((unsigned int) (a) + (unsigned int) (b)))
+
+#define sfnt_sub(a, b) \
+ ((int) ((unsigned int) (a) - (unsigned int) (b)))
+
+#define sfnt_mul(a, b) \
+ ((int) ((unsigned int) (a) * (unsigned int) (b)))
+
+
+
+/* Register, alu and logic instructions. */
+
+#ifndef TEST
+
+#define POP() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : (*(--interpreter->SP)))
+
+#define POP_UNCHECKED() (*(--interpreter->SP))
+
+#else
+
+#define POP() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : ({uint32_t _value; \
+ _value = *(--interpreter->SP); \
+ if (interpreter->pop_hook) \
+ interpreter->pop_hook (interpreter, \
+ _value); \
+ _value;}))
+
+#define POP_UNCHECKED() POP ()
+
+#endif
+
+#define LOOK() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : *(interpreter->SP - 1))
+
+#ifndef TEST
+
+#define PUSH(value) \
+ { \
+ if ((char *) (interpreter->SP + 1) \
+ > (char *) interpreter->twilight_x) \
+ TRAP ("stack overflow"); \
+ \
+ *interpreter->SP = (value); \
+ interpreter->SP++; \
+ }
+
+#define PUSH_UNCHECKED(value) \
+ { \
+ *interpreter->SP = (value); \
+ interpreter->SP++; \
+ }
+
+#else
+
+#define PUSH(value) \
+ { \
+ if ((char *) (interpreter->SP + 1) \
+ > (char *) interpreter->twilight_x) \
+ TRAP ("stack overflow"); \
+ \
+ if (interpreter->push_hook) \
+ interpreter->push_hook (interpreter, \
+ value); \
+ \
+ *interpreter->SP = value; \
+ interpreter->SP++; \
+ }
+
+#define PUSH_UNCHECKED(value) PUSH (value)
+
+#endif
+
+#define PUSH2(high, low) \
+ { \
+ PUSH ((int16_t) ((int8_t) high) << 8 \
+ | low); \
+ } \
+
+#define SRP0() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp0 = p; \
+ }
+
+#define SRP1() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp1 = p; \
+ }
+
+#define SRP2() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp2 = p; \
+ }
+
+#define SZP0() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp0 = zone; \
+ }
+
+#define SZP1() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp1 = zone; \
+ }
+
+#define SZP2() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp2 = zone; \
+ }
+
+#define SZPS() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp0 = zone; \
+ interpreter->state.zp1 = zone; \
+ interpreter->state.zp2 = zone; \
+ }
+
+#define SLOOP() \
+ { \
+ uint32_t loop; \
+ \
+ loop = POP (); \
+ \
+ if (!loop) \
+ TRAP ("loop set to 0"); \
+ \
+ interpreter->state.loop = loop; \
+ }
+
+#define SMD() \
+ { \
+ sfnt_f26dot6 md; \
+ \
+ md = POP (); \
+ \
+ interpreter->state.minimum_distance = md; \
+ }
+
+#define ELSE() \
+ { \
+ sfnt_interpret_else (interpreter); \
+ goto skip_step; \
+ }
+
+#define JMPR() \
+ { \
+ int32_t offset; \
+ \
+ offset = POP (); \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define SCVTCI() \
+ { \
+ sfnt_f26dot6 cutin; \
+ \
+ cutin = POP (); \
+ \
+ interpreter->state.cvt_cut_in = cutin; \
+ }
+
+#define SSWCI() \
+ { \
+ sfnt_f26dot6 cutin; \
+ \
+ cutin = POP (); \
+ \
+ interpreter->state.sw_cut_in = cutin; \
+ }
+
+#define SSW() \
+ { \
+ int32_t single_width; \
+ \
+ single_width = POP (); \
+ \
+ interpreter->state.single_width_value \
+ = (interpreter->scale * single_width \
+ / 1024); \
+ }
+
+#define DUP() \
+ { \
+ uint32_t value; \
+ \
+ value = LOOK (); \
+ PUSH (value); \
+ }
+
+#define CLEAR() \
+ { \
+ interpreter->SP = interpreter->stack; \
+ }
+
+#define SWAP() \
+ { \
+ uint32_t a, b; \
+ \
+ a = POP (); \
+ b = POP (); \
+ \
+ PUSH_UNCHECKED (a); \
+ PUSH_UNCHECKED (b); \
+ }
+
+#define DEPTH() \
+ { \
+ ptrdiff_t diff; \
+ \
+ diff = (interpreter->SP \
+ - interpreter->stack); \
+ PUSH (diff); \
+ }
+
+#define CINDEX() \
+ { \
+ int32_t index; \
+ \
+ index = POP (); \
+ \
+ if (index <= 0 || index > STACKSIZE ()) \
+ TRAP ("stack overflow"); \
+ \
+ PUSH_UNCHECKED (*(interpreter->SP \
+ - index)); \
+ }
+
+#define MINDEX() \
+ { \
+ int32_t index, what; \
+ \
+ index = POP (); \
+ \
+ if (index <= 0 || index > STACKSIZE ()) \
+ TRAP ("stack overflow"); \
+ \
+ what = *(interpreter->SP - index); \
+ MOVE (interpreter->SP - index, \
+ interpreter->SP - index + 1, \
+ index - 1); \
+ *(interpreter->SP - 1) = what; \
+ }
+
+#define RAW() \
+ { \
+ if (why != SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("Read Advance Width without loaded" \
+ " glyph"); \
+ PUSH (interpreter->advance_width); \
+ }
+
+#define CALL() \
+ { \
+ uint32_t id, i; \
+ struct sfnt_interpreter_definition *def; \
+ \
+ id = POP (); \
+ \
+ for (i = 0; \
+ i < interpreter->function_defs_size; \
+ ++i) \
+ { \
+ def = &interpreter->function_defs[i]; \
+ \
+ if (!def->instructions) \
+ TRAP ("invalid function"); \
+ \
+ if (def->opcode == id) \
+ sfnt_interpret_call (def, \
+ interpreter, \
+ why); \
+ if (def->opcode == id) \
+ break; \
+ } \
+ \
+ if (i == interpreter->function_defs_size) \
+ TRAP ("invalid function"); \
+ }
+
+#define LOOPCALL() \
+ { \
+ uint32_t id, i; \
+ int32_t n; \
+ struct sfnt_interpreter_definition *def; \
+ \
+ id = POP (); \
+ n = POP (); \
+ def = NULL; /* Pacify -fanalyzer. */ \
+ \
+ if (n > 65535) \
+ TRAP ("invalid LOOPCALL count"); \
+ \
+ for (i = 0; \
+ i < interpreter->function_defs_size; \
+ ++i) \
+ { \
+ def = &interpreter->function_defs[i]; \
+ \
+ if (!def->instructions) \
+ TRAP ("invalid function"); \
+ \
+ if (def->opcode == id) \
+ break; \
+ } \
+ \
+ if (i == interpreter->function_defs_size) \
+ TRAP ("invalid function"); \
+ \
+ loopcall_begin: \
+ if (n-- <= 0) \
+ break; \
+ \
+ sfnt_interpret_call (def, interpreter, \
+ why); \
+ goto loopcall_begin; \
+ }
+
+#define FDEF() \
+ { \
+ if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("FDEF inside glyph program"); \
+ \
+ sfnt_interpret_fdef (interpreter, POP ()); \
+ goto skip_step; \
+ }
+
+#define ENDF() \
+ { \
+ TRAP ("stray ENDF"); \
+ }
+
+#define NPUSHB() \
+ { \
+ int b, nbytes, IP; \
+ \
+ if ((IP = interpreter->IP + 1) \
+ >= interpreter->num_instructions) \
+ TRAP ("Missing arg to NPUSHB"); \
+ \
+ nbytes \
+ = interpreter->instructions[IP]; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to NPUSHB lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + 1 + nbytes; ++b) \
+ PUSH (interpreter->instructions[b]); \
+ \
+ interpreter->IP += nbytes + 1; \
+ }
+
+#define NPUSHW() \
+ { \
+ int b, nbytes, IP; \
+ \
+ if ((IP = interpreter->IP + 1) \
+ >= interpreter->num_instructions) \
+ TRAP ("Missing arg to NPUSHW"); \
+ \
+ nbytes \
+ = interpreter->instructions[IP] * 2; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to NPUSHW lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + 1 + nbytes; \
+ b += 2) \
+ PUSH2 (interpreter->instructions[b], \
+ interpreter->instructions[b + 1]); \
+ \
+ interpreter->IP += nbytes + 1; \
+ }
+
+#define WS() \
+ { \
+ uint32_t address, value; \
+ \
+ value = POP (); \
+ address = POP (); \
+ \
+ if (address >= interpreter->storage_size) \
+ TRAP ("invalid WS"); \
+ \
+ interpreter->storage[address] = value; \
+ }
+
+#define RS() \
+ { \
+ uint32_t address, value; \
+ \
+ address = POP (); \
+ \
+ if (address >= interpreter->storage_size) \
+ TRAP ("invalid RS"); \
+ \
+ value = interpreter->storage[address]; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define WCVTP() \
+ { \
+ sfnt_f26dot6 value; \
+ uint32_t location; \
+ \
+ value = POP (); \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("WCVTP out of bounds"); \
+ \
+ interpreter->cvt[location] = value; \
+ }
+
+#define RCVT() \
+ { \
+ sfnt_f26dot6 value; \
+ uint32_t location; \
+ \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("out of bounds RCVT"); \
+ \
+ value = interpreter->cvt[location]; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define MPPEM() \
+ { \
+ PUSH (interpreter->ppem); \
+ }
+
+#define MPS() \
+ { \
+ PUSH (interpreter->point_size); \
+ }
+
+#define FLIPON() \
+ { \
+ interpreter->state.auto_flip = true; \
+ }
+
+#define FLIPOFF() \
+ { \
+ interpreter->state.auto_flip = false; \
+ }
+
+#define DEBUG() \
+ { \
+ POP (); /* Value is ignored. */ \
+ }
+
+#define LT() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 < e2 ? 1 : 0); \
+ }
+
+#define LTEQ() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 <= e2 ? 1 : 0); \
+ }
+
+#define GT() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 > e2 ? 1 : 0); \
+ }
+
+#define GTEQ() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 >= e2 ? 1 : 0); \
+ }
+
+#define EQ() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 == e2 ? 1 : 0); \
+ }
+
+#define NEQ() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 != e2 ? 1 : 0); \
+ }
+
+#define ODD() \
+ { \
+ sfnt_f26dot6 e1, result; \
+ \
+ e1 = POP (); \
+ result = abs (e1); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ PUSH_UNCHECKED (((result & 127) \
+ == 64) ? 1 : 0); \
+ }
+
+#define EVEN() \
+ { \
+ sfnt_f26dot6 e1, result; \
+ uint32_t value; \
+ \
+ e1 = POP (); \
+ result = abs (e1); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ value = ((result & 127) == 64) ? 0 : 1; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define IF() \
+ { \
+ uint32_t condition; \
+ \
+ condition = POP (); \
+ sfnt_interpret_if (interpreter, condition); \
+ goto skip_step; \
+ }
+
+#define EIF() \
+ { \
+ \
+ }
+
+#define AND() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 && e2 ? 1 : 0); \
+ }
+
+#define OR() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 || e2 ? 1 : 0); \
+ }
+
+#define NOT() \
+ { \
+ uint32_t e1; \
+ \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (!e1 ? 1 : 0); \
+ }
+
+#define SDB() \
+ { \
+ uint32_t base; \
+ \
+ base = POP (); \
+ \
+ interpreter->state.delta_base = base; \
+ }
+
+#define SDS() \
+ { \
+ uint32_t shift; \
+ \
+ shift = POP (); \
+ \
+ if (shift > 6) \
+ TRAP ("invalid delta shift"); \
+ \
+ interpreter->state.delta_shift = shift; \
+ }
+
+#define ADD() \
+ { \
+ sfnt_f26dot6 n1, n2; \
+ \
+ n1 = POP (); \
+ n2 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_add (n1, n2)); \
+ }
+
+#define SUB() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_sub (n1, n2)); \
+ }
+
+#define DIV() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ if (!n2) \
+ TRAP ("DIV by 0"); \
+ \
+ PUSH_UNCHECKED (sfnt_div_f26dot6 (n1, n2)); \
+ }
+
+#define MUL() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_mul_f26dot6 (n2, n1)); \
+ }
+
+#define ABS() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ \
+ if (n == INT32_MIN) \
+ PUSH_UNCHECKED (0) \
+ else \
+ PUSH_UNCHECKED (n < 0 ? -n : n) \
+ }
+
+#define NEG() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ \
+ if (n == INT32_MIN) \
+ PUSH_UNCHECKED (0) \
+ else \
+ PUSH_UNCHECKED (-n) \
+ }
+
+#define FLOOR() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (sfnt_floor_f26dot6 (n)); \
+ }
+
+#define CEILING() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (sfnt_ceil_f26dot6 (n)); \
+ }
+
+#define WCVTF() \
+ { \
+ int32_t value; \
+ uint32_t location; \
+ \
+ value = POP (); \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("WCVTF out of bounds"); \
+ \
+ interpreter->cvt[location] \
+ = (interpreter->scale * value \
+ / 1024); \
+ }
+
+#define JROT() \
+ { \
+ uint32_t e; \
+ int32_t offset; \
+ \
+ e = POP (); \
+ offset = POP (); \
+ \
+ if (!e) \
+ break; \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define JROF() \
+ { \
+ uint32_t e; \
+ int32_t offset; \
+ \
+ e = POP (); \
+ offset = POP (); \
+ \
+ if (e) \
+ break; \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define ILLEGAL_INSTRUCTION() \
+ { \
+ TRAP ("MS reserved illegal instruction"); \
+ }
+
+#define SCANCTRL() \
+ { \
+ uint32_t value; \
+ \
+ value = POP (); \
+ interpreter->state.scan_control = value; \
+ }
+
+/* Selector bit 8 is undocumented, but present in the Macintosh
+ rasterizer. 02000 is returned if there is a variation axis in
+ use. */
+
+#define GETINFO() \
+ { \
+ uint32_t selector, k; \
+ \
+ selector = POP (); \
+ \
+ k = 0; \
+ \
+ if (selector & 1) \
+ k |= 02; \
+ \
+ if (selector & 8 \
+ && interpreter->norm_coords) \
+ k |= 02000; \
+ \
+ PUSH_UNCHECKED (k); \
+ }
+
+#define IDEF() \
+ { \
+ if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("IDEF inside glyph program"); \
+ \
+ sfnt_interpret_idef (interpreter, POP ()); \
+ goto skip_step; \
+ }
+
+#define ROLL() \
+ { \
+ uint32_t a, b, c; \
+ \
+ CHECK_STACK_ELEMENTS (3); \
+ \
+ a = POP_UNCHECKED (); \
+ b = POP_UNCHECKED (); \
+ c = POP_UNCHECKED (); \
+ \
+ PUSH_UNCHECKED (b); \
+ PUSH_UNCHECKED (a); \
+ PUSH_UNCHECKED (c); \
+ }
+
+#define _MAX() \
+ { \
+ int32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (MAX (e1, e2)); \
+ }
+
+#define _MIN() \
+ { \
+ int32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (MIN (e1, e2)); \
+ }
+
+#define SCANTYPE() \
+ { \
+ POP (); \
+ }
+
+#define INSTCTRL() \
+ { \
+ uint32_t s, v; \
+ \
+ CHECK_PREP (); \
+ s = POP (); \
+ v = POP (); \
+ \
+ if (!s || s > 2) \
+ break; \
+ \
+ interpreter->state.instruct_control \
+ &= ~(1 << s); \
+ \
+ if (v) \
+ interpreter->state.instruct_control \
+ |= (1 << s); \
+ }
+
+/* GXAXIS is undocumented. It seems to return each axis in shortFrac
+ format. */
+
+#define GXAXIS() \
+ { \
+ uint32_t v; \
+ int i; \
+ \
+ for (i = 0; i < interpreter->n_axis; ++i) \
+ { \
+ if (interpreter->norm_coords) \
+ v = interpreter->norm_coords[i] / 4; \
+ else \
+ v = 0; \
+ \
+ PUSH (v); \
+ } \
+ }
+
+#define PUSHB() \
+ { \
+ int b, nbytes, IP; \
+ \
+ IP = interpreter->IP; \
+ nbytes = opcode - 0xb0 + 1; \
+ \
+ if (IP + nbytes + 1 \
+ > interpreter->num_instructions) \
+ TRAP ("args to PUSHB lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + nbytes + 1; ++b) \
+ PUSH (interpreter->instructions[b]); \
+ \
+ interpreter->IP += nbytes; \
+ }
+
+#define PUSHW() \
+ { \
+ int b, nbytes, IP; \
+ \
+ IP = interpreter->IP; \
+ nbytes = (opcode - 0xb8 + 1) * 2; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to PUSHW lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + nbytes + 1; \
+ b += 2) \
+ PUSH2 (interpreter->instructions[b], \
+ interpreter->instructions[b + 1]); \
+ \
+ interpreter->IP += nbytes; \
+ }
+
+
+
+/* Rounding instructions. */
+
+#define ROUND() \
+ { \
+ sfnt_f26dot6 n, result; \
+ \
+ n = POP (); \
+ result = abs (n); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ PUSH_UNCHECKED (n < 0 ? -result : result); \
+ }
+
+#define NROUND() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (n); \
+ }
+
+#define ROFF() \
+ { \
+ interpreter->state.round_state = 5; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RUTG() \
+ { \
+ interpreter->state.round_state = 4; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RDTG() \
+ { \
+ interpreter->state.round_state = 3; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTG() \
+ { \
+ interpreter->state.round_state = 1; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTHG() \
+ { \
+ interpreter->state.round_state = 0; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTDG() \
+ { \
+ interpreter->state.round_state = 2; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define SROUND() \
+ { \
+ uint32_t operand; \
+ \
+ operand = POP (); \
+ sfnt_set_srounding_state (interpreter, \
+ operand, \
+ 0x4000); \
+ interpreter->state.round_state = 6; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define S45ROUND() \
+ { \
+ uint32_t operand; \
+ \
+ operand = POP (); \
+ sfnt_set_srounding_state (interpreter, \
+ operand, \
+ 0x5a82); \
+ interpreter->state.round_state = 7; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+
+
+/* CVT and point delta exception instructions.
+
+ ``Exceptions'' can be placed directly inside the control value
+ table, as it is reloaded every time the point size changes. */
+
+#define DELTAC1() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac1_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (1, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac1_start; \
+ }
+
+#define DELTAC2() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac2_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (2, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac2_start; \
+ }
+
+#define DELTAC3() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac3_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (3, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac3_start; \
+ }
+
+#define DELTAP1() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap1_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (1, interpreter, argn, pn); \
+ n--; \
+ goto deltap1_start; \
+ }
+
+#define DELTAP2() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap2_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (2, interpreter, argn, pn); \
+ n--; \
+ goto deltap2_start; \
+ }
+
+#define DELTAP3() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap3_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (3, interpreter, argn, pn); \
+ n--; \
+ goto deltap3_start; \
+ }
+
+
+
+/* Anachronistic angle instructions. */
+
+#define AA() \
+ { \
+ POP (); \
+ }
+
+#define SANGW() \
+ { \
+ POP (); \
+ }
+
+
+
+/* Projection and freedom vector operations. */
+
+#define PROJECT(x, y) \
+ sfnt_project_vector (interpreter, x, y)
+
+#define DUAL_PROJECT(x, y) \
+ sfnt_dual_project_vector (interpreter, x, y)
+
+#define SVTCAy() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 0, 040000); \
+ sfnt_set_projection_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SVTCAx() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 040000, 0); \
+ sfnt_set_projection_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SPvTCAy() \
+ { \
+ sfnt_set_projection_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SPvTCAx() \
+ { \
+ sfnt_set_projection_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SFvTCAy() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SFvTCAx() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SPVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x07, \
+ false); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ false); \
+ }
+
+#define SFVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x09, \
+ false); \
+ \
+ sfnt_save_freedom_vector (interpreter, \
+ &vector); \
+ }
+
+#define SPVFS() \
+ { \
+ uint32_t y, x; \
+ \
+ y = POP (); \
+ x = POP (); \
+ \
+ sfnt_set_projection_vector (interpreter, x, \
+ y); \
+ }
+
+#define SFVFS() \
+ { \
+ uint16_t y, x; \
+ \
+ y = POP (); \
+ x = POP (); \
+ \
+ sfnt_set_freedom_vector (interpreter, x, \
+ y); \
+ }
+
+#define GPV() \
+ { \
+ struct sfnt_unit_vector vector; \
+ \
+ vector \
+ = interpreter->state.projection_vector; \
+ \
+ PUSH ((uint16_t) vector.x); \
+ PUSH ((uint16_t) vector.y); \
+ }
+
+#define GFV() \
+ { \
+ struct sfnt_unit_vector vector; \
+ \
+ vector \
+ = interpreter->state.freedom_vector; \
+ \
+ PUSH ((uint16_t) vector.x); \
+ PUSH ((uint16_t) vector.y); \
+ }
+
+#define SFVTPV() \
+ { \
+ interpreter->state.freedom_vector \
+ = interpreter->state.projection_vector; \
+ \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define ISECT() \
+ { \
+ uint32_t a0, a1, b0, b1, p; \
+ \
+ a0 = POP (); \
+ a1 = POP (); \
+ b0 = POP (); \
+ b1 = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_isect (interpreter, \
+ a0, a1, b0, b1, p); \
+ }
+
+#define ALIGNPTS() \
+ { \
+ uint32_t p1, p2; \
+ \
+ p1 = POP (); \
+ p2 = POP (); \
+ \
+ sfnt_interpret_alignpts (interpreter, p1, \
+ p2); \
+ }
+
+#define UTP() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ sfnt_interpret_utp (interpreter, p); \
+ }
+
+#define MDAP() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ sfnt_interpret_mdap (interpreter, p, \
+ opcode); \
+ }
+
+#define IUP() \
+ { \
+ sfnt_interpret_iup (interpreter, opcode); \
+ }
+
+#define SHP() \
+ { \
+ sfnt_interpret_shp (interpreter, opcode); \
+ }
+
+#define SHC() \
+ { \
+ uint32_t contour; \
+ \
+ contour = POP (); \
+ \
+ sfnt_interpret_shc (interpreter, contour, \
+ opcode); \
+ }
+
+#define SHZ() \
+ { \
+ uint32_t e; \
+ \
+ e = POP (); \
+ \
+ if (e > 1) \
+ TRAP ("invalid zone!"); \
+ \
+ sfnt_interpret_shz (interpreter, e, \
+ opcode); \
+ }
+
+#define SHPIX() \
+ { \
+ sfnt_f26dot6 pixels, dx, dy; \
+ uint32_t p; \
+ \
+ pixels = POP (); \
+ sfnt_scale_by_freedom_vector (interpreter, \
+ pixels, &dx, \
+ &dy); \
+ \
+ while (interpreter->state.loop--) \
+ { \
+ p = POP (); \
+ sfnt_direct_move_zp2 (interpreter, \
+ p, dx, dy); \
+ } \
+ \
+ interpreter->state.loop = 1; \
+ }
+
+#define IP() \
+ { \
+ sfnt_interpret_ip (interpreter); \
+ }
+
+#define MSIRP() \
+ { \
+ sfnt_f26dot6 d; \
+ uint32_t p; \
+ \
+ d = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_msirp (interpreter, d, p, \
+ opcode); \
+ }
+
+#define ALIGNRP() \
+ { \
+ sfnt_interpret_alignrp (interpreter); \
+ }
+
+#define MIAP() \
+ { \
+ uint32_t cvt; \
+ uint32_t p; \
+ \
+ cvt = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_miap (interpreter, cvt, p, \
+ opcode); \
+ }
+
+#define GC() \
+ { \
+ uint32_t p; \
+ sfnt_f26dot6 x, y, value; \
+ sfnt_f26dot6 org_x, org_y; \
+ \
+ p = POP (); \
+ \
+ sfnt_address_zp2 (interpreter, p, &x, &y, \
+ &org_x, &org_y); \
+ \
+ if (opcode == 0x47) \
+ value = DUAL_PROJECT (org_x, org_y); \
+ else \
+ value = PROJECT (x, y); \
+ \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define SCFS() \
+ { \
+ uint32_t p; \
+ sfnt_f26dot6 c; \
+ \
+ c = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_scfs (interpreter, p, c); \
+ }
+
+#define MD() \
+ { \
+ uint32_t p1, p2; \
+ sfnt_f26dot6 distance; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ distance \
+ = sfnt_measure_distance (interpreter, \
+ p1, p2, \
+ opcode); \
+ PUSH_UNCHECKED (distance); \
+ }
+
+#define FLIPPT() \
+ { \
+ sfnt_interpret_flippt (interpreter); \
+ }
+
+#define FLIPRGOFF() \
+ { \
+ uint32_t h, l; \
+ \
+ h = POP (); \
+ l = POP (); \
+ \
+ sfnt_interpret_fliprgoff (interpreter, \
+ h, l); \
+ }
+
+#define FLIPRGON() \
+ { \
+ uint32_t h, l; \
+ \
+ h = POP (); \
+ l = POP (); \
+ \
+ sfnt_interpret_fliprgon (interpreter, \
+ h, l); \
+ }
+
+#define SDPVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x87, \
+ false); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ false); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x87, \
+ true); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ true); \
+ }
+
+#define MIRP() \
+ { \
+ sfnt_interpret_mirp (interpreter, opcode); \
+ }
+
+#define MDRP() \
+ { \
+ sfnt_interpret_mdrp (interpreter, opcode); \
+ }
+
+
+
+#define NOT_IMPLEMENTED() \
+ sfnt_interpret_unimplemented (interpreter, \
+ opcode, why)
+
+
+
+/* Multiply the specified MAGNITUDE by the contents of INTERPRETER's
+ freedom vector and return the result in *DX and *DY. */
+
+static void
+sfnt_scale_by_freedom_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 magnitude, sfnt_f26dot6 *dx,
+ sfnt_f26dot6 *dy)
+{
+ struct sfnt_unit_vector *vector;
+
+ vector = &interpreter->state.freedom_vector;
+ *dx = sfnt_mul_f2dot14 (vector->x, magnitude);
+ *dy = sfnt_mul_f2dot14 (vector->y, magnitude);
+}
+
+/* Interpret a UTP instruction with the point P in INTERPRETER.
+ Unset any ``touched'' flag inside the point P, relative to the
+ zone in INTERPRETER's ZP0 register.
+
+ Trap upon encountering an out of bounds point. */
+
+static void
+sfnt_interpret_utp (struct sfnt_interpreter *interpreter,
+ uint32_t p)
+{
+ if (!interpreter->state.zp0)
+ {
+ if (p >= interpreter->twilight_zone_size)
+ TRAP ("UTP[] p lies outside twilight zone");
+
+ /* There are no flags in the twilight zone. */
+ return;
+ }
+
+ if (!interpreter->glyph_zone
+ || p >= interpreter->glyph_zone->num_points)
+ TRAP ("UTP[] p lies outside glyph zone");
+
+ interpreter->glyph_zone->flags[p] &= ~SFNT_POINT_TOUCHED_X;
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics state as
+ both the projection and the dual projection vectors.
+
+ If not DUAL_ONLY, set VECTOR as both the projection and dual
+ projection vectors. Otherwise, only set VECTOR as the dual
+ projection vector. */
+
+static void
+sfnt_save_projection_vector (struct sfnt_interpreter *interpreter,
+ struct sfnt_unit_vector *vector,
+ bool dual_only)
+{
+ if (!dual_only)
+ interpreter->state.projection_vector = *vector;
+
+ interpreter->state.dual_projection_vector = *vector;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics
+ state. */
+
+static void
+sfnt_save_freedom_vector (struct sfnt_interpreter *interpreter,
+ struct sfnt_unit_vector *vector)
+{
+ interpreter->state.freedom_vector = *vector;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP2 register.
+
+ If X_ORG and Y_ORG are set, return the original values (prior to
+ any instruction interpretations) in those two locations.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp2 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp2)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP2 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP2 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP1 register.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp1 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp1)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP1 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP1 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP1 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP0 register.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp0 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp0)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP0 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP0 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Set the point NUMBER in the zone referenced by INTERPRETER's ZP2
+ register to the specified X and Y.
+
+ Apply FLAGS to NUMBER's flags in that zone. Trap if NUMBER is out
+ of bounds. */
+
+static void
+sfnt_store_zp2 (struct sfnt_interpreter *interpreter,
+ uint32_t number, sfnt_f26dot6 x, sfnt_f26dot6 y,
+ int flags)
+{
+ if (!interpreter->state.zp2)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+ interpreter->twilight_x[number] = x;
+ interpreter->twilight_y[number] = y;
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP0 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+ interpreter->glyph_zone->x_current[number] = x;
+ interpreter->glyph_zone->y_current[number] = y;
+ interpreter->glyph_zone->flags[number] |= flags;
+}
+
+#if 0
+
+/* Convert the line between the points X1, Y1 and X2, Y2 to standard
+ form.
+
+ Return the two coefficients in *A0 and *B0, and the constant in
+ *C. */
+
+static void
+sfnt_line_to_standard_form (sfnt_f26dot6 x1, sfnt_f26dot6 y1,
+ sfnt_f26dot6 x2, sfnt_f26dot6 y2,
+ sfnt_f26dot6 *a, sfnt_f26dot6 *b,
+ sfnt_f26dot6 *c)
+{
+ sfnt_f26dot6 a_temp, b_temp, c_temp;
+
+ a_temp = sfnt_sub (y2, y1);
+ b_temp = sfnt_sub (x1, x2);
+ c_temp = sfnt_sub (sfnt_mul_f26dot6 (x1, y2),
+ sfnt_mul_f26dot6 (x2, y1));
+
+ *a = a_temp;
+ *b = b_temp;
+ *c = c_temp;
+}
+
+#endif
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP2 register. Trap if it does not. */
+
+static void
+sfnt_check_zp2 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp2)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP2)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP2)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP0 register. Trap if it does not. */
+
+static void
+sfnt_check_zp0 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp0)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP0)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP1 register. Trap if it does not. */
+
+static void
+sfnt_check_zp1 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp1)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP0)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP0 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp0 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp0)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP1 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp1 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp1)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP2 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp2)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's glyph zone
+ by the given DISTANCE along the freedom vector.
+
+ Do not touch the points that are moved.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_glyph_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance, NULL);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's twilight
+ zone by the given DISTANCE along the freedom vector.
+
+ Do not touch the points that are moved.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_twilight_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+}
+
+/* Move the point P in the zone pointed to by the ZP2 register in
+ INTERPRETER's graphics state by DX, and DY.
+
+ Touch the point P in the directions of the movement.
+
+ Check that P is valid; if not, trap. Else, perform the move
+ directly without converting it from the projection vector or to the
+ freedom vector. */
+
+static void
+sfnt_direct_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t p,
+ sfnt_f26dot6 dx, sfnt_f26dot6 dy)
+{
+ if (!interpreter->state.zp2)
+ {
+ if (p >= interpreter->twilight_zone_size)
+ TRAP ("point out of bounds");
+
+ interpreter->twilight_x[p]
+ = sfnt_add (interpreter->twilight_x[p], dx);
+ interpreter->twilight_y[p]
+ = sfnt_add (interpreter->twilight_y[p], dy);
+ }
+ else
+ {
+ if (!interpreter->glyph_zone
+ || p >= interpreter->glyph_zone->num_points)
+ TRAP ("point out of bounds");
+
+ interpreter->glyph_zone->x_current[p]
+ = sfnt_add (interpreter->glyph_zone->x_current[p], dx);
+ interpreter->glyph_zone->y_current[p]
+ = sfnt_add (interpreter->glyph_zone->y_current[p], dy);
+
+ if (dx)
+ interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_X;
+
+ if (dy)
+ interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_Y;
+ }
+}
+
+/* Project the vector VX, VY onto INTERPRETER's projection vector.
+ Return the magnitude of the projection. */
+
+static sfnt_f26dot6
+sfnt_project_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+ return interpreter->state.project (vx, vy, interpreter);
+}
+
+/* Project the vector VX, VY onto INTERPRETER's dual projection
+ vector. Return the magnitude of the projection. */
+
+static sfnt_f26dot6
+sfnt_dual_project_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+ return interpreter->state.dual_project (vx, vy, interpreter);
+}
+
+/* Interpret a FLIPRGOFF instruction in INTERPRTER. Make each point
+ in ZP0 between L and H an off-curve point. */
+
+static void
+sfnt_interpret_fliprgoff (struct sfnt_interpreter *interpreter,
+ uint32_t l, uint32_t h)
+{
+ uint32_t i;
+
+ sfnt_check_zp0 (interpreter, l);
+ sfnt_check_zp0 (interpreter, h);
+
+ if (!interpreter->state.zp0)
+ return;
+
+ for (i = l; i < h; ++i)
+ interpreter->glyph_zone->flags[i] &= ~01;
+}
+
+/* Interpret a FLIPRGON instruction in INTERPRTER. Make each point in
+ ZP0 between L and H an on-curve point. */
+
+static void
+sfnt_interpret_fliprgon (struct sfnt_interpreter *interpreter,
+ uint32_t l, uint32_t h)
+{
+ uint32_t i;
+
+ sfnt_check_zp0 (interpreter, l);
+ sfnt_check_zp0 (interpreter, h);
+
+ if (!interpreter->state.zp0)
+ return;
+
+ for (i = l; i < h; ++i)
+ interpreter->glyph_zone->flags[i] |= ~01;
+}
+
+/* Interpret a FLIPPT instruction in INTERPRETER. For loop times, pop
+ a point in ZP0. If it is an on-curve point, make it an off-curve
+ one, and vice versa. */
+
+static void
+sfnt_interpret_flippt (struct sfnt_interpreter *interpreter)
+{
+ uint32_t point;
+
+ while (interpreter->state.loop--)
+ {
+ point = POP ();
+
+ /* There are no flags in the twilight zone.
+ But first check that the point is within bounds. */
+
+ sfnt_check_zp0 (interpreter, point);
+
+ if (!interpreter->state.zp0)
+ continue;
+
+ /* If POINT is on the curve, make it off the curve and vice
+ versa. */
+
+ if (interpreter->glyph_zone->flags[point] & 01)
+ interpreter->glyph_zone->flags[point] &= ~01;
+ else
+ interpreter->glyph_zone->flags[point] |= 01;
+ }
+
+ /* Restore loop. */
+ interpreter->state.loop = 1;
+}
+
+/* Interpret an SCFS instruction.
+ Move P in ZP2 along the freedom vector until its projection is
+ equal to C.
+
+ If ZP2 is the twilight zone, ``create'' P by setting its original
+ position to the projection. */
+
+static void
+sfnt_interpret_scfs (struct sfnt_interpreter *interpreter,
+ uint32_t p, sfnt_f26dot6 c)
+{
+ sfnt_f26dot6 x, y, distance;
+
+ sfnt_address_zp2 (interpreter, p, &x, &y, NULL, NULL);
+ distance = PROJECT (x, y);
+ sfnt_move_zp2 (interpreter, p, 1, sfnt_sub (c, distance));
+
+ if (!interpreter->state.zp2)
+ {
+ interpreter->twilight_original_x[p] = interpreter->twilight_x[p];
+ interpreter->twilight_original_y[p] = interpreter->twilight_y[p];
+ }
+}
+
+/* Symmetrically round the 26.6 fixed point value X using the rounding
+ mode in INTERPRETER. Return the result. */
+
+static sfnt_f26dot6
+sfnt_round_symmetric (struct sfnt_interpreter *interpreter, sfnt_f26dot6 x)
+{
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ {
+ sign = -1;
+ x = -x;
+ }
+
+ return interpreter->state.round (x, interpreter) * sign;
+}
+
+/* Interpret an MIAP (``Move Indirect Absolute Point'') instruction
+ using INTERPRETER.
+
+ Move P in ZP0 along the freedom vector until its projection on the
+ projection vector is equal to CVT units in the projection vector.
+
+ Finally, set RP0 and RP1 to P.
+
+ If ZP0 is the twilight zone, then first create that point in the
+ twilight zone by setting its ``original position'' to the
+ projection of the value.
+
+ If OPCODE is 0x3f, then in addition check the CVT value against the
+ control value cut-in, and round the magnitudes of the movement. */
+
+static void
+sfnt_interpret_miap (struct sfnt_interpreter *interpreter,
+ uint32_t cvt, uint32_t p, unsigned char opcode)
+{
+ sfnt_f26dot6 x, y, distance, value, delta;
+
+ /* Read the cvt value. */
+
+ if (cvt >= interpreter->cvt_size)
+ TRAP ("out of bounds read to cvt");
+
+ value = interpreter->cvt[cvt];
+
+ /* Now load the point. */
+ sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL);
+
+ /* Create the twilight zone point if necessary.
+ Note that the value used is not rounded. */
+
+ if (!interpreter->state.zp0)
+ {
+ x = interpreter->twilight_x[p]
+ = interpreter->twilight_original_x[p]
+ = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+ value);
+
+ y = interpreter->twilight_y[p]
+ = interpreter->twilight_original_y[p]
+ = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+ value);
+ }
+
+ /* Obtain the original distance. */
+ distance = sfnt_project_vector (interpreter, x, y);
+
+ /* Round the distance and apply the cvt cut in if necessary. */
+
+ if (opcode == 0x3f)
+ {
+ delta = sfnt_sub (value, distance);
+
+ if (delta < 0)
+ delta = -delta;
+
+ /* If delta is more than the cvt cut in (more aptly named ``cut
+ out''), use the original distance. */
+
+ if (delta > interpreter->state.cvt_cut_in)
+ value = distance;
+
+ /* Round value. */
+ value = sfnt_round_symmetric (interpreter, value);
+ }
+
+ /* Move the point by the distance. */
+ sfnt_move_zp0 (interpreter, p, 1, sfnt_sub (value, distance));
+
+ /* Set reference points. */
+ interpreter->state.rp0 = p;
+ interpreter->state.rp1 = p;
+}
+
+/* Perform a single iteration of sfnt_interpret_alignrp. RP0X and
+ RP0Y should be the position of the reference point RP0 in ZP0. */
+
+static void
+sfnt_interpret_alignrp_1 (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 rp0x, sfnt_f26dot6 rp0y)
+{
+ sfnt_f26dot6 distance, x, y;
+ uint32_t point;
+
+ point = POP ();
+
+ /* Load this point. */
+ sfnt_address_zp1 (interpreter, point, &x, &y, NULL, NULL);
+
+ /* Measure the distance from here to rp0. */
+ distance = sfnt_project_vector (interpreter, sfnt_sub (x, rp0x),
+ sfnt_sub (y, rp0y));
+
+ /* Move by the opposite. */
+ sfnt_move_zp1 (interpreter, point, 1, -distance);
+}
+
+/* For loop times, pop a point in ZP1 and align it to RP0 in ZP0 by
+ moving it along the freedom vector until its projected distance
+ from RP0 becomes 0. */
+
+static void
+sfnt_interpret_alignrp (struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 rp0x, rp0y;
+
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, NULL, NULL);
+
+ while (interpreter->state.loop--)
+ {
+ sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y);
+
+ /* Reload RP0 if it is in the same zone as ZP1. */
+ if (interpreter->state.zp0 == interpreter->state.zp1)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, NULL, NULL);
+ }
+
+ interpreter->state.loop = 1;
+}
+
+/* Align the two points P1 and P2 relative to the projection vector.
+ P1 is addressed relative to ZP0, and P2 is addressed relative to
+ ZP1.
+
+ Move both points along the freedom vector by half the magnitude of
+ the the projection of a vector formed by P1.x - P2.x, P1.y - P2.y,
+ upon the projection vector. */
+
+static void
+sfnt_interpret_alignpts (struct sfnt_interpreter *interpreter,
+ uint32_t p1, uint32_t p2)
+{
+ sfnt_f26dot6 p1x, p1y, p2x, p2y;
+ sfnt_f26dot6 magnitude;
+
+ sfnt_address_zp0 (interpreter, p1, &p1x, &p1y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, NULL, NULL);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (p1x, p2x),
+ sfnt_sub (p1y, p2y));
+ magnitude = magnitude / 2;
+
+ /* Now move both points along the freedom vector. */
+ sfnt_move_zp0 (interpreter, p1, 1, magnitude);
+ sfnt_move_zp1 (interpreter, p2, 1, -magnitude);
+}
+
+/* Set the point P in the zone referenced in INTERPRETER's ZP2
+ register to the intersection between the line formed by the points
+ POINT_A0 to POINT_A1 in ZP0 and another line formed by POINT_B0 to
+ POINT_B1 in ZP1.
+
+ Touch the point P. */
+
+static void
+sfnt_interpret_isect (struct sfnt_interpreter *interpreter,
+ uint32_t point_a0, uint32_t point_a1,
+ uint32_t point_b0, uint32_t point_b1,
+ uint32_t p)
+{
+ sfnt_f26dot6 a0x, a0y, a1x, a1y;
+ sfnt_f26dot6 b0x, b0y, b1x, b1y;
+#if 0
+ sfnt_f26dot6 determinant, dx, dy;
+ sfnt_f26dot6 a0, b0, a1, b1;
+ sfnt_f26dot6 c0, c1, px, py;
+#else
+ sfnt_f26dot6 dx, dy, dax, day, dbx, dby;
+ sfnt_f26dot6 discriminant, val, dot_product;
+ sfnt_f26dot6 px, py;
+#endif
+
+ /* Load points. */
+ sfnt_address_zp0 (interpreter, point_a0, &a0x, &a0y, NULL, NULL);
+ sfnt_address_zp0 (interpreter, point_a1, &a1x, &a1y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, point_b0, &b0x, &b0y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, point_b1, &b1x, &b1y, NULL, NULL);
+
+#if 0
+ /* The system is determined from the standard form (look this up) of
+ both lines.
+
+ (the variables below have no relation to C identifiers
+ unless otherwise specified.)
+
+ a0*x + b0*y = c0
+ a1*x + b1*y = c1
+
+ The coefficient matrix is thus
+
+ [ a0 b0
+ a1 b1 ]
+
+ the vector of constants (also just dubbed the ``column vector''
+ by some people)
+
+ [ c0
+ c1 ]
+
+ and the solution vector becomes
+
+ [ x
+ y ]
+
+ Since there are exactly two equations and two unknowns, Cramer's
+ rule applies, and there is no need for any Gaussian elimination.
+
+ The determinant for the coefficient matrix is:
+
+ D = a0*b1 - b0*a1
+
+ the first and second determinants are:
+
+ Dx = c0*b1 - a0*c1
+ Dy = a1*c1 - c0*b1
+
+ and x = Dx / D, y = Dy / D.
+
+ If the system is indeterminate, D will be 0. */
+
+ sfnt_line_to_standard_form (a0x, a0y, a1x, a1y,
+ &a0, &b0, &c0);
+ sfnt_line_to_standard_form (b0x, b0y, b1x, b1y,
+ &a1, &b1, &c1);
+
+
+ /* Compute determinants. */
+ determinant = sfnt_sub (sfnt_mul_fixed (a0, b1),
+ sfnt_mul_fixed (b0, a1));
+ dx = sfnt_sub (sfnt_mul_fixed (c0, b1),
+ sfnt_mul_fixed (a1, c1));
+ dy = sfnt_sub (sfnt_mul_fixed (a0, c1),
+ sfnt_mul_fixed (c0, b0));
+
+ /* Detect degenerate cases. */
+
+ if (determinant == 0)
+ goto degenerate_case;
+#else
+ /* The algorithm above would work with floating point, but overflows
+ too easily with fixed point numbers.
+
+ Instead, use the modified vector projection algorithm found in
+ FreeType. */
+
+ dbx = sfnt_sub (b1x, b0x);
+ dby = sfnt_sub (b1y, b0y);
+ dax = sfnt_sub (a1x, a0x);
+ day = sfnt_sub (a1y, a0y);
+
+ /* Compute vector cross product. */
+ discriminant = sfnt_add (sfnt_mul_f26dot6 (dax, -dby),
+ sfnt_mul_f26dot6 (day, dbx));
+ dot_product = sfnt_add (sfnt_mul_f26dot6 (dax, dbx),
+ sfnt_mul_f26dot6 (day, dby));
+
+ /* Reject any non-intersections and grazing intersections. */
+ if (!(sfnt_mul (19, abs (discriminant)) > abs (dot_product)))
+ return;
+
+ /* Reject any non-intersections. */
+ if (!discriminant)
+ goto degenerate_case;
+
+ dx = sfnt_sub (b0x, a0x);
+ dy = sfnt_sub (b0y, a0y);
+ val = sfnt_add (sfnt_mul_f26dot6 (dx, -dby),
+ sfnt_mul_f26dot6 (dy, dbx));
+
+ /* Project according to these values. */
+ dx = sfnt_add (a0x, sfnt_multiply_divide_signed (val, dax,
+ discriminant));
+ dy = sfnt_add (a0y, sfnt_multiply_divide_signed (val, day,
+ discriminant));
+#endif
+
+ sfnt_store_zp2 (interpreter, p,
+#if 0
+ sfnt_div_fixed (dx, determinant),
+ sfnt_div_fixed (dy, determinant),
+#else
+ dx, dy,
+#endif
+ SFNT_POINT_TOUCHED_BOTH);
+ return;
+
+ degenerate_case:
+
+ /* Apple says that in this case:
+
+ Px = (a0x + a1x) / 2 + (b0x + b1x) / 2
+ ---------------------------------
+ 2
+ Py = (a0y + a1y) / 2 + (b0y + b1y) / 2
+ ---------------------------------
+ 2 */
+
+ px = (sfnt_add (a0x, a1x) / 2 + sfnt_add (b0x, b1x) / 2) / 2;
+ py = (sfnt_add (a0y, a1y) / 2 + sfnt_add (b0y, b1y) / 2) / 2;
+ sfnt_store_zp2 (interpreter, p, px, py,
+ SFNT_POINT_TOUCHED_BOTH);
+}
+
+/* Compute the square root of the 16.16 fixed point number N. */
+
+static sfnt_fixed
+sfnt_sqrt_fixed (sfnt_fixed n)
+{
+ int count;
+ unsigned int root, rem_hi, rem_lo, possible;
+
+ root = 0;
+
+ if (n > 0)
+ {
+ rem_hi = 0;
+ rem_lo = n;
+ count = 24;
+
+ do
+ {
+ rem_hi = (rem_hi << 2) | (rem_lo >> 30);
+ rem_lo <<= 2;
+ root <<= 1;
+ possible = (root << 1) + 1;
+
+ if (rem_hi >= possible)
+ {
+ rem_hi -= possible;
+ root += 1;
+ }
+ }
+ while (--count);
+ }
+
+ return root;
+}
+
+/* Compute a unit vector describing a vector VX, VY. Return the value
+ in *VECTOR. */
+
+static void
+sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_unit_vector *vector)
+{
+ sfnt_f26dot6 x_squared, y_squared;
+ sfnt_fixed n, magnitude;
+
+ if (!vx && !vy)
+ {
+ /* If vx and vy are both zero, then just project
+ horizontally. */
+
+ fail:
+ vector->x = 04000;
+ vector->y = 0;
+ return;
+ }
+
+ /* Scale vx and vy up if they won't at least make 1. */
+
+ while (!(vx < -32 || vx > 32) && !(vy < -32 || vy > 32))
+ {
+ vx = vx * 2;
+ vy = vy * 2;
+ }
+
+ /* Compute the magnitude of this vector. */
+ x_squared = sfnt_mul_f26dot6 (vx, vx);
+ y_squared = sfnt_mul_f26dot6 (vy, vy);
+
+ /* x_squared and y_squared can end up too large to fit in a 16.16
+ fixed. Scale both values down until they fit. */
+
+ while (x_squared > 0x200000 || y_squared > 0x200000
+ || x_squared < -0x200000 || y_squared < -0x200000)
+ {
+ x_squared /= 2;
+ y_squared /= 2;
+ }
+
+ /* Convert to 16.16 for greater precision. */
+ n = sfnt_add (x_squared, y_squared) * 1024;
+
+ /* Get hypotenuse of the triangle from vx, 0, to 0, vy. */
+ magnitude = sfnt_sqrt_fixed (n);
+
+ /* Avoid division by zero. */
+ if (!magnitude)
+ goto fail;
+
+ /* Long division.. eek! */
+ vector->x = (sfnt_div_fixed (vx * 1024, magnitude) / 4);
+ vector->y = (sfnt_div_fixed (vy * 1024, magnitude) / 4);
+}
+
+/* Compute a unit vector describing the direction of a line from the
+ point P2 to the point P1. Save the result in *VECTOR.
+
+ P2 is the address of a point in the zone specified in the ZP2
+ register. P1 is the address of a point in the zone specified in
+ the ZP1 register. Take the values of both registers from the
+ specified INTERPRETER's graphics state.
+
+ If PERPENDICULAR, then *VECTOR will be rotated 90 degrees
+ counter-clockwise. Else, *VECTOR will be parallel to the line.
+
+ If ORIGINAL, then the coordinates used to calculate the line will
+ be those prior to instructing. Otherwise, the current coordinates
+ will be used. */
+
+static void
+sfnt_line_to_vector (struct sfnt_interpreter *interpreter,
+ uint32_t p2, uint32_t p1,
+ struct sfnt_unit_vector *vector,
+ bool perpendicular, bool original)
+{
+ sfnt_f26dot6 x2, y2, original_x2, original_y2;
+ sfnt_f26dot6 x1, y1, original_x1, original_y1;
+ sfnt_f26dot6 a, b, temp;
+
+ sfnt_address_zp2 (interpreter, p2, &x2, &y2, &original_x2,
+ &original_y2);
+ sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1,
+ &original_y1);
+
+ /* Use original coordinates if specified. */
+
+ if (original)
+ {
+ x2 = original_x2;
+ y2 = original_y2;
+ x1 = original_x1;
+ y1 = original_y1;
+ }
+
+ /* Calculate the vector between X2, Y2, and X1, Y1. */
+ a = sfnt_sub (x1, x2);
+ b = sfnt_sub (y1, y2);
+
+ /* Rotate counterclockwise if necessary. */
+
+ if (perpendicular)
+ {
+ temp = b;
+ b = a;
+ a = -temp;
+ }
+
+ /* Normalize this vector, turning it into a unit vector. */
+ sfnt_normalize_vector (a, b, vector);
+}
+
+/* Measure the distance between P1 in ZP0 and P2 in ZP1,
+ relative to the projection or dual projection vector.
+
+ Return the distance of P1 and P2 relative to their original
+ un-instructed positions should OPCODE be 0x4A, and to their
+ instructed positions should OPCODE be 0x49. */
+
+static sfnt_f26dot6
+sfnt_measure_distance (struct sfnt_interpreter *interpreter,
+ uint32_t p1, uint32_t p2,
+ unsigned char opcode)
+{
+ sfnt_f26dot6 p1x, p1y, p1_original_x, p1_original_y;
+ sfnt_f26dot6 p2x, p2y, p2_original_x, p2_original_y;
+
+ /* P1 is relative to ZP0 and P2 is relative to ZP1.
+ Apple's manual says this, Microsoft's does not. */
+
+ sfnt_address_zp0 (interpreter, p1, &p1x, &p1y,
+ &p1_original_x, &p1_original_y);
+ sfnt_address_zp1 (interpreter, p2, &p2x, &p2y,
+ &p2_original_x, &p2_original_y);
+
+ if (opcode == 0x4A)
+ return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x),
+ sfnt_sub (p1_original_y, p2_original_y));
+
+ return PROJECT (sfnt_sub (p1x, p2x),
+ sfnt_sub (p1y, p2y));
+}
+
+/* Interpret an MSIRP instruction in INTERPRETER.
+ Take a point P, and make the distance between P in ZP1 and the
+ current position of RP0 in ZP0 equal to D.
+
+ If ZP1 is the twilight zone, then create the point P by setting its
+ position and relative positions.
+
+ Then, if OPCODE is equal to 0x3b, make P RP0. */
+
+static void
+sfnt_interpret_msirp (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 d, uint32_t p, unsigned char opcode)
+{
+ sfnt_f26dot6 rp0x, rp0y, rp0_original_x, rp0_original_y;
+ sfnt_f26dot6 x, y;
+ sfnt_f26dot6 old_distance, temp;
+
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, &rp0_original_x,
+ &rp0_original_y);
+ sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
+
+ if (!interpreter->state.zp1)
+ {
+ /* Create this point in the twilight zone at RP0. */
+
+ x = interpreter->twilight_x[p] = rp0x;
+ y = interpreter->twilight_y[p] = rp0y;
+
+ /* Now set the original positions to the projected difference
+ from rp0. This makes sense once you think about it. */
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, d);
+ temp = sfnt_add (temp, rp0_original_x);
+ interpreter->twilight_original_x[p] = temp;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, d);
+ temp = sfnt_add (temp, rp0_original_y);
+ interpreter->twilight_original_y[p] = temp;
+ }
+
+ /* Compute the original distance. */
+ old_distance = sfnt_project_vector (interpreter,
+ sfnt_sub (x, rp0x),
+ sfnt_sub (y, rp0y));
+
+ /* Move the point. */
+ sfnt_move_zp1 (interpreter, p, 1, sfnt_sub (d, old_distance));
+
+ /* Nothing in the TrueType reference manual says directly that this
+ instruction should change rp1 and rp2. However, it says this
+ instruction is ``very similar to the MIRP[] instruction
+ except...'', and FreeType seems to do this, so do it as well. */
+
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode == 0x3b)
+ interpreter->state.rp0 = p;
+}
+
+/* Interpret an IP instruction in INTERPRETER. For loop times, pop a
+ single point in ZP2, and interpolate it so that its original
+ relationship to the points RP1 in ZP0 and RP2 in ZP1 as measured
+ along the dual projection vector continues to hold true. */
+
+static void
+sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 rp1x, rp1y, rp1_original_x, rp1_original_y;
+ sfnt_f26dot6 rp2x, rp2y, rp2_original_x, rp2_original_y;
+ sfnt_f26dot6 range, new_range, org_distance, cur_distance;
+ sfnt_f26dot6 new_distance;
+ uint32_t p;
+ sfnt_f26dot6 x, y, original_x, original_y;
+
+ /* First load both reference points. */
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &rp1x, &rp1y, &rp1_original_x,
+ &rp1_original_y);
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &rp2x, &rp2y, &rp2_original_x,
+ &rp2_original_y);
+
+ /* Get the original distance between of RP1 and RP2 measured
+ relative to the dual projection vector. */
+ range = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (rp2_original_x,
+ rp1_original_x),
+ sfnt_sub (rp2_original_y,
+ rp1_original_y));
+
+ /* Get the new distance. */
+ new_range = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (rp2x, rp1x),
+ sfnt_sub (rp2y, rp1y));
+
+ while (interpreter->state.loop--)
+ {
+ p = POP ();
+
+ /* Load this point relative to zp2. */
+ sfnt_address_zp2 (interpreter, p, &x, &y, &original_x,
+ &original_y);
+
+ /* Now compute the old distance from this point to rp1. */
+ org_distance
+ = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (original_x,
+ rp1_original_x),
+ sfnt_sub (original_y,
+ rp1_original_y));
+
+ /* And the current distance from this point to rp1, so
+ how much to move can be determined. */
+ cur_distance
+ = sfnt_project_vector (interpreter,
+ sfnt_sub (x, rp1x),
+ sfnt_sub (y, rp1y));
+
+ /* Finally, apply the ratio of the new distance between RP1 and
+ RP2 to that of the old distance between the two reference
+ points to org_distance, making new_distance.
+
+ If both reference points occupy the same position on the dual
+ projection vector, then simply use the old distance. */
+
+ if (org_distance)
+ {
+ if (range)
+ new_distance
+ = sfnt_multiply_divide_signed (org_distance,
+ new_range, range);
+ else
+ new_distance = org_distance;
+ }
+ else
+ new_distance = 0;
+
+ /* And move the point along the freedom vector to reflect the
+ change in distance. */
+ sfnt_move_zp2 (interpreter, p, 1,
+ sfnt_sub (new_distance, cur_distance));
+ }
+
+ interpreter->state.loop = 1;
+}
+
+/* Apply the delta specified by OPERAND to the control value table
+ entry at INDEX currently loaded inside INTERPRETER.
+
+ Trap if INDEX is out of bounds.
+
+ NUMBER is the number of the specific DELTAC instruction this delta
+ is being applied on behalf of. It must be between 1 and 3. */
+
+static void
+sfnt_deltac (int number, struct sfnt_interpreter *interpreter,
+ unsigned int index, unsigned char operand)
+{
+ int ppem, delta;
+
+ /* Make sure INDEX is a valid cvt entry. */
+
+ if (index >= interpreter->cvt_size)
+ TRAP ("DELTACn instruction out of bounds");
+
+ /* operand is an 8 bit number. The most significant 4 bits
+ represent a specific PPEM size at which to apply the delta
+ specified in the low 4 bits, summed with an instruction specific
+ delta, and the current delta base. */
+
+ ppem = (operand >> 4) + interpreter->state.delta_base;
+
+ switch (number)
+ {
+ case 1:
+ break;
+
+ case 2:
+ ppem += 16;
+ break;
+
+ case 3:
+ ppem += 32;
+ break;
+ }
+
+ /* Don't apply the delta if the ppem size doesn't match. */
+
+ if (interpreter->ppem != ppem)
+ return;
+
+ /* Now, determine the delta using the low 4 bits. The low 4 bits
+ actually specify a ``magnitude'' to apply to the delta, and do
+ not have an encoding for the delta 0. */
+
+ switch (operand & 0xf)
+ {
+ case 0:
+ delta = -8;
+ break;
+
+ case 1:
+ delta = -7;
+ break;
+
+ case 2:
+ delta = -6;
+ break;
+
+ case 3:
+ delta = -5;
+ break;
+
+ case 4:
+ delta = -4;
+ break;
+
+ case 5:
+ delta = -3;
+ break;
+
+ case 6:
+ delta = -2;
+ break;
+
+ case 7:
+ delta = -1;
+ break;
+
+ case 8:
+ delta = 1;
+ break;
+
+ case 9:
+ delta = 2;
+ break;
+
+ case 10:
+ delta = 3;
+ break;
+
+ case 11:
+ delta = 4;
+ break;
+
+ case 12:
+ delta = 5;
+ break;
+
+ case 13:
+ delta = 6;
+ break;
+
+ case 14:
+ delta = 7;
+ break;
+
+ case 15:
+ delta = 8;
+ break;
+
+ /* To pacify -fanalyzer. */
+ default:
+ abort ();
+ }
+
+ /* Now, scale up the delta by the step size, which is determined by
+ the delta shift. */
+ delta *= 1l << (6 - interpreter->state.delta_shift);
+
+ /* Finally, apply the delta to the CVT entry. */
+ interpreter->cvt[index] = sfnt_add (interpreter->cvt[index],
+ delta);
+}
+
+/* Interpret an MDAP (Move Direct Absolute Point) instruction with the
+ opcode OPCODE and the operand P in INTERPRETER.
+
+ Touch the point P (within the zone specified in zp0) in the
+ directions specified in the freedom vector. Then, if OPCODE is
+ 0x7f, round the point and move it the rounded distance along the
+ freedom vector.
+
+ Finally, set the RP0 and RP1 registers to P. */
+
+static void
+sfnt_interpret_mdap (struct sfnt_interpreter *interpreter,
+ uint32_t p, uint32_t opcode)
+{
+ sfnt_f26dot6 here, distance, px, py;
+
+ sfnt_address_zp0 (interpreter, p, &px, &py, NULL, NULL);
+
+ /* Measure the current distance. */
+ here = sfnt_project_vector (interpreter, px, py);
+
+ if (opcode == 0x7f)
+ {
+ /* Measure distance, round, then move to the distance. */
+ distance = sfnt_project_vector (interpreter, px, py);
+ distance = sfnt_round_symmetric (interpreter, distance);
+ distance = sfnt_sub (distance, here);
+ }
+ else
+ /* Don't move. Just touch the point. */
+ distance = 0;
+
+ sfnt_move_zp0 (interpreter, p, 1, distance);
+
+ interpreter->state.rp0 = p;
+ interpreter->state.rp1 = p;
+}
+
+/* Apply the delta specified by OPERAND to the point P in ZP0
+ currently loaded inside INTERPRETER.
+
+ Trap if P is out of bounds.
+
+ NUMBER is the number of the specific DELTAP instruction this delta
+ is being applied on behalf of. It must be between 1 and 3. */
+
+static void
+sfnt_deltap (int number, struct sfnt_interpreter *interpreter,
+ unsigned char operand, unsigned int index)
+{
+ int ppem, delta;
+
+ return;
+
+ /* Extract the ppem from OPERAND. The format is the same as in
+ sfnt_deltac. */
+
+ ppem = (operand >> 4) + interpreter->state.delta_base;
+
+ switch (number)
+ {
+ case 1:
+ break;
+
+ case 2:
+ ppem += 16;
+ break;
+
+ case 3:
+ ppem += 32;
+ break;
+ }
+
+ /* Don't apply the delta if the ppem size doesn't match. */
+
+ if (interpreter->ppem != ppem)
+ return;
+
+ /* Now, determine the magnitude of the movement and find the
+ delta. */
+
+ switch (operand & 0xf)
+ {
+ case 0:
+ delta = -8;
+ break;
+
+ case 1:
+ delta = -7;
+ break;
+
+ case 2:
+ delta = -6;
+ break;
+
+ case 3:
+ delta = -5;
+ break;
+
+ case 4:
+ delta = -4;
+ break;
+
+ case 5:
+ delta = -3;
+ break;
+
+ case 6:
+ delta = -2;
+ break;
+
+ case 7:
+ delta = -1;
+ break;
+
+ case 8:
+ delta = 1;
+ break;
+
+ case 9:
+ delta = 2;
+ break;
+
+ case 10:
+ delta = 3;
+ break;
+
+ case 11:
+ delta = 4;
+ break;
+
+ case 12:
+ delta = 5;
+ break;
+
+ case 13:
+ delta = 6;
+ break;
+
+ case 14:
+ delta = 7;
+ break;
+
+ case 15:
+ delta = 8;
+ break;
+
+ /* To pacify -fanalyzer. */
+ default:
+ abort ();
+ }
+
+ /* Now, scale up the delta by the step size, which is determined by
+ the delta shift. */
+ delta *= 1l << (6 - interpreter->state.delta_shift);
+
+ /* Move the point. */
+ sfnt_check_zp0 (interpreter, index);
+ sfnt_move_zp0 (interpreter, index, 1, delta);
+}
+
+/* Needed by sfnt_interpret_call. */
+static void sfnt_interpret_run (struct sfnt_interpreter *,
+ enum sfnt_interpreter_run_context);
+
+/* Call DEFINITION inside INTERPRETER.
+
+ Save INTERPRETER->IP, INTERPRETER->instructions, and
+ INTERPRETER->num_instructions onto the C stack.
+
+ Then, load the instructions in DEFINITION, and run the interpreter
+ again with the context CONTEXT.
+
+ Finally, restore all values. */
+
+static void
+sfnt_interpret_call (struct sfnt_interpreter_definition *definition,
+ struct sfnt_interpreter *interpreter,
+ enum sfnt_interpreter_run_context context)
+{
+ uint16_t num_instructions;
+ int IP;
+ unsigned char *instructions;
+
+ /* Check that no recursion is going on. */
+ if (interpreter->call_depth++ >= 128)
+ TRAP ("CALL called CALL more than 127 times");
+
+ /* Save the old IP, instructions and number of instructions. */
+ num_instructions = interpreter->num_instructions;
+ IP = interpreter->IP;
+ instructions = interpreter->instructions;
+
+ /* Load and run the definition. */
+ interpreter->num_instructions = definition->instruction_count;
+ interpreter->instructions = definition->instructions;
+ interpreter->IP = 0;
+ sfnt_interpret_run (interpreter, context);
+
+ /* Restore the old values. */
+ interpreter->num_instructions = num_instructions;
+ interpreter->IP = IP;
+ interpreter->instructions = instructions;
+ interpreter->call_depth--;
+}
+
+/* Set the detailed rounding state in interpreter, on behalf of either
+ an SROUND or S45ROUND instruction that has been given the operand
+ OPERAND.
+
+ Use the specified GRID_PERIOD to determine the period. It is is a
+ 18.14 fixed point number, but the rounding state set will be a 26.6
+ fixed point number. */
+
+static void
+sfnt_set_srounding_state (struct sfnt_interpreter *interpreter,
+ uint32_t operand, sfnt_f18dot14 grid_period)
+{
+ sfnt_f18dot14 period, phase, threshold;
+
+ /* The most significant 2 bits in the 8 bit OPERAND determine the
+ period. */
+
+ switch ((operand & 0xc0) >> 6)
+ {
+ case 0:
+ period = grid_period / 2;
+ break;
+
+ case 1:
+ period = grid_period;
+ break;
+
+ case 2:
+ period = grid_period * 2;
+ break;
+
+ case 3:
+ default:
+ TRAP ("reserved period given to SROUND");
+ }
+
+ /* The next two bits determine the phase. */
+
+ switch ((operand & 0x30) >> 4)
+ {
+ case 0:
+ phase = 0;
+ break;
+
+ case 1:
+ phase = period / 4;
+ break;
+
+ case 2:
+ phase = period / 2;
+ break;
+
+ case 3:
+ default:
+ phase = period * 3 / 2;
+ break;
+ }
+
+ /* And the least significant 4 bits determine the threshold. */
+
+ if (operand & 0x0f)
+ threshold = (((int) (operand & 0x0f) - 4)
+ * period / 8);
+ else
+ threshold = period - 1;
+
+ /* Now extend these values to 26.6 format and set them. */
+ interpreter->period = period >> 8;
+ interpreter->phase = phase >> 8;
+ interpreter->threshold = threshold >> 8;
+}
+
+/* Move to the next opcode in INTERPRETER's instruction stream.
+ Value is the opcode originally at INTERPRETER->IP. */
+
+static unsigned char
+sfnt_skip_code (struct sfnt_interpreter *interpreter)
+{
+ unsigned char opcode;
+ int nbytes;
+
+ if (interpreter->IP == interpreter->num_instructions)
+ TRAP ("IP at end of instruction stream");
+
+ /* Load opcode at IP. */
+ opcode = interpreter->instructions[interpreter->IP];
+
+ if (opcode == 0x40 || opcode == 0x41)
+ {
+ if (interpreter->IP + 1 >= interpreter->num_instructions)
+ TRAP ("Missing arg to NPUSHB or NPUSHW");
+
+ /* Figure out how many bytes or words to push. */
+
+ nbytes = interpreter->instructions[interpreter->IP + 1];
+
+ if (opcode == 0x41)
+ nbytes *= 2;
+
+ if (interpreter->IP + 2 + nbytes > interpreter->num_instructions)
+ TRAP ("args to NPUSH instruction lie outside IS");
+
+ /* Increment IP by so much. */
+ interpreter->IP += 2 + nbytes;
+ }
+ else if (opcode >= 0xb0 && opcode <= 0xb7)
+ {
+ nbytes = opcode - 0xb0 + 1;
+
+ if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+ TRAP ("args to PUSHB instruction lie outide IS");
+
+ interpreter->IP += 1 + nbytes;
+ }
+ else if (opcode >= 0xb8 && opcode <= 0xbf)
+ {
+ nbytes = (opcode - 0xb8 + 1) * 2;
+
+ if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+ TRAP ("args to PUSHW instruction lie outide IS");
+
+ interpreter->IP += 1 + nbytes;
+ }
+ else
+ interpreter->IP++;
+
+ return opcode;
+}
+
+/* Interpret the unimplemented operation OPCODE using INTERPRETER, and
+ the context WHY. If there is no instruction definition named
+ OPCODE, trap. */
+
+static void
+sfnt_interpret_unimplemented (struct sfnt_interpreter *interpreter,
+ unsigned char opcode,
+ enum sfnt_interpreter_run_context why)
+{
+ uint32_t i;
+ struct sfnt_interpreter_definition *def;
+
+ for (i = 0; i < interpreter->instruction_defs_size; ++i)
+ {
+ def = &interpreter->instruction_defs[i];
+
+ if (def->opcode == opcode)
+ {
+ if (!def->instructions)
+ TRAP ("** ERROR ** malformed internal instruction"
+ " definition");
+
+ sfnt_interpret_call (def, interpreter, why);
+ return;
+ }
+ }
+
+ TRAP ("invalid instruction");
+}
+
+/* Start a function definition in INTERPRETER, with the function
+ opcode OPCODE. */
+
+static void
+sfnt_interpret_fdef (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ size_t i, num_fdefs;
+ int IP;
+ unsigned char instruction;
+
+ IP = interpreter->IP + 1;
+ num_fdefs = 0;
+
+ /* Now find an ENDF. */
+
+ while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+ {
+ if (interpreter->IP >= interpreter->num_instructions)
+ TRAP ("missing ENDF");
+
+ /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+ Prohibit nested FDEFs or IDEFS. */
+ if (instruction == 0x2c || instruction == 0x89)
+ ++num_fdefs;
+
+ if (num_fdefs > 1)
+ TRAP ("IDEF or FDEF before ENDF");
+ }
+
+ /* ENDF has been found. Now save the function definition. Try to
+ find an existing function definition with this opcode. If that
+ fails, make i the first available function definition. */
+
+ for (i = 0; i < interpreter->function_defs_size; ++i)
+ {
+ if (interpreter->function_defs[i].opcode == opcode
+ || !interpreter->function_defs[i].instructions)
+ break;
+ }
+
+ if (i == interpreter->function_defs_size)
+ TRAP ("number of fdefs exceeded maxp->max_function_defs");
+
+ /* Save the opcode of this function definition. */
+ interpreter->function_defs[i].opcode = opcode;
+
+ /* Make sure to ignore the trailing ENDF instruction. */
+ interpreter->function_defs[i].instruction_count
+ = interpreter->IP - IP - 1;
+
+ /* Now save a pointer to the instructions. */
+ interpreter->function_defs[i].instructions = interpreter->instructions + IP;
+}
+
+/* Start an instruction definition in INTERPRETER, with the
+ instruction opcode OPCODE. */
+
+static void
+sfnt_interpret_idef (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ size_t i, num_fdefs;
+ int IP;
+ unsigned char instruction;
+
+ IP = interpreter->IP + 1;
+ num_fdefs = 0;
+
+ /* Now find an ENDF. */
+
+ while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+ {
+ if (interpreter->IP >= interpreter->num_instructions)
+ TRAP ("missing ENDF");
+
+ /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+ Prohibit nested FDEFs or IDEFS. */
+ if (instruction == 0x2c || instruction == 0x89)
+ ++num_fdefs;
+
+ if (num_fdefs > 1)
+ TRAP ("IDEF or FDEF before ENDF");
+ }
+
+ /* ENDF has been found. Now save the instruction definition. Try to
+ find an existing instruction definition with this opcode. If that
+ fails, make i the first available instruction definition. */
+
+ for (i = 0; i < interpreter->instruction_defs_size; ++i)
+ {
+ if (interpreter->instruction_defs[i].opcode == opcode
+ || !interpreter->instruction_defs[i].instructions)
+ break;
+ }
+
+ if (i == interpreter->instruction_defs_size)
+ TRAP ("number of defs exceeded maxp->max_instruction_defs");
+
+ /* Save the opcode of this instruction definition. */
+ interpreter->instruction_defs[i].opcode = opcode;
+
+ /* Make sure to ignore the trailing ENDF instruction. */
+ interpreter->instruction_defs[i].instruction_count
+ = interpreter->IP - IP - 1;
+
+ /* Now save a pointer to the instructions. */
+ interpreter->instruction_defs[i].instructions
+ = interpreter->instructions + IP;
+}
+
+/* Interpret the specified conditional at INTERPRETER->IP.
+ If CONDITION, evaluate this branch up until the next ELSE or ENDIF.
+ Else, evaluate the branch from a matching ELSE condition, if
+ one exists. */
+
+static void
+sfnt_interpret_if (struct sfnt_interpreter *interpreter,
+ bool condition)
+{
+ int nifs;
+ bool need_break;
+ unsigned char opcode;
+
+ if (condition)
+ {
+ interpreter->IP++;
+ return;
+ }
+
+ /* Number of ifs. */
+ nifs = 0;
+ need_break = false;
+
+ /* Break past the matching else condition. */
+ do
+ {
+ /* Load the current opcode, then increase IP. */
+ opcode = sfnt_skip_code (interpreter);
+
+ if (interpreter->IP >= interpreter->num_instructions)
+ break;
+
+ switch (opcode)
+ {
+ case 0x58: /* IF */
+ nifs++;
+ break;
+
+ case 0x1B: /* ELSE */
+ if (nifs == 1)
+ need_break = true;
+
+ break;
+
+ case 0x59: /* EIF */
+ nifs--;
+ if (nifs == 0)
+ need_break = true;
+
+ break;
+ }
+ }
+ while (!need_break);
+}
+
+/* Interpret the specified ELSE branch at INTERPRETER->IP.
+ Evaluate starting from a matching ENDIF instruction.
+
+ If IF has set INTERPRETER->IP to a code within an ELSE branch, this
+ will not be called. */
+
+static void
+sfnt_interpret_else (struct sfnt_interpreter *interpreter)
+{
+ int nifs;
+ unsigned char opcode;
+
+ /* Number of ifs. */
+ nifs = 1;
+
+ /* Break past the matching ENDIF condition. */
+ do
+ {
+ /* Load the current opcode, then increase IP. */
+ opcode = sfnt_skip_code (interpreter);
+
+ if (interpreter->IP >= interpreter->num_instructions)
+ break;
+
+ switch (opcode)
+ {
+ case 0x58: /* IF */
+ nifs++;
+ break;
+
+ case 0x59: /* EIF */
+ nifs--;
+
+ break;
+ }
+ }
+ while (nifs > 0);
+}
+
+/* ``Add engine compensation to X''. Since engine compensation is not
+ implemented here, this simply returns X. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_none (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+ return x;
+}
+
+/* Round X to the grid after adding engine compensation. Return the
+ result. INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_grid (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+ return sfnt_round_f26dot6 (x);
+}
+
+/* Round X to the nearest half integer or integer and return the
+ result. INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_double_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return (x + 020) & ~037;
+}
+
+/* Take the floor of X and return the result. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_down_to_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_floor_f26dot6 (x);
+}
+
+/* Take the ceiling of X and return the result. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_up_to_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_ceil_f26dot6 (x);
+}
+
+/* Round X to only the nearest half integer and return the result.
+ INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_half_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_floor_f26dot6 (x) + 32;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+ state'' in INTERPRETER. Value is the result. */
+
+static sfnt_f26dot6
+sfnt_round_super (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 value;
+
+ /* Compute the rounded value. */
+ value = sfnt_add ((interpreter->threshold
+ - interpreter->phase), x);
+ value = sfnt_add (value & -interpreter->period,
+ interpreter->phase);
+
+ /* Remember that since the phase is specified by font instructions,
+ it is possible for the sign to be changed. In that case, return
+ the phase itself. */
+
+ return value < 0 ? interpreter->phase : value;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+ state'' in INTERPRETER, but suitably for values that are multiples
+ of the sqrt of 2. Value is the result. */
+
+static sfnt_f26dot6
+sfnt_round_super45 (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 value;
+
+ /* Compute the rounded value. */
+
+ value = ((sfnt_add (x, (interpreter->threshold
+ - interpreter->phase))
+ / interpreter->period)
+ * interpreter->period);
+ value = sfnt_add (value, interpreter->phase);
+
+ /* Remember that since the phase is specified by font instructions,
+ it is possible for the sign to be changed. In that case, return
+ the phase itself. */
+
+ return value < 0 ? interpreter->phase : value;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, assuming that INTERPRETER's
+ projection vector is on the X axis.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_x_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return vx;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, assuming that INTERPRETER's
+ projection vector is on the Y axis.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_y_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return vy;
+}
+
+/* Calculate AX * BX + AY * BY divided by 16384. */
+
+static int32_t
+sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
+{
+#ifndef INT64_MAX
+ int32_t m, s, hi1, hi2, hi;
+ uint32_t l, lo1, lo2, lo;
+
+
+ /* Compute ax*bx as 64-bit value. */
+ l = (uint32_t) ((ax & 0xffffu) * bx);
+ m = (ax >> 16) * bx;
+
+ lo1 = l + ((uint32_t) m << 16);
+ hi1 = (m >> 16) + ((int32_t) l >> 31) + (lo1 < l);
+
+ /* Compute ay*by as 64-bit value. */
+ l = (uint32_t) ((ay & 0xffffu) * by);
+ m = (ay >> 16) * by;
+
+ lo2 = l + ((uint32_t) m << 16);
+ hi2 = (m >> 16) + ((int32_t) l >> 31) + (lo2 < l);
+
+ /* Add them. */
+ lo = lo1 + lo2;
+ hi = hi1 + hi2 + (lo < lo1);
+
+ /* Divide the result by 2^14 with rounding. */
+ s = hi >> 31;
+ l = lo + (uint32_t) s;
+ hi += s + (l < lo);
+ lo = l;
+
+ l = lo + 0x2000u;
+ hi += (l < lo);
+
+ return (int32_t) (((uint32_t) hi << 18) | (l >> 14));
+#else
+ int64_t xx, yy;
+
+ xx = (int64_t) ax * bx;
+ yy = (int64_t) ay * by;
+
+ xx += yy;
+ yy = xx >> 63;
+ xx += 0x2000 + yy;
+
+ return (int32_t) (xx / (2 << 14));
+#endif
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, making only the assumption that the
+ projection vector is a valid unit vector.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_dot_fix_14 (vx, vy,
+ interpreter->state.projection_vector.x,
+ interpreter->state.projection_vector.y);
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's dual projection vector, making only the assumption
+ that the dual projection vector is a valid unit vector.
+
+ The dual projection vector is a vector that is normally the
+ projection vector, but can be set using the original unscaled
+ coordinates of two points as well.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_dual_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_dot_fix_14 (vx, vy,
+ interpreter->state.dual_projection_vector.x,
+ interpreter->state.dual_projection_vector.y);
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when non-NULL.
+
+ Assume both vectors are aligned to the X axis. */
+
+static void
+sfnt_move_x (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ while (n--)
+ {
+ *x = sfnt_add (*x, distance);
+ x++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_X;
+ }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when non-NULL.
+
+ Assume both vectors are aligned to the Y axis. */
+
+static void
+sfnt_move_y (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ while (n--)
+ {
+ *y = sfnt_add (*y, distance);
+ y++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_Y;
+ }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when
+ non-NULL. */
+
+static void
+sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ sfnt_f26dot6 versor, k;
+ sfnt_f2dot14 dot_product;
+ size_t num;
+
+ dot_product = interpreter->state.vector_dot_product;
+
+ /* If the vectors are orthogonal, it is impossible to move anywhere,
+ so simply return. */
+ if (!dot_product)
+ return;
+
+ /* Not actually 26.6, but the multiply-divisions below cancel each
+ other out, so the result is 26.6. */
+ versor = interpreter->state.freedom_vector.x;
+
+ if (versor)
+ {
+ /* Move along X axis, converting the distance to the freedom
+ vector. */
+ num = n;
+ k = sfnt_multiply_divide_signed (distance,
+ versor,
+ dot_product);
+
+ while (num--)
+ {
+ *x = sfnt_add (*x, k);
+ x++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_X;
+ }
+ }
+
+ versor = interpreter->state.freedom_vector.y;
+
+ if (versor)
+ {
+ /* Move along X axis, converting the distance to the freedom
+ vector. */
+ num = n;
+ k = sfnt_multiply_divide_signed (distance,
+ versor,
+ dot_product);
+
+ while (num--)
+ {
+ *y = sfnt_add (*y, k);
+ y++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_Y;
+ }
+ }
+}
+
+/* Validate the graphics state GS.
+ Establish function pointers for rounding and projection.
+ Establish dot product used to convert vector distances between
+ each other. */
+
+static void
+sfnt_validate_gs (struct sfnt_graphics_state *gs)
+{
+ /* Establish the function used for rounding based on the round
+ state. */
+
+ switch (gs->round_state)
+ {
+ case 5: /* Rounding off. */
+ gs->round = sfnt_round_none;
+ break;
+
+ case 0: /* Round to half grid. */
+ gs->round = sfnt_round_to_half_grid;
+ break;
+
+ case 1: /* Round to grid. */
+ gs->round = sfnt_round_to_grid;
+ break;
+
+ case 2: /* Round to double grid. */
+ gs->round = sfnt_round_to_double_grid;
+ break;
+
+ case 4: /* Round up to grid. */
+ gs->round = sfnt_round_up_to_grid;
+ break;
+
+ case 3: /* Round down to grid. */
+ gs->round = sfnt_round_down_to_grid;
+ break;
+
+ case 6: /* Fine grained rounding. */
+ gs->round = sfnt_round_super;
+ break;
+
+ case 7: /* Fine grained rounding 45 degree variant. */
+ gs->round = sfnt_round_super45;
+ break;
+ }
+
+ /* Establish the function used for vector projection.
+ When the projection vector is an axis vector, a fast
+ version can be used. */
+
+ if (gs->projection_vector.x == 040000)
+ gs->project = sfnt_project_onto_x_axis_vector;
+ else if (gs->projection_vector.y == 040000)
+ gs->project = sfnt_project_onto_y_axis_vector;
+ else
+ gs->project = sfnt_project_onto_any_vector;
+
+ /* Do the same for the dual projection vector. */
+
+ if (gs->dual_projection_vector.x == 040000)
+ gs->dual_project = sfnt_project_onto_x_axis_vector;
+ else if (gs->dual_projection_vector.y == 040000)
+ gs->dual_project = sfnt_project_onto_y_axis_vector;
+ else
+ gs->dual_project = sfnt_dual_project_onto_any_vector;
+
+ /* Compute dot product of the freedom and projection vectors.
+ Handle the common case where the freedom vector is aligned
+ to an axis. */
+
+ if (gs->freedom_vector.x == 040000)
+ gs->vector_dot_product = gs->projection_vector.x;
+ else if (gs->freedom_vector.y == 040000)
+ gs->vector_dot_product = gs->projection_vector.y;
+ else
+ /* Actually calculate the dot product. */
+ gs->vector_dot_product = ((((long) gs->projection_vector.x
+ * gs->freedom_vector.x)
+ + ((long) gs->projection_vector.y
+ * gs->freedom_vector.y))
+ / 16384);
+
+ /* Now figure out which function to use to move distances. Handle
+ the common case where both the freedom and projection vectors are
+ aligned to an axis. */
+
+ if (gs->freedom_vector.x == 040000
+ && gs->projection_vector.x == 040000)
+ gs->move = sfnt_move_x;
+ else if (gs->freedom_vector.y == 040000
+ && gs->projection_vector.y == 040000)
+ gs->move = sfnt_move_y;
+ else
+ gs->move = sfnt_move;
+}
+
+/* Set the X and Y versors of the freedom vector of INTERPRETER's
+ graphics state to the specified X and Y, in 2.14 fixed point
+ format. */
+
+static void
+sfnt_set_freedom_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+ interpreter->state.freedom_vector.x = x;
+ interpreter->state.freedom_vector.y = y;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Set the X and Y versors of the projection vector of INTERPRETER's
+ graphics state to the specified X and Y, in 2.14 fixed point
+ format. */
+
+static void
+sfnt_set_projection_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+ interpreter->state.projection_vector.x = x;
+ interpreter->state.projection_vector.y = y;
+ interpreter->state.dual_projection_vector.x = x;
+ interpreter->state.dual_projection_vector.y = y;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Interpret an SHZ instruction with the specified OPCODE. Like
+ sfnt_interpret_shc, but do the move for each point in the entire
+ specified ZONE. */
+
+static void
+sfnt_interpret_shz (struct sfnt_interpreter *interpreter,
+ uint32_t zone, unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+
+ if (zone != 0 && !interpreter->glyph_zone)
+ /* There are no points in the glyph zone. */
+ return;
+
+ if (opcode == 0x37)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ if (zone == 0)
+ sfnt_move_twilight_zone (interpreter, 0,
+ interpreter->twilight_zone_size,
+ magnitude);
+ else
+ sfnt_move_glyph_zone (interpreter, 0,
+ interpreter->glyph_zone->num_points,
+ magnitude);
+}
+
+/* Interpret an SHC instruction with the specified OPCODE and CONTOUR.
+ Like sfnt_interpret_shp, but do the move for each point in the
+ specified contour. */
+
+static void
+sfnt_interpret_shc (struct sfnt_interpreter *interpreter,
+ uint32_t contour, unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+ size_t start, end, n;
+
+ if (!interpreter->glyph_zone)
+ TRAP ("SHC without glyph zone");
+
+ /* Check that the contour is within bounds. */
+ if (contour >= interpreter->glyph_zone->num_contours)
+ TRAP ("contour out of bounds");
+
+ /* Figure out the magnitude of the change, measured from the
+ projection vector. */
+
+ if (opcode == 0x35)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ /* Now obtain the start and end of the contour.
+ Verify that both are valid. */
+
+ if (contour)
+ start = interpreter->glyph_zone->contour_end_points[contour - 1];
+ else
+ start = 0;
+
+ end = interpreter->glyph_zone->contour_end_points[contour];
+
+ if (start > end || end >= interpreter->glyph_zone->num_points)
+ TRAP ("invalid contour data in glyph");
+
+ /* Compute the number of points to move. */
+ n = end - start + 1;
+
+ /* Move that many points. */
+ sfnt_move_glyph_zone (interpreter, start, n, magnitude);
+}
+
+/* Interpret an SHP instruction with the specified OPCODE. Move a
+ popped point in ZP2 along the freedom vector by the distance
+ between a specified point from its original position, which is RP1
+ in ZP0 if OPCODE is 0x33, and RP2 in ZP1 if OPCODE is 0x32.
+
+ Repeat for the number of iterations specified by a prior SLOOP
+ instruction. */
+
+static void
+sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
+ unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+ uint32_t point;
+
+ /* Figure out the magnitude of the change, measured from the
+ projection vector. */
+
+ if (opcode == 0x33)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ /* Now project it onto the freedom vector and move the point that
+ much for loop variable times. */
+
+ while (interpreter->state.loop--)
+ {
+ point = POP ();
+
+ sfnt_check_zp2 (interpreter, point);
+ sfnt_move_zp2 (interpreter, point, 1, magnitude);
+ }
+
+ /* Restore interpreter->state.loop to 1. */
+ interpreter->state.loop = 1;
+}
+
+#define load_point(p) \
+ (opcode == 0x31 \
+ ? interpreter->glyph_zone->x_current[p] \
+ : interpreter->glyph_zone->y_current[p])
+
+#define store_point(p, val) \
+ (opcode == 0x31 \
+ ? (interpreter->glyph_zone->x_current[p] = (val)) \
+ : (interpreter->glyph_zone->y_current[p] = (val)))
+
+#define load_original(p) \
+ (opcode == 0x31 \
+ ? interpreter->glyph_zone->x_points[p] \
+ : interpreter->glyph_zone->y_points[p])
+
+#define IUP_SINGLE_PAIR() \
+ /* Now make touch_start the first point before, i.e. the first \
+ touched point in this pair. */ \
+ \
+ if (touch_start == start) \
+ touch_start = end; \
+ else \
+ touch_start = touch_start - 1; \
+ \
+ /* Set point_min and point_max based on which glyph is at a \
+ lower value. */ \
+ \
+ if (load_original (touch_start) < load_original (touch_end)) \
+ { \
+ point_min = touch_start; \
+ point_max = touch_end; \
+ } \
+ else \
+ { \
+ point_max = touch_start; \
+ point_min = touch_end; \
+ } \
+ \
+ min_pos = load_point (point_min); \
+ max_pos = load_point (point_max); \
+ \
+ /* This is needed for interpolation. */ \
+ original_max_pos = load_original (point_max); \
+ original_min_pos = load_original (point_min); \
+ \
+ /* Now process points between touch_start and touch_end. */ \
+ \
+ i = touch_start + 1; \
+ \
+ /* touch_start might be the last point in the contour. */ \
+ \
+ if (i > end) \
+ i = start; \
+ \
+ while (i != touch_end) \
+ { \
+ /* Movement is always relative to the original position of \
+ the point. */ \
+ position = load_original (i); \
+ \
+ /* If i is in between touch_start and touch_end... */ \
+ if (position >= original_min_pos \
+ && position <= original_max_pos) \
+ { \
+ /* Handle the degenerate case where original_min_pos and \
+ original_max_pos have not changed by placing the point in \
+ the middle. */ \
+ if (original_min_pos == original_max_pos) \
+ ratio = 077777; \
+ else \
+ /* ... preserve the ratio of i between min_pos and \
+ max_pos... */ \
+ ratio = sfnt_div_fixed ((sfnt_sub (position, \
+ original_min_pos) \
+ * 1024), \
+ (sfnt_sub (original_max_pos, \
+ original_min_pos) \
+ * 1024)); \
+ \
+ delta = sfnt_sub (max_pos, min_pos); \
+ delta = sfnt_mul_fixed (ratio, delta); \
+ store_point (i, sfnt_add (min_pos, delta)); \
+ } \
+ else \
+ { \
+ /* ... otherwise, move i by how much the nearest touched \
+ point moved. */ \
+ \
+ if (position >= original_max_pos) \
+ delta = sfnt_sub (max_pos, original_max_pos); \
+ else \
+ delta = sfnt_sub (min_pos, original_min_pos); \
+ \
+ store_point (i, sfnt_add (position, delta)); \
+ } \
+ \
+ if (++i > end) \
+ i = start; \
+ } \
+
+/* Interpolate untouched points in the contour between and including
+ START and END inside INTERPRETER's glyph zone according to the
+ rules specified for an IUP instruction. Perform interpolation on
+ the axis specified by OPCODE and MASK. */
+
+static void
+sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
+ size_t start, size_t end,
+ unsigned char opcode, int mask)
+{
+ size_t point;
+ size_t touch_start, touch_end;
+ size_t first_point;
+ size_t point_min, point_max, i;
+ sfnt_f26dot6 position, min_pos, max_pos, delta, ratio;
+ sfnt_f26dot6 original_max_pos;
+ sfnt_f26dot6 original_min_pos;
+
+ /* Find the first touched point. If none is found, simply
+ return. */
+
+ for (point = start; point <= end; ++point)
+ {
+ if (interpreter->glyph_zone->flags[point] & mask)
+ goto touched;
+ }
+
+ goto untouched;
+
+ touched:
+
+ point = start;
+
+ /* Find the first touched point. */
+ while (!(interpreter->glyph_zone->flags[point] & mask))
+ {
+ point++;
+
+ /* There are no touched points. */
+ if (point > end)
+ goto untouched;
+ }
+
+ first_point = point;
+
+ while (point <= end)
+ {
+ /* Find the next untouched point. */
+ while (interpreter->glyph_zone->flags[point] & mask)
+ {
+ point++;
+
+ if (point > end)
+ goto wraparound;
+ }
+
+ /* touch_start is now the first untouched point. */
+ touch_start = point;
+
+ /* Find the next touched point. */
+ while (!(interpreter->glyph_zone->flags[point] & mask))
+ {
+ point++;
+
+ /* Move back to start if point has gone past the end of the
+ contour. */
+ if (point > end)
+ goto wraparound_1;
+ }
+
+ /* touch_end is now the next touched point. */
+ touch_end = point;
+
+ /* Do the interpolation. */
+ IUP_SINGLE_PAIR ();
+ }
+
+ goto untouched;
+
+ wraparound:
+ /* This is like wraparound_1, except that no untouched points have
+ yet to be found.
+
+ This means the first untouched point is start. */
+ touch_start = start;
+
+ wraparound_1:
+ /* If point > end, wrap around. Here, touch_start is set
+ properly, so touch_end must be first_point. */
+
+ touch_end = first_point;
+ IUP_SINGLE_PAIR ();
+
+ untouched:
+ /* No points were touched or all points have been considered, so
+ return immediately. */
+ return;
+}
+
+#undef load_point
+#undef store_point
+#undef load_original
+
+/* Interpret an IUP (``interpolate untouched points'') instruction.
+ INTERPRETER is the interpreter, and OPCODE is the instruction
+ number. See the TrueType Reference Manual for more details. */
+
+static void
+sfnt_interpret_iup (struct sfnt_interpreter *interpreter,
+ unsigned char opcode)
+{
+ int mask;
+ size_t i, point, end, first_point;
+
+ /* Check that the zone is the glyph zone. */
+
+ if (!interpreter->state.zp2)
+ TRAP ("trying to iup in twilight zone");
+
+ if (!interpreter->glyph_zone)
+ TRAP ("iup without loaded glyph!");
+
+ /* Figure out what axis to interpolate in based on the opcode. */
+ if (opcode == 0x30)
+ mask = SFNT_POINT_TOUCHED_Y;
+ else
+ mask = SFNT_POINT_TOUCHED_X;
+
+ /* Now, for each contour, interpolate untouched points. */
+ point = 0;
+ for (i = 0; i < interpreter->glyph_zone->num_contours; ++i)
+ {
+ first_point = point;
+ end = interpreter->glyph_zone->contour_end_points[i];
+
+ if (point >= interpreter->glyph_zone->num_points
+ || end >= interpreter->glyph_zone->num_points)
+ TRAP ("glyph contains out of bounds contour end point"
+ " data!");
+
+ sfnt_interpret_iup_1 (interpreter, first_point, end,
+ opcode, mask);
+ point = end + 1;
+
+ /* Skip the subsequent phantom points, which may end up
+ intermixed with contours inside a compound glyph. */
+
+ while (point < interpreter->glyph_zone->num_points
+ && interpreter->glyph_zone->flags[point] & SFNT_POINT_PHANTOM)
+ point++;
+ }
+}
+
+/* Interpret an MIRP instruction with the specified OPCODE in
+ INTERPRETER. Pop a point in ZP1 and CVT index, and move the point
+ until its distance from RP0 in ZP0 is the same as in the control
+ value. If the point lies in the twilight zone, then ``create'' it
+ as well.
+
+ OPCODE contains a great many flags.
+ They are all described in the TrueType reference manual. */
+
+static void
+sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ uint32_t n;
+ uint32_t p;
+ sfnt_f26dot6 distance, delta, temp;
+ sfnt_f26dot6 current_projection, original_projection;
+ sfnt_f26dot6 x, y, org_x, org_y;
+ sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+ /* CVT index. */
+ n = POP ();
+
+ /* Point number. */
+ p = POP ();
+
+ /* Now get the distance from the CVT. */
+ if (n >= interpreter->cvt_size)
+ TRAP ("cvt index out of bounds");
+
+ distance = interpreter->cvt[n];
+
+ /* Test against the single width value. */
+
+ delta = sfnt_sub (distance,
+ interpreter->state.single_width_value);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta < interpreter->state.sw_cut_in)
+ {
+ /* Use the single width instead, as the CVT entry is too
+ small. */
+
+ if (distance >= 0)
+ distance = interpreter->state.single_width_value;
+ else
+ distance = -interpreter->state.single_width_value;
+ }
+
+ /* Load the reference point. */
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rx, &ry, &org_rx, &org_ry);
+
+ /* Create the point in the twilight zone, should that be ZP1. */
+
+ if (!interpreter->state.zp1)
+ {
+ /* Since P hasn't been loaded yet, whether or not it is valid is
+ not known. */
+ sfnt_check_zp1 (interpreter, p);
+
+ interpreter->twilight_x[p] = rx;
+ interpreter->twilight_y[p] = ry;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+ distance);
+ temp = sfnt_add (temp, org_rx);
+ interpreter->twilight_original_x[p] = temp;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+ distance);
+ temp = sfnt_add (temp, org_ry);
+ interpreter->twilight_original_y[p] = temp;
+ }
+
+ /* Load P. */
+ sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+
+ /* If distance would be negative and auto_flip is on, flip it. */
+
+ original_projection = DUAL_PROJECT (org_x - org_rx,
+ org_y - org_ry);
+ current_projection = PROJECT (x - rx, y - ry);
+
+ if (interpreter->state.auto_flip)
+ {
+ if ((original_projection ^ distance) < 0)
+ distance = -distance;
+ }
+
+ /* Flag B means look at the cvt cut in and round the
+ distance. */
+
+ if (opcode & 4)
+ {
+ delta = sfnt_sub (distance, original_projection);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta > interpreter->state.cvt_cut_in)
+ distance = original_projection;
+
+ /* Now, round the distance. */
+ distance = sfnt_round_symmetric (interpreter, distance);
+ }
+
+ /* Flag C means look at the minimum distance. */
+
+ if (opcode & 8)
+ {
+ if (original_projection >= 0
+ && distance < interpreter->state.minimum_distance)
+ distance = interpreter->state.minimum_distance;
+ else if (original_projection < 0
+ && distance > -interpreter->state.minimum_distance)
+ distance = -interpreter->state.minimum_distance;
+ }
+
+ /* Finally, move the point. */
+ sfnt_move_zp1 (interpreter, p, 1,
+ sfnt_sub (distance, current_projection));
+
+ /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
+ it RP0. */
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode & 16)
+ interpreter->state.rp0 = p;
+}
+
+/* Interpret an MDRP instruction with the specified OPCODE in
+ INTERPRETER. Pop a point in ZP1, and move the point until its
+ distance from RP0 in ZP0 is the same as in the original outline.
+
+ This is almost like MIRP[abcde].
+
+ OPCODE contains a great many flags.
+ They are all described in the TrueType reference manual. */
+
+static void
+sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ uint32_t p;
+ sfnt_f26dot6 distance, delta;
+ sfnt_f26dot6 current_projection, original_projection;
+ sfnt_f26dot6 x, y, org_x, org_y;
+ sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+ /* Point number. */
+ p = POP ();
+
+ /* Load the points. */
+ sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rx, &ry, &org_rx, &org_ry);
+
+ distance = DUAL_PROJECT (org_x - org_rx,
+ org_y - org_ry);
+ original_projection = distance;
+ current_projection = PROJECT (x - rx, y - ry);
+
+ /* Test against the single width value. */
+
+ delta = sfnt_sub (distance,
+ interpreter->state.single_width_value);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta < interpreter->state.sw_cut_in)
+ {
+ /* Use the single width instead, as the CVT entry is too
+ small. */
+
+ if (distance >= 0)
+ distance = interpreter->state.single_width_value;
+ else
+ distance = -interpreter->state.single_width_value;
+ }
+
+ /* Flag B means look at the cvt cut in and round the
+ distance. */
+
+ if (opcode & 4)
+ {
+ delta = sfnt_sub (distance, original_projection);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta > interpreter->state.cvt_cut_in)
+ distance = original_projection;
+
+ /* Now, round the distance. */
+ distance = sfnt_round_symmetric (interpreter, distance);
+ }
+
+ /* Flag C means look at the minimum distance. */
+
+ if (opcode & 8)
+ {
+ if (original_projection >= 0
+ && distance < interpreter->state.minimum_distance)
+ distance = interpreter->state.minimum_distance;
+ else if (original_projection < 0
+ && distance > -interpreter->state.minimum_distance)
+ distance = -interpreter->state.minimum_distance;
+ }
+
+ /* Finally, move the point. */
+ sfnt_move_zp1 (interpreter, p, 1,
+ sfnt_sub (distance, current_projection));
+
+ /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
+ it RP0. */
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode & 16)
+ interpreter->state.rp0 = p;
+}
+
+/* Execute the program now loaded into INTERPRETER.
+ WHY specifies why the interpreter is being run, and is used to
+ control the behavior of instructions such IDEF[] and FDEF[].
+
+ Transfer control to INTERPRETER->trap if interpretation is aborted
+ due to an error, and set INTERPRETER->trap_reason to a string
+ describing the error.
+
+ INTERPRETER->glyph_zone should be cleared before calling this
+ function. */
+
+static void
+sfnt_interpret_run (struct sfnt_interpreter *interpreter,
+ enum sfnt_interpreter_run_context why)
+{
+ unsigned char opcode;
+ bool is_prep;
+
+ /* Determine whether or not this is the control value program. */
+ is_prep = (why == SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+#ifdef TEST
+ /* Allow testing control value program instructions as well. */
+ if (why == SFNT_RUN_CONTEXT_TEST)
+ is_prep = true;
+#endif
+
+ while (interpreter->IP < interpreter->num_instructions)
+ {
+ opcode = interpreter->instructions[interpreter->IP];
+
+#ifdef TEST
+ if (interpreter->run_hook)
+ interpreter->run_hook (interpreter);
+#endif
+
+ switch (opcode)
+ {
+ case 0x00: /* SVTCA y */
+ SVTCAy ();
+ break;
+
+ case 0x01: /* SVTCA x */
+ SVTCAx ();
+ break;
+
+ case 0x02: /* SPvTCA y */
+ SPvTCAy ();
+ break;
+
+ case 0x03: /* SPvTCA x */
+ SPvTCAx ();
+ break;
+
+ case 0x04: /* SFvTCA y */
+ SFvTCAy ();
+ break;
+
+ case 0x05: /* SFvTCA x */
+ SFvTCAx ();
+ break;
+
+ case 0x06: /* SPvTL // */
+ case 0x07: /* SPvTL + */
+ SPVTL ();
+ break;
+
+ case 0x08: /* SFvTL // */
+ case 0x09: /* SFvTL + */
+ SFVTL ();
+ break;
+
+ case 0x0A: /* SPvFS */
+ SPVFS ();
+ break;
+
+ case 0x0B: /* SFvFS */
+ SFVFS ();
+ break;
+
+ case 0x0C: /* GPv */
+ GPV ();
+ break;
+
+ case 0x0D: /* GFv */
+ GFV ();
+ break;
+
+ case 0x0E: /* SFvTPv */
+ SFVTPV ();
+ break;
+
+ case 0x0F: /* ISECT */
+ ISECT ();
+ break;
+
+ case 0x10: /* SRP0 */
+ SRP0 ();
+ break;
+
+ case 0x11: /* SRP1 */
+ SRP1 ();
+ break;
+
+ case 0x12: /* SRP2 */
+ SRP2 ();
+ break;
+
+ case 0x13: /* SZP0 */
+ SZP0 ();
+ break;
+
+ case 0x14: /* SZP1 */
+ SZP1 ();
+ break;
+
+ case 0x15: /* SZP2 */
+ SZP2 ();
+ break;
+
+ case 0x16: /* SZPS */
+ SZPS ();
+ break;
+
+ case 0x17: /* SLOOP */
+ SLOOP ();
+ break;
+
+ case 0x18: /* RTG */
+ RTG ();
+ break;
+
+ case 0x19: /* RTHG */
+ RTHG ();
+ break;
+
+ case 0x1A: /* SMD */
+ SMD ();
+ break;
+
+ case 0x1B: /* ELSE */
+ ELSE ();
+ break;
+
+ case 0x1C: /* JMPR */
+ JMPR ();
+ break;
+
+ case 0x1D: /* SCVTCI */
+ SCVTCI ();
+ break;
+
+ case 0x1E: /* SSWCI */
+ SSWCI ();
+ break;
+
+ case 0x1F: /* SSW */
+ SSW ();
+ break;
+
+ case 0x20: /* DUP */
+ DUP ();
+ break;
+
+ case 0x21: /* POP */
+ POP ();
+ break;
+
+ case 0x22: /* CLEAR */
+ CLEAR ();
+ break;
+
+ case 0x23: /* SWAP */
+ SWAP ();
+ break;
+
+ case 0x24: /* DEPTH */
+ DEPTH ();
+ break;
+
+ case 0x25: /* CINDEX */
+ CINDEX ();
+ break;
+
+ case 0x26: /* MINDEX */
+ MINDEX ();
+ break;
+
+ case 0x27: /* ALIGNPTS */
+ ALIGNPTS ();
+ break;
+
+ case 0x28: /* RAW */
+ RAW ();
+ break;
+
+ case 0x29: /* UTP */
+ UTP ();
+ break;
+
+ case 0x2A: /* LOOPCALL */
+ LOOPCALL ();
+ break;
+
+ case 0x2B: /* CALL */
+ CALL ();
+ break;
+
+ case 0x2C: /* FDEF */
+ FDEF ();
+ break;
+
+ case 0x2D: /* ENDF */
+ ENDF ();
+ break;
+
+ case 0x2E: /* MDAP */
+ case 0x2F: /* MDAP */
+ MDAP ();
+ break;
+
+ case 0x30: /* IUP */
+ case 0x31: /* IUP */
+ IUP ();
+ break;
+
+ case 0x32: /* SHP */
+ case 0x33: /* SHP */
+ SHP ();
+ break;
+
+ case 0x34: /* SHC */
+ case 0x35: /* SHC */
+ SHC ();
+ break;
+
+ case 0x36: /* SHZ */
+ case 0x37: /* SHZ */
+ SHZ ();
+ break;
+
+ case 0x38: /* SHPIX */
+ SHPIX ();
+ break;
+
+ case 0x39: /* IP */
+ IP ();
+ break;
+
+ case 0x3A: /* MSIRP */
+ case 0x3B: /* MSIRP */
+ MSIRP ();
+ break;
+
+ case 0x3C: /* ALIGNRP */
+ ALIGNRP ();
+ break;
+
+ case 0x3D: /* RTDG */
+ RTDG ();
+ break;
+
+ case 0x3E: /* MIAP */
+ case 0x3F: /* MIAP */
+ MIAP ();
+ break;
+
+ case 0x40: /* NPUSHB */
+ NPUSHB ();
+ break;
+
+ case 0x41: /* NPUSHW */
+ NPUSHW ();
+ break;
+
+ case 0x42: /* WS */
+ WS ();
+ break;
+
+ case 0x43: /* RS */
+ RS ();
+ break;
+
+ case 0x44: /* WCVTP */
+ WCVTP ();
+ break;
+
+ case 0x45: /* RCVT */
+ RCVT ();
+ break;
+
+ case 0x46: /* GC */
+ case 0x47: /* GC */
+ GC ();
+ break;
+
+ case 0x48: /* SCFS */
+ SCFS ();
+ break;
+
+ case 0x49: /* MD */
+ case 0x4A: /* MD */
+ MD ();
+ break;
+
+ case 0x4B: /* MPPEM */
+ MPPEM ();
+ break;
+
+ case 0x4C: /* MPS */
+ MPS ();
+ break;
+
+ case 0x4D: /* FLIPON */
+ FLIPON ();
+ break;
+
+ case 0x4E: /* FLIPOFF */
+ FLIPOFF ();
+ break;
+
+ case 0x4F: /* DEBUG */
+ DEBUG ();
+ break;
+
+ case 0x50: /* LT */
+ LT ();
+ break;
+
+ case 0x51: /* LTEQ */
+ LTEQ ();
+ break;
+
+ case 0x52: /* GT */
+ GT ();
+ break;
+
+ case 0x53: /* GTEQ */
+ GTEQ ();
+ break;
+
+ case 0x54: /* EQ */
+ EQ ();
+ break;
+
+ case 0x55: /* NEQ */
+ NEQ ();
+ break;
+
+ case 0x56: /* ODD */
+ ODD ();
+ break;
+
+ case 0x57: /* EVEN */
+ EVEN ();
+ break;
+
+ case 0x58: /* IF */
+ IF ();
+ break;
+
+ case 0x59: /* EIF */
+ EIF ();
+ break;
+
+ case 0x5A: /* AND */
+ AND ();
+ break;
+
+ case 0x5B: /* OR */
+ OR ();
+ break;
+
+ case 0x5C: /* NOT */
+ NOT ();
+ break;
+
+ case 0x5D: /* DELTAP1 */
+ DELTAP1 ();
+ break;
+
+ case 0x5E: /* SDB */
+ SDB ();
+ break;
+
+ case 0x5F: /* SDS */
+ SDS ();
+ break;
+
+ case 0x60: /* ADD */
+ ADD ();
+ break;
+
+ case 0x61: /* SUB */
+ SUB ();
+ break;
+
+ case 0x62: /* DIV */
+ DIV ();
+ break;
+
+ case 0x63: /* MUL */
+ MUL ();
+ break;
+
+ case 0x64: /* ABS */
+ ABS ();
+ break;
+
+ case 0x65: /* NEG */
+ NEG ();
+ break;
+
+ case 0x66: /* FLOOR */
+ FLOOR ();
+ break;
+
+ case 0x67: /* CEILING */
+ CEILING ();
+ break;
+
+ case 0x68: /* ROUND */
+ case 0x69: /* ROUND */
+ case 0x6A: /* ROUND */
+ case 0x6B: /* ROUND */
+ ROUND ();
+ break;
+
+ case 0x6C: /* NROUND */
+ case 0x6D: /* NROUND */
+ case 0x6E: /* NRRUND */
+ case 0x6F: /* NROUND */
+ NROUND ();
+ break;
+
+ case 0x70: /* WCVTF */
+ WCVTF ();
+ break;
+
+ case 0x71: /* DELTAP2 */
+ DELTAP2 ();
+ break;
+
+ case 0x72: /* DELTAP3 */
+ DELTAP3 ();
+ break;
+
+ case 0x73: /* DELTAC1 */
+ DELTAC1 ();
+ break;
+
+ case 0x74: /* DELTAC2 */
+ DELTAC2 ();
+ break;
+
+ case 0x75: /* DELTAC3 */
+ DELTAC3 ();
+ break;
+
+ case 0x76: /* SROUND */
+ SROUND ();
+ break;
+
+ case 0x77: /* S45Round */
+ S45ROUND ();
+ break;
+
+ case 0x78: /* JROT */
+ JROT ();
+ break;
+
+ case 0x79: /* JROF */
+ JROF ();
+ break;
+
+ case 0x7A: /* ROFF */
+ ROFF ();
+ break;
+
+ case 0x7B: /* ILLEGAL_INSTRUCTION */
+ ILLEGAL_INSTRUCTION ();
+ break;
+
+ case 0x7C: /* RUTG */
+ RUTG ();
+ break;
+
+ case 0x7D: /* RDTG */
+ RDTG ();
+ break;
+
+ case 0x7E: /* SANGW */
+ SANGW ();
+ break;
+
+ case 0x7F: /* AA */
+ AA ();
+ break;
+
+ case 0x80: /* FLIPPT */
+ FLIPPT ();
+ break;
+
+ case 0x81: /* FLIPRGON */
+ FLIPRGON ();
+ break;
+
+ case 0x82: /* FLIPRGOFF */
+ FLIPRGOFF ();
+ break;
+
+ case 0x83: /* RMVT */
+ case 0x84: /* WMVT */
+ NOT_IMPLEMENTED ();
+ break;
+
+ case 0x85: /* SCANCTRL */
+ SCANCTRL ();
+ break;
+
+ case 0x86: /* SDPVTL */
+ case 0x87: /* SDPVTL */
+ SDPVTL ();
+ break;
+
+ case 0x88: /* GETINFO */
+ GETINFO ();
+ break;
+
+ case 0x89: /* IDEF */
+ IDEF ();
+ break;
+
+ case 0x8A: /* ROLL */
+ ROLL ();
+ break;
+
+ case 0x8B: /* MAX */
+ _MAX ();
+ break;
+
+ case 0x8C: /* MIN */
+ _MIN ();
+ break;
+
+ /* Scan or dropout control is not implemented. Instead, 256
+ grays are used to display pixels which are partially
+ turned on. */
+ case 0x8D: /* SCANTYPE */
+ SCANTYPE ();
+ break;
+
+ case 0x8E: /* INSTCTRL */
+ INSTCTRL ();
+ break;
+
+ case 0x8F: /* ADJUST */
+ case 0x90: /* ADJUST */
+ NOT_IMPLEMENTED ();
+ break;
+
+ case 0x91: /* GXAXIS */
+ GXAXIS ();
+ break;
+
+ default:
+ if (opcode >= 0xE0) /* MIRP */
+ {
+ MIRP ();
+ }
+ else if (opcode >= 0xC0) /* MDRP */
+ {
+ MDRP ();
+ }
+ else if (opcode >= 0xB8) /* PUSHW */
+ {
+ PUSHW ();
+ }
+ else if (opcode >= 0xB0) /* PUSHB */
+ {
+ PUSHB ();
+ }
+ else
+ NOT_IMPLEMENTED ();
+ }
+
+ /* In the case of an NPUSHB or NPUSHW instruction,
+ interpreter->IP has only been increased to skip over the
+ extra bytes, and not the byte containing the instruction
+ itself. */
+ interpreter->IP++;
+
+ /* This label is used by instructions to continue without
+ incrementing IP. It is used by instructions which set IP
+ themselves, such as ELSE, IF, FDEF, IDEF and JMPR. */
+ skip_step:
+ continue;
+ }
+}
+
+/* Execute the font program FPGM using INTERPRETER.
+ This must only be called once per interpreter, else behavior is
+ undefined.
+
+ Value is NULL upon success, else it is a string describing the
+ reason for failure.
+
+ The caller must save the graphics state after interpreting the font
+ program and restore it prior to instructing each glyph. */
+
+TEST_STATIC const char *
+sfnt_interpret_font_program (struct sfnt_interpreter *interpreter,
+ struct sfnt_fpgm_table *fpgm)
+{
+ if (setjmp (interpreter->trap))
+ return interpreter->trap_reason;
+
+ /* Set up the interpreter to evaluate the font program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = fpgm->instructions;
+ interpreter->num_instructions = fpgm->num_instructions;
+ interpreter->glyph_zone = NULL;
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_FONT_PROGRAM);
+ return NULL;
+}
+
+/* Execute the control value program PREP using INTERPRETER.
+
+ Return NULL and the graphics state after the execution of the
+ program in *STATE, or a string describing the reason for a failure
+ to interpret the program.
+
+ The caller must save the graphics state after interpreting the
+ control value program and restore it prior to instructing each
+ glyph. */
+
+TEST_STATIC const char *
+sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter,
+ struct sfnt_prep_table *prep,
+ struct sfnt_graphics_state *state)
+{
+ if (setjmp (interpreter->trap))
+ return interpreter->trap_reason;
+
+ /* Set up the interpreter to evaluate the control value program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = prep->instructions;
+ interpreter->num_instructions = prep->num_instructions;
+ interpreter->glyph_zone = NULL;
+
+ sfnt_interpret_run (interpreter,
+ SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+ /* If instruct_control & 4, then changes to the graphics state made
+ in this program should be reverted. */
+
+ if (interpreter->state.instruct_control & 4)
+ sfnt_init_graphics_state (&interpreter->state);
+
+ /* Save the graphics state upon success. */
+ memcpy (state, &interpreter->state, sizeof *state);
+ return NULL;
+}
+
+
+
+/* Glyph hinting. The routines here perform hinting on simple and
+ compound glyphs.
+
+ In order to keep the hinting mechanism separate from the rest of
+ the code, the routines here perform outline decomposition and
+ scaling separately. It might be nice to fix that in the
+ future. */
+
+/* Instructed glyph outline decomposition. This is separate from
+ sfnt_decompose_glyph because this file should be able to be built
+ with instructions disabled. */
+
+/* Decompose OUTLINE, an instructed outline, into its individual
+ components.
+
+ Call MOVE_TO to move to a specific location. For each line
+ encountered, call LINE_TO to draw a line to that location. For
+ each spline encountered, call CURVE_TO to draw the curves
+ comprising the spline. Call each of those functions with 16.16
+ fixed point coordinates.
+
+ Call all functions with DCONTEXT as an argument.
+
+ The winding rule used to fill the resulting lines is described in
+ chapter 2 of the TrueType reference manual, under the heading
+ "distinguishing the inside from the outside of a glyph."
+
+ Value is 0 upon success, or some non-zero value upon failure, which
+ can happen if the glyph is invalid. */
+
+static int
+sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext)
+{
+ size_t here, last, n;
+
+ if (!outline->num_contours)
+ return 0;
+
+ here = 0;
+
+ for (n = 0; n < outline->num_contours; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = outline->contour_end_points[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= outline->num_points)
+ goto fail;
+
+ if (sfnt_decompose_glyph_2 (here, last, move_to,
+ line_to, curve_to, dcontext,
+ outline->x_points,
+ outline->y_points,
+ outline->flags, 1024))
+ goto fail;
+
+ /* Move forward to the start of the next contour. */
+ here = last + 1;
+
+ /* here may be a phantom point when outlining a compound glyph,
+ as they can have phantom points mixed in with contours.
+
+ When that happens, skip past all the phantom points. */
+
+ while (here < outline->num_points
+ && outline->flags[here] & SFNT_POINT_PHANTOM)
+ here++;
+ }
+
+ return 0;
+
+ fail:
+ return 1;
+}
+
+/* Decompose and build an outline for the specified instructed outline
+ INSTRUCTED. Return the outline data with a refcount of 0 upon
+ success, or NULL upon failure.
+
+ This function is not reentrant. */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed)
+{
+ struct sfnt_glyph_outline *outline;
+ int rc;
+
+ memset (&build_outline_context, 0, sizeof build_outline_context);
+
+ /* Allocate the outline now with enough for 44 words at the end. */
+ outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+ outline->outline_size = 40;
+ outline->outline_used = 0;
+ outline->refcount = 0;
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+ /* Clear outline bounding box. */
+ outline->xmin = 0;
+ outline->ymin = 0;
+ outline->xmax = 0;
+ outline->ymax = 0;
+
+ /* Set up the context. */
+ build_outline_context.outline = outline;
+ build_outline_context.factor = 0177777;
+
+ /* Start decomposing. */
+ rc = sfnt_decompose_instructed_outline (instructed,
+ sfnt_move_to_and_build,
+ sfnt_line_to_and_build,
+ sfnt_curve_to_and_build,
+ NULL);
+
+ /* Synchronize the outline object with what might have changed
+ inside sfnt_decompose_glyph. */
+ outline = build_outline_context.outline;
+
+ /* Finally, obtain the origin point of the glyph after it has been
+ instructed. */
+
+ if (instructed->num_points > 1)
+ outline->origin
+ = instructed->x_points[instructed->num_points - 2];
+ else
+ outline->origin = 0;
+
+ if (rc)
+ {
+ xfree (outline);
+ return NULL;
+ }
+
+ return outline;
+}
+
+
+
+/* Compute phantom points for the specified glyph GLYPH. Use the
+ unscaled metrics specified in METRICS, and the 16.16 fixed point
+ scale SCALE.
+
+ Place the X and Y coordinates of the first phantom point in *X1 and
+ *Y1, and those of the second phantom point in *X2 and *Y2. */
+
+static void
+sfnt_compute_phantom_points (struct sfnt_glyph *glyph,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed scale,
+ sfnt_f26dot6 *x1, sfnt_f26dot6 *y1,
+ sfnt_f26dot6 *x2, sfnt_f26dot6 *y2)
+{
+ sfnt_fword f1, f2;
+
+ /* Two ``phantom points'' are appended to each outline by the scaler
+ prior to instruction interpretation. One of these points
+ represents the left-side bearing distance from xmin, while the
+ other represents the advance width. Both are then used after the
+ hinting process to ensure that the reported glyph metrics are
+ consistent with the instructed outline. */
+
+ /* First compute both values in fwords. */
+ f1 = glyph->xmin - metrics->lbearing;
+ f2 = f1 + metrics->advance;
+
+ /* Apply the metrics distortion. */
+ f1 += glyph->origin_distortion;
+ f2 += glyph->advance_distortion;
+
+ /* Next, scale both up. */
+ *x1 = sfnt_mul_f26dot6_fixed (f1 * 64, scale);
+ *x2 = sfnt_mul_f26dot6_fixed (f2 * 64, scale);
+
+ /* Clear y1 and y2. */
+ *y1 = 0;
+ *y2 = 0;
+}
+
+/* Load the simple glyph GLYPH into the specified INTERPRETER, scaling
+ it up by INTERPRETER's scale, and run its glyph program if
+ present. Use the unscaled metrics specified in METRICS.
+
+ Upon success, return NULL and the resulting points and contours in
+ *VALUE. Else, value is the reason interpretation failed. */
+
+TEST_STATIC const char *
+sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_instructed_outline **value)
+{
+ size_t zone_size, temp, outline_size, i;
+ struct sfnt_interpreter_zone *zone;
+ struct sfnt_interpreter_zone *volatile preserved_zone;
+ sfnt_f26dot6 phantom_point_1_x;
+ sfnt_f26dot6 phantom_point_1_y;
+ sfnt_f26dot6 phantom_point_2_x;
+ sfnt_f26dot6 phantom_point_2_y;
+ sfnt_f26dot6 tem;
+ volatile bool zone_was_allocated;
+ struct sfnt_instructed_outline *outline;
+
+ zone_size = 0;
+ zone_was_allocated = false;
+
+ /* Calculate the size of the zone structure. */
+
+ if (INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+ sizeof *zone->x_points * 4,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (glyph->number_of_contours,
+ sizeof *zone->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+ sizeof *zone->flags,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+ return "Glyph exceeded maximum permissible size";
+
+ /* Don't use malloc if possible. */
+
+ if (zone_size <= 1024 * 16)
+ zone = alloca (zone_size);
+ else
+ {
+ zone = xmalloc (zone_size);
+ zone_was_allocated = true;
+ }
+
+ /* Now load the zone with data. */
+ zone->num_points = glyph->simple->number_of_points + 2;
+ zone->num_contours = glyph->number_of_contours;
+ zone->contour_end_points = (size_t *) (zone + 1);
+ zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ + zone->num_contours);
+ zone->x_current = zone->x_points + zone->num_points;
+ zone->y_points = zone->x_current + zone->num_points;
+ zone->y_current = zone->y_points + zone->num_points;
+ zone->flags = (unsigned char *) (zone->y_current
+ + zone->num_points);
+
+ /* Load x_points and x_current. */
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ /* Load the fword. */
+ tem = glyph->simple->x_coordinates[i];
+
+ /* Scale that fword. */
+ tem = sfnt_mul_f26dot6_fixed (tem * 64, interpreter->scale);
+
+ /* Set x_points and x_current. */
+ zone->x_points[i] = tem;
+ zone->x_current[i] = tem;
+ }
+
+ /* Compute phantom points. */
+ sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+ &phantom_point_1_x, &phantom_point_1_y,
+ &phantom_point_2_x, &phantom_point_2_y);
+
+ /* Load phantom points. */
+ zone->x_points[i] = phantom_point_1_x;
+ zone->x_points[i + 1] = phantom_point_2_x;
+ zone->x_current[i] = phantom_point_1_x;
+ zone->x_current[i + 1] = phantom_point_2_x;
+
+ /* Load y_points and y_current, along with flags. */
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ /* Load the fword. */
+ tem = glyph->simple->y_coordinates[i];
+
+ /* Scale that fword. Make sure not to round Y, as this could
+ lead to Y spilling over to the next line. */
+ tem = sfnt_mul_fixed (tem * 64, interpreter->scale);
+
+ /* Set y_points and y_current. */
+ zone->y_points[i] = tem;
+ zone->y_current[i] = tem;
+
+ /* Set flags. */
+ zone->flags[i] = (glyph->simple->flags[i]
+ & ~SFNT_POINT_TOUCHED_BOTH);
+
+ /* Make sure to clear the phantom points flag. */
+ zone->flags[i] &= ~SFNT_POINT_PHANTOM;
+ }
+
+ /* Load phantom points. */
+ zone->y_points[i] = phantom_point_1_y;
+ zone->y_points[i + 1] = phantom_point_2_y;
+ zone->y_current[i] = phantom_point_1_x;
+ zone->y_current[i + 1] = phantom_point_2_x;
+
+ /* Load phantom point flags. */
+ zone->flags[i] = SFNT_POINT_PHANTOM;
+ zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+ /* Load contour end points. */
+ for (i = 0; i < zone->num_contours; ++i)
+ zone->contour_end_points[i]
+ = glyph->simple->end_pts_of_contours[i];
+
+ /* Load the glyph program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = glyph->simple->instructions;
+ interpreter->num_instructions = glyph->simple->instruction_length;
+ interpreter->glyph_zone = zone;
+
+ /* Copy zone over to this volatile variable. */
+ preserved_zone = zone;
+
+ if (setjmp (interpreter->trap))
+ {
+ if (zone_was_allocated)
+ xfree (preserved_zone);
+
+ interpreter->glyph_zone = NULL;
+ return interpreter->trap_reason;
+ }
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+ interpreter->glyph_zone = NULL;
+
+ /* Move preserved_zone back to zone. */
+ zone = preserved_zone;
+
+ /* Now that the program has been run, build the scaled outline. */
+
+ outline_size = sizeof (*outline);
+ outline_size += (zone->num_contours
+ * sizeof *outline->contour_end_points);
+ outline_size += (zone->num_points
+ * sizeof *outline->x_points * 2);
+ outline_size += zone->num_points;
+
+ /* Allocate the outline. */
+ outline = xmalloc (outline_size);
+ outline->num_points = zone->num_points;
+ outline->num_contours = zone->num_contours;
+ outline->contour_end_points = (size_t *) (outline + 1);
+ outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ + outline->num_contours);
+ outline->y_points = outline->x_points + outline->num_points;
+ outline->flags = (unsigned char *) (outline->y_points
+ + outline->num_points);
+
+ /* Copy over the contour endpoints, points, and flags. */
+ memcpy (outline->contour_end_points, zone->contour_end_points,
+ zone->num_contours * sizeof *outline->contour_end_points);
+ memcpy (outline->x_points, zone->x_current,
+ zone->num_points * sizeof *outline->x_points);
+ memcpy (outline->y_points, zone->y_current,
+ zone->num_points * sizeof *outline->y_points);
+ memcpy (outline->flags, zone->flags, zone->num_points);
+
+ /* Free the zone if necessary. */
+ if (zone_was_allocated)
+ xfree (zone);
+
+ /* Return the outline and NULL. */
+ *value = outline;
+ return NULL;
+}
+
+/* Apply the transform in the compound glyph component COMPONENT to
+ the array of points of length NUM_COORDINATES given as X and Y.
+
+ Treat X and Y as arrays of 26.6 fixed point values.
+
+ Also, apply the 26.6 fixed point offsets X_OFF and Y_OFF to each X
+ and Y coordinate.
+
+ See sfnt_decompose_compound_glyph for an explanation of why offsets
+ might be applied here, and not while reading the subglyph
+ itself. */
+
+static void
+sfnt_transform_f26dot6 (struct sfnt_compound_glyph_component *component,
+ sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t num_coordinates,
+ sfnt_f26dot6 x_off, sfnt_f26dot6 y_off)
+{
+ double m1, m2, m3;
+ double m4, m5, m6;
+ size_t i;
+
+ if (component->flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.scale / 16384.0;
+ y[i] *= component->u.scale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.a.xscale / 16384.0;
+ y[i] *= component->u.a.yscale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is
+ simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+ 1.0. See the definition of matrix3x3 in image.c for some
+ more explanations about this.) */
+ m1 = component->u.b.xscale / 16384.0;
+ m2 = component->u.b.scale01 / 16384.0;
+ m3 = 0;
+ m4 = component->u.b.scale10 / 16384.0;
+ m5 = component->u.b.yscale / 16384.0;
+ m6 = 0;
+
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+ y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph_3.
+
+ Instruct the compound glyph GLYPH using INTERPRETER after all of
+ its components have been instructed.
+
+ Use the unscaled METRICS to compute the phantom points of this
+ glyph.
+
+ CONTEXT contains the points and contours of this compound glyph,
+ numbered starting from BASE_INDEX and BASE_CONTOUR respectively.
+
+ Value is NULL upon success, or a description of the error upon
+ failure. */
+
+static const char *
+sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_compound_glyph_context *context,
+ size_t base_index, size_t base_contour,
+ struct sfnt_glyph_metrics *metrics)
+{
+ size_t num_points, num_contours, i;
+ size_t zone_size, temp;
+ struct sfnt_interpreter_zone *zone;
+ struct sfnt_interpreter_zone *volatile preserved_zone;
+ sfnt_f26dot6 phantom_point_1_x;
+ sfnt_f26dot6 phantom_point_1_y;
+ sfnt_f26dot6 phantom_point_2_x;
+ sfnt_f26dot6 phantom_point_2_y;
+ volatile bool zone_was_allocated;
+ int rc;
+ sfnt_f26dot6 *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+
+ /* Figure out how many points and contours there are to
+ instruct. */
+ num_points = context->num_points - base_index;
+ num_contours = context->num_end_points - base_contour;
+
+ /* Nothing to instruct! */
+ if (!num_points && !num_contours)
+ return NULL;
+
+ /* Build the zone. First, calculate the size of the zone
+ structure. */
+
+ zone_size = 0;
+ zone_was_allocated = false;
+
+ if (INT_MULTIPLY_WRAPV (num_points + 2,
+ sizeof *zone->x_points * 4,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (num_contours,
+ sizeof *zone->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (num_points + 2,
+ sizeof *zone->flags,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+ return "Glyph exceeded maximum permissible size";
+
+ /* Don't use malloc if possible. */
+
+ if (zone_size <= 1024 * 16)
+ zone = alloca (zone_size);
+ else
+ {
+ zone = xmalloc (zone_size);
+ zone_was_allocated = true;
+ }
+
+ /* Now load the zone with data. */
+ zone->num_points = num_points + 2;
+ zone->num_contours = num_contours;
+ zone->contour_end_points = (size_t *) (zone + 1);
+ zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ + zone->num_contours);
+ zone->x_current = zone->x_points + zone->num_points;
+ zone->y_points = zone->x_current + zone->num_points;
+ zone->y_current = zone->y_points + zone->num_points;
+ zone->flags = (unsigned char *) (zone->y_current
+ + zone->num_points);
+
+ /* Copy and renumber all contour end points to start from
+ base_index. */
+
+ for (i = 0; i < zone->num_contours; ++i)
+ zone->contour_end_points[i]
+ = (context->contour_end_points[base_contour + i]
+ - base_index);
+
+ /* Now copy over x_points, x_current, y_points and y_current. */
+
+ for (i = 0; i < num_points; ++i)
+ {
+ zone->x_current[i] = context->x_coordinates[i + base_index];
+ zone->x_points[i] = context->x_coordinates[i + base_index];
+ }
+
+ /* Compute phantom points. */
+ sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+ &phantom_point_1_x, &phantom_point_1_y,
+ &phantom_point_2_x, &phantom_point_2_y);
+
+ /* Load phantom points. */
+ zone->x_points[i] = phantom_point_1_x;
+ zone->x_points[i + 1] = phantom_point_2_x;
+ zone->x_current[i] = phantom_point_1_x;
+ zone->x_current[i + 1] = phantom_point_2_x;
+
+ for (i = 0; i < num_points; ++i)
+ {
+ zone->y_current[i] = context->y_coordinates[i + base_index];
+ zone->y_points[i] = context->y_coordinates[i + base_index];
+
+ /* Set flags. */
+ zone->flags[i] = (context->flags[i + base_index]
+ & ~SFNT_POINT_TOUCHED_BOTH);
+ }
+
+ /* Load phantom points. */
+ zone->y_points[i] = phantom_point_1_y;
+ zone->y_points[i + 1] = phantom_point_2_y;
+ zone->y_current[i] = phantom_point_1_x;
+ zone->y_current[i + 1] = phantom_point_2_x;
+
+ /* Load phantom point flags. */
+ zone->flags[i] = SFNT_POINT_PHANTOM;
+ zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+ /* Load the compound glyph program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = glyph->compound->instructions;
+ interpreter->num_instructions = glyph->compound->instruction_length;
+ interpreter->glyph_zone = zone;
+
+ /* Copy zone over to this volatile variable. */
+ preserved_zone = zone;
+
+ if (setjmp (interpreter->trap))
+ {
+ if (zone_was_allocated)
+ xfree (preserved_zone);
+
+ interpreter->glyph_zone = NULL;
+ return interpreter->trap_reason;
+ }
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+ interpreter->glyph_zone = NULL;
+
+ /* Move preserved_zone back to zone. */
+ zone = preserved_zone;
+
+ /* Now copy the instructed points back, and add the two phantom
+ points to the end. */
+
+ for (i = 0; i < num_points; ++i)
+ {
+ context->x_coordinates[base_index + i] = zone->x_current[i];
+ context->y_coordinates[base_index + i] = zone->y_current[i];
+ }
+
+ /* Grow various arrays to fit the phantom points. */
+ rc = sfnt_expand_compound_glyph_context (context, 0, 2,
+ &x_base, &y_base,
+ &flags_base,
+ &contour_base);
+
+ if (rc)
+ {
+ if (zone_was_allocated)
+ xfree (zone);
+
+ return "Failed to expand arrays for phantom points";
+ }
+
+ /* Copy over the phantom points. */
+ x_base[0] = zone->x_current[num_points - 2];
+ x_base[1] = zone->x_current[num_points - 1];
+ y_base[0] = zone->y_current[num_points - 2];
+ y_base[1] = zone->y_current[num_points - 1];
+ flags_base[0] = zone->flags[num_points - 2];
+ flags_base[1] = zone->flags[num_points - 1];
+
+ /* Free the zone if needed. */
+ if (zone_was_allocated)
+ xfree (zone);
+
+ return NULL;
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph.
+ RECURSION_COUNT is the number of times this function has called itself.
+ OFF_X and OFF_Y are the offsets to apply to the glyph outline.
+
+ METRICS are the unscaled metrics of this compound glyph.
+
+ Other arguments mean the same as they do in
+ `sfnt_interpret_compound_glyph'. */
+
+static const char *
+sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_graphics_state *state,
+ struct sfnt_compound_glyph_context *context,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed off_x, sfnt_fixed off_y,
+ int recursion_count,
+ void *dcontext)
+{
+ struct sfnt_glyph *subglyph;
+ int i, j, rc;
+ const char *error;
+ bool need_free;
+ struct sfnt_compound_glyph_component *component;
+ sfnt_f26dot6 x, y, xtemp, ytemp;
+ size_t point, point2;
+ size_t last_point, number_of_contours;
+ sfnt_f26dot6 *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+ size_t base_index, contour_start, base_contour;
+ bool defer_offsets;
+ struct sfnt_instructed_outline *value;
+ struct sfnt_glyph_metrics sub_metrics;
+
+ error = NULL;
+
+ /* Set up the base index. This is the index from where on point
+ renumbering starts.
+
+ In other words, point 0 in this glyph will be 0 + base_index,
+ point 1 will be 1 + base_index, and so on. */
+ base_index = context->num_points;
+
+ /* And this is the index of the first contour in this glyph. */
+ base_contour = context->num_end_points;
+
+ /* Prevent infinite loops. */
+ if (recursion_count > 12)
+ return "Overly deep recursion in compound glyph data";
+
+ /* Don't defer offsets. */
+ defer_offsets = false;
+
+ /* Pacify -Wmaybe-uninitialized. */
+ point = point2 = 0;
+
+ for (j = 0; j < glyph->compound->num_components; ++j)
+ {
+ /* Look up the associated subglyph. */
+ component = &glyph->compound->components[j];
+ subglyph = get_glyph (component->glyph_index,
+ dcontext, &need_free);
+
+ if (!subglyph)
+ return "Failed to obtain component glyph";
+
+ /* Record the size of the point array before expansion. This
+ will be the base to apply to all points coming from this
+ subglyph. */
+ contour_start = context->num_points;
+
+ /* Compute the offset for the component. */
+ if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+ {
+ /* Component offsets are X/Y values as opposed to points
+ GLYPH. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ /* X and Y are signed bytes. */
+ x = component->argument1.b * 64;
+ y = component->argument2.b * 64;
+ }
+ else
+ {
+ /* X and Y are signed words. */
+ x = component->argument1.d * 64;
+ y = component->argument2.d * 64;
+ }
+
+ /* Now convert X and Y into device coordinates. */
+ x = sfnt_mul_f26dot6_fixed (x, interpreter->scale);
+ y = sfnt_mul_f26dot6_fixed (y, interpreter->scale);
+
+ /* If there is some kind of scale and component offsets are
+ scaled, then apply the transform to the offset. */
+ if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+ sfnt_transform_f26dot6 (component, &x, &y, 1,
+ 0, 0);
+
+ if (component->flags & 04) /* ROUND_XY_TO_GRID */
+ {
+ x = sfnt_round_f26dot6 (x);
+ y = sfnt_round_f26dot6 (y);
+ }
+ }
+ else
+ {
+ /* The offset is determined by matching a point location in
+ a preceeding component with a point location in the
+ current component. The index of the point in the
+ previous component can be determined by adding
+ component->argument1.a or component->argument1.c to
+ point. argument2 contains the index of the point in the
+ current component. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ point = base_index + component->argument1.a;
+ point2 = component->argument2.a;
+ }
+ else
+ {
+ point = base_index + component->argument1.c;
+ point2 = component->argument2.c;
+ }
+
+ /* Now, check that the anchor point specified lies inside
+ the glyph. */
+
+ if (point >= contour_start)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid anchor point";
+ }
+
+ if (!subglyph->compound)
+ {
+ if (point2 >= subglyph->simple->number_of_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid anchored point";
+ }
+
+ /* Get the points and use them to compute the offsets. */
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - subglyph->simple->x_coordinates[point2] * 64);
+ y = (ytemp - subglyph->simple->y_coordinates[point2] * 64);
+ }
+ else
+ {
+ /* First, set offsets to 0, because it is not yet
+ possible to determine the position of the anchor
+ point in the child. */
+ x = 0;
+ y = 0;
+
+ /* Set a flag which indicates that offsets must be
+ resolved from the child glyph after it is loaded, but
+ before it is incorporated into the parent glyph. */
+ defer_offsets = true;
+ }
+ }
+
+ /* Obtain the glyph metrics. If doing so fails, then cancel
+ decomposition. */
+
+ if (sfnt_lookup_glyph_metrics (component->glyph_index,
+ -1, &sub_metrics,
+ hmtx, hhea, NULL, maxp))
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Failed to obtain component metrics";
+ }
+
+ if (subglyph->simple)
+ {
+ /* Simple subglyph. Copy over the points and contours,
+ then transform and instruct them.
+
+ Skip this step for glyphs without contours. */
+
+ if (subglyph->number_of_contours)
+ {
+ /* Now instruct the simple glyph, and copy it over,
+ including the two phantom points at the end. */
+ interpreter->state = *state;
+ error = sfnt_interpret_simple_glyph (subglyph, interpreter,
+ &sub_metrics, &value);
+
+ /* Cancel instructing if an error occurs. */
+
+ if (error)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return error;
+ }
+
+ /* Figure out how many more points and contours are
+ needed. Here, last_point is not the end of the
+ glyph's contours, as two phantom points are
+ included. */
+ last_point = value->num_points;
+ number_of_contours = value->num_contours;
+
+ /* Grow various arrays. */
+ rc = sfnt_expand_compound_glyph_context (context,
+ /* Number of
+ new contours
+ required. */
+ number_of_contours,
+ /* Number of new
+ points
+ required. */
+ last_point,
+ &x_base,
+ &y_base,
+ &flags_base,
+ &contour_base);
+ if (rc)
+ {
+ xfree (value);
+
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Failed to grow arrays";
+ }
+
+ /* Copy the values in VALUE into the context and free
+ VALUE, including phantom points. */
+
+ for (i = 0; i < last_point; ++i)
+ {
+ x_base[i] = value->x_points[i] + off_x + x;
+ y_base[i] = value->y_points[i] + off_y + y;
+ flags_base[i] = value->flags[i];
+ }
+
+ /* Copy over the contours. */
+ for (i = 0; i < number_of_contours; ++i)
+ contour_base[i] = (contour_start
+ + value->contour_end_points[i]);
+
+ xfree (value);
+
+ /* Apply the transform to the points. */
+ sfnt_transform_f26dot6 (component, x_base, y_base,
+ last_point, 0, 0);
+ }
+ }
+ else
+ {
+ /* Compound subglyph. Decompose and instruct the glyph
+ recursively, and then apply the transform. */
+
+ error = sfnt_interpret_compound_glyph_1 (subglyph, interpreter,
+ state,
+ context, get_glyph,
+ free_glyph, hmtx, hhea,
+ maxp, &sub_metrics,
+ off_x + x, off_y + y,
+ recursion_count + 1,
+ dcontext);
+
+ if (error)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return error;
+ }
+
+ /* When an anchor point is being used to translate the
+ glyph, and the subglyph in question is actually a
+ compound glyph, it is impossible to know which offset to
+ use until the compound subglyph has actually been
+ loaded.
+
+ As a result, the offset is calculated here, using the
+ points in the loaded child compound glyph. But first, X
+ and Y must be reset to 0, as otherwise the translation
+ might be applied twice if defer_offsets is not set. */
+
+ x = 0;
+ y = 0;
+
+ if (defer_offsets)
+ {
+ /* Renumber the non renumbered point2 to point into the
+ decomposed component. */
+ point2 += contour_start;
+
+ /* Next, check that the non-renumbered point being
+ anchored lies inside the glyph data that was
+ decomposed. */
+
+ if (point2 >= context->num_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid point2";
+ }
+
+ /* Get the points and use them to compute the
+ offsets. */
+
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - context->x_coordinates[point2]);
+ y = (ytemp - context->y_coordinates[point2]);
+ }
+
+ sfnt_transform_f26dot6 (component,
+ context->x_coordinates + contour_start,
+ context->y_coordinates + contour_start,
+ contour_start - context->num_points,
+ x, y);
+ }
+
+ /* Finally, free the subglyph. */
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+ }
+
+ /* Run the program for the entire compound glyph, if any. */
+
+ if (glyph->compound->instruction_length)
+ {
+ interpreter->state = *state;
+ error = sfnt_interpret_compound_glyph_2 (glyph, interpreter,
+ context, base_index,
+ base_contour,
+ metrics);
+ }
+
+ return error;
+}
+
+/* Interpret the compound glyph GLYPH using the specified INTERPRETER.
+ Load each component of the compound glyph GLYPH. CONTEXT should be
+ a reference to a `struct sfnt_compound_glyph_context' that is on
+ the stack.
+
+ Use glyph metrics specified in the HMTX, HHEA and MAXP tables, and
+ the unscaled metrics for GLYPH specified in METRICS.
+
+ If the component is a simple glyph, scale and instruct it
+ immediately.
+
+ If the component is a compound glyph, then load it recursively
+ until a predetermined recursion level is exceeded.
+
+ Set INTERPRETER's state to STATE prior to instructing each
+ component.
+
+ Load component glyphs using GET_GLYPH, which should return whether
+ or not FREE_GLYPH should be called with the glyph that was loaded.
+ Call both functions with DCONTEXT as an argument.
+
+ Finally, append the resulting contours, run any compound glyph
+ program, and return the instructed outline in *VALUE.
+
+ Value is NULL upon success, and the type of error upon failure. */
+
+TEST_STATIC const char *
+sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_graphics_state *state,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_glyph_metrics *metrics,
+ void *dcontext,
+ struct sfnt_instructed_outline **value)
+{
+ struct sfnt_compound_glyph_context context;
+ const char *error;
+ struct sfnt_instructed_outline *outline;
+ size_t outline_size, temp;
+
+ /* Set up the glyph decomposition context. */
+ memset (&context, 0, sizeof context);
+
+ /* Now start decomposing the glyph. */
+ error = sfnt_interpret_compound_glyph_1 (glyph, interpreter,
+ state, &context,
+ get_glyph, free_glyph,
+ hmtx, hhea, maxp,
+ metrics, 0, 0, 0,
+ dcontext);
+
+ /* If an error occurs, free the data in the context and return. */
+
+ if (error)
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return error;
+ }
+
+ /* Copy the compound glyph data into an instructed outline. */
+ outline_size = sizeof (*outline);
+
+ if (INT_MULTIPLY_WRAPV (context.num_end_points,
+ sizeof *outline->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+ || INT_MULTIPLY_WRAPV (context.num_points,
+ sizeof *outline->x_points * 2,
+ &temp)
+ || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+ || INT_ADD_WRAPV (context.num_points, outline_size,
+ &outline_size))
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return "Glyph exceeds maximum permissible size";
+ }
+
+ /* Allocate the outline. */
+ outline = xmalloc (outline_size);
+ outline->num_points = context.num_points;
+ outline->num_contours = context.num_end_points;
+ outline->contour_end_points = (size_t *) (outline + 1);
+ outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ + outline->num_contours);
+ outline->y_points = outline->x_points + outline->num_points;
+ outline->flags = (unsigned char *) (outline->y_points
+ + outline->num_points);
+
+ /* Copy over the contour endpoints, points, and flags. */
+ memcpy (outline->contour_end_points, context.contour_end_points,
+ outline->num_contours * sizeof *outline->contour_end_points);
+ memcpy (outline->x_points, context.x_coordinates,
+ outline->num_points * sizeof *outline->x_points);
+ memcpy (outline->y_points, context.y_coordinates,
+ outline->num_points * sizeof *outline->y_points);
+ memcpy (outline->flags, context.flags, context.num_points);
+
+ /* Free the context data. */
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+
+ *value = outline;
+ return NULL;
+}
+
+
+
+
+/* Unicode Variation Sequence (UVS) support.
+
+ Unicode defines a mechanism by which a two-codepoint sequence
+ consisting of a ``base character'' and ``variation selector'' is
+ able to produce a glyph that is a variant of the glyph that would
+ conventionally have been mapped to the ``base character''.
+
+ TrueType describes variation selector sequences through a type of
+ character mapping table that is given the format 14. The character
+ mapping table consists of an array of variation selectors, each of
+ which have a corresponding ``default UVS table'', which describes
+ ranges of ``base characters'' having no special variant glyphs, and
+ a ``non-default UVS table'', which is a map of ``base characters''
+ to their corresponding variant glyphs. */
+
+/* Read a default UVS table from the font file FD, at the specified
+ OFFSET. Value is the default UVS table upon success, else
+ NULL. */
+
+static struct sfnt_default_uvs_table *
+sfnt_read_default_uvs_table (int fd, off_t offset)
+{
+ struct sfnt_default_uvs_table *uvs;
+ uint32_t num_ranges, i, j;
+ size_t size, temp;
+ char data[512];
+
+ /* First, seek to the given offset. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ return NULL;
+
+ /* Next, read the number of ranges present. */
+
+ if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges)
+ return NULL;
+
+ /* Swap the number of ranges present. */
+ sfnt_swap32 (&num_ranges);
+
+ /* Now, allocate enough to hold the UVS table. */
+
+ size = sizeof *uvs;
+ if (INT_MULTIPLY_WRAPV (sizeof *uvs->ranges, num_ranges,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+
+ uvs = xmalloc (size);
+
+ /* Fill in the data which was already read. */
+ uvs->num_unicode_value_ranges = num_ranges;
+
+ /* Fill in the pointer to the ranges. */
+ uvs->ranges = (struct sfnt_unicode_value_range *) (uvs + 1);
+ i = 0;
+
+ /* Read each default UVS range in multiples of 512 bytes. Then,
+ fill in uvs->ranges. */
+
+ while (num_ranges)
+ {
+ size = (num_ranges > 128 ? 512 : num_ranges * 4);
+
+ if (read (fd, data, size) != size)
+ {
+ xfree (uvs);
+ return NULL;
+ }
+
+ for (j = 0; j < size / 4; ++j)
+ {
+ uvs->ranges[i + j].start_unicode_value
+ = sfnt_read_24 ((unsigned char *) data + j * 4);
+ uvs->ranges[i + j].additional_count = data[j * 4 + 1];
+ }
+
+ i += j;
+ num_ranges -= size / 4;
+ }
+
+ /* Return the resulting default UVS table. */
+ return uvs;
+}
+
+/* Read a non-default UVS table from the font file FD, at the
+ specified OFFSET. Value is the non-default UVS table upon success,
+ else NULL. */
+
+static struct sfnt_nondefault_uvs_table *
+sfnt_read_nondefault_uvs_table (int fd, off_t offset)
+{
+ struct sfnt_nondefault_uvs_table *uvs;
+ uint32_t num_mappings, i, j;
+ size_t size, temp;
+ char data[500];
+
+ /* First, seek to the given offset. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ return NULL;
+
+ /* Next, read the number of mappings present. */
+
+ if (read (fd, &num_mappings, sizeof num_mappings)
+ != sizeof num_mappings)
+ return NULL;
+
+ /* Swap the number of mappings present. */
+ sfnt_swap32 (&num_mappings);
+
+ /* Now, allocate enough to hold the UVS table. */
+
+ size = sizeof *uvs;
+ if (INT_MULTIPLY_WRAPV (sizeof *uvs->mappings, num_mappings,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+
+ uvs = xmalloc (size);
+
+ /* Fill in the data which was already read. */
+ uvs->num_uvs_mappings = num_mappings;
+
+ /* Fill in the pointer to the mappings. */
+ uvs->mappings = (struct sfnt_uvs_mapping *) (uvs + 1);
+
+ i = 0;
+
+ /* Read each nondefault UVS mapping in multiples of 500 bytes.
+ Then, fill in uvs->ranges. */
+
+ while (num_mappings)
+ {
+ size = (num_mappings > 100 ? 500 : num_mappings * 5);
+
+ if (read (fd, data, size) != size)
+ {
+ xfree (uvs);
+ return NULL;
+ }
+
+ for (j = 0; j < size / 5; ++j)
+ {
+ uvs->mappings[i + j].unicode_value
+ = sfnt_read_24 ((unsigned char *) data + j * 5);
+ memcpy (&uvs->mappings[i + j].base_character_value,
+ data + j * 5 + 3,
+ sizeof uvs->mappings[i + j].base_character_value);
+ sfnt_swap16 (&uvs->mappings[i + j].base_character_value);
+ }
+
+ i += j;
+ num_mappings -= size / 5;
+ }
+
+ /* Return the nondefault UVS table. */
+ return uvs;
+}
+
+/* Perform comparison of A and B, two table offsets. */
+
+static int
+sfnt_compare_table_offsets (const void *a, const void *b)
+{
+ const struct sfnt_table_offset_rec *rec_a, *rec_b;
+
+ rec_a = a;
+ rec_b = b;
+
+ if (rec_a->offset < rec_b->offset)
+ return -1;
+ else if (rec_a->offset > rec_b->offset)
+ return 1;
+
+ return 0;
+}
+
+/* Create a variation selection context based on the format 14 cmap
+ subtable CMAP.
+
+ FD is the font file to which the table belongs.
+
+ Value is the variation selection context upon success, else NULL.
+ The context contains each variation selector record and their
+ associated default and nondefault UVS tables. Free the context
+ with `sfnt_free_uvs_context'. */
+
+TEST_STATIC struct sfnt_uvs_context *
+sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd)
+{
+ struct sfnt_table_offset_rec *table_offsets, *rec, template;
+ size_t size, i, nmemb, j;
+ off_t offset;
+ struct sfnt_uvs_context *context;
+
+ if (INT_MULTIPLY_WRAPV (cmap->num_var_selector_records,
+ sizeof *table_offsets, &size)
+ || INT_MULTIPLY_WRAPV (size, 2, &size))
+ return NULL;
+
+ context = NULL;
+
+ /* First, record and sort the UVS and nondefault UVS table offsets
+ in ascending order. */
+
+ table_offsets = xmalloc (size);
+ memset (table_offsets, 0, size);
+ nmemb = cmap->num_var_selector_records * 2;
+ j = 0;
+
+ for (i = 0; i < cmap->num_var_selector_records; ++i)
+ {
+ /* Note that either offset may be 0, meaning there is no such
+ table. */
+
+ if (cmap->records[i].default_uvs_offset)
+ {
+ if (INT_ADD_WRAPV (cmap->offset,
+ cmap->records[i].default_uvs_offset,
+ &table_offsets[j].offset))
+ goto bail;
+
+ table_offsets[j++].is_nondefault_table = false;
+ }
+
+ if (cmap->records[i].nondefault_uvs_offset)
+ {
+ if (INT_ADD_WRAPV (cmap->offset,
+ cmap->records[i].nondefault_uvs_offset,
+ &table_offsets[j].offset))
+ goto bail;
+
+ table_offsets[j++].is_nondefault_table = true;
+ }
+ }
+
+ /* Make nmemb the number of offsets actually looked up. */
+ nmemb = j;
+
+ qsort (table_offsets, nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ /* Now go through table_offsets, and read everything. nmemb is the
+ number of elements in table_offsets[i]; it is kept up to date
+ when duplicate members are removed. */
+ offset = -1;
+
+ for (i = 0; i < nmemb; ++i)
+ {
+ /* Skip past duplicate tables. */
+
+ while (table_offsets[i].offset == offset && i < nmemb)
+ {
+ nmemb--;
+ table_offsets[i] = table_offsets[i + 1];
+ }
+
+ /* If the last element of the array is a duplicate, break out of
+ the loop. */
+
+ if (i == nmemb)
+ break;
+
+ /* Read the correct type of table depending on
+ table_offsets[i].is_nondefault_table. Then skip past
+ duplicate tables. Don't handle the case where two different
+ kind of tables share the same offset, because that is not
+ possible in a valid variation selector record. */
+
+ offset = table_offsets[i].offset;
+
+ if (table_offsets[i].is_nondefault_table)
+ table_offsets[i].table
+ = sfnt_read_nondefault_uvs_table (fd, offset);
+ else
+ table_offsets[i].table
+ = sfnt_read_default_uvs_table (fd, offset);
+ }
+
+ /* Now make the context. */
+ context = xmalloc (sizeof *context);
+ context->num_records = cmap->num_var_selector_records;
+ context->nmemb = nmemb;
+ context->records = xmalloc (sizeof *context->records
+ * cmap->num_var_selector_records);
+
+ for (i = 0; i < cmap->num_var_selector_records; ++i)
+ {
+ context->records[i].selector = cmap->records[i].var_selector;
+
+ /* Either offset may be 0, meaning no such table exists. Also,
+ the code below will lose if more than one kind of table
+ shares the same offset, because that is impossible. */
+
+ if (cmap->records[i].default_uvs_offset)
+ {
+ /* Resolve the default table. */
+ template.offset = (cmap->records[i].default_uvs_offset
+ + cmap->offset);
+ rec = bsearch (&template, table_offsets,
+ nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ /* Make sure this record is the right type. */
+ if (!rec || rec->is_nondefault_table || !rec->table)
+ goto bail;
+
+ context->records[i].default_uvs = rec->table;
+ }
+ else
+ context->records[i].default_uvs = NULL;
+
+ if (cmap->records[i].nondefault_uvs_offset)
+ {
+ /* Resolve the nondefault table. */
+ template.offset = (cmap->records[i].nondefault_uvs_offset
+ + cmap->offset);
+ rec = bsearch (&template, table_offsets,
+ nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ if (!rec)
+ goto bail;
+
+ /* Make sure this record is the right type. */
+ if (!rec || !rec->is_nondefault_table || !rec->table)
+ goto bail;
+
+ context->records[i].nondefault_uvs = rec->table;
+ }
+ else
+ context->records[i].nondefault_uvs = NULL;
+ }
+
+ context->tables = table_offsets;
+ return context;
+
+ bail:
+
+ if (context)
+ {
+ xfree (context->records);
+ xfree (context);
+ }
+
+ /* Loop through and free any tables that might have been read
+ already. */
+
+ for (i = 0; i < nmemb; ++i)
+ xfree (table_offsets[i].table);
+
+ xfree (table_offsets);
+ return NULL;
+}
+
+/* Free the specified variation selection context C. */
+
+TEST_STATIC void
+sfnt_free_uvs_context (struct sfnt_uvs_context *c)
+{
+ size_t i;
+
+ xfree (c->records);
+
+ for (i = 0; i < c->nmemb; ++i)
+ xfree (c->tables[i].table);
+
+ xfree (c->tables);
+ xfree (c);
+}
+
+/* Compare *(sfnt_char *) K to ((struct sfnt_uvs_mapping *)
+ V)->unicode_value appropriately for bsearch. */
+
+static int
+sfnt_compare_uvs_mapping (const void *k, const void *v)
+{
+ const sfnt_char *key;
+ const struct sfnt_uvs_mapping *value;
+
+ key = k;
+ value = v;
+
+ if (*key < value->unicode_value)
+ return -1;
+ else if (*key == value->unicode_value)
+ return 0;
+
+ return 1;
+}
+
+/* Return the ID of a variation glyph for the character C in the
+ nondefault UVS mapping table UVS.
+
+ Value is the glyph ID upon success, or 0 if there is no variation
+ glyph for the base character C. */
+
+TEST_STATIC sfnt_glyph
+sfnt_variation_glyph_for_char (struct sfnt_nondefault_uvs_table *uvs,
+ sfnt_char c)
+{
+ struct sfnt_uvs_mapping *mapping;
+
+ mapping = bsearch (&c, uvs->mappings, uvs->num_uvs_mappings,
+ sizeof *uvs->mappings,
+ sfnt_compare_uvs_mapping);
+
+ return mapping ? mapping->base_character_value : 0;
+}
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.
+ It useful to map OpenType layout tables prior to using them in
+ an external shaping engine such as HarfBuzz. */
+
+/* Map a table identified by TAG into the structure *TABLE.
+ TAG is swapped into host byte order.
+
+ Use the table directory SUBTABLE, which corresponds to the font
+ file FD.
+
+ Return 0 upon success, and set TABLE->data to the table data,
+ TABLE->mapping to the start of the mapped area, TABLE->length to
+ the length of the table contents, and TABLE->size to the size of
+ the mapping.
+
+ Return 1 upon failure. */
+
+int
+sfnt_map_table (int fd, struct sfnt_offset_subtable *subtable,
+ uint32_t tag, struct sfnt_mapped_table *table)
+{
+ struct sfnt_table_directory *directory;
+ size_t offset, page, map_offset;
+ void *data;
+ int i;
+
+ /* Find the table in the directory. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == tag)
+ {
+ directory = &subtable->subtables[i];
+ break;
+ }
+ }
+
+ if (i == subtable->num_tables)
+ return 1;
+
+ /* Now try to map the glyph data. Make sure offset is a multiple of
+ the page size. */
+
+ page = getpagesize ();
+ offset = directory->offset & ~(page - 1);
+
+ /* Figure out how much larger the mapping should be. */
+ map_offset = directory->offset - offset;
+
+ /* Do the mmap. */
+ data = mmap (NULL, directory->length + map_offset,
+ PROT_READ, MAP_PRIVATE, fd, offset);
+
+ if (data == MAP_FAILED)
+ return 1;
+
+ /* Fill in *TABLE. */
+ table->data = (unsigned char *) data + map_offset;
+ table->mapping = data;
+ table->length = directory->length;
+ table->size = directory->length + map_offset;
+ return 0;
+}
+
+/* Unmap the table inside *TABLE.
+ Value is 0 upon success, 1 otherwise. */
+
+int
+sfnt_unmap_table (struct sfnt_mapped_table *table)
+{
+ return munmap (table->mapping, table->size) != 0;
+}
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+#ifndef TEST
+
+/* Reading table contents. */
+
+/* Read the table with the specified TAG from the font file FD.
+ Return its length in *LENGTH, and its data upon success, else
+ NULL. */
+
+void *
+sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable,
+ uint32_t tag, size_t *length)
+{
+ struct sfnt_table_directory *directory;
+ void *data;
+ int i;
+
+ /* Find the table in the directory. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == tag)
+ {
+ directory = &subtable->subtables[i];
+ break;
+ }
+ }
+
+ if (i == subtable->num_tables)
+ return NULL;
+
+ /* Seek to the table. */
+
+ if (lseek (fd, directory->offset, SEEK_SET) != directory->offset)
+ return NULL;
+
+ /* Now allocate enough to hold the data and read into it. */
+
+ data = xmalloc (directory->length);
+ if (read (fd, data, directory->length) != directory->length)
+ {
+ xfree (data);
+ return NULL;
+ }
+
+ /* Return the length and table data. */
+ *length = directory->length;
+ return data;
+}
+
+#endif /* !TEST */
+
+
+
+/* Glyph variations. Instead of defining separate fonts for each
+ combination of weight, width and slant (bold, condensed, italic,
+ etc), some fonts specify a list of ``variation axes'', each of
+ which determines one delta to apply to each point in every
+ glyph.
+
+ This optional information is specified in the `fvar' (font
+ variation), `gvar' (glyph variation) and `cvar' (CVT variation)
+ tables in a font file. */
+
+/* Read an fvar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the fvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_fvar_table *
+sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_fvar_table *fvar;
+ ssize_t rc;
+ size_t min_bytes, ps_size, non_ps_size, temp, pad;
+ off_t offset;
+ int i, j;
+ char *buffer;
+ sfnt_fixed *coords;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_fvar_table,
+ instance_size, uint16_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (directory->length < min_bytes)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the fvar table header. */
+ fvar = xmalloc (sizeof *fvar);
+
+ /* Read the fvar table header. */
+ buffer = NULL;
+ rc = read (fd, fvar, min_bytes);
+ if (rc != min_bytes)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap16 (&fvar->major_version);
+ sfnt_swap16 (&fvar->minor_version);
+ sfnt_swap16 (&fvar->offset_to_data);
+ sfnt_swap16 (&fvar->count_size_pairs);
+ sfnt_swap16 (&fvar->axis_count);
+ sfnt_swap16 (&fvar->axis_size);
+ sfnt_swap16 (&fvar->instance_count);
+ sfnt_swap16 (&fvar->instance_size);
+
+ /* major_version should be 1, and minor_version 0. */
+
+ if (fvar->major_version != 1 || fvar->minor_version)
+ goto bail;
+
+ /* count_size_pairs should be more than 2. */
+
+ if (fvar->count_size_pairs < 2)
+ goto bail;
+
+ /* Don't try to read tables where the axis format differs. */
+
+ if (fvar->axis_size != 20)
+ goto bail;
+
+ /* The instance size must either be 2 * sizeof (uint16_t) +
+ axisCount * sizeof (sfnt_fixed), meaning there is no PostScript
+ name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof
+ (sfnt_fixed), meaning there is. */
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+ &temp)
+ || INT_ADD_WRAPV (2 * sizeof (uint16_t), temp, &non_ps_size))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+ &temp)
+ || INT_ADD_WRAPV (3 * sizeof (uint16_t), temp, &ps_size))
+ goto bail;
+
+ if (fvar->instance_size != non_ps_size
+ && fvar->instance_size != ps_size)
+ goto bail;
+
+ /* Now compute the offset of the axis data from the start of the
+ font file. */
+
+ if (INT_ADD_WRAPV (fvar->offset_to_data, directory->offset,
+ &offset))
+ goto bail;
+
+ /* Seek there. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ min_bytes = sizeof *fvar;
+
+ /* Now, read each axis and instance. Calculate how much extra data
+ needs to be allocated for the axes and instances: this is
+ fvar->axis_count * sizeof (struct sfnt_variation_axis), some
+ padding, and finally fvar->instance_count * sizeof (struct
+ sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count *
+ fvar->axis_count. */
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof *fvar->axis,
+ &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ pad = alignof (struct sfnt_variation_axis);
+ pad -= min_bytes & (pad - 1);
+
+ if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+ sizeof *fvar->instance,
+ &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+ sizeof *fvar->instance->coords,
+ &temp)
+ || INT_MULTIPLY_WRAPV (temp, fvar->axis_count, &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ /* Reallocate fvar. */
+ fvar = xrealloc (fvar, min_bytes);
+
+ /* Fill in offsets. */
+ fvar->axis = (struct sfnt_variation_axis *) (fvar + 1);
+ fvar->instance
+ = (struct sfnt_instance *) (((char *) (fvar->axis
+ + fvar->axis_count))
+ + pad);
+
+ /* Read axes. */
+
+ if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table,
+ instance_size, uint16_t)
+ < sizeof *fvar->axis * fvar->axis_count)
+ goto bail;
+
+ rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count);
+ if (rc != sizeof *fvar->axis * fvar->axis_count)
+ goto bail;
+
+ /* Swap each axis. */
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ {
+ sfnt_swap32 (&fvar->axis[i].axis_tag);
+ sfnt_swap32 (&fvar->axis[i].min_value);
+ sfnt_swap32 (&fvar->axis[i].default_value);
+ sfnt_swap32 (&fvar->axis[i].max_value);
+ sfnt_swap16 (&fvar->axis[i].flags);
+ sfnt_swap16 (&fvar->axis[i].name_id);
+ }
+
+ /* Read each instance. */
+
+ if (fvar->instance_size < 1024 * 16)
+ buffer = alloca (fvar->instance_size);
+ else
+ buffer = xmalloc (fvar->instance_size);
+
+ coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count);
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ rc = read (fd, buffer, fvar->instance_size);
+ if (rc != fvar->instance_size)
+ goto bail;
+
+ /* Fill in various fields. */
+
+ fvar->instance[i].name_id = *((uint16_t *) buffer);
+ fvar->instance[i].flags = *((uint16_t *) buffer + 1);
+ fvar->instance[i].ps_name_id = 0;
+
+ sfnt_swap16 (&fvar->instance[i].name_id);
+ sfnt_swap16 (&fvar->instance[i].flags);
+
+ /* Read coordinates. */
+
+ fvar->instance[i].coords = coords;
+ coords += fvar->axis_count;
+
+ memcpy (fvar->instance[i].coords, buffer + 4,
+ sizeof *fvar->instance[i].coords * fvar->axis_count);
+
+ /* Swap coordinates. */
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ sfnt_swap32 (&fvar->instance[i].coords[j]);
+
+ /* Read the PostScript name ID if necessary. If not, set it to
+ nil. */
+
+ if (fvar->instance_size == ps_size)
+ {
+ fvar->instance[i].ps_name_id
+ = *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords
+ * fvar->axis_count));
+ sfnt_swap16 (&fvar->instance[i].ps_name_id);
+ }
+ }
+
+ /* Free the temporary buffer. */
+ if (buffer && fvar->instance_size >= 1024 * 16)
+ xfree (buffer);
+
+ /* Return the fvar table. */
+ return fvar;
+
+ bail:
+ if (buffer && fvar->instance_size >= 1024 * 16)
+ xfree (buffer);
+
+ xfree (fvar);
+ return NULL;
+}
+
+
+
+/* Read a gvar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the gvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_gvar_table *
+sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_gvar_table *gvar;
+ ssize_t rc;
+ size_t min_bytes, off_size, coordinate_size, data_size;
+ int i;
+ off_t offset;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_gvar_table,
+ offset_to_data, uint32_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (directory->length < min_bytes)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the gvar table header. */
+ gvar = xmalloc (sizeof *gvar);
+
+ /* Read the gvar table header. */
+ rc = read (fd, gvar, min_bytes);
+ if (rc != min_bytes)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap16 (&gvar->version);
+ sfnt_swap16 (&gvar->reserved);
+ sfnt_swap16 (&gvar->axis_count);
+ sfnt_swap16 (&gvar->shared_coord_count);
+ sfnt_swap32 (&gvar->offset_to_coord);
+ sfnt_swap16 (&gvar->glyph_count);
+ sfnt_swap16 (&gvar->flags);
+ sfnt_swap32 (&gvar->offset_to_data);
+
+ if (gvar->version != 1)
+ goto bail;
+
+ if (gvar->offset_to_data > directory->length)
+ goto bail;
+
+ /* Figure out the size required for the offset array. Note that
+ there is one extra offset at the end of the array to mark the
+ size of the last glyph. */
+
+ if (gvar->flags & 1)
+ /* Offsets are long words. */
+ off_size = sizeof (uint32_t) * (gvar->glyph_count + 1);
+ else
+ /* Offsets are words. */
+ off_size = sizeof (uint16_t) * (gvar->glyph_count + 1);
+
+ /* Now figure out the size of the shared coordinates. */
+ coordinate_size = (gvar->shared_coord_count * gvar->axis_count
+ * sizeof (uint16_t));
+
+ /* And the size of the glyph variation data. */
+ data_size = directory->length - gvar->offset_to_data;
+
+ /* Wraparound. */
+ if (data_size > directory->length)
+ goto bail;
+
+ /* Figure out how big gvar needs to be. */
+ if (INT_ADD_WRAPV (sizeof *gvar, coordinate_size, &min_bytes)
+ || INT_ADD_WRAPV (min_bytes, off_size, &min_bytes)
+ || INT_ADD_WRAPV (min_bytes, data_size, &min_bytes))
+ goto bail;
+
+ /* Now allocate enough for all of this extra data. */
+ gvar = xrealloc (gvar, min_bytes);
+
+ /* Start reading offsets. */
+
+ if (!(gvar->flags & 1))
+ {
+ gvar->u.offset_word = (uint16_t *) (gvar + 1);
+ rc = read (fd, gvar->u.offset_word, off_size);
+ if (rc != off_size)
+ goto bail;
+
+ for (i = 0; i <= gvar->glyph_count; ++i)
+ sfnt_swap16 (&gvar->u.offset_word[i]);
+ }
+ else
+ {
+ gvar->u.offset_long = (uint32_t *) (gvar + 1);
+ rc = read (fd, gvar->u.offset_long, off_size);
+ if (rc != off_size)
+ goto bail;
+
+ for (i = 0; i <= gvar->glyph_count; ++i)
+ sfnt_swap32 (&gvar->u.offset_long[i]);
+ }
+
+ /* Start reading shared coordinates. */
+
+ gvar->global_coords = ((sfnt_f2dot14 *) ((char *) gvar + off_size));
+
+ if (gvar->shared_coord_count)
+ {
+ if (INT_ADD_WRAPV (gvar->offset_to_coord, directory->offset,
+ &offset))
+ goto bail;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ if (read (fd, gvar->global_coords, coordinate_size)
+ != coordinate_size)
+ goto bail;
+
+ for (i = 0; i <= coordinate_size / sizeof *gvar->global_coords; ++i)
+ sfnt_swap16 (&gvar->global_coords[i]);
+ }
+
+ /* Finally, read the rest of the glyph variation data. */
+ gvar->data_size = data_size;
+ gvar->glyph_variation_data
+ = (unsigned char *) (gvar->global_coords
+ + (coordinate_size
+ / sizeof *gvar->global_coords));
+
+ if (gvar->data_size)
+ {
+ if (INT_ADD_WRAPV (gvar->offset_to_data, directory->offset,
+ &offset))
+ goto bail;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ if (read (fd, gvar->glyph_variation_data,
+ gvar->data_size) != gvar->data_size)
+ goto bail;
+ }
+
+ /* Return the read gvar table. */
+ return gvar;
+
+ bail:
+ xfree (gvar);
+ return NULL;
+}
+
+
+
+/* Read an avar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the avar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_avar_table *
+sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_avar_table *avar;
+ ssize_t rc;
+ size_t min_size, size, i, k, j;
+ uint16_t *buffer;
+ struct sfnt_short_frac_correspondence *correspondences;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t);
+
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the avar table header. */
+ avar = xmalloc (sizeof *avar);
+
+ /* Read the avar table header. */
+ rc = read (fd, avar, min_size);
+ if (rc != min_size)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap32 (&avar->version);
+ sfnt_swap32 (&avar->axis_count);
+
+ if (avar->version != 0x00010000)
+ goto bail;
+
+ if (avar->axis_count < 0)
+ goto bail;
+
+ /* Allocate a buffer that holds the rest of the data. */
+ size = directory->length - min_size;
+ buffer = xmalloc (size);
+ rc = read (fd, buffer, size);
+ if (rc != size)
+ goto bail1;
+
+ /* Swap each word. */
+ for (i = 0; i < size / sizeof *buffer; ++i)
+ sfnt_swap16 (&buffer[i]);
+
+ /* Now, determine how big the resulting data needs to be. Each
+ struct has a pointer field, and that should be its alignment. */
+
+ k = 0;
+ min_size = sizeof *avar;
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ /* Check that k remains within bounds. */
+ if (k >= size / sizeof *buffer)
+ goto bail1;
+
+ /* Now add one struct sfnt_short_frac_segment for each axis and
+ each of its correspondences. */
+ if (INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_segment),
+ min_size, &min_size)
+ || INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_correspondence)
+ * buffer[k], min_size, &min_size))
+ goto bail1;
+
+ /* Verify that words from here to buffer[1 + buffer[k] * 2], the
+ next pairCount field, are within bounds. */
+ j = k + 1 + buffer[k] * 2;
+ if (j > size / sizeof *buffer)
+ goto bail1;
+
+ /* Move to the next pairCount field. */
+ k = j;
+ }
+
+ /* Resize avar to min_size and start filling in various
+ pointers. */
+ avar = xrealloc (avar, min_size);
+ avar->segments = (struct sfnt_short_frac_segment *) (avar + 1);
+ correspondences
+ = ((struct sfnt_short_frac_correspondence *) (avar->segments
+ + avar->axis_count));
+
+ k = 0;
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ avar->segments[i].pair_count = buffer[k++];
+ avar->segments[i].correspondence = correspondences;
+
+ for (j = 0; j < avar->segments[i].pair_count; ++j)
+ {
+ correspondences->from_coord = buffer[k++];
+ correspondences->to_coord = buffer[k++];
+ correspondences++;
+ }
+ }
+
+ /* Return the read avar table. Free buffer. */
+ xfree (buffer);
+ return avar;
+
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (avar);
+ return NULL;
+}
+
+
+
+/* Read a sequence of packed points starting from DATA. Return the
+ number of points read in *NPOINTS_RETURN and the array of unpacked
+ points, or NULL upon failure.
+
+ If non-NULL, set LOCATION to DATA plus the number of bytes read
+ upon success.
+
+ Return (uint16_t *) -1 if there are no points at all.
+ In this case, deltas will apply to all points in the glyph,
+ and *NPOINTS_RETURN will be UINT16_MAX.
+
+ END is one byte past the last byte in DATA. */
+
+static uint16_t *
+sfnt_read_packed_points (unsigned char *restrict data,
+ uint16_t *npoints_return,
+ unsigned char *restrict end,
+ unsigned char *restrict *location)
+{
+ int npoints;
+ uint16_t *points;
+ int i, first, control;
+
+ points = NULL;
+ npoints = 0;
+
+ if (data >= end)
+ return NULL;
+
+ /* Load the control byte. */
+ control = *data++;
+
+ if (!control)
+ {
+ *npoints_return = UINT16_MAX;
+ *location = data;
+ return (uint16_t *) -1;
+ }
+
+ /* Now figure out the number of points within. */
+
+ if (control & 0x80)
+ {
+ npoints = control & 0x7f;
+ npoints <<= 8;
+
+ if (data >= end)
+ return NULL;
+
+ npoints |= *data++;
+ }
+ else
+ npoints = control;
+
+ /* Start reading points. */
+ first = 0;
+ i = 0;
+ points = xmalloc (sizeof *points * npoints);
+
+ while (i < npoints)
+ {
+ if (data >= end)
+ goto bail;
+
+ control = *data++;
+
+ if (control & 0x80)
+ {
+ /* Next control & 0x7f words are points. */
+
+ control &= 0x7f;
+
+ while (control != -1 && i < npoints)
+ {
+ if (data >= end || data + 1 >= end)
+ goto bail;
+
+ first += *data++ << 8u;
+ first += *data++;
+ points[i] = first;
+ control -= 1, ++i;
+ }
+ }
+ else
+ {
+ /* Next control bytes are points. */
+
+ while (control != -1 && i < npoints)
+ {
+ if (data >= end)
+ goto bail;
+
+ first += *data++;
+ points[i] = first;
+ control -= 1, ++i;
+ }
+ }
+ }
+
+ /* Return the points read. */
+ *npoints_return = npoints;
+ *location = data;
+ return points;
+
+ bail:
+ xfree (points);
+ return NULL;
+}
+
+/* Read and return N packed deltas from DATA. Set *DATA_RETURN to
+ DATA plus the number of bytes read.
+
+ END is the end of the glyph variation data. Value is an array of N
+ deltas upon success, and NULL upon failure. */
+
+static sfnt_fword *
+sfnt_read_packed_deltas (unsigned char *restrict data,
+ unsigned char *restrict end,
+ int n,
+ unsigned char *restrict *data_return)
+{
+ sfnt_fword *deltas;
+ int i, count;
+ unsigned char control;
+ uint16_t value;
+
+ if (data >= end)
+ return NULL;
+
+ deltas = xmalloc (sizeof *deltas * n);
+ i = 0;
+
+ while (i < n)
+ {
+ if (data >= end)
+ goto fail;
+
+ control = *data++;
+ count = control & 0x3f;
+
+ while (count != -1 && i < n)
+ {
+ if (control & 0x80)
+ deltas[i++] = 0;
+ else if (control & 0x40)
+ {
+ if (data + 1 >= end)
+ goto fail;
+
+ value = *data++ << 8;
+ value |= *data++;
+ deltas[i++] = value;
+ }
+ else
+ {
+ if (data >= end)
+ goto fail;
+
+ deltas[i++] = (signed char) *data++;
+ }
+
+ --count;
+ }
+ }
+
+ *data_return = data;
+ return deltas;
+
+ fail:
+ xfree (deltas);
+ return NULL;
+}
+
+/* Read a cvar table from the given font FD. Use the table directory
+ specified in SUBTABLE, axis information provided in the fvar table
+ FVAR, and CVT information provided in the cvt table CVT.
+
+ Return the cvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_cvar_table *
+sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_fvar_table *fvar,
+ struct sfnt_cvt_table *cvt)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_cvar_table *cvar;
+ ssize_t rc;
+ size_t ntuples, size;
+ int i, j;
+ sfnt_f2dot14 *coords;
+ uint16_t *local, *points, npoints, data_size, min_size, index;
+ unsigned char *buffer, *data, *end, *tuple;
+ ptrdiff_t data_offset;
+ sfnt_fword *deltas;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset,
+ uint16_t);
+
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the cvar table header. */
+ cvar = xmalloc (sizeof *cvar);
+
+ /* Read the cvar table header. */
+ rc = read (fd, cvar, min_size);
+ if (rc != min_size)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap32 (&cvar->version);
+ sfnt_swap16 (&cvar->tuple_count);
+ sfnt_swap16 (&cvar->data_offset);
+
+ /* Read the rest of the table. */
+ size = directory->length - min_size;
+ buffer = xmalloc (size);
+ rc = read (fd, buffer, size);
+ if (rc != size)
+ goto bail;
+
+ /* Now figure out how large cvar must be by reading the tuples. */
+
+ ntuples = cvar->tuple_count & 0x0fff;
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
+ end = buffer + size;
+
+ if (data_offset < 0)
+ goto bail1;
+
+ /* See if there are shared points, and read them if there are. */
+
+ data = buffer + data_offset;
+ tuple = buffer;
+ points = NULL;
+
+ if (cvar->tuple_count & 0x8000)
+ {
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+ if (!points)
+ goto bail1;
+
+ /* Add npoints words to the size. */
+ size = npoints * sizeof *points;
+ }
+ else
+ size = 0;
+
+ while (ntuples--)
+ {
+ data = buffer + data_offset;
+
+ /* Read the tuple. */
+ if (tuple + 3 >= end)
+ goto bail2;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the size. */
+
+ if (tuple + fvar->axis_count * sizeof *coords - 1 >= end)
+ goto bail2;
+
+ tuple += sizeof *coords * fvar->axis_count;
+ if (INT_ADD_WRAPV (size, sizeof *coords * fvar->axis_count,
+ &size))
+ goto bail2;
+ }
+ else
+ /* This table is invalid, as cvar tables don't have global
+ coordinates. */
+ goto bail2;
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ tuple += fvar->axis_count * 4;
+ if (INT_ADD_WRAPV (size, fvar->axis_count * 4, &size))
+ goto bail2;
+ }
+
+ /* Add one point and one delta for each CVT element. */
+ if (INT_ADD_WRAPV (size, cvt->num_elements * 4, &size))
+ goto bail2;
+
+ /* Now add the size of the tuple. */
+ if (INT_ADD_WRAPV (size, sizeof *cvar->variation, &size))
+ goto bail2;
+ }
+
+ if (INT_ADD_WRAPV (sizeof *cvar, size, &size))
+ goto bail2;
+
+ /* Reallocate cvar. */
+ cvar = xrealloc (cvar, size);
+ ntuples = cvar->tuple_count & 0x0fff;
+ cvar->variation = (struct sfnt_tuple_variation *) (cvar + 1);
+ coords = (sfnt_f2dot14 *) (cvar->variation + ntuples);
+ tuple = buffer;
+
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
+
+ /* Start reading the tuples into cvar. */
+ for (i = 0; i < ntuples; ++i)
+ {
+ data = buffer + data_offset;
+
+ /* Read the tuple. */
+ if (tuple + 3 >= end)
+ goto bail2;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ cvar->variation[i].intermediate_start = NULL;
+ cvar->variation[i].intermediate_end = NULL;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate. */
+ cvar->variation[i].coordinates = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
+ else
+ goto bail2;
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ cvar->variation[i].intermediate_start = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+
+ cvar->variation[i].intermediate_end = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
+
+ /* Finally, read private ``point'' numbers. If this flag is not
+ set, use shared point numbers previously read.
+
+ Read at most CVT->num_elements points, as that is all the
+ storage allocated. */
+
+ if (index & 0x2000)
+ {
+ local = sfnt_read_packed_points (data, &cvar->variation[i].num_points,
+ end, &data);
+ if (!local)
+ goto bail2;
+
+ /* If points apply to all CVT indices, skip this part. */
+
+ if (cvar->variation[i].num_points != UINT16_MAX)
+ {
+ if (cvar->variation[i].num_points > cvt->num_elements)
+ cvar->variation[i].num_points = cvt->num_elements;
+
+ cvar->variation[i].points = (uint16_t *) coords;
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = local[j];
+ xfree (local);
+ }
+ else
+ cvar->variation[i].points = NULL;
+ }
+ else
+ {
+ /* Copy in the shared point numbers instead. */
+ cvar->variation[i].num_points = npoints;
+
+ if (npoints != UINT16_MAX)
+ {
+ if (cvar->variation[i].num_points > cvt->num_elements)
+ cvar->variation[i].num_points = cvt->num_elements;
+
+ cvar->variation[i].points = (uint16_t *) coords;
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = points[j];
+ }
+ else
+ cvar->variation[i].points = NULL;
+ }
+
+ /* And read packed deltas. If cvar->variation[i].num_points is
+ UINT16_MAX, then there is one delta for each CVT entry.
+ Otherwise, there are that many deltas. */
+
+ if (cvar->variation[i].num_points == UINT16_MAX)
+ {
+ deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements,
+ &data);
+
+ if (!deltas)
+ goto bail2;
+
+ cvar->variation[i].deltas = coords;
+
+ for (j = 0; j < cvt->num_elements; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ else
+ {
+ deltas = sfnt_read_packed_deltas (data, end,
+ cvar->variation[i].num_points,
+ &data);
+ if (!deltas)
+ goto bail2;
+
+ cvar->variation[i].deltas = coords;
+
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ }
+
+ /* Free data and return the read cvar table. */
+ if (points != (void *) -1)
+ xfree (points);
+ xfree (buffer);
+ return cvar;
+
+ bail2:
+ if (points != (void *) -1)
+ xfree (points);
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (cvar);
+ return NULL;
+}
+
+
+
+/* Initialize the specified BLEND with the given FVAR and GVAR tables.
+ If non-NULL, adjust normalized coordinates using the axis variation
+ table AVAR; similarly, adjust interpreter CVT values using CVAR, if
+ specified. */
+
+TEST_STATIC void
+sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar,
+ struct sfnt_gvar_table *gvar, struct sfnt_avar_table *avar,
+ struct sfnt_cvar_table *cvar)
+{
+ size_t size;
+
+ blend->fvar = fvar;
+ blend->gvar = gvar;
+ blend->avar = avar;
+ blend->cvar = cvar;
+
+ /* Allocate a single array to hold both coords and norm_coords. */
+ size = (fvar->axis_count * sizeof *blend->coords * 2);
+ blend->coords = xmalloc (size);
+ blend->norm_coords = blend->coords + fvar->axis_count;
+}
+
+/* Free what was initialized in the specified BLEND. */
+
+TEST_STATIC void
+sfnt_free_blend (struct sfnt_blend *blend)
+{
+ xfree (blend->coords);
+}
+
+/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and
+ place the result in BLEND->norm_coords. */
+
+TEST_STATIC void
+sfnt_normalize_blend (struct sfnt_blend *blend)
+{
+ struct sfnt_variation_axis *axis;
+ int i, j;
+ sfnt_fixed from, coord, j0, j1, j2;
+ sfnt_fixed from_last, coord_last;
+ struct sfnt_short_frac_segment *segment;
+
+ /* For each axis... */
+ for (i = 0; i < blend->fvar->axis_count; ++i)
+ {
+ /* Normalize based on [min, default, max], into [-1, 0, 1]. */
+ axis = &blend->fvar->axis[i];
+
+ /* Load the current design coordinate. */
+ coord = blend->coords[i];
+
+ /* Keep it within bounds. */
+
+ if (coord > axis->max_value)
+ coord = axis->max_value;
+ else if (coord < axis->min_value)
+ coord = axis->min_value;
+
+ if (coord > axis->default_value)
+ {
+ /* Avoid division by 0. */
+ if (axis->max_value != axis->default_value)
+ blend->norm_coords[i]
+ = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+ sfnt_sub (axis->max_value,
+ axis->default_value));
+ else
+ blend->norm_coords[i] = 0;
+ }
+ else if (coord < axis->default_value)
+ {
+ if (axis->default_value != axis->min_value)
+ blend->norm_coords[i]
+ = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+ sfnt_sub (axis->default_value,
+ axis->min_value));
+ else
+ blend->norm_coords[i] = 0;
+ }
+ else
+ blend->norm_coords[i] = 0;
+ }
+
+ /* Now, apply axis variations, but only if the avar table has the
+ right number of axes. */
+
+ if (blend->avar && (blend->fvar->axis_count
+ == blend->avar->axis_count))
+ {
+ for (i = 0; i < blend->fvar->axis_count; ++i)
+ {
+ segment = &blend->avar->segments[i];
+
+ /* Search for a correspondence record above the normalized
+ coordinate of this axis. */
+
+ for (j = 1; j < segment->pair_count; ++j)
+ {
+ from = segment->correspondence[j].from_coord * 4;
+ coord = segment->correspondence[j].to_coord * 4;
+
+ if (blend->norm_coords[i] < from)
+ {
+ from_last
+ = segment->correspondence[j - 1].from_coord * 4;
+ coord_last
+ = segment->correspondence[j - 1].to_coord * 4;
+
+ j0 = blend->norm_coords[i] - from_last;
+ j1 = coord - coord_last;
+ j2 = from - from_last;
+
+ blend->norm_coords[i]
+ = (sfnt_multiply_divide_signed (j0, j1, j2) + coord_last);
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+
+struct sfnt_gvar_glyph_header
+{
+ /* A packed field. The high 4 bits are flags and the low 12 bits are
+ the number of tuples for this glyph. The number of tuples can be
+ any number between 1 and 4095. */
+ uint16_t tuple_count;
+
+ /* Offset from the start of the GlyphVariationData table to the
+ serialized data. */
+ uint16_t data_offset;
+};
+
+/* Given a BLEND containing normalized coordinates, an array of
+ BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P,
+ a range of tuple coordinates from INTERMEDIATE_START to
+ INTERMEDIATE_END, return the scaling factor to apply to deltas for
+ each corresponding point. */
+
+static sfnt_fixed
+sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
+ sfnt_f2dot14 *coords,
+ sfnt_f2dot14 *intermediate_start,
+ sfnt_f2dot14 *intermediate_end)
+{
+ int i;
+ sfnt_fixed coord, start, end;
+ sfnt_fixed scale;
+
+ /* scale is initially 1.0. */
+ scale = 0200000;
+
+ for (i = 0; i < blend->gvar->axis_count; ++i)
+ {
+ /* Load values for this axis, scaled up to sfnt_fixed. */
+ coord = coords[i] * 4;
+
+ if (intermediate_p)
+ {
+ start = intermediate_start[i] * 4;
+ end = intermediate_start[i] * 4;
+ }
+
+ /* Ignore tuples that can be skipped. */
+
+ if (!coord)
+ continue;
+
+ /* If the coordinate is set to 0, then deltas should not be
+ applied. Return 0. */
+
+ if (!blend->norm_coords[i])
+ return 0;
+
+ /* If no scaling need take place, continue. */
+
+ if (blend->norm_coords[i] == coord)
+ continue;
+
+ if (!intermediate_p)
+ {
+ /* Not an intermediate tuple; if coord is less than 0 and
+ blend->norm_coords[i] < coord, or coord is more than 0
+ and blend->norm_coords[i] > coord, then it doesn't fit,
+ so return. */
+
+ if (blend->norm_coords[i] < MIN (0, coord)
+ || blend->norm_coords[i] > MAX (0, coord))
+ return 0;
+
+ scale = sfnt_multiply_divide_signed (scale,
+ blend->norm_coords[i],
+ coord);
+ }
+ else
+ {
+ /* Otherwise, renormalize between start and end. */
+
+ if (blend->norm_coords[i] < start
+ || blend->norm_coords[i] > end)
+ return 0;
+
+ if (blend->norm_coords[i] < coord)
+ scale = sfnt_multiply_divide (scale,
+ blend->norm_coords[i] - start,
+ coord - start);
+ else
+ scale = sfnt_multiply_divide (scale,
+ end - blend->norm_coords[i],
+ end - coord);
+ }
+ }
+
+ return scale;
+}
+
+/* Infer point positions for points that have been partially moved
+ within the contour in GLYPH denoted by START and END. */
+
+static void
+sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
+ size_t end, bool *touched, sfnt_fword *x,
+ sfnt_fword *y)
+{
+ size_t i, pair_start, pair_end, pair_first, j;
+ sfnt_fword min_pos, max_pos, position;
+ sfnt_fixed ratio, delta;
+
+ pair_start = pair_first = -1;
+
+ /* Look for pairs of touched points. */
+
+ for (i = start; i <= end; ++i)
+ {
+ if (!touched[i])
+ continue;
+
+ if (pair_start == -1)
+ {
+ pair_first = i;
+ goto next;
+ }
+
+ pair_end = i;
+
+ /* pair_start to pair_end are now a pair of points, where points
+ in between should be interpolated. */
+
+ for (j = pair_start + 1; j < pair_end; ++j)
+ {
+ /* Consider the X axis. Set min_pos and max_pos to the
+ smallest and greatest values along that axis. */
+ min_pos = MIN (x[pair_start], x[pair_end]);
+ max_pos = MAX (x[pair_start], x[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ continue;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->x_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (x[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->x_coordinates[j] = x[j] + delta;
+ }
+
+ /* Now, consider the Y axis. */
+ min_pos = MIN (y[pair_start], y[pair_end]);
+ max_pos = MAX (y[pair_start], y[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ continue;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->y_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (y[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->y_coordinates[j] = y[j] + delta;
+ }
+ }
+
+ next:
+ pair_start = i;
+ }
+
+ /* If pair_start is set, then lerp points between it and
+ pair_first. */
+
+ if (pair_start != (size_t) -1)
+ {
+ j = pair_start + 1;
+
+ if (j > end)
+ j = start;
+
+ pair_end = pair_first;
+
+ while (j != pair_first)
+ {
+ /* Consider the X axis. Set min_pos and max_pos to the
+ smallest and greatest values along that axis. */
+ min_pos = MIN (x[pair_start], x[pair_end]);
+ max_pos = MAX (x[pair_start], x[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ goto next_1;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->x_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (x[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->x_coordinates[j] = x[j] + delta;
+ }
+
+ /* Now, consider the Y axis. */
+ min_pos = MIN (y[pair_start], y[pair_end]);
+ max_pos = MAX (y[pair_start], y[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ goto next_1;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->y_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (y[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->y_coordinates[j] = y[j] + delta;
+ }
+
+ next_1:
+ j++;
+ if (j > end)
+ j = start;
+ }
+ }
+}
+
+/* Infer point positions for contours that have been partially moved
+ by variation. For each contour in GLYPH, find pairs of points
+ which have had deltas applied. For each such pair, interpolate
+ points between the first point in the pair and the second by
+ considering each point along every one of the two axes (X and Y)
+ like so:
+
+ - For each point that lies between the first point and the last
+ on the axis currently being considered, interpolate its
+ position in that axis so that the ratio between the first
+ point and the last in the original outline still holds.
+
+ - For each point that lies to the left or top of the first point
+ on the axis being considered, use the delta of the first point.
+
+ - And finally, for each point that lies to the right or bottom of
+ the last point on that axis, use the delta of the last
+ point.
+
+ X and Y contain the original positions positions of each point.
+ TOUCHED contains whether or not each point has been changed by
+ an explicitly specified delta.
+
+ Apply the inferred deltas back to GLYPH. */
+
+static void
+sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched,
+ sfnt_fword *x, sfnt_fword *y)
+{
+ size_t i;
+ int point, first, end;
+
+ point = 0;
+ for (i = 0; i < glyph->number_of_contours; ++i)
+ {
+ first = point;
+ end = glyph->simple->end_pts_of_contours[i];
+
+ /* Return if the glyph is invalid. */
+
+ if (first >= glyph->simple->number_of_points
+ || end >= glyph->simple->number_of_points
+ || first > end)
+ return;
+
+ sfnt_infer_deltas_1 (glyph, first, end, touched, x, y);
+ point = end + 1;
+ }
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+ BLEND's gvar table. Apply the offsets to each point in the
+ specified simple GLYPH, based on the specified BLEND.
+
+ Value is 0 upon success, else 1.
+
+ The glyph variation data consists of a number of elements, each of
+ which has its own associated point numbers and deltas, and a list
+ of one or two coordinates for each axis. Each such list is
+ referred to as a ``tuple''.
+
+ The deltas, one for each point, are multipled by the normalized
+ value of each axis and applied to those points for each tuple that
+ is found to be applicable.
+
+ Each element of the glyph variation data is applicable to an axis
+ if its list of coordinates:
+
+ - contains one element for each axis, and its axis has a value
+ between 0 and that element.
+
+ - contains two elements for each axis, and its axis has a value
+ between the first element and the second.
+
+ Return the deltas that would normally be applied to the two phantom
+ points describing horizontal bounds in *DISTORTION. Do not
+ transform the outline to reflect adjustments to the origin
+ point. */
+
+TEST_STATIC int
+sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+ struct sfnt_glyph *glyph,
+ struct sfnt_metrics_distortion *distortion)
+{
+ uint32_t offset;
+ struct sfnt_gvar_glyph_header header;
+ uint16_t *points, npoints;
+ int i, ntuples, j, point_count;
+ unsigned char *tuple, *end, *data;
+ uint16_t data_size, index, *glyph_points;
+ sfnt_f2dot14 *restrict coords;
+ sfnt_f2dot14 *restrict intermediate_start;
+ sfnt_f2dot14 *restrict intermediate_end;
+ sfnt_fword *restrict dx, *restrict dy, fword;
+ struct sfnt_gvar_table *gvar;
+ uint16_t *local_points, n_local_points;
+ sfnt_fixed scale;
+ ptrdiff_t data_offset;
+ bool *touched;
+ sfnt_fword *restrict original_x, *restrict original_y;
+
+ gvar = blend->gvar;
+
+ if (gvar->axis_count != blend->fvar->axis_count)
+ return 1;
+
+ if (gvar->glyph_count <= id)
+ return 1;
+
+ if (gvar->flags & 1)
+ offset = gvar->u.offset_long[id];
+ else
+ offset = gvar->u.offset_word[id] * 2u;
+
+ if (offset >= gvar->data_size)
+ return 1;
+
+ end = gvar->glyph_variation_data + gvar->data_size;
+
+ /* Start reading the header. */
+
+ if (offset + sizeof header > gvar->data_size)
+ return 1;
+
+ /* Clear the distortion. */
+ distortion->origin = 0;
+ distortion->advance = 0;
+
+ memcpy (&header, gvar->glyph_variation_data + offset,
+ sizeof header);
+
+ /* Swap the header. */
+ sfnt_swap16 (&header.tuple_count);
+ sfnt_swap16 (&header.data_offset);
+
+ /* Prepare to read each tuple. */
+ ntuples = header.tuple_count & 0x0fff;
+
+ /* Initialize the data offset. This is incremented with each tuple
+ read. */
+ data_offset = header.data_offset;
+
+ /* If gvar->flags & tuples_share_point_numbers, read the shared
+ point numbers. */
+
+ npoints = 0;
+
+ if (header.tuple_count & 0x8000)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+
+ if (!points)
+ return 1;
+
+ /* Shared point numbers are part of the data after the tuple
+ array. Thus, increment data_offset by tuple - data. `tuple'
+ here holds no relation to a pointer to the current part of
+ the tuple array that is being read later on. */
+ data_offset += tuple - data;
+ }
+ else
+ points = NULL;
+
+ /* Start reading each tuple. */
+ tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+ else
+ coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+ intermediate_start = coords + gvar->axis_count;
+ intermediate_end = coords + gvar->axis_count;
+
+ /* Allocate arrays of booleans and fwords to keep track of which
+ points have been touched. */
+ touched = NULL;
+ original_x = NULL;
+ original_y = NULL;
+
+ while (ntuples--)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+
+ if (tuple + 3 >= end)
+ goto fail1;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the tuple. */
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&coords[j], tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (&coords[j]);
+ }
+ }
+ else if ((index & 0xfff) > gvar->shared_coord_count)
+ /* index exceeds the number of shared tuples present. */
+ goto fail1;
+ else
+ /* index points into gvar->axis_count coordinates making up
+ the tuple. */
+ memcpy (coords, (gvar->global_coords
+ + ((index & 0xfff) * gvar->axis_count)),
+ gvar->axis_count * sizeof *coords);
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_start[j], tuple,
+ sizeof *intermediate_start);
+ tuple += sizeof *intermediate_start;
+ sfnt_swap16 (&intermediate_start[j]);
+ }
+
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_end[j], tuple,
+ sizeof *intermediate_end);
+ tuple += sizeof *intermediate_end;
+ sfnt_swap16 (&intermediate_end[j]);
+ }
+ }
+
+ /* See whether or not the tuple applies to the current variation
+ configuration, and how much to scale them by. */
+
+ scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+ coords, intermediate_start,
+ intermediate_end);
+
+ if (!scale)
+ continue;
+
+ local_points = NULL;
+
+ /* Finally, read private point numbers.
+ Set local_points to those numbers; it will be freed
+ once the loop ends. */
+
+ if (index & 0x2000)
+ {
+ local_points = sfnt_read_packed_points (data, &n_local_points,
+ end, &data);
+ if (!local_points)
+ goto fail1;
+
+ point_count = n_local_points;
+ glyph_points = local_points;
+ }
+ else
+ {
+ /* If there are no private point numbers, use global
+ points. */
+ point_count = npoints;
+ glyph_points = points;
+ }
+
+ /* Now, read packed deltas. */
+
+ dx = NULL;
+ dy = NULL;
+
+ switch (point_count)
+ {
+ case UINT16_MAX:
+ /* Deltas are provided for all points in the glyph.
+ No glyph should have more than 65535 points. */
+
+ /* Add 4 phantom points to each end. */
+ dx = sfnt_read_packed_deltas (data, end,
+ glyph->simple->number_of_points + 4,
+ &data);
+ dy = sfnt_read_packed_deltas (data, end,
+ glyph->simple->number_of_points + 4,
+ &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Apply each delta to the simple glyph. */
+
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ glyph->simple->x_coordinates[i] += fword;
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+ glyph->simple->y_coordinates[i] += fword;
+ }
+
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ break;
+
+ default:
+ dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+ dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Deltas are only applied for each point number read. */
+
+ if (!original_x)
+ {
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ touched = xmalloc (sizeof *touched
+ * glyph->simple->number_of_points);
+ else
+ touched = alloca (sizeof *touched
+ * glyph->simple->number_of_points);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ original_x = xmalloc (sizeof *original_x * 2
+ * glyph->simple->number_of_points);
+ else
+ original_x = alloca (sizeof *original_x * 2
+ * glyph->simple->number_of_points);
+
+ original_y = original_x + glyph->simple->number_of_points;
+ memcpy (original_x, glyph->simple->x_coordinates,
+ (sizeof *original_x
+ * glyph->simple->number_of_points));
+ memcpy (original_y, glyph->simple->y_coordinates,
+ (sizeof *original_y
+ * glyph->simple->number_of_points));
+ }
+
+ memset (touched, 0, (sizeof *touched
+ * glyph->simple->number_of_points));
+
+ for (i = 0; i < point_count; ++i)
+ {
+ /* Apply deltas to phantom points. */
+
+ if (glyph_points[i] == glyph->simple->number_of_points)
+ {
+ distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ if (glyph_points[i] == glyph->simple->number_of_points + 1)
+ {
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ /* Make sure the point doesn't end up out of bounds. */
+ if (glyph_points[i] >= glyph->simple->number_of_points)
+ continue;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ glyph->simple->x_coordinates[glyph_points[i]] += fword;
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+ glyph->simple->y_coordinates[glyph_points[i]] += fword;
+ touched[glyph_points[i]] = true;
+ }
+
+ sfnt_infer_deltas (glyph, touched, original_x,
+ original_y);
+ break;
+ }
+
+ xfree (dx);
+ xfree (dy);
+
+ if (local_points != (uint16_t *) -1)
+ xfree (local_points);
+ }
+
+ /* Return success. */
+
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ /* Set the glyph metrics distortion as well. */
+ glyph->advance_distortion = distortion->advance;
+ glyph->origin_distortion = distortion->origin;
+
+ return 0;
+
+ fail3:
+ xfree (dx);
+ xfree (dy);
+ xfree (local_points);
+ fail1:
+
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ return 1;
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+ BLEND's gvar table. Apply the deltas specified within to each
+ component with offsets in the specified compound GLYPH, based on
+ the specified BLEND. Return distortions to phantom points in
+ *DISTORTION.
+
+ Value is 0 upon success, 1 otherwise. */
+
+TEST_STATIC int
+sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+ struct sfnt_glyph *glyph,
+ struct sfnt_metrics_distortion *distortion)
+{
+ uint32_t offset;
+ struct sfnt_gvar_glyph_header header;
+ uint16_t *points, npoints;
+ int i, ntuples, j, point_count;
+ unsigned char *tuple, *end, *data;
+ uint16_t data_size, index, *glyph_points;
+ sfnt_f2dot14 *restrict coords;
+ sfnt_f2dot14 *restrict intermediate_start;
+ sfnt_f2dot14 *restrict intermediate_end;
+ sfnt_fword *restrict dx, *restrict dy, fword, word;
+ struct sfnt_gvar_table *gvar;
+ uint16_t *local_points, n_local_points;
+ sfnt_fixed scale;
+ ptrdiff_t data_offset;
+ struct sfnt_compound_glyph_component *component;
+
+ gvar = blend->gvar;
+
+ if (gvar->axis_count != blend->fvar->axis_count)
+ return 1;
+
+ if (gvar->glyph_count <= id)
+ return 1;
+
+ if (gvar->flags & 1)
+ offset = gvar->u.offset_long[id];
+ else
+ offset = gvar->u.offset_word[id] * 2u;
+
+ if (offset >= gvar->data_size)
+ return 1;
+
+ end = gvar->glyph_variation_data + gvar->data_size;
+
+ /* Start reading the header. */
+
+ if (offset + sizeof header > gvar->data_size)
+ return 1;
+
+ /* Clear the distortion. */
+ distortion->origin = 0;
+ distortion->advance = 0;
+
+ memcpy (&header, gvar->glyph_variation_data + offset,
+ sizeof header);
+
+ /* Swap the header. */
+ sfnt_swap16 (&header.tuple_count);
+ sfnt_swap16 (&header.data_offset);
+
+ /* Prepare to read each tuple. */
+ ntuples = header.tuple_count & 0x0fff;
+
+ /* Initialize the data offset. This is incremented with each tuple
+ read. */
+ data_offset = header.data_offset;
+
+ /* If gvar->flags & tuples_share_point_numbers, read the shared
+ point numbers. */
+
+ npoints = 0;
+
+ if (header.tuple_count & 0x8000)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+
+ if (!points)
+ return 1;
+
+ /* Shared point numbers are part of the data after the tuple
+ array. Thus, increment data_offset by tuple - data. `tuple'
+ here holds no relation to a pointer to the current part of
+ the tuple array that is being read later on. */
+ data_offset += tuple - data;
+ }
+ else
+ points = NULL;
+
+ /* Start reading each tuple. */
+ tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+ else
+ coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+ intermediate_start = coords + gvar->axis_count;
+ intermediate_end = coords + gvar->axis_count;
+
+ while (ntuples--)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+
+ if (tuple + 3 >= end)
+ goto fail1;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the tuple. */
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&coords[j], tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (&coords[j]);
+ }
+ }
+ else if ((index & 0xfff) > gvar->shared_coord_count)
+ /* index exceeds the number of shared tuples present. */
+ goto fail1;
+ else
+ /* index points into gvar->axis_count coordinates making up
+ the tuple. */
+ memcpy (coords, (gvar->global_coords
+ + ((index & 0xfff) * gvar->axis_count)),
+ gvar->axis_count * sizeof *coords);
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_start[j], tuple,
+ sizeof *intermediate_start);
+ tuple += sizeof *intermediate_start;
+ sfnt_swap16 (&intermediate_start[j]);
+ }
+
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_end[j], tuple,
+ sizeof *intermediate_end);
+ tuple += sizeof *intermediate_end;
+ sfnt_swap16 (&intermediate_end[j]);
+ }
+ }
+
+ /* See whether or not the tuple applies to the current variation
+ configuration, and how much to scale them by. */
+
+ scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+ coords, intermediate_start,
+ intermediate_end);
+
+ if (!scale)
+ continue;
+
+ local_points = NULL;
+
+ /* Finally, read private point numbers.
+ Set local_points to those numbers; it will be freed
+ once the loop ends. */
+
+ if (index & 0x2000)
+ {
+ local_points = sfnt_read_packed_points (data, &n_local_points,
+ end, &data);
+ if (!local_points)
+ goto fail1;
+
+ point_count = n_local_points;
+ glyph_points = local_points;
+ }
+ else
+ {
+ /* If there are no private point numbers, use global
+ points. */
+ point_count = npoints;
+ glyph_points = points;
+ }
+
+ /* Now, read packed deltas. */
+
+ dx = NULL;
+ dy = NULL;
+
+ switch (point_count)
+ {
+ case UINT16_MAX:
+ /* Deltas are provided for all components in the glyph. */
+
+ /* Add 4 phantom points to each end. */
+ dx = sfnt_read_packed_deltas (data, end,
+ glyph->compound->num_components + 4,
+ &data);
+ dy = sfnt_read_packed_deltas (data, end,
+ glyph->compound->num_components + 4,
+ &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Apply each delta to the compound glyph. */
+
+ for (i = 0; i < glyph->compound->num_components; ++i)
+ {
+ component = &glyph->compound->components[i];
+
+ /* Check if the component uses deltas at all. */
+ if (!(component->flags & 02))
+ continue;
+
+ /* Vary the X offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument1.b;
+ else
+ word = component->argument1.d;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ component->argument1.d = word + fword;
+
+ /* Vary the Y offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument2.b;
+ else
+ word = component->argument2.d;
+
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+
+ /* Set the flag that says offsets are words. */
+ component->flags |= 01;
+ component->argument2.d = word + fword;
+ }
+
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ break;
+
+ default:
+ dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+ dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Deltas are only applied for each point number read. */
+
+ for (i = 0; i < point_count; ++i)
+ {
+ /* Apply deltas to phantom points. */
+
+ if (glyph_points[i] == glyph->compound->num_components)
+ {
+ distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ if (glyph_points[i] == glyph->compound->num_components + 1)
+ {
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ /* Make sure the point doesn't end up out of bounds. */
+ if (glyph_points[i] >= glyph->compound->num_components)
+ continue;
+
+ component = &glyph->compound->components[glyph_points[i]];
+
+ /* Check if the component uses deltas at all. */
+ if (!(component->flags & 02))
+ continue;
+
+ /* Vary the X offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument1.b;
+ else
+ word = component->argument1.d;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ component->argument1.d = word + fword;
+
+ /* Vary the Y offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument2.b;
+ else
+ word = component->argument2.d;
+
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+
+ /* Set the flag that says offsets are words. */
+ component->flags |= 01;
+ component->argument2.d = word + fword;
+ }
+
+ break;
+ }
+
+ xfree (dx);
+ xfree (dy);
+
+ if (local_points != (uint16_t *) -1)
+ xfree (local_points);
+ }
+
+ /* Return success. */
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ /* Set the glyph metrics distortion as well. */
+ glyph->advance_distortion = distortion->advance;
+ glyph->origin_distortion = distortion->origin;
+
+ return 0;
+
+ fail3:
+ xfree (dx);
+ xfree (dy);
+ xfree (local_points);
+ fail1:
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ return 1;
+}
+
+/* Vary the specified INTERPRETER's control value table using the
+ variations in BLEND's CVT variations table, then record the blend's
+ normalized coordinates and axis count in the interpreter.
+
+ The CVT table used to create INTERPRETER must be the same used
+ to read BLEND->cvar. If not, behavior is undefined. */
+
+TEST_STATIC void
+sfnt_vary_interpreter (struct sfnt_interpreter *interpreter,
+ struct sfnt_blend *blend)
+{
+ sfnt_fixed scale;
+ int i;
+ struct sfnt_tuple_variation *variation;
+ size_t ndeltas, j, index;
+ sfnt_f26dot6 delta;
+
+ /* Return if there's no cvar table. */
+ if (!blend->cvar)
+ return;
+
+ /* For each tuple in the cvar table... */
+ for (i = 0; i < (blend->cvar->tuple_count & 0x0fff); ++i)
+ {
+ /* See if the tuple applies. */
+ variation = &blend->cvar->variation[i];
+ scale = sfnt_compute_tuple_scale (blend,
+ variation->intermediate_start != NULL,
+ variation->coordinates,
+ variation->intermediate_start,
+ variation->intermediate_end);
+ if (!scale)
+ continue;
+
+ /* Figure out how many deltas there are. If variation->points,
+ there are num_points deltas. Otherwise, there are
+ interpreter->cvt->num_elements deltas. */
+
+ ndeltas = (variation->points
+ ? variation->num_points
+ : interpreter->cvt_size);
+
+ for (j = 0; j < ndeltas; ++j)
+ {
+ /* Figure out which CVT entry this applies to. */
+ index = variation->points ? variation->points[j] : j;
+
+ if (index > interpreter->cvt_size)
+ continue;
+
+ /* Multiply the delta by the interpreter scale factor and
+ then the tuple scale factor. */
+ delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64,
+ interpreter->scale);
+ delta = sfnt_mul_fixed_round (delta, scale);
+
+ /* Apply the delta to the control value table. */
+ interpreter->cvt[i] += delta;
+ }
+ }
+
+ interpreter->n_axis = blend->fvar->axis_count;
+ interpreter->norm_coords = blend->norm_coords;
+}
+
+
+
+#ifdef TEST
+
+struct sfnt_test_dcontext
+{
+ /* Context for sfnt_test_get_glyph. */
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_blend *blend;
+};
+
+/* Global context for test functions. Height of glyph. */
+static sfnt_fixed sfnt_test_max;
+
+static void
+sfnt_test_move_to (struct sfnt_point point, void *dcontext)
+{
+ printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+ sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_line_to (struct sfnt_point point, void *dcontext)
+{
+ printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+ sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_curve_to (struct sfnt_point control,
+ struct sfnt_point endpoint,
+ void *dcontext)
+{
+ printf ("curve_to: %g, %g - %g, %g\n",
+ sfnt_coerce_fixed (control.x),
+ sfnt_coerce_fixed (control.y),
+ sfnt_coerce_fixed (endpoint.x),
+ sfnt_coerce_fixed (endpoint.y));
+}
+
+static struct sfnt_glyph *
+sfnt_test_get_glyph (sfnt_glyph id, void *dcontext,
+ bool *need_free)
+{
+ struct sfnt_test_dcontext *tables;
+ struct sfnt_glyph *glyph;
+ struct sfnt_metrics_distortion distortion;
+
+ tables = dcontext;
+ *need_free = true;
+
+ glyph = sfnt_read_glyph (id, tables->glyf,
+ tables->loca_short,
+ tables->loca_long);
+
+ if (tables->blend && glyph)
+ {
+ if (glyph->simple)
+ sfnt_vary_simple_glyph (tables->blend, id, glyph,
+ &distortion);
+ else
+ sfnt_vary_compound_glyph (tables->blend, id, glyph,
+ &distortion);
+ }
+
+ return glyph;
+}
+
+static void
+sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+ sfnt_free_glyph (glyph);
+}
+
+static void
+sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y,
+ void *dcontext)
+{
+#if 1
+ printf ("/* span at %g */\n", sfnt_coerce_fixed (y));
+ for (; edge; edge = edge->next)
+ {
+ if (y >= edge->bottom && y < edge->top)
+ printf ("ctx.fillRect (%g, %g, 1, 1); "
+ "/* %g top: %g bot: %g stepx: %g winding: %d */\n",
+ sfnt_coerce_fixed (edge->x),
+ sfnt_coerce_fixed (sfnt_test_max - y),
+ sfnt_coerce_fixed (y),
+ sfnt_coerce_fixed (edge->top),
+ sfnt_coerce_fixed (edge->bottom),
+ sfnt_coerce_fixed (edge->step_x),
+ edge->winding);
+ else
+ printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32
+ " %"PRIi32" (winding: %d)\n",
+ sfnt_coerce_fixed (edge->top),
+ sfnt_coerce_fixed (edge->bottom),
+ edge->top, y, edge->winding);
+ }
+#elif 0
+ int winding;
+ short x, dx;
+
+ winding = 0;
+ x = 0;
+
+ for (; edge; edge = edge->next)
+ {
+ dx = (edge->x >> 16) - x;
+ x = edge->x >> 16;
+
+ for (; dx > 0; --dx)
+ putc (winding ? '.' : ' ', stdout);
+
+ winding = !winding;
+ }
+
+ putc ('\n', stdout);
+#elif 0
+ for (; edge; edge = edge->next)
+ printf ("%g-", sfnt_coerce_fixed (edge->x));
+ puts ("");
+#endif
+}
+
+static void
+sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+
+}
+
+/* The same debugger stuff is used here. */
+static void sfnt_setup_debugger (void);
+
+/* The debugger's X display. */
+static Display *display;
+
+/* The debugger window. */
+static Window window;
+
+/* The GC. */
+static GC point_gc, background_gc;
+
+static void
+sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+ static sfnt_fixed y;
+ size_t i;
+
+ for (i = 0; i < num_edges; ++i)
+ {
+ if (y >= edges[i].bottom && y < edges[i].top)
+ {
+ XDrawPoint (display, window, point_gc,
+ edges[i].x / 65536, 100 - (y / 65536));
+ printf ("sfnt_test_edges: %d %d\n",
+ edges[i].x / 65536, 100 - (y / 65536));
+ }
+ }
+
+ y += SFNT_POLY_STEP;
+
+ for (i = 0; i < num_edges; ++i)
+ sfnt_step_edge (&edges[i]);
+}
+
+static void
+sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+ XEvent event;
+
+ sfnt_setup_debugger ();
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ switch (event.type)
+ {
+ case KeyPress:
+ XDestroyWindow (display, window);
+ XCloseDisplay (display);
+ exit (0);
+ break;
+
+ case Expose:
+
+ while (true)
+ {
+ sfnt_test_edges (edges, num_edges);
+ XFlush (display);
+ usleep (50000);
+ }
+
+ break;
+ }
+ }
+}
+
+static void
+sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+ size_t i;
+
+ printf ("built %zu edges\n", num_edges);
+
+ for (i = 0; i < num_edges; ++i)
+ {
+ printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n"
+ "/* edge step_x: %g */\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (edges[i].top),
+ sfnt_coerce_fixed (edges[i].bottom),
+ edges[i].winding,
+ sfnt_coerce_fixed (edges[i].step_x));
+#ifdef TEST_VERTEX
+ printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (sfnt_test_max
+ - edges[i].y));
+#else
+ printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (sfnt_test_max
+ - edges[i].bottom));
+#endif
+ }
+
+ if (getenv ("SFNT_DEBUG_STEP"))
+ {
+ if (!fork ())
+ sfnt_debug_edges (edges, num_edges);
+ }
+
+ printf ("==end of edges==\n");
+
+ sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL);
+}
+
+static void
+sfnt_x_raster (struct sfnt_raster **rasters,
+ int *advances,
+ int nrasters,
+ struct sfnt_hhea_table *hhea,
+ sfnt_fixed scale)
+{
+ Display *display;
+ Window window;
+ Pixmap *pixmaps;
+ Picture *glyphs, drawable, solid;
+ int event_base, error_base;
+ int major, minor, *depths, count;
+ XRenderPictFormat *format, *glyph_format;
+ Visual *visual;
+ XImage image;
+ GC gc;
+ XGCValues gcvalues;
+ XEvent event;
+ XRenderColor white, black;
+ int i, ascent, origin, x, y;
+ Font font;
+
+ if (!nrasters)
+ exit (0);
+
+ display = XOpenDisplay (NULL);
+
+ if (!display)
+ exit (0);
+
+ if (!XRenderQueryExtension (display, &event_base, &error_base)
+ || !XRenderQueryVersion (display, &major, &minor))
+ exit (0);
+
+ if (major == 0 && minor < 10)
+ exit (0);
+
+ window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+ 0, 0, 100, 150, 0, 0,
+ WhitePixel (display,
+ DefaultScreen (display)));
+ XSelectInput (display, window, ExposureMask);
+ XMapWindow (display, window);
+
+ visual = DefaultVisual (display, DefaultScreen (display));
+ format = XRenderFindVisualFormat (display, visual);
+
+ if (!format)
+ exit (0);
+
+ glyph_format = XRenderFindStandardFormat (display, PictStandardA8);
+ depths = XListDepths (display, DefaultScreen (display), &count);
+
+ for (i = 0; i < count; ++i)
+ {
+ if (depths[i] == 8)
+ goto depth_found;
+ }
+
+ exit (0);
+
+ depth_found:
+
+ XFree (depths);
+ pixmaps = alloca (sizeof *pixmaps * nrasters);
+ glyphs = alloca (sizeof *glyphs * nrasters);
+ gc = None;
+
+ for (i = 0; i < nrasters; ++i)
+ {
+ pixmaps[i] = XCreatePixmap (display, DefaultRootWindow (display),
+ rasters[i]->width, rasters[i]->height, 8);
+ if (!gc)
+ gc = XCreateGC (display, pixmaps[i], 0, &gcvalues);
+
+ /* Upload the raster. */
+ image.width = rasters[i]->width;
+ image.height = rasters[i]->height;
+ image.xoffset = 0;
+ image.format = ZPixmap;
+ image.data = (char *) rasters[i]->cells;
+ image.byte_order = MSBFirst;
+ image.bitmap_unit = 8;
+ image.bitmap_bit_order = LSBFirst;
+ image.bitmap_pad = SFNT_POLY_ALIGNMENT * 8;
+ image.depth = 8;
+ image.bytes_per_line = rasters[i]->stride;
+ image.bits_per_pixel = 8;
+ image.red_mask = 0;
+ image.green_mask = 0;
+ image.blue_mask = 0;
+
+ if (!XInitImage (&image))
+ abort ();
+
+ XPutImage (display, pixmaps[i], gc, &image,
+ 0, 0, 0, 0, image.width, image.height);
+
+ glyphs[i] = XRenderCreatePicture (display, pixmaps[i],
+ glyph_format, 0, NULL);
+ }
+
+ XFreeGC (display, gc);
+
+ font = XLoadFont (display, "6x13");
+
+ if (!font)
+ exit (1);
+
+ gcvalues.font = font;
+ gcvalues.foreground = BlackPixel (display, DefaultScreen (display));
+ gc = XCreateGC (display, window, GCForeground | GCFont, &gcvalues);
+
+ drawable = XRenderCreatePicture (display, window, format,
+ 0, NULL);
+ memset (&black, 0, sizeof black);
+ black.alpha = 65535;
+
+ solid = XRenderCreateSolidFill (display, &black);
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ if (event.type == Expose)
+ {
+ white.red = 65535;
+ white.green = 65535;
+ white.blue = 65535;
+ white.alpha = 65535;
+
+ /* Clear the background. */
+ XRenderFillRectangle (display, PictOpSrc, drawable,
+ &white, 0, 0, 65535, 65535);
+
+ /* Compute ascent line. */
+ ascent = sfnt_mul_fixed (hhea->ascent * 65536,
+ scale) / 65536;
+
+ origin = 0;
+
+ for (i = 0; i < nrasters; ++i)
+ {
+ /* Compute the base position. */
+ x = origin + rasters[i]->offx;
+ y = ascent - rasters[i]->height - rasters[i]->offy;
+
+ /* Draw the solid fill with the glyph as clip mask. */
+ XRenderComposite (display, PictOpOver, solid, glyphs[i],
+ drawable, 0, 0, 0, 0, x, y,
+ rasters[i]->width, rasters[i]->height);
+
+ origin += advances[i];
+ }
+ }
+ }
+}
+
+static void
+sfnt_test_raster (struct sfnt_raster *raster,
+ struct sfnt_hhea_table *hhea,
+ sfnt_fixed scale)
+{
+ int x, y, i;
+
+ for (y = 0; y < raster->height; ++y)
+ {
+ for (x = 0; x < raster->width; ++x)
+ printf ("%3d ", (int) raster->cells[y * raster->stride + x]);
+ puts ("");
+ }
+
+ if (hhea && getenv ("SFNT_X"))
+ {
+ i = 0;
+
+ if (!fork ())
+ sfnt_x_raster (&raster, &i, 1, hhea, scale);
+ }
+}
+
+
+
+/* Instruction execution tests. */
+
+static struct sfnt_maxp_table test_interpreter_profile =
+ {
+ 0x00010000,
+ 650,
+ 100,
+ 100,
+ 100,
+ 100,
+ 2,
+ 100,
+ 255,
+ 12,
+ 12,
+ 100,
+ 5000,
+ 100,
+ 1,
+ };
+
+static sfnt_fword test_cvt_values[] =
+ {
+ 100, 100, -100, -100, 50, 50, 50, 50, 0, 0,
+ };
+
+static struct sfnt_cvt_table test_interpreter_cvt =
+ {
+ 10,
+ test_cvt_values,
+ };
+
+static struct sfnt_head_table test_interpreter_head =
+ {
+ 0x00010000,
+ 0x00010000,
+ 0,
+ 0x5f0f3cf5,
+ 0,
+ 800,
+ 0,
+ 0,
+ 0,
+ 0,
+ -312,
+ -555,
+ 1315,
+ 2163,
+ 0,
+ 12,
+ 0,
+ 0,
+ 0,
+ };
+
+static struct sfnt_interpreter *
+sfnt_make_test_interpreter (void)
+{
+ return sfnt_make_interpreter (&test_interpreter_profile,
+ &test_interpreter_cvt,
+ &test_interpreter_head,
+ NULL, 17, 17);
+}
+
+struct sfnt_interpreter_test
+{
+ const char *name;
+ unsigned char *instructions;
+ int num_instructions;
+ void *arg;
+ void (*check) (struct sfnt_interpreter *, void *, bool);
+};
+
+static void
+sfnt_run_interpreter_test (struct sfnt_interpreter_test *test,
+ struct sfnt_interpreter *interpreter)
+{
+ fprintf (stderr, "Testing %s: ", test->name);
+
+ if (setjmp (interpreter->trap))
+ test->check (interpreter, test->arg, true);
+ else
+ {
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = test->instructions;
+ interpreter->num_instructions = test->num_instructions;
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_TEST);
+ test->check (interpreter, test->arg, false);
+ }
+}
+
+struct sfnt_generic_test_args
+{
+ uint32_t *expected_stack;
+ int expected_stack_elements;
+ bool expected_trap;
+ int expected_IP;
+};
+
+static void
+sfnt_generic_check (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ struct sfnt_generic_test_args *args;
+ int i;
+
+ args = arg;
+
+ if (((interpreter->SP - interpreter->stack)
+ != args->expected_stack_elements))
+ {
+ fprintf (stderr,
+ "failed at IP %d:%d (expected %d stack elements,"
+ " got %td); last trap string: %s\n",
+ interpreter->call_depth, interpreter->IP,
+ args->expected_stack_elements,
+ interpreter->SP - interpreter->stack,
+ ((trap && interpreter->trap_reason)
+ ? interpreter->trap_reason
+ : "NULL"));
+
+ for (i = 0; i < interpreter->SP - interpreter->stack; ++i)
+ fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ if (memcmp (interpreter->stack, args->expected_stack,
+ ((char *) interpreter->SP
+ - (char *) interpreter->stack)))
+ {
+ fprintf (stderr, "failed (inconsistent stack elements)\n"
+ "machine stack ------------------------->\n");
+
+ for (i = 0; i < args->expected_stack_elements; ++i)
+ fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+
+ fprintf (stderr,
+ "\nexpected stack ------------------------>\n");
+
+ for (i = 0; i < args->expected_stack_elements; ++i)
+ fprintf (stderr, "%8d ", (int) args->expected_stack[i]);
+
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ if (args->expected_IP != -1
+ && interpreter->IP != args->expected_IP)
+ {
+ fprintf (stderr, "failed (IP is %d, not %d)\n",
+ interpreter->IP, args->expected_IP);
+ return;
+ }
+
+ if (trap)
+ {
+ if (args->expected_trap)
+ fprintf (stderr, "passed (with trap %s)\n",
+ interpreter->trap_reason);
+ else
+ fprintf (stderr, "failed (unexpected trap %s)\n",
+ interpreter->trap_reason);
+
+ return;
+ }
+
+ if (args->expected_trap)
+ fprintf (stderr, "failed, trap not encountered\n");
+ else
+ fprintf (stderr, "passed\n");
+
+ return;
+}
+
+static void
+sfnt_check_srp0 (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed (unexpected trap %s)\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.rp0 != 0)
+ {
+ fprintf (stderr, "failed, rp0 is not 0, but %d\n",
+ interpreter->state.rp0);
+ return;
+ }
+
+ if (interpreter->state.rp1 != 1)
+ {
+ fprintf (stderr, "failed, rp1 is not 1, but %d\n",
+ interpreter->state.rp1);
+ return;
+ }
+
+ if (interpreter->state.rp2 != 2)
+ {
+ fprintf (stderr, "failed, rp2 is not 2, but %d\n",
+ interpreter->state.rp2);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_szp0 (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (!trap)
+ {
+ fprintf (stderr, "failed, expected trap\n");
+ return;
+ }
+
+ if (interpreter->state.zp0 != 1
+ || interpreter->state.zp1 != 1
+ || interpreter->state.zp2 != 0)
+ {
+ fprintf (stderr,
+ "failed, unexpected values of zone pointers: %d %d %d\n",
+ interpreter->state.zp0, interpreter->state.zp1,
+ interpreter->state.zp2);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed with expected trap %s\n",
+ interpreter->trap_reason);
+ return;
+}
+
+static void
+sfnt_check_sloop (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (interpreter->state.loop != 1)
+ {
+ /* The trap should've restored GS->loop to 1. */
+ fprintf (stderr, "failed, GS->loop should be 1, not %d\n",
+ interpreter->state.loop);
+ return;
+ }
+
+ if (!trap)
+ {
+ fprintf (stderr, "failed, expected trap\n");
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed with expected trap %s\n",
+ interpreter->trap_reason);
+ return;
+}
+
+struct sfnt_rounding_test_args
+{
+ sfnt_f26dot6 value;
+};
+
+static void
+sfnt_check_rounding (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ sfnt_f26dot6 value;
+ struct sfnt_rounding_test_args *args;
+
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap: %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP == interpreter->stack)
+ {
+ fprintf (stderr, "failed, empty stack\n");
+ return;
+ }
+
+ value = *(interpreter->SP - 1);
+ args = arg;
+
+ if (value != args->value)
+ {
+ fprintf (stderr, "failed. value is: %d %d, but wanted: %d %d\n",
+ value >> 6, value & 63, args->value >> 6,
+ args->value & 63);
+ return;
+ }
+
+ fprintf (stderr, "passed, expected value %d\n", value);
+ return;
+}
+
+static void
+sfnt_check_smd (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.minimum_distance != 32)
+ {
+ fprintf (stderr, "failed, expected minimum distance"
+ " of 32, got %d\n",
+ interpreter->state.minimum_distance);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_scvtci (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.cvt_cut_in != 128)
+ {
+ fprintf (stderr, "failed, expected 128, got %d\n",
+ interpreter->state.cvt_cut_in);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_sswci (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.sw_cut_in != 512)
+ {
+ fprintf (stderr, "failed, expected 512, got %d\n",
+ interpreter->state.sw_cut_in);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_ssw (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.single_width_value
+ != sfnt_mul_f26dot6_fixed (-64, interpreter->scale))
+ {
+ fprintf (stderr, "failed, got %d at scale %d,"
+ " expected %d\n",
+ interpreter->state.single_width_value,
+ interpreter->scale,
+ sfnt_mul_f26dot6_fixed (-64, interpreter->scale));
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_flipon (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (!interpreter->state.auto_flip)
+ fprintf (stderr, "failed, auto flip not enabled\n");
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_flipoff (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.auto_flip)
+ fprintf (stderr, "failed, auto flip not disabled\n");
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_sdb (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.delta_base != 8)
+ fprintf (stderr, "failed, delta base is %d, not 8\n",
+ interpreter->state.delta_base);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_sds (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.delta_shift != 1)
+ fprintf (stderr, "failed, delta shift is %d, not 1\n",
+ interpreter->state.delta_shift);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_scanctrl (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, expected empty stack\n");
+ return;
+ }
+
+ if (interpreter->state.scan_control != 1)
+ fprintf (stderr, "failed, scan control is %d, not 1\n",
+ interpreter->state.scan_control);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_instctrl (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, expected empty stack\n");
+ return;
+ }
+
+ if (interpreter->state.instruct_control != 2)
+ fprintf (stderr, "failed, inst control is %d, not 2\n",
+ interpreter->state.instruct_control);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static struct sfnt_generic_test_args npushb_test_args =
+ {
+ (uint32_t []) { 1U, 2U, 3U, 4U, },
+ 4,
+ true,
+ 6,
+ };
+
+static struct sfnt_generic_test_args npushw_test_args =
+ {
+ (uint32_t []) { 0x101U, 0x202U, 0x303U, 0x404U, },
+ 4,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args pushb_test_args =
+ {
+ (uint32_t []) { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U,
+ 1U, },
+ 9,
+ true,
+ 11,
+ };
+
+static struct sfnt_generic_test_args pushw_test_args =
+ {
+ (uint32_t []) { 0x203U, 0x204U, 0x205U, 0x206U, 0x207U, 0x208U,
+ 0x909U, 0x909U, (uint32_t) -1, },
+ 9,
+ true,
+ 20,
+ };
+
+static struct sfnt_generic_test_args stack_overflow_test_args =
+ {
+ (uint32_t[100]) { },
+ 100,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args stack_underflow_test_args =
+ {
+ /* GCC BUG, this should be []! */
+ (uint32_t []) { },
+ 0,
+ true,
+ 4,
+ };
+
+static struct sfnt_rounding_test_args rtg_test_args =
+ {
+ 64,
+ };
+
+static struct sfnt_rounding_test_args rtg_symmetric_test_args =
+ {
+ -64,
+ };
+
+static struct sfnt_rounding_test_args rtg_1_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rtg_1_symmetric_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rthg_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rthg_1_test_args =
+ {
+ 96,
+ };
+
+static struct sfnt_rounding_test_args rtdg_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rtdg_1_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rtdg_2_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rtdg_3_test_args =
+ {
+ 64,
+ };
+
+static struct sfnt_generic_test_args else_test_args =
+ {
+ (uint32_t []) { 77U, 90U, 83U, },
+ 3,
+ false,
+ 40,
+ };
+
+static struct sfnt_generic_test_args jmpr_test_args =
+ {
+ /* What ends up on the stack?
+
+ First, there are the three words that the first PUSHW[2]
+ instruction has pushed:
+
+ 0, 0xb2, -3
+
+ After those three words are pushed, JMPR[] is called, and pops an
+ offset:
+
+ -3
+
+ so now the stack is:
+
+ 0, 0xb2
+
+ as a result of the relative jump, IP is now at the least
+ significant byte of the word inside what was originally a
+ PUSHW[2] instruction, 0xb2, which itself is PUSHB[2]!
+
+ As a result of that instruction, three more bytes, including
+ JMPR[] itself are pushed onto the stack, making it:
+
+ 0, 0xb2, 255, 253, 0x1c
+
+ Then, execution continues as usual. 4 is pushed on to the
+ stack, making it:
+
+ 0, 0xb2, 255, 253, 0x1c, 4
+
+ Another JMPR[] pops:
+
+ 4
+
+ making the stack:
+
+ 0, 0xb2, 255, 253, 0x1c
+
+ And skips the next three padding bytes, finally reaching a
+ PUSHW[0] instruction which pushes -30 onto the stack:
+
+ 0, 0xb2, 255, 253, 0x1c, -30
+
+ and a JMPR[] instruction, which pops:
+
+ -30
+
+ making:
+
+ 0, 0xb2, 255, 253,
+
+ and subsequently traps, as -30 would underflow the instruction
+ stream. */
+ (uint32_t []) { 0, 0xb2, 255, 253, 0x1c, },
+ 5,
+ true,
+ 17,
+ };
+
+static struct sfnt_generic_test_args dup_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args pop_test_args =
+ {
+ (uint32_t []) { 70, 70, },
+ 2,
+ false,
+ 5,
+ };
+
+static struct sfnt_generic_test_args clear_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args swap_test_args =
+ {
+ (uint32_t []) { 2, 1, },
+ 2,
+ false,
+ 4,
+ };
+
+static struct sfnt_generic_test_args depth_test_args =
+ {
+ (uint32_t []) { 3, 3, 3, 3, },
+ 4,
+ false,
+ 5,
+ };
+
+static struct sfnt_generic_test_args cindex_test_args =
+ {
+ (uint32_t []) { 0, 3, 3, 4, 0, },
+ 5,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args mindex_test_args =
+ {
+ (uint32_t []) { 0, 3, 7, 4, 4, },
+ 5,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args raw_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args loopcall_test_args =
+ {
+ (uint32_t []) { 10, },
+ 1,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args call_test_args =
+ {
+ (uint32_t []) { 11, },
+ 1,
+ true,
+ 2,
+ };
+
+static struct sfnt_generic_test_args fdef_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 4,
+ };
+
+static struct sfnt_generic_test_args fdef_1_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 9,
+ };
+
+static struct sfnt_generic_test_args endf_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args ws_test_args =
+ {
+ (uint32_t []) { 40, },
+ 1,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rs_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 2,
+ };
+
+static struct sfnt_generic_test_args wcvtp_test_args =
+ {
+ (uint32_t []) { 32, },
+ 1,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rcvt_test_args =
+ {
+ (uint32_t []) { 136, },
+ 1,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args mppem_test_args =
+ {
+ (uint32_t []) { 17, },
+ 1,
+ false,
+ 1,
+ };
+
+static struct sfnt_generic_test_args mps_test_args =
+ {
+ (uint32_t []) { 17, },
+ 1,
+ false,
+ 1,
+ };
+
+static struct sfnt_generic_test_args debug_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args lt_test_args =
+ {
+ (uint32_t []) { 1, 0, 0, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args lteq_test_args =
+ {
+ (uint32_t []) { 1, 0, 1, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args gt_test_args =
+ {
+ (uint32_t []) { 0, 1, 0, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args gteq_test_args =
+ {
+ (uint32_t []) { 0, 1, 1, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args eq_test_args =
+ {
+ (uint32_t []) { 0, 1, 0, },
+ 3,
+ false,
+ 18,
+ };
+
+static struct sfnt_generic_test_args neq_test_args =
+ {
+ (uint32_t []) { 1, 0, 1, },
+ 3,
+ false,
+ 18,
+ };
+
+static struct sfnt_generic_test_args odd_test_args =
+ {
+ (uint32_t []) { 1, 0, },
+ 2,
+ false,
+ 9,
+ };
+
+static struct sfnt_generic_test_args even_test_args =
+ {
+ (uint32_t []) { 0, 1, },
+ 2,
+ false,
+ 9,
+ };
+
+static struct sfnt_generic_test_args if_test_args =
+ {
+ (uint32_t []) { 17, 24, 1, 2, 3, 4, 5, -1, -1,
+ 88, 1, 3, },
+ 12,
+ false,
+ 185,
+ };
+
+static struct sfnt_generic_test_args eif_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args and_test_args =
+ {
+ (uint32_t []) { 0, 0, 1, 0, },
+ 4,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args or_test_args =
+ {
+ (uint32_t []) { 1, 1, 1, 0, },
+ 4,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args not_test_args =
+ {
+ (uint32_t []) { 0, 1, },
+ 2,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args sds_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args add_test_args =
+ {
+ (uint32_t []) { 96, -1, },
+ 2,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args sub_test_args =
+ {
+ (uint32_t []) { 64, -64, 431, },
+ 3,
+ false,
+ 14,
+ };
+
+static struct sfnt_generic_test_args div_test_args =
+ {
+ (uint32_t []) { 32, -64, },
+ 2,
+ true,
+ 15,
+ };
+
+static struct sfnt_generic_test_args mul_test_args =
+ {
+ (uint32_t []) { 255, -255, 255, },
+ 3,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args abs_test_args =
+ {
+ (uint32_t []) { 1, 1, },
+ 2,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args neg_test_args =
+ {
+ (uint32_t []) { 1, -1, },
+ 2,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args floor_test_args =
+ {
+ (uint32_t []) { -128, -64, 0, 64, 128, },
+ 5,
+ false,
+ 17,
+ };
+
+static struct sfnt_generic_test_args ceiling_test_args =
+ {
+ (uint32_t []) { -128, -128, -64, 0, 64, 128, 128, },
+ 7,
+ false,
+ 25,
+ };
+
+static struct sfnt_generic_test_args round_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args nround_test_args =
+ {
+ (uint32_t []) { 63, },
+ 1,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args wcvtf_test_args =
+ {
+ (uint32_t []) { (63 * 17 * 65535 / 800) >> 10, },
+ 1,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args jrot_test_args =
+ {
+ (uint32_t []) { 40, 40, },
+ 2,
+ false,
+ 13,
+ };
+
+static struct sfnt_generic_test_args jrof_test_args =
+ {
+ (uint32_t []) { 4, },
+ 1,
+ false,
+ 13,
+ };
+
+static struct sfnt_generic_test_args deltac1_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+static struct sfnt_generic_test_args deltac2_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+static struct sfnt_generic_test_args deltac3_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+/* Macros and instructions for detailed rounding tests. */
+
+/* PUSHB[0] period:phase:threshold
+ SROUND[] */
+#define SFNT_ROUNDING_OPERAND(period, phase, threshold) \
+ 0xb0, (((unsigned char) period << 6) \
+ | ((unsigned char) phase & 3) << 4 \
+ | ((unsigned char) threshold & 15)), 0x76
+
+/* PUSHB[0] period:phase:threshold
+ S45ROUND[] */
+#define SFNT_ROUNDING_OPERAND_45(period, phase, threshold) \
+ 0xb0, (((unsigned char) period << 6) \
+ | ((unsigned char) phase & 3) << 4 \
+ | ((unsigned char) threshold & 15)), 0x77
+
+/* PUSHB[0] value
+ ROUND[] */
+#define SFNT_ROUND_VALUE(value) 0xb0, value, 0x68
+
+static unsigned char sfnt_sround_instructions[] =
+ {
+ SFNT_ROUNDING_OPERAND (0, 0, 8),
+ SFNT_ROUND_VALUE (15),
+ SFNT_ROUND_VALUE (17),
+ SFNT_ROUNDING_OPERAND (1, 0, 8),
+ SFNT_ROUND_VALUE (32),
+ SFNT_ROUND_VALUE (16),
+ SFNT_ROUNDING_OPERAND (2, 0, 8),
+ SFNT_ROUND_VALUE (64),
+ SFNT_ROUND_VALUE (63),
+ SFNT_ROUNDING_OPERAND (0, 1, 8),
+ SFNT_ROUND_VALUE (16),
+ SFNT_ROUND_VALUE (24),
+ SFNT_ROUNDING_OPERAND (0, 2, 8),
+ SFNT_ROUND_VALUE (20),
+ SFNT_ROUND_VALUE (48),
+ SFNT_ROUNDING_OPERAND (0, 3, 8),
+ SFNT_ROUND_VALUE (7),
+ SFNT_ROUND_VALUE (70),
+ };
+
+static uint32_t sfnt_sround_values[] =
+ {
+ /* 0, 0, 8 = RTDG; 15 rounded to the double grid and becomes 0, 17
+ is 32. */
+ 0, 32,
+ /* 1, 0, 8 = RTG; 32 rounded to the grid is 64, 16 is 0. */
+ 64, 0,
+ /* 2, 0, 8 = round to a grid separated by 128s. 64 is 128, 63 is
+ 0. */
+ 128, 0,
+ /* 0, 1, 8 = round to a double grid with a phase of 8. 16 rounds
+ down to 8, 24 rounds up to 40. */
+ 8, 40,
+ /* 0, 2, 8 = round to a double grid with a phase of 16. 20 rounds
+ down to 16, 40 rounds up to 48. */
+ 16, 48,
+ /* 0, 3, 8 = round to a double grid with a phase of 48. 7 rounds
+ up to 16, 70 rounds up to 80. */
+ 16, 80,
+ };
+
+static struct sfnt_generic_test_args sround_test_args =
+ {
+ sfnt_sround_values,
+ ARRAYELTS (sfnt_sround_values),
+ false,
+ ARRAYELTS (sfnt_sround_instructions),
+ };
+
+static unsigned char sfnt_s45round_instructions[] =
+ {
+ SFNT_ROUNDING_OPERAND_45 (0, 0, 0),
+ SFNT_ROUND_VALUE (1),
+ SFNT_ROUND_VALUE (45),
+ };
+
+static uint32_t sfnt_s45round_values[] =
+ {
+ /* 0, 0, 0: 1 rounded to the double cubic grid becomes 45, and 46
+ rounded to the double cubic grid becomes 90. */
+ 45, 90,
+ };
+
+static struct sfnt_generic_test_args s45round_test_args =
+ {
+ sfnt_s45round_values,
+ ARRAYELTS (sfnt_s45round_values),
+ false,
+ ARRAYELTS (sfnt_s45round_instructions),
+ };
+
+static struct sfnt_generic_test_args rutg_test_args =
+ {
+ (uint32_t []) { 64, 64, 0, },
+ 3,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rdtg_test_args =
+ {
+ (uint32_t []) { 0, 0, 64, },
+ 3,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args sangw_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args aa_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args getinfo_test_args =
+ {
+ /* Pretend to be the Macintosh System 7 scaler.
+
+ This lets the interpreter get away with only two phantom
+ points, as specified in Apple's TrueType reference manual. */
+ (uint32_t []) { 2, 0, },
+ 2,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args idef_test_args =
+ {
+ (uint32_t []) { 1, 2, 3, },
+ 3,
+ false,
+ 11,
+ };
+
+static struct sfnt_generic_test_args roll_test_args =
+ {
+ (uint32_t []) { 1, 2, 4, 5, 3, },
+ 5,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args roll_1_test_args =
+ {
+ (uint32_t []) { 1, 2, },
+ 2,
+ true,
+ 3,
+ };
+
+static struct sfnt_generic_test_args max_test_args =
+ {
+ (uint32_t []) { 70, },
+ 1,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args min_test_args =
+ {
+ (uint32_t []) { -70, },
+ 1,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args scantype_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_interpreter_test all_tests[] =
+ {
+ {
+ "NPUSHB",
+ /* NPUSHB[] 4 1 2 3 4
+ NPUSHB[] 5 1 2 3 4 */
+ (unsigned char []) { 0x40, 4, 1, 2, 3, 4,
+ 0x40, 5, 1, 2, 3, 4, },
+ 10,
+ &npushb_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NPUSHW",
+ /* NPUSHW[] 4 0x101 0x202 0x303 0x404
+ NPUSHW[] 4 0x101 0x202 0x303 0x4?? */
+ (unsigned char []) { 0x41, 4, 1, 1, 2, 2, 3, 3, 4, 4,
+ 0x41, 4, 1, 1, 2, 2, 3, 3, 4, },
+ 19,
+ &npushw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "PUSHB",
+ /* PUSHB[7] 1 2 3 4 5 6 7 8
+ PUSHB[0] 1
+ PUSHB[5] 1 2 3 4 5 ? */
+ (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0xb0, 1,
+ 0xb5, 1, 2, 3, 4, 5, },
+ 17,
+ &pushb_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "PUSHW",
+ /* PUSHW[7] 2 3 2 4 2 5 2 6 2 7 2 8 9 9 9 9
+ PUSHW[0] 255 255 -- this should get sign-extended
+ PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 ? */
+ (unsigned char []) { 0xbf, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 8, 9, 9, 9, 9,
+ 0xb8, 255, 255,
+ 0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, },
+ 28,
+ &pushw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that stack overflow is handled correctly",
+ /* NPUSHB[] 101 0... */
+ (unsigned char [103]) { 0x40, 101, },
+ 103,
+ &stack_overflow_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that stack underflow is handled correctly",
+ /* PUSHW[0] 100 100
+ POP[]
+ POP[] */
+ (unsigned char []) { 0xb8, 100, 100,
+ 0x21,
+ 0x21, },
+ 5,
+ &stack_underflow_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SRP0, SRP1, SRP2",
+ /* PUSHB[0] 0
+ SRP0[]
+ PUSHB[0] 1
+ SRP1[]
+ PUSHB[0] 2
+ SRP2[] */
+ (unsigned char []) { 0xb0, 0,
+ 0x10,
+ 0xb0, 1,
+ 0x11,
+ 0xb0, 2,
+ 0x12, },
+ 9,
+ NULL,
+ sfnt_check_srp0,
+ },
+ {
+ "SZP0, SZP1, SZP2, SZPS",
+ /* PUSHB[0] 1
+ SZP0[]
+ PUSHB[0] 1
+ SZP1[]
+ PUSHB[0] 0
+ SZP2[]
+ PUSHB[0] 5
+ SZPS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x13,
+ 0xb0, 1,
+ 0x14,
+ 0xb0, 0,
+ 0x15,
+ 0xb0, 5,
+ 0x16, },
+ 12,
+ NULL,
+ sfnt_check_szp0,
+ },
+ {
+ "SLOOP",
+ /* PUSHB[0] 2
+ SLOOP[]
+ PUSHB[0] 0
+ SLOOP[] */
+ (unsigned char []) { 0xb0, 2,
+ 0x17,
+ 0xb0, 0,
+ 0x17, },
+ 6,
+ NULL,
+ sfnt_check_sloop,
+ },
+ {
+ "RTG",
+ /* RTG[]
+ PUSHB[0] 32
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb0, 32,
+ 0x68, },
+ 4,
+ &rtg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "rounding symmetry",
+ /* RTG[]
+ PUSHW[0] 255 -32
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, - (signed char) 32,
+ 0x68, },
+ 5,
+ &rtg_symmetric_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTG to 0",
+ /* RTG[]
+ PUSHB[0] 31
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb0, 31,
+ 0x68, },
+ 4,
+ &rtg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "rounding symmetry to 0",
+ /* RTG[]
+ PUSHB[0] 255 -31
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, - (signed char) 31,
+ 0x68, },
+ 5,
+ &rtg_1_symmetric_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTHG",
+ /* RTHG[]
+ PUSHB[0] 0
+ ROUND[] */
+ (unsigned char []) { 0x19,
+ 0xb0, 0,
+ 0x68, },
+ 4,
+ &rthg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTHG to 96",
+ /* RTHG[]
+ PUSHB[0] 64
+ ROUND[] */
+ (unsigned char []) { 0x19,
+ 0xb0, 64,
+ 0x68, },
+ 4,
+ &rthg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "SMD",
+ /* PUSHB[0] 32
+ SMD[] */
+ (unsigned char []) { 0xb0, 32,
+ 0x1a, },
+ 3,
+ NULL,
+ sfnt_check_smd,
+ },
+ {
+ "ELSE",
+ /* ELSE[]
+ ;; Lots of variable length instructions
+ ;; which will not be executed, like:
+ NPUSHW[] 3 11 22 33 44 55 66
+ NPUSHB[] 1 3
+ PUSHW[2] 1 1 2 2 3 3
+ PUSHB[2] 1 2 3
+ ;; Also test nested ifs.
+ PUSHW[0] 1 1
+ IF[]
+ PUSHW[0] 1 1
+ ELSE[]
+ PUSHW[0] 1 1
+ EIF[]
+ EIF[]
+ PUSHW[0] 1 1
+ ;; the actual contents of the stack.
+ PUSHB[2] 77 90 83 */
+ (unsigned char []) { 0x1b,
+ 0x41, 3, 11, 22, 33, 44, 55, 66,
+ 0x40, 1, 3,
+ 0xba, 1, 1, 2, 2, 3, 3,
+ 0xb2, 1, 2, 3,
+ 0xb8, 1, 1,
+ 0x58,
+ 0xb8, 1, 1,
+ 0x1b,
+ 0xb8, 1, 1,
+ 0x59,
+ 0x59,
+ 0xb2, 77, 90, 83, },
+ 40,
+ &else_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JMPR",
+ /* PUSHW[2] 00 00 00 PUSHB[2] 255 253 JMPR[]
+ PUSHB[0] 4
+ JMPR[]
+ 255 255 255
+ PUSHW[0] 255 -30
+ JMPR[] */
+ (unsigned char []) { 0xba, 00, 00, 00, 0xb2, 255, 253, 0x1c,
+ 0xb0, 4,
+ 0x1c,
+ 255, 255, 255,
+ 0xb8, 255, -30,
+ 0x1c, },
+ 18,
+ &jmpr_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCVTCI",
+ /* PUSHB[0] 128
+ SCVTCI[] */
+ (unsigned char []) { 0xb0, 128,
+ 0x1d, },
+ 3,
+ NULL,
+ sfnt_check_scvtci,
+ },
+ {
+ "SSWCI",
+ /* PUSHW[0] 2 0 ;; 512
+ SSWCI[] */
+ (unsigned char []) { 0xb8, 2, 0,
+ 0x1e, },
+ 4,
+ NULL,
+ sfnt_check_sswci,
+ },
+ {
+ "SSW",
+ /* PUSHW[0] 255 255 ; -1
+ SSW[] ; this should be converted to device-space */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x1f, },
+ 4,
+ NULL,
+ sfnt_check_ssw,
+ },
+ {
+ "DUP",
+ /* PUSHB[0] 70
+ DUP[]
+ POP[]
+ POP[]
+ DUP[] */
+ (unsigned char []) { 0xb0, 70,
+ 0x20,
+ 0x21,
+ 0x21,
+ 0x70, },
+ 6,
+ &dup_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "POP",
+ /* PUSHB[0] 70
+ DUP[]
+ DUP[]
+ POP[] */
+ (unsigned char []) { 0xb0, 70,
+ 0x20,
+ 0x20,
+ 0x21, },
+ 5,
+ &pop_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CLEAR",
+ /* PUSHB[7] 1 2 3 4 5 6 7 8
+ CLEAR[] */
+ (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x22, },
+ 10,
+ &clear_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SWAP",
+ /* PUSHB[1] 1 2
+ SWAP[] */
+ (unsigned char []) { 0xb1, 1, 2,
+ 0x23, },
+ 4,
+ &swap_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DEPTH",
+ /* PUSHB[2] 3 3 3
+ DEPTH[] */
+ (unsigned char []) { 0xb2, 3, 3, 3,
+ 0x24, },
+ 5,
+ &depth_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CINDEX",
+ /* PUSHB[4] 0 3 3 4 1
+ CINDEX[] ; pops 1, indices 4
+ CINDEX[] ; pops 4, indices 0
+ PUSHB[0] 6
+ CINDEX[] ; pops 6, trap */
+ (unsigned char []) { 0xb4, 0, 3, 3, 4, 1,
+ 0x25,
+ 0x25,
+ 0xb0, 6,
+ 0x25, },
+ 11,
+ &cindex_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MINDEX",
+ /* PUSHB[6] 0 3 4 7 3 4 2
+ MINDEX[] ; pops 2, array becomes 0 3 4 7 4 3
+ MINDEX[] ; pops 3, array becomes 0 3 7 4 4 */
+ (unsigned char []) { 0xb6, 0, 3, 4, 7, 3, 4, 2,
+ 0x26,
+ 0x26, },
+ 10,
+ &mindex_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RAW",
+ /* RAW[] */
+ (unsigned char []) { 0x28, },
+ 1,
+ &raw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LOOPCALL",
+ /* PUSHB[1] 0 2
+ FDEF[]
+ PUSHB[0] 1
+ ADD[]
+ ENDF[]
+ PUSHB[1] 10 2
+ LOOPCALL[] */
+ (unsigned char []) { 0xb1, 0, 2,
+ 0x2c,
+ 0xb0, 1,
+ 0x60,
+ 0x2d,
+ 0xb1, 10, 2,
+ 0x2a, },
+ 12,
+ &loopcall_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CALL",
+ /* PUSHB[1] 7 2
+ FDEF[]
+ PUSHB[0] 1
+ ADD[]
+ ENDF[]
+ PUSHB[0] 2
+ CALL[]
+ PUSHB[0] 3
+ ADD[]
+ ;; Test that infinite recursion fails.
+ PUSHB[0] 3
+ FDEF[]
+ PUSHB[0] 3
+ CALL[]
+ ENDF[]
+ PUSHB[0] 3
+ CALL[] */
+ (unsigned char []) { 0xb1, 7, 2,
+ 0x2c,
+ 0xb0, 1,
+ 0x60,
+ 0x2d,
+ 0xb0, 2,
+ 0x2b,
+ 0xb0, 3,
+ 0x60,
+ 0xb0, 3,
+ 0x2c,
+ 0xb0, 3,
+ 0x2b,
+ 0x2d,
+ 0xb0, 3,
+ 0x2b, },
+ 24,
+ &call_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that FDEF traps inside nested definitions",
+ /* PUSHB[0] 1
+ FDEF[]
+ FDEF[]
+ ENDF[]
+ ENDF[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x2c,
+ 0x2c,
+ 0x2d,
+ 0x2d, },
+ 6,
+ &fdef_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that FDEF traps upon missing ENDF",
+ /* PUSHB[0] 1
+ FDEF[]
+ PUSHB[3] 1 2 3 4
+ POP[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x2c,
+ 0xb3, 1, 2, 3, 4,
+ 0x21, },
+ 9,
+ &fdef_1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ENDF",
+ /* ENDF[] */
+ (unsigned char []) { 0x2d, },
+ 1,
+ &endf_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RTDG",
+ /* RTDG[]
+ PUSHB[0] 16
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 16,
+ 0x68, },
+ 4,
+ &rtdg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG down to 0",
+ /* RTDG[]
+ PUSHB[0] 15
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 15,
+ 0x68, },
+ 4,
+ &rtdg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG down to 32",
+ /* RTDG[]
+ PUSHB[0] 47
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 47,
+ 0x68, },
+ 4,
+ &rtdg_2_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG up to 64",
+ /* RTDG[]
+ PUSHB[0] 48
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 48,
+ 0x68, },
+ 4,
+ &rtdg_3_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "WS",
+ /* PUSHB[1] 240 40
+ WS[]
+ PUSHB[0] 240
+ RS[]
+ PUSHB[1] 255 40
+ WS[] */
+ (unsigned char []) { 0xb1, 240, 40,
+ 0x42,
+ 0xb0, 240,
+ 0x43,
+ 0xb1, 255, 40,
+ 0x42, },
+ 11,
+ &ws_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RS",
+ /* PUSHB[0] 255
+ RS[] */
+ (unsigned char []) { 0xb0, 255,
+ 0x43, },
+ 3,
+ &rs_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "WCVTP",
+ /* PUSHB[1] 9 32
+ WCVTP[]
+ PUSHB[0] 9
+ RCVT[]
+ PUSHB[1] 10 10
+ WCVTP[] */
+ (unsigned char []) { 0xb1, 9, 32,
+ 0x44,
+ 0xb0, 9,
+ 0x45,
+ 0xb1, 10, 10,
+ 0x44, },
+ 11,
+ &wcvtp_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RCVT",
+ /* PUSHB[0] 1
+ RCVT[]
+ PUSHB[0] 10
+ RCVT[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x45,
+ 0xb0, 10,
+ 0x45, },
+ 6,
+ &rcvt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MPPEM",
+ /* MPPEM[] */
+ (unsigned char []) { 0x4b, },
+ 1,
+ &mppem_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MPS",
+ /* MPS[] */
+ (unsigned char []) { 0x4c, },
+ 1,
+ &mps_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "FLIPON",
+ /* FLIPON[] */
+ (unsigned char []) { 0x4d, },
+ 1,
+ NULL,
+ sfnt_check_flipon,
+ },
+ {
+ "FLIPOFF",
+ /* FLIPOFF[] */
+ (unsigned char []) { 0x4e, },
+ 1,
+ NULL,
+ sfnt_check_flipoff,
+ },
+ {
+ "DEBUG",
+ /* PUSHB[0] 1
+ DEBUG[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x4f, },
+ 3,
+ &debug_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LT",
+ /* PUSHB[1] 47 48
+ LT[]
+ PUSHB[1] 48 47
+ LT[]
+ PUSHB[1] 47 47
+ LT[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x50,
+ 0xb1, 48, 47,
+ 0x50,
+ 0xb1, 47, 47,
+ 0x50, },
+ 12,
+ &lt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LTEQ",
+ /* PUSHB[1] 47 48
+ LTEQ[]
+ PUSHB[1] 48 47
+ LTEQ[]
+ PUSHB[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x51,
+ 0xb1, 48, 47,
+ 0x51,
+ 0xb1, 47, 47,
+ 0x51, },
+ 12,
+ &lteq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "GT",
+ /* PUSHB[1] 47 48
+ GT[]
+ PUSHB[1] 48 47
+ GT[]
+ GT[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x52,
+ 0xb1, 48, 47,
+ 0x52,
+ 0xb1, 47, 47,
+ 0x52, },
+ 12,
+ &gt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "GTEQ",
+ /* PUSHB[1] 47 48
+ GTEQ[]
+ PUSHB[1] 48 47
+ GTEQ[]
+ GTEQ[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x53,
+ 0xb1, 48, 47,
+ 0x53,
+ 0xb1, 47, 47,
+ 0x53, },
+ 12,
+ &gteq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EQ",
+ /* PUSHW[1] 255 253 255 255
+ EQ[]
+ PUSHW[1] 27 27 27 27
+ EQ[]
+ PUSHB[0] 3
+ PUSHW[0] 255 254
+ EQ[] */
+ (unsigned char []) { 0xb9, 255, 253, 255, 255,
+ 0x54,
+ 0xb9, 27, 27, 27, 27,
+ 0x54,
+ 0xb0, 3,
+ 0xb8, 255, 254,
+ 0x54, },
+ 18,
+ &eq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NEQ",
+ /* PUSHW[1] 255 253 255 255
+ NEQ[]
+ PUSHW[1] 27 27 27 27
+ NEQ[]
+ PUSHB[0] 3
+ PUSHW[0] 255 254
+ NEQ[] */
+ (unsigned char []) { 0xb9, 255, 253, 255, 255,
+ 0x55,
+ 0xb9, 27, 27, 27, 27,
+ 0x55,
+ 0xb0, 3,
+ 0xb8, 255, 254,
+ 0x55, },
+ 18,
+ &neq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ODD",
+ /* RTG[]
+ PUSHW[1] 255 224 ;; -32
+ ODD[] ;; Rounds symmetrically to -64, which is odd.
+ PUSHW[1] 255 159 ;; -96
+ ODD[] ;; Rounds symmetrically to -128, which is even. */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, 224,
+ 0x56,
+ 0xb8, 255, 159,
+ 0x56, },
+ 9,
+ &odd_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EVEN",
+ /* RTG[]
+ PUSHW[1] 255 224 ;; -32
+ EVEN[] ;; Rounds symmetrically to -64, which is odd.
+ PUSHW[1] 255 159 ;; -96
+ EVEN[] ;; Rounds symmetrically to -128, which is even. */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, 224,
+ 0x57,
+ 0xb8, 255, 159,
+ 0x57, },
+ 9,
+ &even_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "IF",
+ /* NPUSHB[] 1 0
+ IF[]
+ PUSHW[0] 1 1
+ PUSHW[1] 1 1 2 2
+ PUSHW[2] 1 1 2 2 3 3
+ PUSHW[3] 1 1 2 2 3 3 4 4
+ PUSHW[4] 1 1 2 2 3 3 4 4 5 5
+ PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 6
+ PUSHW[6] 1 1 2 2 3 3 4 4 5 5 6 6 7 7
+ PUSHW[7] 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8
+ PUSHB[0] 1
+ PUSHB[1] 2 1
+ PUSHB[2] 3 2 1
+ PUSHB[3] 4 3 2 1
+ PUSHB[4] 5 4 3 2 1
+ PUSHB[5] 6 5 4 3 2 1
+ PUSHB[6] 7 6 5 4 3 2 1
+ PUSHB[7] 8 7 6 5 4 3 2 1
+ DEBUG[]
+ IF[]
+ PUSHB[7] 12 12 12 12 12 12 12 12
+ ELSE[]
+ EIF[]
+ ELSE[]
+ PUSHB[1] 17 24
+ NPUSHB[] 5 1 2 3 4 5
+ NPUSHW[] 2 255 255 255 255
+ EIF[]
+
+ PUSHB[0] 1
+ IF[]
+ NPUSHB[] 2 43 43
+ IF[]
+ PUSHB[0] 45
+ ELSE[]
+ PUSHB[0] 14
+ EIF[]
+ ADD[]
+ ELSE[]
+ NPUSHB[] 4 3 2 1 0
+ EIF[]
+ PUSHB[1] 1 3 */
+ (unsigned char []) { 0x40, 1, 0,
+ 0x58,
+ 0xb8, 1, 1,
+ 0xb9, 1, 1, 2, 2,
+ 0xba, 1, 1, 2, 2, 3, 3,
+ 0xbb, 1, 1, 2, 2, 3, 3, 4, 4,
+ 0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+ 0xbd, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 0xbe, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 0xbf, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 0xb0, 1,
+ 0xb1, 2, 1,
+ 0xb2, 3, 2, 1,
+ 0xb3, 4, 3, 2, 1,
+ 0xb4, 5, 4, 3, 2, 1,
+ 0xb5, 6, 5, 4, 3, 2, 1,
+ 0xb6, 7, 6, 5, 4, 3, 2, 1,
+ 0xb7, 8, 7, 6, 5, 4, 3, 2, 1,
+ 0x4f,
+ 0x58,
+ 0xb7, 12, 12, 12, 12, 12, 12, 12, 12,
+ 0x1b,
+ 0x59,
+ 0x1b,
+ 0xb1, 17, 24,
+ 0x40, 5, 1, 2, 3, 4, 5,
+ 0x41, 2, 255, 255, 255, 255,
+ 0x59,
+ 0xb0, 1,
+ 0x58,
+ 0x40, 2, 43, 43,
+ 0x58,
+ 0xb0, 45,
+ 0x1b,
+ 0xb0, 14,
+ 0x59,
+ 0x60,
+ 0x1b,
+ 0x40, 4, 3, 2, 1, 0,
+ 0x59,
+ 0xb1, 1, 3, },
+ 185,
+ &if_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EIF",
+ /* PUSHB[0] 1
+ IF[]
+ EIF[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x58,
+ 0x59, },
+ 3,
+ &eif_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "AND",
+ /* PUSHB[1] 0 1
+ AND[]
+ PUSHB[1] 37 0
+ AND[]
+ PUSHB[1] 40 1
+ AND[]
+ PUSHB[1] 0 0
+ AND[] */
+ (unsigned char []) { 0xb1, 0, 1,
+ 0x5a,
+ 0xb1, 37, 0,
+ 0x5a,
+ 0xb1, 40, 1,
+ 0x5a,
+ 0xb1, 0, 0,
+ 0x5a, },
+ 16,
+ &and_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "OR",
+ /* PUSHB[1] 0 1
+ OR[]
+ PUSHB[1] 37 0
+ OR[]
+ PUSHB[1] 40 1
+ OR[]
+ PUSHB[1] 0 0
+ OR[] */
+ (unsigned char []) { 0xb1, 0, 1,
+ 0x5b,
+ 0xb1, 37, 0,
+ 0x5b,
+ 0xb1, 40, 1,
+ 0x5b,
+ 0xb1, 0, 0,
+ 0x5b, },
+ 16,
+ &or_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NOT",
+ /* PUSHB[0] 1
+ NOT[]
+ PUSHB[0] 0
+ NOT[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5c,
+ 0xb0, 0,
+ 0x5c, },
+ 6,
+ &not_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SDB",
+ /* PUSHB[0] 8
+ SDB[] */
+ (unsigned char []) { 0xb0, 8,
+ 0x5e, },
+ 3,
+ NULL,
+ sfnt_check_sdb,
+ },
+ {
+ "SDS",
+ /* PUSHB[0] 1
+ SDS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5f, },
+ 3,
+ NULL,
+ sfnt_check_sds,
+ },
+ {
+ "that SDS rejects invalid values",
+ /* PUSHB[0] 1,
+ SDS[]
+ PUSHB[0] 7
+ SDS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5f,
+ 0xb0, 7,
+ 0x5f, },
+ 6,
+ &sds_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ADD",
+ /* PUSHB[1] 64 32
+ ADD[]
+ PUSHW[1] 255 40 0 215 ;; -216 + 215
+ ADD[] */
+ (unsigned char []) { 0xb1, 64, 32,
+ 0x60,
+ 0xb9, 255, 40, 0, 215,
+ 0x60, },
+ 10,
+ &add_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SUB",
+ /* PUSHB[1] 96 32
+ SUB[]
+ PUSHB[1] 32 96
+ SUB[]
+ PUSHW[1] 0 215 255 40 ;; 215 - -216
+ SUB[] */
+ (unsigned char []) { 0xb1, 96, 32,
+ 0x61,
+ 0xb1, 32, 96,
+ 0x61,
+ 0xb9, 0, 215, 255, 40,
+ 0x61, },
+ 14,
+ &sub_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DIV",
+ /* PUSHB[1] 64 128
+ DIV[] ; 1 / 2 = 0.5
+ PUSHW[1] 0 32 255 224
+ DIV[] ; 0.5 / -0.5 = -1.0
+ PUSHW[1] 255 255 0 0
+ DIV[] ; -1 / 0 = trap */
+ (unsigned char []) { 0xb1, 64, 128,
+ 0x62,
+ 0xb9, 0, 32, 255, 224,
+ 0x62,
+ 0xb9, 255, 255, 0, 0,
+ 0x62, },
+ 16,
+ &div_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MUL",
+ /* PUSHB[1] 255 64
+ MUL[] ; 255 * 1 = 255
+ PUSHW[1] 0 255 255 192
+ MUL[] ; 255 * -1 = -255
+ PUSHW[1] 255 1 255 192
+ MUL[] ; -255 * -1 = 255 */
+ (unsigned char []) { 0xb1, 255, 64,
+ 0x63,
+ 0xb9, 0, 255, 255, 192,
+ 0x63,
+ 0xb9, 255, 1, 255, 192,
+ 0x63, },
+ 16,
+ &mul_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ABS",
+ /* PUSHW[0] 255 255
+ ABS[] ;; abs (-1) == 1
+ PUSHB[0] 1
+ ABS[] ;; abs (1) == 1 */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x64,
+ 0xb0, 1,
+ 0x64, },
+ 7,
+ &abs_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NEG",
+ /* PUSHW[0] 255 255
+ NEG[] ;; neg (-1) == 1
+ PUSHB[0] 1
+ NEG[] ;; neg (1) == -1 */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x65,
+ 0xb0, 1,
+ 0x65, },
+ 7,
+ &neg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "FLOOR",
+ /* PUSHW[0] 255 129 ; -127
+ FLOOR[] ; floor (-127) == -128
+ PUSHW[0] 255 193 ; -63
+ FLOOR[] ; floor (-63) == -64
+ PUSHB[0] 63
+ FLOOR[] ; floor (63) == 0
+ PUSHB[0] 127
+ FLOOR[] ; floor (127) == 64
+ PUSHB[0] 191
+ FLOOR[] ; floor (191) == 128 */
+ (unsigned char []) { 0xb8, 255, 129,
+ 0x66,
+ 0xb8, 255, 193,
+ 0x66,
+ 0xb0, 63,
+ 0x66,
+ 0xb0, 127,
+ 0x66,
+ 0xb0, 191,
+ 0x66, },
+ 17,
+ &floor_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CEILING",
+ /* PUSHW[0] 255 128 ; -128
+ CEILING[] ; ceiling (-128) == -128
+ PUSHW[0] 255 127 ; -129
+ CEILING[] ; ceiling (-129) == -128
+ PUSHW[0] 255 191 ; -65
+ CEILING[] ; ceiling (-65) == -64
+ PUSHW[0] 255 255 ; -1
+ CEILING[] ; ceiling (-1) == 0
+ PUSHB[0] 63
+ CEILING[] ; ceiling (63) == 64
+ PUSHB[0] 65
+ CEILING[] ; ceiling (65) == 128
+ PUSHB[0] 128
+ CEILING[] ; ceiling (128) == 128 */
+ (unsigned char []) { 0xb8, 255, 128,
+ 0x67,
+ 0xb8, 255, 127,
+ 0x67,
+ 0xb8, 255, 191,
+ 0x67,
+ 0xb8, 255, 255,
+ 0x67,
+ 0xb0, 63,
+ 0x67,
+ 0xb0, 65,
+ 0x67,
+ 0xb0, 128,
+ 0x67, },
+ 25,
+ &ceiling_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ROUND",
+ /* ROUND[] */
+ (unsigned char []) { 0x68, },
+ 1,
+ &round_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NROUND",
+ /* PUSHB[0] 63
+ NROUND[] */
+ (unsigned char []) { 0xb0, 63,
+ 0x6c, },
+ 3,
+ &nround_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "WCVTF",
+ /* PUSHB[1] 1 63
+ WCVTF[]
+ PUSHB[0] 1
+ RCVT[] */
+ (unsigned char []) { 0xb1, 1, 63,
+ 0x70,
+ 0xb0, 1,
+ 0x45, },
+ 7,
+ &wcvtf_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JROT",
+ /* PUSHB[1] 4 0
+ JROT[] ; this should not skip past the next instruction
+ PUSHB[1] 40 40
+ PUSHB[1] 3 1
+ JROT[] ; this should skip past the next instruction
+ PUSHB[0] 4 */
+ (unsigned char []) { 0xb1, 4, 0,
+ 0x78,
+ 0xb1, 40, 40,
+ 0xb1, 3, 1,
+ 0x78,
+ 0xb0, 4, },
+ 13,
+ &jrot_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JROF",
+ /* PUSHB[1] 4 0
+ JROF[] ; this should skip past the next instruction
+ PUSHB[1] 40 40
+ PUSHB[1] 3 1
+ JROF[] ; this should not skip past the next instruction
+ PUSHB[0] 4 */
+ (unsigned char []) { 0xb1, 4, 0,
+ 0x79,
+ 0xb1, 40, 40,
+ 0xb1, 3, 1,
+ 0x79,
+ 0xb0, 4, },
+ 13,
+ &jrof_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC1",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2, magnitude 15
+ DELTAC1[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should now be greater by 8 / 64
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2, magnitude 15
+ DELTAC1[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x73,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x73,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC2",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 16, magnitude 15
+ DELTAC2[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 16, magnitude 15
+ DELTAC2[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x74,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x74,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac2_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC3",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 32, magnitude 15
+ DELTAC3[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 32, magnitude 15
+ DELTAC3[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x75,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x75,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac3_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SROUND",
+ sfnt_sround_instructions,
+ ARRAYELTS (sfnt_sround_instructions),
+ &sround_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "S45ROUND",
+ sfnt_s45round_instructions,
+ ARRAYELTS (sfnt_s45round_instructions),
+ &s45round_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RUTG",
+ /* RUTG[]
+ PUSHB[0] 1
+ ROUND[]
+ PUSHB[0] 64
+ ROUND[]
+ PUSHB[0] 0
+ ROUND[] */
+ (unsigned char []) { 0x7c,
+ 0xb0, 1,
+ 0x68,
+ 0xb0, 64,
+ 0x68,
+ 0xb0, 0,
+ 0x68, },
+ 10,
+ &rutg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RDTG",
+ /* RUTG[]
+ PUSHB[0] 1
+ ROUND[]
+ PUSHB[0] 63
+ ROUND[]
+ PUSHB[0] 64
+ ROUND[] */
+ (unsigned char []) { 0x7d,
+ 0xb0, 1,
+ 0x68,
+ 0xb0, 63,
+ 0x68,
+ 0xb0, 64,
+ 0x68, },
+ 10,
+ &rdtg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SANGW",
+ /* PUSHB[0] 3
+ SANGW[] */
+ (unsigned char []) { 0xb0, 3,
+ 0x7e, },
+ 3,
+ &sangw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "AA",
+ /* PUSHB[0] 3
+ AA[] */
+ (unsigned char []) { 0xb0, 3,
+ 0x7f, },
+ 3,
+ &aa_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCANCTRL",
+ /* PUSHB[0] 1
+ SCANCTRL[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x85, },
+ 3,
+ NULL,
+ sfnt_check_scanctrl,
+ },
+ {
+ "GETINFO",
+ /* PUSHB[0] 1
+ GETINFO[]
+ PUSHB[0] 6
+ GETINFO[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x88,
+ 0xb0, 6,
+ 0x88, },
+ 6,
+ &getinfo_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "IDEF",
+ /* PUSHB[0] 0x83
+ IDEF[]
+ PUSHB[3] 1 2 3 4
+ POP[]
+ ENDF[]
+ 0x83 */
+ (unsigned char []) { 0xb0, 0x83,
+ 0x89,
+ 0xb3, 1, 2, 3, 4,
+ 0x21,
+ 0x2d,
+ 0x83, },
+ 11,
+ &idef_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ROLL",
+ /* PUSHB[4] 1 2 3 4 5
+ ROLL[] ; this should become 1 2 4 5 3 */
+ (unsigned char []) { 0xb4, 1, 2, 3, 4, 5,
+ 0x8a, },
+ 7,
+ &roll_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that ROLL correctly handles underflow",
+ /* PUSHB[1] 1 2
+ ROLL[] */
+ (unsigned char []) { 0xb1, 1, 2,
+ 0x8a, },
+ 4,
+ &roll_1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MAX",
+ /* PUSHW[1] 0 70 255 186 ; 70, -70
+ MAX[] */
+ (unsigned char []) { 0xb9, 0, 70, 255, 186,
+ 0x8b, },
+ 6,
+ &max_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MIN",
+ /* PUSHW[1] 0 70 255 186 ; 70, -70
+ MIN[] */
+ (unsigned char []) { 0xb9, 0, 70, 255, 186,
+ 0x8c, },
+ 6,
+ &min_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCANTYPE",
+ /* PUSHB[0] 0
+ SCANTYPE[] */
+ (unsigned char []) { 0xb0, 0,
+ 0x8d, },
+ 3,
+ &scantype_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "INSTCTRL",
+ /* PUSHB[1] 1 1
+ INSTCTRL[] ; (1 << 1) should now be set
+ PUSHB[1] 2 1
+ INSTCTRL[] ; (1 << 2) should now be set
+ PUSHB[1] 2 0
+ INSTCTRL[] ; (1 << 2) should no longer be set */
+ (unsigned char []) { 0xb1, 1, 1,
+ 0x8e,
+ 0xb1, 2, 1,
+ 0x8e,
+ 0xb1, 2, 0,
+ 0x8e, },
+ 12,
+ NULL,
+ sfnt_check_instctrl,
+ },
+ };
+
+
+
+/* Instruction debugger. */
+
+static void
+sfnt_setup_debugger (void)
+{
+ XGCValues gcv;
+ Font font;
+
+ display = XOpenDisplay (NULL);
+
+ if (!display)
+ exit (1);
+
+ window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+ 0, 0, 200, 200, 0, 0,
+ WhitePixel (display,
+ DefaultScreen (display)));
+ XMapWindow (display, window);
+
+ /* Select for the appropriate events. */
+ XSelectInput (display, window, KeyPressMask | ExposureMask);
+
+ /* Find an appropriate font. */
+ font = XLoadFont (display, "6x13");
+
+ if (!font)
+ exit (1);
+
+ /* The debugger has been set up. Set up the GCs for drawing points
+ and backgrounds. */
+
+ gcv.foreground = BlackPixel (display, DefaultScreen (display));
+ gcv.font = font;
+ point_gc = XCreateGC (display, window, GCForeground | GCFont,
+ &gcv);
+ gcv.foreground = WhitePixel (display, DefaultScreen (display));
+ background_gc = XCreateGC (display, window, GCForeground, &gcv);
+}
+
+static const char *
+sfnt_name_instruction (unsigned char opcode)
+{
+ static const char *const opcode_names[256] = {
+ "7 SVTCA y",
+ "7 SVTCA x",
+ "8 SPvTCA y",
+ "8 SPvTCA x",
+ "8 SFvTCA y",
+ "8 SFvTCA x",
+ "8 SPvTL ||",
+ "7 SPvTL +",
+ "8 SFvTL ||",
+ "7 SFvTL +",
+ "5 SPvFS",
+ "5 SFvFS",
+ "3 GPv",
+ "3 GFv",
+ "6 SFvTPv",
+ "5 ISECT",
+
+ "4 SRP0",
+ "4 SRP1",
+ "4 SRP2",
+ "4 SZP0",
+ "4 SZP1",
+ "4 SZP2",
+ "4 SZPS",
+ "5 SLOOP",
+ "3 RTG",
+ "4 RTHG",
+ "3 SMD",
+ "4 ELSE",
+ "4 JMPR",
+ "6 SCvTCi",
+ "5 SSwCi",
+ "3 SSW",
+
+ "3 DUP",
+ "3 POP",
+ "5 CLEAR",
+ "4 SWAP",
+ "5 DEPTH",
+ "6 CINDEX",
+ "6 MINDEX",
+ "8 AlignPTS",
+ "7 INS_$28",
+ "3 UTP",
+ "8 LOOPCALL",
+ "4 CALL",
+ "4 FDEF",
+ "4 ENDF",
+ "7 MDAP[0]",
+ "7 MDAP[1]",
+
+ "6 IUP[0]",
+ "6 IUP[1]",
+ "6 SHP[0]",
+ "6 SHP[1]",
+ "6 SHC[0]",
+ "6 SHC[1]",
+ "6 SHZ[0]",
+ "6 SHZ[1]",
+ "5 SHPIX",
+ "2 IP",
+ "8 MSIRP[0]",
+ "8 MSIRP[1]",
+ "7 AlignRP",
+ "4 RTDG",
+ "7 MIAP[0]",
+ "7 MIAP[1]",
+
+ "6 NPushB",
+ "6 NPushW",
+ "2 WS",
+ "2 RS",
+ "5 WCvtP",
+ "4 RCvt",
+ "5 GC[0]",
+ "5 GC[1]",
+ "4 SCFS",
+ "5 MD[0]",
+ "5 MD[1]",
+ "5 MPPEM",
+ "3 MPS",
+ "6 FlipON",
+ "7 FlipOFF",
+ "5 DEBUG",
+
+ "2 LT",
+ "4 LTEQ",
+ "2 GT",
+ "4 GTEQ",
+ "2 EQ",
+ "3 NEQ",
+ "3 ODD",
+ "4 EVEN",
+ "2 IF",
+ "3 EIF",
+ "3 AND",
+ "2 OR",
+ "3 NOT",
+ "7 DeltaP1",
+ "3 SDB",
+ "3 SDS",
+
+ "3 ADD",
+ "3 SUB",
+ "3 DIV",
+ "3 MUL",
+ "3 ABS",
+ "3 NEG",
+ "5 FLOOR",
+ "7 CEILING",
+ "8 ROUND[0]",
+ "8 ROUND[1]",
+ "8 ROUND[2]",
+ "8 ROUND[3]",
+ "9 NROUND[0]",
+ "9 NROUND[1]",
+ "9 NROUND[2]",
+ "9 NROUND[3]",
+
+ "5 WCvtF",
+ "7 DeltaP2",
+ "7 DeltaP3",
+ "A DeltaCn[0]",
+ "A DeltaCn[1]",
+ "A DeltaCn[2]",
+ "6 SROUND",
+ "8 S45Round",
+ "4 JROT",
+ "4 JROF",
+ "4 ROFF",
+ "7 INS_$7B",
+ "4 RUTG",
+ "4 RDTG",
+ "5 SANGW",
+ "2 AA",
+
+ "6 FlipPT",
+ "8 FlipRgON",
+ "9 FlipRgOFF",
+ "7 INS_$83",
+ "7 INS_$84",
+ "8 ScanCTRL",
+ "9 SDPvTL[0]",
+ "9 SDPvTL[1]",
+ "7 GetINFO",
+ "4 IDEF",
+ "4 ROLL",
+ "3 MAX",
+ "3 MIN",
+ "8 ScanTYPE",
+ "8 InstCTRL",
+ "7 INS_$8F",
+
+ "7 INS_$90",
+ "7 GXAXIS",
+ "7 INS_$92",
+ "7 INS_$93",
+ "7 INS_$94",
+ "7 INS_$95",
+ "7 INS_$96",
+ "7 INS_$97",
+ "7 INS_$98",
+ "7 INS_$99",
+ "7 INS_$9A",
+ "7 INS_$9B",
+ "7 INS_$9C",
+ "7 INS_$9D",
+ "7 INS_$9E",
+ "7 INS_$9F",
+
+ "7 INS_$A0",
+ "7 INS_$A1",
+ "7 INS_$A2",
+ "7 INS_$A3",
+ "7 INS_$A4",
+ "7 INS_$A5",
+ "7 INS_$A6",
+ "7 INS_$A7",
+ "7 INS_$A8",
+ "7 INS_$A9",
+ "7 INS_$AA",
+ "7 INS_$AB",
+ "7 INS_$AC",
+ "7 INS_$AD",
+ "7 INS_$AE",
+ "7 INS_$AF",
+
+ "8 PushB[0]",
+ "8 PushB[1]",
+ "8 PushB[2]",
+ "8 PushB[3]",
+ "8 PushB[4]",
+ "8 PushB[5]",
+ "8 PushB[6]",
+ "8 PushB[7]",
+ "8 PushW[0]",
+ "8 PushW[1]",
+ "8 PushW[2]",
+ "8 PushW[3]",
+ "8 PushW[4]",
+ "8 PushW[5]",
+ "8 PushW[6]",
+ "8 PushW[7]",
+
+ "7 MDRP[G]",
+ "7 MDRP[B]",
+ "7 MDRP[W]",
+ "7 MDRP[?]",
+ "8 MDRP[rG]",
+ "8 MDRP[rB]",
+ "8 MDRP[rW]",
+ "8 MDRP[r?]",
+ "8 MDRP[mG]",
+ "8 MDRP[mB]",
+ "8 MDRP[mW]",
+ "8 MDRP[m?]",
+ "9 MDRP[mrG]",
+ "9 MDRP[mrB]",
+ "9 MDRP[mrW]",
+ "9 MDRP[mr?]",
+
+ "8 MDRP[pG]",
+ "8 MDRP[pB]",
+ "8 MDRP[pW]",
+ "8 MDRP[p?]",
+ "9 MDRP[prG]",
+ "9 MDRP[prB]",
+ "9 MDRP[prW]",
+ "9 MDRP[pr?]",
+ "9 MDRP[pmG]",
+ "9 MDRP[pmB]",
+ "9 MDRP[pmW]",
+ "9 MDRP[pm?]",
+ "A MDRP[pmrG]",
+ "A MDRP[pmrB]",
+ "A MDRP[pmrW]",
+ "A MDRP[pmr?]",
+
+ "7 MIRP[G]",
+ "7 MIRP[B]",
+ "7 MIRP[W]",
+ "7 MIRP[?]",
+ "8 MIRP[rG]",
+ "8 MIRP[rB]",
+ "8 MIRP[rW]",
+ "8 MIRP[r?]",
+ "8 MIRP[mG]",
+ "8 MIRP[mB]",
+ "8 MIRP[mW]",
+ "8 MIRP[m?]",
+ "9 MIRP[mrG]",
+ "9 MIRP[mrB]",
+ "9 MIRP[mrW]",
+ "9 MIRP[mr?]",
+
+ "8 MIRP[pG]",
+ "8 MIRP[pB]",
+ "8 MIRP[pW]",
+ "8 MIRP[p?]",
+ "9 MIRP[prG]",
+ "9 MIRP[prB]",
+ "9 MIRP[prW]",
+ "9 MIRP[pr?]",
+ "9 MIRP[pmG]",
+ "9 MIRP[pmB]",
+ "9 MIRP[pmW]",
+ "9 MIRP[pm?]",
+ "A MIRP[pmrG]",
+ "A MIRP[pmrB]",
+ "A MIRP[pmrW]",
+ "A MIRP[pmr?]"
+ };
+
+ return opcode_names[opcode];
+}
+
+static void
+sfnt_draw_debugger (struct sfnt_interpreter *interpreter)
+{
+ int x, y, i;
+ char buffer[80];
+ const char *name;
+ int opcode;
+
+ sprintf (buffer, "opcode:IP:depth: 0x%x:%d:%d",
+ interpreter->instructions[interpreter->IP],
+ interpreter->IP,
+ interpreter->call_depth);
+
+ /* Clear the window. */
+ XFillRectangle (display, window, background_gc,
+ 0, 0, 65535, 65535);
+
+ /* Draw some information about the opcode. */
+ XDrawString (display, window, point_gc, 0, 13, buffer,
+ strlen (buffer));
+
+ opcode = interpreter->instructions[interpreter->IP];
+
+ sprintf (buffer, "opcode: %s",
+ sfnt_name_instruction (opcode));
+
+ XDrawString (display, window, point_gc, 14, 27, buffer,
+ strlen (buffer));
+
+ if (interpreter->state.project
+ == sfnt_project_onto_x_axis_vector)
+ name = "X axis";
+ else if (interpreter->state.project
+ == sfnt_project_onto_y_axis_vector)
+ name = "Y axis";
+ else
+ name = "Any";
+
+ sprintf (buffer, "projection function: %s", name);
+
+ XDrawString (display, window, point_gc, 28, 42, buffer,
+ strlen (buffer));
+
+ /* Draw each point onto the window. */
+ for (i = 0; i < interpreter->glyph_zone->num_points; ++i)
+ {
+ x = interpreter->glyph_zone->x_current[i] / 16;
+ y = (200 - interpreter->glyph_zone->y_current[i] / 16);
+
+ XFillRectangle (display, window, point_gc, x, y, 4, 4);
+ }
+}
+
+static void
+sfnt_run_hook (struct sfnt_interpreter *interpreter)
+{
+ pid_t pid;
+ XEvent event;
+
+#ifdef TEST_BREAK_AFTER
+ static unsigned int instructions;
+
+ if (++instructions < TEST_BREAK_AFTER)
+ return;
+#endif
+
+ pid = fork ();
+
+ if (pid == 0)
+ {
+ sfnt_setup_debugger ();
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ switch (event.type)
+ {
+ case KeyPress:
+ XDestroyWindow (display, window);
+ XCloseDisplay (display);
+ exit (0);
+ break;
+
+ case Expose:
+ sfnt_draw_debugger (interpreter);
+ break;
+ }
+ }
+ }
+ else
+ {
+ while (waitpid (pid, NULL, 0) != pid && errno == EINTR)
+ /* Spin. */;
+ }
+}
+
+static struct sfnt_prep_table *exec_prep;
+static struct sfnt_fpgm_table *exec_fpgm;
+
+static const char *
+sfnt_identify_instruction (struct sfnt_interpreter *interpreter)
+{
+ static char buffer[256];
+ unsigned char *where;
+
+ where = interpreter->instructions + interpreter->IP;
+
+ if (exec_prep
+ && where >= exec_prep->instructions
+ && where < (exec_prep->instructions
+ + exec_prep->num_instructions))
+ {
+ sprintf (buffer, "prep+%td",
+ where - exec_prep->instructions);
+ return buffer;
+ }
+
+ if (exec_fpgm->instructions
+ && where >= exec_fpgm->instructions
+ && where < (exec_fpgm->instructions
+ + exec_fpgm->num_instructions))
+ {
+ sprintf (buffer, "fpgm+%td",
+ where - exec_fpgm->instructions);
+ return buffer;
+ }
+
+ sprintf (buffer, "IP+%td", where - interpreter->instructions);
+ return buffer;
+}
+
+static void
+sfnt_verbose (struct sfnt_interpreter *interpreter)
+{
+ struct sfnt_instructed_outline temp;
+ struct sfnt_glyph_outline *outline;
+ struct sfnt_raster *raster;
+ unsigned char opcode;
+ const char *name;
+ static unsigned int instructions;
+
+ /* Build a temporary outline containing the values of the
+ interpreter's glyph zone. */
+
+ if (interpreter->glyph_zone)
+ {
+ temp.num_points = interpreter->glyph_zone->num_points;
+ temp.num_contours = interpreter->glyph_zone->num_contours;
+ temp.contour_end_points = interpreter->glyph_zone->contour_end_points;
+ temp.x_points = interpreter->glyph_zone->x_current;
+ temp.y_points = interpreter->glyph_zone->y_current;
+ temp.flags = interpreter->glyph_zone->flags;
+
+ outline = sfnt_build_instructed_outline (&temp);
+
+ if (!outline)
+ return;
+
+ printf ("outline bounds: %g %g, %g %g\n",
+ sfnt_coerce_fixed (outline->xmin),
+ sfnt_coerce_fixed (outline->ymin),
+ sfnt_coerce_fixed (outline->xmax),
+ sfnt_coerce_fixed (outline->ymax));
+
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (raster)
+ sfnt_test_raster (raster, NULL, 0);
+
+ xfree (outline);
+ xfree (raster);
+ }
+
+ opcode = interpreter->instructions[interpreter->IP];
+ printf ("opcode, number of instructions: %s %u\n",
+ sfnt_name_instruction (opcode), instructions++);
+ printf ("instruction: %s\n",
+ sfnt_identify_instruction (interpreter));
+
+ if (interpreter->state.project
+ == sfnt_project_onto_x_axis_vector)
+ name = "X axis";
+ else if (interpreter->state.project
+ == sfnt_project_onto_y_axis_vector)
+ name = "Y axis";
+ else
+ name = "Any";
+
+ printf ("projection function: %s\n", name);
+
+ printf ("proj and free vecs: %d %d %d %d\n",
+ interpreter->state.projection_vector.x,
+ interpreter->state.projection_vector.y,
+ interpreter->state.freedom_vector.x,
+ interpreter->state.freedom_vector.y);
+}
+
+static void
+sfnt_push_hook (struct sfnt_interpreter *interpreter,
+ uint32_t value)
+{
+ int32_t alternate;
+
+ alternate = value;
+
+ fprintf (stderr, "--> %"PRIi32"\n", alternate);
+}
+
+static void
+sfnt_pop_hook (struct sfnt_interpreter *interpreter,
+ uint32_t value)
+{
+ int32_t alternate;
+
+ alternate = value;
+
+ fprintf (stderr, "<<- %"PRIi32"\n", alternate);
+}
+
+
+
+static void
+sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14)
+{
+ struct sfnt_uvs_context *context;
+ size_t i, j;
+ sfnt_glyph glyph;
+ sfnt_char c;
+ struct sfnt_nondefault_uvs_table *uvs;
+
+ context = sfnt_create_uvs_context (format14, fd);
+
+ /* Print each variation selector and its associated ranges. */
+
+ if (!context)
+ fprintf (stderr, "failed to read uvs data\n");
+ else
+ {
+ fprintf (stderr, "UVS context with %zu records and %zu tables\n",
+ context->num_records, context->nmemb);
+
+ for (i = 0; i < context->num_records; ++i)
+ {
+ if (!context->records[i].nondefault_uvs)
+ continue;
+
+ uvs = context->records[i].nondefault_uvs;
+
+ for (j = 0; j < uvs->num_uvs_mappings; ++j)
+ {
+ c = uvs->mappings[j].unicode_value;
+ glyph = sfnt_variation_glyph_for_char (uvs, c);
+
+ if (glyph != uvs->mappings[j].base_character_value)
+ abort ();
+
+ fprintf (stderr, " UVS: %"PRIx32" (%"PRIx32") -> %"PRIu32"\n",
+ c, context->records[i].selector, glyph);
+ }
+ }
+
+ sfnt_free_uvs_context (context);
+ }
+}
+
+
+
+/* Main entry point. */
+
+/* Simple tests that were used while developing this file. By the
+ time you are reading this, they probably no longer work.
+
+ Compile like so in this directory:
+
+ gcc -Demacs -I. -I. -I../lib -I../lib -MMD -MF deps/.d -MP
+ -fno-common -Wall -Warith-conversion -Wdate-time
+ -Wdisabled-optimization -Wdouble-promotion -Wduplicated-cond
+ -Wextra -Wformat-signedness -Winit-self -Winvalid-pch -Wlogical-op
+ -Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes
+ -Wnested-externs -Wnull-dereference -Wold-style-definition
+ -Wopenmp-simd -Wpacked -Wpointer-arith -Wstrict-prototypes
+ -Wsuggest-attribute=format -Wsuggest-final-methods
+ -Wsuggest-final-types -Wtrampolines -Wuninitialized
+ -Wunknown-pragmas -Wunused-macros -Wvariadic-macros
+ -Wvector-operation-performance -Wwrite-strings -Warray-bounds=2
+ -Wattribute-alias=2 -Wformat=2 -Wformat-truncation=2
+ -Wimplicit-fallthrough=5 -Wshift-overflow=2 -Wuse-after-free=3
+ -Wvla-larger-than=4031 -Wredundant-decls
+ -Wno-missing-field-initializers -Wno-override-init
+ -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter
+ -Wno-format-nonliteral -Wno-bidi-chars -g3 -O0 -DTEST sfnt.c -o
+ sfnt ../lib/libgnu.a -lX11 -lXrender
+
+ after gnulib has been built. Then, run ./sfnt
+ /path/to/font.ttf. */
+
+int
+main (int argc, char **argv)
+{
+ struct sfnt_offset_subtable *font;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_table *table;
+ int fd, i, j;
+ sfnt_char character;
+ struct sfnt_head_table *head;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_glyph *glyph;
+ sfnt_glyph code;
+ struct sfnt_test_dcontext dcontext;
+ struct sfnt_glyph_outline *outline;
+ struct timespec start, end, sub, sub1, sub2, sub3;
+ static struct sfnt_maxp_table *maxp;
+ struct sfnt_raster *raster;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyph_metrics metrics;
+ struct sfnt_name_table *name;
+ unsigned char *string;
+ struct sfnt_name_record record;
+ struct sfnt_meta_table *meta;
+ struct sfnt_ttc_header *ttc;
+ struct sfnt_interpreter *interpreter;
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fpgm_table *fpgm;
+ const char *trap;
+ struct sfnt_prep_table *prep;
+ struct sfnt_graphics_state state;
+ struct sfnt_instructed_outline *value;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_gvar_table *gvar;
+ struct sfnt_avar_table *avar;
+ struct sfnt_cvar_table *cvar;
+ sfnt_fixed scale;
+ char *fancy;
+ int *advances;
+ struct sfnt_raster **rasters;
+ size_t length;
+ char *axis_name;
+ struct sfnt_instance *instance;
+ struct sfnt_blend blend;
+ struct sfnt_metrics_distortion distortion;
+
+ if (argc < 2)
+ return 1;
+
+ instance = NULL;
+
+ if (!strcmp (argv[1], "--check-interpreter"))
+ {
+ interpreter = sfnt_make_test_interpreter ();
+
+ if (!interpreter)
+ abort ();
+
+ for (i = 0; i < ARRAYELTS (all_tests); ++i)
+ sfnt_run_interpreter_test (&all_tests[i], interpreter);
+
+ exit (0);
+ }
+
+ fd = open (argv[1], O_RDONLY);
+
+ if (fd < 1)
+ return 1;
+
+ ttc = NULL;
+
+ font = sfnt_read_table_directory (fd);
+
+ if (font == (struct sfnt_offset_subtable *) -1)
+ {
+ if (lseek (fd, 0, SEEK_SET) != 0)
+ return 1;
+
+ ttc = sfnt_read_ttc_header (fd);
+
+ if (!ttc)
+ return 1;
+
+ fprintf (stderr, "TrueType collection: %"PRIu32" fonts installed\n",
+ ttc->num_fonts);
+ fflush (stderr);
+
+ printf ("Which font? ");
+ if (scanf ("%d", &i) == EOF)
+ return 1;
+
+ if (i >= ttc->num_fonts || i < 0)
+ {
+ printf ("out of range\n");
+ return 1;
+ }
+
+ if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+ != ttc->offset_table[i])
+ return 1;
+
+ font = sfnt_read_table_directory (fd);
+ }
+
+ if (!font || font == (struct sfnt_offset_subtable *) -1)
+ {
+ close (fd);
+ return 1;
+ }
+
+ for (i = 0; i < font->num_tables; ++i)
+ fprintf (stderr, "Found new subtable with tag %"PRIx32
+ " at offset %"PRIu32"\n",
+ font->subtables[i].tag,
+ font->subtables[i].offset);
+
+ table = sfnt_read_cmap_table (fd, font, &subtables, &data);
+
+ if (!table)
+ {
+ close (fd);
+ xfree (font);
+ return 1;
+ }
+
+ fprintf (stderr, "number of subtables: %"PRIu16"\n",
+ table->num_subtables);
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
+ subtables[i].offset, (void *) data[i]);
+
+ if (data[i])
+ fprintf (stderr, " format: %"PRIu16"\n",
+ data[i]->format);
+ }
+
+ if (argc >= 3 && !strcmp (argv[2], "--check-variation-selectors"))
+ {
+ /* Look for a format 14 cmap table. */
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ if (data[i]->format == 14)
+ {
+ fprintf (stderr, "format 14 subtable found\n");
+ sfnt_test_uvs (fd, (struct sfnt_cmap_format_14 *) data[i]);
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+
+#define FANCY_PPEM 19
+#define EASY_PPEM 19
+
+ interpreter = NULL;
+ head = sfnt_read_head_table (fd, font);
+ hhea = sfnt_read_hhea_table (fd, font);
+ glyf = sfnt_read_glyf_table (fd, font);
+ maxp = sfnt_read_maxp_table (fd, font);
+ name = sfnt_read_name_table (fd, font);
+ meta = sfnt_read_meta_table (fd, font);
+ cvt = sfnt_read_cvt_table (fd, font);
+ fpgm = sfnt_read_fpgm_table (fd, font);
+ prep = sfnt_read_prep_table (fd, font);
+ fvar = sfnt_read_fvar_table (fd, font);
+ gvar = sfnt_read_gvar_table (fd, font);
+ avar = sfnt_read_avar_table (fd, font);
+ cvar = NULL;
+ hmtx = NULL;
+
+ if (fvar && cvt)
+ cvar = sfnt_read_cvar_table (fd, font, fvar, cvt);
+
+ if (cvar)
+ fprintf (stderr, "cvar table found\n");
+
+ exec_prep = prep;
+ exec_fpgm = fpgm;
+ fancy = getenv ("SFNT_FANCY_TEST");
+
+ loca_long = NULL;
+ loca_short = NULL;
+
+ if (fvar)
+ {
+ fprintf (stderr, "FVAR table found!\n"
+ "version: %"PRIu16".%"PRIu16"\n"
+ "axis_count: %"PRIu16"\n"
+ "axis_size: %"PRIu16"\n"
+ "instance_count: %"PRIu16"\n"
+ "instance_size: %"PRIu16"\n",
+ fvar->major_version,
+ fvar->minor_version,
+ fvar->axis_count,
+ fvar->axis_size,
+ fvar->instance_count,
+ fvar->instance_size);
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ {
+ if (name)
+ {
+ axis_name
+ = (char *) sfnt_find_name (name, fvar->axis[i].name_id,
+ &record);
+
+ if (axis_name)
+ fprintf (stderr, "axis no: %d; name: %.*s\n",
+ i, record.length, axis_name);
+ }
+
+ fprintf (stderr, " axis: %"PRIx32" %g %g %g\n",
+ fvar->axis[i].axis_tag,
+ sfnt_coerce_fixed (fvar->axis[i].min_value),
+ sfnt_coerce_fixed (fvar->axis[i].default_value),
+ sfnt_coerce_fixed (fvar->axis[i].max_value));
+ }
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ if (name)
+ {
+ axis_name
+ = (char *) sfnt_find_name (name, fvar->instance[i].name_id,
+ &record);
+
+ if (axis_name)
+ fprintf (stderr, "instance no: %d; name: %.*s\n",
+ i, record.length, axis_name);
+ }
+ }
+
+ if (fvar->instance_count > 1)
+ {
+ printf ("instance? ");
+
+ if (scanf ("%d", &i) == EOF)
+ goto free_lab;
+
+ if (i >= fvar->instance_count)
+ goto free_lab;
+
+ if (i >= 0)
+ instance = &fvar->instance[i];
+ }
+ }
+
+ if (gvar)
+ fprintf (stderr, "gvar table found\n");
+
+ if (avar)
+ {
+ fprintf (stderr, "avar table found\n");
+
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ fprintf (stderr, "axis: %d, %"PRIu16" pairs\n",
+ i, avar->segments[i].pair_count);
+
+ for (j = 0; j < avar->segments[i].pair_count; ++j)
+ fprintf (stderr, "pair: %g, %g\n",
+ (avar->segments[i].correspondence[j].from_coord
+ / 16384.0),
+ (avar->segments[i].correspondence[j].to_coord
+ / 16384.0));
+ }
+ }
+
+ memset (&blend, 0, sizeof blend);
+
+ if (instance && gvar)
+ {
+ sfnt_init_blend (&blend, fvar, gvar, avar,
+ cvar);
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ blend.coords[i] = instance->coords[i];
+
+ sfnt_normalize_blend (&blend);
+ }
+
+ if (fancy)
+ {
+ length = strlen (fancy);
+ scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em);
+
+ if (hhea && maxp)
+ hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+ if (!maxp || !head || !prep || !hmtx || !hhea
+ || table->num_subtables < 1)
+ exit (1);
+
+ if (head->index_to_loc_format)
+ {
+ loca_long = sfnt_read_loca_table_long (fd, font);
+ if (!loca_long)
+ return 1;
+
+ fprintf (stderr, "long loca table has %zu glyphs\n",
+ loca_long->num_offsets);
+ }
+ else
+ {
+ loca_short = sfnt_read_loca_table_short (fd, font);
+ if (!loca_short)
+ return 1;
+
+ fprintf (stderr, "short loca table has %zu glyphs\n",
+ loca_short->num_offsets);
+ }
+
+ interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar,
+ FANCY_PPEM, FANCY_PPEM);
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
+
+ if (!interpreter)
+ exit (1);
+
+ if (fpgm)
+ {
+ fprintf (stderr, "interpreting the font program, with"
+ " %zu instructions\n", fpgm->num_instructions);
+ trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ if (prep)
+ {
+ fprintf (stderr, "interpreting the control value program, with"
+ " %zu instructions\n", prep->num_instructions);
+ trap = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ state = interpreter->state;
+
+ advances = alloca (sizeof *advances * length);
+ rasters = alloca (sizeof *rasters * length);
+
+ for (i = 0; i < length; ++i)
+ {
+ code = sfnt_lookup_glyph (fancy[i], data[0]);
+
+ if (!code)
+ exit (2);
+
+ glyph = sfnt_read_glyph (code, glyf, loca_short,
+ loca_long);
+
+ if (!glyph || !glyph->simple)
+ exit (3);
+
+ if (instance && gvar)
+ sfnt_vary_simple_glyph (&blend, code, glyph,
+ &distortion);
+
+ if (sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ exit (4);
+
+ interpreter->state = state;
+ trap = sfnt_interpret_simple_glyph (glyph, interpreter,
+ &metrics, &value);
+
+ if (trap)
+ {
+ fprintf (stderr, "*TRAP*: %s\n", trap);
+ exit (5);
+ }
+
+ outline = sfnt_build_instructed_outline (value);
+
+ if (!outline)
+ exit (6);
+
+ xfree (value);
+
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (!raster)
+ exit (7);
+
+ xfree (outline);
+
+ rasters[i] = raster;
+ advances[i] = (sfnt_mul_fixed (metrics.advance, scale)
+ + sfnt_mul_fixed (distortion.advance, scale));
+ }
+
+ sfnt_x_raster (rasters, advances, length, hhea, scale);
+ exit (0);
+ }
+
+ if (hhea && maxp)
+ hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+ if (maxp)
+ fprintf (stderr, "maxp says num glyphs is %"PRIu16"\n",
+ maxp->num_glyphs);
+
+ if (name)
+ {
+ fprintf (stderr, "name table of format: %"PRIu16" count: %"
+ PRIu16"\n", name->format, name->count);
+
+ string = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+ &record);
+
+ if (string)
+ fprintf (stderr, "FONT_FAMILY: %"PRIu16", %"PRIu16"\n",
+ record.platform_id, record.length);
+ }
+
+ if (meta)
+ {
+ 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;
+
+ if (head)
+ {
+ fprintf (stderr, "HEAD table:\n"
+ "version: \t\t\t%g\n"
+ "revision: \t\t\t%g\n"
+ "checksum_adjustment: \t\t%"PRIu32"\n"
+ "magic: \t\t\t\t%"PRIx32"\n"
+ "flags: \t\t\t\t%"PRIx16"\n"
+ "units_per_em: \t\t\t%"PRIu16"\n"
+ "xmin, ymin, xmax, ymax: \t%d, %d, %d, %d\n"
+ "mac_style: \t\t\t%"PRIx16"\n"
+ "lowest_rec_ppem: \t\t%"PRIu16"\n"
+ "font_direction_hint: \t\t%"PRIi16"\n"
+ "index_to_loc_format: \t\t%"PRIi16"\n"
+ "glyph_data_format: \t\t%"PRIi16"\n",
+ sfnt_coerce_fixed (head->version),
+ sfnt_coerce_fixed (head->revision),
+ head->checksum_adjustment,
+ head->magic,
+ head->flags,
+ head->units_per_em,
+ (int) head->xmin,
+ (int) head->ymin,
+ (int) head->xmax,
+ (int) head->ymax,
+ head->mac_style,
+ head->lowest_rec_ppem,
+ head->font_direction_hint,
+ head->index_to_loc_format,
+ head->glyph_data_format);
+
+ if (head->index_to_loc_format)
+ {
+ loca_long = sfnt_read_loca_table_long (fd, font);
+ if (!loca_long)
+ return 1;
+
+ fprintf (stderr, "long loca table has %zu glyphs\n",
+ loca_long->num_offsets);
+ }
+ else
+ {
+ loca_short = sfnt_read_loca_table_short (fd, font);
+ if (!loca_short)
+ return 1;
+
+ fprintf (stderr, "short loca table has %zu glyphs\n",
+ loca_short->num_offsets);
+ }
+ }
+
+ if (hhea)
+ fprintf (stderr, "HHEA table:\n"
+ "version: \t\t\t%g\n"
+ "ascent, descent: \t\t%d %d\n"
+ "line_gap: \t\t\t%d\n"
+ "advance_width_max: \t\t%u\n"
+ "min_lsb: \t\t\t%d\n"
+ "min_rsb: \t\t\t%d\n"
+ "caret_srise: \t\t\t%d\n"
+ "caret_srun: \t\t\t%d\n",
+ sfnt_coerce_fixed (hhea->version),
+ (int) hhea->ascent,
+ (int) hhea->descent,
+ (int) hhea->line_gap,
+ (unsigned int) hhea->advance_width_max,
+ (int) hhea->min_left_side_bearing,
+ (int) hhea->min_right_side_bearing,
+ (int) hhea->caret_slope_rise,
+ (int) hhea->caret_slope_run);
+
+ if (head && maxp && maxp->version >= 0x00010000)
+ {
+ fprintf (stderr, "creating interpreter\n"
+ "the size of the stack is %"PRIu16"\n"
+ "the size of the twilight zone is %"PRIu16"\n"
+ "the size of the storage area is %"PRIu16"\n"
+ "there are at most %"PRIu16" idefs\n"
+ "there are at most %"PRIu16" fdefs\n"
+ "the cvt is %zu fwords in length\n",
+ maxp->max_stack_elements,
+ maxp->max_twilight_points,
+ maxp->max_storage,
+ maxp->max_instruction_defs,
+ maxp->max_function_defs,
+ cvt ? cvt->num_elements : 0ul);
+
+ interpreter = sfnt_make_interpreter (maxp, cvt, head,
+ fvar, FANCY_PPEM,
+ FANCY_PPEM);
+ state = interpreter->state;
+
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
+
+ if (fpgm)
+ {
+ fprintf (stderr, "interpreting the font program, with"
+ " %zu instructions\n", fpgm->num_instructions);
+
+ trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ if (prep)
+ {
+ fprintf (stderr, "interpreting the control value program, with"
+ " %zu instructions\n", prep->num_instructions);
+
+ trap = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+ }
+
+ while (true)
+ {
+ printf ("table, character? ");
+
+ if (scanf ("%d %"SCNu32"", &i, &character) == EOF)
+ break;
+
+ if (i < 0 || i >= table->num_subtables)
+ {
+ printf ("table out of range\n");
+ continue;
+ }
+
+ if (!data[i])
+ {
+ printf ("table not present\n");
+ continue;
+ }
+
+ code = sfnt_lookup_glyph (character, data[i]);
+ printf ("glyph is %"PRIu32"\n", code);
+
+ if ((loca_long || loca_short) && glyf)
+ {
+ scale = sfnt_div_fixed (EASY_PPEM, head->units_per_em);
+ glyph = sfnt_read_glyph (code, glyf, loca_short,
+ loca_long);
+
+ if (glyph)
+ {
+ printf ("glyph is: %s\n",
+ glyph->simple ? "simple" : "compound");
+
+ dcontext.glyf = glyf;
+ dcontext.loca_short = loca_short;
+ dcontext.loca_long = loca_long;
+
+ if (instance && gvar)
+ dcontext.blend = &blend;
+ else
+ dcontext.blend = NULL;
+
+ if (glyph->simple && instance && gvar)
+ {
+ printf ("applying variations to simple glyph...\n");
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ if (sfnt_vary_simple_glyph (&blend, code, glyph,
+ &distortion))
+ printf ("variation failed!\n");
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub = timespec_sub (end, start);
+
+ printf ("time spent varying: %lld sec %ld nsec\n",
+ (long long) sub.tv_sec, sub.tv_nsec);
+ printf ("distortions: %"PRIi16", %"PRIi16"\n",
+ distortion.origin, distortion.advance);
+ }
+ else if (instance && gvar)
+ {
+ printf ("applying variations to compound glyph...\n");
+
+ if (sfnt_vary_compound_glyph (&blend, code, glyph,
+ &distortion))
+ printf ("variation failed!\n");
+ }
+
+ if (sfnt_decompose_glyph (glyph, sfnt_test_move_to,
+ sfnt_test_line_to,
+ sfnt_test_curve_to,
+ sfnt_test_get_glyph,
+ sfnt_test_free_glyph,
+ &dcontext))
+ printf ("decomposition failure\n");
+
+ if (sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ {
+ printf ("metrics lookup failure");
+ memset (&metrics, 0, sizeof metrics);
+ }
+
+ /* Time this important bit. */
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &metrics,
+ sfnt_test_get_glyph,
+ sfnt_test_free_glyph,
+ &dcontext);
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub = timespec_sub (end, start);
+ memset (&sub1, 0, sizeof sub1);
+
+ if (outline)
+ {
+ fprintf (stderr, "outline origin, rbearing: %"
+ PRIi32" %"PRIi32"\n",
+ outline->origin,
+ outline->ymax - outline->origin);
+ sfnt_test_max = outline->ymax - outline->ymin;
+
+ for (i = 0; i < outline->outline_used; i++)
+ printf ("ctx.%s (%g, %g) /* %g, %g */\n",
+ ((outline->outline[i].flags
+ & SFNT_GLYPH_OUTLINE_LINETO)
+ ? "lineTo" : "moveTo"),
+ sfnt_coerce_fixed (outline->outline[i].x
+ - outline->xmin),
+ sfnt_coerce_fixed (sfnt_test_max
+ - (outline->outline[i].y
+ - outline->ymin)),
+ sfnt_coerce_fixed (outline->outline[i].x
+ - outline->xmin),
+ sfnt_coerce_fixed (outline->outline[i].y
+ - outline->ymin));
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ sfnt_build_outline_edges (outline, sfnt_test_edge_ignore,
+ NULL);
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub1 = timespec_sub (end, start);
+
+ sfnt_build_outline_edges (outline, sfnt_test_edge,
+ NULL);
+
+ raster = NULL;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+
+ for (i = 0; i < 120; ++i)
+ {
+ xfree (raster);
+ raster = sfnt_raster_glyph_outline (outline);
+ }
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub2 = timespec_sub (end, start);
+
+ /* Print out the raster. */
+ sfnt_test_raster (raster, hhea, scale);
+ printf ("raster offsets: %d, %d\n",
+ raster->offx, raster->offy);
+
+ xfree (raster);
+
+ printf ("outline bounds: %g %g, %g %g\n",
+ sfnt_coerce_fixed (outline->xmin),
+ sfnt_coerce_fixed (outline->ymin),
+ sfnt_coerce_fixed (outline->xmax),
+ sfnt_coerce_fixed (outline->ymax));
+ }
+
+ if (hmtx && head)
+ {
+ if (!sfnt_lookup_glyph_metrics (code, EASY_PPEM,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ printf ("lbearing, advance: %g, %g\n",
+ sfnt_coerce_fixed (metrics.lbearing),
+ sfnt_coerce_fixed (metrics.advance));
+
+ if (interpreter)
+ {
+ if (getenv ("SFNT_DEBUG"))
+ interpreter->run_hook = sfnt_run_hook;
+ else if (getenv ("SFNT_VERBOSE"))
+ {
+ interpreter->run_hook = sfnt_verbose;
+ interpreter->push_hook = sfnt_push_hook;
+ interpreter->pop_hook = sfnt_pop_hook;
+ }
+
+ if (!sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ {
+ printf ("interpreting glyph\n");
+ interpreter->state = state;
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ if (glyph->simple)
+ trap
+ = sfnt_interpret_simple_glyph (glyph,
+ interpreter,
+ &metrics,
+ &value);
+ else
+#define GG sfnt_test_get_glyph
+#define FG sfnt_test_free_glyph
+ trap
+ = sfnt_interpret_compound_glyph (glyph,
+ interpreter,
+ &state,
+ GG, FG,
+ hmtx, hhea,
+ maxp,
+ &metrics,
+ &dcontext,
+ &value);
+#undef GG
+#undef FG
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub3 = timespec_sub (end, start);
+
+ if (trap)
+ printf ("**TRAP**: %s\n", trap);
+ else
+ {
+ printf ("rasterizing instructed outline\n");
+ if (outline)
+ xfree (outline);
+ outline = sfnt_build_instructed_outline (value);
+ xfree (value);
+
+ if (outline)
+ {
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (raster)
+ {
+ sfnt_test_raster (raster, hhea, scale);
+ printf ("raster offsets: %d, %d\n",
+ raster->offx, raster->offy);
+ xfree (raster);
+ }
+ }
+ }
+
+ fprintf (stderr, "execution time: %lld sec %ld nse"
+ "c\n",
+ (long long) sub3.tv_sec, sub3.tv_nsec);
+ }
+
+ interpreter->run_hook = NULL;
+ }
+ }
+
+ printf ("time spent outlining: %lld sec %ld nsec\n",
+ (long long) sub.tv_sec, sub.tv_nsec);
+ printf ("time spent building edges: %lld sec %ld nsec\n",
+ (long long) sub1.tv_sec, sub1.tv_nsec);
+ printf ("time spent rasterizing: %lld sec %ld nsec\n",
+ (long long) sub2.tv_sec / 120, sub2.tv_nsec / 120);
+
+ xfree (outline);
+ }
+
+ sfnt_free_glyph (glyph);
+ }
+ }
+
+ free_lab:
+
+ xfree (font);
+
+ for (i = 0; i < table->num_subtables; ++i)
+ xfree (data[i]);
+
+ if (instance && gvar)
+ sfnt_free_blend (&blend);
+
+ xfree (table);
+ xfree (data);
+ xfree (subtables);
+ xfree (head);
+ xfree (hhea);
+ xfree (loca_long);
+ xfree (loca_short);
+ xfree (glyf);
+ xfree (maxp);
+ xfree (hmtx);
+ xfree (name);
+ xfree (meta);
+ xfree (ttc);
+ xfree (cvt);
+ xfree (fpgm);
+ xfree (interpreter);
+ xfree (prep);
+ xfree (fvar);
+ xfree (gvar);
+ xfree (avar);
+ xfree (cvar);
+
+ return 0;
+}
+
+#endif
diff --git a/src/sfnt.h b/src/sfnt.h
new file mode 100644
index 00000000000..365595fa37d
--- /dev/null
+++ b/src/sfnt.h
@@ -0,0 +1,1994 @@
+/* sfnt format font support for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _SFNT_H_
+#define _SFNT_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <setjmp.h>
+
+#include <sys/types.h>
+
+
+
+/* Container structure and enumerator definitions. */
+
+/* The sfnt container format is organized into different tables, such
+ as ``cmap'' or ``glyf''. Each of these tables has a specific
+ format and use. These are all the tables known to Emacs. */
+
+enum sfnt_table
+ {
+ SFNT_TABLE_CMAP,
+ SFNT_TABLE_GLYF,
+ SFNT_TABLE_HEAD,
+ SFNT_TABLE_HHEA,
+ SFNT_TABLE_HMTX,
+ SFNT_TABLE_LOCA,
+ SFNT_TABLE_MAXP,
+ SFNT_TABLE_NAME,
+ SFNT_TABLE_META,
+ SFNT_TABLE_CVT ,
+ SFNT_TABLE_FPGM,
+ SFNT_TABLE_PREP,
+ SFNT_TABLE_FVAR,
+ SFNT_TABLE_GVAR,
+ SFNT_TABLE_CVAR,
+ SFNT_TABLE_AVAR,
+ };
+
+#define SFNT_ENDOF(type, field, type1) \
+ ((size_t) offsetof (type, field) + sizeof (type1))
+
+/* Each of these structures must be aligned so that no compiler will
+ ever generate padding bytes on platforms where the alignment
+ requirements for uint32_t and uint16_t are no larger than 4 and 2
+ bytes respectively.
+
+ Pointer types are assumed to impose an alignmnent requirement no
+ less than that of uint32_t.
+
+ If a table has more than one kind of variable-length subtable array
+ at the end, make sure to pad subsequent subtables
+ appropriately. */
+
+struct sfnt_offset_subtable
+{
+ /* The scaler type. */
+ uint32_t scaler_type;
+
+ /* The number of tables. */
+ uint16_t num_tables;
+
+ /* (Maximum power of 2 <= numTables) * 16. */
+ uint16_t search_range;
+
+ /* log2 (maximum power of 2 <= numTables) */
+ uint16_t entry_selector;
+
+ /* numTables * 16 - searchRange. */
+ uint16_t range_shift;
+
+ /* Variable length data. */
+ struct sfnt_table_directory *subtables;
+};
+
+/* The table directory. Follows the offset subtable, with one for
+ each table. */
+
+struct sfnt_table_directory
+{
+ /* 4-byte identifier for each table. See sfnt_table_names. */
+ uint32_t tag;
+
+ /* Table checksum. */
+ uint32_t checksum;
+
+ /* Offset from the start of the file. */
+ uint32_t offset;
+
+ /* Length of the table in bytes, not subject to padding. */
+ uint32_t length;
+};
+
+enum sfnt_scaler_type
+ {
+ SFNT_SCALER_TRUE = 0x74727565,
+ SFNT_SCALER_VER1 = 0x00010000,
+ SFNT_SCALER_TYP1 = 0x74797031,
+ SFNT_SCALER_OTTO = 0x4F54544F,
+ };
+
+typedef int32_t sfnt_fixed;
+typedef int16_t sfnt_fword;
+typedef uint16_t sfnt_ufword;
+
+#define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0)
+
+typedef unsigned int sfnt_glyph;
+typedef unsigned int sfnt_char;
+
+struct sfnt_head_table
+{
+ /* The version. This is a 16.16 fixed point number. */
+ sfnt_fixed version;
+
+ /* The revision. */
+ sfnt_fixed revision;
+
+ /* Checksum adjustment. */
+ uint32_t checksum_adjustment;
+
+ /* Magic number, should be 0x5F0F3CF5. */
+ uint32_t magic;
+
+ /* Flags for the font. */
+ uint16_t flags;
+
+ /* Units per em. */
+ uint16_t units_per_em;
+
+ /* Time of creation. */
+ uint32_t created_high, created_low;
+
+ /* Time of modification. */
+ uint32_t modified_high, modified_low;
+
+ /* Minimum bounds. */
+ sfnt_fword xmin, ymin, xmax, ymax;
+
+ /* Mac specific stuff. */
+ uint16_t mac_style;
+
+ /* Smallest readable size in pixels. */
+ uint16_t lowest_rec_ppem;
+
+ /* Font direction hint. */
+ int16_t font_direction_hint;
+
+ /* Index to loc format. 0 for short offsets, 1 for long. */
+ int16_t index_to_loc_format;
+
+ /* Unused. */
+ int16_t glyph_data_format;
+};
+
+struct sfnt_hhea_table
+{
+ /* The version. This is a 16.16 fixed point number. */
+ sfnt_fixed version;
+
+ /* The maximum ascent and descent values for this font. */
+ sfnt_fword ascent, descent;
+
+ /* The typographic line gap. */
+ sfnt_fword line_gap;
+
+ /* The maximum advance width. */
+ sfnt_ufword advance_width_max;
+
+ /* The minimum bearings on either side. */
+ sfnt_fword min_left_side_bearing, min_right_side_bearing;
+
+ /* The maximum extent. */
+ sfnt_fword x_max_extent;
+
+ /* Caret slope. */
+ int16_t caret_slope_rise, caret_slope_run;
+
+ /* Caret offset for non slanted fonts. */
+ sfnt_fword caret_offset;
+
+ /* Reserved values. */
+ int16_t reserved1, reserved2, reserved3, reserved4;
+
+ /* Should always be zero. */
+ int16_t metric_data_format;
+
+ /* Number of advanced widths in metrics table. */
+ uint16_t num_of_long_hor_metrics;
+};
+
+struct sfnt_cmap_table
+{
+ /* Should be zero. */
+ uint16_t version;
+
+ /* Number of subtables. */
+ uint16_t num_subtables;
+};
+
+enum sfnt_platform_id
+ {
+ SFNT_PLATFORM_UNICODE = 0,
+ SFNT_PLATFORM_MACINTOSH = 1,
+ SFNT_PLATFORM_RESERVED = 2,
+ SFNT_PLATFORM_MICROSOFT = 3,
+ };
+
+enum sfnt_unicode_platform_specific_id
+ {
+ SFNT_UNICODE_1_0 = 0,
+ SFNT_UNICODE_1_1 = 1,
+ SFNT_UNICODE_ISO_10646_1993 = 2,
+ SFNT_UNICODE_2_0_BMP = 3,
+ SFNT_UNICODE_2_0 = 4,
+ SFNT_UNICODE_VARIATION_SEQUENCES = 5,
+ SFNT_UNICODE_LAST_RESORT = 6,
+ };
+
+enum sfnt_macintosh_platform_specific_id
+ {
+ SFNT_MACINTOSH_ROMAN = 0,
+ SFNT_MACINTOSH_JAPANESE = 1,
+ SFNT_MACINTOSH_TRADITIONAL_CHINESE = 2,
+ SFNT_MACINTOSH_KOREAN = 3,
+ SFNT_MACINTOSH_ARABIC = 4,
+ SFNT_MACINTOSH_HEBREW = 5,
+ SFNT_MACINTOSH_GREEK = 6,
+ SFNT_MACINTOSH_RUSSIAN = 7,
+ SFNT_MACINTOSH_RSYMBOL = 8,
+ SFNT_MACINTOSH_DEVANGARI = 9,
+ SFNT_MACINTOSH_GURMUKHI = 10,
+ SFNT_MACINTOSH_GUJARATI = 11,
+ SFNT_MACINTOSH_ORIYA = 12,
+ SFNT_MACINTOSH_BENGALI = 13,
+ SFNT_MACINTOSH_TAMIL = 14,
+ SFNT_MACINTOSH_TELUGU = 15,
+ SFNT_MACINTOSH_KANNADA = 16,
+ SFNT_MACINTOSH_MALAYALAM = 17,
+ SFNT_MACINTOSH_SINHALESE = 18,
+ SFNT_MACINTOSH_BURMESE = 19,
+ SFNT_MACINTOSH_KHMER = 20,
+ SFNT_MACINTOSH_THAI = 21,
+ SFNT_MACINTOSH_LAOTIAN = 22,
+ SFNT_MACINTOSH_GEORGIAN = 23,
+ SFNT_MACINTOSH_ARMENIAN = 24,
+ SFNT_MACINTOSH_SIMPLIFIED_CHINESE = 25,
+ SFNT_MACINTOSH_TIBETIAN = 26,
+ SFNT_MACINTOSH_MONGOLIAN = 27,
+ SFNT_MACINTOSH_GEEZ = 28,
+ SFNT_MACINTOSH_SLAVIC = 29,
+ SFNT_MACINTOSH_VIETNAMESE = 30,
+ SFNT_MACINTOSH_SINDHI = 31,
+ SFNT_MACINTOSH_UNINTERPRETED = 32,
+ };
+
+enum sfnt_microsoft_platform_specific_id
+ {
+ SFNT_MICROSOFT_SYMBOL = 0,
+ SFNT_MICROSOFT_UNICODE_BMP = 1,
+ SFNT_MICROSOFT_SHIFT_JIS = 2,
+ SFNT_MICROSOFT_PRC = 3,
+ SFNT_MICROSOFT_BIG_FIVE = 4,
+ SFNT_MICROSOFT_WANSUNG = 5,
+ SFNT_MICROSOFT_JOHAB = 6,
+ SFNT_MICROSOFT_UNICODE_UCS_4 = 10,
+ };
+
+struct sfnt_cmap_encoding_subtable
+{
+ /* The platform ID. */
+ uint16_t platform_id;
+
+ /* Platform specific ID. */
+ uint16_t platform_specific_id;
+
+ /* Mapping table offset. */
+ uint32_t offset;
+};
+
+struct sfnt_cmap_encoding_subtable_data
+{
+ /* Format and possibly the length in bytes. */
+ uint16_t format, length;
+};
+
+struct sfnt_cmap_format_0
+{
+ /* Format, set to 0. */
+ uint16_t format;
+
+ /* Length in bytes. Should be 262. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* Character code to glyph index map. */
+ uint8_t glyph_index_array[256];
+};
+
+struct sfnt_cmap_format_2_subheader
+{
+ uint16_t first_code;
+ uint16_t entry_count;
+ int16_t id_delta;
+ uint16_t id_range_offset;
+};
+
+struct sfnt_cmap_format_2
+{
+ /* Format, set to 2. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* Array mapping high bytes to subheaders. */
+ uint16_t sub_header_keys[256];
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_2_subheader *subheaders;
+ uint16_t *glyph_index_array;
+ uint16_t num_glyphs;
+};
+
+struct sfnt_cmap_format_4
+{
+ /* Format, set to 4. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* 2 * seg_count. */
+ uint16_t seg_count_x2;
+
+ /* 2 * (2**FLOOR(log2(segCount))) */
+ uint16_t search_range;
+
+ /* log2(searchRange/2) */
+ uint16_t entry_selector;
+
+ /* Variable-length data. */
+ uint16_t *end_code;
+ uint16_t *reserved_pad;
+ uint16_t *start_code;
+ int16_t *id_delta;
+ int16_t *id_range_offset;
+ uint16_t *glyph_index_array;
+
+ /* The number of elements in glyph_index_array. */
+ size_t glyph_index_size;
+};
+
+struct sfnt_cmap_format_6
+{
+ /* Format, set to 6. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* First character code in subrange. */
+ uint16_t first_code;
+
+ /* Number of character codes. */
+ uint16_t entry_count;
+
+ /* Variable-length data. */
+ uint16_t *glyph_index_array;
+};
+
+struct sfnt_cmap_format_8_or_12_group
+{
+ uint32_t start_char_code;
+ uint32_t end_char_code;
+ uint32_t start_glyph_code;
+};
+
+struct sfnt_cmap_format_8
+{
+ /* Format, set to 8. */
+ uint16_t format;
+
+ /* Reserved. */
+ uint16_t reserved;
+
+ /* Length in bytes. */
+ uint32_t length;
+
+ /* Language code. */
+ uint32_t language;
+
+ /* Tightly packed array of bits (8K bytes total) indicating whether
+ the particular 16-bit (index) value is the start of a 32-bit
+ character code. */
+ uint8_t is32[65536];
+
+ /* Number of groups. */
+ uint32_t num_groups;
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+/* cmap formats 10, 13 unsupported. */
+
+struct sfnt_cmap_format_12
+{
+ /* Format, set to 12. */
+ uint16_t format;
+
+ /* Reserved. */
+ uint16_t reserved;
+
+ /* Length in bytes. */
+ uint32_t length;
+
+ /* Language code. */
+ uint32_t language;
+
+ /* Number of groups. */
+ uint32_t num_groups;
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+struct sfnt_cmap_format_14
+{
+ /* Format, set to 14. */
+ uint16_t format;
+
+ /* The length of the table in bytes. */
+ uint32_t length;
+
+ /* Number of variation selector records. */
+ uint16_t num_var_selector_records;
+
+ /* The offset of this table in the font file. */
+ off_t offset;
+
+ /* Variable length data. */
+ struct sfnt_variation_selector_record *records;
+};
+
+struct sfnt_variation_selector_record
+{
+ /* 24-bit unsigned variation selector. */
+ unsigned int var_selector;
+
+ /* Offset to default UVS table. */
+ uint32_t default_uvs_offset;
+
+ /* Offset to non-default UVS table. */
+ uint32_t nondefault_uvs_offset;
+};
+
+struct sfnt_maxp_table
+{
+ /* Table version. */
+ sfnt_fixed version;
+
+ /* The number of glyphs in this font - 1. Set at version 0.5 or
+ later. */
+ uint16_t num_glyphs;
+
+ /* These fields are only set in version 1.0 or later. Maximum
+ points in a non-composite glyph. */
+ uint16_t max_points;
+
+ /* Maximum contours in a non-composite glyph. */
+ uint16_t max_contours;
+
+ /* Maximum points in a composite glyph. */
+ uint16_t max_composite_points;
+
+ /* Maximum contours in a composite glyph. */
+ uint16_t max_composite_contours;
+
+ /* 1 if instructions do not use the twilight zone (Z0), or 2 if
+ instructions do use Z0; should be set to 2 in most cases. */
+ uint16_t max_zones;
+
+ /* Maximum points used in Z0. */
+ uint16_t max_twilight_points;
+
+ /* Number of Storage Area locations. */
+ uint16_t max_storage;
+
+ /* Number of FDEFs, equal to the highest function number + 1. */
+ uint16_t max_function_defs;
+
+ /* Number of IDEFs. */
+ uint16_t max_instruction_defs;
+
+ /* Maximum stack depth across Font Program ('fpgm' table), CVT
+ Program ('prep' table) and all glyph instructions (in the 'glyf'
+ table). */
+ uint16_t max_stack_elements;
+
+ /* Maximum byte count for glyph instructions. */
+ uint16_t max_size_of_instructions;
+
+ /* Maximum number of components referenced at ``top level'' for any
+ composite glyph. */
+ uint16_t max_component_elements;
+
+ /* Maximum levels of recursion; 1 for simple components. */
+ uint16_t max_component_depth;
+};
+
+struct sfnt_loca_table_short
+{
+ /* Offsets to glyph data divided by two. */
+ uint16_t *offsets;
+
+ /* Size of the offsets list. */
+ size_t num_offsets;
+};
+
+struct sfnt_loca_table_long
+{
+ /* Offsets to glyph data. */
+ uint32_t *offsets;
+
+ /* Size of the offsets list. */
+ size_t num_offsets;
+};
+
+struct sfnt_glyf_table
+{
+ /* Size of the glyph data. */
+ size_t size;
+
+ /* Pointer to possibly unaligned glyph data. */
+ unsigned char *glyphs;
+
+ /* Pointer to the start of the mapping.
+ Only initialized if this table was mmapped. */
+ unsigned char *start;
+};
+
+struct sfnt_simple_glyph
+{
+ /* The total number of points in this glyph. */
+ size_t number_of_points;
+
+ /* Array containing the last points of each contour. */
+ uint16_t *restrict end_pts_of_contours;
+
+ /* Total number of bytes needed for instructions. */
+ uint16_t instruction_length;
+
+ /* Instruction data. */
+ uint8_t *restrict instructions;
+
+ /* Array of flags. */
+ uint8_t *restrict flags;
+
+ /* Array of X coordinates. */
+ int16_t *restrict x_coordinates;
+
+ /* Array of Y coordinates. */
+ int16_t *restrict y_coordinates;
+
+ /* Pointer to the end of that array. */
+ int16_t *restrict y_coordinates_end;
+};
+
+struct sfnt_compound_glyph_component
+{
+ /* Compound glyph flags. */
+ uint16_t flags;
+
+ /* Component glyph index. */
+ uint16_t glyph_index;
+
+ /* X-offset for component or point number; type depends on bits 0
+ and 1 in component flags. */
+ union {
+ uint8_t a;
+ int8_t b;
+ uint16_t c;
+ int16_t d;
+ } argument1;
+
+ /* Y-offset for component or point number; type depends on bits 0
+ and 1 in component flags. */
+ union {
+ uint8_t a;
+ int8_t b;
+ uint16_t c;
+ int16_t d;
+ } argument2;
+
+ /* Various scale formats. */
+ union {
+ uint16_t scale;
+ struct {
+ uint16_t xscale;
+ uint16_t yscale;
+ } a;
+ struct {
+ uint16_t xscale;
+ uint16_t scale01;
+ uint16_t scale10;
+ uint16_t yscale;
+ } b;
+ } u;
+};
+
+struct sfnt_compound_glyph
+{
+ /* Pointer to array of components. */
+ struct sfnt_compound_glyph_component *components;
+
+ /* Number of elements in that array. */
+ size_t num_components;
+
+ /* Instruction data. */
+ uint8_t *instructions;
+
+ /* Length of instructions. */
+ uint16_t instruction_length;
+};
+
+struct sfnt_glyph
+{
+ /* Number of contours in this glyph. */
+ int16_t number_of_contours;
+
+ /* Coordinate bounds. */
+ sfnt_fword xmin, ymin, xmax, ymax;
+
+ /* Distortion applied to the right side phantom point. */
+ sfnt_fword advance_distortion;
+
+ /* Distortion applied to the origin point. */
+ sfnt_fword origin_distortion;
+
+ /* Either a simple glyph or a compound glyph, depending on which is
+ set. */
+ struct sfnt_simple_glyph *simple;
+ struct sfnt_compound_glyph *compound;
+};
+
+
+
+/* Glyph outline decomposition. */
+
+struct sfnt_point
+{
+ /* X and Y in em space. */
+ sfnt_fixed x, y;
+};
+
+typedef void (*sfnt_move_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_line_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_curve_to_proc) (struct sfnt_point,
+ struct sfnt_point,
+ void *);
+
+typedef struct sfnt_glyph *(*sfnt_get_glyph_proc) (sfnt_glyph, void *,
+ bool *);
+typedef void (*sfnt_free_glyph_proc) (struct sfnt_glyph *, void *);
+
+
+
+/* Decomposed glyph outline. */
+
+struct sfnt_glyph_outline_command
+{
+ /* Flags for this outline command. */
+ int flags;
+
+ /* X and Y position of this command. */
+ sfnt_fixed x, y;
+};
+
+/* Structure describing a single recorded outline in fixed pixel
+ space. */
+
+struct sfnt_glyph_outline
+{
+ /* Array of outlines elements. */
+ struct sfnt_glyph_outline_command *outline;
+
+ /* Size of the outline data, and how much is full. */
+ size_t outline_size, outline_used;
+
+ /* Rectangle defining bounds of the outline. Namely, the minimum
+ and maximum X and Y positions. */
+ sfnt_fixed xmin, ymin, xmax, ymax;
+
+ /* The origin point of the outline on the X axis. Value defaults to
+ 0. */
+ sfnt_fixed origin;
+
+ /* Reference count. Initially zero. */
+ short refcount;
+};
+
+enum sfnt_glyph_outline_flags
+ {
+ SFNT_GLYPH_OUTLINE_LINETO = (1 << 1),
+ };
+
+
+
+/* Glyph rasterization. */
+
+struct sfnt_raster
+{
+ /* Pointer to coverage data. */
+ unsigned char *cells;
+
+ /* Basic dimensions of the raster. */
+ unsigned short width, height;
+
+ /* Integer offset to apply to positions in the raster so that they
+ start from the origin point of the glyph. */
+ short offx, offy;
+
+ /* The raster stride. */
+ unsigned short stride;
+
+ /* Reference count. Initially zero. */
+ unsigned short refcount;
+};
+
+struct sfnt_edge
+{
+ /* Next edge in this chain. */
+ struct sfnt_edge *next;
+
+ /* Winding direction. 1 if clockwise, -1 if counterclockwise. */
+ int winding;
+
+ /* X position, top and bottom of edges. */
+ sfnt_fixed x, top, bottom;
+
+ /* Amount to move X by upon each change of Y. */
+ sfnt_fixed step_x;
+};
+
+
+
+/* Polygon rasterization constants. */
+
+enum
+ {
+ SFNT_POLY_SHIFT = 3,
+ SFNT_POLY_SAMPLE = (1 << SFNT_POLY_SHIFT),
+ SFNT_POLY_MASK = (SFNT_POLY_SAMPLE - 1),
+ SFNT_POLY_STEP = (0x10000 >> SFNT_POLY_SHIFT),
+ SFNT_POLY_START = (SFNT_POLY_STEP >> 1),
+ };
+
+
+
+/* Glyph metrics computation. */
+
+struct sfnt_long_hor_metric
+{
+ uint16_t advance_width;
+ int16_t left_side_bearing;
+};
+
+struct sfnt_hmtx_table
+{
+ /* Array of horizontal metrics for each glyph. */
+ struct sfnt_long_hor_metric *h_metrics;
+
+ /* Lbearing for remaining glyphs. */
+ int16_t *left_side_bearing;
+};
+
+/* Structure describing the metrics of a single glyph. The fields
+ mean the same as in XCharStruct, except they are 16.16 fixed point
+ values, and are missing significant information. */
+
+struct sfnt_glyph_metrics
+{
+ /* Distance between origin and left edge of raster. Positive
+ changes move rightwards.
+
+ If sfnt_lookup_glyph_metrics is given a pixel size of -1,
+ this is actually a sign extended fword. */
+ sfnt_fixed lbearing;
+
+ /* Advance to next glyph's origin.
+
+ If sfnt_lookup_glyph_metrics is given a pixel size of -1, this is
+ actually a sign extended fword. */
+ sfnt_fixed advance;
+};
+
+
+
+/* Font style parsing. */
+
+struct sfnt_name_record
+{
+ /* Platform identifier code. */
+ uint16_t platform_id;
+
+ /* Platform specific ID. */
+ uint16_t platform_specific_id;
+
+ /* Language identifier. */
+ uint16_t language_id;
+
+ /* Name identifier. */
+ uint16_t name_id;
+
+ /* String length in bytes. */
+ uint16_t length;
+
+ /* Offset from start of storage area. */
+ uint16_t offset;
+};
+
+struct sfnt_name_table
+{
+ /* Format selector of name table. */
+ uint16_t format;
+
+ /* Number of name records. */
+ uint16_t count;
+
+ /* Offset to start of string data. */
+ uint16_t string_offset;
+
+ /* Variable length data. */
+ struct sfnt_name_record *name_records;
+
+ /* Start of string data. */
+ unsigned char *data;
+};
+
+/* Name identifier codes. These are Apple's codes, not
+ Microsoft's. */
+
+enum sfnt_name_identifier_code
+ {
+ SFNT_NAME_COPYRIGHT_NOTICE = 0,
+ SFNT_NAME_FONT_FAMILY = 1,
+ SFNT_NAME_FONT_SUBFAMILY = 2,
+ SFNT_NAME_UNIQUE_SUBFAMILY_IDENTIFICATION = 3,
+ SFNT_NAME_FULL_NAME = 4,
+ SFNT_NAME_NAME_TABLE_VERSION = 5,
+ SFNT_NAME_POSTSCRIPT_NAME = 6,
+ SFNT_NAME_TRADEMARK_NOTICE = 7,
+ SFNT_NAME_MANUFACTURER_NAME = 8,
+ SFNT_NAME_DESIGNER = 9,
+ SFNT_NAME_DESCRIPTION = 10,
+ SFNT_NAME_FONT_VENDOR_URL = 11,
+ SFNT_NAME_FONT_DESIGNER_URL = 12,
+ SFNT_NAME_LICENSE_DESCRIPTION = 13,
+ SFNT_NAME_LICENSE_INFORMATION_URL = 14,
+ SFNT_NAME_PREFERRED_FAMILY = 16,
+ SFNT_NAME_PREFERRED_SUBFAMILY = 17,
+ SFNT_NAME_COMPATIBLE_FULL = 18,
+ SFNT_NAME_SAMPLE_TEXT = 19,
+ SFNT_NAME_VARIATIONS_POSTSCRIPT_NAME_PREFIX = 25,
+ };
+
+struct sfnt_meta_data_map
+{
+ /* Identifier for the tag. */
+ uint32_t tag;
+
+ /* Offset from start of table to data. */
+ uint32_t data_offset;
+
+ /* Length of the data. */
+ uint32_t data_length;
+};
+
+struct sfnt_meta_table
+{
+ /* Version of the table. Currently set to 1. */
+ uint32_t version;
+
+ /* Flags. Currently 0. */
+ uint32_t flags;
+
+ /* Offset from start of table to beginning of variable length
+ data. */
+ uint32_t data_offset;
+
+ /* Number of data maps in the table. */
+ uint32_t num_data_maps;
+
+ /* Beginning of variable length data. */
+ struct sfnt_meta_data_map *data_maps;
+
+ /* The whole table contents. */
+ unsigned char *data;
+};
+
+enum sfnt_meta_data_tag
+ {
+ SFNT_META_DATA_TAG_DLNG = 0x646c6e67,
+ SFNT_META_DATA_TAG_SLNG = 0x736c6e67,
+ };
+
+
+
+/* TrueType collection format support. */
+
+struct sfnt_ttc_header
+{
+ /* TrueType collection ID tag. */
+ uint32_t ttctag;
+
+ /* Version of the TTC header. */
+ uint32_t version;
+
+ /* Number of fonts in the TTC header. */
+ uint32_t num_fonts;
+
+ /* Array of offsets to the offset table for each font in the
+ file. */
+ uint32_t *offset_table;
+
+ /* Tag indicating that a DSIG table exists, or 0. Fields from here
+ on are only set on version 2.0 headers or later. */
+ uint32_t ul_dsig_tag;
+
+ /* Length in bytes of the signature table, or 0 if there is no
+ signature. */
+ uint32_t ul_dsig_length;
+
+ /* Offset in bytes of the dsig table from the beginning of the TTC
+ file. */
+ uint32_t ul_dsig_offset;
+};
+
+enum sfnt_ttc_tag
+ {
+ SFNT_TTC_TTCF = 0x74746366,
+ SFNT_TTC_DSIG = 0x44534947,
+ };
+
+
+
+/* Unicode Variation Sequence (UVS) support. */
+
+struct sfnt_default_uvs_table
+{
+ /* Number of ranges that follow. */
+ uint32_t num_unicode_value_ranges;
+
+ /* Variable length data. */
+ struct sfnt_unicode_value_range *ranges;
+};
+
+struct sfnt_unicode_value_range
+{
+ /* First value in this range. */
+ unsigned int start_unicode_value;
+
+ /* Number of additional values in this range. */
+ unsigned char additional_count;
+};
+
+struct sfnt_nondefault_uvs_table
+{
+ /* Number of UVS mappings which follow. */
+ uint32_t num_uvs_mappings;
+
+ /* Variable length data. */
+ struct sfnt_uvs_mapping *mappings;
+};
+
+struct sfnt_uvs_mapping
+{
+ /* Base character value. */
+ unsigned int unicode_value;
+
+ /* Glyph ID of the base character value. */
+ uint16_t base_character_value;
+};
+
+struct sfnt_mapped_variation_selector_record
+{
+ /* The variation selector. */
+ unsigned int selector;
+
+ /* Its default UVS table. */
+ struct sfnt_default_uvs_table *default_uvs;
+
+ /* Its nondefault UVS table. */
+ struct sfnt_nondefault_uvs_table *nondefault_uvs;
+};
+
+/* Structure describing a single offset to load into a variation
+ selection context. */
+
+struct sfnt_table_offset_rec
+{
+ /* The offset from the start of the font file. */
+ off_t offset;
+
+ /* Whether or not the offset points to a non-default UVS table. */
+ bool is_nondefault_table;
+
+ /* Pointer to the UVS table. */
+ void *table;
+};
+
+struct sfnt_uvs_context
+{
+ /* Number of records and tables. */
+ size_t num_records, nmemb;
+
+ /* Array of UVS tables. */
+ struct sfnt_table_offset_rec *tables;
+
+ /* Array of variation selector records mapped to
+ their corresponding tables. */
+ struct sfnt_mapped_variation_selector_record *records;
+};
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support. */
+
+struct sfnt_mapped_table
+{
+ /* Pointer to table data. */
+ void *data;
+
+ /* Pointer to table mapping. */
+ void *mapping;
+
+ /* Size of mapped data and size of mapping. */
+ size_t length, size;
+};
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+/* Glyph variation support. */
+
+/* 2.14 fixed point type used to represent versors of unit
+ vectors. */
+typedef int16_t sfnt_f2dot14;
+
+/* Forward declaration used only for the distortable font stuff. */
+struct sfnt_cvt_table;
+
+struct sfnt_variation_axis
+{
+ /* The axis tag. */
+ uint32_t axis_tag;
+
+ /* The minimum style coordinate for the axis. */
+ sfnt_fixed min_value;
+
+ /* The default style coordinate for the axis. */
+ sfnt_fixed default_value;
+
+ /* The maximum style coordinate for the axis. */
+ sfnt_fixed max_value;
+
+ /* Set to zero. */
+ uint16_t flags;
+
+ /* Identifier under which this axis's name will be found in the
+ `name' table. */
+ uint16_t name_id;
+};
+
+struct sfnt_instance
+{
+ /* The instance name ID. */
+ uint16_t name_id;
+
+ /* Flags. */
+ uint16_t flags;
+
+ /* Optional PostScript name. */
+ uint16_t ps_name_id;
+
+ /* Coordinates of each defined instance. */
+ sfnt_fixed *coords;
+};
+
+struct sfnt_fvar_table
+{
+ /* Major version; should be 1. */
+ uint16_t major_version;
+
+ /* Minor version; should be 0. */
+ uint16_t minor_version;
+
+ /* Offset in bytes from the beginning of the table to the beginning
+ of the first axis data. */
+ uint16_t offset_to_data;
+
+ /* Reserved field; always 2. */
+ uint16_t count_size_pairs;
+
+ /* Number of style axes in this font. */
+ uint16_t axis_count;
+
+ /* The number of bytes in each variation axis record. Currently 20
+ bytes. */
+ uint16_t axis_size;
+
+ /* The number of named instances for the font found in the
+ instance array. */
+ uint16_t instance_count;
+
+ /* The size of each instance record. */
+ uint16_t instance_size;
+
+ /* Variable length data. */
+ struct sfnt_variation_axis *axis;
+ struct sfnt_instance *instance;
+};
+
+struct sfnt_short_frac_correspondence
+{
+ /* Value in normalized user space. */
+ sfnt_f2dot14 from_coord;
+
+ /* Value in normalized axis space. */
+ sfnt_f2dot14 to_coord;
+};
+
+struct sfnt_short_frac_segment
+{
+ /* The number of pairs for this axis. */
+ uint16_t pair_count;
+
+ /* Variable length data. */
+ struct sfnt_short_frac_correspondence *correspondence;
+};
+
+struct sfnt_avar_table
+{
+ /* The version of the table. Should be 1.0. */
+ sfnt_fixed version;
+
+ /* Number of variation axes defined in this table.
+ XXX: why is this signed? */
+ int32_t axis_count;
+
+ /* Variable length data. */
+ struct sfnt_short_frac_segment *segments;
+};
+
+struct sfnt_tuple_variation
+{
+ /* Tuple point numbers. */
+ uint16_t *points;
+
+ /* Deltas. */
+ sfnt_fword *deltas;
+
+ /* Tuple coordinates. One for each axis specified in the [gaf]var
+ tables. */
+ sfnt_f2dot14 *coordinates;
+
+ /* Intermediate start and end coordinates. */
+ sfnt_f2dot14 *restrict intermediate_start;
+
+ /* Intermediate start and end coordinates. */
+ sfnt_f2dot14 *restrict intermediate_end;
+
+ /* The number of points and deltas present.
+
+ UINT16_MAX and POINTS set to NULL means there are deltas for each
+ CVT entry. */
+ uint16_t num_points;
+};
+
+struct sfnt_cvar_table
+{
+ /* The version of this CVT variations table. */
+ sfnt_fixed version;
+
+ /* Flags. */
+ uint16_t tuple_count;
+
+ /* Offset from the beginning of the table to the tuple data. */
+ uint16_t data_offset;
+
+ /* Variable length data. */
+ struct sfnt_tuple_variation *variation;
+};
+
+struct sfnt_gvar_table
+{
+ /* Version of the glyph variations table. */
+ uint16_t version;
+
+ /* Reserved, currently 0. */
+ uint16_t reserved;
+
+ /* The number of style axes for this font. This must be the same
+ number as axisCount in the 'fvar' table. */
+ uint16_t axis_count;
+
+ /* The number of shared coordinates. */
+ uint16_t shared_coord_count;
+
+ /* Byte offset from the beginning of this table to the list of
+ shared style coordinates. */
+ uint32_t offset_to_coord;
+
+ /* The number of glyphs in this font; this should match the number
+ of the glyphs store elsewhere in the font. */
+ uint16_t glyph_count;
+
+ /* Bit-field that gives the format of the offset array that
+ follows. If the flag is 0, the type is uint16. If the flag is 1,
+ the type is unit 32. */
+ uint16_t flags;
+
+ /* Byte offset from the beginning of this table to the first glyph
+ glyphVariationData. */
+ uint32_t offset_to_data;
+
+ /* Number of bytes in the glyph variation data. */
+ size_t data_size;
+
+ /* Byte offsets from the beginning of the glyphVariationData array
+ to the glyphVariationData for each glyph in the font. The format
+ of this field is set by the flags field. */
+ union {
+ uint16_t *offset_word;
+ uint32_t *offset_long;
+ } u;
+
+ /* Other variable length data. */
+ sfnt_f2dot14 *global_coords;
+ unsigned char *glyph_variation_data;
+};
+
+/* Structure repesenting a set of axis coordinates and their
+ normalized equivalents.
+
+ To use this structure, call
+
+ sfnt_init_blend (&blend, fvar, gvar)
+
+ on a `struct sfnt_blend *', with an appropriate fvar and gvar
+ table.
+
+ Then, fill in blend.coords with the un-normalized coordinates,
+ and call
+
+ sfnt_normalize_blend (&blend)
+
+ finally, call sfnt_vary_simple_glyph and related functions. */
+
+struct sfnt_blend
+{
+ /* The fvar table. This determines the number of elements in each
+ of the arrays below. */
+ struct sfnt_fvar_table *fvar;
+
+ /* The gvar table. This provides the glyph variation data. */
+ struct sfnt_gvar_table *gvar;
+
+ /* The avar table. This provides adjustments to normalized axis
+ values, and may be NULL. */
+ struct sfnt_avar_table *avar;
+
+ /* The cvar table. This provides adjustments to CVT values, and may
+ be NULL. */
+ struct sfnt_cvar_table *cvar;
+
+ /* Un-normalized coordinates. */
+ sfnt_fixed *coords;
+
+ /* Normalized coordinates. */
+ sfnt_fixed *norm_coords;
+};
+
+struct sfnt_metrics_distortion
+{
+ /* Distortion applied to the origin point. */
+ sfnt_fword origin;
+
+ /* Distortion applied to the advance point. */
+ sfnt_fword advance;
+};
+
+
+
+#define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000)
+#define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000)
+
+
+
+/* Function declarations. Keep these sorted by the order in which
+ they appear in sfnt.c. Keep each line no longer than 80
+ columns. */
+
+#ifndef TEST
+
+extern struct sfnt_offset_subtable *sfnt_read_table_directory (int);
+
+#define PROTOTYPE \
+ int, struct sfnt_offset_subtable *, \
+ struct sfnt_cmap_encoding_subtable **, \
+ struct sfnt_cmap_encoding_subtable_data ***
+extern struct sfnt_cmap_table *sfnt_read_cmap_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern sfnt_glyph sfnt_lookup_glyph (sfnt_char,
+ struct sfnt_cmap_encoding_subtable_data *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_head_table *sfnt_read_head_table (PROTOTYPE);
+extern struct sfnt_hhea_table *sfnt_read_hhea_table (PROTOTYPE);
+extern struct sfnt_loca_table_short *sfnt_read_loca_table_short (PROTOTYPE);
+extern struct sfnt_loca_table_long *sfnt_read_loca_table_long (PROTOTYPE);
+extern struct sfnt_maxp_table *sfnt_read_maxp_table (PROTOTYPE);
+extern struct sfnt_glyf_table *sfnt_read_glyf_table (PROTOTYPE);
+
+#ifdef HAVE_MMAP
+extern struct sfnt_glyf_table *sfnt_map_glyf_table (PROTOTYPE);
+extern int sfnt_unmap_glyf_table (struct sfnt_glyf_table *);
+#endif /* HAVE_MMAP */
+#undef PROTOTYPE
+
+extern struct sfnt_glyph *sfnt_read_glyph (sfnt_glyph, struct sfnt_glyf_table *,
+ struct sfnt_loca_table_short *,
+ struct sfnt_loca_table_long *);
+extern void sfnt_free_glyph (struct sfnt_glyph *);
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ sfnt_fixed, \
+ struct sfnt_glyph_metrics *, \
+ sfnt_get_glyph_proc, \
+ sfnt_free_glyph_proc, \
+ void *
+extern struct sfnt_glyph_outline *sfnt_build_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+extern void sfnt_prepare_raster (struct sfnt_raster *,
+ struct sfnt_glyph_outline *);
+
+#define PROTOTYPE struct sfnt_glyph_outline *
+extern struct sfnt_raster *sfnt_raster_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ int, \
+ struct sfnt_offset_subtable *, \
+ struct sfnt_hhea_table *, \
+ struct sfnt_maxp_table *
+extern struct sfnt_hmtx_table *sfnt_read_hmtx_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern int sfnt_lookup_glyph_metrics (sfnt_glyph, int,
+ struct sfnt_glyph_metrics *,
+ struct sfnt_hmtx_table *,
+ struct sfnt_hhea_table *,
+ struct sfnt_head_table *,
+ struct sfnt_maxp_table *);
+
+extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *,
+ sfnt_fixed);
+extern sfnt_fixed sfnt_get_scale (struct sfnt_head_table *, int);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern unsigned char *sfnt_find_name (struct sfnt_name_table *,
+ enum sfnt_name_identifier_code,
+ struct sfnt_name_record *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_meta_table *sfnt_read_meta_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern char *sfnt_find_metadata (struct sfnt_meta_table *,
+ enum sfnt_meta_data_tag,
+ struct sfnt_meta_data_map *);
+
+extern struct sfnt_ttc_header *sfnt_read_ttc_header (int);
+
+
+
+#define PROTOTYPE struct sfnt_cmap_format_14 *, int
+
+extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE);
+
+#undef PROTOTYPE
+
+extern void sfnt_free_uvs_context (struct sfnt_uvs_context *);
+
+#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char
+
+extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+#ifdef HAVE_MMAP
+
+extern int sfnt_map_table (int, struct sfnt_offset_subtable *,
+ uint32_t, struct sfnt_mapped_table *);
+extern int sfnt_unmap_table (struct sfnt_mapped_table *);
+
+#endif /* HAVE_MMAP */
+
+
+
+extern void *sfnt_read_table (int, struct sfnt_offset_subtable *,
+ uint32_t, size_t *);
+
+
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_fvar_table *sfnt_read_fvar_table (PROTOTYPE);
+extern struct sfnt_gvar_table *sfnt_read_gvar_table (PROTOTYPE);
+extern struct sfnt_avar_table *sfnt_read_avar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ int, \
+ struct sfnt_offset_subtable *, \
+ struct sfnt_fvar_table *, \
+ struct sfnt_cvt_table *
+
+extern struct sfnt_cvar_table *sfnt_read_cvar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_init_blend (struct sfnt_blend *,
+ struct sfnt_fvar_table *,
+ struct sfnt_gvar_table *,
+ struct sfnt_avar_table *,
+ struct sfnt_cvar_table *);
+extern void sfnt_free_blend (struct sfnt_blend *);
+extern void sfnt_normalize_blend (struct sfnt_blend *);
+
+
+
+extern int sfnt_vary_simple_glyph (struct sfnt_blend *, sfnt_glyph,
+ struct sfnt_glyph *,
+ struct sfnt_metrics_distortion *);
+extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph,
+ struct sfnt_glyph *,
+ struct sfnt_metrics_distortion *);
+
+#endif /* TEST */
+
+
+
+/* TrueType hinting support. */
+
+/* Structure definitions for tables used by the TrueType
+ interpreter. */
+
+struct sfnt_cvt_table
+{
+ /* Number of elements in the control value table. */
+ size_t num_elements;
+
+ /* Pointer to elements in the control value table. */
+ sfnt_fword *values;
+};
+
+struct sfnt_fpgm_table
+{
+ /* Number of instructions in the font program table. */
+ size_t num_instructions;
+
+ /* Pointer to elements in the font program table. */
+ unsigned char *instructions;
+};
+
+struct sfnt_prep_table
+{
+ /* Number of instructions in the control value program (pre-program)
+ table. */
+ size_t num_instructions;
+
+ /* Pointer to elements in the preprogram table. */
+ unsigned char *instructions;
+};
+
+
+
+/* Fixed point types used by the TrueType interpreter. */
+
+/* 26.6 fixed point type used within the interpreter. */
+typedef int32_t sfnt_f26dot6;
+
+/* 18.14 fixed point type used to calculate rounding details. */
+typedef int32_t sfnt_f18dot14;
+
+
+
+/* Interpreter execution environment. */
+
+struct sfnt_unit_vector
+{
+ /* X and Y versors of the 2d unit vector. */
+ sfnt_f2dot14 x, y;
+};
+
+struct sfnt_interpreter_definition
+{
+ /* The opcode of this instruction or function. */
+ uint16_t opcode;
+
+ /* The number of instructions. */
+ uint16_t instruction_count;
+
+ /* Pointer to instructions belonging to the definition. This
+ pointer points directly into the control value or font program.
+ Make sure both programs are kept around as long as the
+ interpreter continues to exist. */
+ unsigned char *instructions;
+};
+
+/* This structure represents a ``struct sfnt_glyph'' that has been
+ scaled to a given pixel size.
+
+ It can either contain a simple glyph, or a decomposed compound
+ glyph; instructions are interpreted for both simple glyphs, simple
+ glyph components inside a compound glyph, and compound glyphs as a
+ whole.
+
+ In addition to the glyph data itself, it also records various
+ information for the instruction interpretation process:
+
+ - ``current'' point coordinates, which have been modified
+ by the instructing process.
+
+ - two phantom points at the origin and the advance of the
+ glyph. */
+
+struct sfnt_interpreter_zone
+{
+ /* The number of points in this zone, including the two phantom
+ points at the end. */
+ size_t num_points;
+
+ /* The number of contours in this zone. */
+ size_t num_contours;
+
+ /* The end points of each contour. */
+ size_t *contour_end_points;
+
+ /* Pointer to the X axis point data. */
+ sfnt_f26dot6 *restrict x_points;
+
+ /* Pointer to the X axis current point data. */
+ sfnt_f26dot6 *restrict x_current;
+
+ /* Pointer to the Y axis point data. */
+ sfnt_f26dot6 *restrict y_points;
+
+ /* Pointer to the Y axis current point data. */
+ sfnt_f26dot6 *restrict y_current;
+
+ /* Pointer to the flags associated with this data. */
+ unsigned char *flags;
+};
+
+enum
+ {
+ /* Bits 1 stands for X_SHORT_VECTOR on disk and in the tables, but
+ this representation is not useful in memory. Inside an
+ instructed glyph, this bit is repurposed to mean that the
+ corresponding point is a phantom point. */
+ SFNT_POINT_PHANTOM = (1 << 1),
+ /* Bits 7 and 6 of a glyph point's flags is reserved. This scaler
+ uses it to mean that the point has been touched in one axis or
+ another. */
+ SFNT_POINT_TOUCHED_X = (1 << 7),
+ SFNT_POINT_TOUCHED_Y = (1 << 6),
+ SFNT_POINT_TOUCHED_BOTH = (SFNT_POINT_TOUCHED_X
+ | SFNT_POINT_TOUCHED_Y),
+ };
+
+/* This is needed because `round' below needs an interpreter
+ argument. */
+struct sfnt_interpreter;
+
+struct sfnt_graphics_state
+{
+ /* Pointer to the function used for rounding. This function is
+ asymmetric, so -0.5 rounds up to 0, not -1. It is up to the
+ caller to handle negative values.
+
+ Value is undefined unless sfnt_validate_gs has been called, and
+ the second argument may be used to provide detailed rounding
+ information (``super rounding state''.) */
+ sfnt_f26dot6 (*round) (sfnt_f26dot6, struct sfnt_interpreter *);
+
+ /* Pointer to the function used to project euclidean vectors onto
+ the projection vector. Value is the magnitude of the projected
+ vector. */
+ sfnt_f26dot6 (*project) (sfnt_f26dot6, sfnt_f26dot6,
+ struct sfnt_interpreter *);
+
+ /* Pointer to the function used to project euclidean vectors onto
+ the dual projection vector. Value is the magnitude of the
+ projected vector. */
+ sfnt_f26dot6 (*dual_project) (sfnt_f26dot6, sfnt_f26dot6,
+ struct sfnt_interpreter *);
+
+ /* Pointer to the function used to move specified points
+ along the freedom vector by a distance specified in terms
+ of the projection vector. */
+ void (*move) (sfnt_f26dot6 *restrict,
+ sfnt_f26dot6 *restrict, size_t,
+ struct sfnt_interpreter *,
+ sfnt_f26dot6, unsigned char *);
+
+ /* Dot product between the freedom and the projection vectors. */
+ sfnt_f2dot14 vector_dot_product;
+
+ /* Controls whether the sign of control value table entries will be
+ changed to match the sign of the actual distance measurement with
+ which it is compared. Setting auto flip to TRUE makes it
+ possible to control distances measured with or against the
+ projection vector with a single control value table entry. When
+ auto flip is set to FALSE, distances must be measured with the
+ projection vector. */
+ bool auto_flip;
+
+ /* Limits the regularizing effects of control value table entries to
+ cases where the difference between the table value and the
+ measurement taken from the original outline is sufficiently
+ small. */
+ sfnt_f26dot6 cvt_cut_in;
+
+ /* Establishes the base value used to calculate the range of point
+ sizes to which a given DELTAC[] or DELTAP[] instruction will
+ apply. The formulas given below are used to calculate the range
+ of the various DELTA instructions.
+
+ DELTAC1 DELTAP1 (delta_base) through (delta_base + 15)
+ DELTAC2 DELTAP2 (delta_base + 16) through (delta_base + 31)
+ DELTAC3 DELTAP3 (delta_base + 32) through (delta_base + 47)
+
+ Please keep this documentation in sync with the TrueType
+ reference manual. */
+ unsigned short delta_base;
+
+ /* Determines the range of movement and smallest magnitude of
+ movement (the step) in a DELTAC[] or DELTAP[] instruction.
+ Changing the value of the delta shift makes it possible to trade
+ off fine control of point movement for range of movement. A low
+ delta shift favors range of movement over fine control. A high
+ delta shift favors fine control over range of movement. The step
+ has the value 1/2 to the power delta shift. The range of
+ movement is calculated by taking the number of steps allowed (16)
+ and multiplying it by the step.
+
+ The legal range for delta shift is zero through six. Negative
+ values are illegal. */
+ unsigned short delta_shift;
+
+ /* A second projection vector set to a line defined by the original
+ outline location of two points. The dual projection vector is
+ used when it is necessary to measure distances from the scaled
+ outline before any instructions were executed. */
+ struct sfnt_unit_vector dual_projection_vector;
+
+ /* A unit vector that establishes an axis along which points can
+ move. */
+ struct sfnt_unit_vector freedom_vector;
+
+ /* Makes it possible to turn off instructions under some
+ circumstances. When flag 1 is set, changes to the graphics state
+ made in the control value program will be ignored. When flag is
+ 1, grid fitting instructions will be ignored. */
+ unsigned char instruct_control;
+
+ /* Makes it possible to repeat certain instructions a designated
+ number of times. The default value of one assures that unless
+ the value of loop is altered, these instructions will execute one
+ time. */
+ unsigned short loop;
+
+ /* Establishes the smallest possible value to which a distance will
+ be rounded. */
+ sfnt_f26dot6 minimum_distance;
+
+ /* A unit vector whose direction establishes an axis along which
+ distances are measured. */
+ struct sfnt_unit_vector projection_vector;
+
+ /* Determines the manner in which values are rounded. Can be set to
+ a number of predefined states or to a customized state with the
+ SROUND or S45ROUND instructions. */
+ int round_state;
+
+ /* Reference points. These reference point numbers, which together
+ with a zone designation, specify a point in either the glyph zone
+ or the twilight zone. */
+ uint16_t rp0, rp1, rp2;
+
+ /* Flags which determine whether the interpreter will activate
+ dropout control for the current glyph. */
+ int scan_control;
+
+ /* The distance difference below which the interpreter will replace
+ a CVT distance or an actual distance in favor of the single width
+ value. */
+ sfnt_f26dot6 sw_cut_in;
+
+ /* The value used in place of the control value table distance or
+ the actual distance value when the difference between that
+ distance and the single width value is less than the single width
+ cut-in. */
+ sfnt_f26dot6 single_width_value;
+
+ /* Zone pointers, which reference a zone. */
+ int zp0, zp1, zp2;
+};
+
+struct sfnt_interpreter
+{
+ /* The number of elements in the stack. */
+ uint16_t max_stack_elements;
+
+ /* The number of instructions in INSTRUCTIONS. */
+ uint16_t num_instructions;
+
+ /* Size of the storage area. */
+ uint16_t storage_size;
+
+ /* Size of the function definition area. */
+ uint16_t function_defs_size;
+
+ /* Size of the instruction definition area. */
+ uint16_t instruction_defs_size;
+
+ /* Size of the twilight zone. */
+ uint16_t twilight_zone_size;
+
+ /* The instruction pointer. This points to the instruction
+ currently being executed. */
+ int IP;
+
+ /* The current scale. */
+ sfnt_fixed scale;
+
+ /* The current ppem and point size. */
+ int ppem, point_size;
+
+ /* The execution stack. This has at most max_stack_elements
+ elements. */
+ uint32_t *stack;
+
+ /* Pointer past the top of the stack. */
+ uint32_t *SP;
+
+ /* The size of the control value table. */
+ size_t cvt_size;
+
+ /* Pointer to instructions currently being executed. */
+ unsigned char *restrict instructions;
+
+ /* The twilight zone. May not be NULL. */
+ sfnt_f26dot6 *restrict twilight_x, *restrict twilight_y;
+
+ /* The original X positions of points in the twilight zone. */
+ sfnt_f26dot6 *restrict twilight_original_x;
+
+ /* The original Y positions of points in the twilight zone.
+
+ Apple does not directly say whether or not points in the twilight
+ zone can have their original positions changed. But this is
+ implied by ``create points in the twilight zone''. */
+ sfnt_f26dot6 *restrict twilight_original_y;
+
+ /* The scaled outlines being manipulated. May be NULL. */
+ struct sfnt_interpreter_zone *glyph_zone;
+
+ /* The glyph advance width. Value is undefined unless GLYPH_ZONE is
+ set. */
+ sfnt_f26dot6 advance_width;
+
+ /* The storage area. */
+ uint32_t *storage;
+
+ /* Control value table values. */
+ sfnt_f26dot6 *cvt;
+
+ /* Function definitions. */
+ struct sfnt_interpreter_definition *function_defs;
+
+ /* Instruction definitions. */
+ struct sfnt_interpreter_definition *instruction_defs;
+
+ /* Interpreter registers. */
+ struct sfnt_graphics_state state;
+
+ /* Detailed rounding state used when state.round_state indicates
+ that fine grained rounding should be used.
+
+ PERIOD says how often a round value occurs, for numbers
+ increasing from PHASE to infinity.
+
+ THRESHOLD says when to round a value between two increasing
+ periods towards the larger period. */
+ sfnt_f26dot6 period, phase, threshold;
+
+ /* The depth of any ongoing calls. */
+ int call_depth;
+
+ /* Jump buffer for traps. */
+ jmp_buf trap;
+
+ /* What was the trap. */
+ const char *trap_reason;
+
+ /* Number of variation axes provided by this distortable font. */
+ int n_axis;
+
+ /* Normalized axis coordinates set for this distortable font. */
+ sfnt_fixed *norm_coords;
+
+#ifdef TEST
+ /* If non-NULL, function called before each instruction is
+ executed. */
+ void (*run_hook) (struct sfnt_interpreter *);
+
+ /* If non-NULL, function called before each stack element is
+ pushed. */
+ void (*push_hook) (struct sfnt_interpreter *, uint32_t);
+
+ /* If non-NULL, function called before each stack element is
+ popped. */
+ void (*pop_hook) (struct sfnt_interpreter *, uint32_t);
+#endif
+};
+
+
+
+/* Glyph hinting. */
+
+/* Structure describing a single scaled and fitted outline. */
+
+struct sfnt_instructed_outline
+{
+ /* The number of points in this contour, including the two phantom
+ points at the end. */
+ size_t num_points;
+
+ /* The number of contours in this outline. */
+ size_t num_contours;
+
+ /* The end points of each contour. */
+ size_t *contour_end_points;
+
+ /* The points of each contour, with two additional phantom points at
+ the end. */
+ sfnt_f26dot6 *restrict x_points, *restrict y_points;
+
+ /* The flags of each point. */
+ unsigned char *flags;
+};
+
+
+
+/* Functions used to read tables used by the TrueType interpreter. */
+
+#ifndef TEST
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_cvt_table *sfnt_read_cvt_table (PROTOTYPE);
+extern struct sfnt_fpgm_table *sfnt_read_fpgm_table (PROTOTYPE);
+extern struct sfnt_prep_table *sfnt_read_prep_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_maxp_table *, \
+ struct sfnt_cvt_table *, \
+ struct sfnt_head_table *, \
+ struct sfnt_fvar_table *, \
+ int, int
+
+extern struct sfnt_interpreter *sfnt_make_interpreter (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_interpreter *, \
+ struct sfnt_fpgm_table *
+
+extern const char *sfnt_interpret_font_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_interpreter *, \
+ struct sfnt_prep_table *, \
+ struct sfnt_graphics_state *
+
+extern const char *sfnt_interpret_control_value_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE struct sfnt_instructed_outline *
+
+extern struct sfnt_glyph_outline *sfnt_build_instructed_outline (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ struct sfnt_interpreter *, \
+ struct sfnt_glyph_metrics *, \
+ struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_simple_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ struct sfnt_interpreter *, \
+ struct sfnt_graphics_state *, \
+ sfnt_get_glyph_proc, \
+ sfnt_free_glyph_proc, \
+ struct sfnt_hmtx_table *, \
+ struct sfnt_hhea_table *, \
+ struct sfnt_maxp_table *, \
+ struct sfnt_glyph_metrics *, \
+ void *, \
+ struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_compound_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_vary_interpreter (struct sfnt_interpreter *,
+ struct sfnt_blend *);
+
+#endif /* TEST */
+
+
+
+#endif /* _SFNT_H_ */
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
new file mode 100644
index 00000000000..de2a9253b57
--- /dev/null
+++ b/src/sfntfont-android.c
@@ -0,0 +1,793 @@
+/* sfnt format font driver for Android.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#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"
+#include "pdumper.h"
+#include "blockinput.h"
+#include "android.h"
+
+/* Structure describing a temporary buffer. */
+
+struct sfntfont_android_scanline_buffer
+{
+ /* Size of this buffer. */
+ size_t buffer_size;
+
+ /* Pointer to the buffer data. */
+ void *buffer_data;
+};
+
+/* Array of directories to search for system fonts. */
+static char *system_font_directories[] =
+ {
+ (char *) "/system/fonts",
+ (char *) "/product/fonts",
+ /* This should be filled in by init_sfntfont_android. */
+ (char[PATH_MAX]) { },
+ };
+
+/* The font cache. */
+static Lisp_Object font_cache;
+
+/* The scanline buffer. */
+static struct sfntfont_android_scanline_buffer scanline_buffer;
+
+/* The largest size of the scanline buffer since the last window
+ update. */
+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 \
+ { \
+ size_t _size; \
+ \
+ if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \
+ memory_full (SIZE_MAX); \
+ \
+ if (_size < MAX_ALLOCA) \
+ (buffer) = alloca (_size); \
+ else \
+ { \
+ if (_size > scanline_buffer.buffer_size) \
+ { \
+ (buffer) \
+ = scanline_buffer.buffer_data \
+ = xrealloc (scanline_buffer.buffer_data, \
+ _size); \
+ 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);
+
+#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
+ SCALE. Return the result.
+
+ SCALE is an integer between 0 and 256. */
+
+static unsigned int
+sfntfont_android_scale32 (unsigned int scale, unsigned int p)
+{
+ uint32_t ag, rb;
+ uint32_t scaled_ag, scaled_rb;
+
+ ag = (p & 0xFF00FF00) >> 8;
+ rb = (p & 0x00FF00FF);
+
+ scaled_ag = (scale * ag) & 0xFF00FF00;
+ scaled_rb = (scale * rb) >> 8 & 0x00FF00FF;
+
+ return scaled_ag | scaled_rb;
+}
+
+static unsigned int
+sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32)
+{
+ unsigned int i;
+
+ b32 &= 0xff00ff;
+ i = a8 * b32 + 0x800080;
+
+ return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff;
+}
+
+#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
+
+/* Blend two pixels SRC and DST without utilizing any control flow.
+ Both SRC and DST are expected to be in premultiplied ABGB8888
+ format. Value is returned in premultiplied ARGB8888 format. */
+
+static unsigned int
+sfntfont_android_blend (unsigned int src, unsigned int dst)
+{
+ unsigned int a, br_part, ag_part, both;
+
+ a = (src >> 24);
+ br_part = sfntfont_android_mul8x2 (255 - a, dst);
+ ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8;
+
+ both = ag_part | br_part;
+
+ /* This addition need not be saturating because both has already
+ been multiplied by 255 - a. */
+ 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. */
+
+static void
+sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
+ size_t stride,
+ unsigned char *restrict dest,
+ AndroidBitmapInfo *bitmap_info,
+ struct android_rectangle *text_rectangle,
+ struct android_rectangle *rect)
+{
+ unsigned int *src_row;
+ unsigned int *dst_row;
+ unsigned int i, src_y, x, src_x, max_x, dst_x;
+#ifdef __aarch64__
+ unsigned int lim_x;
+#endif
+
+ if ((intptr_t) dest & 3 || bitmap_info->stride & 3)
+ /* This shouldn't be possible as Android is supposed to align the
+ bitmap to at least a 4 byte boundary. */
+ emacs_abort ();
+ else
+ {
+ for (i = 0; i < rect->height; ++i)
+ {
+ if (i + rect->y >= bitmap_info->height)
+ /* Done. */
+ return;
+
+ src_y = i + (rect->y - text_rectangle->y);
+
+ if (src_y > text_rectangle->height)
+ /* Huh? */
+ return;
+
+ src_row = (unsigned int *) ((buffer + src_y * stride));
+ dst_row = (unsigned int *) (dest + ((i + rect->y)
+ * bitmap_info->stride));
+
+ /* Figure out where the loop below should end. */
+ max_x = min (rect->width, bitmap_info->width - rect->x);
+
+ /* Keep this loop simple! */
+ for (x = 0; x < max_x; ++x)
+ {
+ src_x = x + (rect->x - text_rectangle->x);
+ dst_x = x + rect->x;
+
+#ifdef __aarch64__
+ /* This is the largest value of src_x. */
+ lim_x = max_x + (rect->x - text_rectangle->x);
+
+ 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]);
+ }
+ }
+ }
+}
+
+/* Calculate the union containing both A and B, both boxes. Place the
+ result in RESULT. */
+
+static void
+sfntfont_android_union_boxes (struct gui_box a, struct gui_box b,
+ struct gui_box *result)
+{
+ result->x1 = min (a.x1, b.x1);
+ result->y1 = min (a.y1, b.y1);
+ result->x2 = max (a.x2, b.x2);
+ result->y2 = max (a.y2, b.y2);
+}
+
+/* Draw the specified glyph rasters from FROM to TO on behalf of S,
+ using S->gc. Fill the background if WITH_BACKGROUND is true.
+
+ See init_sfntfont_vendor and sfntfont_draw for more details. */
+
+static void
+sfntfont_android_put_glyphs (struct glyph_string *s, int from,
+ int to, int x, int y, bool with_background,
+ struct sfnt_raster **rasters,
+ int *x_coords)
+{
+ struct android_rectangle background, text_rectangle, rect;
+ struct gui_box text, character;
+ unsigned int *buffer, *row;
+ unsigned char *restrict raster_row;
+ size_t stride, i;
+ AndroidBitmapInfo bitmap_info;
+ unsigned char *bitmap_data;
+ jobject bitmap;
+ int left, top, temp_y;
+ unsigned int prod, raster_y;
+ unsigned long foreground, back_pixel, rb;
+
+ if (!s->gc->num_clip_rects)
+ /* Clip region is empty. */
+ return;
+
+ if (from == to)
+ /* Nothing to draw. */
+ return;
+
+ /* Swizzle the foreground and background in s->gc into BGR, then add
+ an alpha channel. */
+ foreground = s->gc->foreground;
+ back_pixel = s->gc->background;
+ rb = foreground & 0x00ff00ff;
+ foreground &= ~0x00ff00ff;
+ foreground |= rb >> 16 | rb << 16 | 0xff000000;
+ rb = back_pixel & 0x00ff00ff;
+ back_pixel &= ~0x00ff00ff;
+ back_pixel |= rb >> 16 | rb << 16 | 0xff000000;
+
+ prepare_face_for_display (s->f, s->face);
+
+ /* Build the scanline buffer. Figure out the bounds of the
+ background. */
+ memset (&background, 0, sizeof background);
+
+ if (with_background)
+ {
+ background.x = x;
+ background.y = y - FONT_BASE (s->font);
+ background.width = s->width;
+ background.height = FONT_HEIGHT (s->font);
+ }
+
+ /* Now figure out the bounds of the text. */
+
+ if (rasters[0])
+ {
+ text.x1 = x_coords[0] + rasters[0]->offx;
+ text.x2 = text.x1 + rasters[0]->width;
+ text.y1 = y - rasters[0]->height - rasters[0]->offy;
+ text.y2 = y - rasters[0]->offy;
+ }
+ else
+ memset (&text, 0, sizeof text);
+
+ for (i = 1; i < to - from; ++i)
+ {
+ /* See if text has to be extended. */
+
+ if (!rasters[i])
+ continue;
+
+ character.x1 = x_coords[i] + rasters[i]->offx;
+ character.x2 = character.x1 + rasters[i]->width;
+ character.y1 = y - rasters[i]->height - rasters[i]->offy;
+ character.y2 = y - rasters[i]->offy;
+
+ sfntfont_android_union_boxes (text, character, &text);
+ }
+
+ /* Union the background rect with the text rectangle. */
+ text_rectangle.x = text.x1;
+ text_rectangle.y = text.y1;
+ text_rectangle.width = text.x2 - text.x1;
+ text_rectangle.height = text.y2 - text.y1;
+ gui_union_rectangles (&background, &text_rectangle,
+ &text_rectangle);
+
+ /* 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);
+
+ /* 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)
+ {
+ /* Fill the background. First, offset the background rectangle
+ to become relative from text_rectangle.x,
+ text_rectangle.y. */
+ background.x = background.x - text_rectangle.x;
+ background.y = background.y - text_rectangle.y;
+ eassert (background.x >= 0 && background.y >= 0);
+
+ for (temp_y = background.y; (temp_y
+ < (background.y
+ + background.height));
+ ++temp_y)
+ {
+ row = (unsigned int *) ((unsigned char *) buffer
+ + stride * temp_y);
+
+ for (x = background.x; x < background.x + background.width; ++x)
+ row[x] = back_pixel;
+ }
+ }
+
+ /* Draw all the rasters onto the buffer. */
+ for (i = 0; i < to - from; ++i)
+ {
+ if (!rasters[i])
+ continue;
+
+ /* Figure out the top and left of the raster relative to
+ text_rectangle. */
+ left = x_coords[i] + rasters[i]->offx - text_rectangle.x;
+
+ /* Note that negative offy represents the part of the text that
+ lies below the baseline. */
+ top = (y - (rasters[i]->height + rasters[i]->offy)
+ - text_rectangle.y);
+ eassert (left >= 0 && top >= 0);
+
+ /* Draw the raster onto the temporary bitmap using the
+ foreground color scaled by the alpha map. */
+
+ for (raster_y = 0; raster_y < rasters[i]->height; ++raster_y)
+ {
+ row = (unsigned int *) ((unsigned char *) buffer
+ + stride * (raster_y + top));
+ raster_row = &rasters[i]->cells[raster_y * rasters[i]->stride];
+
+ for (x = 0; x < rasters[i]->width; ++x)
+ {
+ prod
+ = sfntfont_android_scale32 (U255TO256 (raster_row[x]),
+ foreground);
+ row[left + x]
+ = sfntfont_android_blend (prod, row[left + x]);
+ }
+ }
+ }
+
+ /* Lock the bitmap. It must be unlocked later. */
+ bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f),
+ &bitmap_info, &bitmap);
+
+ /* If locking the bitmap fails, just discard the data that was
+ allocated. */
+ if (!bitmap_data)
+ return;
+
+ /* Loop over each clip rect in the GC. */
+ eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+ if (s->gc->num_clip_rects > 0)
+ {
+ for (i = 0; i < s->gc->num_clip_rects; ++i)
+ {
+ if (!gui_intersect_rectangles (&s->gc->clip_rects[i],
+ &text_rectangle, &rect))
+ /* Outside the clip region. */
+ continue;
+
+ /* Composite the intersection onto the buffer. */
+ sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+ stride, bitmap_data,
+ &bitmap_info,
+ &text_rectangle, &rect);
+ }
+ }
+ else /* gc->num_clip_rects < 0 */
+ sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+ stride, bitmap_data,
+ &bitmap_info,
+ &text_rectangle,
+ &text_rectangle);
+
+ /* Release the bitmap. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+
+ /* Damage the window by the text rectangle. */
+ android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
+ &text_rectangle);
+
+#undef MAX_ALLOCA
+}
+
+
+
+/* Shrink the scanline buffer after a window update. If
+ max_scanline_buffer_size is not zero, and is less than
+ scanline_buffer.buffer_size / 2, then resize the scanline buffer to
+ max_scanline_buffer_size. */
+
+void
+sfntfont_android_shrink_scanline_buffer (void)
+{
+ if (!max_scanline_buffer_size)
+ return;
+
+ if (max_scanline_buffer_size
+ < scanline_buffer.buffer_size / 2)
+ {
+ scanline_buffer.buffer_size
+ = max_scanline_buffer_size;
+ scanline_buffer.buffer_data
+ = xrealloc (scanline_buffer.buffer_data,
+ max_scanline_buffer_size);
+ }
+
+ max_scanline_buffer_size = 0;
+}
+
+
+
+/* Font driver definition. */
+
+/* Return the font cache for this font driver. F is ignored. */
+
+static Lisp_Object
+sfntfont_android_get_cache (struct frame *f)
+{
+ return font_cache;
+}
+
+/* The Android sfntfont driver. */
+const struct font_driver android_sfntfont_driver =
+ {
+ .type = LISPSYM_INITIALLY (Qsfnt_android),
+ .case_sensitive = true,
+ .get_cache = sfntfont_android_get_cache,
+ .list = sfntfont_list,
+ .match = sfntfont_match,
+ .draw = sfntfont_draw,
+ .open_font = sfntfont_open,
+ .close_font = sfntfont_close,
+ .encode_char = sfntfont_encode_char,
+ .text_extents = sfntfont_text_extents,
+ .list_family = sfntfont_list_family,
+ .get_variation_glyphs = sfntfont_get_variation_glyphs,
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz support is enabled transparently on Android without
+ using a separate font driver. */
+ .begin_hb_font = sfntfont_begin_hb_font,
+ .combining_capability = hbfont_combining_capability,
+ .shape = hbfont_shape,
+ .otf_capability = hbfont_otf_capability,
+#endif /* HAVE_HARFBUZZ */
+ };
+
+
+
+/* This is an ugly hack that should go away, but I can't think of
+ how. */
+
+DEFUN ("android-enumerate-fonts", Fandroid_enumerate_fonts,
+ Sandroid_enumerate_fonts, 0, 0, 0,
+ doc: /* Enumerate fonts present on the system.
+
+Signal an error if fonts have already been enumerated. This would
+normally have been done in C, but reading fonts require Lisp to be
+loaded before character sets are made available. */)
+ (void)
+{
+ DIR *dir;
+ int i;
+ struct dirent *dirent;
+ char name[PATH_MAX * 2];
+ static bool enumerated;
+
+ if (enumerated)
+ error ("Fonts have already been enumerated");
+ enumerated = true;
+
+ block_input ();
+
+ /* Scan through each of the system font directories. Enumerate each
+ font that looks like a TrueType font. */
+ for (i = 0; i < ARRAYELTS (system_font_directories); ++i)
+ {
+ dir = opendir (system_font_directories[i]);
+
+ __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+ "Loading fonts from: %s",
+ system_font_directories[i]);
+
+ if (!dir)
+ continue;
+
+ while ((dirent = readdir (dir)))
+ {
+ /* If it contains (not ends with!) with .ttf or .ttc, then
+ enumerate it. */
+
+ if ((strstr (dirent->d_name, ".ttf")
+ || strstr (dirent->d_name, ".ttc"))
+ /* Ignore the non-variable Roboto font. */
+ && (i != 0 || strcmp (dirent->d_name,
+ "RobotoStatic-Regular.ttf")))
+ {
+ sprintf (name, "%s/%s", system_font_directories[i],
+ dirent->d_name);
+ sfnt_enum_font (name);
+ }
+ }
+
+ closedir (dir);
+ }
+
+ unblock_input ();
+
+ return Qnil;
+}
+
+
+
+static void
+syms_of_sfntfont_android_for_pdumper (void)
+{
+ init_sfntfont_vendor (Qsfnt_android, &android_sfntfont_driver,
+ sfntfont_android_put_glyphs);
+ register_font_driver (&android_sfntfont_driver, NULL);
+}
+
+void
+init_sfntfont_android (void)
+{
+ if (!android_init_gui)
+ return;
+
+ /* Make sure to pick the right Sans Serif font depending on what
+ version of Android the device is running. */
+ if (android_get_current_api_level () >= 15)
+ Vsfnt_default_family_alist
+ = list3 (Fcons (build_string ("Monospace"),
+ build_string ("Droid Sans Mono")),
+ /* Android doesn't come with a Monospace Serif font, so
+ this will have to do. */
+ Fcons (build_string ("Monospace Serif"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Sans Serif"),
+ build_string ("Roboto")));
+ else
+ Vsfnt_default_family_alist
+ = list3 (Fcons (build_string ("Monospace"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Monospace Serif"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Sans Serif"),
+ build_string ("Droid Sans")));
+
+ /* Set up the user fonts directory. This directory is ``fonts'' in
+ the Emacs files directory. */
+ snprintf (system_font_directories[2], PATH_MAX, "%s/fonts",
+ android_get_home_directory ());
+}
+
+void
+syms_of_sfntfont_android (void)
+{
+ DEFSYM (Qsfnt_android, "sfnt-android");
+ DEFSYM (Qandroid_enumerate_fonts, "android-enumerate-fonts");
+ Fput (Qandroid, Qfont_driver_superseded_by, Qsfnt_android);
+
+ font_cache = list (Qnil);
+ staticpro (&font_cache);
+
+ defsubr (&Sandroid_enumerate_fonts);
+
+ pdumper_do_now_and_after_load (syms_of_sfntfont_android_for_pdumper);
+}
diff --git a/src/sfntfont.c b/src/sfntfont.c
new file mode 100644
index 00000000000..71399b890d2
--- /dev/null
+++ b/src/sfntfont.c
@@ -0,0 +1,3874 @@
+/* sfnt format font driver for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "lisp.h"
+
+#include "blockinput.h"
+#include "charset.h"
+#include "coding.h"
+#include "font.h"
+#include "frame.h"
+#include "math.h"
+#include "sfnt.h"
+#include "sfntfont.h"
+
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ot.h>
+#endif /* HAVE_HARFBUZZ */
+
+/* For FRAME_FONT. */
+#include TERM_HEADER
+
+/* Generic font driver for sfnt-based fonts (currently TrueType, but
+ it would be easy to add CFF support in the future with a PostScript
+ renderer.)
+
+ This is not a complete font driver. Hooks must be supplied by the
+ platform implementer to draw glyphs. */
+
+
+
+/* Tables associated with each font, be it distortable or not. This
+ allows different font objects sharing the same underlying font file
+ to share tables. */
+
+struct sfnt_font_tables
+{
+ /* Various tables required to use the font. */
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_head_table *head;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_prep_table *prep;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_avar_table *avar;
+ struct sfnt_gvar_table *gvar;
+ struct sfnt_cvar_table *cvar;
+
+ /* The selected character map. */
+ struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+ /* Data identifying that character map. */
+ struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+ /* The UVS context. */
+ struct sfnt_uvs_context *uvs;
+
+#ifdef HAVE_MMAP
+ /* Whether or not the glyph table has been mmapped. */
+ bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* File descriptor associated with this font. */
+ int fd;
+
+ /* The table directory of the font file. */
+ struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+/* Description of a font that hasn't been opened. */
+
+struct sfnt_font_desc
+{
+ /* Next font in this list. */
+ struct sfnt_font_desc *next;
+
+ /* Family name of the font. */
+ Lisp_Object family;
+
+ /* Style name of the font. */
+ Lisp_Object style;
+
+ /* 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;
+
+ /* Font registry that this font supports. */
+ Lisp_Object registry;
+
+ /* Vector of instances. Each element is another of the instance's
+ `style', `adstyle', and numeric width, weight, and slant. May be
+ nil. */
+ Lisp_Object instances;
+
+ /* Numeric width, weight, slant and spacing. */
+ int width, weight, slant, spacing;
+
+ /* Path to the font file. */
+ char *path;
+
+ /* char table consisting of characters already known to be
+ present in the font. */
+ Lisp_Object char_cache;
+
+ /* Whether or not the character map can't be used by Emacs. */
+ bool cmap_invalid;
+
+ /* The header of the cmap being used. May be invalid, in which case
+ platform_id will be 500. */
+ struct sfnt_cmap_encoding_subtable subtable;
+
+ /* The offset of the table directory within PATH. */
+ off_t offset;
+
+ /* The number of glyphs in this font. Used to catch invalid cmap
+ tables. This is actually the number of glyphs - 1. */
+ int num_glyphs;
+
+ /* The number of references to the font tables below. */
+ int refcount;
+
+ /* List of font tables. */
+ struct sfnt_font_tables *tables;
+};
+
+/* List of fonts. */
+
+static struct sfnt_font_desc *system_fonts;
+
+/* Font enumeration and matching. The sfnt driver assumes it can read
+ data from each font at startup. It then reads the head, meta and
+ name tables to determine font data, and records the font in a list
+ of system fonts that is then matched against. */
+
+/* Set up the coding system CODING to decode string data from the
+ given platform id ID and platform specific id PLATFORM_SPECIFIC_ID.
+ Value is 0 upon success, 1 upon failure. */
+
+static int
+sfnt_setup_coding_system (enum sfnt_platform_id id, int platform_specific_id,
+ struct coding_system *coding)
+{
+ Lisp_Object system;
+
+ system = Qnil;
+
+ /* Figure out what coding system to use. */
+
+ switch (id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ system = Qutf_16be;
+ break;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ if (platform_specific_id == SFNT_MACINTOSH_ROMAN)
+ system = Qmac_roman;
+ else
+ /* MULE doesn't support the rest... */
+ system = Qnil;
+
+ break;
+
+ case SFNT_PLATFORM_MICROSOFT:
+ system = Qutf_16be;
+
+ /* Not sure if this is right. */
+ if (platform_specific_id == SFNT_MICROSOFT_BIG_FIVE)
+ system = Qchinese_big5;
+
+ break;
+
+ default:
+ system = Qnil;
+ }
+
+ if (NILP (system))
+ return 1;
+
+ setup_coding_system (system, coding);
+ return 0;
+}
+
+/* Globals used to communicate inside the condition-case wrapper. */
+static struct coding_system *sfnt_font_coding;
+
+/* The src_object being encoded from. This should be on the stack as
+ well, or it will get garbage collected. */
+static Lisp_Object sfnt_font_src_object;
+
+/* From-position. */
+static ptrdiff_t sfnt_font_from, sfnt_font_from_byte;
+
+/* To-position. */
+static ptrdiff_t sfnt_font_to, sfnt_font_to_byte;
+
+/* Destination object. Once again, this should also be on the
+ stack. */
+static Lisp_Object sfnt_font_dst_object;
+
+/* Error flag. Set to true if a signal was caught. */
+static bool sfnt_font_signal;
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_1 (void)
+{
+ decode_coding_object (sfnt_font_coding,
+ sfnt_font_src_object,
+ sfnt_font_from,
+ sfnt_font_from_byte,
+ sfnt_font_to,
+ sfnt_font_to_byte,
+ sfnt_font_dst_object);
+ return Qnil;
+}
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_2 (Lisp_Object error)
+{
+ sfnt_font_signal = true;
+
+ return Qnil;
+}
+
+/* Like decode_coding_object, but return 1 if a signal happens. Value
+ is otherwise 0. */
+
+static int
+sfnt_safe_decode_coding_object (struct coding_system *coding,
+ Lisp_Object src_object,
+ ptrdiff_t from, ptrdiff_t from_byte,
+ ptrdiff_t to, ptrdiff_t to_byte,
+ Lisp_Object dst_object)
+{
+ sfnt_font_coding = coding;
+ sfnt_font_src_object = src_object;
+ sfnt_font_from = from;
+ sfnt_font_from_byte = from_byte;
+ sfnt_font_to = to;
+ sfnt_font_to_byte = to_byte;
+ sfnt_font_dst_object = dst_object;
+ sfnt_font_signal = false;
+
+ internal_condition_case (sfnt_safe_decode_coding_object_1,
+ Qt,
+ sfnt_safe_decode_coding_object_2);
+
+ return (int) sfnt_font_signal;
+}
+
+/* Decode the specified string DATA. The encoding is determined based
+ on PLATFORM_ID, PLATFORM_SPECIFIC_ID and LANGUAGE_ID. Consult
+ sfnt.h and the TrueType Reference Manual for more details. LENGTH
+ is the length of DATA in bytes.
+
+ Value is nil upon failure, else the decoded string. */
+
+static Lisp_Object
+sfnt_decode_font_string (unsigned char *data, enum sfnt_platform_id id,
+ int platform_specific_id, int language_id,
+ size_t length)
+{
+ struct coding_system coding;
+
+ memset (&coding, 0, sizeof coding);
+ sfnt_setup_coding_system (id, platform_specific_id, &coding);
+ coding.mode |= CODING_MODE_SAFE_ENCODING;
+ coding.mode |= CODING_MODE_LAST_BLOCK;
+ /* Suppress producing escape sequences for composition. */
+ coding.common_flags &= ~CODING_ANNOTATION_MASK;
+ coding.source = data;
+
+ if (sfnt_safe_decode_coding_object (&coding, Qnil, 0, 0,
+ length, length, Qt))
+ return Qnil;
+
+ return coding.dst_object;
+}
+
+/* Decode the family and style names from the name table NAME. Return
+ 0 and the family and style names upon success, else 1. */
+
+static int
+sfnt_decode_family_style (struct sfnt_name_table *name,
+ Lisp_Object *family, Lisp_Object *style)
+{
+ struct sfnt_name_record family_rec, style_rec;
+ unsigned char *family_data, *style_data;
+
+ family_data = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+ &family_rec);
+ style_data = sfnt_find_name (name, SFNT_NAME_FONT_SUBFAMILY,
+ &style_rec);
+
+ if (!family_data || !style_data)
+ return 1;
+
+ /* Now decode the data. */
+ *family = sfnt_decode_font_string (family_data,
+ family_rec.platform_id,
+ family_rec.platform_specific_id,
+ family_rec.language_id,
+ family_rec.length);
+ *style = sfnt_decode_font_string (style_data,
+ style_rec.platform_id,
+ style_rec.platform_specific_id,
+ style_rec.language_id,
+ style_rec.length);
+
+ /* Return whether or not it was successful. */
+ return (!NILP (*family) && !NILP (*style)) ? 0 : 1;
+}
+
+/* Decode the foundry names from the name table NAME. Return the
+ foundry name, or nil upon failure. */
+
+static Lisp_Object
+sfnt_decode_foundry_name (struct sfnt_name_table *name)
+{
+ struct sfnt_name_record designer_rec;
+ unsigned char *designer_data;
+
+ designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER,
+ &designer_rec);
+
+ if (!designer_data)
+ return Qnil;
+
+ return sfnt_decode_font_string (designer_data,
+ designer_rec.platform_id,
+ designer_rec.platform_specific_id,
+ designer_rec.language_id,
+ designer_rec.length);
+}
+
+/* Decode the name of the specified font INSTANCE using the given NAME
+ table. Return the name of that instance, or nil upon failure. */
+
+static Lisp_Object
+sfnt_decode_instance_name (struct sfnt_instance *instance,
+ struct sfnt_name_table *name)
+{
+ struct sfnt_name_record name_rec;
+ unsigned char *name_data;
+
+ name_data = sfnt_find_name (name, instance->name_id,
+ &name_rec);
+
+ if (!name_data)
+ return Qnil;
+
+ return sfnt_decode_font_string (name_data,
+ name_rec.platform_id,
+ name_rec.platform_specific_id,
+ name_rec.language_id,
+ name_rec.length);
+}
+
+struct sfnt_style_desc
+{
+ /* The C string to match against. */
+ const char *c_string;
+
+ /* The value of the style field. */
+ int value;
+};
+
+/* Array of style descriptions describing weight. */
+static struct sfnt_style_desc sfnt_weight_descriptions[] =
+ {
+ { "thin", 0, },
+ { "extralight", 40, },
+ { "ultralight", 40, },
+ { "light", 50, },
+ { "demilight", 55, },
+ { "semilight", 55, },
+ { "book", 75, },
+ { "medium", 100, },
+ { "demibold", 180, },
+ { "semibold", 180, },
+ { "bold", 200, },
+ { "extrabold", 205, },
+ { "ultrabold", 205, },
+ { "black", 210, },
+ { "heavy", 210, },
+ { "extrablack", 215, },
+ { "ultrablack", 215, },
+ };
+
+/* Array of style descriptions describing slant. */
+static struct sfnt_style_desc sfnt_slant_descriptions[] =
+ {
+ { "italic", 200, },
+ { "oblique", 210, },
+ };
+
+/* Array of style descriptions describing width. */
+static struct sfnt_style_desc sfnt_width_descriptions[] =
+ {
+ { "ultracondensed", 50, },
+ { "extracondensed", 63, },
+ { "condensed", 75, },
+ { "semicondensed", 87, },
+ { "semiexpanded", 113, },
+ { "expanded", 125, },
+ { "extraexpanded", 150, },
+ { "ultraexpanded", 200, },
+ };
+
+/* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing
+ 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)
+{
+ char *style, *single, *saveptr;
+ int i;
+
+ /* Fill in default values. slant seems to not be consistent with
+ Fontconfig. */
+ desc->weight = 80;
+ desc->slant = 100;
+ desc->width = 100;
+
+ /* Split the style into spaces. As long as no weight, slant, or
+ width is encountered, look in the corresponding descriptions
+ array. GC must not happen inside this block. */
+ style = SSDATA (Fdowncase (style_name));
+ saveptr = NULL;
+
+ while ((single = strtok_r (style, " ", &saveptr)))
+ {
+ style = NULL;
+
+ if (desc->weight == 80)
+ {
+ /* Weight hasn't been found yet. Scan through the weight
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_weight_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_weight_descriptions[i].c_string,
+ single))
+ {
+ /* Weight found. Continue on reading the slant and
+ width. */
+ desc->weight = sfnt_weight_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ if (desc->slant == 100)
+ {
+ /* Slant hasn't been found yet. Scan through the slant
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_slant_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_slant_descriptions[i].c_string,
+ single))
+ {
+ /* Slant found. Continue on reading the weight and
+ width. */
+ desc->slant = sfnt_slant_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ if (desc->width == 100)
+ {
+ /* Width hasn't been found yet. Scan through the width
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_width_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_width_descriptions[i].c_string,
+ single))
+ {
+ /* Width found. Continue on reading the slant and
+ weight. */
+ desc->width = sfnt_width_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ /* This token is extraneous or was not recognized. Capitalize
+ the first letter and set it as the adstyle. */
+
+ 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)
+ {
+ /* Fall back to the supported languages metadata. */
+ data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_SLNG,
+ &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 ();
+}
+
+/* Return the font registry corresponding to the encoding subtable
+ SUBTABLE.
+
+ Under X, the font registry is an atom registered with the Open
+ Group uniquely identifying the organization which defines the
+ font's character set.
+
+ In practice, the registry overlaps with the character set itself.
+ So Emacs just uses the ``registry'' field of each font object and
+ entity to represent both instead. */
+
+static Lisp_Object
+sfnt_registry_for_subtable (struct sfnt_cmap_encoding_subtable *subtable)
+{
+ switch (subtable->platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ /* Reject variation selector and last resort tables. */
+ if ((subtable->platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable->platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return Qnil;
+
+ return Qiso10646_1;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable->platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ /* X calls mac-roman ``apple-roman''. */
+ return Qapple_roman;
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return Qnil;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable->platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return Qiso10646_1;
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ return Qjisx0208_1983_0;
+
+ case SFNT_MICROSOFT_PRC:
+ return Qgbk;
+
+ case SFNT_MICROSOFT_JOHAB:
+ return Qksc5601_1987_0;
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ return Qiso10646_1;
+ }
+
+ default:
+ return Qnil;
+ }
+}
+
+/* Return the type of characters that the cmap subtable SUBTABLE maps
+ from. Value is:
+
+ 2 if SUBTABLE maps from Unicode characters, including those outside
+ the Unicode Basic Multilingual Plane (BMP).
+
+ 1 if SUBTABLE maps from Unicode characters within the BMP.
+
+ 0 if SUBTABLE maps from some other character set that Emacs knows
+ about.
+
+ 3 if SUBTABLE cannot be used by Emacs. */
+
+static int
+sfntfont_identify_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+ switch (subtable.platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+
+ /* Reject variation selector and last resort tables. */
+ if ((subtable.platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable.platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return 3;
+
+ /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+ the BMP. */
+ if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+ return 1;
+
+ return 2;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ /* mac-roman */
+ return 0;
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return 3;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return 1;
+
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ return 1;
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ /* PCK aka japanese-jisx0208. */
+ return 0;
+
+ case SFNT_MICROSOFT_PRC:
+ /* GBK, GB2312 or GB18030. */
+ return 0;
+
+ case SFNT_MICROSOFT_JOHAB:
+ /* KS C 5601-1992, aka korean-ksc5601. */
+ return 0;
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ /* Unicode past the BMP. */
+ return 2;
+ }
+
+ default:
+ return 3;
+ }
+}
+
+/* Figure out which registry DESC, backed by FD, whose table directory
+ is SUBTABLE, is likely to support.
+
+ Read the header of each subtable in the character map and compute
+ the registry to use; then, set DESC->registry to that value. */
+
+static void
+sfnt_grok_registry (int fd, struct sfnt_font_desc *desc,
+ struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ int i;
+
+ cmap = sfnt_read_cmap_table (fd, subtable, &subtables, NULL);
+
+ if (!cmap)
+ return;
+
+ /* Now pick the ``best'' character map the same way as sfntfont_open
+ does. The caveat is that since the subtable data has not been
+ read, Emacs cannot determine whether or not the encoding subtable
+ is valid.
+
+ Once platform_id is set, that value becomes much more
+ reliable. */
+
+ /* First look for a non-BMP Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 2)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* Next, look for a BMP only Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 1)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* Finally, use the first cmap that appears and can be
+ identified. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 0)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* There are no cmaps available to Emacs. */
+ done:
+ xfree (cmap);
+ xfree (subtables);
+}
+
+/* Return whether or not the font description PREV conflicts with the
+ newer font description DESC, and should be removed from the list of
+ system fonts.
+
+ If PREV is a variable font, potentially adjust its list of
+ instances. */
+
+static bool
+sfnt_replace_fonts_p (struct sfnt_font_desc *prev,
+ struct sfnt_font_desc *desc)
+{
+ int i, width, weight, slant, count_instance;
+ Lisp_Object tem;
+ bool family_equal_p;
+
+ family_equal_p = !NILP (Fstring_equal (prev->family,
+ desc->family));
+
+ if ((!NILP (desc->instances)
+ || !NILP (Fstring_equal (prev->style, desc->style)))
+ && family_equal_p)
+ return true;
+
+ if (NILP (prev->instances) || !family_equal_p)
+ return false;
+
+ /* Look through instances in PREV to see if DESC provides the same
+ thing. */
+
+ count_instance = 0;
+ for (i = 0; i < ASIZE (prev->instances); ++i)
+ {
+ tem = AREF (prev->instances, i);
+
+ if (NILP (tem))
+ continue;
+
+ width = XFIXNUM (AREF (tem, 2));
+ weight = XFIXNUM (AREF (tem, 3));
+ slant = XFIXNUM (AREF (tem, 4));
+
+ if (desc->width == width
+ && desc->weight == weight
+ && desc->slant == slant)
+ {
+ /* Remove this instance. */
+ ASET (prev->instances, i, Qnil);
+ continue;
+ }
+
+ count_instance++;
+ }
+
+ /* Remove this desc if there are no more instances. */
+ return count_instance < 1;
+}
+
+/* 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
+ failure, else 0. */
+
+static int
+sfnt_enum_font_1 (int fd, const char *file,
+ struct sfnt_offset_subtable *subtables,
+ off_t offset)
+{
+ struct sfnt_font_desc *desc, **next, *prev;
+ struct sfnt_head_table *head;
+ struct sfnt_name_table *name;
+ struct sfnt_meta_table *meta;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_font_desc temp;
+ Lisp_Object family, style, instance, style1;
+ int i;
+
+ /* Create the font desc and copy in the file name. */
+ desc = xzalloc (sizeof *desc + strlen (file) + 1);
+ desc->path = (char *) (desc + 1);
+ memcpy (desc->path, file, strlen (file) + 1);
+ desc->offset = offset;
+
+ /* Check that this is a TrueType font. */
+ if (subtables->scaler_type != SFNT_SCALER_TRUE
+ && subtables->scaler_type != SFNT_SCALER_VER1)
+ goto bail1;
+
+ /* Read required tables. */
+ head = sfnt_read_head_table (fd, subtables);
+ if (!head)
+ goto bail1;
+
+ name = sfnt_read_name_table (fd, subtables);
+ if (!name)
+ goto bail2;
+
+ maxp = sfnt_read_maxp_table (fd, subtables);
+ if (!maxp)
+ goto bail3;
+
+ /* meta is not required, nor present on many non-Apple fonts. */
+ meta = sfnt_read_meta_table (fd, subtables);
+
+ /* Decode the family and style from the name table. */
+ if (sfnt_decode_family_style (name, &family, &style))
+ goto bail4;
+
+ /* See if this is a distortable/variable/multiple master font (all
+ three terms mean the same time.) */
+ fvar = sfnt_read_fvar_table (fd, subtables);
+
+ /* Set the family. */
+ desc->family = family;
+ desc->designer = sfnt_decode_foundry_name (name);
+ desc->char_cache = Qnil;
+ desc->subtable.platform_id = 500;
+
+ /* Set the largest glyph identifier. */
+ desc->num_glyphs = maxp->num_glyphs;
+
+ /* 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),
+ build_string ("mono"),
+ Qnil)))
+ desc->spacing = 100; /* FC_MONO */
+
+ /* Finally add mac-style flags. Allow them to override styles that
+ have not been found. */
+
+ if (head->mac_style & 01 && desc->weight == 80) /* Bold */
+ desc->weight = 200;
+
+ if (head->mac_style & 02 && desc->slant == 0) /* Italic */
+ desc->slant = 100;
+
+ /* Figure out what registry this font is likely to support. */
+ sfnt_grok_registry (fd, desc, subtables);
+
+ if (fvar && fvar->instance_count)
+ {
+ /* If there is an fvar table with instances, then this is a font
+ which defines different axes along which the points in each
+ glyph can be changed.
+
+ Instead of enumerating the font itself, enumerate each
+ instance within, which specifies how to configure each axis
+ to achieve a specified style. */
+
+ desc->instances = make_vector (fvar->instance_count, Qnil);
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ style1 = sfnt_decode_instance_name (&fvar->instance[i],
+ name);
+
+ if (!style1)
+ continue;
+
+ /* Now parse the style. */
+ temp.adstyle = Qnil;
+ sfnt_parse_style (style1, &temp);
+
+ /* Set each field of the vector. */
+ instance = make_vector (5, Qnil);
+ ASET (instance, 0, style1);
+ ASET (instance, 1, temp.adstyle);
+ ASET (instance, 2, make_fixnum (temp.width));
+ ASET (instance, 3, make_fixnum (temp.weight));
+ ASET (instance, 4, make_fixnum (temp.slant));
+
+ /* Place the vector in desc->instances. */
+ ASET (desc->instances, i, instance);
+ }
+ }
+
+ /* Set the style, link the desc onto system_fonts and return. */
+ desc->style = style;
+ desc->next = system_fonts;
+ system_fonts = desc;
+
+ /* Remove any fonts which have the same style as this one. For
+ distortable fonts, only remove overlapping styles, unless this is
+ also a distortable font. */
+
+ next = &system_fonts->next;
+ prev = *next;
+ for (; *next; prev = *next)
+ {
+ if (sfnt_replace_fonts_p (prev, desc))
+ {
+ *next = prev->next;
+ xfree (prev);
+ }
+ else
+ next = &prev->next;
+ }
+
+ xfree (fvar);
+ xfree (meta);
+ xfree (maxp);
+ xfree (name);
+ xfree (head);
+ return 0;
+
+ bail4:
+ xfree (meta);
+ xfree (maxp);
+ bail3:
+ xfree (name);
+ bail2:
+ xfree (head);
+ bail1:
+ xfree (desc);
+ return 1;
+}
+
+/* Enumerate the font FILE into the list of system fonts. Return 1 if
+ it could not be enumerated, 0 otherwise.
+
+ Remove any font whose family and style is a duplicate of this one.
+
+ FILE can either be a TrueType collection file containing TrueType
+ fonts, or a TrueType font itself. */
+
+int
+sfnt_enum_font (const char *file)
+{
+ int fd, rc;
+ struct sfnt_offset_subtable *subtables;
+ struct sfnt_ttc_header *ttc;
+ size_t i;
+
+ /* Now open the font for reading. */
+ fd = emacs_open (file, O_RDONLY, 0);
+
+ if (fd == -1)
+ goto bail;
+
+ /* Read the table directory. */
+ subtables = sfnt_read_table_directory (fd);
+
+ if (subtables == (struct sfnt_offset_subtable *) -1)
+ {
+ /* This is actually a TrueType container file. Go back to the
+ beginning and read the TTC header. */
+
+ if (lseek (fd, 0, SEEK_SET))
+ goto bail0;
+
+ ttc = sfnt_read_ttc_header (fd);
+
+ if (!ttc)
+ goto bail0;
+
+ /* Enumerate each of the fonts in the collection. */
+
+ for (i = 0; i < ttc->num_fonts; ++i)
+ {
+ if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+ != ttc->offset_table[i])
+ continue;
+
+ subtables = sfnt_read_table_directory (fd);
+
+ if (!subtables)
+ continue;
+
+ sfnt_enum_font_1 (fd, file, subtables,
+ ttc->offset_table[i]);
+ xfree (subtables);
+ }
+
+ /* Always treat reading containers as having been
+ successful. */
+
+ emacs_close (fd);
+ xfree (ttc);
+ return 0;
+ }
+
+ if (!subtables)
+ goto bail0;
+
+ /* Now actually enumerate this font. */
+ rc = sfnt_enum_font_1 (fd, file, subtables, 0);
+ xfree (subtables);
+ emacs_close (fd);
+ return rc;
+
+ bail0:
+ emacs_close (fd);
+ bail:
+ return 1;
+}
+
+
+
+/* Font discovery and matching. */
+
+static struct charset *
+sfntfont_charset_for_name (Lisp_Object symbol)
+{
+ ptrdiff_t idx;
+ int id;
+
+ idx = CHARSET_SYMBOL_HASH_INDEX (symbol);
+
+ if (idx == -1)
+ return NULL;
+
+ /* Vcharset_hash_table is not a real variable, so Lisp programs
+ can't clobber it. */
+ id = XFIXNUM (AREF (HASH_VALUE (XHASH_TABLE (Vcharset_hash_table),
+ idx),
+ charset_id));
+
+ return CHARSET_FROM_ID (id);
+}
+
+/* Return the character set corresponding to a cmap subtable SUBTABLE.
+ Value is NULL if the subtable is not supported. */
+
+static struct charset *
+sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+ switch (subtable.platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ /* Reject variation selector and last resort tables. */
+ if ((subtable.platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable.platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return NULL;
+
+ /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+ the BMP. */
+ if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+ return sfntfont_charset_for_name (Qunicode_bmp);
+
+ return sfntfont_charset_for_name (Qunicode);
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ return sfntfont_charset_for_name (Qmac_roman);
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return NULL;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return sfntfont_charset_for_name (Qunicode);
+
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ return sfntfont_charset_for_name (Qunicode_bmp);
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ /* PCK aka japanese-jisx0208. */
+ return sfntfont_charset_for_name (Qjapanese_jisx0208);
+
+ case SFNT_MICROSOFT_PRC:
+ /* GBK, GB2312 or GB18030. */
+ return sfntfont_charset_for_name (Qgbk);
+
+ case SFNT_MICROSOFT_JOHAB:
+ /* KS C 5601-1992, aka korean-ksc5601. */
+ return sfntfont_charset_for_name (Qkorean_ksc5601);
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ /* Unicode past the BMP. */
+ return sfntfont_charset_for_name (Qucs);
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+/* Pick the best character map in the cmap table CMAP. Use the
+ subtables in SUBTABLES and DATA. Return the subtable data and the
+ subtable in *SUBTABLE upon success, NULL otherwise.
+
+ If FORMAT14 is non-NULL, return any associated format 14 variation
+ selection context in *FORMAT14 should the selected charcter map be
+ a Unicode character map. */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
+ struct sfnt_cmap_encoding_subtable *subtables,
+ struct sfnt_cmap_encoding_subtable_data **data,
+ struct sfnt_cmap_encoding_subtable *subtable,
+ struct sfnt_cmap_format_14 **format14)
+{
+ int i, j;
+
+ /* First look for a non-BMP Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2)
+ {
+ *subtable = subtables[i];
+
+ if (!format14)
+ return data[i];
+
+ /* Search for a correspoinding format 14 character map.
+ This is used in conjunction with the selected character
+ map to map variation sequences. */
+
+ for (j = 0; j < cmap->num_subtables; ++j)
+ {
+ if (data[j]
+ && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+ && (subtables[j].platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ && data[j]->format == 14)
+ *format14 = (struct sfnt_cmap_format_14 *) data[j];
+ }
+
+ return data[i];
+ }
+ }
+
+ /* Next, look for a BMP only Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1)
+ {
+ *subtable = subtables[i];
+
+ if (!format14)
+ return data[i];
+
+ /* Search for a correspoinding format 14 character map.
+ This is used in conjunction with the selected character
+ map to map variation sequences. */
+
+ for (j = 0; j < cmap->num_subtables; ++j)
+ {
+ if (data[j]
+ && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+ && (subtables[j].platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ && data[j]->format == 14)
+ *format14 = (struct sfnt_cmap_format_14 *) data[j];
+ }
+
+ return data[i];
+ }
+ }
+
+ /* Finally, use the first cmap that appears and can be
+ identified. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 0)
+ {
+ *subtable = subtables[i];
+ return data[i];
+ }
+ }
+
+ /* There are no cmaps available to Emacs. */
+ return NULL;
+}
+
+/* Read the cmap from the font descriptor DESC, and place it in CMAP.
+ Keep *CMAP untouched if opening the cmap fails. Set SUBTABLE to
+ the cmap's header upon success. */
+
+static void
+sfntfont_read_cmap (struct sfnt_font_desc *desc,
+ struct sfnt_cmap_encoding_subtable_data **cmap,
+ struct sfnt_cmap_encoding_subtable *subtable)
+{
+ struct sfnt_offset_subtable *font;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_table *table;
+ int fd, i;
+
+ /* Pick a character map and place it in *CMAP. */
+ fd = emacs_open (desc->path, O_RDONLY, 0);
+
+ if (fd < 0)
+ return;
+
+ font = sfnt_read_table_directory (fd);
+
+ if (!font)
+ {
+ emacs_close (fd);
+ return;
+ }
+
+ table = sfnt_read_cmap_table (fd, font, &subtables,
+ &data);
+ xfree (font);
+
+ if (!table)
+ {
+ emacs_close (fd);
+ return;
+ }
+
+ /* Now pick the best character map. */
+
+ *cmap = sfntfont_select_cmap (table, subtables, data,
+ subtable, NULL);
+
+ /* Free the cmap data. */
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ if (data[i] != *cmap)
+ xfree (data[i]);
+ }
+
+ xfree (data);
+ xfree (subtables);
+ xfree (table);
+ emacs_close (fd);
+}
+
+/* Return whether or not CHARACTER has an associated mapping in CMAP,
+ and the mapping points to a valid glyph. DESC is the font
+ descriptor associated with the font. */
+
+static bool
+sfntfont_glyph_valid (struct sfnt_font_desc *desc,
+ sfnt_char font_character,
+ struct sfnt_cmap_encoding_subtable_data *cmap)
+{
+ sfnt_glyph glyph;
+
+ glyph = sfnt_lookup_glyph (font_character, cmap);
+
+ if (!glyph)
+ return false;
+
+ return glyph <= desc->num_glyphs;
+}
+
+/* Look up a character CHARACTER in the font description DESC. Cache
+ the results. Return true if the character exists, false otherwise.
+
+ If *CMAP is NULL, select a character map for the font and save it
+ there. Otherwise, use the character map in *CMAP. Save data
+ associated with the character map in *SUBTABLE. */
+
+static bool
+sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character,
+ struct sfnt_cmap_encoding_subtable_data **cmap,
+ struct sfnt_cmap_encoding_subtable *subtable)
+{
+ Lisp_Object cached;
+ sfnt_char font_character;
+ struct charset *charset;
+ bool present;
+
+ /* Return false for characters that don't fit in a char table. */
+ if (XFIXNUM (character) > INT_MAX || XFIXNUM (character) < 0)
+ return false;
+
+ if (!NILP (desc->char_cache))
+ {
+ cached = char_table_ref (desc->char_cache,
+ XFIXNUM (character));
+ if (!NILP (cached))
+ return (EQ (cached, Qlambda) ? false : true);
+ }
+
+ if (!*cmap && !desc->cmap_invalid)
+ sfntfont_read_cmap (desc, cmap, subtable);
+
+ /* Check that a cmap is now present. */
+ if (!*cmap)
+ {
+ /* Opening the cmap failed. Set desc->cmap_invalid to avoid
+ opening it again. */
+ desc->cmap_invalid = true;
+ return false;
+ }
+
+ /* Otherwise, encode the character. */
+
+ charset = sfntfont_charset_for_cmap (*subtable);
+ if (!charset)
+ /* Emacs missing charsets? */
+ return false;
+
+ font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character));
+
+ if (font_character == CHARSET_INVALID_CODE (charset))
+ return false;
+
+ /* Now return whether or not the glyph is present. Noto Sans
+ Georgian comes with a corrupt format 4 cmap table that somehow
+ tries to express glyphs greater than 65565. */
+ present = sfntfont_glyph_valid (desc, font_character, *cmap);
+
+ /* Cache the result. Store Qlambda when not present, Qt
+ otherwise. */
+
+ if (NILP (desc->char_cache))
+ desc->char_cache = Fmake_char_table (Qfont_lookup_cache,
+ Qnil);
+
+ Fset_char_table_range (desc->char_cache, character,
+ present ? Qt : Qlambda);
+ return present;
+}
+
+/* Return whether or not the specified registry A is ``compatible''
+ with registry B.
+
+ Compatibility does not refer to whether or not the font registries
+ have an identical character set or repertory of characters.
+
+ Instead, it refers to whether or not Emacs expects looking for A to
+ result in fonts used with B. */
+
+static bool
+sfntfont_registries_compatible_p (Lisp_Object a, Lisp_Object b)
+{
+ if (EQ (a, Qiso8859_1) && EQ (b, Qiso10646_1))
+ return true;
+
+ return EQ (a, b);
+}
+
+/* Return whether or not the font description DESC satisfactorily
+ matches the font specification FONT_SPEC.
+
+ Value is 0 if there is no match, -1 if there is a match against
+ DESC itself, and the number of matching instances if the style
+ matches one or more instances defined in in DESC. Return the index
+ of each matching instance in INSTANCES; it should be SIZE big. */
+
+static int
+sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec,
+ int *instances, int size)
+{
+ Lisp_Object tem, extra, tail;
+ struct sfnt_cmap_encoding_subtable_data *cmap;
+ size_t i;
+ struct sfnt_cmap_encoding_subtable subtable;
+ int instance, num_instance;
+ Lisp_Object item;
+
+ /* cmap and subtable are caches for sfntfont_lookup_char. */
+
+ /* Check that the family name in SPEC matches DESC->family if it is
+ specified. */
+
+ tem = AREF (spec, FONT_FAMILY_INDEX);
+
+ /* If TEM is a family listed in Vsfnt_default_family_alist,
+ then use that instead. */
+
+ if (SYMBOLP (tem) && CONSP (Vsfnt_default_family_alist))
+ {
+ tail = Vsfnt_default_family_alist;
+ FOR_EACH_TAIL_SAFE (tail)
+ {
+ if (!CONSP (XCAR (tail)))
+ continue;
+
+ if (STRINGP (XCAR (XCAR (tail)))
+ && STRINGP (XCDR (XCAR (tail)))
+ && Fstring_equal (SYMBOL_NAME (tem),
+ XCAR (XCAR (tail))))
+ {
+ /* Special family found. */
+ tem = Fintern (XCDR (XCAR (tail)), Qnil);
+ break;
+ }
+ }
+ }
+
+ if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem),
+ desc->family)))
+ return 0;
+
+ instance = -1;
+
+ /* If a registry is set and wrong, then reject the font desc
+ immediately. This detects 50% of mismatches from fontset.c.
+
+ If DESC->registry is nil, then the registry couldn't be
+ determined beforehand. */
+
+ tem = AREF (spec, FONT_REGISTRY_INDEX);
+ if (!NILP (tem) && !NILP (desc->registry)
+ && !sfntfont_registries_compatible_p (tem, desc->registry))
+ return 0;
+
+ /* Check the style. If DESC is a fixed font, just check once.
+ Otherwise, check each instance. */
+
+ if (NILP (desc->instances))
+ {
+ tem = AREF (spec, FONT_ADSTYLE_INDEX);
+ if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle)))
+ return 0;
+
+ if (FONT_WIDTH_NUMERIC (spec) != -1
+ && FONT_WIDTH_NUMERIC (spec) != desc->width)
+ return 0;
+
+ if (FONT_WEIGHT_NUMERIC (spec) != -1
+ && FONT_WEIGHT_NUMERIC (spec) != desc->weight)
+ return 0;
+
+ if (FONT_SLANT_NUMERIC (spec) != -1
+ && FONT_SLANT_NUMERIC (spec) != desc->slant)
+ return 0;
+ }
+ else
+ {
+ num_instance = 0;
+
+ /* Find the indices of instances in this distortable font which
+ match the given font spec. */
+
+ for (i = 0; i < ASIZE (desc->instances); ++i)
+ {
+ item = AREF (desc->instances, i);
+
+ if (NILP (item))
+ continue;
+
+ /* Check that the adstyle specified matches. */
+
+ tem = AREF (spec, FONT_ADSTYLE_INDEX);
+ if (!NILP (tem) && NILP (Fequal (tem, AREF (item, 1))))
+ continue;
+
+ /* Check the style. */
+
+ if (FONT_WIDTH_NUMERIC (spec) != -1
+ && (FONT_WIDTH_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 2))))
+ continue;
+
+ if (FONT_WEIGHT_NUMERIC (spec) != -1
+ && (FONT_WEIGHT_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 3))))
+ continue;
+
+ if (FONT_SLANT_NUMERIC (spec) != -1
+ && (FONT_SLANT_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 4))))
+ continue;
+
+ if (num_instance == size)
+ break;
+
+ /* A matching instance has been found. Set its index, then
+ go back to the rest of the font matching. */
+ instances[num_instance++] = i;
+ }
+
+ instance = num_instance;
+ }
+
+ /* Handle extras. */
+ extra = AREF (spec, FONT_EXTRA_INDEX);
+
+ if (NILP (extra))
+ return instance;
+
+ tem = assq_no_quit (QCscript, extra);
+ cmap = NULL;
+
+ if (!NILP (tem))
+ {
+ /* If a script has been specified, look up its representative
+ characters and see if they are present in the font. This
+ requires reading the cmap. */
+ tem = assq_no_quit (XCDR (tem), Vscript_representative_chars);
+
+ if (CONSP (tem) && VECTORP (XCDR (tem)))
+ {
+ tem = XCDR (tem);
+
+ /* The vector contains characters, of which one must be
+ present in the font. */
+ for (i = 0; i < ASIZE (tem); ++i)
+ {
+ if (FIXNUMP (AREF (tem, i)))
+ {
+ if (!sfntfont_lookup_char (desc, AREF (tem, i),
+ &cmap, &subtable))
+ goto fail;
+
+ /* One character is enough to pass a font. Don't
+ look at too many. */
+ break;
+ }
+ }
+ }
+ else if (CONSP (tem) && CONSP (XCDR (tem)))
+ {
+ tem = XCDR (tem);
+
+ /* tem is a list of each characters, all of which must be
+ present in the font. */
+ FOR_EACH_TAIL_SAFE (tem)
+ {
+ if (FIXNUMP (XCAR (tem))
+ && !sfntfont_lookup_char (desc, XCAR (tem), &cmap,
+ &subtable))
+ goto fail;
+ }
+
+ /* One or more characters are missing. */
+ if (!NILP (tem))
+ goto fail;
+ }
+ /* Fail if there are no matching fonts at all. */
+ else if (NILP (tem))
+ goto fail;
+ }
+
+ /* 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;
+
+ xfree (cmap);
+ return instance;
+
+ fail:
+ /* The cmap might've been read in and require deallocation. */
+ xfree (cmap);
+ return 0;
+}
+
+/* Type of font entities and font objects created. */
+static Lisp_Object sfnt_vendor_name;
+
+/* Font driver used in font objects created. */
+static const struct font_driver *sfnt_font_driver;
+
+/* Return the font registry corresponding to the font descriptor DESC.
+ Under X, the font registry is an atom registered with the Open
+ Group uniquely identifying the organization which defines the
+ font's character set.
+
+ In practice, the registry overlaps with the character set itself.
+ So Emacs just uses the ``registry'' field to represent both
+ instead. */
+
+static Lisp_Object
+sfntfont_registry_for_desc (struct sfnt_font_desc *desc)
+{
+ struct sfnt_cmap_encoding_subtable_data *cmap;
+
+ cmap = NULL;
+
+ if (desc->cmap_invalid)
+ return Qnil;
+
+ if (desc->subtable.platform_id == 500)
+ {
+ /* Read in the cmap to determine the registry. */
+ sfntfont_read_cmap (desc, &cmap, &desc->subtable);
+
+ if (!cmap)
+ {
+ desc->cmap_invalid = true;
+ return Qnil;
+ }
+ }
+
+ xfree (cmap);
+
+ if (desc->subtable.platform_id != 500)
+ /* desc->subtable.platform_id is now set. CMAP is already free,
+ because it is not actually used. */
+ return sfnt_registry_for_subtable (&desc->subtable);
+
+ return Qnil;
+}
+
+/* Return a font-entity that represents the font descriptor (unopened
+ font) DESC. If INSTANCE is more than or equal to 1, then it is the
+ index of the instance in DESC that should be opened plus 1; in that
+ case, DESC must be a distortable font. */
+
+static Lisp_Object
+sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance)
+{
+ Lisp_Object entity, vector;
+
+ entity = font_make_entity ();
+
+ ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+ if (!NILP (desc->designer))
+ ASET (entity, FONT_FOUNDRY_INDEX,
+ Fintern (desc->designer, Qnil));
+
+ ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+ ASET (entity, FONT_ADSTYLE_INDEX, Qnil);
+ ASET (entity, FONT_REGISTRY_INDEX,
+ sfntfont_registry_for_desc (desc));
+
+ /* Size of 0 means the font is scalable. */
+ ASET (entity, FONT_SIZE_INDEX, make_fixnum (0));
+ ASET (entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+ ASET (entity, FONT_SPACING_INDEX,
+ make_fixnum (desc->spacing));
+
+ if (instance >= 1)
+ {
+ if (NILP (desc->instances)
+ || instance > ASIZE (desc->instances))
+ emacs_abort ();
+
+ vector = AREF (desc->instances, instance - 1);
+ FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+ AREF (vector, 2));
+ FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+ AREF (vector, 3));
+ FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+ AREF (vector, 4));
+ ASET (entity, FONT_ADSTYLE_INDEX, AREF (vector, 1));
+ }
+ else
+ {
+ FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+ make_fixnum (desc->width));
+ FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+ make_fixnum (desc->weight));
+ FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+ make_fixnum (desc->slant));
+ ASET (entity, FONT_ADSTYLE_INDEX, desc->adstyle);
+ }
+
+ /* Set FONT_EXTRA_INDEX to a pointer to the font description. Font
+ descriptions are never supposed to be freed. */
+
+ ASET (entity, FONT_EXTRA_INDEX,
+ (instance >= 1
+ ? list2 (Fcons (Qfont_entity, make_mint_ptr (desc)),
+ Fcons (Qfont_instance, make_fixnum (instance - 1)))
+ : list1 (Fcons (Qfont_entity, make_mint_ptr (desc)))));
+
+ return entity;
+}
+
+/* Return a list of font-entities matching the specified
+ FONT_SPEC. */
+
+Lisp_Object
+sfntfont_list (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object matching, tem;
+ struct sfnt_font_desc *desc;
+ int i, rc, instances[100];
+
+ matching = Qnil;
+
+ block_input ();
+ /* Returning irrelevant results on receiving an OTF form will cause
+ fontset.c to loop over and over, making displaying some
+ characters very slow. */
+ tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX));
+ if (CONSP (tem) && !NILP (XCDR (tem)))
+ {
+ unblock_input ();
+ return Qnil;
+ }
+
+ /* Loop through known system fonts and add them one-by-one. */
+
+ for (desc = system_fonts; desc; desc = desc->next)
+ {
+ rc = sfntfont_list_1 (desc, font_spec, instances,
+ ARRAYELTS (instances));
+
+ if (rc < 0)
+ matching = Fcons (sfntfont_desc_to_entity (desc, 0),
+ matching);
+ else if (rc)
+ {
+ /* Add each matching instance. */
+
+ for (i = 0; i < rc; ++i)
+ matching = Fcons (sfntfont_desc_to_entity (desc,
+ instances[i] + 1),
+ matching);
+ }
+ }
+
+ unblock_input ();
+
+ return matching;
+}
+
+/* Return the first font-entity matching the specified FONT_SPEC. */
+
+Lisp_Object
+sfntfont_match (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object matches;
+
+ matches = sfntfont_list (f, font_spec);
+
+ if (!NILP (matches))
+ return XCAR (matches);
+
+ return Qnil;
+}
+
+
+
+enum
+ {
+ SFNT_OUTLINE_CACHE_SIZE = 256,
+ SFNT_RASTER_CACHE_SIZE = 128,
+ };
+
+/* Caching subsystem. Generating outlines from glyphs is expensive,
+ and so is rasterizing them, so two caches are maintained for both
+ glyph outlines and rasters.
+
+ Computing metrics also requires some expensive processing if the
+ glyph has instructions or distortions. */
+
+struct sfnt_outline_cache
+{
+ /* Next and last cache buckets. */
+ struct sfnt_outline_cache *next, *last;
+
+ /* Pointer to outline. */
+ struct sfnt_glyph_outline *outline;
+
+ /* Reference to glyph metrics. */
+ struct sfnt_glyph_metrics metrics;
+
+ /* What glyph this caches. */
+ sfnt_glyph glyph;
+};
+
+struct sfnt_raster_cache
+{
+ /* Next and last cache buckets. */
+ struct sfnt_raster_cache *next, *last;
+
+ /* Pointer to raster. */
+ struct sfnt_raster *raster;
+
+ /* What glyph this caches. */
+ sfnt_glyph glyph;
+};
+
+struct sfntfont_get_glyph_outline_dcontext
+{
+ /* Long and short loca tables. */
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_loca_table_short *loca_short;
+
+ /* glyf table. */
+ struct sfnt_glyf_table *glyf;
+
+ /* Variation settings, or NULL. */
+ struct sfnt_blend *blend;
+};
+
+/* Return the glyph identified by GLYPH_ID from the glyf and loca
+ table specified in DCONTEXT. Set *NEED_FREE to true. */
+
+static struct sfnt_glyph *
+sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext,
+ bool *need_free)
+{
+ struct sfntfont_get_glyph_outline_dcontext *tables;
+ struct sfnt_glyph *glyph;
+ struct sfnt_metrics_distortion distortion;
+
+ tables = dcontext;
+ *need_free = true;
+
+ glyph = sfnt_read_glyph (glyph_id, tables->glyf,
+ tables->loca_short,
+ tables->loca_long);
+
+ if (tables->blend && glyph)
+ {
+ if (glyph->simple)
+ sfnt_vary_simple_glyph (tables->blend, glyph_id, glyph,
+ &distortion);
+ else
+ sfnt_vary_compound_glyph (tables->blend, glyph_id, glyph,
+ &distortion);
+ }
+
+ /* Note that the distortion is not relevant for compound glyphs. */
+ return glyph;
+}
+
+/* Free the glyph identified by GLYPH. */
+
+static void
+sfntfont_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+ sfnt_free_glyph (glyph);
+}
+
+/* Dereference the outline OUTLINE. Free it once refcount reaches
+ 0. */
+
+static void
+sfntfont_dereference_outline (struct sfnt_glyph_outline *outline)
+{
+ eassert (outline->refcount > 0);
+
+ if (--outline->refcount)
+ return;
+
+ xfree (outline);
+}
+
+/* Get the outline corresponding to the specified GLYPH_CODE in CACHE.
+ Use the scale factor SCALE, the glyf table GLYF, and the head table
+ HEAD. Keep *CACHE_SIZE updated with the number of elements in the
+ cache.
+
+ Distort the glyph using BLEND if INDEX is not -1.
+
+ Use the offset information in the long or short loca tables
+ LOCA_LONG and LOCA_SHORT, whichever is set.
+
+ Use the specified HMTX, HEAD, HHEA and MAXP tables when instructing
+ compound glyphs.
+
+ If INTERPRETER is non-NULL, then possibly use it and the
+ interpreter graphics STATE to instruct the glyph.
+
+ If METRICS is non-NULL, return the scaled glyph metrics after
+ variation and instructing.
+
+ Return the outline with an incremented reference count and enter
+ the generated outline into CACHE upon success, possibly discarding
+ any older outlines, or NULL on failure. */
+
+static struct sfnt_glyph_outline *
+sfntfont_get_glyph_outline (sfnt_glyph glyph_code,
+ struct sfnt_outline_cache *cache,
+ sfnt_fixed scale, int *cache_size,
+ struct sfnt_blend *blend,
+ int index,
+ struct sfnt_glyf_table *glyf,
+ struct sfnt_head_table *head,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_loca_table_short *loca_short,
+ struct sfnt_loca_table_long *loca_long,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_graphics_state *state)
+{
+ struct sfnt_outline_cache *start;
+ struct sfnt_glyph_outline *outline;
+ struct sfnt_glyph *glyph;
+ struct sfntfont_get_glyph_outline_dcontext dcontext;
+ struct sfnt_instructed_outline *value;
+ const char *error;
+ struct sfnt_glyph_metrics temp;
+ struct sfnt_metrics_distortion distortion;
+
+ start = cache->next;
+ distortion.advance = 0;
+
+ /* See if the outline is already cached. */
+ for (; start != cache; start = start->next)
+ {
+ if (start->glyph == glyph_code)
+ {
+ /* Move start to the start of the ring. Then increase
+ start->outline->refcount and return it. */
+
+ start->last->next = start->next;
+ start->next->last = start->last;
+
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+ start->outline->refcount++;
+
+ if (metrics)
+ *metrics = start->metrics;
+
+ return start->outline;
+ }
+ }
+
+ /* Not already cached. Get the glyph. */
+ glyph = sfnt_read_glyph (glyph_code, glyf,
+ loca_short, loca_long);
+
+ if (!glyph)
+ return NULL;
+
+ /* Distort the glyph if necessary. */
+
+ if (index != -1)
+ {
+ if (glyph->simple)
+ {
+ if (sfnt_vary_simple_glyph (blend, glyph_code,
+ glyph, &distortion))
+ {
+ sfnt_free_glyph (glyph);
+ return NULL;
+ }
+ }
+ else if (sfnt_vary_compound_glyph (blend, glyph_code,
+ glyph, &distortion))
+ {
+ sfnt_free_glyph (glyph);
+ return NULL;
+ }
+ }
+
+ /* Try to instruct the glyph if INTERPRETER is specified. */
+
+ outline = NULL;
+
+ dcontext.loca_long = loca_long;
+ dcontext.loca_short = loca_short;
+ dcontext.glyf = glyf;
+ dcontext.blend = (index != -1 ? blend : NULL);
+
+ /* Now load the glyph's unscaled metrics into TEMP. */
+
+ if (sfnt_lookup_glyph_metrics (glyph_code, -1, &temp, hmtx, hhea,
+ head, maxp))
+ goto fail;
+
+ /* Add the advance width distortion. */
+ temp.advance += distortion.advance;
+
+ if (interpreter)
+ {
+ if (glyph->simple)
+ {
+ /* Restore the interpreter state from the snapshot taken
+ after loading the preprogram. */
+ interpreter->state = *state;
+
+ error = sfnt_interpret_simple_glyph (glyph, interpreter,
+ &temp, &value);
+ }
+ else
+ /* Restoring the interpreter state is done by
+ sfnt_interpret_compound_glyph; all that must be done here
+ is to give the graphics state to that function. */
+ error = sfnt_interpret_compound_glyph (glyph, interpreter,
+ state,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ hmtx, hhea, maxp,
+ &temp, &dcontext,
+ &value);
+
+ if (!error)
+ {
+ outline = sfnt_build_instructed_outline (value);
+ xfree (value);
+ }
+ }
+
+ if (!outline)
+ {
+ if (!interpreter)
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &temp,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ &dcontext);
+ else
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &temp,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ &dcontext);
+ }
+
+ /* At this point, the glyph metrics are unscaled. Scale them up.
+ If INTERPRETER is set, use the scale placed within. */
+
+ sfnt_scale_metrics (&temp, scale);
+
+ fail:
+
+ xfree (glyph);
+
+ if (!outline)
+ return NULL;
+
+ if (index != -1)
+ /* Finally, adjust the left side bearing of the glyph metrics by
+ the origin point of the outline, should a distortion have been
+ applied. The left side bearing is the distance from the origin
+ point to the left most point on the X axis. */
+ temp.lbearing = outline->xmin - outline->origin;
+
+ start = xmalloc (sizeof *start);
+ start->glyph = glyph_code;
+ start->outline = outline;
+ start->metrics = temp;
+
+ /* One reference goes to the cache. The second reference goes to
+ the caller. */
+ outline->refcount = 2;
+
+ /* Link start onto the cache. */
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+
+ /* Update the cache size. */
+ (*cache_size)++;
+
+ /* Figure out if the least recently used element has to be
+ evicted. */
+ if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+ {
+ start = cache->last;
+ eassert (start != cache);
+
+ /* Free the least recently used entry in the cache. */
+ start->last->next = start->next;
+ start->next->last = start->last;
+ sfntfont_dereference_outline (start->outline);
+ xfree (start);
+
+ (*cache_size)--;
+ }
+
+ /* Return the cached outline and metrics. */
+
+ if (metrics)
+ *metrics = temp;
+
+ return outline;
+}
+
+/* Free the outline cache referred to by CACHE. Dereference each
+ outline contained therein. */
+
+static void
+sfntfont_free_outline_cache (struct sfnt_outline_cache *cache)
+{
+ struct sfnt_outline_cache *next, *last;
+
+ /* Handle partly initialized fonts. */
+ if (!cache->next)
+ return;
+
+ for (next = cache->next; next != cache;)
+ {
+ last = next;
+ next = next->next;
+
+ sfntfont_dereference_outline (last->outline);
+ xfree (last);
+ }
+
+ cache->next = cache;
+ cache->last = cache;
+}
+
+/* Dereference the raster RASTER. Free it once refcount reaches
+ 0. */
+
+static void
+sfntfont_dereference_raster (struct sfnt_raster *raster)
+{
+ eassert (raster->refcount > 0);
+
+ if (--raster->refcount)
+ return;
+
+ xfree (raster);
+}
+
+/* Get the raster corresponding to the specified GLYPH_CODE in CACHE.
+ Use the outline named OUTLINE. Keep *CACHE_SIZE updated with the
+ number of elements in the cache. */
+
+static struct sfnt_raster *
+sfntfont_get_glyph_raster (sfnt_glyph glyph_code,
+ struct sfnt_raster_cache *cache,
+ struct sfnt_glyph_outline *outline,
+ int *cache_size)
+{
+ struct sfnt_raster_cache *start;
+ struct sfnt_raster *raster;
+
+ /* See if the raster is already cached. */
+ start = cache->next;
+
+ for (; start != cache; start = start->next)
+ {
+ if (start->glyph == glyph_code)
+ {
+ /* Move start to the start of the ring. Them, increase
+ start->raster->refcount and return it. */
+
+ start->last->next = start->next;
+ start->next->last = start->last;
+
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+ start->raster->refcount++;
+
+ return start->raster;
+ }
+ }
+
+ /* Not already cached. Raster the outline. */
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (!raster)
+ return NULL;
+
+ start = xmalloc (sizeof *start);
+ start->glyph = glyph_code;
+ start->raster = raster;
+
+ /* One reference goes to the cache. The second reference goes to
+ the caller. */
+ raster->refcount = 2;
+
+ /* Link start onto the cache. */
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+
+ /* Update the cache size. */
+ (*cache_size)++;
+
+ /* Figure out if the least recently used element has to be
+ evicted. */
+ if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+ {
+ start = cache->last;
+ eassert (start != cache);
+
+ /* Free the least recently used entry in the cache. */
+ start->last->next = start->next;
+ start->next->last = start->last;
+ sfntfont_dereference_raster (start->raster);
+ xfree (start);
+
+ (*cache_size)--;
+ }
+
+ /* Return the cached raster. */
+ return raster;
+}
+
+/* Free the raster cache referred to by CACHE. Dereference each
+ raster contained therein. */
+
+static void
+sfntfont_free_raster_cache (struct sfnt_raster_cache *cache)
+{
+ struct sfnt_raster_cache *next, *last;
+
+ /* Handle partly initialized fonts. */
+ if (!cache->next)
+ return;
+
+ for (next = cache->next; next != cache;)
+ {
+ last = next;
+ next = next->next;
+
+ sfntfont_dereference_raster (last->raster);
+ xfree (last);
+ }
+
+ cache->next = cache;
+ cache->last = cache;
+}
+
+
+
+/* Opening fonts. */
+
+struct sfnt_font_info
+{
+ /* Parent font structure. */
+ struct font font;
+
+#ifdef HAVE_MMAP
+ /* The next font in this chain. */
+ struct sfnt_font_info *next;
+#endif /* HAVE_MMAP */
+
+ /* The font description used to create this font. Used to
+ dereference tables associated with this font. */
+ struct sfnt_font_desc *desc;
+
+ /* Various tables required to use the font. */
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_head_table *head;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_prep_table *prep;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_cvt_table *cvt;
+
+ /* The selected character map. */
+ struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+ /* Data identifying that character map. */
+ struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+ /* The UVS context. */
+ struct sfnt_uvs_context *uvs;
+
+ /* Outline cache. */
+ struct sfnt_outline_cache outline_cache;
+
+ /* Number of elements in the outline cache. */
+ int outline_cache_size;
+
+ /* Raster cache. */
+ struct sfnt_raster_cache raster_cache;
+
+ /* Number of elements in the raster cache. */
+ int raster_cache_size;
+
+ /* Interpreter for grid fitting (if enabled). */
+ struct sfnt_interpreter *interpreter;
+
+ /* Graphics state after the execution of the font and control value
+ programs. */
+ struct sfnt_graphics_state state;
+
+ /* Factor used to convert from em space to pixel space. */
+ sfnt_fixed scale;
+
+ /* The blend (configuration of this multiple master font). */
+ struct sfnt_blend blend;
+
+ /* The index of the named instance used to initialize BLEND.
+ -1 if BLEND is not initialized. */
+ int instance;
+
+#ifdef HAVE_MMAP
+ /* Whether or not the glyph table has been mmapped. */
+ bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz font object. */
+ hb_font_t *hb_font;
+
+ /* File descriptor associated with this font. */
+ int fd;
+
+ /* The table directory of the font file. */
+ struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+#ifdef HAVE_MMAP
+
+/* List of all open fonts. */
+
+static struct sfnt_font_info *open_fonts;
+
+#endif /* HAVE_MMAP */
+
+/* Look up the glyph corresponding to the character C in FONT. Return
+ 0 upon failure, and the glyph otherwise. */
+
+static sfnt_glyph
+sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c)
+{
+ struct charset *charset;
+ sfnt_char character;
+ sfnt_glyph glyph;
+
+ charset = CHARSET_FROM_ID (font_info->font.encoding_charset);
+
+ if (!charset)
+ return 0;
+
+ character = ENCODE_CHAR (charset, c);
+
+ if (character == CHARSET_INVALID_CODE (charset))
+ return 0;
+
+ /* Do the actual lookup with the encoded character. */
+ glyph = sfnt_lookup_glyph (character, font_info->cmap_data);
+
+ return glyph;
+}
+
+/* Probe and set FONT_INFO->font.average_width,
+ FONT_INFO->font.space_width, and FONT_INFO->font.min_width
+ according to the tables contained therein. */
+
+static void
+sfntfont_probe_widths (struct sfnt_font_info *font_info)
+{
+ int i, num_characters, total_width;
+ sfnt_glyph glyph;
+ struct sfnt_glyph_metrics metrics;
+
+ num_characters = 0;
+ total_width = 0;
+
+ /* First set some reasonable default values. */
+ font_info->font.average_width = font_info->font.pixel_size;
+ font_info->font.space_width = font_info->font.pixel_size;
+ font_info->font.min_width = 1;
+
+ /* Next, loop through the common ASCII characters. Tally up their
+ advance widths and set space_width if necessary. */
+ for (i = 0; i < 127; ++i)
+ {
+ glyph = sfntfont_lookup_glyph (font_info, i);
+
+ if (!glyph)
+ continue;
+
+ /* Now look up the metrics of this glyph. */
+ if (sfnt_lookup_glyph_metrics (glyph, font_info->font.pixel_size,
+ &metrics, font_info->hmtx,
+ font_info->hhea, font_info->head,
+ font_info->maxp))
+ continue;
+
+ /* Increase the number of characters. */
+ num_characters++;
+
+ /* Add the advance to total_width. */
+ total_width += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+
+ /* Update min_width if it hasn't been set yet or is wider. */
+ if (font_info->font.min_width == 1
+ || font_info->font.min_width > metrics.advance / 65536)
+ font_info->font.min_width = metrics.advance / 65536;
+
+ /* If i is the space character, set the space width. Make sure
+ to round this up. */
+ if (i == 32)
+ font_info->font.space_width
+ = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ }
+
+ /* Now, if characters were found, set average_width. */
+ if (num_characters)
+ font_info->font.average_width = total_width / num_characters;
+}
+
+/* Initialize the instruction interpreter for INFO. Load the font and
+ preprogram for the pixel size in INFO and its corresponding point
+ size POINT_SIZE. Use the FVAR table in DESC.
+
+ The font tables in INFO must already have been initialized.
+
+ Set INFO->interpreter upon success, and leave that field intact
+ otherwise. */
+
+static void
+sfntfont_setup_interpreter (struct sfnt_font_info *info,
+ struct sfnt_font_desc *desc,
+ int point_size)
+{
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_prep_table *prep;
+ struct sfnt_interpreter *interpreter;
+ const char *error;
+ struct sfnt_graphics_state state;
+
+ /* Load the cvt, fpgm and prep already read. */
+
+ cvt = info->cvt ;
+ fpgm = info->fpgm;
+ prep = info->prep;
+
+ /* If both fpgm and prep are NULL, this font likely has no
+ instructions, so don't bother setting up the interpreter. */
+
+ if (!fpgm && !prep)
+ goto bail;
+
+ /* If the interpreter does not use the operand stack at all, it is
+ useless. In addition, some broken fonts specify some unnecessary
+ instructions in prep and set head->max_stack_elements to 0.
+
+ Don't create the interpreter in that case. */
+
+ if (!info->maxp->max_stack_elements)
+ goto bail;
+
+ /* Now, create the interpreter using the limits in info->maxp and
+ info->head. CVT can be NULL. */
+
+ interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head,
+ desc->tables->fvar,
+ info->font.pixel_size,
+ point_size);
+
+ /* Bail if the interpreter couldn't be created. */
+ if (!interpreter)
+ goto bail;
+
+ if (fpgm)
+ {
+ /* Otherwise, evaluate the font and cvt programs.
+
+ FIXME: make sure infinite loops inside these programs
+ cannot lock up Emacs. */
+
+ error = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (error)
+ {
+ /* If an error occurs, log it to the *Messages* buffer. */
+ message_with_string ("While interpreting font program: %s",
+ build_string (error), true);
+ goto bail1;
+ }
+
+ /* Save the graphics state. */
+ state = interpreter->state;
+ }
+
+ if (prep)
+ {
+ /* This will overwrite state if the instruction control is set
+ appropriately. */
+ error = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (error)
+ {
+ /* If an error occurs, log it to the *Messages* buffer. */
+ message_with_string ("While interpreting preprogram: %s",
+ build_string (error), true);
+ goto bail1;
+ }
+ }
+
+ /* The interpreter has been properly set up. */
+ info->fpgm = fpgm;
+ info->prep = prep;
+ info->cvt = cvt;
+ info->state = state;
+ info->interpreter = interpreter;
+
+ return;
+
+ bail1:
+ xfree (interpreter);
+ bail:
+ return;
+}
+
+/* Free each of the tables opened by `sfnt_open_tables', and possibly
+ file descriptors as well. Then, free TABLES itself. */
+
+static void
+sfnt_close_tables (struct sfnt_font_tables *tables)
+{
+ int rc;
+
+ xfree (tables->cmap);
+ xfree (tables->hhea);
+ xfree (tables->maxp);
+ xfree (tables->head);
+ xfree (tables->hmtx);
+#ifdef HAVE_MMAP
+ if (tables->glyf_table_mapped)
+ {
+ rc = sfnt_unmap_glyf_table (tables->glyf);
+
+ if (rc)
+ emacs_abort ();
+ }
+ else
+#endif /* HAVE_MMAP */
+ xfree (tables->glyf);
+ xfree (tables->loca_short);
+ xfree (tables->loca_long);
+ xfree (tables->prep);
+ xfree (tables->fpgm);
+ xfree (tables->cvt);
+ xfree (tables->fvar);
+ xfree (tables->avar);
+ xfree (tables->gvar);
+ xfree (tables->cvar);
+ xfree (tables->cmap_data);
+
+ if (tables->uvs)
+ sfnt_free_uvs_context (tables->uvs);
+
+#ifdef HAVE_HARFBUZZ
+ /* Close the font file. */
+
+ if (tables->fd != -1)
+ {
+ emacs_close (tables->fd);
+ tables->fd = -1;
+ }
+
+ /* Free its table directory. */
+ xfree (tables->directory);
+ tables->directory = NULL;
+#endif
+}
+
+/* Open font tables associated with the specified font description
+ DESC. Return the font tables, or NULL upon failure. */
+
+static struct sfnt_font_tables *
+sfnt_open_tables (struct sfnt_font_desc *desc)
+{
+ struct sfnt_font_tables *tables;
+ struct sfnt_offset_subtable *subtable;
+ int fd, i, rc;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_format_14 *format14;
+
+ tables = xzalloc (sizeof *tables);
+
+ /* Open the font. */
+ fd = emacs_open (desc->path, O_RDONLY, 0);
+
+ if (fd == -1)
+ goto bail;
+
+ /* Seek to the offset specified to the table directory. */
+
+ if (desc->offset
+ && lseek (fd, desc->offset, SEEK_SET) != desc->offset)
+ goto bail;
+
+ /* Read the offset subtable. */
+ subtable = sfnt_read_table_directory (fd);
+
+ if (!subtable)
+ goto bail1;
+
+ /* Read required tables. This font backend is supposed to be used
+ mostly on devices with flash memory, so the order in which they
+ are read is insignificant. */
+
+ tables->cmap = sfnt_read_cmap_table (fd, subtable, &subtables,
+ &data);
+ if (!tables->cmap)
+ goto bail2;
+
+ format14 = NULL;
+ tables->cmap_data
+ = sfntfont_select_cmap (tables->cmap,
+ subtables, data,
+ &tables->cmap_subtable,
+ &format14);
+
+ if (format14)
+ {
+ /* Build a UVS context from this format 14 mapping table. A UVS
+ context contains each variation selector supported by the
+ font, and a list of ``non-default'' mappings between base
+ characters and variation glyph IDs. */
+
+ tables->uvs = sfnt_create_uvs_context (format14, fd);
+ xfree (format14);
+ }
+
+ for (i = 0; i < tables->cmap->num_subtables; ++i)
+ {
+ if (data[i] != tables->cmap_data
+ /* format14 has already been freed. */
+ && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14)
+ xfree (data[i]);
+ }
+
+ xfree (subtables);
+ xfree (data);
+
+ if (!tables->cmap_data)
+ goto bail3;
+
+ /* Read the hhea, maxp, glyf, and head tables. */
+ tables->hhea = sfnt_read_hhea_table (fd, subtable);
+ tables->maxp = sfnt_read_maxp_table (fd, subtable);
+
+#ifdef HAVE_MMAP
+
+ /* First try to map the glyf table. If that fails, then read the
+ glyf table. */
+
+ tables->glyf = sfnt_map_glyf_table (fd, subtable);
+
+ /* Next, if this fails, read the glyf table. */
+
+ if (!tables->glyf)
+#endif /* HAVE_MMAP */
+ tables->glyf = sfnt_read_glyf_table (fd, subtable);
+#ifdef HAVE_MMAP
+ else
+ tables->glyf_table_mapped = true;
+#endif /* HAVE_MMAP */
+
+ tables->head = sfnt_read_head_table (fd, subtable);
+
+ /* If any of those tables couldn't be read, bail. */
+ if (!tables->hhea || !tables->maxp || !tables->glyf
+ || !tables->head)
+ goto bail4;
+
+ /* Now figure out which kind of loca table must be read based on
+ head->index_to_loc_format. */
+
+ if (tables->head->index_to_loc_format)
+ {
+ tables->loca_long
+ = sfnt_read_loca_table_long (fd, subtable);
+
+ if (!tables->loca_long)
+ goto bail4;
+ }
+ else
+ {
+ tables->loca_short
+ = sfnt_read_loca_table_short (fd, subtable);
+
+ if (!tables->loca_short)
+ goto bail4;
+ }
+
+ /* Read the horizontal metrics table. */
+ tables->hmtx = sfnt_read_hmtx_table (fd, subtable,
+ tables->hhea,
+ tables->maxp);
+ if (!tables->hmtx)
+ goto bail5;
+
+ /* Read instruction related font tables. These might not be
+ present, which is OK, since instructing fonts is optional. */
+ tables->prep = sfnt_read_prep_table (fd, subtable);
+ tables->fpgm = sfnt_read_fpgm_table (fd, subtable);
+ tables->cvt = sfnt_read_cvt_table (fd, subtable);
+
+ /* Read distortion related tables. These might not be present. */
+ tables->fvar = sfnt_read_fvar_table (fd, subtable);
+ tables->avar = sfnt_read_avar_table (fd, subtable);
+ tables->gvar = sfnt_read_gvar_table (fd, subtable);
+
+ if (tables->cvt && tables->fvar)
+ tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar,
+ tables->cvt);
+
+#ifdef HAVE_HARFBUZZ
+ /* Now copy over the subtable if necessary, as it is needed to read
+ extra font tables required by HarfBuzz. */
+ tables->directory = subtable;
+ tables->fd = fd;
+#else /* !HAVE_HARFBUZZ */
+ /* Otherwise, close the fd and free the table directory. */
+ xfree (subtable);
+ emacs_close (fd);
+#endif /* HAVE_HARFBUZZ */
+
+ return tables;
+
+ bail5:
+ xfree (tables->loca_long);
+ xfree (tables->loca_short);
+ bail4:
+ xfree (tables->hhea);
+ xfree (tables->maxp);
+
+#ifdef HAVE_MMAP
+ if (tables->glyf_table_mapped)
+ {
+ rc = sfnt_unmap_glyf_table (tables->glyf);
+
+ if (rc)
+ emacs_abort ();
+ }
+ else
+#endif /* HAVE_MMAP */
+ xfree (tables->glyf);
+
+ xfree (tables->head);
+
+ /* This comes under bail4 due to a peculiarity of how the four
+ tables above are validated. */
+ xfree (tables->cmap_data);
+ bail3:
+ if (tables->uvs)
+ sfnt_free_uvs_context (tables->uvs);
+
+ xfree (tables->cmap);
+ bail2:
+ xfree (subtable);
+ bail1:
+ emacs_close (fd);
+ bail:
+ xfree (tables);
+ return NULL;
+}
+
+/* Open or reference font tables corresponding to the specified font
+ DESC. Return NULL upon failure. */
+
+static struct sfnt_font_tables *
+sfnt_reference_font_tables (struct sfnt_font_desc *desc)
+{
+ if (desc->refcount)
+ {
+ desc->refcount++;
+ return desc->tables;
+ }
+
+ desc->tables = sfnt_open_tables (desc);
+
+ if (!desc->tables)
+ return NULL;
+
+ desc->refcount++;
+ return desc->tables;
+}
+
+/* Dereference font tables corresponding to the specified font
+ DESC. */
+
+static void
+sfnt_dereference_font_tables (struct sfnt_font_desc *desc)
+{
+ if (!desc->refcount)
+ emacs_abort ();
+
+ if (--desc->refcount)
+ return;
+
+ sfnt_close_tables (desc->tables);
+ desc->tables = NULL;
+ return;
+}
+
+/* Open the font corresponding to the font-entity FONT_ENTITY. Return
+ nil upon failure, else the opened font-object. */
+
+Lisp_Object
+sfntfont_open (struct frame *f, Lisp_Object font_entity,
+ int pixel_size)
+{
+ struct sfnt_font_info *font_info;
+ struct font *font;
+ struct sfnt_font_desc *desc;
+ Lisp_Object font_object;
+ struct charset *charset;
+ int point_size, instance, i;
+ Display_Info *dpyinfo;
+ struct sfnt_font_tables *tables;
+ Lisp_Object tem;
+
+ if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+ pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+ else if (pixel_size == 0)
+ {
+ /* This bit was copied from xfont.c. The values might need
+ adjustment. */
+
+ if (FRAME_FONT (f))
+ pixel_size = FRAME_FONT (f)->pixel_size;
+ else
+ pixel_size = 12;
+ }
+
+ /* Now find the font description corresponding to FONT_ENTITY. */
+
+ tem = AREF (font_entity, FONT_EXTRA_INDEX);
+ if (NILP (tem))
+ return Qnil;
+
+ desc = xmint_pointer (XCDR (XCAR (tem)));
+
+ /* Finally, see if a specific instance is associated with
+ FONT_ENTITY. */
+
+ instance = -1;
+ if (!NILP (XCDR (tem)))
+ instance = XFIXNUM (XCDR (XCAR (XCDR (tem))));
+
+ /* Build the font object. */
+ font_object = font_make_object (VECSIZE (struct sfnt_font_info),
+ font_entity, pixel_size);
+ font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object);
+
+ block_input ();
+
+ /* Initialize all the font driver specific data. */
+
+ font_info->cmap = NULL;
+ font_info->hhea = NULL;
+ font_info->maxp = NULL;
+ font_info->head = NULL;
+ font_info->glyf = NULL;
+ font_info->hmtx = NULL;
+ font_info->loca_short = NULL;
+ font_info->loca_long = NULL;
+ font_info->cmap_data = NULL;
+ font_info->prep = NULL;
+ font_info->fpgm = NULL;
+ font_info->cvt = NULL;
+ font_info->uvs = NULL;
+
+ font_info->outline_cache.next = &font_info->outline_cache;
+ font_info->outline_cache.last = &font_info->outline_cache;
+ font_info->outline_cache_size = 0;
+ font_info->raster_cache.next = &font_info->raster_cache;
+ font_info->raster_cache.last = &font_info->raster_cache;
+ font_info->raster_cache_size = 0;
+ font_info->interpreter = NULL;
+ font_info->scale = 0;
+ font_info->instance = -1;
+ font_info->blend.coords = NULL;
+#ifdef HAVE_MMAP
+ font_info->glyf_table_mapped = false;
+#endif /* HAVE_MMAP */
+#ifdef HAVE_HARFBUZZ
+ font_info->hb_font = NULL;
+ font_info->fd = -1;
+ font_info->directory = NULL;
+#endif /* HAVE_HARFBUZZ */
+
+ /* Read required tables. This font backend is supposed to be used
+ mostly on devices with flash memory, so the order in which they
+ are read is insignificant. */
+
+ tables = sfnt_reference_font_tables (desc);
+
+ if (!tables)
+ goto bail;
+
+ /* Copy fields from the table structure to the font for fast
+ access. */
+ font_info->cmap = tables->cmap;
+ font_info->hhea = tables->hhea;
+ font_info->maxp = tables->maxp;
+ font_info->head = tables->head;
+ font_info->hmtx = tables->hmtx;
+ font_info->glyf = tables->glyf;
+ font_info->loca_short = tables->loca_short;
+ font_info->loca_long = tables->loca_long;
+ font_info->prep = tables->prep;
+ font_info->fpgm = tables->fpgm;
+ font_info->cvt = tables->cvt ;
+ font_info->cmap_data = tables->cmap_data;
+ font_info->cmap_subtable = tables->cmap_subtable;
+ font_info->uvs = tables->uvs;
+
+ /* Calculate the font's scaling factor. */
+ font_info->scale = sfnt_get_scale (font_info->head, pixel_size);
+
+ /* Fill in font data. */
+ font = &font_info->font;
+ font->pixel_size = pixel_size;
+ font->driver = sfnt_font_driver;
+ font->encoding_charset = font->repertory_charset = -1;
+
+ /* Figure out which character set to use. */
+ charset = sfntfont_charset_for_cmap (font_info->cmap_subtable);
+
+ if (!charset)
+ goto bail6;
+
+ /* Set the character set IDs. */
+ font->encoding_charset = charset->id;
+ font->repertory_charset = charset->id;
+
+ /* Figure out the font ascent and descent. */
+ font->ascent
+ = ceil (font_info->hhea->ascent
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+ font->descent
+ = -floor (font_info->hhea->descent
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+ font->height = font->ascent + font->descent;
+
+ /* Set font->max_width to the maximum advance width. */
+ font->max_width = (font_info->hhea->advance_width_max
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+
+ /* Set generic attributes such as type and style. */
+ ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+ if (!NILP (desc->designer))
+ ASET (font_object, FONT_FOUNDRY_INDEX,
+ Fintern (desc->designer, Qnil));
+
+ ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+ ASET (font_object, FONT_REGISTRY_INDEX,
+ sfntfont_registry_for_desc (desc));
+
+ /* Size of 0 means the font is scalable. */
+ ASET (font_object, FONT_SIZE_INDEX, make_fixnum (0));
+ ASET (font_object, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+ ASET (font_object, FONT_SPACING_INDEX,
+ make_fixnum (desc->spacing));
+
+ /* Set the font style. */
+
+ FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+ make_fixnum (desc->width));
+ FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+ make_fixnum (desc->weight));
+ FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+ make_fixnum (desc->slant));
+
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+
+ /* Find out the minimum, maximum and average widths. */
+ sfntfont_probe_widths (font_info);
+
+ /* Clear various offsets. */
+ font_info->font.baseline_offset = 0;
+ font_info->font.relative_compose = 0;
+ font_info->font.default_ascent = 0;
+ font_info->font.vertical_centering = 0;
+ font_info->font.underline_position = -1;
+ font_info->font.underline_thickness = 0;
+
+ /* Now try to set up grid fitting for this font. */
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx
+ * dpyinfo->resy
+ / 2));
+ sfntfont_setup_interpreter (font_info, desc, point_size);
+
+ /* If an instance was specified and the font is distortable, set up
+ the blend. */
+
+ if (instance != -1
+ && desc->tables->fvar && desc->tables->gvar
+ /* Make sure the instance is within range. */
+ && instance < desc->tables->fvar->instance_count)
+ {
+ tem = AREF (desc->instances, instance);
+
+ if (!NILP (tem))
+ {
+ sfnt_init_blend (&font_info->blend, desc->tables->fvar,
+ desc->tables->gvar, desc->tables->avar,
+ desc->tables->cvar);
+
+ /* Copy over the coordinates. */
+ for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+ font_info->blend.coords[i]
+ = desc->tables->fvar->instance[instance].coords[i];
+
+ sfnt_normalize_blend (&font_info->blend);
+
+ /* Test whether or not the instance is actually redundant,
+ as all of its axis are at their default values. If so,
+ free the instance. */
+
+ for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+ {
+ if (font_info->blend.norm_coords[i])
+ break;
+ }
+
+ if (i == desc->tables->fvar->axis_count)
+ {
+ sfnt_free_blend (&font_info->blend);
+ goto cancel_blend;
+ }
+
+ /* If an interpreter was specified, distort it now. */
+
+ if (font_info->interpreter)
+ sfnt_vary_interpreter (font_info->interpreter,
+ &font_info->blend);
+
+ font_info->instance = instance;
+
+ /* Replace the style information with that of the
+ instance. */
+
+ FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+ AREF (tem, 2));
+ FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+ AREF (tem, 3));
+ FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+ AREF (tem, 4));
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+ }
+ }
+
+ cancel_blend:
+ /* Calculate the xfld name. */
+ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz will potentially read font tables after the font has
+ been opened by Emacs. Keep the font open, and record its offset
+ subtable. */
+ font_info->fd = tables->fd;
+ font_info->directory = tables->directory;
+#endif /* HAVE_HARFBUZZ */
+
+ /* Set font->desc so that font tables can be dereferenced if
+ anything goes wrong. */
+ font_info->desc = desc;
+
+#ifdef HAVE_MMAP
+ /* Link the font onto the font table. */
+ font_info->next = open_fonts;
+ open_fonts = font_info;
+#endif /* HAVE_MMAP */
+
+ /* All done. */
+ unblock_input ();
+ return font_object;
+
+ bail6:
+ sfnt_dereference_font_tables (desc);
+ font_info->desc = NULL;
+ bail:
+ unblock_input ();
+ return Qnil;
+}
+
+
+
+/* Metrics computation and other similar font backend functions. */
+
+/* Return the glyph code corresponding to C inside the font-object
+ FONT. Value is the glyph code upon success, else
+ FONT_INVALID_CODE. */
+
+unsigned int
+sfntfont_encode_char (struct font *font, int c)
+{
+ sfnt_glyph glyph;
+
+ /* Now look up the glyph. */
+ glyph = sfntfont_lookup_glyph ((struct sfnt_font_info *) font, c);
+
+ if (!glyph)
+ return FONT_INVALID_CODE;
+
+ return glyph;
+}
+
+/* Measure the single glyph GLYPH in the font FONT and return its
+ metrics in *PCM.
+
+ Instruct the glyph if possible.
+
+ Value is 0 upon success, 1 otherwise. */
+
+static int
+sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph,
+ struct font_metrics *pcm)
+{
+ struct sfnt_glyph_metrics metrics;
+ struct sfnt_glyph_outline *outline;
+
+ /* Now get the glyph outline, which is required to obtain the rsb,
+ ascent and descent. */
+ outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache,
+ font->scale,
+ &font->outline_cache_size,
+ &font->blend,
+ font->instance,
+ font->glyf, font->head,
+ font->hmtx, font->hhea,
+ font->maxp,
+ font->loca_short,
+ font->loca_long,
+ font->interpreter, &metrics,
+ &font->state);
+
+ if (!outline)
+ return 1;
+
+ /* Round the left side bearing downwards. */
+ pcm->lbearing = SFNT_FLOOR_FIXED (metrics.lbearing) / 65536;
+ pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536;
+
+ /* Round the advance, ascent and descent upwards. */
+ pcm->width = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) / 65536;
+ pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) / 65536;
+
+ sfntfont_dereference_outline (outline);
+ return 0;
+}
+
+/* Return the total text extents of NGLYPHS glyphs given as CODE in
+ the single font metrics array METRICS. */
+
+void
+sfntfont_text_extents (struct font *font, const unsigned int *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ int i, total_width;
+ struct font_metrics pcm;
+
+ total_width = 0;
+
+ /* First clear the metrics array. */
+ memset (metrics, 0, sizeof *metrics);
+
+ /* Get the metrcs one by one, then sum them up. */
+ for (i = 0; i < nglyphs; ++i)
+ {
+ if (!sfntfont_measure_pcm ((struct sfnt_font_info *) font,
+ code[i], &pcm))
+ {
+ /* Add the per-char metric (PCM) to the metrics in
+ METRICS. */
+
+ if (total_width + pcm.lbearing < metrics->lbearing)
+ metrics->lbearing = total_width + pcm.lbearing;
+
+ if (total_width + pcm.rbearing > metrics->rbearing)
+ metrics->rbearing = total_width + pcm.rbearing;
+
+ if (pcm.ascent > metrics->ascent)
+ metrics->ascent = pcm.ascent;
+
+ if (pcm.descent > metrics->descent)
+ metrics->descent = pcm.descent;
+
+ total_width += pcm.width;
+ }
+ }
+
+ metrics->width = total_width;
+}
+
+/* Close the font FONT, discarding all tables inside it and
+ dereferencing all cached outlines and rasters. */
+
+void
+sfntfont_close (struct font *font)
+{
+ struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+ struct sfnt_font_info **next;
+#endif /* HAVE_MMAP */
+
+ info = (struct sfnt_font_info *) font;
+
+ /* If info->desc is still set, dereference the font tables. */
+ if (info->desc)
+ sfnt_dereference_font_tables (info->desc);
+ info->desc = NULL;
+
+ /* Free the interpreter, which is created on a per font basis. */
+ xfree (info->interpreter);
+
+ /* Clear these fields. It seems that close can be called twice,
+ once during font driver destruction, and once during GC. */
+
+ info->cmap = NULL;
+ info->hhea = NULL;
+ info->maxp = NULL;
+ info->head = NULL;
+ info->hhea = NULL;
+ info->glyf = NULL;
+ info->loca_short = NULL;
+ info->loca_long = NULL;
+ info->cmap_data = NULL;
+ info->prep = NULL;
+ info->fpgm = NULL;
+ info->cvt = NULL;
+ info->interpreter = NULL;
+ info->uvs = NULL;
+
+ /* Deinitialize the blend. */
+ if (info->instance != -1 && info->blend.coords)
+ sfnt_free_blend (&info->blend);
+ info->instance = -1;
+
+#ifdef HAVE_MMAP
+
+ /* Unlink INFO. */
+
+ next = &open_fonts;
+ while (*next && (*next) != info)
+ next = &(*next)->next;
+
+ if (*next)
+ *next = info->next;
+ info->next = NULL;
+
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* These fields will be freed or closed by
+ sfnt_dereference_font_tables, but clear them here for good
+ measure. */
+ info->directory = NULL;
+ info->fd = -1;
+
+ /* Free any hb_font created. */
+
+ if (info->hb_font)
+ {
+ hb_font_destroy (info->hb_font);
+ info->hb_font = NULL;
+ }
+#endif
+
+ sfntfont_free_outline_cache (&info->outline_cache);
+ sfntfont_free_raster_cache (&info->raster_cache);
+}
+
+
+
+/* Glyph display. */
+
+/* Function called to actually draw rasters to the glass. */
+static sfntfont_put_glyph_proc sfnt_put_glyphs;
+
+/* Draw glyphs in S->char2b starting from FROM to TO, with the origin
+ at X and baseline at Y. Fill the background from X, Y +
+ FONT_DESCENT to X + S->background_width, Y - FONT_ASCENT with the
+ background color if necessary. Use the foreground and background
+ colors in S->gc. */
+
+int
+sfntfont_draw (struct glyph_string *s, int from, int to,
+ int x, int y, bool with_background)
+{
+ int length;
+ struct sfnt_raster **rasters;
+ int *x_coords, current_x, i;
+ struct sfnt_glyph_outline *outline;
+ struct font *font;
+ struct sfnt_font_info *info;
+ struct sfnt_glyph_metrics metrics;
+
+ length = to - from;
+ font = s->font;
+ info = (struct sfnt_font_info *) font;
+
+ rasters = alloca (length * sizeof *rasters);
+ x_coords = alloca (length * sizeof *x_coords);
+ current_x = x;
+
+ /* Get rasters and outlines for them. */
+ for (i = from; i < to; ++i)
+ {
+ /* Look up the outline. */
+ outline = sfntfont_get_glyph_outline (s->char2b[i],
+ &info->outline_cache,
+ info->scale,
+ &info->outline_cache_size,
+ &info->blend,
+ info->instance,
+ info->glyf, info->head,
+ info->hmtx, info->hhea,
+ info->maxp,
+ info->loca_short,
+ info->loca_long,
+ info->interpreter,
+ &metrics,
+ &info->state);
+ x_coords[i - from] = 0;
+
+ if (!outline)
+ {
+ rasters[i - from] = NULL;
+ continue;
+ }
+
+ /* Rasterize the outline. */
+ rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i],
+ &info->raster_cache,
+ outline,
+ &info->raster_cache_size);
+ sfntfont_dereference_outline (outline);
+
+ if (!rasters[i - from])
+ continue;
+
+ /* Now work out where to put the outline. */
+ x_coords[i - from] = current_x;
+
+ if (s->padding_p)
+ current_x += 1;
+ else
+ current_x += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ }
+
+ /* Call the window system function to put the glyphs to the
+ frame. */
+ sfnt_put_glyphs (s, from, to, x, y, with_background,
+ rasters, x_coords);
+
+ /* Dereference all the rasters. */
+ for (i = 0; i < from - to; ++i)
+ {
+ if (rasters[i])
+ sfntfont_dereference_raster (rasters[i]);
+ }
+
+ return 1;
+}
+
+
+
+/* Other callbacks. */
+
+/* Return a list of each font family known to Emacs. F is supposed to
+ be a frame but is ignored. */
+
+Lisp_Object
+sfntfont_list_family (struct frame *f)
+{
+ Lisp_Object families;
+ struct sfnt_font_desc *desc;
+
+ families = Qnil;
+
+ for (desc = system_fonts; desc; desc = desc->next)
+ /* Add desc->family to the list. */
+ families = Fcons (desc->family, families);
+
+ /* Not sure if deleting duplicates is worth it. Is this ever
+ called? */
+ return families;
+}
+
+
+
+/* Unicode Variation Selector (UVS) support. This is typically
+ required for Harfbuzz. */
+
+/* Given a FONT object, a character C, and VARIATIONS, return the
+ number of non-default variation glyphs, and their glyph ids in
+ VARIATIONS.
+
+ For each variation selector character K with a non-default glyph in
+ the variation selector range 0xFE00 to 0xFE0F, set variations[K -
+ 0xFE0] to its ID.
+
+ For each variation selector character K with a non-default glyph in
+ the variation selector range 0xE0100 to 0xE01EF, set variations[K -
+ 0xE0100 + 16] to its ID.
+
+ If value is more than 0, set all other members of VARIATIONS to 0.
+ Else, the contents of VARIATIONS are undefined. */
+
+int
+sfntfont_get_variation_glyphs (struct font *font, int c,
+ unsigned variations[256])
+{
+ struct sfnt_font_info *info;
+ size_t i;
+ int n;
+ struct sfnt_mapped_variation_selector_record *record;
+
+ info = (struct sfnt_font_info *) font;
+ n = 0;
+
+ /* Return 0 if there is no UVS mapping table. */
+
+ if (!info->uvs)
+ return 0;
+
+ /* Clear the variations array. */
+
+ memset (variations, 0, sizeof *variations * 256);
+
+ /* Find the first 0xFExx selector. */
+
+ i = 0;
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector < 0xfe00)
+ ++i;
+
+ /* Fill in selectors 0 to 15. */
+
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector <= 0xfe0f)
+ {
+ record = &info->uvs->records[i];
+
+ /* If record has no non-default mappings, continue on to the
+ next selector. */
+
+ if (!record->nondefault_uvs)
+ goto next_selector;
+
+ /* Handle invalid unsorted tables. */
+
+ if (record->selector < 0xfe00)
+ return 0;
+
+ /* Find the glyph ID associated with C and put it in
+ VARIATIONS. */
+
+ variations[info->uvs->records[i].selector - 0xfe00]
+ = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+ if (variations[info->uvs->records[i].selector - 0xfe00])
+ ++n;
+
+ next_selector:
+ ++i;
+ }
+
+ /* Find the first 0xE0100 selector. */
+
+ i = 0;
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector < 0xe0100)
+ ++i;
+
+ /* Fill in selectors 16 to 255. */
+
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector <= 0xe01ef)
+ {
+ record = &info->uvs->records[i];
+
+ /* If record has no non-default mappings, continue on to the
+ next selector. */
+
+ if (!record->nondefault_uvs)
+ goto next_selector_1;
+
+ /* Handle invalid unsorted tables. */
+
+ if (record->selector < 0xe0100)
+ return 0;
+
+ /* Find the glyph ID associated with C and put it in
+ VARIATIONS. */
+
+ variations[info->uvs->records[i].selector - 0xe0100 + 16]
+ = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+ if (variations[info->uvs->records[i].selector - 0xe0100 + 16])
+ ++n;
+
+ next_selector_1:
+ ++i;
+ }
+
+ return n;
+}
+
+
+
+/* mmap specific stuff. */
+
+#ifdef HAVE_MMAP
+
+/* Return whether or not ADDR lies in a mapped glyph, and bus faults
+ should be ignored. */
+
+bool
+sfntfont_detect_sigbus (void *addr)
+{
+ struct sfnt_font_info *info;
+
+ for (info = open_fonts; info; info = info->next)
+ {
+ if (info->glyf_table_mapped
+ && (unsigned char *) addr >= info->glyf->glyphs
+ && (unsigned char *) addr < (info->glyf->glyphs
+ + info->glyf->size))
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+
+
+/* Harfbuzz font support. */
+
+#ifdef HAVE_HARFBUZZ
+
+#ifdef HAVE_MMAP
+
+/* Unmap the specified table. */
+
+static void
+sfntfont_unmap_blob (void *ptr)
+{
+ if (sfnt_unmap_table (ptr))
+ emacs_abort ();
+
+ xfree (ptr);
+}
+
+#endif /* HAVE_MMAP */
+
+/* Given a font DATA and a tag TAG, return the data of the
+ corresponding font table as a HarfBuzz blob. */
+
+static hb_blob_t *
+sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data)
+{
+ size_t size;
+ struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+ struct sfnt_mapped_table *table;
+ hb_blob_t *blob;
+
+ info = data;
+ table = xmalloc (sizeof *table);
+
+ if (!sfnt_map_table (info->fd, info->directory, tag,
+ table))
+ {
+ /* Create an hb_blob_t and return it.
+ TODO: record this mapping properly so that SIGBUS can
+ be handled. */
+
+ blob = hb_blob_create (table->data, table->length,
+ HB_MEMORY_MODE_READONLY,
+ table, sfntfont_unmap_blob);
+
+ /* Note that sfntfont_unmap_blob will be called if the empty
+ blob is returned. */
+ return blob;
+ }
+
+ xfree (table);
+#else /* !HAVE_MMAP */
+
+ /* Try to read the table conventionally. */
+ info = data;
+#endif /* HAVE_MMAP */
+
+ data = sfnt_read_table (info->fd, info->directory, tag,
+ &size);
+
+ if (!data)
+ return NULL;
+
+ return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE,
+ data, xfree);
+}
+
+/* Create or return a HarfBuzz font object corresponding to the
+ specified FONT. Return the scale to convert between fwords and
+ pixels in POSITION_UNIT. */
+
+hb_font_t *
+sfntfont_begin_hb_font (struct font *font, double *position_unit)
+{
+ struct sfnt_font_info *info;
+ hb_face_t *face;
+ int factor;
+
+ info = (struct sfnt_font_info *) font;
+
+ if (info->hb_font)
+ {
+ /* Calculate the scale factor. */
+ *position_unit = 1.0 / 64.0;
+ return info->hb_font;
+ }
+
+ /* Create a face and then a font. */
+ face = hb_face_create_for_tables (sfntfont_get_font_table, font,
+ NULL);
+
+ if (hb_face_get_glyph_count (face) > 0)
+ {
+ info->hb_font = hb_font_create (face);
+ if (!info->hb_font)
+ goto bail;
+
+ factor = font->pixel_size;
+
+ /* Set the scale and PPEM values. */
+ hb_font_set_scale (info->hb_font, factor * 64, factor * 64);
+ hb_font_set_ppem (info->hb_font, factor, factor);
+
+#ifdef HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE
+ /* Set the instance if this is a distortable font. */
+ if (info->instance != -1)
+ hb_font_set_var_named_instance (info->hb_font,
+ info->instance);
+#endif /* HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE */
+
+ /* This is needed for HarfBuzz before 2.0.0; it is the default
+ in later versions. */
+ hb_ot_font_set_funcs (info->hb_font);
+ }
+
+ bail:
+ hb_face_destroy (face);
+
+ /* Calculate the scale factor. */
+ *position_unit = 1.0 / 64.0;
+ return info->hb_font;
+}
+
+#endif /* HAVE_HARFBUZZ */
+
+
+
+void
+syms_of_sfntfont (void)
+{
+ DEFSYM (Qutf_16be, "utf-16be");
+ DEFSYM (Qmac_roman, "mac-roman");
+ DEFSYM (Qchinese_big5, "chinese-big5");
+ DEFSYM (Qunicode_bmp, "unicode-bmp");
+ DEFSYM (Qucs, "ucs");
+ DEFSYM (Qjapanese_jisx0208, "japanese-jisx0208");
+ DEFSYM (Qgbk, "gbk");
+ DEFSYM (Qkorean_ksc5601, "korean-ksc5601");
+ 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");
+ DEFSYM (Qfont_instance, "font-instance");
+
+ /* Char-table purpose. */
+ DEFSYM (Qfont_lookup_cache, "font-lookup-cache");
+
+ /* Set up staticpros. */
+ sfnt_vendor_name = Qnil;
+ staticpro (&sfnt_vendor_name);
+
+ /* This variable is supposed to be set by the platform specific part
+ of the font backend. */
+ DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist,
+ doc: /* Alist between "emulated" and actual font family names.
+
+Much Emacs code assumes that font families named "Monospace" and "Sans
+Serif" exist, and map to the default monospace and Sans Serif fonts on
+a system. When the `sfnt' font driver is asked to look for a font
+with one of the families in this alist, it uses its value instead. */);
+ Vsfnt_default_family_alist = Qnil;
+}
+
+void
+mark_sfntfont (void)
+{
+ struct sfnt_font_desc *desc;
+
+ /* Mark each font desc. */
+ for (desc = system_fonts; desc; desc = desc->next)
+ {
+ mark_object (desc->family);
+ mark_object (desc->style);
+ mark_object (desc->adstyle);
+ mark_object (desc->instances);
+ mark_object (desc->languages);
+ mark_object (desc->registry);
+ mark_object (desc->char_cache);
+ mark_object (desc->designer);
+ }
+}
+
+void
+init_sfntfont (void)
+{
+
+}
+
+
+
+/* Initialize the sfntfont font driver. VENDOR_TYPE is the type of
+ all font entities created. DRIVER is the font driver that is saved
+ in font objects. PUT_GLYPHS is a function that is called with 8
+ arguments, S, FROM, TO, X, Y, WITH_BACKGROUND, RASTERS, and
+ X_COORDS, and should draw all the rasters in RASTERS to S->f,
+ originating at X_COORDS[i], Y, along with filling the background if
+ WITH_BACKGROUND is specified. */
+
+void
+init_sfntfont_vendor (Lisp_Object vendor_name,
+ const struct font_driver *driver,
+ sfntfont_put_glyph_proc put_glyphs)
+{
+ sfnt_vendor_name = vendor_name;
+ sfnt_font_driver = driver;
+ sfnt_put_glyphs = put_glyphs;
+}
diff --git a/src/sfntfont.h b/src/sfntfont.h
new file mode 100644
index 00000000000..df387512d0d
--- /dev/null
+++ b/src/sfntfont.h
@@ -0,0 +1,79 @@
+/* sfnt format font driver for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _SFNTFONT_H_
+#define _SFNTFONT_H_
+
+#include "lisp.h"
+#include "frame.h"
+#include "font.h"
+#include "sfnt.h"
+
+extern int sfnt_enum_font (const char *);
+
+
+/* Font driver callbacks. */
+
+extern Lisp_Object sfntfont_list (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_match (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_open (struct frame *, Lisp_Object, int);
+
+extern unsigned int sfntfont_encode_char (struct font *, int);
+extern void sfntfont_text_extents (struct font *, const unsigned int *,
+ int, struct font_metrics *);
+extern void sfntfont_close (struct font *);
+extern int sfntfont_draw (struct glyph_string *, int, int,
+ int, int, bool);
+extern Lisp_Object sfntfont_list_family (struct frame *);
+extern int sfntfont_get_variation_glyphs (struct font *, int, unsigned[256]);
+
+
+/* Initialization functions. */
+
+typedef void (*sfntfont_put_glyph_proc) (struct glyph_string *, int, int,
+ int, int, bool, struct sfnt_raster **,
+ int *);
+
+extern void syms_of_sfntfont (void);
+extern void init_sfntfont (void);
+extern void mark_sfntfont (void);
+extern void init_sfntfont_vendor (Lisp_Object, const struct font_driver *,
+ sfntfont_put_glyph_proc);
+
+
+/* mmap specific functions. */
+
+#ifdef HAVE_MMAP
+
+extern bool sfntfont_detect_sigbus (void *);
+
+#endif /* HAVE_MMAP */
+
+
+
+/* HarfBuzz specific functions. */
+
+#ifdef HAVE_HARFBUZZ
+
+extern hb_font_t *sfntfont_begin_hb_font (struct font *, double *);
+
+#endif /* HAVE_HARFBUZZ */
+
+#endif /* _SFNTFONT_H_ */
diff --git a/src/sound.c b/src/sound.c
index 145100cd433..a51cdb6d97a 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -1384,7 +1384,7 @@ Internal use only, use `play-sound' instead. */)
/* Open the sound file. */
current_sound->fd =
openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil,
- false, false);
+ false, false, NULL);
if (current_sound->fd < 0)
sound_perror ("Could not open sound file");
diff --git a/src/sysdep.c b/src/sysdep.c
index 443602a2d6d..bec2c00d3e5 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -134,6 +134,14 @@ int _cdecl _spawnlp (int, const char *, const char *, ...);
# include <sys/socket.h>
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
/* Declare here, including term.h is problematic on some systems. */
extern void tputs (const char *, int, int (*)(int));
@@ -790,6 +798,7 @@ init_sigio (int fd)
#endif
}
+#ifndef HAVE_ANDROID
#ifndef DOS_NT
#ifdef F_SETOWN
static void
@@ -801,6 +810,7 @@ reset_sigio (int fd)
}
#endif /* F_SETOWN */
#endif
+#endif
void
request_sigio (void)
@@ -972,6 +982,8 @@ narrow_foreground_group (int fd)
tcsetpgrp_without_stopping (fd, getpid ());
}
+#ifndef HAVE_ANDROID
+
/* Set the tty to our original foreground group. */
static void
widen_foreground_group (int fd)
@@ -979,6 +991,9 @@ widen_foreground_group (int fd)
if (inherited_pgroup && setpgid (0, inherited_pgroup) == 0)
tcsetpgrp_without_stopping (fd, inherited_pgroup);
}
+
+#endif
+
/* Getting and setting emacs_tty structures. */
@@ -1496,6 +1511,8 @@ reset_sys_modes (struct tty_display_info *tty_out)
fflush (stdout);
return;
}
+
+#ifndef HAVE_ANDROID
if (!tty_out->term_initted)
return;
@@ -1552,6 +1569,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
#endif
widen_foreground_group (fileno (tty_out->input));
+#endif
}
#ifdef HAVE_PTYS
@@ -1802,7 +1820,45 @@ handle_arith_signal (int sig)
xsignal0 (Qarith_error);
}
-#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+
+static void
+handle_sigbus (int sig, siginfo_t *siginfo, void *arg)
+{
+ /* If this arrives during sfntfont_open, then Emacs may be
+ screwed. */
+
+ if (sfntfont_detect_sigbus (siginfo->si_addr))
+ return;
+
+ handle_fatal_signal (sig);
+}
+
+/* Try to set up SIGBUS handling for the sfnt font driver.
+ Value is 1 upon failure, 0 otherwise. */
+
+static int
+init_sigbus (void)
+{
+ struct sigaction sa;
+
+ sigfillset (&sa.sa_mask);
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_SIGINFO;
+
+ if (sigaction (SIGBUS, &sa, NULL))
+ return 1;
+
+ return 0;
+}
+
+#endif
+
+/* This does not work on Android and interferes with the system
+ tombstone generation. */
+
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT \
+ && (!defined HAVE_ANDROID || defined ANDROID_STUBIFY)
/* Alternate stack used by SIGSEGV handler below. */
@@ -1914,12 +1970,16 @@ init_sigsegv (void)
#else /* not HAVE_STACK_OVERFLOW_HANDLING or WINDOWSNT */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
static bool
init_sigsegv (void)
{
return 0;
}
+#endif
+
#endif /* HAVE_STACK_OVERFLOW_HANDLING && !WINDOWSNT */
static void
@@ -2027,12 +2087,17 @@ init_signals (void)
#endif /* __vax__ */
}
+ /* SIGUSR1 and SIGUSR2 are used internally by the android_select
+ function. */
+#if !defined HAVE_ANDROID
#ifdef SIGUSR1
add_user_signal (SIGUSR1, "sigusr1");
#endif
#ifdef SIGUSR2
add_user_signal (SIGUSR2, "sigusr2");
#endif
+#endif
+
sigaction (SIGABRT, &thread_fatal_action, 0);
#ifdef SIGPRE
sigaction (SIGPRE, &thread_fatal_action, 0);
@@ -2056,10 +2121,15 @@ init_signals (void)
sigaction (SIGEMT, &thread_fatal_action, 0);
#endif
#ifdef SIGBUS
- sigaction (SIGBUS, &thread_fatal_action, 0);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+ if (init_sigbus ())
#endif
+ sigaction (SIGBUS, &thread_fatal_action, 0);
+#endif
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
if (!init_sigsegv ())
sigaction (SIGSEGV, &thread_fatal_action, 0);
+#endif
#ifdef SIGSYS
sigaction (SIGSYS, &thread_fatal_action, 0);
#endif
@@ -2313,7 +2383,8 @@ emacs_backtrace (int backtrace_limit)
}
}
-#ifndef HAVE_NTGUI
+#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY)
void
emacs_abort (void)
{
@@ -2335,11 +2406,20 @@ int
emacs_fstatat (int dirfd, char const *filename, void *st, int flags)
{
int r;
- while ((r = fstatat (dirfd, filename, st, flags)) != 0 && errno == EINTR)
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ while ((r = fstatat (dirfd, filename, st, flags)) != 0
+ && errno == EINTR)
maybe_quit ();
+#else
+ while ((r = android_fstatat (dirfd, filename, st, flags)) != 0
+ && errno == EINTR)
+ maybe_quit ();
+#endif
return r;
}
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
static int
sys_openat (int dirfd, char const *file, int oflags, int mode)
{
@@ -2354,6 +2434,28 @@ sys_openat (int dirfd, char const *file, int oflags, int mode)
#endif
}
+#endif
+
+int
+sys_fstat (int fd, struct stat *statb)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return fstat (fd, statb);
+#else
+ return android_fstat (fd, statb);
+#endif
+}
+
+int
+sys_faccessat (int fd, const char *pathname, int mode, int flags)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return faccessat (fd, pathname, mode, flags);
+#else
+ return android_faccessat (fd, pathname, mode, flags);
+#endif
+}
+
/* Assuming the directory DIRFD, open FILE for Emacs use,
using open flags OFLAGS and mode MODE.
Use binary I/O on systems that care about text vs binary I/O.
@@ -2362,6 +2464,8 @@ sys_openat (int dirfd, char const *file, int oflags, int mode)
Do not fail merely because the open was interrupted by a signal.
Allow the user to quit. */
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
int
emacs_openat (int dirfd, char const *file, int oflags, int mode)
{
@@ -2374,10 +2478,23 @@ emacs_openat (int dirfd, char const *file, int oflags, int mode)
return fd;
}
+#endif
+
int
emacs_open (char const *file, int oflags, int mode)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ int fd;
+#endif
+
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return emacs_openat (AT_FDCWD, file, oflags, mode);
+#else
+ while ((fd = android_open (file, oflags, mode)) < 0 && errno == EINTR)
+ maybe_quit ();
+
+ return fd;
+#endif
}
/* Same as above, but doesn't allow the user to quit. */
@@ -2389,9 +2506,15 @@ emacs_open_noquit (char const *file, int oflags, int mode)
if (! (oflags & O_TEXT))
oflags |= O_BINARY;
oflags |= O_CLOEXEC;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
do
fd = open (file, oflags, mode);
while (fd < 0 && errno == EINTR);
+#else
+ do
+ fd = android_open (file, oflags, mode);
+ while (fd < 0 && errno == EINTR);
+#endif
return fd;
}
@@ -2441,6 +2564,8 @@ emacs_pipe (int fd[2])
For the background behind this mess, please see Austin Group defect 529
<https://austingroupbugs.net/view.php?id=529>. */
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
#ifndef POSIX_CLOSE_RESTART
# define POSIX_CLOSE_RESTART 1
static int
@@ -2467,6 +2592,8 @@ posix_close (int fd, int flag)
}
#endif
+#endif
+
/* Close FD, retrying if interrupted. If successful, return 0;
otherwise, return -1 and set errno to a non-EINTR value. Consider
an EINPROGRESS error to be successful, as that's merely a signal
@@ -2479,9 +2606,17 @@ posix_close (int fd, int flag)
int
emacs_close (int fd)
{
+ int r;
+
while (1)
{
- int r = posix_close (fd, POSIX_CLOSE_RESTART);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ r = posix_close (fd, POSIX_CLOSE_RESTART);
+#else
+ r = android_close (fd) == 0 || errno == EINTR ? 0 : -1;
+#define POSIX_CLOSE_RESTART 1
+#endif
+
if (r == 0)
return r;
if (!POSIX_CLOSE_RESTART || errno != EINTR)
@@ -2492,6 +2627,20 @@ emacs_close (int fd)
}
}
+/* Wrapper around fclose. On Android, this calls `android_fclose' to
+ clear information associated with the FILE's file descriptor if
+ necessary. */
+
+int
+emacs_fclose (FILE *stream)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return fclose (stream);
+#else
+ return android_fclose (stream);
+#endif
+}
+
/* Maximum number of bytes to read or write in a single system call.
This works around a serious bug in Linux kernels before 2.6.16; see
<https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.
@@ -2736,6 +2885,15 @@ errwrite (void const *buf, ptrdiff_t nbuf)
void
close_output_streams (void)
{
+ /* Android comes with some kind of ``file descriptor sanitizer''
+ that aborts when stdout or stderr is closed. */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ fflush (stderr);
+ fflush (stdout);
+ return;
+#endif
+
if (close_stream (stdout) != 0)
{
emacs_perror ("Write error to standard output");
diff --git a/src/term.c b/src/term.c
index 4df3de8f4a5..4de57ca1afe 100644
--- a/src/term.c
+++ b/src/term.c
@@ -62,6 +62,8 @@ static int been_here = -1;
#include "w32term.h"
#endif
+#ifndef HAVE_ANDROID
+
static void tty_set_scroll_region (struct frame *f, int start, int stop);
static void turn_on_face (struct frame *, int face_id);
static void turn_off_face (struct frame *, int face_id);
@@ -73,11 +75,15 @@ static void clear_tty_hooks (struct terminal *terminal);
static void set_tty_hooks (struct terminal *terminal);
static void dissociate_if_controlling_tty (int fd);
static void delete_tty (struct terminal *);
+
+#endif
+
static AVOID maybe_fatal (bool, struct terminal *, const char *, const char *,
...)
ATTRIBUTE_FORMAT_PRINTF (3, 5) ATTRIBUTE_FORMAT_PRINTF (4, 5);
static AVOID vfatal (const char *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
+#ifndef HAVE_ANDROID
#define OUTPUT(tty, a) \
emacs_tputs ((tty), a, \
@@ -95,6 +101,8 @@ static AVOID vfatal (const char *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
#define OUTPUT1_IF(tty, a) do { if (a) emacs_tputs ((tty), a, 1, cmputc); } while (0)
+#endif
+
/* Display space properties. */
/* Chain of all tty device parameters. */
@@ -117,10 +125,14 @@ enum no_color_bit
/* internal state */
+#ifndef HAVE_ANDROID
+
/* The largest frame width in any call to calculate_costs. */
static int max_frame_cols;
+#endif
+
#ifdef HAVE_GPM
@@ -133,6 +145,8 @@ struct tty_display_info *gpm_tty = NULL;
static int last_mouse_x, last_mouse_y;
#endif /* HAVE_GPM */
+#ifndef HAVE_ANDROID
+
/* Ring the bell on a tty. */
static void
@@ -718,7 +732,20 @@ encode_terminal_code (struct glyph *src, int src_len,
return (encode_terminal_dst);
}
+#else /* !HAVE_ANDROID */
+unsigned char *
+encode_terminal_code (struct glyph *src, int src_len,
+ struct coding_system *coding)
+{
+ /* Text terminals are simply not supported on Android. */
+ coding->produced = 0;
+ return NULL;
+}
+
+#endif /* HAVE_ANDROID */
+
+#ifndef HAVE_ANDROID
/* An implementation of write_glyphs for termcap frames. */
@@ -1046,8 +1073,10 @@ int
string_cost (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 0, evalcost);
+#endif
return cost;
}
@@ -1058,8 +1087,10 @@ static int
string_cost_one_line (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 1, evalcost);
+#endif
return cost;
}
@@ -1070,11 +1101,13 @@ int
per_line_cost (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 0, evalcost);
cost = - cost;
if (str)
tputs (str, 10, evalcost);
+#endif
return cost;
}
@@ -1147,11 +1180,14 @@ calculate_ins_del_char_costs (struct frame *f)
*p++ = (ins_startup_cost += ins_cost_per_char);
}
+#endif
+
void
calculate_costs (struct frame *frame)
{
FRAME_COST_BAUD_RATE (frame) = baud_rate;
+#ifndef HAVE_ANDROID
if (FRAME_TERMCAP_P (frame))
{
struct tty_display_info *tty = FRAME_TTY (frame);
@@ -1206,13 +1242,15 @@ calculate_costs (struct frame *frame)
cmcostinit (FRAME_TTY (frame)); /* set up cursor motion costs */
}
+#endif
}
-struct fkey_table {
+struct fkey_table
+{
const char *cap, *name;
};
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Termcap capability names that correspond directly to X keysyms.
Some of these (marked "terminfo") aren't supplied by old-style
(Berkeley) termcap entries. They're listed in X keysym order;
@@ -1443,6 +1481,9 @@ term_get_fkeys_1 (void)
#endif /* not DOS_NT */
+
+#ifndef HAVE_ANDROID
+
/***********************************************************************
Character Display Information
***********************************************************************/
@@ -1519,14 +1560,17 @@ append_glyph (struct it *it)
}
}
+#endif
+
/* For external use. */
void
tty_append_glyph (struct it *it)
{
+#ifndef HAVE_ANDROID
append_glyph (it);
+#endif
}
-
/* Produce glyphs for the display element described by IT. *IT
specifies what we want to produce a glyph for (character, image, ...),
and where in the glyph matrix we currently are (glyph row and hpos).
@@ -1549,6 +1593,7 @@ tty_append_glyph (struct it *it)
void
produce_glyphs (struct it *it)
{
+#ifndef HAVE_ANDROID
/* If a hook is installed, let it do the work. */
/* Nothing but characters are supported on terminal frames. */
@@ -1661,8 +1706,11 @@ produce_glyphs (struct it *it)
it->current_x += it->pixel_width;
it->ascent = it->max_ascent = it->phys_ascent = it->max_phys_ascent = 0;
it->descent = it->max_descent = it->phys_descent = it->max_phys_descent = 1;
+#endif
}
+#ifndef HAVE_ANDROID
+
/* Append glyphs to IT's glyph_row for the composition IT->cmp_id.
Called from produce_composite_glyph for terminal frames if
IT->glyph_row != NULL. IT->face_id contains the character's
@@ -2020,6 +2068,7 @@ turn_off_face (struct frame *f, int face_id)
OUTPUT1_IF (tty, tty->TS_orig_pair);
}
+#endif /* !HAVE_ANDROID */
/* Return true if the terminal on frame F supports all of the
capabilities in CAPS simultaneously. */
@@ -2027,8 +2076,9 @@ turn_off_face (struct frame *f, int face_id)
bool
tty_capable_p (struct tty_display_info *tty, unsigned int caps)
{
+#ifndef HAVE_ANDROID
#define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit) \
- if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit))) \
+ if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P (tty, NC_bit))) \
return 0;
TTY_CAPABLE_P_TRY (tty,
@@ -2048,6 +2098,9 @@ tty_capable_p (struct tty_display_info *tty, unsigned int caps)
/* We can do it! */
return 1;
+#else
+ return false;
+#endif
}
/* Return non-zero if the terminal is capable to display colors. */
@@ -2081,7 +2134,7 @@ TERMINAL does not refer to a text terminal. */)
return make_fixnum (t ? t->display_info.tty->TN_max_colors : 0);
}
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Declare here rather than in the function, as in the rest of Emacs,
to work around an HPUX compiler bug (?). See
@@ -2186,7 +2239,7 @@ set_tty_color_mode (struct tty_display_info *tty, struct frame *f)
}
}
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
char *
tty_type_name (Lisp_Object terminal)
@@ -2278,6 +2331,7 @@ suspended.
A suspended tty may be resumed by calling `resume-tty' on it. */)
(Lisp_Object tty)
{
+#ifndef HAVE_ANDROID
struct terminal *t = decode_tty_terminal (tty);
FILE *f;
@@ -2300,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on it. */)
#ifndef MSDOS
if (f != t->display_info.tty->output)
- fclose (t->display_info.tty->output);
- fclose (f);
+ emacs_fclose (t->display_info.tty->output);
+ emacs_fclose (f);
#endif
t->display_info.tty->input = 0;
@@ -2314,6 +2368,10 @@ A suspended tty may be resumed by calling `resume-tty' on it. */)
/* Clear display hooks to prevent further output. */
clear_tty_hooks (t);
+#else
+ /* This will always signal on Android. */
+ decode_tty_terminal (tty);
+#endif
return Qnil;
}
@@ -2337,9 +2395,12 @@ TTY may be a terminal object, a frame, or nil (meaning the selected
frame's terminal). */)
(Lisp_Object tty)
{
- struct terminal *t = decode_tty_terminal (tty);
+#ifndef HAVE_ANDROID
+ struct terminal *t;
int fd;
+ t = decode_tty_terminal (tty);
+
if (!t)
error ("Attempt to resume a non-text terminal device");
@@ -2396,10 +2457,15 @@ frame's terminal). */)
}
set_tty_hooks (t);
+#else
+ decode_tty_terminal (tty);
+#endif
return Qnil;
}
+#ifndef HAVE_ANDROID
+
DEFUN ("tty--set-output-buffer-size", Ftty__set_output_buffer_size,
Stty__set_output_buffer_size, 1, 2, 0, doc:
/* Set the output buffer size for a TTY.
@@ -2438,12 +2504,14 @@ A value of zero means TTY uses the system's default value. */)
error ("Not a tty terminal");
}
+#endif
+
/***********************************************************************
Mouse
***********************************************************************/
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Implementation of draw_row_with_mouse_face for TTY/GPM and macOS. */
void
@@ -2713,7 +2781,7 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
Menus
***********************************************************************/
-#if !defined (MSDOS)
+#if !defined (MSDOS) && !defined HAVE_ANDROID
/* TTY menu implementation and main ideas are borrowed from msdos.c.
@@ -3813,10 +3881,12 @@ tty_menu_show (struct frame *f, int x, int y, int menuflags,
return SAFE_FREE_UNBIND_TO (specpdl_count, entry);
}
-#endif /* !MSDOS */
+#endif /* !MSDOS && !defined HAVE_ANDROID */
-#ifndef MSDOS
+
+#if !defined MSDOS && !defined HAVE_ANDROID
+
/***********************************************************************
Initialization
***********************************************************************/
@@ -3846,7 +3916,7 @@ tty_free_frame_resources (struct frame *f)
xfree (f->output_data.tty);
}
-#else /* MSDOS */
+#elif defined MSDOS
/* Delete frame F's face cache. */
@@ -3856,8 +3926,13 @@ tty_free_frame_resources (struct frame *f)
eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f));
free_frame_faces (f);
}
-#endif /* MSDOS */
+
+#endif
+
+
+#ifndef HAVE_ANDROID
+
/* Reset the hooks in TERMINAL. */
static void
@@ -3952,6 +4027,8 @@ dissociate_if_controlling_tty (int fd)
}
}
+#endif /* !HAVE_ANDROID */
+
/* Create a termcap display on the tty device with the given name and
type.
@@ -3961,11 +4038,23 @@ dissociate_if_controlling_tty (int fd)
TERMINAL_TYPE is the termcap type of the device, e.g. "vt100".
- If MUST_SUCCEED is true, then all errors are fatal. */
+ If MUST_SUCCEED is true, then all errors are fatal. This function
+ always signals on Android, where text terminals are prohibited by
+ system policy (and the required libraries are usually not
+ available.) */
+
+#ifdef HAVE_ANDROID
+_Noreturn
+#endif
struct terminal *
init_tty (const char *name, const char *terminal_type, bool must_succeed)
{
+#ifdef HAVE_ANDROID
+ maybe_fatal (must_succeed, 0, "Text terminals are not supported"
+ " under Android", "Text terminals are not supported"
+ " under Android");
+#else
struct tty_display_info *tty = NULL;
struct terminal *terminal = NULL;
#ifndef DOS_NT
@@ -4455,6 +4544,7 @@ use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\
init_sys_modes (tty);
return terminal;
+#endif /* !HAVE_ANDROID */
}
@@ -4479,8 +4569,13 @@ maybe_fatal (bool must_succeed, struct terminal *terminal,
{
va_list ap;
va_start (ap, str2);
+
+#ifndef HAVE_ANDROID
if (terminal)
delete_tty (terminal);
+#else
+ eassert (terminal == NULL);
+#endif
if (must_succeed)
vfatal (str2, ap);
@@ -4498,6 +4593,8 @@ fatal (const char *str, ...)
+#ifndef HAVE_ANDROID
+
/* Delete the given tty terminal, closing all frames on it. */
static void
@@ -4543,25 +4640,27 @@ delete_tty (struct terminal *terminal)
{
delete_keyboard_wait_descriptor (fileno (tty->input));
if (tty->input != stdin)
- fclose (tty->input);
+ emacs_fclose (tty->input);
}
if (tty->output && tty->output != stdout && tty->output != tty->input)
- fclose (tty->output);
+ emacs_fclose (tty->output);
if (tty->termscript)
- fclose (tty->termscript);
+ emacs_fclose (tty->termscript);
xfree (tty->old_tty);
xfree (tty->Wcm);
xfree (tty);
}
+#endif
+
void
syms_of_term (void)
{
DEFVAR_BOOL ("system-uses-terminfo", system_uses_terminfo,
doc: /* Non-nil means the system uses terminfo rather than termcap.
This variable can be used by terminal emulator packages. */);
-#ifdef TERMINFO
+#if defined TERMINFO || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
system_uses_terminfo = 1;
#else
system_uses_terminfo = 0;
@@ -4602,21 +4701,25 @@ trigger redisplay. */);
defsubr (&Stty_top_frame);
defsubr (&Ssuspend_tty);
defsubr (&Sresume_tty);
+#ifndef HAVE_ANDROID
defsubr (&Stty__set_output_buffer_size);
defsubr (&Stty__output_buffer_size);
+#endif /* !HAVE_ANDROID */
#ifdef HAVE_GPM
defsubr (&Sgpm_mouse_start);
defsubr (&Sgpm_mouse_stop);
#endif /* HAVE_GPM */
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
default_orig_pair = NULL;
default_set_foreground = NULL;
default_set_background = NULL;
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
+#ifndef HAVE_ANDROID
encode_terminal_src = NULL;
encode_terminal_dst = NULL;
+#endif
DEFSYM (Qtty_mode_set_strings, "tty-mode-set-strings");
DEFSYM (Qtty_mode_reset_strings, "tty-mode-reset-strings");
diff --git a/src/termhooks.h b/src/termhooks.h
index ba04a6b7759..99f27cd668e 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -63,7 +63,8 @@ enum output_method
output_w32,
output_ns,
output_pgtk,
- output_haiku
+ output_haiku,
+ output_android,
};
/* Input queue declarations and hooks. */
@@ -516,12 +517,13 @@ struct terminal
/* Device-type dependent data shared amongst all frames on this terminal. */
union display_info
{
- struct tty_display_info *tty; /* termchar.h */
- struct x_display_info *x; /* xterm.h */
- struct w32_display_info *w32; /* w32term.h */
- struct ns_display_info *ns; /* nsterm.h */
- struct pgtk_display_info *pgtk; /* pgtkterm.h */
- struct haiku_display_info *haiku; /* haikuterm.h */
+ struct tty_display_info *tty; /* termchar.h */
+ struct x_display_info *x; /* xterm.h */
+ struct w32_display_info *w32; /* w32term.h */
+ struct ns_display_info *ns; /* nsterm.h */
+ struct pgtk_display_info *pgtk; /* pgtkterm.h */
+ struct haiku_display_info *haiku; /* haikuterm.h */
+ struct android_display_info *android; /* androidterm.h */
} display_info;
@@ -595,7 +597,8 @@ struct terminal
BGCOLOR. */
void (*query_frame_background_color) (struct frame *f, Emacs_Color *bgcolor);
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
/* On frame F, translate pixel colors to RGB values for the NCOLORS
colors in COLORS. Use cached information, if available. */
@@ -930,6 +933,9 @@ extern struct terminal *terminal_list;
#elif defined (HAVE_HAIKU)
#define TERMINAL_FONT_CACHE(t) \
(t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
+#elif defined (HAVE_ANDROID)
+#define TERMINAL_FONT_CACHE(t) \
+ (t->type == output_android ? t->display_info.android->name_list_element : Qnil)
#endif
extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index d13e3466512..07c37883f0e 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -451,6 +451,8 @@ return values. */)
return Qpgtk;
case output_haiku:
return Qhaiku;
+ case output_android:
+ return Qandroid;
default:
emacs_abort ();
}
diff --git a/src/textconv.c b/src/textconv.c
index 7ed8ede3544..26f351dc729 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -25,25 +25,38 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
ability to ``undo'' or ``edit'' previously composed text. This is
most commonly seen in input methods for CJK laguages for X Windows,
and is extensively used throughout Android by input methods for all
- kinds of scripts. */
+ kinds of scripts.
+
+ In addition, these input methods may also need to make detailed
+ edits to the content of a buffer. That is also handled here. */
#include <config.h>
#include "textconv.h"
#include "buffer.h"
#include "syntax.h"
+#include "blockinput.h"
-/* The window system's text conversion interface.
- NULL when the window system has not set up text conversion.
-
- This interface will later be heavily extended on the
- feature/android branch to deal with Android's much less
- straightforward text conversion protocols. */
+/* The window system's text conversion interface. NULL when the
+ window system has not set up text conversion. */
static struct textconv_interface *text_interface;
+/* How many times text conversion has been disabled. */
+
+static int suppress_conversion_count;
+
+/* Flags used to determine what must be sent after a batch edit
+ ends. */
+
+enum textconv_batch_edit_flags
+ {
+ PENDING_POINT_CHANGE = 1,
+ PENDING_COMPOSE_CHANGE = 2,
+ };
+
/* Copy the portion of the current buffer described by BEG, BEG_BYTE,
@@ -77,13 +90,46 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
size = end0 - beg0;
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
if (beg1 != -1)
- memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
+ memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
}
/* Conversion query. */
+/* Return the position of the active mark, or -1 if there is no mark
+ or it is not active. */
+
+static ptrdiff_t
+get_mark (void)
+{
+ if (!NILP (BVAR (current_buffer, mark_active))
+ && XMARKER (BVAR (current_buffer, mark))->buffer)
+ return marker_position (BVAR (current_buffer,
+ mark));
+
+ return -1;
+}
+
+/* Like Fselect_window. However, if WINDOW is a mini buffer window
+ but not the active minibuffer window, select its frame's selected
+ window instead. */
+
+static void
+select_window (Lisp_Object window, Lisp_Object norecord)
+{
+ struct window *w;
+
+ w = XWINDOW (window);
+
+ if (MINI_WINDOW_P (w)
+ && WINDOW_LIVE_P (window)
+ && !EQ (window, Factive_minibuffer_window ()))
+ window = WINDOW_XFRAME (w)->selected_window;
+
+ Fselect_window (window, norecord);
+}
+
/* Perform the text conversion operation specified in QUERY and return
the results.
@@ -91,19 +137,28 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
window and QUERY->factor times QUERY->direction from that
position. Return it in QUERY->text.
+ If QUERY->position is TYPE_MINIMUM (EMACS_INT) or EMACS_INT_MAX,
+ start at the window's last point or mark, whichever is greater or
+ smaller.
+
Then, either delete that text from the buffer if QUERY->operation
is TEXTCONV_SUBSTITUTION, or return 0.
+ If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
+ the conversion region in the specified direction if it is inside.
+
Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
or if deleting the text was successful, and 1 otherwise. */
int
-textconv_query (struct frame *f, struct textconv_callback_struct *query)
+textconv_query (struct frame *f, struct textconv_callback_struct *query,
+ int flags)
{
specpdl_ref count;
- ptrdiff_t pos, pos_byte, end, end_byte;
- ptrdiff_t temp, temp1;
+ ptrdiff_t pos, pos_byte, end, end_byte, start;
+ ptrdiff_t temp, temp1, mark;
char *buffer;
+ struct window *w;
/* Save the excursion, as there will be extensive changes to the
selected window. */
@@ -113,14 +168,69 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
/* Inhibit quitting. */
specbind (Qinhibit_quit, Qt);
- /* Temporarily switch to F's selected window. */
- Fselect_window (f->selected_window, Qt);
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window ((WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window), Qt);
+ w = XWINDOW (selected_window);
/* Now find the appropriate text bounds for QUERY. First, move
point QUERY->position steps forward or backwards. */
pos = PT;
+ /* If QUERY->position is EMACS_INT_MAX, use the last mark or the
+ ephemeral last point, whichever is greater.
+
+ The opposite applies for EMACS_INT_MIN. */
+
+ mark = get_mark ();
+
+ if (query->position == EMACS_INT_MAX)
+ {
+ pos = (mark == -1
+ ? w->ephemeral_last_point
+ : max (w->ephemeral_last_point, mark));
+ goto escape1;
+ }
+ else if (query->position == TYPE_MINIMUM (EMACS_INT))
+ {
+ pos = (mark == -1
+ ? w->ephemeral_last_point
+ : min (w->ephemeral_last_point, mark));
+ goto escape1;
+ }
+
+ /* Next, if POS lies within the conversion region and the caller
+ asked for it to be moved away, move it away from the conversion
+ region. */
+
+ if (flags & TEXTCONV_SKIP_CONVERSION_REGION
+ && MARKERP (f->conversion.compose_region_start))
+ {
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+
+ if (pos >= start && pos < end)
+ {
+ switch (query->direction)
+ {
+ case TEXTCONV_FORWARD_CHAR:
+ case TEXTCONV_FORWARD_WORD:
+ case TEXTCONV_CARET_DOWN:
+ case TEXTCONV_NEXT_LINE:
+ case TEXTCONV_LINE_START:
+ pos = end;
+ break;
+
+ default:
+ pos = max (BEGV, start - 1);
+ break;
+ }
+ }
+ }
+
/* If pos is outside the accessible part of the buffer or if it
overflows, move back to point or to the extremes of the
accessible region. */
@@ -128,6 +238,8 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
if (ckd_add (&pos, pos, query->position))
pos = PT;
+ escape1:
+
if (pos < BEGV)
pos = BEGV;
@@ -287,6 +399,1216 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
return 0;
}
+/* Update the overlay displaying the conversion area on F after a
+ change to the conversion region. */
+
+static void
+sync_overlay (struct frame *f)
+{
+ if (MARKERP (f->conversion.compose_region_start)
+ && !NILP (Vtext_conversion_face))
+ {
+ if (NILP (f->conversion.compose_region_overlay))
+ {
+ f->conversion.compose_region_overlay
+ = Fmake_overlay (f->conversion.compose_region_start,
+ f->conversion.compose_region_end, Qnil,
+ Qt, Qnil);
+ Foverlay_put (f->conversion.compose_region_overlay,
+ Qface, Vtext_conversion_face);
+ }
+
+ Fmove_overlay (f->conversion.compose_region_overlay,
+ f->conversion.compose_region_start,
+ f->conversion.compose_region_end, Qnil);
+ }
+ else if (!NILP (f->conversion.compose_region_overlay))
+ {
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+ f->conversion.compose_region_overlay = Qnil;
+ }
+}
+
+/* Record a change to the current buffer as a result of an
+ asynchronous text conversion operation on F.
+
+ Consult the doc string of `text-conversion-edits' for the meaning
+ of BEG, END, and EPHEMERAL. */
+
+static void
+record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
+ Lisp_Object ephemeral)
+{
+ Lisp_Object buffer, beg_marker, end_marker;
+
+ XSETBUFFER (buffer, current_buffer);
+
+ /* Make markers for both BEG and END. */
+ beg_marker = build_marker (current_buffer, beg,
+ CHAR_TO_BYTE (beg));
+
+ /* If BEG and END are identical, make sure to keep the markers
+ eq. */
+
+ if (beg == end)
+ end_marker = beg_marker;
+ else
+ {
+ end_marker = build_marker (current_buffer, end,
+ CHAR_TO_BYTE (end));
+
+ /* Otherwise, make sure the marker extends past inserted
+ text. */
+ Fset_marker_insertion_type (end_marker, Qt);
+ }
+
+ Vtext_conversion_edits
+ = Fcons (list4 (buffer, beg_marker, end_marker,
+ ephemeral),
+ Vtext_conversion_edits);
+}
+
+/* Reset F's text conversion state. Delete any overlays or
+ markers inside. */
+
+void
+reset_frame_state (struct frame *f)
+{
+ struct text_conversion_action *last, *next;
+
+ /* Make the composition region markers point elsewhere. */
+
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+
+ /* Delete each text conversion action queued up. */
+
+ next = f->conversion.actions;
+ while (next)
+ {
+ last = next;
+ next = next->next;
+
+ /* Say that the conversion is finished. */
+ if (text_interface && text_interface->notify_conversion)
+ text_interface->notify_conversion (last->counter);
+
+ xfree (last);
+ }
+ f->conversion.actions = NULL;
+
+ /* Clear batch edit state. */
+ f->conversion.batch_edit_count = 0;
+ f->conversion.batch_edit_flags = 0;
+}
+
+/* Return whether or not there are pending edits from an input method
+ on any frame. */
+
+bool
+detect_conversion_events (void)
+{
+ Lisp_Object tail, frame;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ if (XFRAME (frame)->conversion.actions)
+ return true;
+ }
+
+ return false;
+}
+
+/* Restore the selected window WINDOW. */
+
+static void
+restore_selected_window (Lisp_Object window)
+{
+ /* FIXME: not sure what to do if WINDOW has been deleted. */
+ select_window (window, Qt);
+}
+
+/* Commit the given text in the composing region. If there is no
+ composing region, then insert the text after F's selected window's
+ last point instead. Finally, remove the composing region.
+
+ Then, move point to POSITION relative to TEXT. If POSITION is
+ greater than zero, it is relative to the character at the end of
+ TEXT; otherwise, it is relative to the start of TEXT. */
+
+static void
+really_commit_text (struct frame *f, EMACS_INT position,
+ Lisp_Object text)
+{
+ specpdl_ref count;
+ ptrdiff_t wanted, start, end;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Now detect whether or not there is a composing region.
+ If there is, then replace it with TEXT. Don't do that
+ otherwise. */
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ /* Replace its contents. */
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+ del_range (start, end);
+ record_buffer_change (start, start, Qnil);
+ Finsert (1, &text);
+ record_buffer_change (start, PT, text);
+
+ /* Move to a the position specified in POSITION. If POSITION is
+ less than zero, it is relative to the start of the text that
+ was inserted. */
+
+ if (position <= 0)
+ {
+ wanted
+ = marker_position (f->conversion.compose_region_start);
+
+ if (INT_ADD_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+
+ set_point (wanted);
+ }
+ else
+ {
+ /* Otherwise, it is relative to the last character in
+ TEXT. */
+
+ wanted
+ = marker_position (f->conversion.compose_region_end);
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+
+ set_point (wanted);
+ }
+
+ /* Make the composition region markers point elsewhere. */
+
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+ }
+ else
+ {
+ /* Otherwise, move the text and point to an appropriate
+ location. */
+ wanted = PT;
+ Finsert (1, &text);
+ record_buffer_change (wanted, PT, text);
+
+ if (position <= 0)
+ {
+ if (INT_ADD_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+
+ set_point (wanted);
+ }
+ else
+ {
+ wanted = PT;
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+
+ set_point (wanted);
+ }
+ }
+
+ /* This should deactivate the mark. */
+ call0 (Qdeactivate_mark);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+ unbind_to (count, Qnil);
+}
+
+/* Remove the composition region on the frame F, while leaving its
+ contents intact. */
+
+static void
+really_finish_composing_text (struct frame *f)
+{
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+}
+
+/* Set the composing text on F to TEXT. Then, move point to an
+ appropriate position relative to POSITION, and call
+ `compose_region_changed' in the text conversion interface should
+ point not have been changed relative to F's old selected window's
+ last point. */
+
+static void
+really_set_composing_text (struct frame *f, ptrdiff_t position,
+ Lisp_Object text)
+{
+ specpdl_ref count;
+ ptrdiff_t start, wanted, end;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ w = XWINDOW (f->old_selected_window);
+ select_window (f->old_selected_window, Qt);
+
+ /* Now set up the composition region if necessary. */
+
+ if (!MARKERP (f->conversion.compose_region_start))
+ {
+ f->conversion.compose_region_start
+ = build_marker (current_buffer, PT, PT_BYTE);
+ f->conversion.compose_region_end
+ = build_marker (current_buffer, PT, PT_BYTE);
+
+ Fset_marker_insertion_type (f->conversion.compose_region_end,
+ Qt);
+
+ start = position;
+ }
+ else
+ {
+ /* Delete the text between the start of the composing region and
+ its end. */
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+ del_range (start, end);
+ set_point (start);
+
+ if (start != end)
+ record_buffer_change (start, start, Qnil);
+ }
+
+ /* Insert the new text. */
+ Finsert (1, &text);
+
+ if (start != PT)
+ record_buffer_change (start, PT, Qnil);
+
+ /* Now move point to an appropriate location. */
+ if (position < 0)
+ {
+ wanted = start;
+
+ if (INT_SUBTRACT_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+ }
+ else
+ {
+ end = marker_position (f->conversion.compose_region_end);
+ wanted = end;
+
+ /* end should be PT after the edit. */
+ eassert (end == PT);
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+ }
+
+ set_point (wanted);
+
+ /* This should deactivate the mark. */
+ call0 (Qdeactivate_mark);
+
+ /* Move the composition overlay. */
+ sync_overlay (f);
+
+ /* If PT hasn't changed, the conversion region definitely has.
+ Otherwise, redisplay will update the input method instead. */
+
+ if (PT == w->ephemeral_last_point
+ && text_interface
+ && text_interface->compose_region_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_COMPOSE_CHANGE;
+ else
+ text_interface->compose_region_changed (f);
+ }
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Set the composing region to START by END. Make it that it is not
+ already set. */
+
+static void
+really_set_composing_region (struct frame *f, ptrdiff_t start,
+ ptrdiff_t end)
+{
+ specpdl_ref count;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ /* If MAX (0, start) == end, then this should behave the same as
+ really_finish_composing_text. */
+
+ if (max (0, start) == max (0, end))
+ {
+ really_finish_composing_text (f);
+ return;
+ }
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Now set up the composition region if necessary. */
+
+ if (!MARKERP (f->conversion.compose_region_start))
+ {
+ f->conversion.compose_region_start = Fmake_marker ();
+ f->conversion.compose_region_end = Fmake_marker ();
+ Fset_marker_insertion_type (f->conversion.compose_region_end,
+ Qt);
+ }
+
+ Fset_marker (f->conversion.compose_region_start,
+ make_fixnum (start), Qnil);
+ Fset_marker (f->conversion.compose_region_end,
+ make_fixnum (end), Qnil);
+ sync_overlay (f);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Delete LEFT and RIGHT chars around point or the active mark,
+ whichever is larger, avoiding the composing region if
+ necessary. */
+
+static void
+really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
+ ptrdiff_t right)
+{
+ specpdl_ref count;
+ ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
+ struct window *w;
+ Lisp_Object text;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Figure out where to start deleting from. */
+
+ a = get_mark ();
+
+ if (a != -1 && a != PT)
+ lstart = rstart = max (a, PT);
+ else
+ lstart = rstart = PT;
+
+ /* Avoid the composing text. This behavior is identical to how
+ Android's BaseInputConnection actually implements avoiding the
+ composing span. */
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ a = marker_position (f->conversion.compose_region_start);
+ b = marker_position (f->conversion.compose_region_end);
+
+ a1 = min (a, b);
+ b1 = max (a, b);
+
+ lstart = min (lstart, min (PT, a1));
+ rstart = max (rstart, max (PT, b1));
+ }
+
+ if (lstart == rstart)
+ {
+ start = max (BEGV, lstart - left);
+ end = min (ZV, rstart + right);
+
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, text);
+ }
+ else
+ {
+ /* Don't record a deletion if the text which was deleted lies
+ after point. */
+
+ start = rstart;
+ end = min (ZV, rstart + right);
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, Qnil);
+
+ /* Now delete what must be deleted on the left. */
+
+ start = max (BEGV, lstart - left);
+ end = lstart;
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, text);
+ }
+
+ /* if the mark is now equal to start, deactivate it. */
+
+ if (get_mark () == PT)
+ call0 (Qdeactivate_mark);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Update the interface with F's new point and mark. If a batch edit
+ is in progress, schedule the update for when it finishes
+ instead. */
+
+static void
+really_request_point_update (struct frame *f)
+{
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else if (text_interface && text_interface->point_changed)
+ text_interface->point_changed (f,
+ XWINDOW (f->old_selected_window),
+ current_buffer);
+}
+
+/* Set point in F to POSITION. If MARK is not POSITION, activate the
+ mark and set MARK to that as well.
+
+ If it has not changed, signal an update through the text input
+ interface, which is necessary for the IME to acknowledge that the
+ change has completed. */
+
+static void
+really_set_point_and_mark (struct frame *f, ptrdiff_t point,
+ ptrdiff_t mark)
+{
+ specpdl_ref count;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ if (point == PT)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else if (text_interface && text_interface->point_changed)
+ text_interface->point_changed (f,
+ XWINDOW (f->old_selected_window),
+ current_buffer);
+ }
+ else
+ /* Set the point. */
+ Fgoto_char (make_fixnum (point));
+
+ if (mark == point && BVAR (current_buffer, mark_active))
+ call0 (Qdeactivate_mark);
+ else
+ call1 (Qpush_mark, make_fixnum (mark));
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Complete the edit specified by the counter value inside *TOKEN. */
+
+static void
+complete_edit (void *token)
+{
+ if (text_interface && text_interface->notify_conversion)
+ text_interface->notify_conversion (*(unsigned long *) token);
+}
+
+/* Context for complete_edit_check. */
+
+struct complete_edit_check_context
+{
+ /* The window. */
+ struct window *w;
+
+ /* Whether or not editing was successful. */
+ bool check;
+};
+
+/* If CONTEXT->check is false, then update W's ephemeral last point
+ and give it to the input method, the assumption being that an
+ editing operation signalled. */
+
+static void
+complete_edit_check (void *ptr)
+{
+ struct complete_edit_check_context *context;
+ struct frame *f;
+
+ context = ptr;
+
+ if (!context->check)
+ {
+ /* Figure out the new position of point. */
+ context->w->ephemeral_last_point
+ = window_point (context->w);
+
+ /* See if the frame is still alive. */
+
+ f = WINDOW_XFRAME (context->w);
+
+ if (!FRAME_LIVE_P (f))
+ return;
+
+ if (text_interface && text_interface->point_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, context->w, NULL);
+ }
+ }
+}
+
+/* Process and free the text conversion ACTION. F must be the frame
+ on which ACTION will be performed.
+
+ Value is the window which was used, or NULL. */
+
+static struct window *
+handle_pending_conversion_events_1 (struct frame *f,
+ struct text_conversion_action *action)
+{
+ Lisp_Object data;
+ enum text_conversion_operation operation;
+ struct buffer *buffer UNINIT;
+ struct window *w;
+ specpdl_ref count;
+ unsigned long token;
+ struct complete_edit_check_context context;
+
+ /* Next, process this action and free it. */
+
+ data = action->data;
+ operation = action->operation;
+ token = action->counter;
+ xfree (action);
+
+ /* Text conversion events can still arrive immediately after
+ `conversion_disabled_p' becomes true. In that case, process all
+ events, but don't perform any associated actions. */
+
+ if (conversion_disabled_p ())
+ return NULL;
+
+ /* check is a flag used by complete_edit_check to determine whether
+ or not the editing operation completed successfully. */
+ context.check = false;
+
+ /* Make sure completion is signalled. */
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (complete_edit, &token);
+ w = NULL;
+
+ if (WINDOW_LIVE_P (f->old_selected_window))
+ {
+ w = XWINDOW (f->old_selected_window);
+ buffer = XBUFFER (WINDOW_BUFFER (w));
+ context.w = w;
+
+ /* Notify the input method of any editing failures. */
+ record_unwind_protect_ptr (complete_edit_check, &context);
+ }
+
+ switch (operation)
+ {
+ case TEXTCONV_START_BATCH_EDIT:
+ f->conversion.batch_edit_count++;
+ break;
+
+ case TEXTCONV_END_BATCH_EDIT:
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_count--;
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ break;
+
+ if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
+ text_interface->point_changed (f, w, buffer);
+
+ if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
+ text_interface->compose_region_changed (f);
+
+ f->conversion.batch_edit_flags = 0;
+ break;
+
+ case TEXTCONV_COMMIT_TEXT:
+ really_commit_text (f, XFIXNUM (XCAR (data)), XCDR (data));
+ break;
+
+ case TEXTCONV_FINISH_COMPOSING_TEXT:
+ really_finish_composing_text (f);
+ break;
+
+ case TEXTCONV_SET_COMPOSING_TEXT:
+ really_set_composing_text (f, XFIXNUM (XCAR (data)),
+ XCDR (data));
+ break;
+
+ case TEXTCONV_SET_COMPOSING_REGION:
+ really_set_composing_region (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_SET_POINT_AND_MARK:
+ really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_DELETE_SURROUNDING_TEXT:
+ really_delete_surrounding_text (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_REQUEST_POINT_UPDATE:
+ really_request_point_update (f);
+ break;
+ }
+
+ /* Signal success. */
+ context.check = true;
+ unbind_to (count, Qnil);
+
+ return w;
+}
+
+/* Decrement the variable pointed to by *PTR. */
+
+static void
+decrement_inside (void *ptr)
+{
+ int *i;
+
+ i = ptr;
+ (*i)--;
+}
+
+/* Process any outstanding text conversion events.
+ This may run Lisp or signal. */
+
+void
+handle_pending_conversion_events (void)
+{
+ struct frame *f;
+ Lisp_Object tail, frame;
+ struct text_conversion_action *action, *next;
+ bool handled;
+ static int inside;
+ specpdl_ref count;
+ ptrdiff_t last_point;
+ struct window *w;
+
+ handled = false;
+
+ /* Reset Vtext_conversion_edits. Do not do this if called
+ reentrantly. */
+
+ if (!inside)
+ Vtext_conversion_edits = Qnil;
+
+ inside++;
+ last_point = -1;
+ w = NULL;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (decrement_inside, &inside);
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ /* Test if F has any outstanding conversion events. Then
+ process them in bottom to up order. */
+ while (true)
+ {
+ /* Update the input method if handled &&
+ w->ephemeral_last_point != last_point. */
+ if (w && (last_point != w->ephemeral_last_point))
+ {
+ if (handled
+ && last_point != -1
+ && text_interface
+ && text_interface->point_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, NULL, NULL);
+ }
+
+ last_point = w->ephemeral_last_point;
+ }
+
+ /* Reload action. This needs to be reentrant as buffer
+ modification functions can call `read-char'. */
+ action = f->conversion.actions;
+
+ /* If there are no more actions, break. */
+
+ if (!action)
+ break;
+
+ /* Unlink this action. */
+ next = action->next;
+ f->conversion.actions = next;
+
+ /* Handle and free the action. */
+ w = handle_pending_conversion_events_1 (f, action);
+ handled = true;
+ }
+ }
+
+ unbind_to (count, Qnil);
+}
+
+/* Start a ``batch edit'' in F. During a batch edit, point_changed
+ will not be called until the batch edit ends.
+
+ Process the actual operation in the event loop in keyboard.c; then,
+ call `notify_conversion' in the text conversion interface with
+ COUNTER. */
+
+void
+start_batch_edit (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_START_BATCH_EDIT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* End a ``batch edit''. It is ok to call this function even if a
+ batch edit has not yet started, in which case it does nothing.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+end_batch_edit (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_END_BATCH_EDIT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Insert the specified STRING into F's current buffer's composition
+ region, and set point to POSITION relative to STRING.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+commit_text (struct frame *f, Lisp_Object string,
+ ptrdiff_t position, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_COMMIT_TEXT;
+ action->data = Fcons (make_fixnum (position), string);
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Remove the composition region and its overlay from F's current
+ buffer. Leave the text being composed intact.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+finish_composing_text (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_FINISH_COMPOSING_TEXT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Insert the given STRING and make it the currently active
+ composition.
+
+ If there is currently no composing region, then the new value of
+ point is used as the composing region.
+
+ Then, the composing region is replaced with the text in the
+ specified string.
+
+ Finally, move point to new_point, which is relative to either the
+ start or the end of OBJECT depending on whether or not it is less
+ than zero.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+set_composing_text (struct frame *f, Lisp_Object object,
+ ptrdiff_t new_point, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_COMPOSING_TEXT;
+ action->data = Fcons (make_fixnum (new_point),
+ object);
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Make the region between START and END the currently active
+ ``composing region''.
+
+ The ``composing region'' is a region of text in the buffer that is
+ about to undergo editing by the input method. */
+
+void
+set_composing_region (struct frame *f, ptrdiff_t start,
+ ptrdiff_t end, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ if (start > MOST_POSITIVE_FIXNUM)
+ start = MOST_POSITIVE_FIXNUM;
+
+ if (end > MOST_POSITIVE_FIXNUM)
+ end = MOST_POSITIVE_FIXNUM;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_COMPOSING_REGION;
+ action->data = Fcons (make_fixnum (start),
+ make_fixnum (end));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Move point in F's selected buffer to POINT and maybe push MARK.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
+ ptrdiff_t mark, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ if (point > MOST_POSITIVE_FIXNUM)
+ point = MOST_POSITIVE_FIXNUM;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_POINT_AND_MARK;
+ action->data = Fcons (make_fixnum (point),
+ make_fixnum (mark));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Delete LEFT and RIGHT characters around point in F's old selected
+ window. */
+
+void
+delete_surrounding_text (struct frame *f, ptrdiff_t left,
+ ptrdiff_t right, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_DELETE_SURROUNDING_TEXT;
+ action->data = Fcons (make_fixnum (left),
+ make_fixnum (right));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Request an immediate call to INTERFACE->point_changed with the new
+ details of F's region unless a batch edit is in progress. */
+
+void
+request_point_update (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_REQUEST_POINT_UPDATE;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Return N characters of text around point in F's old selected
+ window.
+
+ If N is -1, return the text between point and mark instead, given
+ that the mark is active.
+
+ Set *N to the actual number of characters returned, *START_RETURN
+ to the position of the first character returned, *OFFSET to the
+ offset of point within that text, *LENGTH to the actual number of
+ characters returned, and *BYTES to the actual number of bytes
+ returned.
+
+ Value is NULL upon failure, and a malloced string upon success. */
+
+char *
+get_extracted_text (struct frame *f, ptrdiff_t n,
+ ptrdiff_t *start_return,
+ ptrdiff_t *offset, ptrdiff_t *length,
+ ptrdiff_t *bytes)
+{
+ specpdl_ref count;
+ ptrdiff_t start, end, start_byte, end_byte;
+ char *buffer;
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return NULL;
+
+ /* Save the excursion, as there will be extensive changes to the
+ selected window. */
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_excursion ();
+
+ /* Inhibit quitting. */
+ specbind (Qinhibit_quit, Qt);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+ buffer = NULL;
+
+ /* Figure out the bounds of the text to return. */
+ if (n != -1)
+ {
+ /* Make sure n is at least 4, leaving two characters around
+ PT. */
+ n = max (4, n);
+
+ start = PT - n / 2;
+ end = PT + n - n / 2;
+ }
+ else
+ {
+ if (!NILP (BVAR (current_buffer, mark_active))
+ && XMARKER (BVAR (current_buffer, mark))->buffer)
+ {
+ start = marker_position (BVAR (current_buffer, mark));
+ end = PT;
+
+ /* Sort start and end. start_byte is used to hold a
+ temporary value. */
+
+ if (start > end)
+ {
+ start_byte = end;
+ end = start;
+ start = start_byte;
+ }
+ }
+ else
+ goto finish;
+ }
+
+ start = max (start, BEGV);
+ end = min (end, ZV);
+
+ /* Detect overflow. */
+
+ if (!(start <= PT && PT <= end))
+ goto finish;
+
+ /* Convert the character positions to byte positions. */
+ start_byte = CHAR_TO_BYTE (start);
+ end_byte = CHAR_TO_BYTE (end);
+
+ /* Extract the text from the buffer. */
+ buffer = xmalloc (end_byte - start_byte);
+ copy_buffer (start, start_byte, end, end_byte,
+ buffer);
+
+ /* Return the offsets. */
+ *start_return = start;
+ *offset = PT - start;
+ *length = end - start;
+ *bytes = end_byte - start_byte;
+
+ finish:
+ unbind_to (count, Qnil);
+ return buffer;
+}
+
+/* Return whether or not text conversion is temporarily disabled.
+ `reset' should always call this to determine whether or not to
+ disable the input method. */
+
+bool
+conversion_disabled_p (void)
+{
+ return suppress_conversion_count > 0;
+}
+
/* Window system interface. These are called from the rest of
@@ -298,16 +1620,220 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
void
report_selected_window_change (struct frame *f)
{
+ struct window *w;
+
+ reset_frame_state (f);
+
if (!text_interface)
return;
+ /* When called from window.c, F's selected window has already been
+ redisplayed, but w->last_point has not yet been updated. Update
+ it here to avoid race conditions when the IM asks for the initial
+ selection position immediately after. */
+
+ if (WINDOWP (f->selected_window))
+ {
+ w = XWINDOW (f->selected_window);
+ w->ephemeral_last_point = window_point (w);
+ }
+
text_interface->reset (f);
}
+/* Notice that the point in F's selected window's current buffer has
+ changed.
+
+ F is the frame whose selected window was changed, W is the window
+ in question, and BUFFER is that window's current buffer.
+
+ Tell the text conversion interface about the change; it will likely
+ pass the information on to the system input method. */
+
+void
+report_point_change (struct frame *f, struct window *window,
+ struct buffer *buffer)
+{
+ if (!text_interface || !text_interface->point_changed)
+ return;
+
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, window, buffer);
+}
+
+/* Temporarily disable text conversion. Must be paired with a
+ corresponding call to resume_text_conversion. */
+
+void
+disable_text_conversion (void)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ suppress_conversion_count++;
+
+ if (!text_interface || suppress_conversion_count > 1)
+ return;
+
+ /* Loop through and reset the input method on each window system
+ frame. It should call conversion_disabled_p and then DTRT. */
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ reset_frame_state (f);
+
+ if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+ text_interface->reset (f);
+ }
+}
+
+/* Undo the effect of the last call to `disable_text_conversion'. */
+
+void
+resume_text_conversion (void)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ suppress_conversion_count--;
+ eassert (suppress_conversion_count >= 0);
+
+ if (!text_interface || suppress_conversion_count)
+ return;
+
+ /* Loop through and reset the input method on each window system
+ frame. It should call conversion_disabled_p and then DTRT. */
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ reset_frame_state (f);
+
+ if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+ text_interface->reset (f);
+ }
+}
+
/* Register INTERFACE as the text conversion interface. */
void
-register_texconv_interface (struct textconv_interface *interface)
+register_textconv_interface (struct textconv_interface *interface)
{
text_interface = interface;
}
+
+
+
+/* Lisp interface. */
+
+DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
+ Sset_text_conversion_style, 1, 1, 0,
+ doc: /* Set the text conversion style in the current buffer.
+
+Set `text-conversion-style' to VALUE, then force any input method
+editing frame displaying this buffer to stop itself.
+
+This can lead to a significant amount of time being taken by the input
+method resetting itself, so you should not use this function lightly;
+instead, set `text-conversion-style' before your buffer is displayed,
+and let redisplay manage the input method appropriately. */)
+ (Lisp_Object value)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+ Lisp_Object buffer;
+
+ bset_text_conversion_style (current_buffer, value);
+
+ if (!text_interface)
+ return Qnil;
+
+ /* If there are any seleted windows displaying this buffer, reset
+ text conversion on their associated frames. */
+
+ if (buffer_window_count (current_buffer))
+ {
+ buffer = Fcurrent_buffer ();
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ if (WINDOW_LIVE_P (f->old_selected_window)
+ && FRAME_WINDOW_P (f)
+ && EQ (XWINDOW (f->old_selected_window)->contents,
+ buffer))
+ {
+ block_input ();
+ reset_frame_state (f);
+ text_interface->reset (f);
+ unblock_input ();
+ }
+ }
+ }
+
+ return Qnil;
+}
+
+
+
+void
+syms_of_textconv (void)
+{
+ DEFSYM (Qaction, "action");
+ DEFSYM (Qtext_conversion, "text-conversion");
+ DEFSYM (Qpush_mark, "push-mark");
+ DEFSYM (Qunderline, "underline");
+ DEFSYM (Qoverriding_text_conversion_style,
+ "overriding-text-conversion-style");
+
+ DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
+ doc: /* List of buffers that were last edited as a result of text conversion.
+
+This list can be used while handling a `text-conversion' event to
+determine the changes which have taken place.
+
+Each element of the list describes a single edit in a buffer, of the
+form:
+
+ (BUFFER BEG END EPHEMERAL)
+
+If an insertion or a change occured, then BEG and END are markers
+which denote the bounds of the text that was changed or inserted.
+
+If EPHEMERAL is t, then the input method will shortly make more
+changes to the text, so any actions that would otherwise be taken
+(such as indenting or automatically filling text) should not take
+place; otherwise, it is a string describing the text which was
+inserted.
+
+If a deletion occured before point, then BEG and END are the same
+object, and EPHEMERAL is the text which was deleted.
+
+If a deletion occured after point, then BEG and END are also the same
+object, but EPHEMERAL is nil.
+
+The list contents are ordered later edits first, so you must iterate
+through the list in reverse. */);
+ Vtext_conversion_edits = Qnil;
+
+ DEFVAR_LISP ("overriding-text-conversion-style",
+ Voverriding_text_conversion_style,
+ doc: /* Non-buffer local version of `text-conversion-style'.
+
+If this variable is the symbol `lambda', it means to consult the
+buffer local variable `text-conversion-style' to determine whether or
+not to activate the input method. Otherwise, its value is used in
+preference to any buffer local value of `text-conversion-style'. */);
+ Voverriding_text_conversion_style = Qlambda;
+
+ DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
+ doc: /* Face in which to display temporary edits by an input method.
+nil means to display no indication of a temporary edit. */);
+ Vtext_conversion_face = Qunderline;
+
+ defsubr (&Sset_text_conversion_style);
+}
diff --git a/src/textconv.h b/src/textconv.h
index f6e7eb7925f..6abca97bc52 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -34,6 +34,21 @@ struct textconv_interface
happen if the window is deleted or switches buffers, or an
unexpected buffer change occurs.) */
void (*reset) (struct frame *);
+
+ /* Notice that point or mark has moved in the specified frame's
+ selected window's selected buffer. The second argument is the
+ window whose point changed, and the third argument is the
+ buffer. */
+ void (*point_changed) (struct frame *, struct window *,
+ struct buffer *);
+
+ /* Notice that the preconversion region has changed without point
+ being moved. */
+ void (*compose_region_changed) (struct frame *);
+
+ /* Notice that an asynch conversion identified by COUNTER has
+ completed. */
+ void (*notify_conversion) (unsigned long);
};
@@ -103,7 +118,30 @@ struct textconv_callback_struct
struct textconv_conversion_text text;
};
-extern int textconv_query (struct frame *, struct textconv_callback_struct *);
-extern void register_texconv_interface (struct textconv_interface *);
+#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
+
+extern int textconv_query (struct frame *, struct textconv_callback_struct *,
+ int);
+extern bool detect_conversion_events (void);
+extern void handle_pending_conversion_events (void);
+extern void start_batch_edit (struct frame *, unsigned long);
+extern void end_batch_edit (struct frame *, unsigned long);
+extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t,
+ unsigned long);
+extern void finish_composing_text (struct frame *, unsigned long);
+extern void set_composing_text (struct frame *, Lisp_Object,
+ ptrdiff_t, unsigned long);
+extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
+ unsigned long);
+extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
+ ptrdiff_t, unsigned long);
+extern void delete_surrounding_text (struct frame *, ptrdiff_t,
+ ptrdiff_t, unsigned long);
+extern void request_point_update (struct frame *, unsigned long);
+extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
+ ptrdiff_t *, ptrdiff_t *, ptrdiff_t *);
+extern bool conversion_disabled_p (void);
+
+extern void register_textconv_interface (struct textconv_interface *);
#endif /* _TEXTCONV_H_ */
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index a4e2aad9325..97799cee813 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -32,6 +32,11 @@ AM_V_GEN =
AM_V_GLOBALS =
AM_V_NO_PD =
AM_V_RC =
+AM_V_JAVAC =
+AM_V_DX =
+AM_V_AAPT =
+AM_V_ZIPALIGN =
+AM_V_SILENT =
else
# Whether $(info ...) works. This is to work around a bug in GNU Make
@@ -76,4 +81,10 @@ AM_V_GEN = @$(info $ GEN $@)
AM_V_GLOBALS = @$(info $ GEN globals.h)
AM_V_NO_PD = --no-print-directory
AM_V_RC = @$(info $ RC $@)
+
+# These are used for the Android port.
+AM_V_JAVAC = @$(info $ JAVAC $@)
+AM_V_D8 = @$(info $ D8 $@)
+AM_V_AAPT = @$(info $ AAPT $@)
+AM_V_SILENT = @
endif
diff --git a/src/w32.c b/src/w32.c
index a6bc0f4b2ee..c75beb630e5 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10335,7 +10335,8 @@ check_windows_init_file (void)
names from UTF-8 to ANSI. */
init_file = build_string ("term/w32-win");
fd =
- openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0);
+ openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0,
+ NULL);
if (fd < 0)
{
Lisp_Object load_path_print = Fprin1_to_string (Vload_path,
diff --git a/src/w32proc.c b/src/w32proc.c
index 77a4ac1ff7e..edc4394b17f 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -1956,7 +1956,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
program = build_string (cmdname);
full = Qnil;
openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK),
- 0, 0);
+ 0, 0, NULL);
if (NILP (full))
{
errno = EINVAL;
diff --git a/src/window.c b/src/window.c
index f4e09f49eae..8c42d3cdd0c 100644
--- a/src/window.c
+++ b/src/window.c
@@ -3514,7 +3514,10 @@ window-start value is reasonable when this function is called. */)
void
replace_buffer_in_windows (Lisp_Object buffer)
{
- call1 (Qreplace_buffer_in_windows, buffer);
+ /* When kill-buffer is called early during loadup, this function is
+ undefined. */
+ if (!NILP (Ffboundp (Qreplace_buffer_in_windows)))
+ call1 (Qreplace_buffer_in_windows, buffer);
}
/* If BUFFER is shown in a window, safely replace it with some other
diff --git a/src/window.h b/src/window.h
index 2f793ebe438..6b151efbe60 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,25 @@ struct window
it should be positive. */
ptrdiff_t last_point;
+#ifdef HAVE_TEXT_CONVERSION
+ /* ``ephemeral'' last point position. This is used while
+ processing text conversion events.
+
+ `last_point' is normally used during redisplay to indicate the
+ position of point as seem by the input method. However, it is
+ not updated if consequtive conversions are processed at the
+ same time.
+
+ This `ephemeral_last_point' field is either the last point as
+ set in redisplay or the last point after a text editing
+ operation. */
+ ptrdiff_t ephemeral_last_point;
+#endif
+
+ /* Value of mark in the selected window at the time of the last
+ redisplay. */
+ ptrdiff_t last_mark;
+
/* Line number and position of a line somewhere above the top of the
screen. If this field is zero, it means we don't have a base line. */
ptrdiff_t base_line_number;
diff --git a/src/xdisp.c b/src/xdisp.c
index e31533b3680..09f2f31816e 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -14584,21 +14584,32 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph,
Qmenu_item, f->current_tab_bar_string);
if (! FIXNUMP (prop))
return false;
+
*prop_idx = XFIXNUM (prop);
- *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
- Qclose_tab,
- f->current_tab_bar_string));
+ if (close_p)
+ *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
+ Qclose_tab,
+ f->current_tab_bar_string));
return true;
}
-/* Get information about the tab-bar item at position X/Y on frame F.
- Return in *GLYPH a pointer to the glyph of the tab-bar item in
- the current matrix of the tab-bar window of F, or NULL if not
- on a tab-bar item. Return in *PROP_IDX the index of the tab-bar
- item in F->tab_bar_items. Value is
+/* Get information about the tab-bar item at position X/Y on frame F's
+ tab bar window.
+
+ Set *GLYPH to a pointer to the glyph of the tab-bar item in the
+ current matrix of the tab-bar window of F, or NULL if not on a
+ tab-bar item. Return in *PROP_IDX the index of the tab-bar item in
+ F->tab_bar_items.
+
+ Place the window-relative vpos of Y in *VPOS, and the
+ window-relative hpos of X in *HPOS. If CLOSE_P, set it to whether
+ or not the tab bar item represents a button that should close a
+ tab.
+
+ Value is
-1 if X/Y is not on a tab-bar item
0 if X/Y is on the same item that was highlighted before.
@@ -14606,7 +14617,7 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph,
static int
get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph,
- int *hpos, int *vpos, int *prop_idx, bool *close_p)
+ int *hpos, int *vpos, int *prop_idx, bool *close_p)
{
struct window *w = XWINDOW (f->tab_bar_window);
int area;
@@ -14624,6 +14635,38 @@ get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph,
return *prop_idx == f->last_tab_bar_item ? 0 : 1;
}
+/* EXPORT:
+
+ Like `get_tab_bar_item'. However, don't return anything for GLYPH,
+ HPOS, or VPOS, and treat X and Y as relative to F itself, as
+ opposed to its tab bar window. */
+
+int
+get_tab_bar_item_kbd (struct frame *f, int x, int y, int *prop_idx,
+ bool *close_p)
+{
+ struct window *w;
+ int area, vpos, hpos;
+ struct glyph *glyph;
+
+ w = XWINDOW (f->tab_bar_window);
+
+ /* Convert X and Y to window coordinates. */
+ frame_to_window_pixel_xy (w, &x, &y);
+
+ /* Find the glyph under X/Y. */
+ glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0,
+ 0, &area);
+ if (glyph == NULL)
+ return -1;
+
+ /* Get the start of this tab-bar item's properties in
+ f->tab_bar_items. */
+ if (!tab_bar_item_info (f, glyph, prop_idx, close_p))
+ return -1;
+
+ return *prop_idx == f->last_tab_bar_item ? 0 : 1;
+}
/* EXPORT:
Handle mouse button event on the tab-bar of frame F, at
@@ -15380,6 +15423,15 @@ redisplay_tool_bar (struct frame *f)
/* Always do that now. */
clear_glyph_matrix (w->desired_matrix);
f->fonts_changed = true;
+
+ /* Kludge (this applies to the X Windows version as well as
+ Android): when the tool bar size changes,
+ adjust_window_size (presumably called by
+ change_tool_bar_height_hook) does not call through to
+ resize_frame_windows. Pending further investigation,
+ just call it here as well. */
+ resize_frame_windows (f, FRAME_INNER_HEIGHT (f), false);
+
return true;
}
}
@@ -16567,7 +16619,7 @@ redisplay_internal (void)
display area, displaying a different frame means redisplay
the whole thing. */
SET_FRAME_GARBAGED (sf);
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
set_tty_color_mode (FRAME_TTY (sf), sf);
#endif
FRAME_TTY (sf)->previous_frame = sf;
@@ -17408,6 +17460,9 @@ static void
mark_window_display_accurate_1 (struct window *w, bool accurate_p)
{
struct buffer *b = XBUFFER (w->contents);
+#ifdef HAVE_TEXT_CONVERSION
+ ptrdiff_t prev_point, prev_mark;
+#endif
w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0;
@@ -17437,11 +17492,46 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
w->last_cursor_vpos = w->cursor.vpos;
w->last_cursor_off_p = w->cursor_off_p;
+#ifdef HAVE_TEXT_CONVERSION
+ prev_point = w->last_point;
+ prev_mark = w->last_mark;
+#endif
+
if (w == XWINDOW (selected_window))
w->last_point = BUF_PT (b);
else
w->last_point = marker_position (w->pointm);
+ if (XMARKER (BVAR (b, mark))->buffer == b)
+ w->last_mark = marker_position (BVAR (b, mark));
+ else
+ w->last_mark = -1;
+
+#ifdef HAVE_TEXT_CONVERSION
+ /* See the description of this field in struct window. */
+ w->ephemeral_last_point = w->last_point;
+
+ /* Point motion is only propagated to the input method for use
+ in text conversion during a redisplay. While this can lead
+ to inconsistencies when point has moved but the change has
+ not yet been displayed, it leads to better results most of
+ the time, as point often changes within calls to
+ `save-excursion', and the only way to detect such calls is to
+ observe that the next redisplay never ends with those changes
+ applied.
+
+ Changes to buffer text are immediately propagated to the
+ input method, and the position of point is also updated
+ during such a change, so the consequences are not that
+ severe. */
+
+ if ((prev_point != w->last_point
+ || prev_mark != w->last_mark)
+ && FRAME_WINDOW_P (WINDOW_XFRAME (w))
+ && w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
+ report_point_change (WINDOW_XFRAME (w), w, b);
+#endif
+
w->window_end_valid = true;
w->update_mode_line = false;
w->preserve_vscroll_p = false;
@@ -26491,7 +26581,7 @@ display_menu_bar (struct window *w)
init_iterator (&it, w, -1, -1, f->desired_matrix->rows, MENU_FACE_ID);
it.first_visible_x = 0;
it.last_visible_x = FRAME_PIXEL_WIDTH (f);
-#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */
+#elif defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
struct window *menu_window = NULL;
struct face *face = FACE_FROM_ID (f, MENU_FACE_ID);
@@ -26561,7 +26651,11 @@ display_menu_bar (struct window *w)
it.glyph_row->truncated_on_left_p = false;
it.glyph_row->truncated_on_right_p = false;
-#if defined (HAVE_X_WINDOWS) && !defined (USE_X_TOOLKIT) && !defined (USE_GTK)
+ /* This will break the moment someone tries to add another window
+ system that uses the no toolkit menu bar. Oh well. At least
+ there will be an error, meaning he will correct the ifdef inside
+ which `face' is defined. */
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Make a 3D menu bar have a shadow at its right end. */
extend_face_to_end_of_line (&it);
if (face->box != FACE_NO_BOX)
@@ -26602,6 +26696,11 @@ display_menu_bar (struct window *w)
#endif
}
+/* This code is never used on Android where there are only GUI and
+ initial frames. */
+
+#ifndef HAVE_ANDROID
+
/* Deep copy of a glyph row, including the glyphs. */
static void
deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from)
@@ -26722,6 +26821,9 @@ display_tty_menu_item (const char *item_text, int width, int face_id,
row->full_width_p = saved_width;
row->reversed_p = saved_reversed;
}
+
+#endif
+
/***********************************************************************
Mode Line
@@ -33609,7 +33711,9 @@ draw_row_with_mouse_face (struct window *w, int start_x, struct glyph_row *row,
}
#endif
+#ifndef HAVE_ANDROID
tty_draw_row_with_mouse_face (w, row, start_hpos, end_hpos, draw);
+#endif
}
/* Display the active region described by mouse_face_* according to DRAW. */
@@ -35100,7 +35204,8 @@ note_mouse_highlight (struct frame *f, int x, int y)
struct buffer *b;
/* When a menu is active, don't highlight because this looks odd. */
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS) \
+ || defined (HAVE_ANDROID)
if (popup_activated ())
return;
#endif
@@ -36281,14 +36386,10 @@ expose_frame (struct frame *f, int x, int y, int w, int h)
|= expose_window (XWINDOW (f->tool_bar_window), &r);
#endif
-#ifdef HAVE_X_WINDOWS
-#ifndef MSDOS
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
if (WINDOWP (f->menu_bar_window))
mouse_face_overwritten_p
|= expose_window (XWINDOW (f->menu_bar_window), &r);
-#endif /* not USE_X_TOOLKIT and not USE_GTK */
-#endif
#endif
/* Some window managers support a focus-follows-mouse style with
@@ -36372,6 +36473,55 @@ gui_intersect_rectangles (const Emacs_Rectangle *r1, const Emacs_Rectangle *r2,
return intersection_p;
}
+/* EXPORT:
+ Determine the union of the rectangles A and B. Return the smallest
+ rectangle encompassing both the bounds of A and B in *RESULT. It
+ is safe for all three arguments to point to each other. */
+
+void
+gui_union_rectangles (const Emacs_Rectangle *a, const Emacs_Rectangle *b,
+ Emacs_Rectangle *result)
+{
+ struct gui_box a_box, b_box, result_box;
+
+ /* Handle special cases where one of the rectangles is empty. */
+
+ if (!a->width || !a->height)
+ {
+ *result = *b;
+ return;
+ }
+ else if (!b->width || !b->height)
+ {
+ *result = *a;
+ return;
+ }
+
+ /* Convert A and B to boxes. */
+ a_box.x1 = a->x;
+ a_box.y1 = a->y;
+ a_box.x2 = a->x + a->width;
+ a_box.y2 = a->y + a->height;
+
+ b_box.x1 = b->x;
+ b_box.y1 = b->y;
+ b_box.x2 = b->x + b->width;
+ b_box.y2 = b->y + b->height;
+
+ /* Compute the union of the boxes. */
+ result_box.x1 = min (a_box.x1, b_box.x1);
+ result_box.y1 = min (a_box.y1, b_box.y1);
+ result_box.x2 = max (a_box.x2, b_box.x2);
+ result_box.y2 = max (a_box.y2, b_box.y2);
+
+ /* Convert result_box to an XRectangle and put the result in
+ RESULT. */
+ result->x = result_box.x1;
+ result->y = result_box.y1;
+ result->width = result_box.x2 - result_box.x1;
+ result->height = result_box.y2 - result_box.y1;
+}
+
#endif /* HAVE_WINDOW_SYSTEM */
diff --git a/src/xfaces.c b/src/xfaces.c
index 37b703984be..af3428ad995 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -254,6 +254,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifdef HAVE_HAIKU
#define GCGraphicsExposures 0
#endif /* HAVE_HAIKU */
+
+#ifdef HAVE_ANDROID
+#define GCGraphicsExposures 0
+#endif /* HAVE_ANDROID */
#endif /* HAVE_WINDOW_SYSTEM */
#include "buffer.h"
@@ -607,6 +611,39 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
}
#endif /* HAVE_NS */
+#ifdef HAVE_ANDROID
+
+/* Android real GCs. */
+
+static struct android_gc *
+x_create_gc (struct frame *f, unsigned long value_mask,
+ Emacs_GC *xgcv)
+{
+ struct android_gc_values gcv;
+ unsigned long mask;
+
+ gcv.foreground = xgcv->foreground;
+ gcv.background = xgcv->background;
+
+ mask = 0;
+
+ if (value_mask & GCForeground)
+ mask |= ANDROID_GC_FOREGROUND;
+
+ if (value_mask & GCBackground)
+ mask |= ANDROID_GC_BACKGROUND;
+
+ return android_create_gc (mask, &gcv);
+}
+
+static void
+x_free_gc (struct frame *f, struct android_gc *gc)
+{
+ android_free_gc (gc);
+}
+
+#endif
+
/***********************************************************************
Frames and faces
***********************************************************************/
@@ -6952,20 +6989,22 @@ where R,G,B are numbers between 0 and 255 and name is an arbitrary string. */)
int num;
while (fgets (buf, sizeof (buf), fp) != NULL)
- if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
- {
+ {
+ if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
+ {
#ifdef HAVE_NTGUI
- int color = RGB (red, green, blue);
+ int color = RGB (red, green, blue);
#else
- int color = (red << 16) | (green << 8) | blue;
+ int color = (red << 16) | (green << 8) | blue;
#endif
- char *name = buf + num;
- ptrdiff_t len = strlen (name);
- len -= 0 < len && name[len - 1] == '\n';
- cmap = Fcons (Fcons (make_string (name, len), make_fixnum (color)),
- cmap);
- }
- fclose (fp);
+ char *name = buf + num;
+ ptrdiff_t len = strlen (name);
+ len -= 0 < len && name[len - 1] == '\n';
+ cmap = Fcons (Fcons (make_string (name, len), make_fixnum (color)),
+ cmap);
+ }
+ }
+ emacs_fclose (fp);
}
unblock_input ();
return cmap;
diff --git a/src/xfns.c b/src/xfns.c
index 9e004f6a678..b7000462e84 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -3861,7 +3861,7 @@ xic_string_conversion_callback (XIC ic, XPointer client_data,
request.operation = TEXTCONV_RETRIEVAL;
/* Now perform the string conversion. */
- rc = textconv_query (f, &request);
+ rc = textconv_query (f, &request, 0);
if (rc)
{
@@ -5673,6 +5673,8 @@ that operating systems cannot be developed and distributed noncommercially.)
The optional argument TERMINAL specifies which display to ask about.
For GNU and Unix systems, this queries the X server software.
+For Android systems, value is the manufacturer who developed the Android
+system that is being used.
For MS Windows and Nextstep the result is hard-coded.
TERMINAL should be a terminal object, a frame or a display name (a string).
@@ -5696,7 +5698,8 @@ Protocol used on TERMINAL and the 3rd number is the distributor-specific
release number. For MS Windows, the 3 numbers report the OS major and
minor version and build number. For Nextstep, the first 2 numbers are
hard-coded and the 3rd represents the OS version. For Haiku, all 3
-numbers are hard-coded.
+numbers are hard-coded. For Android, the first number represents the
+Android API level, and the next two numbers are all zero.
See also the function `x-server-vendor'.
diff --git a/src/xterm.c b/src/xterm.c
index 15bd9f98d17..111e4ede2c2 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -20105,6 +20105,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif
+ /* See if keysym should make Emacs quit. */
+
+ if (keysym == dpyinfo->quit_keysym
+ && (xkey.time - dpyinfo->quit_keysym_time
+ <= 350))
+ {
+ Vquit_flag = Qt;
+ goto done_keysym;
+ }
+
+ if (keysym == dpyinfo->quit_keysym)
+ {
+ /* Otherwise, set the last time that keysym was
+ pressed. */
+ dpyinfo->quit_keysym_time = xkey.time;
+ goto done_keysym;
+ }
+
/* If not using XIM/XIC, and a compose sequence is in progress,
we break here. Otherwise, chars_matched is always 0. */
if (compose_status.chars_matched > 0 && nbytes == 0)
@@ -23868,6 +23886,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif
+ /* See if keysym should make Emacs quit. */
+
+ if (keysym == dpyinfo->quit_keysym
+ && (xev->time - dpyinfo->quit_keysym_time
+ <= 350))
+ {
+ Vquit_flag = Qt;
+ goto xi_done_keysym;
+ }
+
+ if (keysym == dpyinfo->quit_keysym)
+ {
+ /* Otherwise, set the last time that keysym was
+ pressed. */
+ dpyinfo->quit_keysym_time = xev->time;
+ goto xi_done_keysym;
+ }
+
/* First deal with keysyms which have defined
translations to characters. */
if (keysym >= 32 && keysym < 128)
@@ -29872,6 +29908,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
struct terminal *terminal;
struct x_display_info *dpyinfo;
XrmDatabase xrdb;
+ Lisp_Object tem, quit_keysym;
#ifdef USE_XCB
xcb_connection_t *xcb_conn;
#endif
@@ -29882,7 +29919,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
GdkScreen *gscr;
#endif
#ifdef HAVE_XFIXES
- Lisp_Object tem, lisp_name;
+ Lisp_Object lisp_name;
int num_fast_selections;
Atom selection_name;
#ifdef USE_XCB
@@ -30159,6 +30196,28 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
terminal->kboard->reference_count++;
}
+ /* Now look through Vx_quit_keysym for the quit keysym associated
+ with this display. */
+ tem = Vx_quit_keysym;
+ FOR_EACH_TAIL_SAFE (tem)
+ {
+ quit_keysym = XCAR (tem);
+
+ /* Check if its car is a string and its cdr a valid keysym.
+ Skip if it is not. */
+
+ if (!CONSP (quit_keysym) || !FIXNUMP (XCDR (quit_keysym))
+ || !STRINGP (XCAR (quit_keysym)))
+ continue;
+
+ /* Check if this is the keysym to be used. */
+
+ if (strcmp (SSDATA (XCAR (quit_keysym)), ServerVendor (dpy)))
+ continue;
+
+ dpyinfo->quit_keysym = XFIXNUM (XCDR (quit_keysym));
+ }
+
/* Put this display on the chain. */
dpyinfo->next = x_display_list;
x_display_list = dpyinfo;
@@ -31569,7 +31628,7 @@ init_xterm (void)
#endif
#ifdef HAVE_X_I18N
- register_texconv_interface (&text_conversion_interface);
+ register_textconv_interface (&text_conversion_interface);
#endif
}
@@ -31903,7 +31962,9 @@ adjusted if the default value does not work for whatever reason. */);
A value of nil means Emacs doesn't use toolkit scroll bars.
With the X Window system, the value is a symbol describing the
X toolkit. Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows, Haiku windowing or Nextstep, the value is t. */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.
+With Android, the value is nil, but that is because Emacs on
+Android does not support scroll bars at all. */);
#ifdef USE_TOOLKIT_SCROLL_BARS
#ifdef USE_MOTIF
Vx_toolkit_scroll_bars = intern_c_string ("motif");
@@ -32250,4 +32311,23 @@ frame placement via frame parameters, `set-frame-position', and
`set-frame-size', along with the actual state of a frame after
`x_make_frame_invisible'. */);
Vx_lax_frame_positioning = Qnil;
+
+ DEFVAR_LISP ("x-quit-keysym", Vx_quit_keysym,
+ doc: /* Keysyms which will cause Emacs to quit if rapidly pressed twice.
+
+This is used to support quitting on devices that do not have any kind
+of physical keyboard, or where the physical keyboard is incapable of
+entering `C-g'. It defaults to `XF86XK_AudioLowerVolume' on XFree86
+and X.Org servers, and is unset.
+
+The value is an alist associating between strings, describing X server
+vendor names, and a single number describing the keysym to use. The
+keysym to use for each display connection is determined upon
+connection setup, and does not reflect further changes to this
+variable. */);
+ Vx_quit_keysym
+ = list2 (Fcons (build_string ("The X.Org Foundation"),
+ make_int (269025041)),
+ Fcons (build_string ("The XFree86 Project, Inc."),
+ make_int (269025041)));
}
diff --git a/src/xterm.h b/src/xterm.h
index 28ae00ca190..406a7c5c060 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -920,6 +920,13 @@ struct x_display_info
server_time_monotonic_p will be true). */
int_fast64_t server_time_offset;
#endif
+
+ /* Keysym that will cause Emacs to quit if pressed twice within 150
+ ms. */
+ KeySym quit_keysym;
+
+ /* The last time that keysym was pressed. */
+ Time quit_keysym_time;
};
#ifdef HAVE_X_I18N