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.
This commit is contained in:
parent
60270d8ee3
commit
a1941cd7a7
7 changed files with 120 additions and 209 deletions
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -19,127 +19,114 @@
|
|||
|
||||
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;
|
||||
/* First, remove the old front buffer. */
|
||||
|
||||
holder = getHolder ();
|
||||
|
||||
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;
|
||||
|
||||
/* Next, create the new front buffer if necessary. */
|
||||
|
||||
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 synchronized void
|
||||
setBitmap (Bitmap bitmap, Rect damageRect)
|
||||
{
|
||||
if (bitmap != this.bitmap)
|
||||
reconfigureFrontBuffer (bitmap);
|
||||
else if (bitmap != null)
|
||||
copyToFrontBuffer (damageRect);
|
||||
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void
|
||||
onLayout (boolean changed, int left, int top, int right,
|
||||
int bottom)
|
||||
public synchronized void
|
||||
onDraw (Canvas canvas)
|
||||
{
|
||||
Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
|
||||
+ " " + bottom + " -- " + changed + " visibility "
|
||||
+ getVisibility ()));
|
||||
}
|
||||
/* Paint the view's bitmap; the bitmap might be recycled right
|
||||
now. */
|
||||
|
||||
/* This method is only used during debugging when it seems damage
|
||||
isn't working correctly. */
|
||||
|
||||
public Canvas
|
||||
lockCanvas ()
|
||||
{
|
||||
SurfaceHolder holder;
|
||||
|
||||
holder = getHolder ();
|
||||
return holder.lockCanvas ();
|
||||
}
|
||||
|
||||
public void
|
||||
unlockCanvasAndPost (Canvas canvas)
|
||||
{
|
||||
SurfaceHolder holder;
|
||||
|
||||
holder = getHolder ();
|
||||
holder.unlockCanvasAndPost (canvas);
|
||||
if (frontBuffer != null)
|
||||
canvas.drawBitmap (frontBuffer, 0f, 0f, bitmapPaint);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 @@ else if (child.getVisibility () != GONE)
|
|||
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 @@ else if (child.getVisibility () != GONE)
|
|||
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 @@ else if (child.getVisibility () != GONE)
|
|||
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 @@ else if (child.getVisibility () != GONE)
|
|||
bitmap.recycle ();
|
||||
bitmap = null;
|
||||
canvas = null;
|
||||
surfaceView.setBitmap (null, null);
|
||||
|
||||
/* Collect the bitmap storage; it could be large. */
|
||||
Runtime.getRuntime ().gc ();
|
||||
|
|
|
@ -57,12 +57,6 @@ their views are attached to the parent activity (if any), else
|
|||
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 @@ else if (EmacsWindow.this.isMapped)
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue