[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android a1941cd7a7 3/5: Update Android port
From: |
Po Lu |
Subject: |
feature/android a1941cd7a7 3/5: Update Android port |
Date: |
Fri, 10 Feb 2023 06:16:09 -0500 (EST) |
branch: feature/android
commit a1941cd7a7dc9a6f6b7239ec7d4bd3bdf5d55fc9
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Update Android port
* doc/emacs/android.texi (Android Windowing): Remove yet another
limitation.
* java/debug.sh: Make this work on systems which prohibit
attaching to app processes from adbd.
* java/org/gnu/emacs/EmacsCopyArea.java (perform): Avoid
creating copies whenever possible.
* java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
Remove SurfaceView based implementation and use manual double
buffering with invalidate instead.
* java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap)
(raise, lower, onDetachedFromWindow): Adjust accordingly.
* java/org/gnu/emacs/EmacsWindow.java (windowUpdated): Remove
function.
* src/sfntfont.c (sfntfont_open): Set font->max_width correctly.
---
doc/emacs/android.texi | 5 -
java/debug.sh | 13 ++-
java/org/gnu/emacs/EmacsCopyArea.java | 24 ++++-
java/org/gnu/emacs/EmacsSurfaceView.java | 159 ++++++++++++++-----------------
java/org/gnu/emacs/EmacsView.java | 93 +++---------------
java/org/gnu/emacs/EmacsWindow.java | 23 -----
src/sfntfont.c | 4 +
7 files changed, 116 insertions(+), 205 deletions(-)
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 5fdf5c16b4..5bbb2245a0 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -418,11 +418,6 @@ Due to the unusual nature of the Android windowing
environment, Emacs
only supports a limited subset of GUI features. Here is a list of
known limitations, and features which are not implemented:
-@itemize @bullet
-@item
-The functions @code{raise-frame} and @code{lower-frame} are
-non-functional, because of bugs in the window system.
-
@item
Scroll bars are not supported, as they are close to useless on Android
devices.
diff --git a/java/debug.sh b/java/debug.sh
index 7008664c04..2e95f9738c 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -267,10 +267,14 @@ if [ -z "$gdbserver" ]; then
gdbserver_bin=/system/bin/gdbserver
else
gdbserver_bin=/data/local/tmp/gdbserver
+ gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
+ \"tee gdbserver > /dev/null\""
# Upload the specified gdbserver binary to the device.
adb -s $device push "$gdbserver" "$gdbserver_bin"
- adb -s $device shell chmod +x "$gdbserver_bin"
+ # Copy it to the user directory.
+ adb -s $device shell "$gdbserver_cat"
+ adb -s $device shell "run-as $package chmod +x gdbserver"
fi
# Now start gdbserver on the device asynchronously.
@@ -286,10 +290,9 @@ if [ -z "$gdbserver" ]; then
else
# Normally the program cannot access $gdbserver_bin when it is
# placed in /data/local/tmp.
- adb -s $device shell $gdbserver_bin --once \
- "+/data/local/tmp/debug.$package.socket" \
- --attach $pid >&5 &
- gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
+ adb -s $device shell run-as $package "./gdbserver" --once \
+ "0.0.0.0:7654" --attach $pid >&5 &
+ gdb_socket="tcp:7654"
fi
# Wait until gdbserver successfully runs.
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java
b/java/org/gnu/emacs/EmacsCopyArea.java
index f8974e17c2..11dc22e045 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -110,11 +110,25 @@ public class EmacsCopyArea
if (gc.clip_mask == null)
{
- bitmap = Bitmap.createBitmap (srcBitmap,
- src_x, src_y, width,
- height);
- canvas.drawBitmap (bitmap, null, rect, paint);
- bitmap.recycle ();
+ if (source == destination)
+ {
+ /* Create a copy of the bitmap, since Android can't handle
+ overlapping copies. */
+ bitmap = Bitmap.createBitmap (srcBitmap,
+ src_x, src_y, width,
+ height);
+ canvas.drawBitmap (bitmap, null, rect, paint);
+ bitmap.recycle ();
+ }
+ else
+ {
+ /* But here the bitmaps are known to not overlap, so avoid
+ that extra consing overhead. */
+
+ srcRect = new Rect (src_x, src_y, src_x + width,
+ src_y + height);
+ canvas.drawBitmap (srcBitmap, null, rect, paint);
+ }
}
else
{
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java
b/java/org/gnu/emacs/EmacsSurfaceView.java
index f6cb77bb2b..e9bae62393 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -19,127 +19,114 @@ along with GNU Emacs. If not, see
<https://www.gnu.org/licenses/>. */
package org.gnu.emacs;
-import android.view.SurfaceView;
-import android.view.SurfaceHolder;
+import android.view.View;
import android.os.Build;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.Paint;
-import android.util.Log;
+/* This originally extended SurfaceView. However, doing so proved to
+ be too slow, and Android's surface view keeps up to three of its
+ own back buffers, which use too much memory (up to 96 MB for a
+ single frame.) */
-public class EmacsSurfaceView extends SurfaceView
+public class EmacsSurfaceView extends View
{
private static final String TAG = "EmacsSurfaceView";
- public Object surfaceChangeLock;
- private boolean created;
private EmacsView view;
-
- /* This is the callback used on Android 8 to 25. */
-
- private class Callback implements SurfaceHolder.Callback
- {
- @Override
- public void
- surfaceChanged (SurfaceHolder holder, int format,
- int width, int height)
- {
- Canvas canvas;
-
- Log.d (TAG, "surfaceChanged: " + view + ", ");
-
- view.swapBuffers (true);
- }
-
- @Override
- public void
- surfaceCreated (SurfaceHolder holder)
- {
- synchronized (surfaceChangeLock)
- {
- Log.d (TAG, "surfaceCreated: " + view);
- created = true;
- }
-
- /* Drop the lock when doing this, or a deadlock can
- result. */
- view.swapBuffers (true);
- }
-
- @Override
- public void
- surfaceDestroyed (SurfaceHolder holder)
- {
- synchronized (surfaceChangeLock)
- {
- Log.d (TAG, "surfaceDestroyed: " + view);
- created = false;
- }
- }
- }
+ private Bitmap frontBuffer;
+ private Canvas bitmapCanvas;
+ private Bitmap bitmap;
+ private Paint bitmapPaint;
public
EmacsSurfaceView (final EmacsView view)
{
super (view.getContext ());
- this.surfaceChangeLock = new Object ();
this.view = view;
-
- getHolder ().addCallback (new Callback ());
+ this.bitmapPaint = new Paint ();
}
- public boolean
- isCreated ()
+ private void
+ copyToFrontBuffer (Rect damageRect)
{
- return created;
+ if (damageRect != null)
+ bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect,
+ bitmapPaint);
+ else
+ bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint);
}
- public Canvas
- lockCanvas (Rect damage)
+ private void
+ reconfigureFrontBuffer (Bitmap bitmap)
{
- SurfaceHolder holder;
-
- holder = getHolder ();
+ /* First, remove the old front buffer. */
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ if (frontBuffer != null)
{
- damage.setEmpty ();
- return holder.lockHardwareCanvas ();
+ frontBuffer.recycle ();
+ frontBuffer = null;
+ bitmapCanvas = null;
}
- return holder.lockCanvas (damage);
- }
+ this.bitmap = bitmap;
- @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 ()));
- }
+ /* Next, create the new front buffer if necessary. */
- /* This method is only used during debugging when it seems damage
- isn't working correctly. */
+ if (bitmap != null && frontBuffer == null)
+ {
+ frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
+ bitmap.getHeight (),
+ Bitmap.Config.ARGB_8888,
+ false);
+ bitmapCanvas = new Canvas (frontBuffer);
+
+ /* And copy over the bitmap contents. */
+ copyToFrontBuffer (null);
+ }
+ else if (bitmap != null)
+ /* Just copy over the bitmap contents. */
+ copyToFrontBuffer (null);
+ }
- public Canvas
- lockCanvas ()
+ public synchronized void
+ setBitmap (Bitmap bitmap, Rect damageRect)
{
- SurfaceHolder holder;
+ if (bitmap != this.bitmap)
+ reconfigureFrontBuffer (bitmap);
+ else if (bitmap != null)
+ copyToFrontBuffer (damageRect);
- holder = getHolder ();
- return holder.lockCanvas ();
+ if (bitmap != null)
+ {
+ /* In newer versions of Android, the invalid rectangle is
+ supposedly internally calculated by the system. How that
+ is done is unknown, but calling `invalidateRect' is now
+ deprecated.
+
+ Fortunately, nobody has deprecated the version of
+ `postInvalidate' that accepts a dirty rectangle. */
+
+ if (damageRect != null)
+ postInvalidate (damageRect.left, damageRect.top,
+ damageRect.right, damageRect.bottom);
+ else
+ postInvalidate ();
+ }
}
- public void
- unlockCanvasAndPost (Canvas canvas)
+ @Override
+ public synchronized void
+ onDraw (Canvas canvas)
{
- SurfaceHolder holder;
+ /* Paint the view's bitmap; the bitmap might be recycled right
+ now. */
- holder = getHolder ();
- holder.unlockCanvasAndPost (canvas);
+ if (frontBuffer != null)
+ canvas.drawBitmap (frontBuffer, 0f, 0f, bitmapPaint);
}
};
diff --git a/java/org/gnu/emacs/EmacsView.java
b/java/org/gnu/emacs/EmacsView.java
index fac11870eb..0416301101 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -103,14 +103,6 @@ public class EmacsView extends ViewGroup
displayed whenever possible. */
public boolean isCurrentlyTextEditor;
- /* An empty rectangle. */
- public static final Rect emptyRect;
-
- static
- {
- emptyRect = new Rect ();
- };
-
public
EmacsView (EmacsWindow window)
{
@@ -127,14 +119,8 @@ 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 default focus highlight. */
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
setDefaultFocusHighlightEnabled (false);
@@ -191,8 +177,12 @@ public class EmacsView extends ViewGroup
bitmapDirty = false;
/* Explicitly free the old bitmap's memory. */
+
if (oldBitmap != null)
- oldBitmap.recycle ();
+ {
+ oldBitmap.recycle ();
+ surfaceView.setBitmap (null, null);
+ }
/* Some Android versions still don't free the bitmap until the
next GC. */
@@ -342,67 +332,27 @@ public class EmacsView extends ViewGroup
thread. */
public void
- swapBuffers (boolean force)
+ swapBuffers ()
{
Canvas canvas;
Rect damageRect;
Bitmap bitmap;
- /* Code must always take damageRegion, and then surfaceChangeLock,
- never the other way around! */
+ damageRect = null;
synchronized (damageRegion)
{
- if (!force && damageRegion.isEmpty ())
+ if (damageRegion.isEmpty ())
return;
bitmap = getBitmap ();
- /* 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. */
-
- synchronized (surfaceView.surfaceChangeLock)
- {
- if (!force)
- damageRect = damageRegion.getBounds ();
- else
- damageRect = emptyRect;
-
- if (!surfaceView.isCreated ())
- return;
-
- if (bitmap == null)
- return;
-
- /* Lock the canvas with the specified damage. */
- canvas = surfaceView.lockCanvas (damageRect);
-
- /* Return if locking the canvas failed. */
- if (canvas == null)
- return;
-
- /* Copy from the back buffer to the canvas. If damageRect was
- made empty, then draw the entire back buffer. */
-
- 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 ();
- }
+ /* Transfer the bitmap to the surface view, then invalidate
+ it. */
+ surfaceView.setBitmap (bitmap, damageRect);
}
}
- public void
- swapBuffers ()
- {
- swapBuffers (false);
- }
-
@Override
public boolean
onKeyDown (int keyCode, KeyEvent event)
@@ -486,16 +436,6 @@ public class EmacsView extends ViewGroup
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
@@ -511,16 +451,6 @@ public class EmacsView extends ViewGroup
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);
}
@Override
@@ -574,6 +504,7 @@ public class EmacsView extends ViewGroup
bitmap.recycle ();
bitmap = null;
canvas = null;
+ surfaceView.setBitmap (null, null);
/* Collect the bitmap storage; it could be large. */
Runtime.getRuntime ().gc ();
diff --git a/java/org/gnu/emacs/EmacsWindow.java
b/java/org/gnu/emacs/EmacsWindow.java
index e921b972c2..9e2f2f5327 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -57,12 +57,6 @@ 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
{
@@ -1111,21 +1105,4 @@ public class EmacsWindow extends EmacsHandleObject
}
});
}
-
- /* Notice that outstanding configure events have been processed.
- SERIAL is checked in the UI thread to verify that no new
- configure events have been generated in the mean time. */
-
- public void
- windowUpdated (final long serial)
- {
- EmacsService.SERVICE.runOnUiThread (new Runnable () {
- @Override
- public void
- run ()
- {
- view.windowUpdated (serial);
- }
- });
- }
};
diff --git a/src/sfntfont.c b/src/sfntfont.c
index cc084c7930..a5ed54394a 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -2152,6 +2152,10 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
* pixel_size * 1.0 / font_info->head->units_per_em);
font->height = font->ascent + font->descent;
+ /* Set font->max_width to the maximum advance width. */
+ font->max_width = (font_info->hhea->advance_width_max
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+
/* Set generic attributes such as type and style. */
ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name);