From f9732131cf3c67e24db74a3d49f256d3c189a7e3 Mon Sep 17 00:00:00 2001
From: Po Lu
Date: Fri, 13 Jan 2023 15:53:08 +0800
Subject: 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.
---
java/AndroidManifest.xml.in | 84 ++++
java/Makefile.in | 30 +-
java/debug.sh | 45 +-
java/org/gnu/emacs/EmacsActivity.java | 37 +-
java/org/gnu/emacs/EmacsNative.java | 25 +-
java/org/gnu/emacs/EmacsSdk7FontDriver.java | 7 -
java/org/gnu/emacs/EmacsService.java | 116 +++++
java/org/gnu/emacs/EmacsSurfaceView.java | 22 +-
java/org/gnu/emacs/EmacsView.java | 189 ++++++--
java/org/gnu/emacs/EmacsWindow.java | 498 +++++++++++++++++++--
.../gnu/emacs/EmacsWindowAttachmentManager.java | 54 ++-
11 files changed, 991 insertions(+), 116 deletions(-)
create mode 100644 java/AndroidManifest.xml.in
(limited to 'java')
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
# along with GNU Emacs. If not, see .
top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version = @version@
-include ${top_builddir}/src/verbose.mk
SHELL = @SHELL@
JAVAC = @JAVAC@
AAPT = @AAPT@
-DX = @DX@
+D8 = @D8@
ZIPALIGN = @ZIPALIGN@
JARSIGNER = @JARSIGNER@
ANDROID_JAR = @ANDROID_JAR@
@@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
JAVA_FILES = $(shell find . -type f -name *.java)
CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
+# Compute the name for the Emacs application package. This should be:
+# emacs---.apk
+
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+APK_NAME = emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
+
# How this stuff works.
# 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)
# assets/lisp/
.PHONY: emacs.apk-in all
-all: emacs.apk
+all: $(APK_NAME)
# Binaries to cross-compile.
CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
@@ -118,6 +126,18 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
rm -rf install_temp
+# Makefile itself.
+.PRECIOUS: ../config.status Makefile
+../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
+ $(MAKE) -C $(dir $@) $(notdir $@)
+Makefile: ../config.status $(top_builddir)/java/Makefile.in
+ $(MAKE) -C .. java/$@
+
+# AndroidManifest.xml:
+AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
+ AndroidManifest.xml.in
+ pushd ..; ./config.status java/AndroidManifest.xml; popd
+
.SUFFIXES: .java .class
.java.class &:
$(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
@@ -126,7 +146,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
# nested classes.
classes.dex: $(CLASS_FILES)
- $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \
+ $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
$(subst $$,\$$,$(shell find . -type f -name *.class))
# When emacs.keystore expires, regenerate it with:
@@ -136,7 +156,7 @@ classes.dex: $(CLASS_FILES)
.PHONY: clean maintainer-clean
-emacs.apk: classes.dex emacs.apk-in emacs.keystore
+$(APK_NAME): classes.dex emacs.apk-in emacs.keystore
cp -f emacs.apk-in $@.unaligned
$(AAPT) add $@.unaligned classes.dex
$(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
@@ -144,7 +164,7 @@ emacs.apk: classes.dex emacs.apk-in emacs.keystore
rm -f $@.unaligned
clean:
- rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class
+ rm -f *.apk emacs.apk-in *.dex *.unaligned *.class
rm -rf install-temp
find . -name '*.class' -delete
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
gdb_port=5039
jdb_port=64013
jdb=no
+attach_existing=no
while [ $# -gt 0 ]; do
case "$1" in
@@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do
echo " --port PORT run the GDB server on a specific port"
echo " --jdb-port PORT run the JDB server on a specific port"
echo " --jdb run JDB instead of GDB"
+ echo " --attach-existing attach to an existing process"
echo " --help print this message"
echo ""
echo "Available devices:"
@@ -63,6 +65,9 @@ while [ $# -gt 0 ]; do
"--port" )
gdb_port=$1
;;
+ "--attach-existing" )
+ attach_existing=yes
+ ;;
"--" )
shift
gdbargs=$@
@@ -120,30 +125,32 @@ package_pids=`awk -- '{
print $1
}' <<< $package_pids`
-# Finally, kill each existing process.
-for pid in $package_pids; do
- echo "Killing existing process $pid..."
- adb -s $device shell run-as $package kill -9 $pid &> /dev/null
-done
-
-# Now run the main activity. This must be done as the adb user and
-# not as the package user.
-echo "Starting activity $activity and attaching debugger"
-
-# Exit if the activity could not be started.
-adb -s $device shell am start -D "$package/$activity"
-if [ ! $? ]; then
- exit 1;
-fi
+if [ "$attach_existing" != "yes" ]; then
+ # Finally, kill each existing process.
+ for pid in $package_pids; do
+ echo "Killing existing process $pid..."
+ adb -s $device shell run-as $package kill -9 $pid &> /dev/null
+ done
+
+ # Now run the main activity. This must be done as the adb user and
+ # not as the package user.
+ echo "Starting activity $activity and attaching debugger"
+
+ # Exit if the activity could not be started.
+ adb -s $device shell am start -D "$package/$activity"
+ if [ ! $? ]; then
+ exit 1;
+ fi
-# Now look for processes matching the package again.
-package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
+ # Now look for processes matching the package again.
+ package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
-# Next, remove lines matching "ps" itself.
-package_pids=`awk -- '{
+ # Next, remove lines matching "ps" itself.
+ package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
+fi
pid=$package_pids
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
/* The currently focused window. */
public static EmacsWindow focusedWindow;
+ /* Whether or not this activity is paused. */
+ private boolean isPaused;
+
static
{
focusedActivities = new ArrayList ();
@@ -60,7 +63,7 @@ public class EmacsActivity extends Activity
focusedWindow = window;
for (EmacsWindow child : window.children)
- invalidateFocus1 (window);
+ invalidateFocus1 (child);
}
public static void
@@ -103,6 +106,9 @@ public class EmacsActivity extends Activity
/* Clear the window's pointer to this activity and remove the
window's view. */
window.setConsumer (null);
+
+ /* The window can't be iconified any longer. */
+ window.noticeDeiconified ();
layout.removeView (window.view);
window = null;
@@ -114,6 +120,8 @@ public class EmacsActivity extends Activity
public void
attachWindow (EmacsWindow child)
{
+ Log.d (TAG, "attachWindow: " + child);
+
if (window != null)
throw new IllegalStateException ("trying to attach window when one"
+ " already exists");
@@ -124,6 +132,10 @@ public class EmacsActivity extends Activity
layout.addView (window.view);
child.setConsumer (this);
+ /* If the activity is iconified, send that to the window. */
+ if (isPaused)
+ window.noticeIconified ();
+
/* Invalidate the focus. */
invalidateFocus ();
}
@@ -148,6 +160,9 @@ public class EmacsActivity extends Activity
{
FrameLayout.LayoutParams params;
+ /* Set the theme to one without a title bar. */
+ setTheme (android.R.style.Theme_NoTitleBar);
+
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
@@ -192,4 +207,24 @@ public class EmacsActivity extends Activity
invalidateFocus ();
}
+
+ @Override
+ public void
+ onPause ()
+ {
+ isPaused = true;
+
+ EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
+ super.onResume ();
+ }
+
+ @Override
+ public void
+ onResume ()
+ {
+ isPaused = false;
+
+ EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
+ super.onResume ();
+ }
};
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
long time, int state,
int button);
- /* Send an ANDROID_BUTTON_RELEASE event. */
+ /* Send an ANDROID_BUTTON_RELEASE event. */
public static native void sendButtonRelease (short window, int x, int y,
long time, int state,
int button);
+ /* Send an ANDROID_TOUCH_DOWN event. */
+ public static native void sendTouchDown (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_TOUCH_UP event. */
+ public static native void sendTouchUp (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_TOUCH_MOVE event. */
+ public static native void sendTouchMove (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_WHEEL event. */
+ public static native void sendWheel (short window, int x, int y,
+ long time, int state,
+ float xDelta, float yDelta);
+
+ /* Send an ANDROID_ICONIFIED event. */
+ public static native void sendIconified (short window);
+
+ /* Send an ANDROID_DEICONIFIED event. */
+ public static native void sendDeiconified (short window);
+
static
{
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
}
else
familyName = fileName;
-
- Log.d (TAG, "Initialized new typeface " + familyName);
}
@Override
@@ -321,17 +319,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
list = new LinkedList ();
- Log.d (TAG, ("Looking for fonts matching font spec: "
- + fontSpec.toString ()));
-
for (i = 0; i < typefaceList.length; ++i)
{
if (checkMatch (typefaceList[i], fontSpec))
list.add (new Sdk7FontEntity (typefaceList[i]));
}
- Log.d (TAG, "Found font entities: " + list.toString ());
-
return (FontEntity[]) list.toArray (new FontEntity[0]);
}
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;
import android.graphics.Point;
import android.view.View;
+import android.view.InputDevice;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
+
import android.os.Build;
import android.os.Looper;
import android.os.IBinder;
import android.os.Handler;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
import android.util.Log;
import android.util.DisplayMetrics;
+import android.hardware.input.InputManager;
+
class Holder
{
T thing;
@@ -250,4 +257,113 @@ public class EmacsService extends Service
{
window.clearArea (x, y, width, height);
}
+
+ @SuppressWarnings ("deprecation")
+ public void
+ ringBell ()
+ {
+ Vibrator vibrator;
+ VibrationEffect effect;
+ VibratorManager vibratorManager;
+ Object tem;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ {
+ tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
+ vibratorManager = (VibratorManager) tem;
+ vibrator = vibratorManager.getDefaultVibrator ();
+ }
+ else
+ vibrator
+ = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ {
+ effect
+ = VibrationEffect.createOneShot (50,
+ VibrationEffect.DEFAULT_AMPLITUDE);
+ vibrator.vibrate (effect);
+ }
+ else
+ vibrator.vibrate (50);
+ }
+
+ public short[]
+ queryTree (EmacsWindow window)
+ {
+ short[] array;
+ List windowList;
+ int i;
+
+ if (window == null)
+ /* Just return all the windows without a parent. */
+ windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
+ else
+ windowList = window.children;
+
+ array = new short[windowList.size () + 1];
+ i = 1;
+
+ array[0] = window.parent != null ? 0 : window.parent.handle;
+
+ for (EmacsWindow treeWindow : windowList)
+ array[i++] = treeWindow.handle;
+
+ return array;
+ }
+
+ public int
+ getScreenWidth (boolean mmWise)
+ {
+ DisplayMetrics metrics;
+
+ metrics = getResources ().getDisplayMetrics ();
+
+ if (!mmWise)
+ return metrics.widthPixels;
+ else
+ return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
+ }
+
+ public int
+ getScreenHeight (boolean mmWise)
+ {
+ DisplayMetrics metrics;
+
+ metrics = getResources ().getDisplayMetrics ();
+
+ if (!mmWise)
+ return metrics.heightPixels;
+ else
+ return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
+ }
+
+ public boolean
+ detectMouse ()
+ {
+ InputManager manager;
+ InputDevice device;
+ int[] ids;
+ int i;
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.JELLY_BEAN)
+ return false;
+
+ manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
+ ids = manager.getInputDeviceIds ();
+
+ for (i = 0; i < ids.length; ++i)
+ {
+ device = manager.getInputDevice (ids[i]);
+
+ if (device == null)
+ continue;
+
+ if (device.supportsSource (InputDevice.SOURCE_MOUSE))
+ return true;
+ }
+
+ return false;
+ }
};
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;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.util.Log;
+
public class EmacsSurfaceView extends SurfaceView
{
+ private static final String TAG = "EmacsSurfaceView";
+
public Object surfaceChangeLock;
private boolean created;
@@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
+ Log.d (TAG, "surfaceChanged: " + view);
view.swapBuffers ();
}
@@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView
{
synchronized (surfaceChangeLock)
{
+ Log.d (TAG, "surfaceCreated: " + view);
created = true;
- view.swapBuffers ();
}
+
+ /* Drop the lock when doing this, or a deadlock can
+ result. */
+ view.swapBuffers ();
}
@Override
@@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView
{
synchronized (surfaceChangeLock)
{
+ Log.d (TAG, "surfaceDestroyed: " + view);
created = false;
}
}
@@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView
return holder.lockCanvas (damage);
}
+ @Override
+ protected void
+ onLayout (boolean changed, int left, int top, int right,
+ int bottom)
+ {
+ Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
+ + " " + bottom + " -- " + changed + " visibility "
+ + getVisibility ()));
+ }
+
/* This method is only used during debugging when it seems damage
isn't working correctly. */
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
/* Create the surface view. */
this.surfaceView = new EmacsSurfaceView (this);
+ this.surfaceView.setZOrderMediaOverlay (true);
addView (this.surfaceView);
+
+ /* Not sure exactly what this does but it makes things magically
+ work. Why is something as simple as XRaiseWindow so involved
+ on Android? */
+ setChildrenDrawingOrderEnabled (true);
+
+ /* Get rid of the foreground and background tint. */
+ setBackgroundTintList (null);
+ setForegroundTintList (null);
}
private void
handleDirtyBitmap ()
{
+ Bitmap oldBitmap;
+
+ /* Save the old bitmap. */
+ oldBitmap = bitmap;
+
/* Recreate the front and back buffer bitmaps. */
bitmap
= Bitmap.createBitmap (bitmapDirty.width (),
@@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup
/* And canvases. */
canvas = new Canvas (bitmap);
- /* If Emacs is drawing to the bitmap right now from the
- main thread, the image contents are lost until the next
- ConfigureNotify and complete garbage. Sorry! */
+ /* Copy over the contents of the old bitmap. */
+ if (oldBitmap != null)
+ canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
+
bitmapDirty = null;
}
+ public synchronized void
+ explicitlyDirtyBitmap (Rect rect)
+ {
+ if (bitmapDirty == null
+ && (bitmap == null
+ || rect.width () != bitmap.getWidth ()
+ || rect.height () != bitmap.getHeight ()))
+ bitmapDirty = rect;
+ }
+
public synchronized Bitmap
getBitmap ()
{
@@ -168,25 +194,31 @@ public class EmacsView extends ViewGroup
View child;
Rect windowRect;
+ count = getChildCount ();
+
if (changed || mustReportLayout)
{
mustReportLayout = false;
window.viewLayout (left, top, right, bottom);
}
- if (changed)
+ if (changed
+ /* Check that a change has really happened. */
+ && (bitmapDirty == null
+ || bitmapDirty.width () != right - left
+ || bitmapDirty.height () != bottom - top))
bitmapDirty = new Rect (left, top, right, bottom);
- count = getChildCount ();
-
for (i = 0; i < count; ++i)
{
child = getChildAt (i);
+ Log.d (TAG, "onLayout: " + child);
+
if (child == surfaceView)
/* The child is the surface view, so give it the entire
view. */
- child.layout (left, top, right, bottom);
+ child.layout (0, 0, right - left, bottom - top);
else if (child.getVisibility () != GONE)
{
if (!(child instanceof EmacsView))
@@ -201,59 +233,68 @@ public class EmacsView extends ViewGroup
}
}
- public synchronized void
+ public void
damageRect (Rect damageRect)
{
- damageRegion.union (damageRect);
+ synchronized (damageRegion)
+ {
+ damageRegion.union (damageRect);
+ }
}
/* This method is called from both the UI thread and the Emacs
thread. */
- public synchronized void
+ public void
swapBuffers (boolean force)
{
Canvas canvas;
Rect damageRect;
Bitmap bitmap;
- if (damageRegion.isEmpty ())
- return;
+ /* Code must always take damageRegion, and then surfaceChangeLock,
+ never the other way around! */
- bitmap = getBitmap ();
+ synchronized (damageRegion)
+ {
+ if (damageRegion.isEmpty ())
+ return;
- /* Emacs must take the following lock to ensure the access to the
- canvas occurs with the surface created. Otherwise, Android
- will throttle calls to lockCanvas. */
+ bitmap = getBitmap ();
- synchronized (surfaceView.surfaceChangeLock)
- {
- damageRect = damageRegion.getBounds ();
+ /* Emacs must take the following lock to ensure the access to the
+ canvas occurs with the surface created. Otherwise, Android
+ will throttle calls to lockCanvas. */
- if (!surfaceView.isCreated ())
- return;
+ synchronized (surfaceView.surfaceChangeLock)
+ {
+ damageRect = damageRegion.getBounds ();
- if (bitmap == null)
- return;
+ if (!surfaceView.isCreated ())
+ return;
- /* Lock the canvas with the specified damage. */
- canvas = surfaceView.lockCanvas (damageRect);
+ if (bitmap == null)
+ return;
- /* Return if locking the canvas failed. */
- if (canvas == null)
- return;
+ /* Lock the canvas with the specified damage. */
+ canvas = surfaceView.lockCanvas (damageRect);
- /* Copy from the back buffer to the canvas. If damageRect was
- made empty, then draw the entire back buffer. */
+ /* Return if locking the canvas failed. */
+ if (canvas == null)
+ return;
- if (damageRect.isEmpty ())
- canvas.drawBitmap (bitmap, 0f, 0f, paint);
- else
- canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+ /* Copy from the back buffer to the canvas. If damageRect was
+ made empty, then draw the entire back buffer. */
- /* Unlock the canvas and clear the damage. */
- surfaceView.unlockCanvasAndPost (canvas);
- damageRegion.setEmpty ();
+ if (damageRect.isEmpty ())
+ canvas.drawBitmap (bitmap, 0f, 0f, paint);
+ else
+ canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+
+ /* Unlock the canvas and clear the damage. */
+ surfaceView.unlockCanvasAndPost (canvas);
+ damageRegion.setEmpty ();
+ }
}
}
@@ -308,6 +349,78 @@ public class EmacsView extends ViewGroup
public boolean
onTouchEvent (MotionEvent motion)
{
- return window.onSomeKindOfMotionEvent (motion);
+ return window.onTouchEvent (motion);
+ }
+
+ private void
+ moveChildToBack (View child)
+ {
+ int index;
+
+ index = indexOfChild (child);
+
+ if (index > 0)
+ {
+ detachViewFromParent (index);
+
+ /* The view at 0 is the surface view. */
+ attachViewToParent (child, 1,
+ child.getLayoutParams());
+ }
+ }
+
+
+ /* The following two functions must not be called if the view has no
+ parent, or is parented to an activity. */
+
+ public void
+ raise ()
+ {
+ EmacsView parent;
+
+ parent = (EmacsView) getParent ();
+
+ Log.d (TAG, "raise: parent " + parent);
+
+ if (parent.indexOfChild (this)
+ == parent.getChildCount () - 1)
+ return;
+
+ parent.bringChildToFront (this);
+
+ /* Yes, all of this is really necessary! */
+ parent.requestLayout ();
+ parent.invalidate ();
+ requestLayout ();
+ invalidate ();
+
+ /* The surface view must be destroyed and recreated. */
+ removeView (surfaceView);
+ addView (surfaceView, 0);
+ }
+
+ public void
+ lower ()
+ {
+ EmacsView parent;
+
+ parent = (EmacsView) getParent ();
+
+ Log.d (TAG, "lower: parent " + parent);
+
+ if (parent.indexOfChild (this) == 1)
+ return;
+
+ parent.moveChildToBack (this);
+
+ /* Yes, all of this is really necessary! */
+ parent.requestLayout ();
+ parent.invalidate ();
+ requestLayout ();
+ invalidate ();
+
+ /* The surface view must be removed and attached again. */
+ removeView (surfaceView);
+ addView (surfaceView, 0);
}
};
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;
import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
+import java.util.HashMap;
import android.graphics.Rect;
import android.graphics.Canvas;
@@ -50,9 +51,29 @@ import android.os.Build;
Views are also drawables, meaning they can accept drawing
requests. */
+/* Help wanted. What does not work includes `EmacsView.raise',
+ `EmacsView.lower', reparenting a window onto another window.
+
+ All three are likely undocumented restrictions within
+ EmacsSurface. */
+
public class EmacsWindow extends EmacsHandleObject
implements EmacsDrawable
{
+ private static final String TAG = "EmacsWindow";
+
+ private class Coordinate
+ {
+ /* Integral coordinate. */
+ int x, y;
+
+ Coordinate (int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+ };
+
/* The view associated with the window. */
public EmacsView view;
@@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject
private Rect rect;
/* The parent window, or null if it is the root window. */
- private EmacsWindow parent;
+ public EmacsWindow parent;
/* List of all children in stacking order. This must be kept
consistent! */
public ArrayList children;
+ /* Map between pointer identifiers and last known position. Used to
+ compute which pointer changed upon a touch event. */
+ private HashMap pointerMap;
+
/* The window consumer currently attached, if it exists. */
private EmacsWindowAttachmentManager.WindowConsumer attached;
@@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject
last button press or release event. */
private int lastButtonState, lastModifiers;
+ /* Whether or not the window is mapped, and whether or not it is
+ deiconified. */
+ private boolean isMapped, isIconified;
+
+ /* Whether or not to ask for focus upon being mapped, and whether or
+ not the window should be focusable. */
+ private boolean dontFocusOnMap, dontAcceptFocus;
+
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height)
@@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject
super (handle);
rect = new Rect (x, y, x + width, y + height);
+ pointerMap = new HashMap ();
/* Create the view from the context's UI thread. The window is
unmapped, so the view is GONE. */
@@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject
if (parent != null)
{
parent.children.add (this);
- parent.view.post (new Runnable () {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
@@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject
}
});
}
- else
- EmacsService.SERVICE.runOnUiThread (new Runnable () {
- @Override
- public void
- run ()
- {
- EmacsWindowAttachmentManager manager;
-
- manager = EmacsWindowAttachmentManager.MANAGER;
-
- /* If parent is the root window, notice that there are new
- children available for interested activites to pick
- up. */
-
- manager.registerWindow (EmacsWindow.this);
- }
- });
scratchGC = new EmacsGC ((short) 0);
}
@@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject
+ "children!");
/* Remove the view from its parent and make it invisible. */
- view.post (new Runnable () {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
public void
run ()
{
@@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject
parent = (View) view.getParent ();
- if (parent != null && attached == null)
+ if (parent != null)
((ViewGroup) parent).removeView (view);
manager.detachWindow (EmacsWindow.this);
@@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject
public void
viewLayout (int left, int top, int right, int bottom)
{
+ int rectWidth, rectHeight;
+
synchronized (this)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
-
- EmacsNative.sendConfigureNotify (this.handle,
- System.currentTimeMillis (),
- left, top, rect.width (),
- rect.height ());
}
+
+ rectWidth = right - left;
+ rectHeight = bottom - top;
+
+ EmacsNative.sendConfigureNotify (this.handle,
+ System.currentTimeMillis (),
+ left, top, rectWidth,
+ rectHeight);
}
public void
requestViewLayout ()
{
- view.post (new Runnable () {
+ /* This is necessary because otherwise subsequent drawing on the
+ Emacs thread may be lost. */
+ view.explicitlyDirtyBitmap (rect);
+
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
@@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject
public void
mapWindow ()
{
- view.post (new Runnable () {
- @Override
- public void
- run ()
- {
+ if (isMapped)
+ return;
- view.setVisibility (View.VISIBLE);
- /* Eventually this should check no-focus-on-map. */
- view.requestFocus ();
- }
- });
+ isMapped = true;
+
+ if (parent == null)
+ {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsWindowAttachmentManager manager;
+
+ /* Make the view visible, first of all. */
+ view.setVisibility (View.VISIBLE);
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
+ /* If parent is the root window, notice that there are new
+ children available for interested activites to pick
+ up. */
+ manager.registerWindow (EmacsWindow.this);
+
+ if (!getDontFocusOnMap ())
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
+ }
+ });
+ }
+ else
+ {
+ /* Do the same thing as above, but don't register this
+ window. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.setVisibility (View.VISIBLE);
+
+ if (!getDontFocusOnMap ())
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
+ }
+ });
+ }
}
public void
unmapWindow ()
{
+ if (!isMapped)
+ return;
+
+ isMapped = false;
+
view.post (new Runnable () {
@Override
public void
run ()
{
+ EmacsWindowAttachmentManager manager;
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
view.setVisibility (View.GONE);
+
+ /* Now that the window is unmapped, unregister it as
+ well. */
+ manager.detachWindow (EmacsWindow.this);
}
});
}
@@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject
return 4;
}
+ /* Return the ID of the pointer which changed in EVENT. Value is -1
+ if it could not be determined, else the pointer that changed, or
+ -2 if -1 would have been returned, but there is also a pointer
+ that is a mouse. */
+
+ private int
+ figureChange (MotionEvent event)
+ {
+ int pointerID, i, truncatedX, truncatedY, pointerIndex;
+ Coordinate coordinate;
+ boolean mouseFlag;
+
+ /* pointerID is always initialized but the Java compiler is too
+ dumb to know that. */
+ pointerID = -1;
+ mouseFlag = false;
+
+ switch (event.getActionMasked ())
+ {
+ case MotionEvent.ACTION_DOWN:
+ /* Primary pointer pressed with index 0. */
+
+ /* Detect mice. If this is a mouse event, give it to
+ onSomeKindOfMotionEvent. */
+ if ((Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
+ return -2;
+
+ pointerID = event.getPointerId (0);
+ pointerMap.put (pointerID,
+ new Coordinate ((int) event.getX (0),
+ (int) event.getY (0)));
+ break;
+
+ case MotionEvent.ACTION_UP:
+ /* Primary pointer released with index 0. */
+ pointerID = event.getPointerId (0);
+ pointerMap.remove (pointerID);
+ break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ /* New pointer. Find the pointer ID from the index and place
+ it in the map. */
+ pointerIndex = event.getActionIndex ();
+ pointerID = event.getPointerId (pointerIndex);
+ pointerMap.put (pointerID,
+ new Coordinate ((int) event.getX (pointerID),
+ (int) event.getY (pointerID)));
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ /* Pointer removed. Remove it from the map. */
+ pointerIndex = event.getActionIndex ();
+ pointerID = event.getPointerId (pointerIndex);
+ pointerMap.remove (pointerID);
+ break;
+
+ default:
+
+ /* Loop through each pointer in the event. */
+ for (i = 0; i < event.getPointerCount (); ++i)
+ {
+ pointerID = event.getPointerId (i);
+
+ /* Look up that pointer in the map. */
+ coordinate = pointerMap.get (pointerID);
+
+ if (coordinate != null)
+ {
+ /* See if coordinates have changed. */
+ truncatedX = (int) event.getX (i);
+ truncatedY = (int) event.getY (i);
+
+ if (truncatedX != coordinate.x
+ || truncatedY != coordinate.y)
+ {
+ /* The pointer changed. Update the coordinate and
+ break out of the loop. */
+ coordinate.x = truncatedX;
+ coordinate.y = truncatedY;
+
+ break;
+ }
+ }
+
+ /* See if this is a mouse. If so, set the mouseFlag. */
+ if ((Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
+ mouseFlag = true;
+ }
+
+ /* Set the pointer ID to -1 if the loop failed to find any
+ changed pointer. If a mouse pointer was found, set it to
+ -2. */
+ if (i == event.getPointerCount ())
+ pointerID = (mouseFlag ? -2 : -1);
+ }
+
+ /* Return the pointer ID. */
+ return pointerID;
+ }
+
+ public boolean
+ onTouchEvent (MotionEvent event)
+ {
+ int pointerID, index;
+
+ /* Extract the ``touch ID'' (or in Android, the ``pointer
+ ID''.) */
+ pointerID = figureChange (event);
+
+ if (pointerID < 0)
+ {
+ /* If this is a mouse event, give it to
+ onSomeKindOfMotionEvent. */
+ if (pointerID == -2)
+ return onSomeKindOfMotionEvent (event);
+
+ return false;
+ }
+
+ /* Find the pointer index corresponding to the event. */
+ index = event.findPointerIndex (pointerID);
+
+ switch (event.getActionMasked ())
+ {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ /* Touch down event. */
+ EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ /* Touch up event. */
+ EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ /* Pointer motion event. */
+ EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+ }
+
+ return false;
+ }
+
public boolean
onSomeKindOfMotionEvent (MotionEvent event)
{
@@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
- /* Emacs must return true even though touch events are not yet
- handled, because the value of this function is used by the
- system to decide whether or not Emacs gets ACTION_MOVE
+ /* Emacs must return true even though touch events are not
+ handled here, because the value of this function is used by
+ the system to decide whether or not Emacs gets ACTION_MOVE
events. */
return true;
+
+ case MotionEvent.ACTION_SCROLL:
+ /* Send a scroll event with the specified deltas. */
+ EmacsNative.sendWheel (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime (),
+ lastModifiers,
+ event.getAxisValue (MotionEvent.AXIS_HSCROLL),
+ event.getAxisValue (MotionEvent.AXIS_VSCROLL));
+ return true;
}
return false;
}
+
+ public void
+ reparentTo (final EmacsWindow otherWindow, int x, int y)
+ {
+ int width, height;
+
+ /* Reparent this window to the other window. */
+
+ if (parent != null)
+ parent.children.remove (this);
+
+ if (otherWindow != null)
+ otherWindow.children.add (this);
+
+ parent = otherWindow;
+
+ /* Move this window to the new location. */
+ synchronized (this)
+ {
+ width = rect.width ();
+ height = rect.height ();
+ rect.left = x;
+ rect.top = y;
+ rect.right = x + width;
+ rect.bottom = y + height;
+ }
+
+ /* Now do the work necessary on the UI thread to reparent the
+ window. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsWindowAttachmentManager manager;
+ View parent;
+
+ /* First, detach this window if necessary. */
+ manager = EmacsWindowAttachmentManager.MANAGER;
+ manager.detachWindow (EmacsWindow.this);
+
+ /* Also unparent this view. */
+ parent = (View) view.getParent ();
+
+ if (parent != null)
+ ((ViewGroup) parent).removeView (view);
+
+ /* Next, either add this window as a child of the new
+ parent's view, or make it available again. */
+ if (otherWindow != null)
+ otherWindow.view.addView (view);
+ else if (EmacsWindow.this.isMapped)
+ manager.registerWindow (EmacsWindow.this);
+
+ /* Request relayout. */
+ view.requestLayout ();
+ }
+ });
+ }
+
+ public void
+ makeInputFocus (long time)
+ {
+ /* TIME is currently ignored. Request the input focus now. */
+
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.requestFocus ();
+ }
+ });
+ }
+
+ public void
+ raise ()
+ {
+ /* This does nothing here. */
+ if (parent == null)
+ return;
+
+ /* Remove and add this view again. */
+ parent.children.remove (this);
+ parent.children.add (this);
+
+ /* Request a relayout. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.raise ();
+ }
+ });
+ }
+
+ public void
+ lower ()
+ {
+ /* This does nothing here. */
+ if (parent == null)
+ return;
+
+ /* Remove and add this view again. */
+ parent.children.remove (this);
+ parent.children.add (this);
+
+ /* Request a relayout. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.lower ();
+ }
+ });
+ }
+
+ public int[]
+ getWindowGeometry ()
+ {
+ int[] array;
+ Rect rect;
+
+ array = new int[4];
+ rect = getGeometry ();
+
+ array[0] = rect.left;
+ array[1] = rect.top;
+ array[2] = rect.width ();
+ array[3] = rect.height ();
+
+ return array;
+ }
+
+ public void
+ noticeIconified ()
+ {
+ isIconified = true;
+ EmacsNative.sendIconified (this.handle);
+ }
+
+ public void
+ noticeDeiconified ()
+ {
+ isIconified = false;
+ EmacsNative.sendDeiconified (this.handle);
+ }
+
+ public synchronized void
+ setDontAcceptFocus (final boolean dontAcceptFocus)
+ {
+ this.dontAcceptFocus = dontAcceptFocus;
+
+ /* Update the view's focus state. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.setFocusable (!dontAcceptFocus);
+ view.setFocusableInTouchMode (!dontAcceptFocus);
+ }
+ });
+ }
+
+ public synchronized void
+ setDontFocusOnMap (final boolean dontFocusOnMap)
+ {
+ this.dontFocusOnMap = dontFocusOnMap;
+ }
+
+ public synchronized boolean
+ getDontFocusOnMap ()
+ {
+ return dontFocusOnMap;
+ }
};
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 . */
package org.gnu.emacs;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -68,7 +69,7 @@ public class EmacsWindowAttachmentManager
};
private List consumers;
- private List windows;
+ public List windows;
public
EmacsWindowAttachmentManager ()
@@ -98,12 +99,19 @@ public class EmacsWindowAttachmentManager
EmacsNative.sendWindowAction ((short) 0, 0);
}
- public void
+ public synchronized void
registerWindow (EmacsWindow window)
{
Intent intent;
- Log.d (TAG, "registerWindow " + window);
+ Log.d (TAG, "registerWindow (maybe): " + window);
+
+ if (windows.contains (window))
+ /* The window is already registered. */
+ return;
+
+ Log.d (TAG, "registerWindow: " + window);
+
windows.add (window);
for (WindowConsumer consumer : consumers)
@@ -146,7 +154,7 @@ public class EmacsWindowAttachmentManager
consumers.remove (consumer);
}
- public void
+ public synchronized void
detachWindow (EmacsWindow window)
{
WindowConsumer consumer;
@@ -162,5 +170,43 @@ public class EmacsWindowAttachmentManager
consumers.remove (consumer);
consumer.destroy ();
}
+
+ windows.remove (window);
+ }
+
+ public void
+ noticeIconified (WindowConsumer consumer)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "noticeIconified " + consumer);
+
+ /* If a window is attached, send the appropriate iconification
+ events. */
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ window.noticeIconified ();
+ }
+
+ public void
+ noticeDeiconified (WindowConsumer consumer)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "noticeDeiconified " + consumer);
+
+ /* If a window is attached, send the appropriate iconification
+ events. */
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ window.noticeDeiconified ();
+ }
+
+ public synchronized List
+ copyWindows ()
+ {
+ return new ArrayList (windows);
}
};
--
cgit v1.2.1