1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

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

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