emacs-diffs
[Top][All Lists]
Advanced

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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]