1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-01 08:17:38 +00:00

Update Java part of Android port

* java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea, perform)
(paintTo):
* java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine):
* java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint):
* java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle)
(paintTo):
* java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
* java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon):
* java/org/gnu/emacs/EmacsFillRectangle.java
(EmacsFillRectangle):
* java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver):
* java/org/gnu/emacs/EmacsGC.java (EmacsGC):
* java/org/gnu/emacs/EmacsNative.java (EmacsNative):
* java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
* java/org/gnu/emacs/EmacsSdk23FontDriver.java
(EmacsSdk23FontDriver):
* java/org/gnu/emacs/EmacsSdk7FontDriver.java
(EmacsSdk7FontDriver, textExtents1, textExtents, draw):
* java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea):
* java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
* java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout)
(onFocusChanged):
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, run)
(resizeWindow, lockCanvas, getBitmap, onKeyDown, onKeyUp)
(onActivityDetached): Move rendering to main thread.  Make
drawing operations completely static.
This commit is contained in:
Po Lu 2023-01-08 15:39:02 +08:00
parent e816e57039
commit 695e26079e
17 changed files with 695 additions and 766 deletions

View File

@ -27,54 +27,16 @@
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Xfermode; import android.graphics.Xfermode;
public class EmacsCopyArea implements EmacsPaintReq public class EmacsCopyArea
{ {
private int src_x, src_y, dest_x, dest_y, width, height; private static Xfermode overAlu;
private EmacsDrawable destination, source;
private EmacsGC immutableGC;
private static Xfermode xorAlu, srcInAlu, overAlu;
static static
{ {
overAlu = new PorterDuffXfermode (Mode.SRC_OVER); overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
}; };
public private static void
EmacsCopyArea (EmacsDrawable source, EmacsDrawable destination,
int src_x, int src_y, int width, int height,
int dest_x, int dest_y, EmacsGC immutableGC)
{
Bitmap bitmap;
this.destination = destination;
this.source = source;
this.src_x = src_x;
this.src_y = src_y;
this.width = width;
this.height = height;
this.dest_x = dest_x;
this.dest_y = dest_y;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (dest_x, dest_y, dest_x + width,
dest_y + height);
}
@Override
public EmacsDrawable
getDrawable ()
{
return destination;
}
private void
insetRectBy (Rect rect, int left, int top, int right, insetRectBy (Rect rect, int left, int top, int right,
int bottom) int bottom)
{ {
@ -84,30 +46,38 @@ public class EmacsCopyArea implements EmacsPaintReq
rect.bottom -= bottom; rect.bottom -= bottom;
} }
@Override public static void
public EmacsGC perform (EmacsDrawable source, EmacsGC gc,
getGC () EmacsDrawable destination,
int src_x, int src_y, int width, int height,
int dest_x, int dest_y)
{ {
return immutableGC; int i;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Bitmap bitmap; Bitmap bitmap;
Paint maskPaint; Paint maskPaint, paint;
Canvas maskCanvas; Canvas maskCanvas, canvas;
Bitmap srcBitmap, maskBitmap, clipBitmap; Bitmap srcBitmap, maskBitmap, clipBitmap;
Rect rect, maskRect, srcRect, dstRect, maskDestRect; Rect rect, maskRect, srcRect, dstRect, maskDestRect;
boolean needFill; boolean needFill;
/* TODO implement stippling. */ /* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return; return;
alu = immutableGC.function; paint = gc.gcPaint;
canvas = destination.lockCanvas ();
if (canvas == null)
return;
canvas.save ();
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
/* A copy must be created or drawBitmap could end up overwriting /* A copy must be created or drawBitmap could end up overwriting
itself. */ itself. */
@ -137,14 +107,10 @@ public class EmacsCopyArea implements EmacsPaintReq
if (src_y + height > srcBitmap.getHeight ()) if (src_y + height > srcBitmap.getHeight ())
height = srcBitmap.getHeight () - src_y; height = srcBitmap.getHeight () - src_y;
rect = getRect (); rect = new Rect (dest_x, dest_y, dest_x + width,
dest_y + height);
if (alu == EmacsGC.GC_COPY) if (gc.clip_mask == null)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{ {
bitmap = Bitmap.createBitmap (srcBitmap, bitmap = Bitmap.createBitmap (srcBitmap,
src_x, src_y, width, src_x, src_y, width,
@ -156,17 +122,17 @@ public class EmacsCopyArea implements EmacsPaintReq
/* Drawing with a clip mask involves calculating the /* Drawing with a clip mask involves calculating the
intersection of the clip mask with the dst rect, and intersection of the clip mask with the dst rect, and
extrapolating the corresponding part of the src rect. */ extrapolating the corresponding part of the src rect. */
clipBitmap = immutableGC.clip_mask.bitmap; clipBitmap = gc.clip_mask.bitmap;
dstRect = new Rect (dest_x, dest_y, dstRect = new Rect (dest_x, dest_y,
dest_x + width, dest_x + width,
dest_y + height); dest_y + height);
maskRect = new Rect (immutableGC.clip_x_origin, maskRect = new Rect (gc.clip_x_origin,
immutableGC.clip_y_origin, gc.clip_y_origin,
(immutableGC.clip_x_origin (gc.clip_x_origin
+ clipBitmap.getWidth ()), + clipBitmap.getWidth ()),
(immutableGC.clip_y_origin (gc.clip_y_origin
+ clipBitmap.getHeight ())); + clipBitmap.getHeight ()));
clipBitmap = immutableGC.clip_mask.bitmap; clipBitmap = gc.clip_mask.bitmap;
if (!maskRect.setIntersect (dstRect, maskRect)) if (!maskRect.setIntersect (dstRect, maskRect))
/* There is no intersection between the clip mask and the /* There is no intersection between the clip mask and the
@ -191,20 +157,21 @@ public class EmacsCopyArea implements EmacsPaintReq
/* Draw the mask onto the maskBitmap. */ /* Draw the mask onto the maskBitmap. */
maskCanvas = new Canvas (maskBitmap); maskCanvas = new Canvas (maskBitmap);
maskRect.offset (-immutableGC.clip_x_origin, maskPaint = new Paint ();
-immutableGC.clip_y_origin); maskRect.offset (-gc.clip_x_origin,
maskCanvas.drawBitmap (immutableGC.clip_mask.bitmap, -gc.clip_y_origin);
maskRect, new Rect (0, 0, maskCanvas.drawBitmap (gc.clip_mask.bitmap,
maskRect.width (), maskRect,
maskRect.height ()), new Rect (0, 0,
paint); maskRect.width (),
maskRect.offset (immutableGC.clip_x_origin, maskRect.height ()),
immutableGC.clip_y_origin); maskPaint);
maskRect.offset (gc.clip_x_origin,
gc.clip_y_origin);
/* Set the transfer mode to SRC_IN to preserve only the parts /* Set the transfer mode to SRC_IN to preserve only the parts
of the source that overlap with the mask. */ of the source that overlap with the mask. */
maskPaint = new Paint (); maskPaint.setXfermode (EmacsGC.srcInAlu);
maskPaint.setXfermode (srcInAlu);
/* Draw the source. */ /* Draw the source. */
maskDestRect = new Rect (0, 0, srcRect.width (), maskDestRect = new Rect (0, 0, srcRect.width (),
@ -215,6 +182,10 @@ maskRect, new Rect (0, 0,
/* Finally, draw the mask bitmap to the destination. */ /* Finally, draw the mask bitmap to the destination. */
paint.setXfermode (overAlu); paint.setXfermode (overAlu);
canvas.drawBitmap (maskBitmap, null, maskRect, paint); canvas.drawBitmap (maskBitmap, null, maskRect, paint);
gc.resetXfermode ();
} }
canvas.restore ();
destination.damageRect (rect);
} }
} }

View File

@ -29,109 +29,49 @@
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Xfermode; import android.graphics.Xfermode;
public class EmacsDrawLine implements EmacsPaintReq public class EmacsDrawLine
{ {
private int x, y, x2, y2; public static void
private EmacsDrawable drawable; perform (EmacsDrawable drawable, EmacsGC gc,
private EmacsGC immutableGC; int x, int y, int x2, int y2)
private static Xfermode xorAlu, srcInAlu;
static
{ {
xorAlu = new PorterDuffXfermode (Mode.XOR); Rect rect;
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); Canvas canvas;
}; Paint paint;
int i;
public /* TODO implement stippling. */
EmacsDrawLine (EmacsDrawable drawable, int x, int y, if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
int x2, int y2, EmacsGC immutableGC) return;
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.x2 = x2;
this.y2 = y2;
this.immutableGC = immutableGC;
}
@Override paint = gc.gcPaint;
public Rect rect = new Rect (Math.min (x, x2 + 1),
getRect ()
{
return new Rect (Math.min (x, x2 + 1),
Math.min (y, y2 + 1), Math.min (y, y2 + 1),
Math.max (x2 + 1, x), Math.max (x2 + 1, x),
Math.max (y2 + 1, y)); Math.max (y2 + 1, y));
} canvas = drawable.lockCanvas ();
@Override if (canvas == null)
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect, srcRect;
int width, height;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return; return;
alu = immutableGC.function; canvas.save ();
rect = getRect ();
width = rect.width (); if (gc.real_clip_rects != null)
height = rect.height (); {
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
paint.setStyle (Paint.Style.STROKE); paint.setStyle (Paint.Style.STROKE);
if (alu == EmacsGC.GC_COPY) if (gc.clip_mask == null)
paint.setXfermode (null); canvas.drawLine ((float) x, (float) y,
else (float) x2, (float) y2,
paint.setXfermode (xorAlu); paint);
if (immutableGC.clip_mask == null) /* DrawLine with clip mask not implemented; it is not used by
{ Emacs. */
paint.setColor (immutableGC.foreground | 0xff000000); canvas.restore ();
canvas.drawLine ((float) x, (float) y, drawable.damageRect (rect);
(float) x2, (float) y2,
paint);
}
else
{
maskPaint = new Paint ();
maskBitmap
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null)
return;
maskPaint.setXfermode (srcInAlu);
maskPaint.setColor (immutableGC.foreground | 0xff000000);
maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (),
maskBitmap.getHeight ());
maskCanvas.drawLine (0.0f, 0.0f, (float) Math.abs (x - x2),
(float) Math.abs (y - y2), maskPaint);
canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
}
paint.setXfermode (null);
} }
} }

