Bring up the Android operating system and its window system

* .dir-locals.el (c-mode): Add ANDROID_EXPORT noise macro.
* .gitignore: Add new files to ignore.
* Makefile.in: Adjust for Android.
* admin/merge-gnulib: Add new warning.
* configure.ac: Detect Android.  Run cross-configuration for
Android when appropriate.

* etc/DEBUG: Document how to debug Emacs on Android.

* java/AndroidManifest.xml:
* java/Makefile.in:
* java/README:
* java/debug.sh:
* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity):
* java/org/gnu/emacs/EmacsApplication.java (EmacsApplication):
* java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea):
* java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine):
* java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint):
* java/org/gnu/emacs/EmacsDrawRectangle.java
(EmacsDrawRectangle):
* java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
* java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon):
* java/org/gnu/emacs/EmacsFillRectangle.java
(EmacsFillRectangle):
* java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver):
* java/org/gnu/emacs/EmacsGC.java (EmacsGC):
* java/org/gnu/emacs/EmacsHandleObject.java (EmacsHandleObject):
* java/org/gnu/emacs/EmacsNative.java (EmacsNative):
* java/org/gnu/emacs/EmacsPaintQueue.java (EmacsPaintQueue):
* java/org/gnu/emacs/EmacsPaintReq.java (EmacsPaintReq):
* java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
* java/org/gnu/emacs/EmacsSdk7FontDriver.java
(EmacsSdk7FontDriver):
* java/org/gnu/emacs/EmacsService.java (class Holder<T>)
(EmacsService):
* java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
* java/org/gnu/emacs/EmacsThread.java (EmacsThread):
* java/org/gnu/emacs/EmacsView.java (EmacsView):
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New files
and classes.

* lib-src/Makefile.in (srcdir):
* lib/Makefile.in (VPATH):
(HAVE_NATIVE_COMP):
(libgnu_a_SOURCES):
(DEPFLAGS): Configure correctly for cross-compiling.

* lib/faccessat.c:
* lib/fpending.c (__fpending):
* lib/open.c:
* lib/unistd.c (_GL_UNISTD_INLINE): Temporary adjustments to
gnulib.

