aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-01-13 15:53:08 +0800
committerPo Lu2023-01-13 15:53:08 +0800
commitf9732131cf3c67e24db74a3d49f256d3c189a7e3 (patch)
treee44e097c0c4c513390f58fa0020c8662943fbac6 /java
parent2fa5583d96fe78ff66d6fd41f18e54e4e20ea7d6 (diff)
downloademacs-f9732131cf3c67e24db74a3d49f256d3c189a7e3.tar.gz
emacs-f9732131cf3c67e24db74a3d49f256d3c189a7e3.zip
Update Android port
* configure.ac (ANDROID_MIN_SDK): New variable. (DX): Remove and replace with D8. (XCONFIGURE): Check for the minimum version of Android the cross compiler compiles for. Generate java/AndroidManifest.xml from java/AndroidManifest.xml.in. Allow using Zlib on Android. * java/AndroidManifest.xml.in: New file. Use the minimum SDK detected by configure. * java/Makefile.in (top_srcdir, version): New variables. (DX, D8): Replace with D8. (ANDROID_MIN_SDK, APK_NAME): New variables. (.PHONY): (.PRECIOUS): (classes.dex): (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'. * java/debug.sh: New option --attach-existing. Attach to an existing Emacs instance when specified. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New field `isPaused'. (invalidateFocus1): Fix infinite recursion. (detachWindow): Deiconify window. (attachWindow): Iconify the window if the activity is paused. (onCreate): Use the ``no title bar'' theme. (onPause, onResume): New functions. * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown) (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New functions. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface): (list): Remove logging for code that is mostly going to be unused. * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree) (getScreenWidth, getScreenHeight, detectMouse): New functions. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra debug logging. Avoid deadlock in surfaceCreated. * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard to make the SurfaceView respect Z order. It didn't work. (handleDirtyBitmap): Copy over the contents from the old bitmap. (explicitlyDirtyBitmap): New function. (onLayout): Don't dirty bitmap if unnecessary. (damageRect, swapBuffers): Don't synchronize so hard. (onTouchEvent): Call window.onTouchEvent instead. (moveChildToBack, raise, lower): New functions. * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New subclass. (pointerMap, isMapped, isIconified, dontFocusOnMap) (dontAcceptFocus): New fields. (EmacsWindow): Don't immediately register unmapped window. (viewLayout): Send configure event outside the lock. (requestViewLayout): Explicitly dirty the bitmap. (mapWindow): Register the window now. Respect dontFocusOnMap. (unmapWindow): Unregister the window now. (figureChange, onTouchEvent): New functions. (onSomeKindOfMotionEvent): Handle scroll wheel events. (reparentTo, makeInputFocus, raise, lower, getWindowGeometry) (noticeIconified, noticeDeiconified, setDontAcceptFocus) (setDontFocusOnMap, getDontFocusOnMap): New functions. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (registerWindow, detachWindow): Synchronize. (noticeIconified, noticeDeiconified): New functions. (copyWindows): New function. * lisp/frame.el (frame-geometry, frame-edges) (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position) (frame-list-z-order, frame-restack, display-mouse-p) (display-monitor-attributes-list): Implement on Android. * lisp/mwheel.el (mouse-wheel-down-event): (mouse-wheel-up-event): (mouse-wheel-left-event): (mouse-wheel-right-event): Define on Android. * src/android.c (struct android_emacs_service): New methods `ringBell', `queryTree', `getScreenWidth', `getScreenHeight', and `detectMouse'. (struct android_event_queue, android_init_events) (android_next_event, android_write_event): Remove write limit. (android_file_access_p): Handle directories correcty. (android_close): Fix coding style. (android_fclose): New function. (android_init_emacs_service): Initialize new methods. (android_reparent_window): Implement function. (android_bell, android_set_input_focus, android_raise_window) (android_lower_window, android_query_tree, android_get_geometry) (android_get_screen_width, android_get_screen_height) (android_get_mm_width, android_get_mm_height, android_detect_mouse) (android_set_dont_focus_on_map, android_set_dont_accept_focus): New functions. (struct android_dir): New structure. (android_opendir, android_readdir, android_closedir): New functions. (emacs_abort): Implement here on Android and poke debuggerd into generating a tombstone. * src/android.h: Update prototypes. * src/androidfns.c (android_set_parent_frame): New function. (android_default_font_parameter): Use sane font size by default. (Fx_display_pixel_width, Fx_display_pixel_height) (Fx_display_mm_width, Fx_display_mm_height) (Fx_display_monitor_attributes_list): Rename to start with `android-'. Implement. Fiddle with documentation to introduce Android specific nuances. (Fandroid_display_monitor_attributes_list): New function. (Fx_frame_geometry, frame_geometry): New function. (Fandroid_frame_geometry): Implement correctly. (Fx_frame_list_z_order): Rename to start with `android-'. (android_frame_list_z_order, Fandroid_frame_list_z_order): Implement. (Fx_frame_restack): Rename to start with `android-'. (Fandroid_frame_restack): ``Implement''. (Fx_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_mouse_absolute_pixel_position): ``Implement''. (Fx_set_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_set_mouse_absolute_pixel_position): ``Implement''. (Fandroid_detect_mouse): New function. (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when clearing area. (android_set_no_focus_on_map, android_set_no_accept_focus): New functions. (android_frame_parm_handlers): Register new frame parameter handlers. (syms_of_androidfns): Update appropriately. * src/androidfont.c (androidfont_draw): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. * src/androidgui.h (enum android_event_type): New events. (struct android_touch_event, struct android_wheel_event) (struct android_iconify_event): New structures. (union android_event): Add new events. * src/androidterm.c (android_clear_frame): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. (android_flash, android_ring_bell): Implement bell ringing. (android_toggle_invisible_pointer): Don't TODO function that can't be implemented. (show_back_buffer, android_flush_dirty_back_buffer_on): Check if a buffer flip is required before doing the flip. (android_lower_frame, android_raise_frame): Implement functions. (android_update_tools, android_find_tool): New functions. (handle_one_android_event): Handle new iconification, wheel and touch events. (android_read_socket): Implement pending-autoraise-frames. (android_frame_up_to_date): Implement bell ringing. (android_buffer_flipping_unblocked_hook): Check if a buffer flip is required before doing the flip. (android_focus_frame, android_frame_highlight) (android_frame_unhighlight): New function. (android_frame_rehighlight): Implement functions. (android_iconify_frame): Always display error. (android_set_alpha): Update commentary. (android_free_frame_resources): Free frame touch points. (android_scroll_run, android_flip_and_flush) (android_clear_rectangle, android_draw_fringe_bitmap) (android_draw_glyph_string_background, android_fill_triangle) (android_clear_point, android_draw_relief_rect) (android_draw_box_rect, android_draw_glyph_string_bg_rect) (android_draw_image_foreground, 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_clear_frame_area) (android_clear_under_internal_border, android_draw_hollow_cursor) (android_draw_bar_cursor, android_draw_vertical_window_border) (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW for drawing operations. * src/androidterm.h (struct android_touch_point): New structure. (struct android_output): New fields. (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro. * src/dired.c (emacs_readdir, open_directory) (directory_files_internal_unwind, read_dirent) (directory_files_internal, file_name_completion): Add indirection over readdir and opendir. Use android variants on Android. * src/dispnew.c (Fopen_termscript): * src/fileio.c (fclose_unwind): Use emacs_fclose. (Faccess_file): Call android_file_access_p. (file_accessible_directory_p): Append right suffix to Android assets directory. (do_auto_save_unwind): Use emacs_fclose. * src/keyboard.c (lispy_function_keys): Use right function key for page up and page down. (Fopen_dribble_file): Use emacs_fclose. * src/lisp.h: New prototype emacs_fclose. * src/lread.c (close_infile_unwind): Use emacs_fclose. * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation. (sfnt_prepare_raster): Compute raster width and height consistently with outline building. (sfnt_build_outline_edges): Use the same offsets used to set offy and offx. (main): Adjust debug code. * src/sfntfont-android.c (sfntfont_android_saturate32): Delete function. (sfntfont_android_blend, sfntfont_android_blendrgb): Remove unnecessary debug code. (sfntfont_android_composite_bitmap): Prevent out of bounds write. (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE. (init_sfntfont_android): Initialize Monospace Serif font to something sensible. * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics before summing up pcm. (sfntfont_draw): Use s->font instead of s->face->font. * src/sysdep.c (emacs_fclose): Wrap around android_fclose on android. * src/term.c (Fsuspend_tty): (delete_tty): Use emacs_fclose. * src/verbose.mk.in (AM_V_DX): Replace with D8 version.
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml.in84
-rw-r--r--java/Makefile.in30
-rwxr-xr-xjava/debug.sh45
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java37
-rw-r--r--java/org/gnu/emacs/EmacsNative.java25
-rw-r--r--java/org/gnu/emacs/EmacsSdk7FontDriver.java7
-rw-r--r--java/org/gnu/emacs/EmacsService.java116
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java22
-rw-r--r--java/org/gnu/emacs/EmacsView.java189
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java498
-rw-r--r--java/org/gnu/emacs/EmacsWindowAttachmentManager.java54
11 files changed, 991 insertions, 116 deletions
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
new file mode 100644
index 00000000000..b680137a9d0
--- /dev/null
+++ b/java/AndroidManifest.xml.in
@@ -0,0 +1,84 @@
1<!-- @configure_input@
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
19
20<!-- targetSandboxVersion must be 1. Otherwise, fascist security
21 restrictions prevent Emacs from making HTTP connections. -->
22
23<manifest xmlns:android="http://schemas.android.com/apk/res/android"
24 package="org.gnu.emacs"
25 android:targetSandboxVersion="1"
26 android:installLocation="auto"
27 android:versionName="@version@">
28
29 <!-- Paste in every permission in existence so Emacs can do
30 anything. -->
31
32 <uses-permission android:name="android.permission.READ_CONTACTS" />
33 <uses-permission android:name="android.permission.WRITE_CONTACTS" />
34 <uses-permission android:name="android.permission.VIBRATE" />
35 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
36 <uses-permission android:name="android.permission.INTERNET" />
37 <uses-permission android:name="android.permission.SET_WALLPAPER" />
38 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
39 <uses-permission android:name="android.permission.SEND_SMS" />
40 <uses-permission android:name="android.permission.RECEIVE_SMS" />
41 <uses-permission android:name="android.permission.RECEIVE_MMS"/>
42 <uses-permission android:name="android.permission.WRITE_SMS"/>
43 <uses-permission android:name="android.permission.READ_SMS"/>
44 <uses-permission android:name="android.permission.NFC" />
45 <uses-permission android:name="android.permission.TRANSMIT_IR" />
46 <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
47 <uses-permission android:name="android.permission.WAKE_LOCK"/>
48 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
49 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
50 <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
51 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
52 <uses-permission android:name="android.permission.RECORD_AUDIO" />
53 <uses-permission android:name="android.permission.CAMERA" />
54
55 <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
56 android:targetSdkVersion="28"/>
57
58 <application android:name="org.gnu.emacs.EmacsApplication"
59 android:label="Emacs"
60 android:hardwareAccelerated="true"
61 android:supportsRtl="true"
62 android:theme="@android:style/Theme"
63 android:debuggable="true"
64 android:extractNativeLibs="true">
65 <activity android:name="org.gnu.emacs.EmacsActivity"
66 android:launchMode="singleTop"
67 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
68 <intent-filter>
69 <action android:name="android.intent.action.MAIN" />
70 <category android:name="android.intent.category.DEFAULT" />
71 <category android:name="android.intent.category.LAUNCHER" />
72 </intent-filter>
73 </activity>
74
75 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
76 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
77
78 <service android:name="org.gnu.emacs.EmacsService"
79 android:directBootAware="false"
80 android:enabled="true"
81 android:exported="false"
82 android:label="GNU Emacs service"/>
83 </application>
84</manifest>
diff --git a/java/Makefile.in b/java/Makefile.in
index 31bd22b5a2e..05e61dede89 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -18,13 +18,15 @@
18# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 18# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19 19
20top_builddir = @top_builddir@ 20top_builddir = @top_builddir@
21top_srcdir = @top_srcdir@
22version = @version@
21 23
22-include ${top_builddir}/src/verbose.mk 24-include ${top_builddir}/src/verbose.mk
23 25
24SHELL = @SHELL@ 26SHELL = @SHELL@
25JAVAC = @JAVAC@ 27JAVAC = @JAVAC@
26AAPT = @AAPT@ 28AAPT = @AAPT@
27DX = @DX@ 29D8 = @D8@
28ZIPALIGN = @ZIPALIGN@ 30ZIPALIGN = @ZIPALIGN@
29JARSIGNER = @JARSIGNER@ 31JARSIGNER = @JARSIGNER@
30ANDROID_JAR = @ANDROID_JAR@ 32ANDROID_JAR = @ANDROID_JAR@
@@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
39JAVA_FILES = $(shell find . -type f -name *.java) 41JAVA_FILES = $(shell find . -type f -name *.java)
40CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class) 42CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
41 43
44# Compute the name for the Emacs application package. This should be:
45# emacs-<version>-<min-sdk>-<abi>.apk
46
47ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
48APK_NAME = emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
49
42# How this stuff works. 50# How this stuff works.
43 51
44# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive 52# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
@@ -55,7 +63,7 @@ CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
55# assets/lisp/ 63# assets/lisp/
56 64
57.PHONY: emacs.apk-in all 65.PHONY: emacs.apk-in all
58all: emacs.apk 66all: $(APK_NAME)
59 67
60# Binaries to cross-compile. 68# Binaries to cross-compile.
61CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \ 69CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
@@ -118,6 +126,18 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
118 pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd 126 pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
119 rm -rf install_temp 127 rm -rf install_temp
120 128
129# Makefile itself.
130.PRECIOUS: ../config.status Makefile
131../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
132 $(MAKE) -C $(dir $@) $(notdir $@)
133Makefile: ../config.status $(top_builddir)/java/Makefile.in
134 $(MAKE) -C .. java/$@
135
136# AndroidManifest.xml:
137AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
138 AndroidManifest.xml.in
139 pushd ..; ./config.status java/AndroidManifest.xml; popd
140
121.SUFFIXES: .java .class 141.SUFFIXES: .java .class
122.java.class &: 142.java.class &:
123 $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $< 143 $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
@@ -126,7 +146,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
126# nested classes. 146# nested classes.
127 147
128classes.dex: $(CLASS_FILES) 148classes.dex: $(CLASS_FILES)
129 $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \ 149 $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
130 $(subst $$,\$$,$(shell find . -type f -name *.class)) 150 $(subst $$,\$$,$(shell find . -type f -name *.class))
131 151
132# When emacs.keystore expires, regenerate it with: 152# When emacs.keystore expires, regenerate it with:
@@ -136,7 +156,7 @@ classes.dex: $(CLASS_FILES)
136 156
137.PHONY: clean maintainer-clean 157.PHONY: clean maintainer-clean
138 158
139emacs.apk: classes.dex emacs.apk-in emacs.keystore 159$(APK_NAME): classes.dex emacs.apk-in emacs.keystore
140 cp -f emacs.apk-in $@.unaligned 160 cp -f emacs.apk-in $@.unaligned
141 $(AAPT) add $@.unaligned classes.dex 161 $(AAPT) add $@.unaligned classes.dex
142 $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore" 162 $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
@@ -144,7 +164,7 @@ emacs.apk: classes.dex emacs.apk-in emacs.keystore
144 rm -f $@.unaligned 164 rm -f $@.unaligned
145 165
146clean: 166clean:
147 rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class 167 rm -f *.apk emacs.apk-in *.dex *.unaligned *.class
148 rm -rf install-temp 168 rm -rf install-temp
149 find . -name '*.class' -delete 169 find . -name '*.class' -delete
150 170
diff --git a/java/debug.sh b/java/debug.sh
index dd710dc31af..3e3e3d9c281 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -30,6 +30,7 @@ activity=org.gnu.emacs.EmacsActivity
30gdb_port=5039 30gdb_port=5039
31jdb_port=64013 31jdb_port=64013
32jdb=no 32jdb=no
33attach_existing=no
33 34
34while [ $# -gt 0 ]; do 35while [ $# -gt 0 ]; do
35 case "$1" in 36 case "$1" in
@@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do
48 echo " --port PORT run the GDB server on a specific port" 49 echo " --port PORT run the GDB server on a specific port"
49 echo " --jdb-port PORT run the JDB server on a specific port" 50 echo " --jdb-port PORT run the JDB server on a specific port"
50 echo " --jdb run JDB instead of GDB" 51 echo " --jdb run JDB instead of GDB"
52 echo " --attach-existing attach to an existing process"
51 echo " --help print this message" 53 echo " --help print this message"
52 echo "" 54 echo ""
53 echo "Available devices:" 55 echo "Available devices:"
@@ -63,6 +65,9 @@ while [ $# -gt 0 ]; do
63 "--port" ) 65 "--port" )
64 gdb_port=$1 66 gdb_port=$1
65 ;; 67 ;;
68 "--attach-existing" )
69 attach_existing=yes
70 ;;
66 "--" ) 71 "--" )
67 shift 72 shift
68 gdbargs=$@ 73 gdbargs=$@
@@ -120,30 +125,32 @@ package_pids=`awk -- '{
120 print $1 125 print $1
121}' <<< $package_pids` 126}' <<< $package_pids`
122 127
123# Finally, kill each existing process. 128if [ "$attach_existing" != "yes" ]; then
124for pid in $package_pids; do 129 # Finally, kill each existing process.
125 echo "Killing existing process $pid..." 130 for pid in $package_pids; do
126 adb -s $device shell run-as $package kill -9 $pid &> /dev/null 131 echo "Killing existing process $pid..."
127done 132 adb -s $device shell run-as $package kill -9 $pid &> /dev/null
128 133 done
129# Now run the main activity. This must be done as the adb user and 134
130# not as the package user. 135 # Now run the main activity. This must be done as the adb user and
131echo "Starting activity $activity and attaching debugger" 136 # not as the package user.
132 137 echo "Starting activity $activity and attaching debugger"
133# Exit if the activity could not be started. 138
134adb -s $device shell am start -D "$package/$activity" 139 # Exit if the activity could not be started.
135if [ ! $? ]; then 140 adb -s $device shell am start -D "$package/$activity"
136 exit 1; 141 if [ ! $? ]; then
137fi 142 exit 1;
143 fi
138 144
139# Now look for processes matching the package again. 145 # Now look for processes matching the package again.
140package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD` 146 package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
141 147
142# Next, remove lines matching "ps" itself. 148 # Next, remove lines matching "ps" itself.
143package_pids=`awk -- '{ 149 package_pids=`awk -- '{
144 if (!match ($0, /(PID|ps)/)) 150 if (!match ($0, /(PID|ps)/))
145 print $1 151 print $1
146}' <<< $package_pids` 152}' <<< $package_pids`
153fi
147 154
148pid=$package_pids 155pid=$package_pids
149num_pids=`wc -w <<< "$package_pids"` 156num_pids=`wc -w <<< "$package_pids"`
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
index 67f411a38c3..2b661024842 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -48,6 +48,9 @@ public class EmacsActivity extends Activity
48 /* The currently focused window. */ 48 /* The currently focused window. */
49 public static EmacsWindow focusedWindow; 49 public static EmacsWindow focusedWindow;
50 50
51 /* Whether or not this activity is paused. */
52 private boolean isPaused;
53
51 static 54 static
52 { 55 {
53 focusedActivities = new ArrayList<EmacsActivity> (); 56 focusedActivities = new ArrayList<EmacsActivity> ();
@@ -60,7 +63,7 @@ public class EmacsActivity extends Activity
60 focusedWindow = window; 63 focusedWindow = window;
61 64
62 for (EmacsWindow child : window.children) 65 for (EmacsWindow child : window.children)
63 invalidateFocus1 (window); 66 invalidateFocus1 (child);
64 } 67 }
65 68
66 public static void 69 public static void
@@ -103,6 +106,9 @@ public class EmacsActivity extends Activity
103 /* Clear the window's pointer to this activity and remove the 106 /* Clear the window's pointer to this activity and remove the
104 window's view. */ 107 window's view. */
105 window.setConsumer (null); 108 window.setConsumer (null);
109
110 /* The window can't be iconified any longer. */
111 window.noticeDeiconified ();
106 layout.removeView (window.view); 112 layout.removeView (window.view);
107 window = null; 113 window = null;
108 114
@@ -114,6 +120,8 @@ public class EmacsActivity extends Activity
114 public void 120 public void
115 attachWindow (EmacsWindow child) 121 attachWindow (EmacsWindow child)
116 { 122 {
123 Log.d (TAG, "attachWindow: " + child);
124
117 if (window != null) 125 if (window != null)
118 throw new IllegalStateException ("trying to attach window when one" 126 throw new IllegalStateException ("trying to attach window when one"
119 + " already exists"); 127 + " already exists");
@@ -124,6 +132,10 @@ public class EmacsActivity extends Activity
124 layout.addView (window.view); 132 layout.addView (window.view);
125 child.setConsumer (this); 133 child.setConsumer (this);
126 134
135 /* If the activity is iconified, send that to the window. */
136 if (isPaused)
137 window.noticeIconified ();
138
127 /* Invalidate the focus. */ 139 /* Invalidate the focus. */
128 invalidateFocus (); 140 invalidateFocus ();
129 } 141 }
@@ -148,6 +160,9 @@ public class EmacsActivity extends Activity
148 { 160 {
149 FrameLayout.LayoutParams params; 161 FrameLayout.LayoutParams params;
150 162
163 /* Set the theme to one without a title bar. */
164 setTheme (android.R.style.Theme_NoTitleBar);
165
151 params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, 166 params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
152 LayoutParams.MATCH_PARENT); 167 LayoutParams.MATCH_PARENT);
153 168
@@ -192,4 +207,24 @@ public class EmacsActivity extends Activity
192 207
193 invalidateFocus (); 208 invalidateFocus ();
194 } 209 }
210
211 @Override
212 public void
213 onPause ()
214 {
215 isPaused = true;
216
217 EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
218 super.onResume ();
219 }
220
221 @Override
222 public void
223 onResume ()
224 {
225 isPaused = false;
226
227 EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
228 super.onResume ();
229 }
195}; 230};
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index ae48ce30408..a11e509cd7f 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -96,11 +96,34 @@ public class EmacsNative
96 long time, int state, 96 long time, int state,
97 int button); 97 int button);
98 98
99 /* Send an ANDROID_BUTTON_RELEASE event. */ 99 /* Send an ANDROID_BUTTON_RELEASE event. */
100 public static native void sendButtonRelease (short window, int x, int y, 100 public static native void sendButtonRelease (short window, int x, int y,
101 long time, int state, 101 long time, int state,
102 int button); 102 int button);
103 103
104 /* Send an ANDROID_TOUCH_DOWN event. */
105 public static native void sendTouchDown (short window, int x, int y,
106 long time, int pointerID);
107
108 /* Send an ANDROID_TOUCH_UP event. */
109 public static native void sendTouchUp (short window, int x, int y,
110 long time, int pointerID);
111
112 /* Send an ANDROID_TOUCH_MOVE event. */
113 public static native void sendTouchMove (short window, int x, int y,
114 long time, int pointerID);
115
116 /* Send an ANDROID_WHEEL event. */
117 public static native void sendWheel (short window, int x, int y,
118 long time, int state,
119 float xDelta, float yDelta);
120
121 /* Send an ANDROID_ICONIFIED event. */
122 public static native void sendIconified (short window);
123
124 /* Send an ANDROID_DEICONIFIED event. */
125 public static native void sendDeiconified (short window);
126
104 static 127 static
105 { 128 {
106 System.loadLibrary ("emacs"); 129 System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 8a9426050ae..c0f24c7433a 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -149,8 +149,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
149 } 149 }
150 else 150 else
151 familyName = fileName; 151 familyName = fileName;
152
153 Log.d (TAG, "Initialized new typeface " + familyName);
154 } 152 }
155 153
156 @Override 154 @Override
@@ -321,17 +319,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
321 319
322 list = new LinkedList<FontEntity> (); 320 list = new LinkedList<FontEntity> ();
323 321
324 Log.d (TAG, ("Looking for fonts matching font spec: "
325 + fontSpec.toString ()));
326
327 for (i = 0; i < typefaceList.length; ++i) 322 for (i = 0; i < typefaceList.length; ++i)
328 { 323 {
329 if (checkMatch (typefaceList[i], fontSpec)) 324 if (checkMatch (typefaceList[i], fontSpec))
330 list.add (new Sdk7FontEntity (typefaceList[i])); 325 list.add (new Sdk7FontEntity (typefaceList[i]));
331 } 326 }
332 327
333 Log.d (TAG, "Found font entities: " + list.toString ());
334
335 return (FontEntity[]) list.toArray (new FontEntity[0]); 328 return (FontEntity[]) list.toArray (new FontEntity[0]);
336 } 329 }
337 330
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index 4444b7f1c56..01a1695f385 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -28,20 +28,27 @@ import android.graphics.Bitmap;
28import android.graphics.Point; 28import android.graphics.Point;
29 29
30import android.view.View; 30import android.view.View;
31import android.view.InputDevice;
31 32
32import android.annotation.TargetApi; 33import android.annotation.TargetApi;
33import android.app.Service; 34import android.app.Service;
34import android.content.Context; 35import android.content.Context;
35import android.content.Intent; 36import android.content.Intent;
36import android.content.res.AssetManager; 37import android.content.res.AssetManager;
38
37import android.os.Build; 39import android.os.Build;
38import android.os.Looper; 40import android.os.Looper;
39import android.os.IBinder; 41import android.os.IBinder;
40import android.os.Handler; 42import android.os.Handler;
43import android.os.Vibrator;
44import android.os.VibratorManager;
45import android.os.VibrationEffect;
41 46
42import android.util.Log; 47import android.util.Log;
43import android.util.DisplayMetrics; 48import android.util.DisplayMetrics;
44 49
50import android.hardware.input.InputManager;
51
45class Holder<T> 52class Holder<T>
46{ 53{
47 T thing; 54 T thing;
@@ -250,4 +257,113 @@ public class EmacsService extends Service
250 { 257 {
251 window.clearArea (x, y, width, height); 258 window.clearArea (x, y, width, height);
252 } 259 }
260
261 @SuppressWarnings ("deprecation")
262 public void
263 ringBell ()
264 {
265 Vibrator vibrator;
266 VibrationEffect effect;
267 VibratorManager vibratorManager;
268 Object tem;
269
270 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
271 {
272 tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
273 vibratorManager = (VibratorManager) tem;
274 vibrator = vibratorManager.getDefaultVibrator ();
275 }
276 else
277 vibrator
278 = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
279
280 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
281 {
282 effect
283 = VibrationEffect.createOneShot (50,
284 VibrationEffect.DEFAULT_AMPLITUDE);
285 vibrator.vibrate (effect);
286 }
287 else
288 vibrator.vibrate (50);
289 }
290
291 public short[]
292 queryTree (EmacsWindow window)
293 {
294 short[] array;
295 List<EmacsWindow> windowList;
296 int i;
297
298 if (window == null)
299 /* Just return all the windows without a parent. */
300 windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
301 else
302 windowList = window.children;
303
304 array = new short[windowList.size () + 1];
305 i = 1;
306
307 array[0] = window.parent != null ? 0 : window.parent.handle;
308
309 for (EmacsWindow treeWindow : windowList)
310 array[i++] = treeWindow.handle;
311
312 return array;
313 }
314
315 public int
316 getScreenWidth (boolean mmWise)
317 {
318 DisplayMetrics metrics;
319
320 metrics = getResources ().getDisplayMetrics ();
321
322 if (!mmWise)
323 return metrics.widthPixels;
324 else
325 return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
326 }
327
328 public int
329 getScreenHeight (boolean mmWise)
330 {
331 DisplayMetrics metrics;
332
333 metrics = getResources ().getDisplayMetrics ();
334
335 if (!mmWise)
336 return metrics.heightPixels;
337 else
338 return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
339 }
340
341 public boolean
342 detectMouse ()
343 {
344 InputManager manager;
345 InputDevice device;
346 int[] ids;
347 int i;
348
349 if (Build.VERSION.SDK_INT
350 < Build.VERSION_CODES.JELLY_BEAN)
351 return false;
352
353 manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
354 ids = manager.getInputDeviceIds ();
355
356 for (i = 0; i < ids.length; ++i)
357 {
358 device = manager.getInputDevice (ids[i]);
359
360 if (device == null)
361 continue;
362
363 if (device.supportsSource (InputDevice.SOURCE_MOUSE))
364 return true;
365 }
366
367 return false;
368 }
253}; 369};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java
index 5efb8882263..f713818d4bc 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -27,8 +27,12 @@ import android.os.Build;
27import android.graphics.Canvas; 27import android.graphics.Canvas;
28import android.graphics.Rect; 28import android.graphics.Rect;
29 29
30import android.util.Log;
31
30public class EmacsSurfaceView extends SurfaceView 32public class EmacsSurfaceView extends SurfaceView
31{ 33{
34 private static final String TAG = "EmacsSurfaceView";
35
32 public Object surfaceChangeLock; 36 public Object surfaceChangeLock;
33 private boolean created; 37 private boolean created;
34 38
@@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView
45 surfaceChanged (SurfaceHolder holder, int format, 49 surfaceChanged (SurfaceHolder holder, int format,
46 int width, int height) 50 int width, int height)
47 { 51 {
52 Log.d (TAG, "surfaceChanged: " + view);
48 view.swapBuffers (); 53 view.swapBuffers ();
49 } 54 }
50 55
@@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView
54 { 59 {
55 synchronized (surfaceChangeLock) 60 synchronized (surfaceChangeLock)
56 { 61 {
62 Log.d (TAG, "surfaceCreated: " + view);
57 created = true; 63 created = true;
58 view.swapBuffers ();
59 } 64 }
65
66 /* Drop the lock when doing this, or a deadlock can
67 result. */
68 view.swapBuffers ();
60 } 69 }
61 70
62 @Override 71 @Override
@@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView
65 { 74 {
66 synchronized (surfaceChangeLock) 75 synchronized (surfaceChangeLock)
67 { 76 {
77 Log.d (TAG, "surfaceDestroyed: " + view);
68 created = false; 78 created = false;
69 } 79 }
70 } 80 }
@@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView
93 return holder.lockCanvas (damage); 103 return holder.lockCanvas (damage);
94 } 104 }
95 105
106 @Override
107 protected void
108 onLayout (boolean changed, int left, int top, int right,
109 int bottom)
110 {
111 Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
112 + " " + bottom + " -- " + changed + " visibility "
113 + getVisibility ()));
114 }
115
96 /* This method is only used during debugging when it seems damage 116 /* This method is only used during debugging when it seems damage
97 isn't working correctly. */ 117 isn't working correctly. */
98 118
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index 169a1e42ee3..41acabab97b 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -87,12 +87,27 @@ public class EmacsView extends ViewGroup
87 87
88 /* Create the surface view. */ 88 /* Create the surface view. */
89 this.surfaceView = new EmacsSurfaceView (this); 89 this.surfaceView = new EmacsSurfaceView (this);
90 this.surfaceView.setZOrderMediaOverlay (true);
90 addView (this.surfaceView); 91 addView (this.surfaceView);
92
93 /* Not sure exactly what this does but it makes things magically
94 work. Why is something as simple as XRaiseWindow so involved
95 on Android? */
96 setChildrenDrawingOrderEnabled (true);
97
98 /* Get rid of the foreground and background tint. */
99 setBackgroundTintList (null);
100 setForegroundTintList (null);
91 } 101 }
92 102
93 private void 103 private void
94 handleDirtyBitmap () 104 handleDirtyBitmap ()
95 { 105 {
106 Bitmap oldBitmap;
107
108 /* Save the old bitmap. */
109 oldBitmap = bitmap;
110
96 /* Recreate the front and back buffer bitmaps. */ 111 /* Recreate the front and back buffer bitmaps. */
97 bitmap 112 bitmap
98 = Bitmap.createBitmap (bitmapDirty.width (), 113 = Bitmap.createBitmap (bitmapDirty.width (),
@@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup
103 /* And canvases. */ 118 /* And canvases. */
104 canvas = new Canvas (bitmap); 119 canvas = new Canvas (bitmap);
105 120
106 /* If Emacs is drawing to the bitmap right now from the 121 /* Copy over the contents of the old bitmap. */
107 main thread, the image contents are lost until the next 122 if (oldBitmap != null)
108 ConfigureNotify and complete garbage. Sorry! */ 123 canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
124
109 bitmapDirty = null; 125 bitmapDirty = null;
110 } 126 }
111 127
128 public synchronized void
129 explicitlyDirtyBitmap (Rect rect)
130 {
131 if (bitmapDirty == null
132 && (bitmap == null
133 || rect.width () != bitmap.getWidth ()
134 || rect.height () != bitmap.getHeight ()))
135 bitmapDirty = rect;
136 }
137
112 public synchronized Bitmap 138 public synchronized Bitmap
113 getBitmap () 139 getBitmap ()
114 { 140 {
@@ -168,25 +194,31 @@ public class EmacsView extends ViewGroup
168 View child; 194 View child;
169 Rect windowRect; 195 Rect windowRect;
170 196
197 count = getChildCount ();
198
171 if (changed || mustReportLayout) 199 if (changed || mustReportLayout)
172 { 200 {
173 mustReportLayout = false; 201 mustReportLayout = false;
174 window.viewLayout (left, top, right, bottom); 202 window.viewLayout (left, top, right, bottom);
175 } 203 }
176 204
177 if (changed) 205 if (changed
206 /* Check that a change has really happened. */
207 && (bitmapDirty == null
208 || bitmapDirty.width () != right - left
209 || bitmapDirty.height () != bottom - top))
178 bitmapDirty = new Rect (left, top, right, bottom); 210 bitmapDirty = new Rect (left, top, right, bottom);
179 211
180 count = getChildCount ();
181
182 for (i = 0; i < count; ++i) 212 for (i = 0; i < count; ++i)
183 { 213 {
184 child = getChildAt (i); 214 child = getChildAt (i);
185 215
216 Log.d (TAG, "onLayout: " + child);
217
186 if (child == surfaceView) 218 if (child == surfaceView)
187 /* The child is the surface view, so give it the entire 219 /* The child is the surface view, so give it the entire
188 view. */ 220 view. */
189 child.layout (left, top, right, bottom); 221 child.layout (0, 0, right - left, bottom - top);
190 else if (child.getVisibility () != GONE) 222 else if (child.getVisibility () != GONE)
191 { 223 {
192 if (!(child instanceof EmacsView)) 224 if (!(child instanceof EmacsView))
@@ -201,59 +233,68 @@ public class EmacsView extends ViewGroup
201 } 233 }
202 } 234 }
203 235
204 public synchronized void 236 public void
205 damageRect (Rect damageRect) 237 damageRect (Rect damageRect)
206 { 238 {
207 damageRegion.union (damageRect); 239 synchronized (damageRegion)
240 {
241 damageRegion.union (damageRect);
242 }
208 } 243 }
209 244
210 /* This method is called from both the UI thread and the Emacs 245 /* This method is called from both the UI thread and the Emacs
211 thread. */ 246 thread. */
212 247
213 public synchronized void 248 public void
214 swapBuffers (boolean force) 249 swapBuffers (boolean force)
215 { 250 {
216 Canvas canvas; 251 Canvas canvas;
217 Rect damageRect; 252 Rect damageRect;
218 Bitmap bitmap; 253 Bitmap bitmap;
219 254
220 if (damageRegion.isEmpty ()) 255 /* Code must always take damageRegion, and then surfaceChangeLock,
221 return; 256 never the other way around! */
222 257
223 bitmap = getBitmap (); 258 synchronized (damageRegion)
259 {
260 if (damageRegion.isEmpty ())
261 return;
224 262
225 /* Emacs must take the following lock to ensure the access to the 263 bitmap = getBitmap ();
226 canvas occurs with the surface created. Otherwise, Android
227 will throttle calls to lockCanvas. */
228 264
229 synchronized (surfaceView.surfaceChangeLock) 265 /* Emacs must take the following lock to ensure the access to the
230 { 266 canvas occurs with the surface created. Otherwise, Android
231 damageRect = damageRegion.getBounds (); 267 will throttle calls to lockCanvas. */
232 268
233 if (!surfaceView.isCreated ()) 269 synchronized (surfaceView.surfaceChangeLock)
234 return; 270 {
271 damageRect = damageRegion.getBounds ();
235 272
236 if (bitmap == null) 273 if (!surfaceView.isCreated ())
237 return; 274 return;
238 275
239 /* Lock the canvas with the specified damage. */ 276 if (bitmap == null)
240 canvas = surfaceView.lockCanvas (damageRect); 277 return;
241 278
242 /* Return if locking the canvas failed. */ 279 /* Lock the canvas with the specified damage. */
243 if (canvas == null) 280 canvas = surfaceView.lockCanvas (damageRect);
244 return;
245 281
246 /* Copy from the back buffer to the canvas. If damageRect was 282 /* Return if locking the canvas failed. */
247 made empty, then draw the entire back buffer. */ 283 if (canvas == null)
284 return;
248 285
249 if (damageRect.isEmpty ()) 286 /* Copy from the back buffer to the canvas. If damageRect was
250 canvas.drawBitmap (bitmap, 0f, 0f, paint); 287 made empty, then draw the entire back buffer. */
251 else
252 canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
253 288
254 /* Unlock the canvas and clear the damage. */ 289 if (damageRect.isEmpty ())
255 surfaceView.unlockCanvasAndPost (canvas); 290 canvas.drawBitmap (bitmap, 0f, 0f, paint);
256 damageRegion.setEmpty (); 291 else
292 canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
293
294 /* Unlock the canvas and clear the damage. */
295 surfaceView.unlockCanvasAndPost (canvas);
296 damageRegion.setEmpty ();
297 }
257 } 298 }
258 } 299 }
259 300
@@ -308,6 +349,78 @@ public class EmacsView extends ViewGroup
308 public boolean 349 public boolean
309 onTouchEvent (MotionEvent motion) 350 onTouchEvent (MotionEvent motion)
310 { 351 {
311 return window.onSomeKindOfMotionEvent (motion); 352 return window.onTouchEvent (motion);
353 }
354
355 private void
356 moveChildToBack (View child)
357 {
358 int index;
359
360 index = indexOfChild (child);
361
362 if (index > 0)
363 {
364 detachViewFromParent (index);
365
366 /* The view at 0 is the surface view. */
367 attachViewToParent (child, 1,
368 child.getLayoutParams());
369 }
370 }
371
372
373 /* The following two functions must not be called if the view has no
374 parent, or is parented to an activity. */
375
376 public void
377 raise ()
378 {
379 EmacsView parent;
380
381 parent = (EmacsView) getParent ();
382
383 Log.d (TAG, "raise: parent " + parent);
384
385 if (parent.indexOfChild (this)
386 == parent.getChildCount () - 1)
387 return;
388
389 parent.bringChildToFront (this);
390
391 /* Yes, all of this is really necessary! */
392 parent.requestLayout ();
393 parent.invalidate ();
394 requestLayout ();
395 invalidate ();
396
397 /* The surface view must be destroyed and recreated. */
398 removeView (surfaceView);
399 addView (surfaceView, 0);
400 }
401
402 public void
403 lower ()
404 {
405 EmacsView parent;
406
407 parent = (EmacsView) getParent ();
408
409 Log.d (TAG, "lower: parent " + parent);
410
411 if (parent.indexOfChild (this) == 1)
412 return;
413
414 parent.moveChildToBack (this);
415
416 /* Yes, all of this is really necessary! */
417 parent.requestLayout ();
418 parent.invalidate ();
419 requestLayout ();
420 invalidate ();
421
422 /* The surface view must be removed and attached again. */
423 removeView (surfaceView);
424 addView (surfaceView, 0);
312 } 425 }
313}; 426};
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index a9a24b61869..1f8596dba50 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -22,6 +22,7 @@ package org.gnu.emacs;
22import java.lang.IllegalStateException; 22import java.lang.IllegalStateException;
23import java.util.ArrayList; 23import java.util.ArrayList;
24import java.util.List; 24import java.util.List;
25import java.util.HashMap;
25 26
26import android.graphics.Rect; 27import android.graphics.Rect;
27import android.graphics.Canvas; 28import android.graphics.Canvas;
@@ -50,9 +51,29 @@ import android.os.Build;
50 Views are also drawables, meaning they can accept drawing 51 Views are also drawables, meaning they can accept drawing
51 requests. */ 52 requests. */
52 53
54/* Help wanted. What does not work includes `EmacsView.raise',
55 `EmacsView.lower', reparenting a window onto another window.
56
57 All three are likely undocumented restrictions within
58 EmacsSurface. */
59
53public class EmacsWindow extends EmacsHandleObject 60public class EmacsWindow extends EmacsHandleObject
54 implements EmacsDrawable 61 implements EmacsDrawable
55{ 62{
63 private static final String TAG = "EmacsWindow";
64
65 private class Coordinate
66 {
67 /* Integral coordinate. */
68 int x, y;
69
70 Coordinate (int x, int y)
71 {
72 this.x = x;
73 this.y = y;
74 }
75 };
76
56 /* The view associated with the window. */ 77 /* The view associated with the window. */
57 public EmacsView view; 78 public EmacsView view;
58 79
@@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject
60 private Rect rect; 81 private Rect rect;
61 82
62 /* The parent window, or null if it is the root window. */ 83 /* The parent window, or null if it is the root window. */
63 private EmacsWindow parent; 84 public EmacsWindow parent;
64 85
65 /* List of all children in stacking order. This must be kept 86 /* List of all children in stacking order. This must be kept
66 consistent! */ 87 consistent! */
67 public ArrayList<EmacsWindow> children; 88 public ArrayList<EmacsWindow> children;
68 89
90 /* Map between pointer identifiers and last known position. Used to
91 compute which pointer changed upon a touch event. */
92 private HashMap<Integer, Coordinate> pointerMap;
93
69 /* The window consumer currently attached, if it exists. */ 94 /* The window consumer currently attached, if it exists. */
70 private EmacsWindowAttachmentManager.WindowConsumer attached; 95 private EmacsWindowAttachmentManager.WindowConsumer attached;
71 96
@@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject
77 last button press or release event. */ 102 last button press or release event. */
78 private int lastButtonState, lastModifiers; 103 private int lastButtonState, lastModifiers;
79 104
105 /* Whether or not the window is mapped, and whether or not it is
106 deiconified. */
107 private boolean isMapped, isIconified;
108
109 /* Whether or not to ask for focus upon being mapped, and whether or
110 not the window should be focusable. */
111 private boolean dontFocusOnMap, dontAcceptFocus;
112
80 public 113 public
81 EmacsWindow (short handle, final EmacsWindow parent, int x, int y, 114 EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
82 int width, int height) 115 int width, int height)
@@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject
84 super (handle); 117 super (handle);
85 118
86 rect = new Rect (x, y, x + width, y + height); 119 rect = new Rect (x, y, x + width, y + height);
120 pointerMap = new HashMap<Integer, Coordinate> ();
87 121
88 /* Create the view from the context's UI thread. The window is 122 /* Create the view from the context's UI thread. The window is
89 unmapped, so the view is GONE. */ 123 unmapped, so the view is GONE. */
@@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject
97 if (parent != null) 131 if (parent != null)
98 { 132 {
99 parent.children.add (this); 133 parent.children.add (this);
100 parent.view.post (new Runnable () { 134 EmacsService.SERVICE.runOnUiThread (new Runnable () {
101 @Override 135 @Override
102 public void 136 public void
103 run () 137 run ()
@@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject
106 } 140 }
107 }); 141 });
108 } 142 }
109 else
110 EmacsService.SERVICE.runOnUiThread (new Runnable () {
111 @Override
112 public void
113 run ()
114 {
115 EmacsWindowAttachmentManager manager;
116
117 manager = EmacsWindowAttachmentManager.MANAGER;
118
119 /* If parent is the root window, notice that there are new
120 children available for interested activites to pick
121 up. */
122
123 manager.registerWindow (EmacsWindow.this);
124 }
125 });
126 143
127 scratchGC = new EmacsGC ((short) 0); 144 scratchGC = new EmacsGC ((short) 0);
128 } 145 }
@@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject
159 + "children!"); 176 + "children!");
160 177
161 /* Remove the view from its parent and make it invisible. */ 178 /* Remove the view from its parent and make it invisible. */
162 view.post (new Runnable () { 179 EmacsService.SERVICE.runOnUiThread (new Runnable () {
163 public void 180 public void
164 run () 181 run ()
165 { 182 {
@@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject
174 191
175 parent = (View) view.getParent (); 192 parent = (View) view.getParent ();
176 193
177 if (parent != null && attached == null) 194 if (parent != null)
178 ((ViewGroup) parent).removeView (view); 195 ((ViewGroup) parent).removeView (view);
179 196
180 manager.detachWindow (EmacsWindow.this); 197 manager.detachWindow (EmacsWindow.this);
@@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject
199 public void 216 public void
200 viewLayout (int left, int top, int right, int bottom) 217 viewLayout (int left, int top, int right, int bottom)
201 { 218 {
219 int rectWidth, rectHeight;
220
202 synchronized (this) 221 synchronized (this)
203 { 222 {
204 rect.left = left; 223 rect.left = left;
205 rect.top = top; 224 rect.top = top;
206 rect.right = right; 225 rect.right = right;
207 rect.bottom = bottom; 226 rect.bottom = bottom;
208
209 EmacsNative.sendConfigureNotify (this.handle,
210 System.currentTimeMillis (),
211 left, top, rect.width (),
212 rect.height ());
213 } 227 }
228
229 rectWidth = right - left;
230 rectHeight = bottom - top;
231
232 EmacsNative.sendConfigureNotify (this.handle,
233 System.currentTimeMillis (),
234 left, top, rectWidth,
235 rectHeight);
214 } 236 }
215 237
216 public void 238 public void
217 requestViewLayout () 239 requestViewLayout ()
218 { 240 {
219 view.post (new Runnable () { 241 /* This is necessary because otherwise subsequent drawing on the
242 Emacs thread may be lost. */
243 view.explicitlyDirtyBitmap (rect);
244
245 EmacsService.SERVICE.runOnUiThread (new Runnable () {
220 @Override 246 @Override
221 public void 247 public void
222 run () 248 run ()
@@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject
261 public void 287 public void
262 mapWindow () 288 mapWindow ()
263 { 289 {
264 view.post (new Runnable () { 290 if (isMapped)
265 @Override 291 return;
266 public void
267 run ()
268 {
269 292
270 view.setVisibility (View.VISIBLE); 293 isMapped = true;
271 /* Eventually this should check no-focus-on-map. */ 294
272 view.requestFocus (); 295 if (parent == null)
273 } 296 {
274 }); 297 EmacsService.SERVICE.runOnUiThread (new Runnable () {
298 @Override
299 public void
300 run ()
301 {
302 EmacsWindowAttachmentManager manager;
303
304 /* Make the view visible, first of all. */
305 view.setVisibility (View.VISIBLE);
306
307 manager = EmacsWindowAttachmentManager.MANAGER;
308
309 /* If parent is the root window, notice that there are new
310 children available for interested activites to pick
311 up. */
312 manager.registerWindow (EmacsWindow.this);
313
314 if (!getDontFocusOnMap ())
315 /* Eventually this should check no-focus-on-map. */
316 view.requestFocus ();
317 }
318 });
319 }
320 else
321 {
322 /* Do the same thing as above, but don't register this
323 window. */
324 EmacsService.SERVICE.runOnUiThread (new Runnable () {
325 @Override
326 public void
327 run ()
328 {
329 view.setVisibility (View.VISIBLE);
330
331 if (!getDontFocusOnMap ())
332 /* Eventually this should check no-focus-on-map. */
333 view.requestFocus ();
334 }
335 });
336 }
275 } 337 }
276 338
277 public void 339 public void
278 unmapWindow () 340 unmapWindow ()
279 { 341 {
342 if (!isMapped)
343 return;
344
345 isMapped = false;
346
280 view.post (new Runnable () { 347 view.post (new Runnable () {
281 @Override 348 @Override
282 public void 349 public void
283 run () 350 run ()
284 { 351 {
352 EmacsWindowAttachmentManager manager;
353
354 manager = EmacsWindowAttachmentManager.MANAGER;
355
285 view.setVisibility (View.GONE); 356 view.setVisibility (View.GONE);
357
358 /* Now that the window is unmapped, unregister it as
359 well. */
360 manager.detachWindow (EmacsWindow.this);
286 } 361 }
287 }); 362 });
288 } 363 }
@@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject
413 return 4; 488 return 4;
414 } 489 }
415 490
491 /* Return the ID of the pointer which changed in EVENT. Value is -1
492 if it could not be determined, else the pointer that changed, or
493 -2 if -1 would have been returned, but there is also a pointer
494 that is a mouse. */
495
496 private int
497 figureChange (MotionEvent event)
498 {
499 int pointerID, i, truncatedX, truncatedY, pointerIndex;
500 Coordinate coordinate;
501 boolean mouseFlag;
502
503 /* pointerID is always initialized but the Java compiler is too
504 dumb to know that. */
505 pointerID = -1;
506 mouseFlag = false;
507
508 switch (event.getActionMasked ())
509 {
510 case MotionEvent.ACTION_DOWN:
511 /* Primary pointer pressed with index 0. */
512
513 /* Detect mice. If this is a mouse event, give it to
514 onSomeKindOfMotionEvent. */
515 if ((Build.VERSION.SDK_INT
516 >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
517 && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
518 return -2;
519
520 pointerID = event.getPointerId (0);
521 pointerMap.put (pointerID,
522 new Coordinate ((int) event.getX (0),
523 (int) event.getY (0)));
524 break;
525
526 case MotionEvent.ACTION_UP:
527 /* Primary pointer released with index 0. */
528 pointerID = event.getPointerId (0);
529 pointerMap.remove (pointerID);
530 break;
531
532 case MotionEvent.ACTION_POINTER_DOWN:
533 /* New pointer. Find the pointer ID from the index and place
534 it in the map. */
535 pointerIndex = event.getActionIndex ();
536 pointerID = event.getPointerId (pointerIndex);
537 pointerMap.put (pointerID,
538 new Coordinate ((int) event.getX (pointerID),
539 (int) event.getY (pointerID)));
540 break;
541
542 case MotionEvent.ACTION_POINTER_UP:
543 /* Pointer removed. Remove it from the map. */
544 pointerIndex = event.getActionIndex ();
545 pointerID = event.getPointerId (pointerIndex);
546 pointerMap.remove (pointerID);
547 break;
548
549 default:
550
551 /* Loop through each pointer in the event. */
552 for (i = 0; i < event.getPointerCount (); ++i)
553 {
554 pointerID = event.getPointerId (i);
555
556 /* Look up that pointer in the map. */
557 coordinate = pointerMap.get (pointerID);
558
559 if (coordinate != null)
560 {
561 /* See if coordinates have changed. */
562 truncatedX = (int) event.getX (i);
563 truncatedY = (int) event.getY (i);
564
565 if (truncatedX != coordinate.x
566 || truncatedY != coordinate.y)
567 {
568 /* The pointer changed. Update the coordinate and
569 break out of the loop. */
570 coordinate.x = truncatedX;
571 coordinate.y = truncatedY;
572
573 break;
574 }
575 }
576
577 /* See if this is a mouse. If so, set the mouseFlag. */
578 if ((Build.VERSION.SDK_INT
579 >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
580 && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
581 mouseFlag = true;
582 }
583
584 /* Set the pointer ID to -1 if the loop failed to find any
585 changed pointer. If a mouse pointer was found, set it to
586 -2. */
587 if (i == event.getPointerCount ())
588 pointerID = (mouseFlag ? -2 : -1);
589 }
590
591 /* Return the pointer ID. */
592 return pointerID;
593 }
594
595 public boolean
596 onTouchEvent (MotionEvent event)
597 {
598 int pointerID, index;
599
600 /* Extract the ``touch ID'' (or in Android, the ``pointer
601 ID''.) */
602 pointerID = figureChange (event);
603
604 if (pointerID < 0)
605 {
606 /* If this is a mouse event, give it to
607 onSomeKindOfMotionEvent. */
608 if (pointerID == -2)
609 return onSomeKindOfMotionEvent (event);
610
611 return false;
612 }
613
614 /* Find the pointer index corresponding to the event. */
615 index = event.findPointerIndex (pointerID);
616
617 switch (event.getActionMasked ())
618 {
619 case MotionEvent.ACTION_DOWN:
620 case MotionEvent.ACTION_POINTER_DOWN:
621 /* Touch down event. */
622 EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
623 (int) event.getY (index),
624 event.getEventTime (), pointerID);
625 return true;
626
627 case MotionEvent.ACTION_UP:
628 case MotionEvent.ACTION_POINTER_UP:
629 /* Touch up event. */
630 EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
631 (int) event.getY (index),
632 event.getEventTime (), pointerID);
633 return true;
634
635 case MotionEvent.ACTION_MOVE:
636 /* Pointer motion event. */
637 EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
638 (int) event.getY (index),
639 event.getEventTime (), pointerID);
640 return true;
641 }
642
643 return false;
644 }
645
416 public boolean 646 public boolean
417 onSomeKindOfMotionEvent (MotionEvent event) 647 onSomeKindOfMotionEvent (MotionEvent event)
418 { 648 {
@@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject
472 702
473 case MotionEvent.ACTION_DOWN: 703 case MotionEvent.ACTION_DOWN:
474 case MotionEvent.ACTION_UP: 704 case MotionEvent.ACTION_UP:
475 /* Emacs must return true even though touch events are not yet 705 /* Emacs must return true even though touch events are not
476 handled, because the value of this function is used by the 706 handled here, because the value of this function is used by
477 system to decide whether or not Emacs gets ACTION_MOVE 707 the system to decide whether or not Emacs gets ACTION_MOVE
478 events. */ 708 events. */
479 return true; 709 return true;
710
711 case MotionEvent.ACTION_SCROLL:
712 /* Send a scroll event with the specified deltas. */
713 EmacsNative.sendWheel (this.handle, (int) event.getX (),
714 (int) event.getY (),
715 event.getEventTime (),
716 lastModifiers,
717 event.getAxisValue (MotionEvent.AXIS_HSCROLL),
718 event.getAxisValue (MotionEvent.AXIS_VSCROLL));
719 return true;
480 } 720 }
481 721
482 return false; 722 return false;
483 } 723 }
724
725 public void
726 reparentTo (final EmacsWindow otherWindow, int x, int y)
727 {
728 int width, height;
729
730 /* Reparent this window to the other window. */
731
732 if (parent != null)
733 parent.children.remove (this);
734
735 if (otherWindow != null)
736 otherWindow.children.add (this);
737
738 parent = otherWindow;
739
740 /* Move this window to the new location. */
741 synchronized (this)
742 {
743 width = rect.width ();
744 height = rect.height ();
745 rect.left = x;
746 rect.top = y;
747 rect.right = x + width;
748 rect.bottom = y + height;
749 }
750
751 /* Now do the work necessary on the UI thread to reparent the
752 window. */
753 EmacsService.SERVICE.runOnUiThread (new Runnable () {
754 @Override
755 public void
756 run ()
757 {
758 EmacsWindowAttachmentManager manager;
759 View parent;
760
761 /* First, detach this window if necessary. */
762 manager = EmacsWindowAttachmentManager.MANAGER;
763 manager.detachWindow (EmacsWindow.this);
764
765 /* Also unparent this view. */
766 parent = (View) view.getParent ();
767
768 if (parent != null)
769 ((ViewGroup) parent).removeView (view);
770
771 /* Next, either add this window as a child of the new
772 parent's view, or make it available again. */
773 if (otherWindow != null)
774 otherWindow.view.addView (view);
775 else if (EmacsWindow.this.isMapped)
776 manager.registerWindow (EmacsWindow.this);
777
778 /* Request relayout. */
779 view.requestLayout ();
780 }
781 });
782 }
783
784 public void
785 makeInputFocus (long time)
786 {
787 /* TIME is currently ignored. Request the input focus now. */
788
789 EmacsService.SERVICE.runOnUiThread (new Runnable () {
790 @Override
791 public void
792 run ()
793 {
794 view.requestFocus ();
795 }
796 });
797 }
798
799 public void
800 raise ()
801 {
802 /* This does nothing here. */
803 if (parent == null)
804 return;
805
806 /* Remove and add this view again. */
807 parent.children.remove (this);
808 parent.children.add (this);
809
810 /* Request a relayout. */
811 EmacsService.SERVICE.runOnUiThread (new Runnable () {
812 @Override
813 public void
814 run ()
815 {
816 view.raise ();
817 }
818 });
819 }
820
821 public void
822 lower ()
823 {
824 /* This does nothing here. */
825 if (parent == null)
826 return;
827
828 /* Remove and add this view again. */
829 parent.children.remove (this);
830 parent.children.add (this);
831
832 /* Request a relayout. */
833 EmacsService.SERVICE.runOnUiThread (new Runnable () {
834 @Override
835 public void
836 run ()
837 {
838 view.lower ();
839 }
840 });
841 }
842
843 public int[]
844 getWindowGeometry ()
845 {
846 int[] array;
847 Rect rect;
848
849 array = new int[4];
850 rect = getGeometry ();
851
852 array[0] = rect.left;
853 array[1] = rect.top;
854 array[2] = rect.width ();
855 array[3] = rect.height ();
856
857 return array;
858 }
859
860 public void
861 noticeIconified ()
862 {
863 isIconified = true;
864 EmacsNative.sendIconified (this.handle);
865 }
866
867 public void
868 noticeDeiconified ()
869 {
870 isIconified = false;
871 EmacsNative.sendDeiconified (this.handle);
872 }
873
874 public synchronized void
875 setDontAcceptFocus (final boolean dontAcceptFocus)
876 {
877 this.dontAcceptFocus = dontAcceptFocus;
878
879 /* Update the view's focus state. */
880 EmacsService.SERVICE.runOnUiThread (new Runnable () {
881 @Override
882 public void
883 run ()
884 {
885 view.setFocusable (!dontAcceptFocus);
886 view.setFocusableInTouchMode (!dontAcceptFocus);
887 }
888 });
889 }
890
891 public synchronized void
892 setDontFocusOnMap (final boolean dontFocusOnMap)
893 {
894 this.dontFocusOnMap = dontFocusOnMap;
895 }
896
897 public synchronized boolean
898 getDontFocusOnMap ()
899 {
900 return dontFocusOnMap;
901 }
484}; 902};
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
index 34be2ab8789..15eb3bb65c2 100644
--- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -19,6 +19,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19 19
20package org.gnu.emacs; 20package org.gnu.emacs;
21 21
22import java.util.ArrayList;
22import java.util.LinkedList; 23import java.util.LinkedList;
23import java.util.List; 24import java.util.List;
24 25
@@ -68,7 +69,7 @@ public class EmacsWindowAttachmentManager
68 }; 69 };
69 70
70 private List<WindowConsumer> consumers; 71 private List<WindowConsumer> consumers;
71 private List<EmacsWindow> windows; 72 public List<EmacsWindow> windows;
72 73
73 public 74 public
74 EmacsWindowAttachmentManager () 75 EmacsWindowAttachmentManager ()
@@ -98,12 +99,19 @@ public class EmacsWindowAttachmentManager
98 EmacsNative.sendWindowAction ((short) 0, 0); 99 EmacsNative.sendWindowAction ((short) 0, 0);
99 } 100 }
100 101
101 public void 102 public synchronized void
102 registerWindow (EmacsWindow window) 103 registerWindow (EmacsWindow window)
103 { 104 {
104 Intent intent; 105 Intent intent;
105 106
106 Log.d (TAG, "registerWindow " + window); 107 Log.d (TAG, "registerWindow (maybe): " + window);
108
109 if (windows.contains (window))
110 /* The window is already registered. */
111 return;
112
113 Log.d (TAG, "registerWindow: " + window);
114
107 windows.add (window); 115 windows.add (window);
108 116
109 for (WindowConsumer consumer : consumers) 117 for (WindowConsumer consumer : consumers)
@@ -146,7 +154,7 @@ public class EmacsWindowAttachmentManager
146 consumers.remove (consumer); 154 consumers.remove (consumer);
147 } 155 }
148 156
149 public void 157 public synchronized void
150 detachWindow (EmacsWindow window) 158 detachWindow (EmacsWindow window)
151 { 159 {
152 WindowConsumer consumer; 160 WindowConsumer consumer;
@@ -162,5 +170,43 @@ public class EmacsWindowAttachmentManager
162 consumers.remove (consumer); 170 consumers.remove (consumer);
163 consumer.destroy (); 171 consumer.destroy ();
164 } 172 }
173
174 windows.remove (window);
175 }
176
177 public void
178 noticeIconified (WindowConsumer consumer)
179 {
180 EmacsWindow window;
181
182 Log.d (TAG, "noticeIconified " + consumer);
183
184 /* If a window is attached, send the appropriate iconification
185 events. */
186 window = consumer.getAttachedWindow ();
187
188 if (window != null)
189 window.noticeIconified ();
190 }
191
192 public void
193 noticeDeiconified (WindowConsumer consumer)
194 {
195 EmacsWindow window;
196
197 Log.d (TAG, "noticeDeiconified " + consumer);
198
199 /* If a window is attached, send the appropriate iconification
200 events. */
201 window = consumer.getAttachedWindow ();
202
203 if (window != null)
204 window.noticeDeiconified ();
205 }
206
207 public synchronized List<EmacsWindow>
208 copyWindows ()
209 {
210 return new ArrayList<EmacsWindow> (windows);
165 } 211 }
166}; 212};