View File

@ -19,12 +19,13 @@
package org.gnu.emacs; package org.gnu.emacs;
public class EmacsDrawPoint extends EmacsDrawRectangle public class EmacsDrawPoint
{ {
public public static void
EmacsDrawPoint (EmacsDrawable drawable, int x, int y, perform (EmacsDrawable drawable,
EmacsGC immutableGC) EmacsGC immutableGC, int x, int y)
{ {
super (drawable, x, y, 1, 1, immutableGC); EmacsDrawRectangle.perform (drawable, immutableGC,
x, y, 1, 1);
} }
} }

View File

@ -22,110 +22,104 @@
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Xfermode;
public class EmacsDrawRectangle implements EmacsPaintReq import android.util.Log;
public class EmacsDrawRectangle
{ {
private int x, y, width, height; public static void
private EmacsDrawable drawable; perform (EmacsDrawable drawable, EmacsGC gc,
private EmacsGC immutableGC; int x, int y, int width, int height)
private static Xfermode xorAlu, srcInAlu;
static
{ {
xorAlu = new PorterDuffXfermode (Mode.XOR); int i;
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); Paint maskPaint, paint;
};
public
EmacsDrawRectangle (EmacsDrawable drawable, int x, int y,
int width, int height,
EmacsGC immutableGC)
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
/* Canvas.drawRect actually behaves exactly like PolyRectangle wrt
to where the lines are placed, so extend the width and height
by 1 in the damage rectangle. */
return new Rect (x, y, x + width + 1, y + height + 1);
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas; Canvas maskCanvas;
Bitmap maskBitmap; Bitmap maskBitmap;
Rect rect, srcRect; Rect rect;
Rect maskRect, dstRect;
Canvas canvas;
Bitmap clipBitmap;
/* TODO implement stippling. */ /* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return; return;
alu = immutableGC.function; canvas = drawable.lockCanvas ();
if (canvas == null)
return;
canvas.save ();
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
paint = gc.gcPaint;
rect = new Rect (x, y, x + width, y + height); rect = new Rect (x, y, x + width, y + height);
paint.setStyle (Paint.Style.STROKE); paint.setStyle (Paint.Style.STROKE);
paint.setStrokeWidth (1);
if (alu == EmacsGC.GC_COPY) if (gc.clip_mask == null)
paint.setXfermode (null); canvas.drawRect (rect, paint);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawRect (rect, paint);
}
else else
{ {
maskPaint = new Paint (); /* Drawing with a clip mask involves calculating the
maskBitmap intersection of the clip mask with the dst rect, and
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, extrapolating the corresponding part of the src rect. */
true); clipBitmap = gc.clip_mask.bitmap;
dstRect = new Rect (x, y, x + width, y + height);
maskRect = new Rect (gc.clip_x_origin,
gc.clip_y_origin,
(gc.clip_x_origin
+ clipBitmap.getWidth ()),
(gc.clip_y_origin
+ clipBitmap.getHeight ()));
clipBitmap = gc.clip_mask.bitmap;
if (maskBitmap == null) if (!maskRect.setIntersect (dstRect, maskRect))
/* There is no intersection between the clip mask and the
dest rect. */
return; return;
maskPaint.setXfermode (srcInAlu); /* Finally, create a temporary bitmap that is the size of
maskPaint.setColor (immutableGC.foreground | 0xff000000); maskRect. */
maskBitmap
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
Bitmap.Config.ARGB_8888);
/* Draw the mask onto the maskBitmap. */
maskCanvas = new Canvas (maskBitmap); maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (), maskRect.offset (-gc.clip_x_origin,
maskBitmap.getHeight ()); -gc.clip_y_origin);
maskCanvas.drawRect (srcRect, maskPaint); maskCanvas.drawBitmap (gc.clip_mask.bitmap,
canvas.drawBitmap (maskBitmap, srcRect, rect, paint); maskRect, new Rect (0, 0,
maskRect.width (),
maskRect.height ()),
paint);
maskRect.offset (gc.clip_x_origin,
gc.clip_y_origin);
/* Set the transfer mode to SRC_IN to preserve only the parts
of the source that overlap with the mask. */
maskPaint = new Paint ();
maskPaint.setXfermode (EmacsGC.srcInAlu);
maskPaint.setStyle (Paint.Style.STROKE);
/* Draw the source. */
maskCanvas.drawRect (maskRect, maskPaint);
/* Finally, draw the mask bitmap to the destination. */
paint.setXfermode (null);
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
} }
paint.setXfermode (null); canvas.restore ();
drawable.damageRect (new Rect (x, y, x + width + 1,
y + height + 1));
} }
} }