* lisp/frame.el (display-graphic-p):
(display-screens):
(display-pixel-height):
(display-pixel-width):
(display-mm-height):
(display-mm-width):
(display-backing-store):
(display-save-under):
(display-planes):
(display-color-cells):
(display-visual-class): Adjust for new window system `android'.

* lisp/image/wallpaper.el (x-open-connection): Add declaration.
* lisp/loadup.el (featurep): Load up files for Android.
* lisp/net/eww.el (eww-form-submit, eww-form-file)
(eww-form-checkbox, eww-form-select): Adjust faces for android.
* lisp/term/android-win.el: New file.
* src/Makefile.in: Add new targets emacs.so and android-emacs,
then adjust for cross compilation.
* src/alloc.c (cleanup_vector): Clean up Android font entities
as well.
(garbage_collect): Mark androidterm.
* src/android-emacs.c (main):
* src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags)
(struct android_emacs_service, struct android_emacs_pixmap)
(struct android_graphics_point, struct android_event_container)
(struct android_event_queue, android_run_select_thread)
(android_handle_sigusr1, android_init_events, android_pending)
(android_next_event, android_write_event, android_select)
(android_run_debug_thread, android_user_full_name)
(android_get_asset_name, android_fstat, android_fstatat)
(android_file_access_p, android_hack_asset_fd, android_open)
(android_close, JNICALL, android_init_emacs_service)
(android_init_emacs_pixmap, android_init_graphics_point)
(MAX_HANDLE, struct android_handle_entry, android_alloc_id)
(android_destroy_handle, android_resolve_handle)
(android_resolve_handle2, android_change_window_attributes)
(android_create_window, android_set_window_background)
(android_destroy_window, android_init_android_rect_class)
(android_init_emacs_gc_class, android_create_gc, android_free_gc)
(android_change_gc, android_set_clip_rectangles)
(android_reparent_window, android_lookup_method)
(android_clear_window, android_map_window, android_unmap_window)
(android_resize_window, android_move_window, android_swap_buffers)
(android_get_gc_values, android_set_foreground)
(android_fill_rectangle, android_create_pixmap_from_bitmap_data)
(android_set_clip_mask, android_set_fill_style, android_copy_area)
(android_free_pixmap, android_set_background, android_fill_polygon)
(android_draw_rectangle, android_draw_point, android_draw_line)
(android_create_pixmap, android_set_ts_origin, android_clear_area):
* src/android.h (ANDROID_EXPORT):
* src/androidfns.c (android_display_info_for_name)
(check_android_display_info, check_x_display_info, gamma_correct)
(android_defined_color, android_decode_color)
(android_implicitly_set_name, android_explicitly_set_name)
(android_set_tool_bar_lines, android_change_tool_bar_height)
(android_set_tab_bar_lines, android_change_tab_bar_height)
(android_set_scroll_bar_default_height)
(android_set_scroll_bar_default_width, android_icon_verify)
(android_icon, android_make_gc, android_free_gcs)
(unwind_create_frame, do_unwind_create_frame)
(android_default_font_parameter, android_create_frame_window)
(Fx_create_frame, Fxw_color_defined_p, Fxw_color_values)
(Fxw_display_color_p, Fx_display_grayscale_p)
(Fx_display_pixel_width, Fx_display_pixel_height)
(Fx_display_planes, Fx_display_color_cells, Fx_display_screens)
(Fx_display_mm_width, Fx_display_mm_height)
(Fx_display_backing_store, Fx_display_visual_class)
(Fx_display_monitor_attributes_list, Fx_frame_geometry)
(Fx_frame_list_z_order, Fx_frame_restack)
(Fx_mouse_absolute_pixel_position)
(Fx_set_mouse_absolute_pixel_position, Fandroid_get_connection)
(Fx_display_list, Fx_show_tip, Fx_hide_tip)
(android_set_background_color, android_set_border_color)
(android_set_cursor_color, android_set_cursor_type)
(android_set_foreground_color)
(android_set_child_frame_border_width)
(android_set_internal_border_width, android_set_menu_bar_lines)
(android_set_mouse_color, android_set_title, android_set_alpha)
(android_frame_parm_handlers, syms_of_androidfns):
* src/androidfont.c (struct android_emacs_font_driver)
(struct android_emacs_font_spec, struct android_emacs_font_metrics)
(struct android_emacs_font_object, struct android_integer)
(struct androidfont_info, struct androidfont_entity)
(android_init_font_driver, android_init_font_spec)
(android_init_font_metrics, android_init_integer)
(android_init_font_object, androidfont_get_cache)
(androidfont_from_lisp, androidfont_from_java, androidfont_list)
(androidfont_match, androidfont_draw, androidfont_open_font)
(androidfont_close_font, androidfont_has_char)
(androidfont_encode_char, androidfont_text_extents)
(androidfont_list_family, androidfont_driver)
(syms_of_androidfont_for_pdumper, syms_of_androidfont)
(init_androidfont, android_finalize_font_entity):
* src/androidgui.h (_ANDROID_GUI_H_, struct android_rectangle)
(struct android_point, enum android_gc_function)
(enum android_gc_value_mask, enum android_fill_style)
(enum android_window_value_mask)
(struct android_set_window_attributes, struct android_gc_values)
(struct android_gc, enum android_swap_action, enum android_shape)
(enum android_coord_mode, struct android_swap_info)
(NativeRectangle, struct android_any_event)
(struct android_key_event, struct android_configure_event)
(union android_event):
* src/androidterm.c (android_window_to_frame, android_clear_frame)
(android_ring_bell, android_toggle_invisible_pointer)
(android_update_begin, android_update_end, show_back_buffer)
(android_flush_dirty_back_buffer_on, handle_one_android_event)
(android_read_socket, android_frame_up_to_date)
(android_buffer_flipping_unblocked_hook)
(android_query_frame_background_color, android_parse_color)
(android_alloc_nearest_color, android_query_colors)
(android_mouse_position, android_get_focus_frame)
(android_focus_frame, android_frame_rehighlight)
(android_frame_raise_lower, android_make_frame_visible)
(android_make_frame_invisible)
(android_make_frame_visible_invisible, android_fullscreen_hook)
(android_iconify_frame, android_set_window_size_1)
(android_set_window_size, android_set_offset, android_set_alpha)
(android_new_font, android_bitmap_icon, android_free_pixmap_hook)
(android_free_frame_resources, android_delete_frame)
(android_delete_terminal, android_scroll_run)
(android_after_update_window_line, android_flip_and_flush)
(android_clear_rectangle, android_reset_clip_rectangles)
(android_clip_to_row, android_draw_fringe_bitmap)
(android_set_cursor_gc, android_set_mouse_face_gc)
(android_set_mode_line_face_gc, android_set_glyph_string_gc)
(android_set_glyph_string_clipping)
(android_set_glyph_string_clipping_exactly)
(android_compute_glyph_string_overhangs)
(android_clear_glyph_string_rect)
(android_draw_glyph_string_background, android_fill_triangle)
(android_make_point, android_inside_rect_p, android_clear_point)
(android_draw_relief_rect, android_draw_box_rect)
(HIGHLIGHT_COLOR_DARK_BOOST_LIMIT, android_setup_relief_color)
(android_setup_relief_colors, android_draw_glyph_string_box)
(android_draw_glyph_string_bg_rect, android_draw_image_relief)
(android_draw_image_foreground, android_draw_image_foreground_1)
(android_draw_image_glyph_string)
(android_draw_stretch_glyph_string, android_draw_underwave)
(android_draw_glyph_string_foreground)
(android_draw_composite_glyph_string_foreground)
(android_draw_glyphless_glyph_string_foreground)
(android_draw_glyph_string, android_define_frame_cursor)
(android_clear_frame_area, android_clear_under_internal_border)
(android_draw_hollow_cursor, android_draw_bar_cursor)
(android_draw_window_cursor, android_draw_vertical_window_border)
(android_draw_window_divider, android_redisplay_interface)
(frame_set_mouse_pixel_position, get_keysym_name)
(android_create_terminal, android_term_init, syms_of_androidterm)
(mark_androidterm):
* src/androidterm.h (_ANDROID_TERM_H_, struct android_display_info)
(struct android_output, FRAME_ANDROID_OUTPUT, XSCROLL_BAR): New
files.
* src/dired.c (file_attributes): Do not use openat on Android.
* src/dispextern.h (No_Cursor): Define appropriately on Android.
(struct glyph_string, struct face): Make gc field of type struct
android_gc on Android.
* src/dispnew.c (clear_current_matrices, clear_desired_matrices)
(adjust_frame_glyphs_for_window_redisplay, free_glyphs)
(update_frame, scrolling, char_ins_del_cost, update_frame_line)
(init_display_interactive): Disable text terminal support
completely on Android.  Fix non-toolkit menus for non-X systems.
* src/editfns.c (Fuser_full_name): Call android_user_full_name.
* src/emacs.c (android_emacs_init): Make main this on Android.
Prohibit argv sorting from exceeding end of argv.
* src/epaths.in: Add path definitions for Android.

* src/fileio.c (file_access_p): Call android_file_access_p.
(file_name_directory): Avoid using openat on Android.
(Fcopy_file): Adjust to call sys_fstat instead.
(file_directory_p):
(Finsert_file_contents):
(write_region): Likewise.
* src/filelock.c:
* src/fns.c (Flocale_info): Pacify warning on Android.
* src/font.c (font_make_entity_android): New function.
* src/font.h:
* src/frame.c (Fframep):
(Fwindow_system): Handle new window system `android'.  Update doc strings.
(Fmake_terminal_frame): Disable on Android.
(gui_display_get_resource): Disable get_string_resource_hook on Android.
(syms_of_frame): New defsym `android'.

* src/frame.h (GCALIGNED_STRUCT): Add new output data for
Android.
(ENUM_BF): Expand enumerator size.
(FRAME_ANDROID_P, FRAME_WINDOW_P, MOUSE_HL_INFO): Add
definitions for Android.

* src/image.c (GET_PIXEL):
(image_create_bitmap_from_file):
(image_create_x_image_and_pixmap_1):
(image_get_x_image):
(slurp_file):
(lookup_rgb_color):
(image_to_emacs_colors):
(image_from_emacs_colors):
(image_pixmap_draw_cross):
(image_disable_image):
(MaskForeground):
(gif_load): Add stubs for Android.

* src/lisp.h:
* src/lread.c (safe_to_load_version, maybe_swap_for_eln1, openp):
* src/pdumper.c (pdumper_load): Call sys_fstat instead of fstat.
* src/process.c (wait_reading_process_output): Use
android_select instead of pselect.
* src/scroll.c: Disable on Android.
* src/sysdep.c (widen_foreground_group, reset_sys_modes)
(init_signals, emacs_fstatat, sys_fstat): New function.
(emacs_open, emacs_open_noquit, emacs_close): Implement
differently on Android.
(close_output_streams): Disable what is not required on Android.

* src/term.c (OUTPUT1_IF, encode_terminal_code, string_cost)
(string_cost_one_line, per_line_cost, calculate_costs)
(struct fkey_table, tty_append_glyph, produce_glyphs)
(tty_capable_p, Fsuspend_tty, Fresume_tty, device, init_tty)
(maybe_fatal, syms_of_term): Disable text terminal support on
Android.

* src/termhooks.h (enum output_method): Add android output
method.
(GCALIGNED_STRUCT, TERMINAL_FONT_CACHE): Define for Android.

* src/terminal.c (Fterminal_live_p): Implement for Android.

* src/verbose.mk.in (AM_V_GLOBALS): Add JAVAC and DX.
* src/xdisp.c (redisplay_internal): Disable text terminals on Android.
(display_menu_bar):
(display_tty_menu_item):
(draw_row_with_mouse_face):
(expose_frame): Make the non toolkit menu bar work on Android.

* src/xfaces.c (GCGraphicsExposures):
(x_create_gc):
(x_free_gc):
(Fx_load_color_file): Define for Android.

* xcompile/Makefile.in (top_srcdir):
(top_builddir):
* xcompile/README:
* xcompile/langinfo.h (nl_langinfo): New files.
This commit is contained in:
Po Lu 2022-12-31 18:04:18 +08:00
parent 785095c416
commit cfbc8a5dbc
83 changed files with 14297 additions and 217 deletions

View file

@ -12,7 +12,7 @@
(c-mode . ((c-file-style . "GNU")
(c-noise-macro-names . ("INLINE" "NO_INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED"
"UNINIT" "CALLBACK" "ALIGN_STACK" "ATTRIBUTE_MALLOC"
"ATTRIBUTE_DEALLOC_FREE"))
"ATTRIBUTE_DEALLOC_FREE" "ANDROID_EXPORT"))
(electric-quote-comment . nil)
(electric-quote-string . nil)
(indent-tabs-mode . t)

17
.gitignore vendored
View file

@ -52,6 +52,15 @@ src/config.h
src/epaths.h
src/emacs-module.h
# Built by recursive call to `configure'.
*.android
# Built by `java'.
java/install_temp/*
java/*.apk*
java/*.dex
java/org/gnu/emacs/*.class
# C-level sources built by 'make'.
lib/alloca.h
lib/assert.h
@ -81,6 +90,14 @@ src/globals.h
src/lisp.mk
src/verbose.mk
# Stuff built during cross compilation
xcompile/lib/*
xcompile/src/*
xcompile/lib-src/*
xcompile/sys/*
xcompile/config.status
xcompile/*.bak
# Lisp-level sources built by 'make'.
*cus-load.el
*loaddefs.el

View file

@ -106,15 +106,15 @@ top_builddir = @top_builddir@
FIND_DELETE = @FIND_DELETE@
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
USE_STARTUP_NOTIFICATION = @USE_STARTUP_NOTIFICATION@
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
HAVE_BE_APP = @HAVE_BE_APP@
HAVE_PGTK = @HAVE_PGTK@
HAVE_GSETTINGS = @HAVE_GSETTINGS@
ANDROID = @ANDROID@
# ==================== Where To Install Things ====================
# Location to install Emacs.app under GNUstep / macOS.
@ -339,6 +339,10 @@ EMACS_PDMP = `./src/emacs${EXEEXT} --fingerprint`.pdmp
# Subdirectories to make recursively.
SUBDIR = $(NTDIR) lib lib-src src lisp
ifeq ($(ANDROID),yes)
SUBDIR := $(SUBDIR) java
endif
# The subdir makefiles created by config.status.
SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
SUBDIR_MAKEFILES = $(patsubst ${srcdir}/%,%,${SUBDIR_MAKEFILES_IN:.in=})
@ -467,20 +471,20 @@ epaths-force:
esac; \
done
@(gamedir='${gamedir}'; \
sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
-e 's;\(#.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
-e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
-e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
-e 's;\(#.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
-e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
-e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
-e 's;\(#.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
-e 's;\(#.*PATH_INFO\).*$$;\1 "${infodir}";' \
-e 's;\(#.*PATH_DATA\).*$$;\1 "${etcdir}";' \
-e 's;\(#.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
-e 's;\(#.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
-e 's;\(#.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
-e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
-e 's;\(#define.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
-e 's;\(#define.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
-e 's;\(#define.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
-e 's;\(#define.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
-e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
-e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
-e 's;\(#define.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
-e 's;\(#define.*PATH_INFO\).*$$;\1 "${infodir}";' \
-e 's;\(#define.*PATH_DATA\).*$$;\1 "${etcdir}";' \
-e 's;\(#define.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
-e 's;\(#define.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
-e 's;\(#define.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
-e 's;\(#define.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
# The w32 build needs a slightly different editing, and it uses
@ -532,6 +536,12 @@ lisp: src
lib lib-src lisp nt: Makefile
$(MAKE) -C $@ all
java: lisp
$(MAKE) -C $@ all
xcompile: src
$(MAKE) -C $@ all
trampolines: src lisp
ifeq ($(HAVE_NATIVE_COMP),yes)
$(MAKE) -C lisp trampolines
@ -561,20 +571,38 @@ blessmail: Makefile src
# then attempts to build that file. This forces 'Makefile', 'lib/Makefile',
# etc. to be built without running into similar recursion problems.
MAKEFILE_NAME = Makefile
ifeq ($(ANDROID),)
$(MAKEFILE_NAME): config.status $(srcdir)/configure \
$(srcdir)/lib/gnulib.mk.in \
$(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
MAKE='$(MAKE)' ./config.status
else
# Note that calling config.status is insufficient on Android due to
# the recursive calls to configure.
$(MAKEFILE_NAME): $(srcdir)/configure \
$(srcdir)/lib/gnulib.mk.in \
$(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS);
endif
# Don't erase these files if make is interrupted while refreshing them.
.PRECIOUS: Makefile config.status
# Note that calling config.status --recheck is insufficient on Android
# due to the recursive calls to configure.
ifneq ($(ANDROID),)
config.status: ${srcdir}/configure
if [ -x ./config.status ]; then \
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS)
else
config.status: ${srcdir}/configure
if [ -x ./config.status ]; then \
$(CFG) ./config.status --recheck; \
else \
else \
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
fi
endif
$(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/m4/*.m4
cd $(srcdir) && ./autogen.sh autoconf
@ -999,7 +1027,7 @@ mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
### with them.
###
### Delete '.dvi' files here if they are not part of the distribution.
clean_dirs = $(mostlyclean_dirs) nextstep admin/charsets admin/unidata
clean_dirs = $(mostlyclean_dirs) java nextstep admin/charsets admin/unidata
$(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))

View file

@ -134,3 +134,5 @@ cp -- "$gnulib_srcdir"/lib/af_alg.h \
"$src"lib &&
{ test -z "$src" || cd "$src"; } &&
./autogen.sh
echo "Please update the block of vpath statements in lib/Makefile.in"

View file

@ -26,6 +26,16 @@ dnl Note this is parsed by (at least) make-dist and lisp/cedet/ede/emacs.el.
AC_INIT([GNU Emacs], [30.0.50], [bug-gnu-emacs@gnu.org], [],
[https://www.gnu.org/software/emacs/])
if test "$XCONFIGURE" = "android"; then
# configure is being called recursively to configure Emacs for
# Android!
AC_MSG_NOTICE([called to recursively configure Emacs \
for Android.])
# Set CC to ANDROID_CC, and CXX to ANDROID_CXX.
CC=$ANDROID_CC
CXX=$ANDROID_CXX
fi
dnl Set emacs_config_options to the options of 'configure', quoted for the shell,
dnl and then quoted again for a C string. Separate options with spaces.
dnl Add some environment variables, if they were passed via the environment
@ -126,7 +136,25 @@ MAKE=$ac_cv_path_MAKE
export MAKE
dnl Canonicalize the configuration name.
if test "$XCONFIGURE" = "android"; then
dnl Set host to whatever Android system Emacs is being configured
dnl for. Determine this by looking at the output of ANDROID_CC.
AC_MSG_CHECKING([the cross-compiler's target])
cc_target=`${CC} -v 2>&1 | sed -n 's/Target: //p'`
case "$cc_target" in
*android*) host_alias=$cc_target
;;
*) AC_MSG_ERROR([The cross compiler does not compile for Android.
Please verify that you specified the correct compiler in the ANDROID_CC
and ANDROID_CXX variables when you ran configure.])
;;
esac
AC_MSG_RESULT([$host_alias])
fi
AC_CANONICAL_HOST
AC_CANONICAL_BUILD
case $host in
*-mingw*)
@ -207,6 +235,13 @@ AC_ARG_WITH([all],
[with_features=$withval],
[with_features=yes])
dnl ARCH_INDEPENDENT_CONFIG_FILES(FILE...)
dnl Like AC_CONFIG_FILES(FILE). However, do not generate this
dnl if configure is being called recursively in preparation
dnl for cross-compilation.
AC_DEFUN([ARCH_INDEPENDENT_CONFIG_FILES], [
AS_IF([test "$XCONFIGURE" != "android"], [AC_CONFIG_FILES([$1])])])
dnl OPTION_DEFAULT_OFF(NAME, HELP-STRING)
dnl Create a new --with option that defaults to being disabled.
dnl NAME is the base name of the option. The shell variable with_NAME
@ -498,6 +533,7 @@ OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 32-bit Cygwin])
OPTION_DEFAULT_ON([xinput2],[don't use version 2 of the X Input Extension for input])
OPTION_DEFAULT_OFF([small-ja-dic],[generate a smaller-size Japanese dictionary])
OPTION_DEFAULT_OFF([android],[cross-compile Android application package])
AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
[use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])],
@ -682,6 +718,237 @@ AC_ARG_ENABLE([build-details],
[test "$enableval" = no && BUILD_DETAILS=--no-build-details])
AC_SUBST([BUILD_DETAILS])
# Start Android configuration. This is done in three steps:
# First, the SDK tools needed to build the Android package on the host
# are found.
# Then, configure is called inside itself with the NDK C and C++
# compilers, and the Makefiles generated, along with config.h, are
# renamed to end with .android.
# Finally, configure continues to configure the Emacs binary that will
# run on the host.
ANDROID=
JAVAC=
AAPT=
JARSIGNER=
ZIPALIGN=
DX=
ANDROID_JAR=
ANDROID_ABI=
# This is a list of Makefiles that have alternative versions for
# Android.
android_makefiles="lib/Makefile lib/gnulib.mk lib-src/Makefile src/Makefile"
AC_ARG_VAR([JAVAC], [Java compiler path. Used for Android.])
AC_ARG_VAR([JARSIGNER], [Java package signer path. Used for Android.])
AC_ARG_VAR([SDK_BULD_TOOLS], [Path to the Android SDK build tools.])
if test "$with_android" = "yes"; then
AC_MSG_ERROR([Please specify the path to the Android.jar file, like so:
./configure --with-android=/path/to/android.jar
along with the path to the SDK build-tools (this is the directory with
tools such as aapt, dx, and aidl):
SDK_BUILD_TOOLS=/path/to/sdk-build-tools
The cross-compiler should then be specified:
ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang
ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++])
elif test "$with_android" = "no" || test "$with_android" = ""; then
ANDROID=no
else
AC_CHECK_PROGS([JAVAC], [javac])
if test "$JAVAC" = ""; then
AC_MSG_ERROR([The Java compiler required to build Emacs was not found.
Please make sure `javac' can be found on your path, or alternatively specify
the path to your Java compiler before configuring Emacs, like so:
JAVAC=/opt/jdk/bin/javac ./configure --with-android])
fi
AC_CHECK_PROGS([JARSIGNER], [jarsigner])
if test "$JARSIGNER" = ""; then
AC_MSG_ERROR([The Java package signing utility was not found.
Please make sure `jarsigner' can be found on your path, or alternatively
specify its location before configuring Emacs, like so:
JARSIGNER=/opt/jdk/bin/jarsigner ./configure --with-android])
fi
AC_CACHE_CHECK([whether or not the Java compiler works],
[emacs_cv_working_javac],
AS_IF([rm -f conftest.class
cat << EOF > conftest.java
import android.app.Activity;
import android.os.Bundle;
class conftest extends Activity
{
@Override
public void
onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
}
}
EOF
("$JAVAC" -classpath "$with_android" -target 1.7 -source 1.7 conftest.java \
-d . >&AS_MESSAGE_LOG_FD 2>&1) && test -s conftest.class && rm -f conftest.class],
[emacs_cv_working_javac=yes],
[emacs_cv_working_javac=no]))
if test "$emacs_cv_working_javac" = "no"; then
AC_MSG_ERROR([The Java compiler does not work, or you did not specify
a valid path to android.jar. See config.log for more details.])
fi
ANDROID_JAR="$with_android"
AC_PATH_PROGS([AAPT], [aapt], [], "${SDK_BUILD_TOOLS}:$PATH")
if test "$AAPT" = ""; then
AC_MSG_ERROR([The Android asset packaging tool was not found.
Please verify that the path to the SDK build tools you specified is correct])
fi
AC_PATH_PROGS([DX], [dx d8], [], "${SDK_BUILD_TOOLS}:$PATH")
if test "DX" = ""; then
AC_MSG_ERROR([The Android dexer was not found.
Please verify that the path to the SDK build tools you specified is correct])
fi
AC_PATH_PROGS([ZIPALIGN], [zipalign], [], "${SDK_BUILD_TOOLS}:$PATH")
if test "ZIPALIGN" = ""; then
AC_MSG_ERROR([The Android ZIP archive alignment utility was not found.
Please verify that the path to the SDK build tools you specified is correct]);
fi
dnl Now configure Emacs to generate binaries for Android. After the
dnl configuration completes, move the generated Makefiles.
if test "$ANDROID_CC" = "" || test "$ANDROID_CXX" = ""; then
AC_MSG_ERROR([Please specify the path to the Android cross-compiler
for your machine. For example:
ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang \\
ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++ \\
./configure --with-android])
fi
dnl Obtain the cross compiler's target to find out where binaries go
dnl in the resulting package.
AC_MSG_CHECKING([for the kind of Android system Emacs is being built for])
cc_target=`${ANDROID_CC} -v 2>&1 | sed -n 's/Target: //p'`
case "$cc_target" in
*i[3-6]86*) android_abi=x86
;;
*x86_64*) android_abi=x86_64
;;
*aarch64*) android_abi=arm64-v8a
;;
*arm*v7a*) android_abi=armeabi-v7a
;;
*mips*) android_abi=mips
;;
*arm*) android_abi=armeabi
;;
*) AC_MSG_ERROR([configure could not determine the type of Android \
binary Emacs is being configured for. Please port this configure script \
to your Android system, or verify that you specified the correct compiler \
in the ANDROID_CC and ANDROID_CXX variables when you ran configure.])
;;
esac
AC_MSG_RESULT([$android_abi])
ANDROID_ABI=$android_abi
# Save confdefs.h and config.log for now.
mv -f confdefs.h _confdefs.h
mv -f config.log _config.log
AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
ANDROID_CXX="$ANDROID_CXX" $0], [], [AC_MSG_ERROR([Failed to cross-\
configure Emacs for android.])])
# Now set ANDROID to yes.
ANDROID=yes
for makefile in $android_makefiles; do
AC_MSG_NOTICE([Generating $makefile.android])
mv -f "$makefile" "$makefile.android"
done
AC_MSG_NOTICE([Generating src/config.h.android])
mv -f src/config.h src/config.h.android
# Move confdefs.h back now that the recursive call to configure is
# complete.
mv -f _confdefs.h confdefs.h
# Move the Android config.log to config.log.android. */
mv -f config.log config.log.android
# And _config.log back.
mv -f _config.log config.log
fi
AC_SUBST([ANDROID])
AC_SUBST([JAVAC])
AC_SUBST([AAPT])
AC_SUBST([DX])
AC_SUBST([ZIPALIGN])
AC_SUBST([ANDROID_JAR])
AC_SUBST([ANDROID_ABI])
if test "$XCONFIGURE" = "android"; then
ANDROID=yes
# Enable cross compiling.
cross_compiling=yes
fi
AC_SUBST([XCONFIGURE])
if test "$ANDROID" = "yes"; then
# When --with-android is specified, all build options must be
# disabled, both within the recursive invocation of configure and
# outside.
with_xpm=no
with_jpeg=no
with_tiff=no
with_gif=no
with_png=no
with_rsvg=no
with_webp=no
with_sqlite3=no
with_lcms2=no
with_libsystemd=no
with_cairo=no
with_xml2=no
with_imagemagick=no
with_json=no
with_tree_sitter=no
with_xft=no
with_harfbuzz=no
with_libotf=no
with_gpm=no
with_dbus=no
with_gsettings=no
with_selinx=no
with_gnutls=no
with_zlib=no
with_modules=no
with_threads=no
fi
dnl This used to use changequote, but, apart from 'changequote is evil'
dnl per the autoconf manual, we can speed up autoconf somewhat by quoting
dnl the great gob of text. Thus it's not processed for possible expansion.
@ -705,6 +972,11 @@ dnl quotation begins
opsys='' unported=no
case "${canonical}" in
## Android
*linux-android* )
opsys=android
;;
## GNU/Linux and similar ports
*-*-linux* )
opsys=gnu-linux
@ -871,6 +1143,7 @@ AC_DEFUN([_AC_PROG_CC_C89], [$2])
dnl Sets GCC=yes if using gcc.
AC_PROG_CC([gcc cc cl clang "$XCRUN gcc" "$XCRUN clang"])
if test -n "$XCRUN"; then
AC_CHECK_PROGS([AR], [ar "$XCRUN ar"])
test -n "$AR" && export AR
@ -1123,6 +1396,12 @@ AS_IF([test $gl_gcc_warnings = no],
nw="$nw -Wsuggest-attribute=format"
fi
# If Emacs is being built for Android and many functions are
# currently stubbed out for operation on the build machine, disable
# -Wsuggest-attribute=noreturn.
nw="$nw -Wsuggest-attribute=noreturn"
gl_MANYWARN_ALL_GCC([ws])
gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
for w in $ws; do
@ -1143,6 +1422,8 @@ AS_IF([test $gl_gcc_warnings = no],
gl_WARN_ADD([-Wno-null-pointer-arithmetic])
gl_WARN_ADD([-Wno-implicit-const-int-float-conversion])
gl_WARN_ADD([-Wno-int-in-bool-context])
gl_WARN_ADD([-Wno-shift-overflow])
gl_WARN_ADD([-Wno-bitwise-instead-of-logical])
fi
# This causes too much noise in the MinGW build
@ -1265,7 +1546,7 @@ else
AM_DEFAULT_VERBOSITY=0
fi
AC_SUBST([AM_DEFAULT_VERBOSITY])
AC_CONFIG_FILES([src/verbose.mk])
ARCH_INDEPENDENT_CONFIG_FILES([src/verbose.mk])
dnl Some other nice autoconf tests.
AC_PROG_INSTALL
@ -1762,7 +2043,10 @@ AC_SUBST([SYSTEM_TYPE])
pre_PKG_CONFIG_CFLAGS=$CFLAGS
pre_PKG_CONFIG_LIBS=$LIBS
PKG_PROG_PKG_CONFIG([0.9.0])
dnl pkg-config does not work when cross-compiling for Android.
if test "${ANDROID}" != "yes"; then
PKG_PROG_PKG_CONFIG([0.9.0])
fi
dnl EMACS_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4])
dnl acts like PKG_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4],
@ -1945,14 +2229,49 @@ AC_SUBST([AUTO_DEPEND])
window_system=none
ANDROID_OBJ=
ANDROID_LIBS=
ANDROID_CFLAGS=
CM_OBJ="cm.o"
if test "${ANDROID}" = "yes"; then
window_system=android
no_x=yes
ANDROID_OBJ="androidterm.o androidfns.o androidfont.o android.o"
ANDROID_LIBS=
CM_OBJ=
AC_DEFINE([HAVE_ANDROID], [1], [Define to 1 if Emacs is being built
with Android support])
if test "${XCONFIGURE}" != "android"; then
AC_DEFINE([ANDROID_STUBIFY], [1], [Define to 1 if Emacs is being built
for Android, but all API calls need to be stubbed out])
else
# Emacs will be built as a shared library, and a wrapper around it
# will also be built for the benefit of applications. This
# requires Emacs be built as a position independent executable.
ANDROID_CFLAGS="-fPIC -fvisibility=hidden"
# Link with libraries required for Android support.
ANDROID_LIBS="-landroid -llog"
fi
fi
AC_SUBST(ANDROID)
AC_SUBST(ANDROID_OBJ)
AC_SUBST(ANDROID_LIBS)
AC_SUBST(ANDROID_CFLAGS)
if test "${with_pgtk}" = "yes"; then
window_system=pgtk
fi
AC_PATH_X
if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
window_system=x11
if test "${ANDROID}" != "yes"; then
AC_PATH_X
if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
window_system=x11
fi
fi
LD_SWITCH_X_SITE_RPATH=
@ -2276,7 +2595,6 @@ NTDIR=
LIBS_ECLIENT=
LIB_WSOCK32=
NTLIB=
CM_OBJ="cm.o"
XARGS_LIMIT=
if test "${HAVE_W32}" = "yes"; then
AC_DEFINE([HAVE_NTGUI], [1], [Define to use native MS Windows GUI.])
@ -2439,7 +2757,11 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
haiku )
term_header=haikuterm.h
;;
android )
term_header=androidterm.h
;;
esac
AC_SUBST([HAVE_PGTK])
if test "$window_system" = none && test "X$with_x" != "Xno"; then
@ -2773,7 +3095,6 @@ fail;
fi
fi
### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
HAVE_RSVG=no
if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
@ -4202,7 +4523,7 @@ AC_SUBST([HAVE_MODULES])
AC_SUBST([MODULES_SUFFIX])
AC_SUBST([MODULES_SECONDARY_SUFFIX])
AC_CONFIG_FILES([src/emacs-module.h])
ARCH_INDEPENDENT_CONFIG_FILES([src/emacs-module.h])
AC_SUBST_FILE([module_env_snippet_25])
AC_SUBST_FILE([module_env_snippet_26])
AC_SUBST_FILE([module_env_snippet_27])
@ -5003,6 +5324,40 @@ getpwent endpwent getgrent endgrent \
renameat2 \
cfmakeraw cfsetspeed __executable_start log2 pthread_setname_np \
pthread_set_name_np])
if test "$ac_cv_func_cfmakeraw" != "yes"; then
# On some systems (Android), cfmakeraw is inline, so AC_CHECK_FUNCS
# cannot find it. Check if some code including termios.h and using
# cfmakeraw builds.
AC_CACHE_CHECK([whether cfmakeraw is inline],
[emacs_cv_func_cfmakeraw_inline],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[#include <termios.h>]],
[[&cfmakeraw;]])],
[emacs_cv_func_cfmakeraw_inline=yes],
[emacs_cv_func_cfmakeraw_inline=no])])
if test "$emacs_cv_func_cfmakeraw_inline" = "yes"; then
# Define HAVE_CFMAKERAW again.
AC_DEFINE([HAVE_CFMAKERAW], [1])
fi
fi
if test "$ac_cv_func_cfsetspeed" != "yes"; then
AC_CACHE_CHECK([whether cfsetspeed is inline],
[emacs_cv_func_cfsetspeed_inline],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[#include <termios.h>]],
[[&cfsetspeed;]])],
[emacs_cv_func_cfsetspeed_inline=yes],
[emacs_cv_func_cfsetspeed_inline=no])])
if test "$emacs_cv_func_cfsetspeed_inline" = "yes"; then
# Define HAVE_CFSETSPEED again.
AC_DEFINE([HAVE_CFSETSPEED], [1])
fi
fi
LIBS=$OLD_LIBS
if test "$ac_cv_func_pthread_setname_np" = "yes"; then
@ -5108,7 +5463,7 @@ AC_DEFUN([tputs_link_source], [
# than to expect to find it in ncurses.
# Also we need tputs and friends to be able to build at all.
AC_CACHE_CHECK([for library containing tputs], [emacs_cv_tputs_lib],
[if test "${opsys}" = "mingw32"; then
[if test "${opsys}" = "mingw32" || test "$opsys" = "android"; then
emacs_cv_tputs_lib='none required'
else
# curses precedes termcap because of AIX (Bug#9736#35) and OpenIndiana.
@ -5175,7 +5530,7 @@ fail;
fi
;;
mingw32)
mingw32 | android)
TERMINFO=no
LIBS_TERMCAP=
;;
@ -5729,7 +6084,7 @@ case $opsys in
AC_DEFINE([FIRST_PTY_LETTER], ['p'])
;;
gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl )
gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl | android )
dnl if HAVE_GRANTPT
if test "x$ac_cv_func_grantpt" = xyes; then
AC_DEFINE([UNIX98_PTYS], [1], [Define if the system has Unix98 PTYs.])
@ -6656,6 +7011,7 @@ AC_DEFINE_UNQUOTED([EMACS_CONFIG_FEATURES], ["${emacs_config_features}"],
[Summary of some of the main features enabled by configure.])
AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D}
Does Emacs use Android? ${ANDROID}
Does Emacs use -lXpm? ${HAVE_XPM}
Does Emacs use -ljpeg? ${HAVE_JPEG}
Does Emacs use -ltiff? ${HAVE_TIFF}
@ -6759,12 +7115,15 @@ fi
AC_CONFIG_FILES([Makefile lib/gnulib.mk])
dnl config.status treats $srcdir specially, so I think this is ok...
AC_CONFIG_FILES([$srcdir/doc/man/emacs.1])
ARCH_INDEPENDENT_CONFIG_FILES([$srcdir/doc/man/emacs.1])
m4_define([subdir_makefiles],
[lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile nextstep/Makefile nt/Makefile])
SUBDIR_MAKEFILES="subdir_makefiles"
AC_CONFIG_FILES(subdir_makefiles)
AC_CONFIG_FILES([lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile
lwlib/Makefile nextstep/Makefile nt/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([doc/emacs/Makefile doc/misc/Makefile
doc/lispintro/Makefile doc/lispref/Makefile
lisp/Makefile leim/Makefile])
SUBDIR_MAKEFILES="lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile lwlib/Makefile nextstep/Makefile nt/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile lisp/Makefile leim/Makefile"
dnl The test/ directory is missing if './make-dist --no-tests' was used.
opt_makefile=test/Makefile
@ -6772,24 +7131,31 @@ if test -f "$srcdir/$opt_makefile.in"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
dnl Again, it's best not to use a variable. Though you can add
dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
AC_CONFIG_FILES([test/Makefile])
AC_CONFIG_FILES([test/manual/noverlay/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([test/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([test/manual/noverlay/Makefile])
fi
opt_makefile=test/infra/Makefile
if test -f "$srcdir/$opt_makefile.in"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
dnl Again, it's best not to use a variable. Though you can add
dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
AC_CONFIG_FILES([test/infra/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([test/infra/Makefile])
fi
if test "$ANDROID" = "yes"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES java/Makefile"
fi
if test "$XCOMPILE" = "yes"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES xcompile/Makefile"
fi
dnl The admin/ directory used to be excluded from tarfiles.
if test -d $srcdir/admin; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES admin/charsets/Makefile admin/unidata/Makefile admin/grammars/Makefile"
AC_CONFIG_FILES([admin/charsets/Makefile])
AC_CONFIG_FILES([admin/unidata/Makefile])
AC_CONFIG_FILES([admin/grammars/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([admin/charsets/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([admin/unidata/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([admin/grammars/Makefile])
fi dnl -d admin
@ -6800,65 +7166,75 @@ AC_SUBST([SUBDIR_MAKEFILES_IN])
SMALL_JA_DIC=$with_small_ja_dic
AC_SUBST([SMALL_JA_DIC])
dnl You might wonder (I did) why epaths.h is generated by running make,
dnl rather than just letting configure generate it from epaths.in.
dnl One reason is that the various paths are not fully expanded (see above);
dnl e.g., gamedir='${localstatedir}/games/emacs'.
dnl Secondly, the GNU Coding standards require that one should be able
dnl to run 'make prefix=/some/where/else' and override the values set
dnl by configure. This also explains the 'move-if-change' test and
dnl the use of force in the 'epaths-force' rule in Makefile.in.
AC_CONFIG_COMMANDS([src/epaths.h], [
if test "${opsys}" = "mingw32"; then
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-ns-self-contained
else
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
dnl The following commands are run on the host system when building
dnl Emacs.
dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
dnl is not yet set, sigh. Or we could use ../$srcdir/src/.gdbinit,
dnl or a symlink?
AC_CONFIG_COMMANDS([src/.gdbinit], [
if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
fi
])
if test "$XCONFIGURE" != "android"; then
dnl You might wonder (I did) why epaths.h is generated by running
dnl make, rather than just letting configure generate it from
dnl epaths.in. One reason is that the various paths are not fully
dnl expanded (see above); e.g.,
dnl gamedir='${localstatedir}/games/emacs'. Secondly, the GNU
dnl Coding standards require that one should be able to run 'make
dnl prefix=/some/where/else' and override the values set by
dnl configure. This also explains the 'move-if-change' test and the
dnl use of force in the 'epaths-force' rule in Makefile.in.
AC_CONFIG_COMMANDS([src/epaths.h], [
if test "${opsys}" = "mingw32"; then
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-ns-self-contained
else
${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
dnl Perhaps this would be better named doc-emacs-emacsver.texi?
dnl See comments for etc-refcards-emacsver.tex.
dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
dnl it is not quite the same. But we are generating in $srcdir.
AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
])
dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
dnl is not yet set, sigh. Or we could use ../$srcdir/src/.gdbinit,
dnl or a symlink?
AC_CONFIG_COMMANDS([src/.gdbinit], [
if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
fi
])
dnl If we give this the more natural name, etc/refcards/emacsver.texi,
dnl then a directory etc/refcards is created in the build directory,
dnl which is probably harmless, but confusing (in out-of-tree builds).
dnl (If we were to generate etc/refcards/Makefile, this might change.)
dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
])
dnl Perhaps this would be better named doc-emacs-emacsver.texi?
dnl See comments for etc-refcards-emacsver.tex.
dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
dnl it is not quite the same. But we are generating in $srcdir.
AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
])
dnl If we give this the more natural name, etc/refcards/emacsver.texi,
dnl then a directory etc/refcards is created in the build directory,
dnl which is probably harmless, but confusing (in out-of-tree builds).
dnl (If we were to generate etc/refcards/Makefile, this might change.)
dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
])
if test $AUTO_DEPEND = yes; then
for dir in $AUTODEPEND_PARENTS; do
AS_MKDIR_P([$dir/deps])
done
fi
if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
AS_MKDIR_P([lib/malloc])
if test $AUTO_DEPEND = yes; then
AS_MKDIR_P([lib/deps/malloc])
for dir in $AUTODEPEND_PARENTS; do
AS_MKDIR_P([$dir/deps])
done
fi
if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
AS_MKDIR_P([lib/malloc])
if test $AUTO_DEPEND = yes; then
AS_MKDIR_P([lib/deps/malloc])
fi
fi
fi
# Make java/Makefile
ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile])
AC_OUTPUT
if test ! "$with_mailutils"; then

View file

@ -1094,6 +1094,30 @@ Please refer to the LLDB reference on the web for more information
about LLDB. If you already know GDB, you will also find a mapping
from GDB commands to corresponding LLDB commands there.
** Debugging Emacs on Android.
Attaching GDB to Emacs running inside the Android application setup
requires a special script found in the java/ directory, and a suitable
GDB server binary to be present on the Android device, which is
present on the free versions of Android. Connecting to the device
also requires the `adb' (Android Debug Bridge) utility, and telling
the Android system to resume the Emacs process after startup requires
the Java debugger (jdb).
If all three of those tools are present, simply run (from the Emacs
source directory):
../java/debug.sh -- [any extra arguments you wish to pass to gdb]
After which, upon waiting a while, the GDB prompt will show up.
If Emacs crashes and "JNI ERROR" shows up in the Android system log,
then placing a breakpoint on:
break art::JavaVMExt::JniAbort
will let you find the source of the crash.
This file is part of GNU Emacs.

54
java/AndroidManifest.xml Normal file
View file

@ -0,0 +1,54 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.gnu.emacs" android:installLocation="auto">
<!-- Paste in every permission in existence so Emacs can do
anything. -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.TRANSMIT_IR" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk android:minSdkVersion="7"
android:targetSdkVersion="28"/>
<application android:name="org.gnu.emacs.EmacsApplication"
android:label="GNU Emacs"
android:hardwareAccelerated="true"
android:supportsRtl="true"
android:theme="@android:style/Theme"
android:debuggable="true"
android:extractNativeLibs="true">
<activity android:name="org.gnu.emacs.EmacsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.gnu.emacs.EmacsService"
android:directBootAware="false"
android:enabled="true"
android:exported="false"
android:label="GNU Emacs service"/>
</application>
</manifest>

150
java/Makefile.in Normal file
View file

@ -0,0 +1,150 @@
### @configure_input@
# 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/>.
top_builddir = @top_builddir@
-include ${top_builddir}/src/verbose.mk
SHELL = @SHELL@
JAVAC = @JAVAC@
AAPT = @AAPT@
DX = @DX@
ZIPALIGN = @ZIPALIGN@
JARSIGNER = @JARSIGNER@
ANDROID_JAR = @ANDROID_JAR@
ANDROID_ABI = @ANDROID_ABI@
WARN_JAVAFLAGS = -Xlint:deprecation
JAVAFLAGS = -classpath "$(ANDROID_JAR):." -target 1.7 -source 1.7 \
$(WARN_JAVAFLAGS)
SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
JAVA_FILES = $(shell find . -type f -name *.java)
CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
# How this stuff works.
# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
# containing the following files:
# lib/$(ANDROID_ABI)/libemacs.so
# lib/$(ANDROID_ABI)/libandroid-emacs.so
# lib/$(ANDROID_ABI)/libctags.so
# lib/$(ANDROID_ABI)/libhexl.so
# lib/$(ANDROID_ABI)/libmovemail.so
# lib/$(ANDROID_ABI)/librcs2log.so
# lib/$(ANDROID_ABI)/libebrowse.so
# assets/info/
# assets/etc/
# assets/lisp/
.PHONY: emacs.apk-in all
all: emacs.apk
# Binaries to cross-compile.
CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
../xcompile/lib-src/ebrowse
# Libraries to cross-compile.
CROSS_LIBS = ../xcompile/src/libemacs.so
.PHONY: $(CROSS_BINS) $(CROSS_LIBS)
../xcompile/src/android-emacs ../xcompile/src/libemacs.so:
make -C ../xcompile src/$(notdir $@)
../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
../xcompile/lib-src/ctags ../xcompile/lib-src/ebrowse &:
make -C ../xcompile lib-src/$(notdir $@)
emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
# Make the working directory for this stuff
rm -rf install_temp
mkdir -p install_temp/lib/$(ANDROID_ABI)
mkdir -p install_temp/assets/etc
mkdir -p install_temp/assets/lisp
mkdir -p install_temp/assets/info
# Install architecture independents to assets/etc and assets/lisp
cp -r $(top_builddir)/lisp install_temp/assets
cp -r $(top_builddir)/etc install_temp/assets
# Remove undesirable files from those directories.
for subdir in `find install_temp -type d -print`; do \
chmod a+rx $${subdir} ; \
rm -rf $${subdir}/.gitignore ; \
rm -rf $${subdir}/.DS_Store ; \
rm -rf $${subdir}/#* ; \
rm -rf $${subdir}/.#* ; \
rm -rf $${subdir}/*~ ; \
rm -rf $${subdir}/*.orig ; \
rm -rf $${subdir}/ChangeLog* ; \
rm -rf $${subdir}/[mM]akefile*[.-]in ; \
rm -rf $${subdir}/Makefile; \
done
# Install architecture dependents to lib/$(ANDROID_ABI). This
# perculiar naming scheme is required to make Android preserve these
# binaries upon installation.
for file in $(CROSS_BINS); do \
if [ -x $$file ]; then \
filename=`basename $$file`; \
cp -f $$file install_temp/lib/$(ANDROID_ABI)/lib$${filename}.so; \
fi \
done
for file in $(CROSS_LIBS); do \
if [ -x $$file ]; then \
cp -f $$file install_temp/lib/$(ANDROID_ABI); \
fi \
done
# Package everything.
$(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml
pushd install_temp; $(AAPT) add ../$@ `find lib -type f`; popd
pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
rm -rf install_temp
.SUFFIXES: .java .class
.java.class &:
$(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
# N.B. that find must be called all over again in case javac generated
# nested classes.
classes.dex: $(CLASS_FILES)
$(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \
$(subst $$,\$$,$(shell find . -type f -name *.class))
# When emacs.keystore expires, regenerate it with:
#
# keytool -genkey -v -keystore emacs.keystore -alias "Emacs keystore" \
# -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 100000
.PHONY: clean maintainer-clean
emacs.apk: classes.dex emacs.apk-in emacs.keystore
cp -f emacs.apk-in $@.unaligned
$(AAPT) add $@.unaligned classes.dex
$(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
$(ZIPALIGN) -f 4 $@.unaligned $@
rm -f $@.unaligned
clean:
rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class
rm -rf install-temp
find . -name '*.class' -delete
maintainer-clean: clean

6
java/README Normal file
View file

@ -0,0 +1,6 @@
This directory holds the Java sources of the port of GNU Emacs to
Android-like systems.
Please keep the Java code indented with tabs and formatted according
to the rules for C code in the GNU coding standards. Always use
C-style comments.

242
java/debug.sh Executable file
View file

@ -0,0 +1,242 @@
#!/bin/bash
### Run Emacs under GDB or JDB on 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, see <https://www.gnu.org/licenses/>.
set -m
oldpwd=`pwd`
cd `dirname $0`
devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
device=
progname=$0
package=org.gnu.emacs
activity=org.gnu.emacs.EmacsActivity
gdb_port=5039
jdb_port=64013
jdb=no
while [ $# -gt 0 ]; do
case "$1" in
## This option specifies the serial number of a device to use.
"--device" )
device="$2"
if [ -z device ]; then
echo "You must specify an argument to --device"
exit 1
fi
;;
"--help" )
echo "Usage: $progname [options] -- [gdb options]"
echo ""
echo " --device DEVICE run Emacs on the specified device"
echo " --port PORT run the GDB server on a specific port"
echo " --jdb-port PORT run the JDB server on a specific port"
echo " --jdb run JDB instead of GDB"
echo " --help print this message"
echo ""
echo "Available devices:"
for device in $devices; do
echo " " $device
done
echo ""
exit 0
;;
"--jdb" )
jdb=yes
;;
"--port" )
gdb_port=$1
;;
"--" )
shift
gdbargs=$@
break;
;;
* )
echo "$progname: Unrecognized argument $1"
exit 1
;;
esac
shift
done
if [ -z $devices ]; then
echo "No devices are available."
exit 1
fi
if [ -z $device ]; then
device=$devices
fi
if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
echo "Multiple devices are available. Please pick one using"
echo "--device and try again."
fi
echo "Looking for $package on device $device"
# Find the application data directory
app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
if [ -z $app_data_dir ]; then
echo "The data directory for the package $package was not found."
echo "Is it installed?"
fi
echo "Found application data directory at $app_data_dir..."
# Find which PIDs are associated with org.gnu.emacs
package_uid=`adb -s $device shell run-as $package id -u`
if [ -z $package_uid ]; then
echo "Failed to obtain UID of packages named $package"
exit 1
fi
# First, run ps -u $package_uid -o PID,CMD to fetch the list of
# process IDs.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
# Finally, kill each existing process.
for pid in $package_pids; do
echo "Killing existing process $pid..."
adb -s $device shell run-as $package kill -9 $pid &> /dev/null
done
# Now run the main activity. This must be done as the adb user and
# not as the package user.
echo "Starting activity $activity and attaching debugger"
# Exit if the activity could not be started.
adb -s $device shell am start -D "$package/$activity"
if [ ! $? ]; then
exit 1;
fi
# Now look for processes matching the package again.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
pid=$package_pids
num_pids=`wc -w <<< "$package_pids"`
if [ $num_pids -gt 1 ]; then
echo "More than one process was started:"
echo ""
adb -s $device shell run-as $package ps -u $package_uid | awk -- '{
if (!match ($0, /ps/))
print $0
}'
echo ""
printf "Which one do you want to attach to? "
read pid
elif [ -z $package_pids ]; then
echo "No processes were found to attach to."
exit 1
fi
# Start JDB to make the wait dialog disappear.
echo "Attaching JDB to unblock the application."
adb -s $device forward --remove-all
adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
if [ ! $? ]; then
echo "Failed to forward jdwp:$pid to $jdb_port!"
echo "Perhaps you need to specify a different port with --port?"
exit 1;
fi
jdb_command="jdb -connect \
com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
if [ $jdb = "yes" ]; then
# Just start JDB and then exit
$jdb_command
exit 1
fi
exec 4<> /tmp/file-descriptor-stamp
# Now run JDB with IO redirected to file descriptor 4 in a subprocess.
$jdb_command <&4 >&4 &
character=
# Next, wait until the prompt is found.
while read -n1 -u 4 character; do
if [ "$character" = ">" ]; then
echo "JDB attached successfully"
break;
fi
done
# Now start gdbserver on the device asynchronously.
echo "Attaching gdbserver to $pid on $device..."
exec 5<> /tmp/file-descriptor-stamp
adb -s $device shell run-as $package /system/bin/gdbserver --once \
"+debug.$package_uid.socket" --attach $pid >&5 &
# Wait until gdbserver successfully runs.
line=
while read -u 5 line; do
case "$line" in
*Attached* )
break;
;;
*error* | *Error* | failed )
echo $line
exit 1
;;
* )
;;
esac
done
# Send EOF to JDB to make it go away. This will also cause Android to
# allow Emacs to continue executing.
echo "Making JDB go away..."
echo "exit" >&4
read -u 4 line
echo "JDB has gone away with $line"
# Forward the gdb server port here.
adb -s $device forward "tcp:$gdb_port" \
"localfilesystem:$app_data_dir/debug.$package_uid.socket"
if [ ! $? ]; then
echo "Failed to forward $app_data_dir/debug.$package_uid.socket"
echo "to $gdb_port! Perhaps you need to specify a different port"
echo "with --port?"
exit 1;
fi
# Finally, start gdb with any extra arguments needed.
cd "$oldpwd"
gdb --eval-command "" --eval-command "target remote localhost:$gdb_port" $gdbargs

BIN
java/emacs.keystore Normal file

Binary file not shown.

View file

@ -0,0 +1,146 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.IllegalStateException;
import java.util.List;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
public class EmacsActivity extends Activity
{
public static final String TAG = "EmacsActivity";
/* List of all activities that do not have an associated
EmacsWindow. */
public static List<EmacsActivity> availableActivities;
/* The currently attached EmacsWindow, or null if none. */
private EmacsWindow window;
/* The frame layout associated with the activity. */
private FrameLayout layout;
static
{
/* Set up the list of available activities. */
availableActivities = new ArrayList<EmacsActivity> ();
};
public void
attachChild (EmacsWindow child)
{
if (window != null)
throw new IllegalStateException ("trying to attach window when one"
+ " already exists");
/* Record and attach the view. */
window = child;
layout.addView (window.view);
/* Remove the objects from the lists of what is available. */
EmacsService.availableChildren.remove (child);
availableActivities.remove (this);
/* Now set child->activity. */
child.setActivity (this);
}
/* Make this activity available for future windows to attach
again. */
public void
makeAvailable ()
{
window = null;
for (EmacsWindow iterWindow
: EmacsService.availableChildren)
{
synchronized (iterWindow)
{
if (!iterWindow.isDestroyed ())
attachChild (iterWindow);
return;
}
}
availableActivities.add (this);
}
@Override
public void
onCreate (Bundle savedInstanceState)
{
FrameLayout.LayoutParams params;
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
/* Make the frame layout. */
layout = new FrameLayout (this);
layout.setLayoutParams (params);
/* Set it as the content view. */
setContentView (layout);
/* Make the activity available before starting the
service. */
makeAvailable ();
if (EmacsService.SERVICE == null)
/* Start the Emacs service now. */
startService (new Intent (this, EmacsService.class));
super.onCreate (savedInstanceState);
}
@Override
public void
onStop ()
{
/* The activity is no longer visible. If there is a window
attached, detach it. */
if (window != null)
{
layout.removeView (window.view);
/* Notice that the window is already available too. But do
not call noticeAvailableChild; that might assign it to some
other activity, which behaves badly. */
EmacsService.availableChildren.add (window);
window = null;
}
/* Finally, remove this activity from the list of available
activities. */
availableActivities.remove (this);
super.onStop ();
}
};

View file

@ -0,0 +1,27 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.app.Application;
public class EmacsApplication extends Application
{
/* This class currently does nothing. */
};

View file

@ -0,0 +1,131 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Xfermode;
public class EmacsCopyArea implements EmacsPaintReq
{
private int src_x, src_y, dest_x, dest_y, width, height;
private EmacsDrawable destination, source;
private EmacsGC immutableGC;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source,
int src_x, int src_y, int width, int height,
int dest_x, int dest_y, EmacsGC immutableGC)
{
this.destination = destination;
this.source = source;
this.src_x = src_x;
this.src_y = src_y;
this.width = width;
this.height = height;
this.dest_x = dest_x;
this.dest_y = dest_y;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (dest_x, dest_y, dest_x + width,
dest_y + height);
}
@Override
public EmacsDrawable
getDrawable ()
{
return destination;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Bitmap bitmap;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect, srcRect;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
bitmap = source.getBitmap ();
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
canvas.drawBitmap (bitmap, new Rect (src_x, src_y,
src_x + width,
src_y + height),
rect, paint);
else
{
maskPaint = new Paint ();
srcRect = new Rect (0, 0, rect.width (),
rect.height ());
maskBitmap
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskCanvas = new Canvas (maskBitmap);
maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y,
src_x + width,
src_y + height),
srcRect, maskPaint);
canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
}
}
}

View file

@ -0,0 +1,137 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.Math;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Xfermode;
public class EmacsDrawLine implements EmacsPaintReq
{
private int x, y, x2, y2;
private EmacsDrawable drawable;
private EmacsGC immutableGC;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsDrawLine (EmacsDrawable drawable, int x, int y,
int x2, int y2, EmacsGC immutableGC)
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.x2 = x2;
this.y2 = y2;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (Math.min (x, x2 + 1),
Math.min (y, y2 + 1),
Math.max (x2 + 1, x),
Math.max (y2 + 1, y));
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect, srcRect;
int width, height;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
width = rect.width ();
height = rect.height ();
paint.setStyle (Paint.Style.STROKE);
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawLine ((float) x, (float) y,
(float) x2, (float) y2,
paint);
}
else
{
maskPaint = new Paint ();
maskBitmap
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskPaint.setColor (immutableGC.foreground | 0xff000000);
maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (),
maskBitmap.getHeight ());
maskCanvas.drawLine (0.0f, 0.0f, (float) Math.abs (x - x2),
(float) Math.abs (y - y2), maskPaint);
canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
}
paint.setXfermode (null);
}
}

View file

@ -0,0 +1,30 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
public class EmacsDrawPoint extends EmacsDrawRectangle
{
public
EmacsDrawPoint (EmacsDrawable drawable, int x, int y,
EmacsGC immutableGC)
{
super (drawable, x, y, 1, 1, immutableGC);
}
}

View file

@ -0,0 +1,127 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Xfermode;
public class EmacsDrawRectangle implements EmacsPaintReq
{
private int x, y, width, height;
private EmacsDrawable drawable;
private EmacsGC immutableGC;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsDrawRectangle (EmacsDrawable drawable, int x, int y,
int width, int height,
EmacsGC immutableGC)
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (x, y, x + width, y + height);
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect, srcRect;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
paint.setStyle (Paint.Style.STROKE);
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawRect (rect, paint);
}
else
{
maskPaint = new Paint ();
maskBitmap
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskPaint.setColor (immutableGC.foreground | 0xff000000);
maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (),
maskBitmap.getHeight ());
maskCanvas.drawRect (srcRect, maskPaint);
canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
}
paint.setXfermode (null);
}
}

View file

@ -0,0 +1,33 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Rect;
import android.graphics.Bitmap;
import android.graphics.Canvas;
public interface EmacsDrawable
{
public Canvas lockCanvas ();
public void unlockCanvas ();
public void damageRect (Rect damageRect);
public Bitmap getBitmap ();
public boolean isDestroyed ();
};

View file

@ -0,0 +1,150 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.Math;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
public class EmacsFillPolygon implements EmacsPaintReq
{
private EmacsDrawable drawable;
private EmacsGC immutableGC;
private Path path;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsFillPolygon (EmacsDrawable drawable, Point points[],
EmacsGC immutableGC)
{
int i;
this.drawable = drawable;
this.immutableGC = immutableGC;
/* Build the path from the given array of points. */
path = new Path ();
if (points.length >= 1)
{
path.moveTo (points[0].x, points[0].y);
for (i = 1; i < points.length; ++i)
path.lineTo (points[i].x, points[i].y);
path.close ();
}
}
@Override
public Rect
getRect ()
{
RectF rect;
rect = new RectF (0, 0, 0, 0);
path.computeBounds (rect, true);
return new Rect ((int) Math.floor (rect.left),
(int) Math.floor (rect.top),
(int) Math.ceil (rect.right),
(int) Math.ceil (rect.bottom));
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
paint.setStyle (Paint.Style.FILL);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawPath (path, paint);
}
else
{
maskPaint = new Paint ();
maskBitmap = immutableGC.clip_mask.bitmap;
maskBitmap = maskBitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskPaint.setColor (immutableGC.foreground | 0xff000000);
maskCanvas = new Canvas (maskBitmap);
path.offset (-rect.left, -rect.top, null);
maskCanvas.drawPath (path, maskPaint);
canvas.drawBitmap (maskBitmap, new Rect (0, 0, rect.width (),
rect.height ()),
rect, paint);
}
}
}

View file

@ -0,0 +1,129 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Xfermode;
import android.util.Log;
public class EmacsFillRectangle implements EmacsPaintReq
{
private int x, y, width, height;
private EmacsDrawable drawable;
private EmacsGC immutableGC;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsFillRectangle (EmacsDrawable drawable, int x, int y,
int width, int height,
EmacsGC immutableGC)
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (x, y, x + width, y + height);
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect, srcRect;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
paint.setStyle (Paint.Style.FILL);
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawRect (rect, paint);
}
else
{
maskPaint = new Paint ();
maskBitmap
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskPaint.setColor (immutableGC.foreground | 0xff000000);
maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (),
maskBitmap.getHeight ());
maskCanvas.drawRect (srcRect, maskPaint);
canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
}
paint.setXfermode (null);
}
}

View file

@ -0,0 +1,150 @@
/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.util.List;
public abstract class EmacsFontDriver
{
/* Font weights. */
public static final int THIN = 0;
public static final int ULTRA_LIGHT = 40;
public static final int LIGHT = 50;
public static final int SEMI_LIGHT = 55;
public static final int REGULAR = 80;
public static final int MEDIUM = 100;
public static final int SEMI_BOLD = 180;
public static final int BOLD = 200;
public static final int EXTRA_BOLD = 205;
public static final int BLACK = 210;
public static final int ULTRA_HEAVY = 250;
/* Font slants. */
public static final int REVERSE_OBLIQUE = 0;
public static final int REVERSE_ITALIC = 10;
public static final int NORMAL = 100;
public static final int ITALIC = 200;
public static final int OBLIQUE = 210;
/* Font widths. */
public static final int ULTRA_CONDENSED = 50;
public static final int EXTRA_CONDENSED = 63;
public static final int CONDENSED = 75;
public static final int SEMI_CONDENSED = 87;
public static final int UNSPECIFIED = 100;
public static final int SEMI_EXPANDED = 113;
public static final int EXPANDED = 125;
public static final int EXTRA_EXPANDED = 150;
public static final int ULTRA_EXPANDED = 200;
/* Font spacings. */
public static final int PROPORTIONAL = 0;
public static final int DUAL = 90;
public static final int MONO = 100;
public static final int CHARCELL = 110;
public class FontSpec
{
/* The fields below mean the same as they do in enum
font_property_index in font.h. */
public String foundry;
public String family;
public String adstyle;
public String registry;
public Integer width;
public Integer weight;
public Integer slant;
public Integer size;
public Integer spacing;
public Integer avgwidth;
@Override
public String
toString ()
{
return ("foundry: " + foundry
+ " family: " + family
+ " adstyle: " + adstyle
+ " registry: " + registry
+ " width: " + width
+ " weight: " + weight
+ " slant: " + slant
+ " spacing: " + spacing
+ " avgwidth: " + avgwidth);
}
};
public class FontMetrics
{
public short lbearing;
public short rbearing;
public short width;
public short ascent;
public short descent;
}
public class FontEntity extends FontSpec
{
/* No extra fields here. */
};
public abstract class FontObject extends FontSpec
{
public int minWidth;
public int maxWidth;
public int pixelSize;
public int height;
public int spaceWidth;
public int averageWidth;
public int ascent;
public int descent;
public int underlineThickness;
public int underlinePosition;
public int baselineOffset;
public int relativeCompose;
public int defaultAscent;
public int encodingCharset;
public int repertoryCharset;
public
FontObject ()
{
encodingCharset = -1;
repertoryCharset = -1;
}
};
/* These mean the same as they do in struct font_driver. */
public abstract FontEntity[] list (FontSpec fontSpec);
public abstract FontEntity match (FontSpec fontSpec);
public abstract String[] listFamilies ();
public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
public abstract int hasChar (FontSpec font, char charCode);
public abstract void textExtents (FontObject font, int code[],
FontMetrics fontMetrics[]);
public abstract int encodeChar (FontObject fontObject, char charCode);
public static EmacsFontDriver
createFontDriver ()
{
return new EmacsSdk7FontDriver ();
}
};

View file

@ -0,0 +1,116 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Rect;
/* X like graphics context structures. Keep the enums in synch with
androidgui.h! */
public class EmacsGC extends EmacsHandleObject
{
public static final int GC_COPY = 0;
public static final int GC_XOR = 1;
public static final int GC_FILL_SOLID = 0;
public static final int GC_FILL_OPAQUE_STIPPLED = 1;
public int function, fill_style;
public int foreground, background;
public int clip_x_origin, clip_y_origin;
public int ts_origin_x, ts_origin_y;
public Rect clip_rects[];
public EmacsPixmap clip_mask, stipple;
private boolean dirty;
private EmacsGC immutableGC;
/* The following fields are only set on immutable GCs. */
public
EmacsGC (short handle)
{
/* For historical reasons the C code has an extra layer of
indirection above this GC handle. struct android_gc is the GC
used by Emacs code, while android_gcontext is the type of the
handle. */
super (handle);
fill_style = GC_FILL_SOLID;
function = GC_COPY;
foreground = 0;
background = 0xffffffff;
}
public
EmacsGC (EmacsGC source)
{
super ((short) 0);
int i;
function = source.function;
fill_style = source.fill_style;
foreground = source.foreground;
background = source.background;
clip_x_origin = source.clip_x_origin;
clip_y_origin = source.clip_y_origin;
clip_rects = source.clip_rects;
clip_mask = source.clip_mask;
stipple = source.stipple;
ts_origin_x = source.ts_origin_x;
ts_origin_y = source.ts_origin_y;
/* Offset all the clip rects by ts_origin_x and ts_origin_y. */
if ((ts_origin_x != 0 || ts_origin_y != 0)
&& clip_rects != null)
{
clip_rects = new Rect[clip_rects.length];
for (i = 0; i < clip_rects.length; ++i)
{
clip_rects[i] = new Rect (source.clip_rects[i]);
clip_rects[i].offset (ts_origin_x,
ts_origin_y);
}
}
}
/* Mark this GC as dirty. This means immutableGC will return a new
copy of this GC the next time it is called. */
public void
markDirty ()
{
dirty = true;
}
public EmacsGC
immutableGC ()
{
if (immutableGC == null || dirty)
{
immutableGC = new EmacsGC (this);
dirty = false;
}
return immutableGC;
};
};

View file

@ -0,0 +1,62 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.util.List;
import java.util.ArrayList;
import java.lang.Object;
import java.lang.IllegalStateException;
/* This defines something that is a so-called ``handle''. Handles
must be created by C code, and will remain existing until
destroyHandle is called. C code then refers to the handle by a
number which maps into the Java object representing the handle.
All handle operations must be done from the Emacs thread. */
public abstract class EmacsHandleObject
{
/* Whether or not this handle has been destroyed. */
volatile boolean destroyed;
/* The handle associated with this object. */
public short handle;
public
EmacsHandleObject (short handle)
{
this.handle = handle;
}
public void
destroyHandle () throws IllegalStateException
{
synchronized (this)
{
destroyed = true;
}
}
public boolean
isDestroyed ()
{
return destroyed;
}
};

View file

@ -0,0 +1,72 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.System;
import android.content.res.AssetManager;
public class EmacsNative
{
/* Set certain parameters before initializing Emacs. This proves
that libemacs.so is being loaded from Java code.
assetManager must be the asset manager associated with the
context that is loading Emacs. It is saved and remains for the
remainder the lifetime of the Emacs process.
filesDir must be the package's data storage location for the
current Android user.
libDir must be the package's data storage location for native
libraries. It is used as PATH.
emacsService must be the emacsService singleton. */
public static native void setEmacsParams (AssetManager assetManager,
String filesDir,
String libDir,
EmacsService emacsService);
/* Initialize Emacs with the argument array ARGV. Each argument
must contain a NULL terminated string, or else the behavior is
undefined. */
public static native void initEmacs (String argv[]);
/* Abort and generate a native core dump. */
public static native void emacsAbort ();
/* Send an ANDROID_CONFIGURE_NOTIFY event. */
public static native void sendConfigureNotify (short window, long time,
int x, int y, int width,
int height);
/* Send an ANDROID_KEY_PRESS event. */
public static native void sendKeyPress (short window, long time, int state,
int keyCode);
/* Send an ANDROID_KEY_RELEASE event. */
public static native void sendKeyRelease (short window, long time, int state,
int keyRelease);
static
{
System.loadLibrary ("emacs");
};
};

View file

@ -0,0 +1,138 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
public class EmacsPaintQueue
{
/* Queue of paint operations. This is modified from the Emacs
thread, and entire paint queues are periodically flushed to the
application thread where it is executed. */
private List<EmacsPaintReq> paintOperations;
/* Number of operations in this queue. */
public int numRequests;
public
EmacsPaintQueue ()
{
paintOperations = new LinkedList<EmacsPaintReq> ();
}
public void
run ()
{
EmacsDrawable drawable, last;
Canvas canvas;
EmacsGC gc, lastGC;
int i;
Paint paint;
Rect rect, offsetRect, copyRect;
canvas = null;
last = null;
gc = null;
paint = new Paint ();
for (EmacsPaintReq req : paintOperations)
{
drawable = req.getDrawable ();
synchronized (req)
{
/* Ignore graphics requests for drawables that have been
destroyed. */
if (drawable.isDestroyed ())
continue;
}
canvas = drawable.lockCanvas ();
if (canvas == null)
/* No canvas is currently available. */
continue;
lastGC = gc;
gc = req.getGC ();
rect = req.getRect ();
if (gc.clip_rects == null)
{
/* No clipping is applied. Just draw and continue. */
canvas.save ();
req.paintTo (canvas, paint, gc);
canvas.restore ();
drawable.damageRect (rect);
continue;
}
if (gc.clip_rects != null && gc.clip_rects.length > 0)
{
canvas.save ();
if (gc.clip_rects.length == 1)
{
/* There is only a single clip rect, which is simple
enough. */
canvas.clipRect (gc.clip_rects[0]);
req.paintTo (canvas, paint, gc);
}
else
{
/* There are multiple clip rects. Android doesn't let
programs use RegionOp.UNION on the clip rectangle,
so Emacs must iterate over each intersection and
paint it manually. This seems inefficient but
thankfully Emacs never seems to use more than one
clip rect. */
for (i = 0; i < gc.clip_rects.length; ++i)
{
copyRect = new Rect (gc.clip_rects[i]);
if (copyRect.intersect (rect))
{
canvas.save ();
canvas.clipRect (copyRect);
req.paintTo (canvas, paint, gc);
canvas.restore ();
}
}
}
drawable.damageRect (rect);
canvas.restore ();
}
}
}
public void
appendPaintOperation (EmacsPaintReq req)
{
paintOperations.add (req);
numRequests++;
}
};

View file

@ -0,0 +1,33 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
public interface EmacsPaintReq
{
public EmacsDrawable getDrawable ();
public EmacsGC getGC ();
public void paintTo (Canvas canvas, Paint paint,
EmacsGC immutableGC);
public Rect getRect ();
};

View file

@ -0,0 +1,102 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.IllegalArgumentException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
/* Drawable backed by bitmap. */
public class EmacsPixmap extends EmacsHandleObject
implements EmacsDrawable
{
/* The depth of the bitmap. This is not actually used, just defined
in order to be consistent with X. */
public int depth, width, height;
/* The bitmap itself. */
public Bitmap bitmap;
/* The canvas used to draw to BITMAP. */
public Canvas canvas;
public
EmacsPixmap (short handle, int colors[], int width,
int height, int depth)
{
super (handle);
if (depth != 1 && depth != 24)
throw new IllegalArgumentException ("Invalid depth specified"
+ " for pixmap: " + depth);
switch (depth)
{
case 1:
bitmap = Bitmap.createBitmap (colors, width, height,
Bitmap.Config.ALPHA_8);
break;
case 24:
bitmap = Bitmap.createBitmap (colors, width, height,
Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha (false);
break;
}
this.width = width;
this.height = height;
this.depth = depth;
}
@Override
public Canvas
lockCanvas ()
{
if (canvas == null)
canvas = new Canvas (bitmap);
return canvas;
}
@Override
public void
unlockCanvas ()
{
}
@Override
public void
damageRect (Rect damageRect)
{
}
@Override
public Bitmap
getBitmap ()
{
return bitmap;
}
};

View file

@ -0,0 +1,460 @@
/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.Log;
public class EmacsSdk7FontDriver extends EmacsFontDriver
{
private static final String TOFU_STRING = "\uDB3F\uDFFD";
private static final String EM_STRING = "m";
private static final String TAG = "EmacsSdk7FontDriver";
private class Sdk7Typeface
{
/* The typeface and paint. */
public Typeface typeface;
public Paint typefacePaint;
public String familyName;
public int slant, width, weight, spacing;
public
Sdk7Typeface (String fileName, Typeface typeface)
{
String style, testString;
int index, measured, i;
float[] widths;
slant = NORMAL;
weight = REGULAR;
width = UNSPECIFIED;
spacing = PROPORTIONAL;
typefacePaint = new Paint ();
typefacePaint.setTypeface (typeface);
/* For the calls to measureText below. */
typefacePaint.setTextSize (10.0f);
/* Parse the file name into some useful data. First, strip off
the extension. */
fileName = fileName.split ("\\.", 2)[0];
/* Next, split the file name by dashes. Everything before the
last dash is part of the family name. */
index = fileName.lastIndexOf ("-");
if (index > 0)
{
style = fileName.substring (index + 1, fileName.length ());
familyName = fileName.substring (0, index);
/* Look for something describing the weight. */
if (style.contains ("Thin"))
weight = THIN;
else if (style.contains ("UltraLight"))
weight = ULTRA_LIGHT;
else if (style.contains ("SemiLight"))
weight = SEMI_LIGHT;
else if (style.contains ("Light"))
weight = LIGHT;
else if (style.contains ("Medium"))
weight = MEDIUM;
else if (style.contains ("SemiBold"))
weight = SEMI_BOLD;
else if (style.contains ("ExtraBold"))
weight = EXTRA_BOLD;
else if (style.contains ("Bold"))
weight = BOLD;
else if (style.contains ("Black"))
weight = BLACK;
else if (style.contains ("UltraHeavy"))
weight = ULTRA_HEAVY;
/* And the slant. */
if (style.contains ("ReverseOblique"))
slant = OBLIQUE;
else if (style.contains ("ReverseItalic"))
slant = REVERSE_ITALIC;
else if (style.contains ("Italic"))
slant = ITALIC;
else if (style.contains ("Oblique"))
slant = OBLIQUE;
/* Finally, the width. */
if (style.contains ("UltraCondensed"))
width = ULTRA_CONDENSED;
else if (style.contains ("ExtraCondensed"))
width = EXTRA_CONDENSED;
else if (style.contains ("SemiCondensed"))
width = SEMI_CONDENSED;
else if (style.contains ("Condensed"))
width = CONDENSED;
else if (style.contains ("SemiExpanded"))
width = SEMI_EXPANDED;
else if (style.contains ("ExtraExpanded"))
width = EXTRA_EXPANDED;
else if (style.contains ("UltraExpanded"))
width = ULTRA_EXPANDED;
else if (style.contains ("Expanded"))
width = EXPANDED;
/* Guess the spacing information. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
widths = new float[testString.length ()];
measured = typefacePaint.getTextWidths (testString,
0, testString.length (),
widths);
spacing = MONO;
for (i = 0; i < measured; ++i)
{
if (i != 0 && widths[i - 1] != widths[i])
/* This isn't a monospace font. */
spacing = PROPORTIONAL;
}
}
else
familyName = fileName;
Log.d (TAG, "Initialized new typeface " + familyName);
}
@Override
public String
toString ()
{
return ("Sdk7Typeface ("
+ String.valueOf (familyName) + ", "
+ String.valueOf (slant) + ", "
+ String.valueOf (width) + ", "
+ String.valueOf (weight) + ", "
+ String.valueOf (spacing) + ")");
}
};
private class Sdk7FontEntity extends FontEntity
{
/* The typeface. */
public Sdk7Typeface typeface;
public
Sdk7FontEntity (Sdk7Typeface typeface)
{
float width;
foundry = "Google";
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
this.typeface = typeface;
}
};
private class Sdk7FontObject extends FontObject
{
/* The typeface. */
public Sdk7Typeface typeface;
/* The text size. */
public int pixelSize;
public
Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
{
float totalWidth;
String testWidth, testString;
this.typeface = typeface;
this.pixelSize = pixelSize;
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
/* Compute the ascent and descent. */
typeface.typefacePaint.setTextSize (pixelSize);
ascent
= Math.round (-typeface.typefacePaint.ascent ());
descent
= Math.round (typeface.typefacePaint.descent ());
/* Compute the average width. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
totalWidth = typeface.typefacePaint.measureText (testString);
if (totalWidth > 0)
avgwidth = Math.round (totalWidth
/ testString.length ());
/* Android doesn't expose the font average width and height
information, so this will have to do. */
minWidth = maxWidth = avgwidth;
/* This is different from avgwidth in the font spec! */
averageWidth = avgwidth;
/* Set the space width. */
totalWidth = typeface.typefacePaint.measureText (" ");
spaceWidth = Math.round (totalWidth);
/* Set the height and default ascent. */
height = ascent + descent;
defaultAscent = ascent;
}
};
private String[] fontFamilyList;
private Sdk7Typeface[] typefaceList;
private Sdk7Typeface fallbackTypeface;
public
EmacsSdk7FontDriver ()
{
int i;
File systemFontsDirectory, fontFile;
Typeface typeface;
systemFontsDirectory = new File ("/system/fonts");
fontFamilyList = systemFontsDirectory.list ();
typefaceList = new Sdk7Typeface[fontFamilyList.length];
/* It would be nice to avoid opening each and every font upon
startup. But that doesn't seem to be possible on
Android. */
for (i = 0; i < fontFamilyList.length; ++i)
{
fontFile = new File (systemFontsDirectory,
fontFamilyList[i]);
typeface = Typeface.createFromFile (fontFile);
typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
typeface);
}
fallbackTypeface = new Sdk7Typeface ("monospace",
Typeface.MONOSPACE);
}
private boolean
checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
{
if (fontSpec.family != null
&& !fontSpec.family.equals (typeface.familyName))
return false;
if (fontSpec.adstyle != null
&& !fontSpec.adstyle.isEmpty ())
/* return false; */;
if (fontSpec.slant != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.spacing != null
&& !fontSpec.spacing.equals (typeface.spacing))
return false;
if (fontSpec.weight != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.width != null
&& !fontSpec.width.equals (typeface.width))
return false;
return true;
}
@Override
public FontEntity[]
list (FontSpec fontSpec)
{
LinkedList<FontEntity> list;
int i;
list = new LinkedList<FontEntity> ();
Log.d (TAG, ("Looking for fonts matching font spec: "
+ fontSpec.toString ()));
for (i = 0; i < typefaceList.length; ++i)
{
if (checkMatch (typefaceList[i], fontSpec))
list.add (new Sdk7FontEntity (typefaceList[i]));
}
Log.d (TAG, "Found font entities: " + list.toString ());
return (FontEntity[]) list.toArray (new FontEntity[0]);
}
@Override
public FontEntity
match (FontSpec fontSpec)
{
FontEntity[] entities;
int i;
entities = this.list (fontSpec);
if (entities.length == 0)
return new Sdk7FontEntity (fallbackTypeface);
return entities[0];
}
@Override
public String[]
listFamilies ()
{
return fontFamilyList;
}
@Override
public FontObject
openFont (FontEntity fontEntity, int pixelSize)
{
return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
pixelSize);
}
@Override
public int
hasChar (FontSpec font, char charCode)
{
float missingGlyphWidth, emGlyphWidth, width;
Rect rect1, rect2;
Paint paint;
Sdk7FontObject fontObject;
if (font instanceof Sdk7FontObject)
{
fontObject = (Sdk7FontObject) font;
paint = fontObject.typeface.typefacePaint;
}
else
paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
paint.setTextSize (10);
if (Character.isWhitespace (charCode))
return 1;
missingGlyphWidth = paint.measureText (TOFU_STRING);
emGlyphWidth = paint.measureText (EM_STRING);
width = paint.measureText ("" + charCode);
if (width == 0f)
return 0;
if (width != missingGlyphWidth)
return 1;
rect1 = new Rect ();
rect2 = new Rect ();
paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
rect1);
paint.getTextBounds ("" + charCode, 0, 1, rect2);
return rect1.equals (rect2) ? 1 : 0;
}
private void
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
Paint paint, Rect bounds)
{
char[] text;
float[] width;
text = new char[1];
text[0] = (char) code;
paint.getTextBounds (text, 0, 1, bounds);
width = new float[1];
paint.getTextWidths (text, 0, 1, width);
/* bounds is the bounding box of the glyph corresponding to CODE.
Translate these into XCharStruct values.
The origin is at 0, 0, and lbearing is the distance counting
rightwards from the origin to the left most pixel in the glyph
raster. rbearing is the distance between the origin and the
rightmost pixel in the glyph raster. ascent is the distance
counting upwards between the the topmost pixel in the glyph
raster. descent is the distance (once again counting
downwards) between the origin and the bottommost pixel in the
glyph raster.
width is the distance between the origin and the origin of any
character to the right. */
metrics.lbearing = (short) bounds.left;
metrics.rbearing = (short) bounds.right;
metrics.ascent = (short) -bounds.top;
metrics.descent = (short) bounds.bottom;
metrics.width = (short) Math.round (width[0]);
}
@Override
public void
textExtents (FontObject font, int code[], FontMetrics fontMetrics[])
{
int i;
Paint paintCache;
Rect boundsCache;
Sdk7FontObject fontObject;
fontObject = (Sdk7FontObject) font;
paintCache = fontObject.typeface.typefacePaint;
paintCache.setTextSize (fontObject.pixelSize);
boundsCache = new Rect ();
for (i = 0; i < code.length; ++i)
textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i],
paintCache, boundsCache);
}
@Override
public int
encodeChar (FontObject fontObject, char charCode)
{
return charCode;
}
};

View file

@ -0,0 +1,384 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.Runnable;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Looper;
import android.os.IBinder;
import android.os.Handler;
import android.util.Log;
class Holder<T>
{
T thing;
};
/* EmacsService is the service that starts the thread running Emacs
and handles requests by that Emacs instance. */
public class EmacsService extends Service
{
public static final String TAG = "EmacsService";
public static final int MAX_PENDING_REQUESTS = 256;
public static volatile EmacsService SERVICE;
private EmacsThread thread;
private Handler handler;
private EmacsPaintQueue paintQueue;
/* List of all EmacsWindows that are available to attach to an
activity. */
public static List<EmacsWindow> availableChildren;
static
{
availableChildren = new ArrayList<EmacsWindow> ();
};
@Override
public int
onStartCommand (Intent intent, int flags, int startId)
{
return START_NOT_STICKY;
}
@Override
public IBinder
onBind (Intent intent)
{
return null;
}
@TargetApi (Build.VERSION_CODES.GINGERBREAD)
String
getLibraryDirectory ()
{
int apiLevel;
Context context;
context = getApplicationContext ();
apiLevel = android.os.Build.VERSION.SDK_INT;
if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
return context.getApplicationInfo().nativeLibraryDir;
else if (apiLevel >= Build.VERSION_CODES.DONUT)
return context.getApplicationInfo().dataDir + "/lib";
return "/data/data/" + context.getPackageName() + "/lib";
}
@Override
public void
onCreate ()
{
AssetManager manager;
Context app_context;
String filesDir, libDir;
SERVICE = this;
handler = new Handler (Looper.getMainLooper ());
manager = getAssets ();
app_context = getApplicationContext ();
try
{
/* Configure Emacs with the asset manager and other necessary
parameters. */
filesDir = app_context.getFilesDir ().getCanonicalPath ();
libDir = getLibraryDirectory ();
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+ " and libDir = " + libDir);
EmacsNative.setEmacsParams (manager, filesDir, libDir,
this);
/* Start the thread that runs Emacs. */
thread = new EmacsThread (this);
thread.start ();
}
catch (IOException exception)
{
EmacsNative.emacsAbort ();
return;
}
}
/* Functions from here on must only be called from the Emacs
thread. */
void
runOnUiThread (Runnable runnable)
{
handler.post (runnable);
}
EmacsView
getEmacsView (final EmacsWindow window)
{
Runnable runnable;
final Holder<EmacsView> view;
view = new Holder<EmacsView> ();
runnable = new Runnable () {
public void
run ()
{
synchronized (this)
{
view.thing = new EmacsView (window);
notify ();
}
}
};
synchronized (runnable)
{
runOnUiThread (runnable);
try
{
runnable.wait ();
}
catch (InterruptedException e)
{
EmacsNative.emacsAbort ();
}
}
return view.thing;
}
/* Notice that a child of the root window named WINDOW is now
available for attachment to a specific activity. */
public void
noticeAvailableChild (final EmacsWindow window)
{
Log.d (TAG, "A new child is available: " + window);
handler.post (new Runnable () {
public void
run ()
{
for (EmacsActivity activity
: EmacsActivity.availableActivities)
{
/* TODO: check if the activity matches. */
activity.attachChild (window);
break;
}
/* Nope, wait for an activity to become available. */
availableChildren.add (window);
}
});
}
/* Notice that a child of the root window named WINDOW has been
destroyed. */
public void
noticeChildDestroyed (final EmacsWindow child)
{
handler.post (new Runnable () {
@Override
public void
run ()
{
availableChildren.remove (child);
}
});
}
/* X drawing operations. These are quite primitive operations. The
drawing queue is kept on the Emacs thread, but is periodically
flushed to the application thread, upon buffers swaps and once it
gets too big. */
private void
ensurePaintQueue ()
{
if (paintQueue == null)
paintQueue = new EmacsPaintQueue ();
}
public void
flushPaintQueue ()
{
final EmacsPaintQueue queue;
if (paintQueue == null)
return;
if (paintQueue.numRequests < 1)
/* No requests to flush. */
return;
queue = paintQueue;
handler.post (new Runnable () {
@Override
public void
run ()
{
queue.run ();
}
});
/* Clear the paint queue. */
paintQueue = null;
}
private void
checkFlush ()
{
if (paintQueue != null
&& paintQueue.numRequests > MAX_PENDING_REQUESTS)
flushPaintQueue ();
}
public void
fillRectangle (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height)
{
EmacsPaintReq req;
ensurePaintQueue ();
req = new EmacsFillRectangle (drawable, x, y,
width, height,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
fillPolygon (EmacsDrawable drawable, EmacsGC gc,
Point points[])
{
EmacsPaintReq req;
ensurePaintQueue ();
req = new EmacsFillPolygon (drawable, points,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
drawRectangle (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height)
{
EmacsPaintReq req;
ensurePaintQueue ();
if (gc.clip_rects != null && gc.clip_rects.length >= 1)
android.util.Log.d ("drawRectangle",
gc.clip_rects[0].toString ()
+ " " + gc.toString ());
req = new EmacsDrawRectangle (drawable, x, y,
width, height,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
drawLine (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int x2, int y2)
{
EmacsPaintReq req;
ensurePaintQueue ();
req = new EmacsDrawLine (drawable, x, y,
x2, y2,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
drawPoint (EmacsDrawable drawable, EmacsGC gc,
int x, int y)
{
EmacsPaintReq req;
ensurePaintQueue ();
req = new EmacsDrawPoint (drawable, x, y,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable,
EmacsGC gc,
int srcX, int srcY, int width, int height, int destX,
int destY)
{
EmacsPaintReq req;
ensurePaintQueue ();
req = new EmacsCopyArea (srcDrawable, dstDrawable,
srcX, srcY, width, height, destX,
destY, gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
}
public void
clearWindow (EmacsWindow window)
{
window.clearWindow ();
}
public void
clearArea (EmacsWindow window, int x, int y, int width,
int height)
{
window.clearArea (x, y, width, height);
}
};

View file

@ -0,0 +1,83 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.graphics.Canvas;
import android.graphics.Rect;
public class EmacsSurfaceView extends SurfaceView
{
private boolean created;
public
EmacsSurfaceView (final EmacsView view)
{
super (view.getContext ());
getHolder ().addCallback (new SurfaceHolder.Callback () {
@Override
public void
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
}
@Override
public void
surfaceCreated (SurfaceHolder holder)
{
created = true;
/* Force a buffer swap now to get the contents of the Emacs
view on screen. */
view.swapBuffers ();
}
@Override
public void
surfaceDestroyed (SurfaceHolder holder)
{
created = false;
}
});
}
public boolean
isCreated ()
{
return created;
}
public Canvas
lockCanvas (Rect damage)
{
return getHolder ().lockCanvas (damage);
}
public void
unlockCanvasAndPost (Canvas canvas)
{
getHolder ().unlockCanvasAndPost (canvas);
}
};

View file

@ -0,0 +1,44 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.Thread;
public class EmacsThread extends Thread
{
EmacsService context;
public
EmacsThread (EmacsService service)
{
context = service;
}
public void
run ()
{
String args[];
args = new String[] { "android-emacs", };
/* Run the native code now. */
EmacsNative.initEmacs (args);
}
};

View file

@ -0,0 +1,211 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import android.view.View;
import android.view.KeyEvent;
import android.view.ViewGroup;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Paint;
import android.util.Log;
import android.os.Build;
/* This is an Android view which has a back and front buffer. When
swapBuffers is called, the back buffer is swapped to the front
buffer, and any damage is invalidated. frontBitmap and backBitmap
are modified and used both from the UI and the Emacs thread. As a
result, there is a lock held during all drawing operations.
It is also a ViewGroup, as it also lays out children. */
public class EmacsView extends ViewGroup
{
public static final String TAG = "EmacsView";
/* The associated EmacsWindow. */
public EmacsWindow window;
/* The buffer bitmap. */
public Bitmap bitmap;
/* The associated canvases. */
public Canvas canvas;
/* The damage region. */
public Region damageRegion;
/* The paint. */
public Paint paint;
/* The associated surface view. */
private EmacsSurfaceView surfaceView;
public
EmacsView (EmacsWindow window)
{
super (EmacsService.SERVICE);
this.window = window;
this.damageRegion = new Region ();
this.paint = new Paint ();
/* Create the surface view. */
this.surfaceView = new EmacsSurfaceView (this);
setFocusable (FOCUSABLE);
addView (this.surfaceView);
}
@Override
protected void
onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
Rect measurements;
int width, height;
/* Return the width and height of the window regardless of what
the parent says. */
measurements = window.getGeometry ();
width = measurements.width ();
height = measurements.height ();
/* Now apply any extra requirements in widthMeasureSpec and
heightMeasureSpec. */
if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY)
width = MeasureSpec.getSize (widthMeasureSpec);
else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST
&& width > MeasureSpec.getSize (widthMeasureSpec))
width = MeasureSpec.getSize (widthMeasureSpec);
if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY)
height = MeasureSpec.getSize (heightMeasureSpec);
else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
&& height > MeasureSpec.getSize (heightMeasureSpec))
height = MeasureSpec.getSize (heightMeasureSpec);
super.setMeasuredDimension (width, height);
}
@Override
protected void
onLayout (boolean changed, int left, int top, int right,
int bottom)
{
int count, i;
View child;
Rect windowRect;
if (changed)
{
window.viewLayout (left, top, right, bottom);
/* Recreate the front and back buffer bitmaps. */
bitmap
= Bitmap.createBitmap (right - left, bottom - top,
Bitmap.Config.ARGB_8888);
/* And canvases. */
canvas = new Canvas (bitmap);
}
count = getChildCount ();
for (i = 0; i < count; ++i)
{
child = getChildAt (i);
if (child == surfaceView)
/* The child is the surface view, so give it the entire
view. */
child.layout (left, top, right, bottom);
else if (child.getVisibility () != GONE)
{
if (!(child instanceof EmacsView))
continue;
/* What to do: lay out the view precisely according to its
window rect. */
windowRect = ((EmacsView) child).window.getGeometry ();
child.layout (windowRect.left, windowRect.top,
windowRect.right, windowRect.bottom);
}
}
}
public void
damageRect (Rect damageRect)
{
damageRegion.union (damageRect);
}
public void
swapBuffers ()
{
Bitmap back;
Canvas canvas;
Rect damageRect;
if (damageRegion.isEmpty ())
return;
if (!surfaceView.isCreated ())
return;
if (bitmap == null)
return;
/* Lock the canvas with the specified damage. */
damageRect = damageRegion.getBounds ();
canvas = surfaceView.lockCanvas (damageRect);
/* Return if locking the canvas failed. */
if (canvas == null)
return;
/* Copy from the back buffer to the canvas. */
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
/* Unlock the canvas and clear the damage. */
surfaceView.unlockCanvasAndPost (canvas);
damageRegion.setEmpty ();
}
@Override
public boolean
onKeyDown (int keyCode, KeyEvent event)
{
window.onKeyDown (keyCode, event);
return true;
}
@Override
public boolean
onKeyUp (int keyCode, KeyEvent event)
{
window.onKeyUp (keyCode, event);
return true;
}
};

View file

@ -0,0 +1,336 @@
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
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/>. */
package org.gnu.emacs;
import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Rect;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.view.View;
import android.view.ViewGroup;
import android.view.KeyEvent;
/* This defines a window, which is a handle. Windows represent a
rectangular subset of the screen with their own contents.
Windows either have a parent window, in which case their views are
attached to the parent's view, or are "floating", in which case
their views are attached to the parent activity (if any), else
nothing.
Views are also drawables, meaning they can accept drawing
requests. */
public class EmacsWindow extends EmacsHandleObject
implements EmacsDrawable
{
/* The view associated with the window. */
public EmacsView view;
/* The geometry of the window. */
private Rect rect;
/* The parent window, or null if it is the root window. */
private EmacsWindow parent;
/* List of all children in stacking order. This must be kept
consistent! */
private ArrayList<EmacsWindow> children;
/* The EmacsActivity currently attached, if it exists. */
private EmacsActivity attached;
/* The window background scratch GC. foreground is always the
window background. */
private EmacsGC scratchGC;
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height)
{
super (handle);
rect = new Rect (x, y, x + width, y + height);
/* Create the view from the context's UI thread. */
view = EmacsService.SERVICE.getEmacsView (this);
this.parent = parent;
children = new ArrayList<EmacsWindow> ();
/* The window is unmapped by default. */
view.setVisibility (View.GONE);
/* If parent is the root window, notice that there are new
children available for interested activites to pick up. */
if (parent == null)
EmacsService.SERVICE.noticeAvailableChild (this);
else
{
/* Otherwise, directly add this window as a child of that
window's view. */
synchronized (parent)
{
parent.children.add (this);
parent.view.post (new Runnable () {
@Override
public void
run ()
{
parent.view.addView (view);
}
});
}
}
scratchGC = new EmacsGC ((short) 0);
}
public void
changeWindowBackground (int pixel)
{
/* scratchGC is used as the argument to a FillRectangles req. */
scratchGC.foreground = pixel;
scratchGC.markDirty ();
}
public Rect
getGeometry ()
{
synchronized (this)
{
/* Huh, this is it. */
return rect;
}
}
@Override
public void
destroyHandle () throws IllegalStateException
{
synchronized (this)
{
if (!children.isEmpty ())
throw new IllegalStateException ("Trying to destroy window with "
+ "children!");
}
/* Notice that the child has been destroyed. */
EmacsService.SERVICE.noticeChildDestroyed (this);
/* Remove the view from its parent and make it invisible. */
view.post (new Runnable () {
public void
run ()
{
view.setVisibility (View.GONE);
if (view.getParent () != null)
((ViewGroup) view.getParent ()).removeView (view);
if (attached != null)
attached.makeAvailable ();
}
});
super.destroyHandle ();
}
public void
setActivity (EmacsActivity activity)
{
synchronized (this)
{
activity = activity;
}
}
public void
viewLayout (int left, int top, int right, int bottom)
{
synchronized (this)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
left, top, rect.width (),
rect.height ());
}
}
public void
requestViewLayout ()
{
view.post (new Runnable () {
@Override
public void
run ()
{
view.requestLayout ();
}
});
}
public void
resizeWindow (int width, int height)
{
synchronized (this)
{
rect.right = rect.left + width;
rect.bottom = rect.top + height;
}
}
public void
moveWindow (int x, int y)
{
int width, height;
synchronized (this)
{
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
requestViewLayout ();
}
}
public void
mapWindow ()
{
view.post (new Runnable () {
@Override
public void
run ()
{
view.setVisibility (View.VISIBLE);
}
});
}
public void
unmapWindow ()
{
view.post (new Runnable () {
@Override
public void
run ()
{
view.setVisibility (View.GONE);
}
});
}
@Override
public Canvas
lockCanvas ()
{
if (view.canvas != null)
return view.canvas;
return null;
}
@Override
public void
unlockCanvas ()
{
}
@Override
public void
damageRect (Rect damageRect)
{
view.damageRect (damageRect);
}
public void
swapBuffers ()
{
/* Before calling swapBuffers, make sure to flush the paint
queue. */
EmacsService.SERVICE.flushPaintQueue ();
view.post (new Runnable () {
@Override
public void
run ()
{
view.swapBuffers ();
}
});
}
public void
clearWindow ()
{
synchronized (this)
{
EmacsService.SERVICE.fillRectangle (this, scratchGC,
0, 0, rect.width (),
rect.height ());
}
}
public void
clearArea (int x, int y, int width, int height)
{
EmacsService.SERVICE.fillRectangle (this, scratchGC,
x, y, width, height);
}
@Override
public Bitmap
getBitmap ()
{
return view.bitmap;
}
public void
onKeyDown (int keyCode, KeyEvent event)
{
EmacsNative.sendKeyPress (this.handle,
event.getEventTime (),
event.getModifiers (),
keyCode);
}
public void
onKeyUp (int keyCode, KeyEvent event)
{
EmacsNative.sendKeyRelease (this.handle,
event.getEventTime (),
event.getModifiers (),
keyCode);
}
};

View file

@ -96,6 +96,14 @@ localstatedir=@localstatedir@
srcdir=@srcdir@
VPATH=@srcdir@
# Cross-compilation setup
XCONFIGURE=@XCONFIGURE@
ifneq ($(XCONFIGURE),)
vpath $(srcdir)
endif
# The top-level source directory, also set by configure.
top_srcdir=@top_srcdir@
# MinGW CPPFLAGS may use this.

View file

@ -20,6 +20,130 @@
srcdir = @srcdir@
VPATH = @srcdir@
# This is not empty if this is a Makefile that will be copied to
# xcompile/lib.
XCONFIGURE = @XCONFIGURE@
# This is required to make sure symbol visibility is correct and
# functions like readlinkat do not end up replacing their OS
# counterparts.
ANDROID_CFLAGS = @ANDROID_CFLAGS@
ifneq ($(XCONFIGURE),)
# Set vpath. Only look for C files and some headers in srcdir:
# Headers that are generated by gnulib must be spared, or otherwise
# the versions previously built on the host will be used, if builddir
# is the same as srcdir. Following this is a list of files in lib/
# that are not generated during the gnulib build process. Please keep
# it up to date!
vpath _Noreturn.h $(srcdir)
vpath acl-internal.h $(srcdir)
vpath acl.h $(srcdir)
vpath af_alg.h $(srcdir)
vpath alloca.in.h $(srcdir)
vpath allocator.h $(srcdir)
vpath arg-nonnull.h $(srcdir)
vpath assert.in.h $(srcdir)
vpath attribute.h $(srcdir)
vpath binary-io.h $(srcdir)
vpath byteswap.in.h $(srcdir)
vpath c++defs.h $(srcdir)
vpath c-ctype.h $(srcdir)
vpath c-strcase.h $(srcdir)
vpath careadlinkat.h $(srcdir)
vpath cdefs.h $(srcdir)
vpath cloexec.h $(srcdir)
vpath close-stream.h $(srcdir)
vpath count-leading-zeros.h $(srcdir)
vpath count-one-bits.h $(srcdir)
vpath count-trailing-zeros.h $(srcdir)
vpath diffseq.h $(srcdir)
vpath dirent.h $(srcdir)
vpath dirent.in.h $(srcdir)
vpath dynarray.h $(srcdir)
vpath eloop-threshold.h $(srcdir)
vpath errno.in.h $(srcdir)
vpath execinfo.in.h $(srcdir)
vpath fcntl.in.h $(srcdir)
vpath filemode.h $(srcdir)
vpath filename.h $(srcdir)
vpath filevercmp.h $(srcdir)
vpath fingerprint.h $(srcdir)
vpath flexmember.h $(srcdir)
vpath fpending.h $(srcdir)
vpath fsusage.h $(srcdir)
vpath ftoastr.h $(srcdir)
vpath getopt-cdefs.in.h $(srcdir)
vpath getopt-core.h $(srcdir)
vpath getopt-ext.h $(srcdir)
vpath getopt-pfx-core.h $(srcdir)
vpath getopt-pfx-ext.h $(srcdir)
vpath getopt.in.h $(srcdir)
vpath getopt_int.h $(srcdir)
vpath gettext.h $(srcdir)
vpath idx.h $(srcdir)
vpath ieee754.in.h $(srcdir)
vpath ignore-value.h $(srcdir)
vpath intprops-internal.h $(srcdir)
vpath intprops.h $(srcdir)
vpath inttypes.in.h $(srcdir)
vpath libc-config.h $(srcdir)
vpath limits.in.h $(srcdir)
vpath malloc/dynarray.h $(srcdir)
vpath malloc/scratch_buffer.h $(srcdir)
vpath md5.h $(srcdir)
vpath min-max.h $(srcdir)
vpath mini-gmp.h $(srcdir)
vpath minmax.h $(srcdir)
vpath mktime-internal.h $(srcdir)
vpath nproc.h $(srcdir)
vpath openat-priv.h $(srcdir)
vpath openat.h $(srcdir)
vpath pathmax.h $(srcdir)
vpath regex.h $(srcdir)
vpath regex_internal.h $(srcdir)
vpath root-uid.h $(srcdir)
vpath save-cwd.h $(srcdir)
vpath scratch_buffer.h $(srcdir)
vpath sha1.h $(srcdir)
vpath sha256.h $(srcdir)
vpath sha512.h $(srcdir)
vpath sig2str.h $(srcdir)
vpath signal.in.h $(srcdir)
vpath stat-time.h $(srcdir)
vpath stdalign.in.h $(srcdir)
vpath stdckdint.in.h $(srcdir)
vpath stddef.in.h $(srcdir)
vpath stdint.in.h $(srcdir)
vpath stdio-impl.h $(srcdir)
vpath stdio.h $(srcdir)
vpath stdio.in.h $(srcdir)
vpath stdlib.in.h $(srcdir)
vpath str-two-way.h $(srcdir)
vpath strftime.h $(srcdir)
vpath string.in.h $(srcdir)
vpath sys_random.in.h $(srcdir)
vpath sys_select.in.h $(srcdir)
vpath sys_stat.in.h $(srcdir)
vpath sys_time.in.h $(srcdir)
vpath sys_types.in.h $(srcdir)
vpath tempname.h $(srcdir)
vpath time-internal.h $(srcdir)
vpath time.in.h $(srcdir)
vpath timespec.h $(srcdir)
vpath u64.h $(srcdir)
vpath unistd.in.h $(srcdir)
vpath unlocked-io.h $(srcdir)
vpath utimens.h $(srcdir)
vpath verify.h $(srcdir)
vpath vla.h $(srcdir)
vpath warn-on-use.h $(srcdir)
vpath xalloc-oversized.h $(srcdir)
vpath %.c $(srcdir)
endif
# Variables substituted by 'configure', and not autogenerated in gnulib.mk,
# or needed before gnulib.mk is included.
abs_top_srcdir = @abs_top_srcdir@
@ -33,11 +157,11 @@ all:
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
ALL_CFLAGS= \
ALL_CFLAGS = \
$(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) $(DEPFLAGS) \
$(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) $(PROFILING_CFLAGS) $(CFLAGS) \
-I. -I../src -I$(srcdir) -I$(srcdir)/../src \
$(if $(patsubst e-%,,$(notdir $<)),,-Demacs)
$(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_CFLAGS)
ifeq ($(HAVE_NATIVE_COMP),yes)
ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM
@ -52,6 +176,12 @@ ifneq ($(SYSTEM_TYPE),windows-nt)
libgnu_a_SOURCES += openat-die.c save-cwd.c
endif
ifeq ($(XCONFIGURE),android)
# The next line is necessary to override -I$(srcdir), which will end
# up pulling in lots of headers from the host.
ALL_CFLAGS += -I$(top_srcdir)/xcompile -I.
endif
DEPDIR = deps
ifeq ($(AUTO_DEPEND),yes)
DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
@ -60,11 +190,14 @@ else
DEPFLAGS =
endif
# This piece of code interferes with cross compilation
ifeq ($(XCONFIGURE),)
.PRECIOUS: ../config.status Makefile
../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
$(MAKE) -C .. $(notdir $@)
Makefile: ../config.status $(srcdir)/Makefile.in
$(MAKE) -C .. lib/$@
endif
# Object modules that need not be built for Emacs.
# Emacs does not need e-regex.o (it has its own regex-emacs.c),

View file

@ -43,7 +43,11 @@ orig_faccessat (int fd, char const *name, int mode, int flag)
/* Write "unistd.h" here, not <unistd.h>, otherwise OSF/1 5.1 DTK cc
eliminates this include because of the preliminary #include <unistd.h>
above. */
#ifdef __ANROID__
#include <unistd.h>
#else
#include "unistd.h"
#endif
#ifndef HAVE_ACCESS
/* Mingw lacks access, but it also lacks real vs. effective ids, so

View file

@ -33,13 +33,15 @@
size_t
__fpending (FILE *fp)
{
#if defined __ANDROID__
return 0;
/* Most systems provide FILE as a struct and the necessary bitmask in
<stdio.h>, because they need it for implementing getc() and putc() as
fast macros. */
#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
#elif defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
/* GNU libc, BeOS, Haiku, Linux libc5 */
return fp->_IO_write_ptr - fp->_IO_write_base;
#elif defined __sferror || defined __DragonFly__ || defined __ANDROID__
#elif defined __sferror || defined __DragonFly__
/* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin < 1.7.34, Minix 3, Android */
return fp->_p - fp->_bf._base;
#elif defined __EMX__ /* emx+gcc */

View file

@ -40,7 +40,11 @@ orig_open (const char *filename, int flags, mode_t mode)
/* Specification. */
/* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
this include because of the preliminary #include <fcntl.h> above. */
#ifdef __ANDROID__
#include <fnctl.h>
#else
#include "fcntl.h"
#endif
#include "cloexec.h"

View file

@ -18,5 +18,5 @@
#include <config.h>
#define _GL_UNISTD_INLINE _GL_EXTERN_INLINE
#include "unistd.h"
#include <unistd.h>
typedef int dummy;

View file

@ -2137,7 +2137,8 @@ frames and several different fonts at once. This is true for displays
that use a window system such as X, and false for text-only terminals.
DISPLAY can be a display name, a frame, or nil (meaning the selected
frame's display)."
(not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku)))))
(not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku
android)))))
(defun display-images-p (&optional display)
"Return non-nil if DISPLAY can display images.
@ -2202,7 +2203,7 @@ DISPLAY should be either a frame or a display name (a string).
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-screens display))
(t
1))))
@ -2222,7 +2223,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-pixel-height display))
(t
(frame-height (if (framep display) display (selected-frame)))))))
@ -2242,7 +2243,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-pixel-width display))
(t
(frame-width (if (framep display) display (selected-frame)))))))
@ -2280,7 +2281,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the height in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
(and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
(and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
(or (cddr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cddr (assoc t display-mm-dimensions-alist))
@ -2301,7 +2302,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the width in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
(and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
(and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
(or (cadr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cadr (assoc t display-mm-dimensions-alist))
@ -2319,7 +2320,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-backing-store display))
(t
'not-useful))))
@ -2332,7 +2333,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-save-under display))
(t
'not-useful))))
@ -2345,7 +2346,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-planes display))
((eq frame-type 'pc)
4)
@ -2360,7 +2361,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-color-cells display))
((eq frame-type 'pc)
16)
@ -2377,7 +2378,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
((memq frame-type '(x w32 ns haiku pgtk))
((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-visual-class display))
((and (memq frame-type '(pc t))
(tty-display-color-p display))

View file

@ -432,6 +432,8 @@ See also `wallpaper-default-width'.")
;;; wallpaper-set
(declare-function x-open-connection "xfns.c")
(defun wallpaper--x-monitor-name ()
"Get the monitor name for `wallpaper-set'.
On a graphical display, try using the same monitor as the current

View file

@ -306,6 +306,11 @@
(load "term/common-win")
(load "term/haiku-win")))
(if (featurep 'android)
(progn
(load "term/common-win")
(load "term/android-win")))
(if (or (eq system-type 'windows-nt)
(featurep 'w32))
(progn

View file

@ -240,7 +240,7 @@ parameter, and should return the (possibly) transformed URL."
:version "29.1")
(defface eww-form-submit
'((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
'((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@ -248,7 +248,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-file
'((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
'((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@ -256,7 +256,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-checkbox
'((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
'((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."
@ -264,7 +264,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-select
'((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
'((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."

62
lisp/term/android-win.el Normal file
View file

@ -0,0 +1,62 @@
;;; x-win.el --- parse relevant switches and set up for Android -*- lexical-binding:t -*-
;; Copyright (C) 2023 Free Software Foundation, Inc.
;; Author: FSF
;; Keywords: terminals, i18n, android
;; 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/>.
;;; Commentary:
;; This file contains the support for initializing the Lisp side of
;; Android windowing.
;;; Code:
(unless (featurep 'android)
(error "%s: Loading android-win without having Android"
invocation-name))
;; Documentation-purposes only: actually loaded in loadup.el.
(require 'frame)
(require 'mouse)
(require 'fontset)
(require 'dnd)
(add-to-list 'display-format-alist '(".*" . android))
;; Window system initialization. This is extremely simple because all
;; initialization is done in android_term_init.
(cl-defmethod window-system-initialization (&context (window-system android)
&optional _ignored)
"Set up the window system. WINDOW-SYSTEM must be ANDROID.
DISPLAY is ignored on Android."
;; Just make sure the window system was initialized at startup.
(android-get-connection))
(cl-defmethod frame-creation-function (params &context (window-system android))
(x-create-frame-with-faces params))
(cl-defmethod handle-args-function (_ignored &context (window-system android))
;; Nothing to do here: Android has no command line to provide
;; arguments on.
(ignore))
(provide 'android-win)
;; android-win.el ends here.

View file

@ -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
# xcompile/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
@ -326,7 +337,7 @@ 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@
@ -370,6 +381,10 @@ HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
HAIKU_LIBS = @HAIKU_LIBS@
HAIKU_CFLAGS = @HAIKU_CFLAGS@
ANDROID_OBJ = @ANDROID_OBJ@
ANDROID_LIBS = @ANDROID_LIBS@
ANDROID_CFLAGS = @ANDROID_CFLAGS@
DUMPING=@DUMPING@
CHECK_STRUCTS = @CHECK_STRUCTS@
HAVE_PDUMPER = @HAVE_PDUMPER@
@ -411,7 +426,8 @@ 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_CFLAGS)
ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
$(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@ -449,7 +465,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)
@ -466,7 +482,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@
@ -569,7 +586,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,
@ -658,7 +676,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
@ -719,6 +737,27 @@ 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.
libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
$(MAKE_PDUMPER_FINGERPRINT)
$(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_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.
android-emacs: libemacs.so android-emacs.o
$(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(LDFLAGS) \
-L. "-l:libemacs.so" android-emacs.o
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
@ -747,6 +786,7 @@ ns-app: emacs$(EXEEXT) $(pdmp)
.PHONY: versionclean
mostlyclean:
rm -f aemacs emacs.so
rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o
rm -f dmpstruct.h
rm -f emacs.pdmp

View file

@ -3342,6 +3342,14 @@ 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)
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));
@ -6467,6 +6475,10 @@ garbage_collect (void)
mark_xselect ();
#endif
#ifdef HAVE_ANDROID
mark_androidterm ();
#endif
#ifdef HAVE_NS
mark_nsterm ();
#endif

30
src/android-emacs.c Normal file
View file

@ -0,0 +1,30 @@
/* 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 "android.h"
/* android-emacs is a wrapper around libemacs. It simply calls
android_emacs_init with the argv and argc given to it. */
int
main (int argc, char **argv)
{
return android_emacs_init (argc, argv);
}

2335
src/android.c Normal file

File diff suppressed because it is too large Load diff

70
src/android.h Normal file
View file

@ -0,0 +1,70 @@
/* 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>
#endif
/* This must be used in every symbol declaration to export it to the
JNI Emacs wrapper. */
#define ANDROID_EXPORT __attribute__ ((visibility ("default")))
extern bool ANDROID_EXPORT android_init_gui;
extern int ANDROID_EXPORT android_emacs_init (int, char **);
#ifndef ANDROID_STUBIFY
extern int android_select (int, fd_set *, fd_set *, fd_set *,
struct timespec *, const sigset_t *);
extern bool android_file_access_p (const char *, int);
extern int android_open (const char *, int, int);
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_close (int);
#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
#endif
#endif /* _ANDROID_H_ */

1779
src/androidfns.c Normal file

File diff suppressed because it is too large Load diff

955
src/androidfont.c Normal file
View file

@ -0,0 +1,955 @@
/* 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 "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;
/* 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;
};
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;
};
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;[I[Lorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
"$FontObject;C)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;");
#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;
}
/* 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);
if (!spec)
{
(*android_java_env)->ExceptionClear (android_java_env);
memory_full (0);
}
#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))); \
if (!string) \
{ \
(*android_java_env)->ExceptionClear (android_java_env); \
memory_full (0); \
} \
\
(*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); \
if (!integer) \
{ \
(*android_java_env)->ExceptionClear (android_java_env); \
memory_full (0); \
} \
\
(*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));
#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);
#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;
spec = androidfont_from_lisp (font_spec);
array = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.list,
spec);
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (spec);
if (!array)
memory_full (0);
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_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (tem);
if (!info->object)
memory_full (0);
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;
spec = androidfont_from_lisp (font_spec);
result = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.match,
spec);
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (spec);
if (!result)
memory_full (0);
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_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (result);
if (!info->object)
memory_full (0);
return entity;
}
static int
androidfont_draw (struct glyph_string *s, int from, int to,
int x, int y, bool with_background)
{
return 0;
}
static Lisp_Object
androidfont_open_font (struct frame *f, Lisp_Object font_entity, int x)
{
struct androidfont_info *font_info;
struct androidfont_entity *entity;
struct font *font;
Lisp_Object font_object, tem;
jobject old;
jint value;
if (x <= 0)
{
/* Get pixel size from frame instead. */
tem = get_frame_param (f, Qfontsize);
x = NILP (tem) ? 0 : XFIXNAT (tem);
}
__android_log_print (ANDROID_LOG_DEBUG, __func__,
"opening font entity %"pI"x:%d",
(EMACS_INT) font_entity, x);
entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
block_input ();
font_object = font_make_object (VECSIZE (struct androidfont_info),
font_entity, x);
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 early in case GC happens later on! */
font_info->object = NULL;
unblock_input ();
font_info->object
= (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.open_font,
entity->object, (jint) x);
if (!font_info->object)
{
(*android_java_env)->ExceptionClear (android_java_env);
return Qnil;
}
old = font_info->object;
font_info->object
= (*android_java_env)->NewGlobalRef (android_java_env, old);
(*android_java_env)->ExceptionClear (android_java_env);
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;
info = (struct androidfont_info *) font;
/* If info->object is NULL, then FONT was unsuccessfully created,
and there is no global reference that has to be deleted. */
if (!info->object)
return;
(*android_java_env)->DeleteGlobalRef (android_java_env,
info->object);
}
static int
androidfont_has_char (Lisp_Object font, int c)
{
struct androidfont_info *info;
struct androidfont_entity *entity;
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;
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_text_extents (struct font *font, const unsigned int *code,
int nglyphs, struct font_metrics *metrics)
{
struct androidfont_info *info;
jarray codepoint_array, metrics_array;
jobject metrics_object;
int i;
short value;
info = (struct androidfont_info *) font;
/* 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);
}
metrics_array
= (*android_java_env)->NewObjectArray (android_java_env,
nglyphs,
font_metrics_class.class,
NULL);
if (!metrics_array)
{
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (metrics_array);
memory_full (0);
}
if (sizeof (unsigned int) == sizeof (jint))
/* Always true on every Android device. */
(*android_java_env)->SetIntArrayRegion (android_java_env,
codepoint_array,
0, nglyphs,
(jint *) code);
else
emacs_abort ();
for (i = 0; i < nglyphs; ++i)
{
metrics_object
= (*android_java_env)->AllocObject (android_java_env,
font_metrics_class.class);
if (!metrics_object)
{
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (metrics_array);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
memory_full (0);
}
(*android_java_env)->SetObjectArrayElement (android_java_env,
metrics_array, i,
metrics_object);
ANDROID_DELETE_LOCAL_REF (metrics_object);
}
(*android_java_env)->CallVoidMethod (android_java_env,
font_driver,
font_driver_class.text_extents,
info->object, codepoint_array,
metrics_array);
if ((*android_java_env)->ExceptionCheck (android_java_env))
{
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (metrics_array);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
memory_full (0);
}
for (i = 0; i < nglyphs; ++i)
{
metrics_object
= (*android_java_env)->GetObjectArrayElement (android_java_env,
metrics_array, i);
#define DO_CARDINAL_FIELD(field) \
value \
= (*android_java_env)->GetShortField (android_java_env, \
metrics_object, \
font_metrics_class.field); \
metrics[i].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 (metrics_array);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
}
static Lisp_Object
androidfont_list_family (struct frame *f)
{
return Qnil;
}
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)
{
jmethodID method;
jobject old;
android_init_font_driver ();
android_init_font_spec ();
android_init_font_metrics ();
android_init_font_object ();
android_init_integer ();
method = font_driver_class.create_font_driver;
/* Initialize the font driver on the Java side. */
font_driver
= (*android_java_env)->CallStaticObjectMethod (android_java_env,
font_driver_class.class,
method);
if (!font_driver)
memory_full (0);
old = font_driver;
font_driver
= (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_driver)
memory_full (0);
}
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

315
src/androidgui.h Normal file
View file

@ -0,0 +1,315 @@
/* 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 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),
};
struct android_set_window_attributes
{
/* The background pixel. */
unsigned long background_pixel;
};
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
{
/* The Java-side handle. */
android_gcontext gcontext;
};
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;
};
/* Android doesn't support cursors, so define this to something
unused. */
typedef char Emacs_Cursor;
#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;
/* 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,
};
struct android_any_event
{
enum android_event_type type;
android_window window;
};
struct android_key_event
{
enum android_event_type type;
android_window window;
android_time time;
unsigned int state;
unsigned int keycode;
};
struct android_configure_event
{
enum android_event_type type;
android_window window;
android_time time;
int x, y;
int width, height;
};
union android_event
{
enum android_event_type type;
struct android_any_event xany;
struct android_key_event xkey;
struct android_configure_event xconfigure;
};
extern int android_pending (void);
extern void android_next_event (union android_event *);
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);
#endif
/* 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_ */

3161
src/androidterm.c Normal file

File diff suppressed because it is too large Load diff

366
src/androidterm.h Normal file
View file

@ -0,0 +1,366 @@
/* 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. */
Emacs_Pixmap img;
/* The file from which it comes. */
char *file;
/* The number of references to it. */
int refcount;
/* The height and width. */
int height, width;
};
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 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;
};
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 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;
/* Unused stuff (cursors). */
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;
/* 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 we've modified the back buffer and
need to publish our modifications to the front buffer at a
convenient time. */
bool_bf need_buffer_flip : 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;
/* 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;
};
/* 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 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);
/* 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 *);
#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_ */

View file

@ -979,14 +979,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

View file

@ -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 Emacs_Pixmap Emacs_Pix_Container;
typedef Emacs_Pixmap Emacs_Pix_Context;
#endif
#ifdef HAVE_WINDOW_SYSTEM
# include <time.h>
# include "fontset.h"
@ -1401,6 +1421,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 +1703,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
@ -3502,9 +3526,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. */
@ -3566,7 +3592,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
@ -3647,6 +3673,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

View file

@ -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. */
{
@ -2296,7 +2302,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))
@ -3234,7 +3240,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))
@ -5059,6 +5065,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;
@ -5149,6 +5159,7 @@ scrolling (struct frame *frame)
free_at_end_vpos - unchanged_at_top);
SAFE_FREE ();
#endif
return false;
}
@ -5190,7 +5201,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. */
@ -5395,7 +5408,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.
@ -5405,7 +5421,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;
@ -6528,6 +6547,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)
{
@ -6582,6 +6610,7 @@ init_display_interactive (void)
exit (1);
}
#ifndef HAVE_ANDROID
{
struct terminal *t;
struct frame *f = XFRAME (selected_frame);
@ -6624,6 +6653,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 ();

View file

@ -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, ',');

View file

@ -33,6 +33,10 @@ 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
#ifdef WINDOWSNT
#include <fcntl.h>
#include <sys/socket.h>
@ -137,6 +141,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"
@ -1123,7 +1131,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 +1233,18 @@ 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)
#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;
/* First, check whether we should apply a seccomp filter. This
should come at the very beginning to allow the filter to protect
@ -1425,8 +1439,10 @@ main (int argc, char **argv)
bool only_version = false;
sort_args (argc, argv);
argc = 0;
while (argv[argc]) argc++;
old_argc = argc, argc = 0;
while (argv[argc]
/* Don't allow going past argv. */
&& argc < old_argc) argc++;
skip_args = 0;
if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
@ -2375,6 +2391,14 @@ 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_fontset ();
#if !defined ANDROID_STUBIFY
syms_of_androidfont ();
#endif /* !ANDROID_STUBIFY */
#endif /* HAVE_ANDROID */
syms_of_gnutls ();
@ -2436,6 +2460,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_haiku_select ();
#endif
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
init_androidfont ();
#endif
init_charset ();
/* This calls putenv and so must precede init_process_emacs. */
@ -2950,7 +2978,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 ())
{
@ -3469,6 +3497,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);

View file

@ -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

View file

@ -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>
@ -135,7 +139,6 @@ 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,
@ -160,6 +163,12 @@ file_access_p (char const *file, int amode)
}
#endif
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
/* FILE may be some kind of special Android file. */
if (android_file_access_p (file, amode))
return true;
#endif
if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
return true;
@ -370,7 +379,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);
@ -2227,7 +2236,7 @@ permissions. */)
record_unwind_protect_int (close_file_unwind, ifd);
if (fstat (ifd, &st) != 0)
if (sys_fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
if (!NILP (preserve_permissions))
@ -2273,7 +2282,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",
@ -3078,7 +3087,7 @@ file_directory_p (Lisp_Object file)
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);
@ -4002,7 +4011,7 @@ by calling `format-decode', which see. */)
XCAR (XCAR (window_markers)));
}
if (fstat (fd, &st) != 0)
if (sys_fstat (fd, &st) != 0)
report_file_error ("Input file status", orig_filename);
mtime = get_stat_mtime (&st);
@ -5394,7 +5403,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;
@ -5432,7 +5441,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

View file

@ -120,6 +120,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
* Non-forced locks on non-MS-Windows systems that support neither
hard nor symbolic links. */
/* Boot time is not available on Android. */
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
#undef BOOT_TIME
#endif
/* Return the time of the last system boot. */

View file

@ -3519,6 +3519,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))
{

View file

@ -180,6 +180,22 @@ font_make_entity (void)
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));
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. */

View file

@ -823,6 +823,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);

View file

@ -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.
@ -1228,6 +1232,7 @@ make_initial_frame (void)
return f;
}
#ifndef HAVE_ANDROID
static struct frame *
make_terminal_frame (struct terminal *terminal)
@ -1317,6 +1322,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 +1343,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 +1447,7 @@ affects all frames on the same terminal device. */)
f->after_make_frame = true;
return frame;
#endif
}
@ -5303,16 +5315,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
}
@ -6218,6 +6237,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");

View file

@ -181,7 +181,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 +377,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,12 +586,13 @@ 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;
@ -713,7 +714,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 +873,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 +896,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 +926,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

View file

@ -175,6 +175,28 @@ typedef struct haiku_bitmap_record Bitmap_Record;
#endif
#ifdef HAVE_ANDROID
#include "androidterm.h"
typedef struct android_bitmap_record Bitmap_Record;
/* TODO: implement images on Android. */
#define GET_PIXEL(ximg, x, y) 0
#define PUT_PIXEL(ximg, x, y, pixel) ((void) (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);
@ -831,6 +853,18 @@ 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
/* you lose, not yet implemented TODO */
return 0;
#endif
#endif
}
/* Free bitmap B. */
@ -3338,6 +3372,16 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
Emacs_Pix_Container *pimg,
Emacs_Pixmap *pixmap, Picture *picture)
{
#ifdef HAVE_ANDROID
#ifdef ANDROID_STUBIFY
/* This function should never be called when building stubs. */
emacs_abort ();
#else
/* you lose, not yet implemented TODO */
return false;
#endif
#endif
#ifdef USE_CAIRO
eassert (input_blocked_p ());
@ -3646,6 +3690,16 @@ image_unget_x_image_or_dc (struct image *img, bool mask_p,
static Emacs_Pix_Container
image_get_x_image (struct frame *f, struct image *img, bool mask_p)
{
#ifdef HAVE_ANDROID
#ifdef ANDROID_STUBIFY
/* This function should never be called when building stubs. */
emacs_abort ();
#else
/* you lose, not yet implemented TODO */
return 0;
#endif
#endif
#if defined USE_CAIRO || defined (HAVE_HAIKU)
return !mask_p ? img->pixmap : img->mask;
#elif defined HAVE_X_WINDOWS
@ -3756,7 +3810,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.
@ -6074,7 +6128,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,
@ -6147,7 +6202,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);
@ -6155,7 +6211,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);
@ -6166,7 +6222,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);
@ -6231,7 +6287,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 ();
@ -6393,7 +6453,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,
@ -6429,8 +6491,16 @@ 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
#ifdef ANDROID_STUBIFY
/* This function should never be called when building stubs. */
emacs_abort ();
#else
/* you lose, not yet implemented TODO */
#endif
#endif
}
#endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
/* Transform image IMG on frame F so that it looks disabled. */
@ -6474,7 +6544,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 */
@ -6482,7 +6552,7 @@ image_disable_image (struct frame *f, struct image *img)
#define MaskForeground(f) PIX_MASK_DRAW
#endif /* USE_CAIRO || HAVE_HAIKU */
#if !defined USE_CAIRO && !defined HAVE_HAIKU
#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
image_sync_to_pixmaps (f, img);
#endif /* !USE_CAIRO && !HAVE_HAIKU */
image_pixmap_draw_cross (f, img->pixmap, 0, 0, img->width, img->height,
@ -9099,7 +9169,7 @@ gif_load (struct frame *f, struct image *img)
`image-cache-size'. */
struct stat st;
FILE *fp = fopen (SSDATA (encoded_file), "rb");
if (fstat (fileno (fp), &st) == 0)
if (sys_fstat (fileno (fp), &st) == 0)
byte_size = st.st_size;
fclose (fp);
}

