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.
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.
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.
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
{
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);
}
};
displayed whenever possible. */
public boolean isCurrentlyTextEditor;
- /* An empty rectangle. */
- public static final Rect emptyRect;
-
- static
- {
- emptyRect = new Rect ();
- };
-
public
EmacsView (EmacsWindow window)
{
/* 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);
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. */
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)
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
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
bitmap.recycle ();
bitmap = null;
canvas = null;
+ surfaceView.setBitmap (null, null);
/* Collect the bitmap storage; it could be large. */
Runtime.getRuntime ().gc ();
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
{
}
});
}
-
- /* 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);
- }
- });
- }
};
* 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);