View File

@ -26,7 +26,6 @@
public interface EmacsDrawable public interface EmacsDrawable
{ {
public Canvas lockCanvas (); public Canvas lockCanvas ();
public void unlockCanvas ();
public void damageRect (Rect damageRect); public void damageRect (Rect damageRect);
public Bitmap getBitmap (); public Bitmap getBitmap ();
public boolean isDestroyed (); public boolean isDestroyed ();

View File

@ -26,34 +26,35 @@
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Xfermode;
public class EmacsFillPolygon implements EmacsPaintReq public class EmacsFillPolygon
{ {
private EmacsDrawable drawable; public static void
private EmacsGC immutableGC; perform (EmacsDrawable drawable, EmacsGC gc, Point points[])
private Path path;
private static Xfermode xorAlu, srcInAlu;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
EmacsFillPolygon (EmacsDrawable drawable, Point points[],
EmacsGC immutableGC)
{ {
Canvas canvas;
Path path;
Paint paint;
Rect rect;
RectF rectF;
int i; int i;
this.drawable = drawable; canvas = drawable.lockCanvas ();
this.immutableGC = immutableGC;
if (canvas == null)
return;
paint = gc.gcPaint;
canvas.save ();
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
/* Build the path from the given array of points. */ /* Build the path from the given array of points. */
path = new Path (); path = new Path ();
@ -67,84 +68,25 @@ public class EmacsFillPolygon implements EmacsPaintReq
path.close (); path.close ();
} }
}
@Override /* Compute the damage rectangle. */
public Rect rectF = new RectF (0, 0, 0, 0);
getRect () path.computeBounds (rectF, true);
{
RectF rect;
rect = new RectF (0, 0, 0, 0); rect = new Rect ((int) Math.floor (rectF.left),
path.computeBounds (rect, true); (int) Math.floor (rectF.top),
(int) Math.ceil (rectF.right),
return new Rect ((int) Math.floor (rect.left), (int) Math.ceil (rectF.bottom));
(int) Math.floor (rect.top),
(int) Math.ceil (rect.right),
(int) Math.ceil (rect.bottom));
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas;
Bitmap maskBitmap;
Rect rect;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
rect = getRect ();
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
else
paint.setXfermode (xorAlu);
paint.setStyle (Paint.Style.FILL); paint.setStyle (Paint.Style.FILL);
if (immutableGC.clip_mask == null) if (gc.clip_mask == null)
{ canvas.drawPath (path, paint);
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawPath (path, paint);
}
else
{
maskPaint = new Paint ();
maskBitmap = immutableGC.clip_mask.bitmap;
maskBitmap = maskBitmap.copy (Bitmap.Config.ARGB_8888,
true);
if (maskBitmap == null) canvas.restore ();
return; drawable.damageRect (rect);
maskPaint.setXfermode (srcInAlu); /* FillPolygon with clip mask not implemented; it is not used by
maskPaint.setColor (immutableGC.foreground | 0xff000000); Emacs. */
maskCanvas = new Canvas (maskBitmap);
path.offset (-rect.left, -rect.top, null);
maskCanvas.drawPath (path, maskPaint);
canvas.drawBitmap (maskBitmap, new Rect (0, 0, rect.width (),
rect.height ()),
rect, paint);
}
} }
} }

View File