View file

@ -30,6 +30,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <inttypes.h>
#include <limits.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <attribute.h>
#include <intprops.h>
#include <verify.h>
@ -4726,6 +4730,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 *);
@ -5063,7 +5068,12 @@ 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
#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]);
@ -5101,7 +5111,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);

View file

@ -1047,7 +1047,7 @@ 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 (sys_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
return 0;
/* Read the first few bytes from the file, and look for a line
@ -1676,7 +1676,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
{
@ -1998,7 +1998,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)
{

View file

@ -5620,7 +5620,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;

View file

@ -119,6 +119,10 @@ static struct rlimit nofile_limit;
#include "gnutls.h"
#endif
#ifdef HAVE_ANDROID
#include "android.h"
#endif
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@ -5679,7 +5683,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, NULL);
#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 +5709,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. */

View file

@ -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

View file

@ -134,6 +134,10 @@ int _cdecl _spawnlp (int, const char *, const char *, ...);
# include <sys/socket.h>
#endif
#ifdef HAVE_ANDROID
#include "android.h"
#endif
/* Declare here, including term.h is problematic on some systems. */
extern void tputs (const char *, int, int (*)(int));
@ -790,6 +794,7 @@ init_sigio (int fd)
#endif
}
#ifndef HAVE_ANDROID
#ifndef DOS_NT
#ifdef F_SETOWN
static void
@ -801,6 +806,7 @@ reset_sigio (int fd)
}
#endif /* F_SETOWN */
#endif
#endif
void
request_sigio (void)
@ -972,6 +978,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 +987,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 +1507,8 @@ reset_sys_modes (struct tty_display_info *tty_out)
fflush (stdout);
return;
}
#ifndef HAVE_ANDROID
if (!tty_out->term_initted)
return;
@ -1552,6 +1565,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
#endif
widen_foreground_group (fileno (tty_out->input));
#endif
}
#ifdef HAVE_PTYS
@ -1802,7 +1816,11 @@ handle_arith_signal (int sig)
xsignal0 (Qarith_error);
}
#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
/* 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 +1932,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
@ -2020,12 +2042,17 @@ init_signals (void)
sigaction (SIGFPE, &action, 0);
}
/* 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);
@ -2051,8 +2078,10 @@ init_signals (void)
#ifdef SIGBUS
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
@ -2328,11 +2357,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)
{
@ -2347,6 +2385,18 @@ 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
}
/* 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.
@ -2355,6 +2405,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)
{
@ -2367,10 +2419,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. */
@ -2382,9 +2447,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;
}
@ -2434,6 +2505,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
@ -2460,6 +2533,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
@ -2472,9 +2547,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)
@ -2729,6 +2812,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");

View file

@ -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;
@ -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
@ -4447,6 +4536,7 @@ use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\
init_sys_modes (tty);
return terminal;
#endif /* !HAVE_ANDROID */
}
@ -4471,8 +4561,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);
@ -4490,6 +4585,8 @@ fatal (const char *str, ...)
#ifndef HAVE_ANDROID
/* Delete the given tty terminal, closing all frames on it. */
static void
@ -4547,6 +4644,8 @@ delete_tty (struct terminal *terminal)
xfree (tty);
}
#endif
void
syms_of_term (void)
{
@ -4594,21 +4693,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");

View file

@ -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);

View file

@ -451,6 +451,8 @@ return values. */)
return Qpgtk;
case output_haiku:
return Qhaiku;
case output_android:
return Qandroid;
default:
emacs_abort ();
}

View file

@ -32,6 +32,10 @@ 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 =
else
# Whether $(info ...) works. This is to work around a bug in GNU Make
@ -76,4 +80,8 @@ 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_DX = @$(info $ DX $@)
endif

View file

@ -16415,7 +16415,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;
@ -26320,7 +26320,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);
@ -26390,7 +26390,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)
@ -26431,6 +26435,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)
@ -26551,6 +26560,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
@ -33432,7 +33444,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. */
@ -36104,14 +36118,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

