diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 72 | ||||
-rw-r--r-- | src/alloc.c | 84 | ||||
-rw-r--r-- | src/android-asset.h | 422 | ||||
-rw-r--r-- | src/android-emacs.c | 169 | ||||
-rw-r--r-- | src/android.c | 6726 | ||||
-rw-r--r-- | src/android.h | 231 | ||||
-rw-r--r-- | src/androidfns.c | 3205 | ||||
-rw-r--r-- | src/androidfont.c | 1108 | ||||
-rw-r--r-- | src/androidgui.h | 724 | ||||
-rw-r--r-- | src/androidmenu.c | 829 | ||||
-rw-r--r-- | src/androidselect.c | 499 | ||||
-rw-r--r-- | src/androidterm.c | 6073 | ||||
-rw-r--r-- | src/androidterm.h | 476 | ||||
-rw-r--r-- | src/buffer.c | 24 | ||||
-rw-r--r-- | src/buffer.h | 11 | ||||
-rw-r--r-- | src/callproc.c | 108 | ||||
-rw-r--r-- | src/charset.c | 5 | ||||
-rw-r--r-- | src/coding.c | 27 | ||||
-rw-r--r-- | src/coding.h | 4 | ||||
-rw-r--r-- | src/conf_post.h | 10 | ||||
-rw-r--r-- | src/dired.c | 65 | ||||
-rw-r--r-- | src/dispextern.h | 63 | ||||
-rw-r--r-- | src/dispnew.c | 51 | ||||
-rw-r--r-- | src/editfns.c | 8 | ||||
-rw-r--r-- | src/emacs-module.c | 162 | ||||
-rw-r--r-- | src/emacs.c | 138 | ||||
-rw-r--r-- | src/epaths.in | 22 | ||||
-rw-r--r-- | src/fileio.c | 343 | ||||
-rw-r--r-- | src/filelock.c | 33 | ||||
-rw-r--r-- | src/fns.c | 4 | ||||
-rw-r--r-- | src/font.c | 26 | ||||
-rw-r--r-- | src/font.h | 14 | ||||
-rw-r--r-- | src/fontset.c | 27 | ||||
-rw-r--r-- | src/frame.c | 58 | ||||
-rw-r--r-- | src/frame.h | 103 | ||||
-rw-r--r-- | src/fringe.c | 23 | ||||
-rw-r--r-- | src/image.c | 731 | ||||
-rw-r--r-- | src/inotify.c | 19 | ||||
-rw-r--r-- | src/keyboard.c | 319 | ||||
-rw-r--r-- | src/keyboard.h | 13 | ||||
-rw-r--r-- | src/lisp.h | 24 | ||||
-rw-r--r-- | src/lread.c | 326 | ||||
-rw-r--r-- | src/marker.c | 26 | ||||
-rw-r--r-- | src/menu.c | 20 | ||||
-rw-r--r-- | src/pdumper.c | 24 | ||||
-rw-r--r-- | src/print.c | 17 | ||||
-rw-r--r-- | src/process.c | 25 | ||||
-rw-r--r-- | src/scroll.c | 7 | ||||
-rw-r--r-- | src/sfnt.c | 19684 | ||||
-rw-r--r-- | src/sfnt.h | 1994 | ||||
-rw-r--r-- | src/sfntfont-android.c | 793 | ||||
-rw-r--r-- | src/sfntfont.c | 3874 | ||||
-rw-r--r-- | src/sfntfont.h | 79 | ||||
-rw-r--r-- | src/sound.c | 2 | ||||
-rw-r--r-- | src/sysdep.c | 168 | ||||
-rw-r--r-- | src/term.c | 147 | ||||
-rw-r--r-- | src/termhooks.h | 22 | ||||
-rw-r--r-- | src/terminal.c | 2 | ||||
-rw-r--r-- | src/textconv.c | 1554 | ||||
-rw-r--r-- | src/textconv.h | 42 | ||||
-rw-r--r-- | src/verbose.mk.in | 11 | ||||
-rw-r--r-- | src/w32.c | 3 | ||||
-rw-r--r-- | src/w32proc.c | 2 | ||||
-rw-r--r-- | src/window.c | 5 | ||||
-rw-r--r-- | src/window.h | 19 | ||||
-rw-r--r-- | src/xdisp.c | 186 | ||||
-rw-r--r-- | src/xfaces.c | 61 | ||||
-rw-r--r-- | src/xfns.c | 7 | ||||
-rw-r--r-- | src/xterm.c | 86 | ||||
-rw-r--r-- | src/xterm.h | 7 |
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, + <_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, + <eq_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, + >_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, + >eq_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, + ¬_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 |