@ -22,108 +22,102 @@
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Xfermode;
import android.util.Log; import android.util.Log;
public class EmacsFillRectangle implements EmacsPaintReq public class EmacsFillRectangle
{ {
private int x, y, width, height; public static void
private EmacsDrawable drawable; perform (EmacsDrawable drawable, EmacsGC gc,
private EmacsGC immutableGC; int x, int y, int width, int height)
private static Xfermode xorAlu, srcInAlu;
static
{ {
xorAlu = new PorterDuffXfermode (Mode.XOR); int i;
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); Paint maskPaint, paint;
};
public
EmacsFillRectangle (EmacsDrawable drawable, int x, int y,
int width, int height,
EmacsGC immutableGC)
{
this.drawable = drawable;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.immutableGC = immutableGC;
}
@Override
public Rect
getRect ()
{
return new Rect (x, y, x + width, y + height);
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int alu;
Paint maskPaint;
Canvas maskCanvas; Canvas maskCanvas;
Bitmap maskBitmap; Bitmap maskBitmap;
Rect rect, srcRect; Rect rect;
Rect maskRect, dstRect;
Canvas canvas;
Bitmap clipBitmap;
/* TODO implement stippling. */ /* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return; return;
alu = immutableGC.function; canvas = drawable.lockCanvas ();
rect = getRect ();
if (canvas == null)
return;
canvas.save ();
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
paint = gc.gcPaint;
rect = new Rect (x, y, x + width, y + height);
paint.setStyle (Paint.Style.FILL); paint.setStyle (Paint.Style.FILL);
if (alu == EmacsGC.GC_COPY) if (gc.clip_mask == null)
paint.setXfermode (null); canvas.drawRect (rect, paint);
else
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
{
paint.setColor (immutableGC.foreground | 0xff000000);
canvas.drawRect (rect, paint);
}
else else
{ {
maskPaint = new Paint (); /* Drawing with a clip mask involves calculating the
maskBitmap intersection of the clip mask with the dst rect, and
= immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, extrapolating the corresponding part of the src rect. */
true); clipBitmap = gc.clip_mask.bitmap;
dstRect = new Rect (x, y, x + width, y + height);
maskRect = new Rect (gc.clip_x_origin,
gc.clip_y_origin,
(gc.clip_x_origin
+ clipBitmap.getWidth ()),
(gc.clip_y_origin
+ clipBitmap.getHeight ()));
clipBitmap = gc.clip_mask.bitmap;
if (maskBitmap == null) if (!maskRect.setIntersect (dstRect, maskRect))
/* There is no intersection between the clip mask and the
dest rect. */
return; return;
maskPaint.setXfermode (srcInAlu); /* Finally, create a temporary bitmap that is the size of
maskPaint.setColor (immutableGC.foreground | 0xff000000); maskRect. */
maskBitmap
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
Bitmap.Config.ARGB_8888);
/* Draw the mask onto the maskBitmap. */
maskCanvas = new Canvas (maskBitmap); maskCanvas = new Canvas (maskBitmap);
srcRect = new Rect (0, 0, maskBitmap.getWidth (), maskRect.offset (-gc.clip_x_origin,
maskBitmap.getHeight ()); -gc.clip_y_origin);
maskCanvas.drawRect (srcRect, maskPaint); maskCanvas.drawBitmap (gc.clip_mask.bitmap,
canvas.drawBitmap (maskBitmap, srcRect, rect, paint); maskRect, new Rect (0, 0,
maskRect.width (),
maskRect.height ()),
paint);
maskRect.offset (gc.clip_x_origin,
gc.clip_y_origin);
/* Set the transfer mode to SRC_IN to preserve only the parts
of the source that overlap with the mask. */
maskPaint = new Paint ();
maskPaint.setXfermode (EmacsGC.srcInAlu);
/* Draw the source. */
maskCanvas.drawRect (maskRect, maskPaint);
/* Finally, draw the mask bitmap to the destination. */
paint.setXfermode (null);
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
} }
paint.setXfermode (null); canvas.restore ();
drawable.damageRect (rect);
} }
} }

View File

@ -164,7 +164,7 @@ public abstract int draw (FontObject fontObject, EmacsGC gc,
public static EmacsFontDriver public static EmacsFontDriver
createFontDriver () createFontDriver ()
{ {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
return new EmacsSdk23FontDriver (); return new EmacsSdk23FontDriver ();
return new EmacsSdk7FontDriver (); return new EmacsSdk7FontDriver ();

View File

@ -20,6 +20,11 @@
package org.gnu.emacs; package org.gnu.emacs;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Xfermode;
/* X like graphics context structures. Keep the enums in synch with /* X like graphics context structures. Keep the enums in synch with
androidgui.h! */ androidgui.h! */
@ -32,14 +37,21 @@ public class EmacsGC extends EmacsHandleObject
public static final int GC_FILL_SOLID = 0; public static final int GC_FILL_SOLID = 0;
public static final int GC_FILL_OPAQUE_STIPPLED = 1; public static final int GC_FILL_OPAQUE_STIPPLED = 1;
public static final Xfermode xorAlu, srcInAlu;
public int function, fill_style; public int function, fill_style;
public int foreground, background; public int foreground, background;
public int clip_x_origin, clip_y_origin; public int clip_x_origin, clip_y_origin;
public int ts_origin_x, ts_origin_y; public int ts_origin_x, ts_origin_y;
public Rect clip_rects[]; public Rect clip_rects[], real_clip_rects[];
public EmacsPixmap clip_mask, stipple; public EmacsPixmap clip_mask, stipple;
private boolean dirty; public Paint gcPaint;
private EmacsGC immutableGC;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
}
/* The following fields are only set on immutable GCs. */ /* The following fields are only set on immutable GCs. */
@ -55,62 +67,41 @@ public class EmacsGC extends EmacsHandleObject
fill_style = GC_FILL_SOLID; fill_style = GC_FILL_SOLID;
function = GC_COPY; function = GC_COPY;
foreground = 0; foreground = 0;
background = 0xffffffff; background = 0xffffff;
gcPaint = new Paint ();
} }
public /* Mark this GC as dirty. Apply parameters to the paint and
EmacsGC (EmacsGC source) recompute real_clip_rects. */
{
super ((short) 0);
int i;
function = source.function;
fill_style = source.fill_style;
foreground = source.foreground;
background = source.background;
clip_x_origin = source.clip_x_origin;
clip_y_origin = source.clip_y_origin;
clip_rects = source.clip_rects;
clip_mask = source.clip_mask;
stipple = source.stipple;
ts_origin_x = source.ts_origin_x;
ts_origin_y = source.ts_origin_y;
/* Offset all the clip rects by ts_origin_x and ts_origin_y. */
if ((ts_origin_x != 0 || ts_origin_y != 0)
&& clip_rects != null)
{
clip_rects = new Rect[clip_rects.length];
for (i = 0; i < clip_rects.length; ++i)
{
clip_rects[i] = new Rect (source.clip_rects[i]);
clip_rects[i].offset (ts_origin_x,
ts_origin_y);
}
}
}
/* Mark this GC as dirty. This means immutableGC will return a new
copy of this GC the next time it is called. */
public void public void
markDirty () markDirty ()
{ {
dirty = true; int i;
if ((ts_origin_x != 0 || ts_origin_y != 0)
&& clip_rects != null)
{
real_clip_rects = new Rect[clip_rects.length];
for (i = 0; i < clip_rects.length; ++i)
{
real_clip_rects[i] = new Rect (clip_rects[i]);
real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
}
}
else
real_clip_rects = clip_rects;
gcPaint.setColor (foreground | 0xff000000);
gcPaint.setXfermode (function == GC_XOR
? xorAlu : srcInAlu);
} }
public EmacsGC public void
immutableGC () resetXfermode ()
{ {
if (immutableGC == null || dirty) gcPaint.setXfermode (function == GC_XOR
{ ? xorAlu : srcInAlu);
immutableGC = new EmacsGC (this); }
dirty = false;
}
return immutableGC;
};
}; };