View file

@ -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
***********************************************************************/
@ -6951,19 +6988,21 @@ 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);
}
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);
}
unblock_input ();

209
xcompile/Makefile.in Normal file
View file

@ -0,0 +1,209 @@
### @configure_input@
# 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/>.
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
# Cross-compiling Emacs for Android.
# The cross compiled binaries are built by having ``variant''
# Makefiles generated at configure-time. First,
# $(top_builddir)/src/Makefile.android,
# $(top_builddir)/lib/Makefile.android,
# $(top_builddir)/lib/gnulib.mk.android and
# $(top_builddir)/lib-src/Makefile.android are copied to their usual
# locations in this directory.
# Finally, the following commands are executed in order, to produce
# libgnu.a, various binaries in lib-src, and src/aemacs:
# make -C lib libgnu.a
# make -C lib-src src/aemacs
# make -C src aemacs
# This is possibly the ugliest Makefile ever written!
LIB_SRCDIR = $(realpath $(top_srcdir)/lib)
LIB_TOP_SRCDIR = $(realpath $(top_srcdir))
SRC_SRCDIR = $(realpath $(top_srcdir)/src)
SRC_TOP_SRCDIR = $(realpath $(top_srcdir))
LIB_SRC_SRCDIR = $(realpath $(top_srcdir)/lib-src)
LIB_SRC_TOP_SRCDIR = $(realpath $(top_src))
# This is a list of binaries to build and install in lib-src.
LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \
lib-src/ebrowse lib-src/hexl lib-src/movemail
CLEAN_SUBDIRS=lib src lib-src
.PHONY: all
all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES)
# This Makefile relies on builddir and top_builddir being relative
# paths in *.android.
# This file is used to trick lib/gnulib.mk, it is not actually useful.
config.status:
touch config.status
src/verbose.mk: verbose.mk.android
mkdir -p src
cp -f verbose.mk.android src/verbose.mk
UGLY_HOST_HEADERS = dirent.h stdlib.h sys/random.h limits.h \
string.h signal.h time.h inttypes.h assert.h \
stdint.h unistd.h stdlib.h sys/types.h sys/time.h \
sys/stat.h getopt.h fcntl.h sys/select.h alloca.h \
stdio.h sys/random.h
# Gnulib, make-fingerprint and make-docfile must be built before
# entering any of the rules below, or they will get the Android
# versions of many headers.
.PHONY: $(top_builddir)/lib/libgnu.a
$(top_builddir)/lib/libgnu.a:
+ make -C $(top_builddir)/lib libgnu.a
.PHONY: $(top_builddir)/lib-src/make-fingerprint
$(top_builddir)/lib-src/make-fingerprint:
make -C $(top_builddir)/lib-src make-fingerprint
.PHONY: $(top_builddir)/lib-src/make-docfile
$(top_builddir)/lib-src/make-docfile:
make -C $(top_builddir)/lib-src make-docfile
PRE_BUILD_DEPS=$(top_builddir)/lib/libgnu.a \
$(top_builddir)/lib-src/make-fingerprint \
$(top_builddir)/lib-src/make-docfile
.PHONY: lib/libgnu.a
lib/libgnu.a: src/verbose.mk config.status $(PRE_BUILD_DEPS)
mkdir -p lib/deps lib/deps/malloc
# Temporarily move config.h to config.h.bak and config.h.android to
# config.h
cp -f -p $(top_builddir)/src/config.h.android lib/config.h
# And the Makefiles.
cp -f -p $(top_builddir)/lib/gnulib.mk.android lib/gnulib.mk
cp -f -p $(top_builddir)/lib/Makefile.android lib/Makefile
# Next, move srcdir and top_srcdir in the Makefiles copied.
sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' lib/Makefile
sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
lib/Makefile
sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' lib/gnulib.mk
# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
# while building lib. Otherwise, they will end up overriding the
# system headers used on Android through #include_next and cause
# trouble.
mkdir -p sys
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then \
mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak; \
fi \
done
# And make libgnu.a. Restore config.h if it fails.
-make -C lib libgnu.a
# Move the hiden headers back
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$$ugly_header.bak" ]; then \
mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header; \
fi \
done
src/Makefile src/config.h &: $(top_builddir)/src/config.h.android \
$(top_builddir)/src/Makefile.android $(PRE_BUILD_DEPS)
mkdir -p src src/deps
# Copy config.h to src/
cp -f -p $(top_builddir)/src/config.h.android src/config.h
# And the Makefile.
cp -f -p $(top_builddir)/src/Makefile.android src/Makefile
# Next, edit srcdir and top_srcdir to the right location.
sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' src/Makefile
sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
src/Makefile
# Edit references to ../admin/unidata to read ../../admin/unidata.
sed -i 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' src/Makefile
sed -i 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' src/Makefile
# Next, edit libsrc to the location at top_srcdir! It is important
# that src/Makefile uses the binaries there, instead of any
# cross-compiled binaries at ./lib-src.
sed -i 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' src/Makefile
.PHONY: src/android-emacs src/libemacs.so
src/android-emacs src/libemacs.so &: src/Makefile src/config.h \
src/verbose.mk lib/libgnu.a $(PRE_BUILD_DEPS)
# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
# while building lib. Otherwise, they will end up overriding the
# system headers used on Android through #include_next and cause
# trouble.
mkdir -p sys
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then \
mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak; \
fi \
done
# Finally, go into src and make
+make -C src android-emacs libemacs.so
# Move the hidden headers back
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$$ugly_header.bak" ]; then \
mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header; \
fi \
done
lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android
mkdir -p lib-src
cp -f -p $< $@
.PHONY: $(LIBSRC_BINARIES)
$(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \
src/config.h lib-src/Makefile $(PRE_BUILD_DEPS)
mkdir -p src lib-src
# Next, edit srcdir and top_srcdir to the right location.
sed -i 's/srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \
lib-src/Makefile
sed -i 's/top_srcdir=.*$$/top_srcdir = $(subst /,\/,$(LIB_SRC_TOP_SRCDIR))/g' \
lib-src/Makefile
# Edit out SCRIPTS, it interferes with the build.
sed -i 's/^SCRIPTS=.*$$/SCRIPTS=/g' lib-src/Makefile
# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
# while building lib. Otherwise, they will end up overriding the
# system headers used on Android through #include_next and cause
# trouble.
mkdir -p sys
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then \
mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak; \
fi \
done
# Finally, go into lib-src and make everything being built
+make -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
# Move the hidden headers back
for ugly_header in $(UGLY_HOST_HEADERS); do \
if [ -e "$$ugly_header.bak" ]; then \
mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header; \
fi \
done
.PHONY: clean maintainer-clean
clean:
rm -rf $(CLEAN_SUBDIRS) *.bak sys
maintainer-clean: clean

7
xcompile/README Normal file
View file

@ -0,0 +1,7 @@
This directory holds Makefiles and other required assets to build an
Emacs binary independently for another toolchain, which is currently
required when building for Android.
The files here are extremely ugly, and contain rules that cannot be
interrupted without leaving the build tree in an unsafe state. At
some point that should be fixed!

20
xcompile/langinfo.h Normal file
View file

@ -0,0 +1,20 @@
/* Replacement langinfo.h file for building GNU Emacs on 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, see <https://www.gnu.org/licenses/>. */
#define nl_langinfo(ignore) "ASCII"