diff options
| author | Po Lu | 2023-01-13 15:53:08 +0800 |
|---|---|---|
| committer | Po Lu | 2023-01-13 15:53:08 +0800 |
| commit | f9732131cf3c67e24db74a3d49f256d3c189a7e3 (patch) | |
| tree | e44e097c0c4c513390f58fa0020c8662943fbac6 /java | |
| parent | 2fa5583d96fe78ff66d6fd41f18e54e4e20ea7d6 (diff) | |
| download | emacs-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.in | 84 | ||||
| -rw-r--r-- | java/Makefile.in | 30 | ||||
| -rwxr-xr-x | java/debug.sh | 45 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsActivity.java | 37 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsNative.java | 25 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsSdk7FontDriver.java | 7 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 116 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsSurfaceView.java | 22 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsView.java | 189 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsWindow.java | 498 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsWindowAttachmentManager.java | 54 |
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 | |||
| 3 | Copyright (C) 2023 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or | ||
| 10 | (at your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along 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 | ||
| 20 | top_builddir = @top_builddir@ | 20 | top_builddir = @top_builddir@ |
| 21 | top_srcdir = @top_srcdir@ | ||
| 22 | version = @version@ | ||
| 21 | 23 | ||
| 22 | -include ${top_builddir}/src/verbose.mk | 24 | -include ${top_builddir}/src/verbose.mk |
| 23 | 25 | ||
| 24 | SHELL = @SHELL@ | 26 | SHELL = @SHELL@ |
| 25 | JAVAC = @JAVAC@ | 27 | JAVAC = @JAVAC@ |
| 26 | AAPT = @AAPT@ | 28 | AAPT = @AAPT@ |
| 27 | DX = @DX@ | 29 | D8 = @D8@ |
| 28 | ZIPALIGN = @ZIPALIGN@ | 30 | ZIPALIGN = @ZIPALIGN@ |
| 29 | JARSIGNER = @JARSIGNER@ | 31 | JARSIGNER = @JARSIGNER@ |
| 30 | ANDROID_JAR = @ANDROID_JAR@ | 32 | ANDROID_JAR = @ANDROID_JAR@ |
| @@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1 | |||
| 39 | JAVA_FILES = $(shell find . -type f -name *.java) | 41 | JAVA_FILES = $(shell find . -type f -name *.java) |
| 40 | CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class) | 42 | CLASS_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 | |||
| 47 | ANDROID_MIN_SDK = @ANDROID_MIN_SDK@ | ||
| 48 | APK_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 |
| 58 | all: emacs.apk | 66 | all: $(APK_NAME) |
| 59 | 67 | ||
| 60 | # Binaries to cross-compile. | 68 | # Binaries to cross-compile. |
| 61 | CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \ | 69 | CROSS_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 $@) | ||
| 133 | Makefile: ../config.status $(top_builddir)/java/Makefile.in | ||
| 134 | $(MAKE) -C .. java/$@ | ||
| 135 | |||
| 136 | # AndroidManifest.xml: | ||
| 137 | AndroidManifest.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 | ||
| 128 | classes.dex: $(CLASS_FILES) | 148 | classes.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 | ||
| 139 | emacs.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 | ||
| 146 | clean: | 166 | clean: |
| 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 | |||
| 30 | gdb_port=5039 | 30 | gdb_port=5039 |
| 31 | jdb_port=64013 | 31 | jdb_port=64013 |
| 32 | jdb=no | 32 | jdb=no |
| 33 | attach_existing=no | ||
| 33 | 34 | ||
| 34 | while [ $# -gt 0 ]; do | 35 | while [ $# -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. | 128 | if [ "$attach_existing" != "yes" ]; then |
| 124 | for 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..." |
| 127 | done | 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 |
| 131 | echo "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 | |
| 134 | adb -s $device shell am start -D "$package/$activity" | 139 | # Exit if the activity could not be started. |
| 135 | if [ ! $? ]; then | 140 | adb -s $device shell am start -D "$package/$activity" |
| 136 | exit 1; | 141 | if [ ! $? ]; then |
| 137 | fi | 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. |
| 140 | package_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. |
| 143 | package_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` |
| 153 | fi | ||
| 147 | 154 | ||
| 148 | pid=$package_pids | 155 | pid=$package_pids |
| 149 | num_pids=`wc -w <<< "$package_pids"` | 156 | num_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; | |||
| 28 | import android.graphics.Point; | 28 | import android.graphics.Point; |
| 29 | 29 | ||
| 30 | import android.view.View; | 30 | import android.view.View; |
| 31 | import android.view.InputDevice; | ||
| 31 | 32 | ||
| 32 | import android.annotation.TargetApi; | 33 | import android.annotation.TargetApi; |
| 33 | import android.app.Service; | 34 | import android.app.Service; |
| 34 | import android.content.Context; | 35 | import android.content.Context; |
| 35 | import android.content.Intent; | 36 | import android.content.Intent; |
| 36 | import android.content.res.AssetManager; | 37 | import android.content.res.AssetManager; |
| 38 | |||
| 37 | import android.os.Build; | 39 | import android.os.Build; |
| 38 | import android.os.Looper; | 40 | import android.os.Looper; |
| 39 | import android.os.IBinder; | 41 | import android.os.IBinder; |
| 40 | import android.os.Handler; | 42 | import android.os.Handler; |
| 43 | import android.os.Vibrator; | ||
| 44 | import android.os.VibratorManager; | ||
| 45 | import android.os.VibrationEffect; | ||
| 41 | 46 | ||
| 42 | import android.util.Log; | 47 | import android.util.Log; |
| 43 | import android.util.DisplayMetrics; | 48 | import android.util.DisplayMetrics; |
| 44 | 49 | ||
| 50 | import android.hardware.input.InputManager; | ||
| 51 | |||
| 45 | class Holder<T> | 52 | class 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; | |||
| 27 | import android.graphics.Canvas; | 27 | import android.graphics.Canvas; |
| 28 | import android.graphics.Rect; | 28 | import android.graphics.Rect; |
| 29 | 29 | ||
| 30 | import android.util.Log; | ||
| 31 | |||
| 30 | public class EmacsSurfaceView extends SurfaceView | 32 | public 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; | |||
| 22 | import java.lang.IllegalStateException; | 22 | import java.lang.IllegalStateException; |
| 23 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
| 24 | import java.util.List; | 24 | import java.util.List; |
| 25 | import java.util.HashMap; | ||
| 25 | 26 | ||
| 26 | import android.graphics.Rect; | 27 | import android.graphics.Rect; |
| 27 | import android.graphics.Canvas; | 28 | import 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 | |||
| 53 | public class EmacsWindow extends EmacsHandleObject | 60 | public 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 | ||
| 20 | package org.gnu.emacs; | 20 | package org.gnu.emacs; |
| 21 | 21 | ||
| 22 | import java.util.ArrayList; | ||
| 22 | import java.util.LinkedList; | 23 | import java.util.LinkedList; |
| 23 | import java.util.List; | 24 | import 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 | }; |