View File

@ -79,6 +79,28 @@ public static native void sendKeyRelease (short window, long time, int state,
/* Send an ANDROID_WINDOW_ACTION event. */ /* Send an ANDROID_WINDOW_ACTION event. */
public static native void sendWindowAction (short window, int action); public static native void sendWindowAction (short window, int action);
/* Send an ANDROID_ENTER_NOTIFY event. */
public static native void sendEnterNotify (short window, int x, int y,
long time);
/* Send an ANDROID_LEAVE_NOTIFY event. */
public static native void sendLeaveNotify (short window, int x, int y,
long time);
/* Send an ANDROID_MOTION_NOTIFY event. */
public static native void sendMotionNotify (short window, int x, int y,
long time);
/* Send an ANDROID_BUTTON_PRESS event. */
public static native void sendButtonPress (short window, int x, int y,
long time, int state,
int button);
/* Send an ANDROID_BUTTON_RELEASE event. */
public static native void sendButtonRelease (short window, int x, int y,
long time, int state,
int button);
static static
{ {
System.loadLibrary ("emacs"); System.loadLibrary ("emacs");

View File

@ -93,6 +93,8 @@ public class EmacsPixmap extends EmacsHandleObject
break; break;
} }
bitmap.eraseColor (0xff000000);
this.width = width; this.width = width;
this.height = height; this.height = height;
this.depth = depth; this.depth = depth;
@ -108,13 +110,6 @@ public class EmacsPixmap extends EmacsHandleObject
return canvas; return canvas;
} }
@Override
public void
unlockCanvas ()
{
}
@Override @Override
public void public void
damageRect (Rect damageRect) damageRect (Rect damageRect)

View File

@ -20,9 +20,80 @@
package org.gnu.emacs; package org.gnu.emacs;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect;
public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
{ {
private void
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
Paint paint, Rect bounds)
{
char[] text;
text = new char[2];
text[0] = (char) code;
text[1] = 'c';
paint.getTextBounds (text, 0, 1, bounds);
metrics.lbearing = (short) bounds.left;
metrics.rbearing = (short) bounds.right;
metrics.ascent = (short) -bounds.top;
metrics.descent = (short) bounds.bottom;
metrics.width
= (short) paint.getRunAdvance (text, 0, 1, 0, 1, false, 1);
}
@Override
public void
textExtents (FontObject font, int code[], FontMetrics fontMetrics)
{
int i;
Paint paintCache;
Rect boundsCache;
Sdk7FontObject fontObject;
char[] text;
float width;
fontObject = (Sdk7FontObject) font;
paintCache = fontObject.typeface.typefacePaint;
paintCache.setTextSize (fontObject.pixelSize);
boundsCache = new Rect ();
if (code.length == 0)
{
fontMetrics.lbearing = 0;
fontMetrics.rbearing = 0;
fontMetrics.ascent = 0;
fontMetrics.descent = 0;
fontMetrics.width = 0;
}
else if (code.length == 1)
textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
paintCache, boundsCache);
else
{
text = new char[code.length + 1];
for (i = 0; i < code.length; ++i)
text[i] = (char) code[i];
text[code.length] = 'c';
paintCache.getTextBounds (text, 0, code.length,
boundsCache);
width = paintCache.getRunAdvance (text, 0, code.length, 0,
code.length,
false, code.length);
fontMetrics.lbearing = (short) boundsCache.left;
fontMetrics.rbearing = (short) boundsCache.right;
fontMetrics.ascent = (short) -boundsCache.top;
fontMetrics.descent = (short) boundsCache.bottom;
fontMetrics.width = (short) width;
}
}
@Override @Override
public int public int
hasChar (FontSpec font, char charCode) hasChar (FontSpec font, char charCode)

View File

@ -243,93 +243,6 @@ protected class Sdk7FontObject extends FontObject
} }
}; };
private class Sdk7DrawString implements EmacsPaintReq
{
private boolean drawBackground;
private Sdk7FontObject fontObject;
private char[] chars;
private EmacsGC immutableGC;
private EmacsDrawable drawable;
private Rect rect;
private int originX, originY;
public
Sdk7DrawString (Sdk7FontObject fontObject, char[] chars,
EmacsGC immutableGC, EmacsDrawable drawable,
boolean drawBackground, Rect rect,
int originX, int originY)
{
this.fontObject = fontObject;
this.chars = chars;
this.immutableGC = immutableGC;
this.drawable = drawable;
this.drawBackground = drawBackground;
this.rect = rect;
this.originX = originX;
this.originY = originY;
}
@Override
public EmacsDrawable
getDrawable ()
{
return drawable;
}
@Override
public EmacsGC
getGC ()
{
return immutableGC;
}
@Override
public void
paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
{
int scratch;
paint.setStyle (Paint.Style.FILL);
if (drawBackground)
{
paint.setColor (immutableGC.background | 0xff000000);
canvas.drawRect (rect, paint);
}
paint.setTextSize (fontObject.pixelSize);
paint.setColor (immutableGC.foreground | 0xff000000);
paint.setTypeface (fontObject.typeface.typeface);
paint.setAntiAlias (true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
/* Disable hinting as that leads to displayed text not
matching the computed metrics. */
paint.setHinting (Paint.HINTING_OFF);
canvas.drawText (chars, 0, chars.length, originX, originY, paint);
paint.setAntiAlias (false);
}
@Override
public Rect
getRect ()
{
Rect rect;
rect = new Rect ();
fontObject.typeface.typefacePaint.setTextSize (fontObject.pixelSize);
fontObject.typeface.typefacePaint.getTextBounds (chars, 0, chars.length,
rect);
/* Add the background rect to the damage as well. */
rect.union (this.rect);
return rect;
}
};
private String[] fontFamilyList; private String[] fontFamilyList;
private Sdk7Typeface[] typefaceList; private Sdk7Typeface[] typefaceList;
private Sdk7Typeface fallbackTypeface; private Sdk7Typeface fallbackTypeface;
@ -498,14 +411,11 @@ private class Sdk7DrawString implements EmacsPaintReq
Paint paint, Rect bounds) Paint paint, Rect bounds)
{ {
char[] text; char[] text;
float[] width;
text = new char[1]; text = new char[1];
text[0] = (char) code; text[0] = (char) code;
paint.getTextBounds (text, 0, 1, bounds); paint.getTextBounds (text, 0, 1, bounds);
width = new float[1];
paint.getTextWidths (text, 0, 1, width);
/* bounds is the bounding box of the glyph corresponding to CODE. /* bounds is the bounding box of the glyph corresponding to CODE.
Translate these into XCharStruct values. Translate these into XCharStruct values.
@ -526,7 +436,7 @@ raster. descent is the distance (once again counting
metrics.rbearing = (short) bounds.right; metrics.rbearing = (short) bounds.right;
metrics.ascent = (short) -bounds.top; metrics.ascent = (short) -bounds.top;
metrics.descent = (short) bounds.bottom; metrics.descent = (short) bounds.bottom;
metrics.width = (short) Math.round (width[0]); metrics.width = (short) paint.measureText ("" + text[0]);
} }
@Override @Override
@ -563,7 +473,8 @@ else if (code.length == 1)
for (i = 0; i < code.length; ++i) for (i = 0; i < code.length; ++i)
text[i] = (char) code[i]; text[i] = (char) code[i];
paintCache.getTextBounds (text, 0, 1, boundsCache); paintCache.getTextBounds (text, 0, code.length,
boundsCache);
width = paintCache.measureText (text, 0, code.length); width = paintCache.measureText (text, 0, code.length);
fontMetrics.lbearing = (short) boundsCache.left; fontMetrics.lbearing = (short) boundsCache.left;
@ -587,11 +498,12 @@ else if (code.length == 1)
int[] chars, int x, int y, int backgroundWidth, int[] chars, int x, int y, int backgroundWidth,
boolean withBackground) boolean withBackground)
{ {
Rect backgroundRect; Rect backgroundRect, bounds;
Sdk7FontObject sdk7FontObject; Sdk7FontObject sdk7FontObject;
Sdk7DrawString op;
char[] charsArray; char[] charsArray;
int i; int i;
Canvas canvas;
Paint paint;
sdk7FontObject = (Sdk7FontObject) fontObject; sdk7FontObject = (Sdk7FontObject) fontObject;
charsArray = new char[chars.length]; charsArray = new char[chars.length];
@ -605,12 +517,41 @@ else if (code.length == 1)
backgroundRect.right = x + backgroundWidth; backgroundRect.right = x + backgroundWidth;
backgroundRect.bottom = y + sdk7FontObject.descent; backgroundRect.bottom = y + sdk7FontObject.descent;
op = new Sdk7DrawString (sdk7FontObject, charsArray, canvas = drawable.lockCanvas ();
gc.immutableGC (), drawable,
withBackground,
backgroundRect, x, y);
EmacsService.SERVICE.appendPaintOperation (op); if (canvas == null)
return 0;
canvas.save ();
paint = gc.gcPaint;
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
paint.setStyle (Paint.Style.FILL);
if (withBackground)
{
paint.setColor (gc.background | 0xff000000);
canvas.drawRect (backgroundRect, paint);
paint.setColor (gc.foreground | 0xff000000);
}
paint.setTextSize (sdk7FontObject.pixelSize);
paint.setTypeface (sdk7FontObject.typeface.typeface);
paint.setAntiAlias (true);
canvas.drawText (charsArray, 0, chars.length, x, y, paint);
canvas.restore ();
bounds = new Rect ();
paint.getTextBounds (charsArray, 0, chars.length, bounds);
bounds.offset (x, y);
bounds.union (backgroundRect);
drawable.damageRect (bounds);
paint.setAntiAlias (false);
return 1; return 1;
} }
}; };

View File

@ -19,7 +19,6 @@
package org.gnu.emacs; package org.gnu.emacs;
import java.lang.Runnable;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
@ -59,7 +58,6 @@ public class EmacsService extends Service
private EmacsThread thread; private EmacsThread thread;
private Handler handler; private Handler handler;
private EmacsPaintQueue paintQueue;
/* Display metrics used by font backends. */ /* Display metrics used by font backends. */
public DisplayMetrics metrics; public DisplayMetrics metrics;
@ -191,126 +189,42 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
return view.thing; return view.thing;
} }
/* X drawing operations. These are quite primitive operations. The
drawing queue is kept on the Emacs thread, but is periodically
flushed to the application thread, upon buffers swaps and once it
gets too big. */
private void
ensurePaintQueue ()
{
if (paintQueue == null)
paintQueue = new EmacsPaintQueue ();
}
public void
flushPaintQueue ()
{
final EmacsPaintQueue queue;
if (paintQueue == null)
return;
if (paintQueue.numRequests < 1)
/* No requests to flush. */
return;
queue = paintQueue;
handler.post (new Runnable () {
@Override
public void
run ()
{
queue.run ();
}
});
/* Clear the paint queue. */
paintQueue = null;
}
private void
checkFlush ()
{
if (paintQueue != null
&& paintQueue.numRequests > MAX_PENDING_REQUESTS)
flushPaintQueue ();
}
public void public void
fillRectangle (EmacsDrawable drawable, EmacsGC gc, fillRectangle (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height) int x, int y, int width, int height)
{ {
EmacsPaintReq req; EmacsFillRectangle.perform (drawable, gc, x, y,
width, height);
ensurePaintQueue ();
req = new EmacsFillRectangle (drawable, x, y,
width, height,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
fillPolygon (EmacsDrawable drawable, EmacsGC gc, fillPolygon (EmacsDrawable drawable, EmacsGC gc,
Point points[]) Point points[])
{ {
EmacsPaintReq req; EmacsFillPolygon.perform (drawable, gc, points);
ensurePaintQueue ();
req = new EmacsFillPolygon (drawable, points,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
drawRectangle (EmacsDrawable drawable, EmacsGC gc, drawRectangle (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height) int x, int y, int width, int height)
{ {
EmacsPaintReq req; EmacsDrawRectangle.perform (drawable, gc, x, y,
width, height);
ensurePaintQueue ();
req = new EmacsDrawRectangle (drawable, x, y,
width, height,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
drawLine (EmacsDrawable drawable, EmacsGC gc, drawLine (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int x2, int y2) int x, int y, int x2, int y2)
{ {
EmacsPaintReq req; EmacsDrawLine.perform (drawable, gc, x, y,
x2, y2);
ensurePaintQueue ();
req = new EmacsDrawLine (drawable, x, y,
x2, y2,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
drawPoint (EmacsDrawable drawable, EmacsGC gc, drawPoint (EmacsDrawable drawable, EmacsGC gc,
int x, int y) int x, int y)
{ {
EmacsPaintReq req; EmacsDrawPoint.perform (drawable, gc, x, y);
ensurePaintQueue ();
req = new EmacsDrawPoint (drawable, x, y,
gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
@ -319,15 +233,9 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
int srcX, int srcY, int width, int height, int destX, int srcX, int srcY, int width, int height, int destX,
int destY) int destY)
{ {
EmacsPaintReq req; EmacsCopyArea.perform (srcDrawable, gc, dstDrawable,
srcX, srcY, width, height, destX,
ensurePaintQueue (); destY);
req = new EmacsCopyArea (srcDrawable, dstDrawable,
srcX, srcY, width, height, destX,
destY, gc.immutableGC ());
paintQueue.appendPaintOperation (req);
checkFlush ();
} }
public void public void
@ -342,12 +250,4 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
{ {
window.clearArea (x, y, width, height); window.clearArea (x, y, width, height);
} }
public void
appendPaintOperation (EmacsPaintReq op)
{
ensurePaintQueue ();
paintQueue.appendPaintOperation (op);
checkFlush ();
}
}; };

View File

@ -29,6 +29,7 @@
public class EmacsSurfaceView extends SurfaceView public class EmacsSurfaceView extends SurfaceView
{ {
public Object surfaceChangeLock;
private boolean created; private boolean created;
public public
@ -36,33 +37,36 @@ public class EmacsSurfaceView extends SurfaceView
{ {
super (view.getContext ()); super (view.getContext ());
surfaceChangeLock = new Object ();
getHolder ().addCallback (new SurfaceHolder.Callback () { getHolder ().addCallback (new SurfaceHolder.Callback () {
@Override @Override
public void public void
surfaceChanged (SurfaceHolder holder, int format, surfaceChanged (SurfaceHolder holder, int format,
int width, int height) int width, int height)
{ {
/* Force a buffer swap now to get the contents of the Emacs view.swapBuffers ();
view on screen. */
view.swapBuffers (true);
} }
@Override @Override
public void public void
surfaceCreated (SurfaceHolder holder) surfaceCreated (SurfaceHolder holder)
{ {
created = true; synchronized (surfaceChangeLock)
{
/* Force a buffer swap now to get the contents of the Emacs created = true;
view on screen. */ view.swapBuffers ();
view.swapBuffers (true); }
} }
@Override @Override
public void public void
surfaceDestroyed (SurfaceHolder holder) surfaceDestroyed (SurfaceHolder holder)
{ {
created = false; synchronized (surfaceChangeLock)
{
created = false;
}
} }
}); });
} }

View File

@ -23,6 +23,7 @@
import android.view.View; import android.view.View;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -64,6 +65,14 @@ public class EmacsView extends ViewGroup
/* The associated surface view. */ /* The associated surface view. */
private EmacsSurfaceView surfaceView; private EmacsSurfaceView surfaceView;
/* Whether or not a configure event must be sent for the next layout
event regardless of what changed. */
public boolean mustReportLayout;
/* If non-null, whether or not bitmaps must be recreated upon the
next call to getBitmap. */
private Rect bitmapDirty;
public public
EmacsView (EmacsWindow window) EmacsView (EmacsWindow window)
{ {
@ -81,6 +90,43 @@ public class EmacsView extends ViewGroup
addView (this.surfaceView); addView (this.surfaceView);
} }
private void
handleDirtyBitmap ()
{
/* Recreate the front and back buffer bitmaps. */
bitmap
= Bitmap.createBitmap (bitmapDirty.width (),
bitmapDirty.height (),
Bitmap.Config.ARGB_8888);
bitmap.eraseColor (0xffffffff);
/* 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! */
bitmapDirty = null;
}
public synchronized Bitmap
getBitmap ()
{
if (bitmapDirty != null)
handleDirtyBitmap ();
return bitmap;
}
public synchronized Canvas
getCanvas ()
{
if (bitmapDirty != null)
handleDirtyBitmap ();
return canvas;
}
@Override @Override
protected void protected void
onMeasure (int widthMeasureSpec, int heightMeasureSpec) onMeasure (int widthMeasureSpec, int heightMeasureSpec)
@ -114,7 +160,7 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
} }
@Override @Override
protected void protected synchronized void
onLayout (boolean changed, int left, int top, int right, onLayout (boolean changed, int left, int top, int right,
int bottom) int bottom)
{ {
@ -122,19 +168,15 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
View child; View child;
Rect windowRect; Rect windowRect;
if (changed) if (changed || mustReportLayout)
{ {
mustReportLayout = false;
window.viewLayout (left, top, right, bottom); window.viewLayout (left, top, right, bottom);
/* Recreate the front and back buffer bitmaps. */
bitmap
= Bitmap.createBitmap (right - left, bottom - top,
Bitmap.Config.ARGB_8888);
/* And canvases. */
canvas = new Canvas (bitmap);
} }
if (changed)
bitmapDirty = new Rect (left, top, right, bottom);
count = getChildCount (); count = getChildCount ();
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
@ -159,47 +201,60 @@ else if (child.getVisibility () != GONE)
} }
} }
public void public synchronized void
damageRect (Rect damageRect) damageRect (Rect damageRect)
{ {
damageRegion.union (damageRect); damageRegion.union (damageRect);
} }
public void /* This method is called from both the UI thread and the Emacs
thread. */
public synchronized void
swapBuffers (boolean force) swapBuffers (boolean force)
{ {
Bitmap back;
Canvas canvas; Canvas canvas;
Rect damageRect; Rect damageRect;
Bitmap bitmap;
if (damageRegion.isEmpty ()) if (damageRegion.isEmpty ())
return; return;
if (!surfaceView.isCreated ()) bitmap = getBitmap ();
return;
if (bitmap == null) /* Emacs must take the following lock to ensure the access to the
return; canvas occurs with the surface created. Otherwise, Android
will throttle calls to lockCanvas. */
/* Lock the canvas with the specified damage. */ synchronized (surfaceView.surfaceChangeLock)
damageRect = damageRegion.getBounds (); {
canvas = surfaceView.lockCanvas (damageRect); damageRect = damageRegion.getBounds ();
/* Return if locking the canvas failed. */ if (!surfaceView.isCreated ())
if (canvas == null) return;
return;
/* Copy from the back buffer to the canvas. If damageRect was if (bitmap == null)
made empty, then draw the entire back buffer. */ return;
if (damageRect.isEmpty ()) /* Lock the canvas with the specified damage. */
canvas.drawBitmap (bitmap, 0f, 0f, paint); canvas = surfaceView.lockCanvas (damageRect);
else
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
/* Unlock the canvas and clear the damage. */ /* Return if locking the canvas failed. */
surfaceView.unlockCanvasAndPost (canvas); if (canvas == null)
damageRegion.setEmpty (); 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 ();
}
} }
public void public void
@ -241,4 +296,18 @@ else if (child.getVisibility () != GONE)
super.onFocusChanged (gainFocus, direction, super.onFocusChanged (gainFocus, direction,
previouslyFocusedRect); previouslyFocusedRect);
} }
@Override
public boolean
onGenericMotionEvent (MotionEvent motion)
{
return window.onSomeKindOfMotionEvent (motion);
}
@Override
public boolean
onTouchEvent (MotionEvent motion)
{
return window.onSomeKindOfMotionEvent (motion);
}
}; };

View File

@ -31,8 +31,13 @@
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.InputDevice;
import android.content.Intent; import android.content.Intent;
import android.util.Log;
import android.os.Build;
/* This defines a window, which is a handle. Windows represent a /* This defines a window, which is a handle. Windows represent a
rectangular subset of the screen with their own contents. rectangular subset of the screen with their own contents.
@ -68,6 +73,10 @@ public class EmacsWindow extends EmacsHandleObject
window background. */ window background. */
private EmacsGC scratchGC; private EmacsGC scratchGC;
/* The button state and keyboard modifier mask at the time of the
last button press or release event. */
private int lastButtonState, lastModifiers;
public public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y, EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height) int width, int height)
@ -212,6 +221,7 @@ public class EmacsWindow extends EmacsHandleObject
public void public void
run () run ()
{ {
view.mustReportLayout = true;
view.requestLayout (); view.requestLayout ();
} }
}); });
@ -224,6 +234,8 @@ public class EmacsWindow extends EmacsHandleObject
{ {
rect.right = rect.left + width; rect.right = rect.left + width;
rect.bottom = rect.top + height; rect.bottom = rect.top + height;
requestViewLayout ();
} }
} }
@ -279,17 +291,7 @@ public class EmacsWindow extends EmacsHandleObject
public Canvas public Canvas
lockCanvas () lockCanvas ()
{ {
if (view.canvas != null) return view.getCanvas ();
return view.canvas;
return null;
}
@Override
public void
unlockCanvas ()
{
} }
@Override @Override
@ -302,17 +304,7 @@ public class EmacsWindow extends EmacsHandleObject
public void public void
swapBuffers () swapBuffers ()
{ {
/* Before calling swapBuffers, make sure to flush the paint view.swapBuffers ();
queue. */
EmacsService.SERVICE.flushPaintQueue ();
view.post (new Runnable () {
@Override
public void
run ()
{
view.swapBuffers ();
}
});
} }
public void public void
@ -337,7 +329,7 @@ public class EmacsWindow extends EmacsHandleObject
public Bitmap public Bitmap
getBitmap () getBitmap ()
{ {
return view.bitmap; return view.getBitmap ();
} }
public void public void
@ -357,6 +349,7 @@ public class EmacsWindow extends EmacsHandleObject
recognized as an ASCII key press recognized as an ASCII key press
event. */ event. */
event.getUnicodeChar (state)); event.getUnicodeChar (state));
lastModifiers = event.getModifiers ();
} }
public void public void
@ -372,6 +365,7 @@ public class EmacsWindow extends EmacsHandleObject
event.getModifiers (), event.getModifiers (),
keyCode, keyCode,
event.getUnicodeChar (state)); event.getUnicodeChar (state));
lastModifiers = event.getModifiers ();
} }
public void public void
@ -386,4 +380,105 @@ public class EmacsWindow extends EmacsHandleObject
/* Destroy the associated frame when the activity is detached. */ /* Destroy the associated frame when the activity is detached. */
EmacsNative.sendWindowAction (this.handle, 0); EmacsNative.sendWindowAction (this.handle, 0);
} }
/* Look through the button state to determine what button EVENT was
generated from. DOWN is true if EVENT is a button press event,
false otherwise. Value is the X number of the button. */
private int
whatButtonWasIt (MotionEvent event, boolean down)
{
int eventState, notIn;
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
/* Earlier versions of Android only support one mouse
button. */
return 1;
eventState = event.getButtonState ();
notIn = (down ? eventState & ~lastButtonState
: lastButtonState & ~eventState);
if ((notIn & MotionEvent.BUTTON_PRIMARY) != 0)
return 1;
if ((notIn & MotionEvent.BUTTON_SECONDARY) != 0)
return 3;
if ((notIn & MotionEvent.BUTTON_TERTIARY) != 0)
return 2;
/* Not a real value. */
return 4;
}
public boolean
onSomeKindOfMotionEvent (MotionEvent event)
{
if (!event.isFromSource (InputDevice.SOURCE_CLASS_POINTER))
return false;
switch (event.getAction ())
{
case MotionEvent.ACTION_HOVER_ENTER:
EmacsNative.sendEnterNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_HOVER_MOVE:
EmacsNative.sendMotionNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_HOVER_EXIT:
EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
/* Find the button which was pressed. */
EmacsNative.sendButtonPress (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
whatButtonWasIt (event, true));
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
return true;
lastButtonState = event.getButtonState ();
return true;
case MotionEvent.ACTION_BUTTON_RELEASE:
/* Find the button which was released. */
EmacsNative.sendButtonRelease (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
whatButtonWasIt (event, false));
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
return true;
lastButtonState = event.getButtonState ();
return true;
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
events. */
return true;
}
return false;
}
}; };