emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android 695e26079e 3/8: Update Java part of Android port


From: Po Lu
Subject: feature/android 695e26079e 3/8: Update Java part of Android port
Date: Sun, 8 Jan 2023 02:50:56 -0500 (EST)

branch: feature/android
commit 695e26079eb60d10ffe25bb8ae91ebc6131fb27d
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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.
---
 java/org/gnu/emacs/EmacsCopyArea.java        | 131 +++++++++-------------
 java/org/gnu/emacs/EmacsDrawLine.java        | 120 +++++---------------
 java/org/gnu/emacs/EmacsDrawPoint.java       |  11 +-
 java/org/gnu/emacs/EmacsDrawRectangle.java   | 160 +++++++++++++--------------
 java/org/gnu/emacs/EmacsDrawable.java        |   1 -
 java/org/gnu/emacs/EmacsFillPolygon.java     | 128 ++++++---------------
 java/org/gnu/emacs/EmacsFillRectangle.java   | 152 ++++++++++++-------------
 java/org/gnu/emacs/EmacsFontDriver.java      |   2 +-
 java/org/gnu/emacs/EmacsGC.java              |  77 ++++++-------
 java/org/gnu/emacs/EmacsNative.java          |  22 ++++
 java/org/gnu/emacs/EmacsPixmap.java          |   9 +-
 java/org/gnu/emacs/EmacsSdk23FontDriver.java |  71 ++++++++++++
 java/org/gnu/emacs/EmacsSdk7FontDriver.java  | 139 +++++++----------------
 java/org/gnu/emacs/EmacsService.java         | 122 ++------------------
 java/org/gnu/emacs/EmacsSurfaceView.java     |  22 ++--
 java/org/gnu/emacs/EmacsView.java            | 133 ++++++++++++++++------
 java/org/gnu/emacs/EmacsWindow.java          | 141 +++++++++++++++++++----
 17 files changed, 685 insertions(+), 756 deletions(-)

diff --git a/java/org/gnu/emacs/EmacsCopyArea.java 
b/java/org/gnu/emacs/EmacsCopyArea.java
index 0dd5b2c1fb..00e817bb97 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -27,54 +27,16 @@ import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 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 EmacsDrawable destination, source;
-  private EmacsGC immutableGC;
-  private static Xfermode xorAlu, srcInAlu, overAlu;
+  private static Xfermode overAlu;
 
   static
   {
     overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
-    xorAlu = new PorterDuffXfermode (Mode.XOR);
-    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
   };
 
-  public
-  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
+  private static void
   insetRectBy (Rect rect, int left, int top, int right,
               int bottom)
   {
@@ -84,30 +46,38 @@ public class EmacsCopyArea implements EmacsPaintReq
     rect.bottom -= bottom;
   }
 
-  @Override
-  public EmacsGC
-  getGC ()
-  {
-    return immutableGC;
-  }
-
-  @Override
-  public void
-  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  public static void
+  perform (EmacsDrawable source, EmacsGC gc,
+          EmacsDrawable destination,
+          int src_x, int src_y, int width, int height,
+          int dest_x, int dest_y)
   {
-    int alu;
+    int i;
     Bitmap bitmap;
-    Paint maskPaint;
-    Canvas maskCanvas;
+    Paint maskPaint, paint;
+    Canvas maskCanvas, canvas;
     Bitmap srcBitmap, maskBitmap, clipBitmap;
     Rect rect, maskRect, srcRect, dstRect, maskDestRect;
     boolean needFill;
 
     /* TODO implement stippling.  */
-    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       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
        itself.  */
@@ -137,14 +107,10 @@ public class EmacsCopyArea implements EmacsPaintReq
     if (src_y + height > srcBitmap.getHeight ())
       height = srcBitmap.getHeight () - src_y;
 
-    rect = getRect ();
-
-    if (alu == EmacsGC.GC_COPY)
-      paint.setXfermode (null);
-    else
-      paint.setXfermode (xorAlu);
+    rect = new Rect (dest_x, dest_y, dest_x + width,
+                    dest_y + height);
 
-    if (immutableGC.clip_mask == null)
+    if (gc.clip_mask == null)
       {
        bitmap = Bitmap.createBitmap (srcBitmap,
                                      src_x, src_y, width,
@@ -156,17 +122,17 @@ public class EmacsCopyArea implements EmacsPaintReq
        /* Drawing with a clip mask involves calculating the
           intersection of the clip mask with the dst rect, and
           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,
                            dest_x + width,
                            dest_y + height);
-       maskRect = new Rect (immutableGC.clip_x_origin,
-                            immutableGC.clip_y_origin,
-                            (immutableGC.clip_x_origin
+       maskRect = new Rect (gc.clip_x_origin,
+                            gc.clip_y_origin,
+                            (gc.clip_x_origin
                              + clipBitmap.getWidth ()),
-                            (immutableGC.clip_y_origin
+                            (gc.clip_y_origin
                              + clipBitmap.getHeight ()));
-       clipBitmap = immutableGC.clip_mask.bitmap;
+       clipBitmap = gc.clip_mask.bitmap;
 
        if (!maskRect.setIntersect (dstRect, maskRect))
          /* 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.  */
        maskCanvas = new Canvas (maskBitmap);
-       maskRect.offset (-immutableGC.clip_x_origin,
-                        -immutableGC.clip_y_origin);
-       maskCanvas.drawBitmap (immutableGC.clip_mask.bitmap,
-                              maskRect, new Rect (0, 0,
-                                                  maskRect.width (),
-                                                  maskRect.height ()),
-                              paint);
-       maskRect.offset (immutableGC.clip_x_origin,
-                        immutableGC.clip_y_origin);
+       maskPaint = new Paint ();
+       maskRect.offset (-gc.clip_x_origin,
+                        -gc.clip_y_origin);
+       maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+                              maskRect,
+                              new Rect (0, 0,
+                                        maskRect.width (),
+                                        maskRect.height ()),
+                              maskPaint);
+       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 (srcInAlu);
+       maskPaint.setXfermode (EmacsGC.srcInAlu);
 
        /* Draw the source.  */
        maskDestRect = new Rect (0, 0, srcRect.width (),
@@ -215,6 +182,10 @@ public class EmacsCopyArea implements EmacsPaintReq
        /* Finally, draw the mask bitmap to the destination.  */
        paint.setXfermode (overAlu);
        canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+       gc.resetXfermode ();
       }
+
+    canvas.restore ();
+    destination.damageRect (rect);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java 
b/java/org/gnu/emacs/EmacsDrawLine.java
index 6389031bbf..8941d4c217 100644
--- a/java/org/gnu/emacs/EmacsDrawLine.java
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -29,109 +29,49 @@ import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.Xfermode;
 
-public class EmacsDrawLine implements EmacsPaintReq
+public class EmacsDrawLine
 {
-  private int x, y, x2, y2;
-  private EmacsDrawable drawable;
-  private EmacsGC immutableGC;
-  private static Xfermode xorAlu, srcInAlu;
-
-  static
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int x2, int y2)
   {
-    xorAlu = new PorterDuffXfermode (Mode.XOR);
-    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
-  };
+    Rect rect;
+    Canvas canvas;
+    Paint paint;
+    int i;
 
-  public
-  EmacsDrawLine (EmacsDrawable drawable, int x, int y,
-                int x2, int y2, EmacsGC immutableGC)
-  {
-    this.drawable = drawable;
-    this.x = x;
-    this.y = y;
-    this.x2 = x2;
-    this.y2 = y2;
-    this.immutableGC = immutableGC;
-  }
+    /* TODO implement stippling.  */
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
 
-  @Override
-  public Rect
-  getRect ()
-  {
-    return new Rect (Math.min (x, x2 + 1),
+    paint = gc.gcPaint;
+    rect = new Rect (Math.min (x, x2 + 1),
                     Math.min (y, y2 + 1),
                     Math.max (x2 + 1, x),
                     Math.max (y2 + 1, y));
-  }
-
-  @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, srcRect;
-    int width, height;
+    canvas = drawable.lockCanvas ();
 
-    /* TODO implement stippling.  */
-    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+    if (canvas == null)
       return;
 
-    alu = immutableGC.function;
-    rect = getRect ();
-    width = rect.width ();
-    height = rect.height ();
-
-    paint.setStyle (Paint.Style.STROKE);
-
-    if (alu == EmacsGC.GC_COPY)
-      paint.setXfermode (null);
-    else
-      paint.setXfermode (xorAlu);
+    canvas.save ();
 
-    if (immutableGC.clip_mask == null)
-      {
-        paint.setColor (immutableGC.foreground | 0xff000000);
-       canvas.drawLine ((float) x, (float) y,
-                        (float) x2, (float) y2,
-                        paint);
-      }
-    else
+    if (gc.real_clip_rects != null)
       {
-       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);
+       for (i = 0; i < gc.real_clip_rects.length; ++i)
+         canvas.clipRect (gc.real_clip_rects[i]);
       }
 
-    paint.setXfermode (null);
+    paint.setStyle (Paint.Style.STROKE);
+
+    if (gc.clip_mask == null)
+      canvas.drawLine ((float) x, (float) y,
+                      (float) x2, (float) y2,
+                      paint);
+
+    /* DrawLine with clip mask not implemented; it is not used by
+       Emacs.  */
+    canvas.restore ();
+    drawable.damageRect (rect);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java 
b/java/org/gnu/emacs/EmacsDrawPoint.java
index 772757ff42..3bc7be1796 100644
--- a/java/org/gnu/emacs/EmacsDrawPoint.java
+++ b/java/org/gnu/emacs/EmacsDrawPoint.java
@@ -19,12 +19,13 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
-public class EmacsDrawPoint extends EmacsDrawRectangle
+public class EmacsDrawPoint
 {
-  public
-  EmacsDrawPoint (EmacsDrawable drawable, int x, int y,
-                 EmacsGC immutableGC)
+  public static void
+  perform (EmacsDrawable drawable,
+          EmacsGC immutableGC, int x, int y)
   {
-    super (drawable, x, y, 1, 1, immutableGC);
+    EmacsDrawRectangle.perform (drawable, immutableGC,
+                               x, y, 1, 1);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
index e3f2822714..b42e9556e8 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -22,110 +22,104 @@ package org.gnu.emacs;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 
-public class EmacsDrawRectangle implements EmacsPaintReq
-{
-  private int x, y, width, height;
-  private EmacsDrawable drawable;
-  private EmacsGC immutableGC;
-  private static Xfermode xorAlu, srcInAlu;
-
-  static
-  {
-    xorAlu = new PorterDuffXfermode (Mode.XOR);
-    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
-  };
-
-  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;
-  }
+import android.util.Log;
 
-  @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)
+public class EmacsDrawRectangle
+{
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int width, int height)
   {
-    int alu;
-    Paint maskPaint;
+    int i;
+    Paint maskPaint, paint;
     Canvas maskCanvas;
     Bitmap maskBitmap;
-    Rect rect, srcRect;
+    Rect rect;
+    Rect maskRect, dstRect;
+    Canvas canvas;
+    Bitmap clipBitmap;
 
     /* TODO implement stippling.  */
-    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       return;
 
-    alu = immutableGC.function;
-    rect = new Rect (x, y, x + width, y + height);
+    canvas = drawable.lockCanvas ();
 
-    paint.setStyle (Paint.Style.STROKE);
-    paint.setStrokeWidth (1);
+    if (canvas == null)
+      return;
 
-    if (alu == EmacsGC.GC_COPY)
-      paint.setXfermode (null);
-    else
-      paint.setXfermode (xorAlu);
+    canvas.save ();
 
-    if (immutableGC.clip_mask == null)
+    if (gc.real_clip_rects != null)
       {
-        paint.setColor (immutableGC.foreground | 0xff000000);
-       canvas.drawRect (rect, paint);
+       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.STROKE);
+
+    if (gc.clip_mask == null)
+      canvas.drawRect (rect, paint);
     else
       {
-       maskPaint = new Paint ();
-       maskBitmap
-         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
-                                              true);
-
-       if (maskBitmap == null)
+       /* Drawing with a clip mask involves calculating the
+          intersection of the clip mask with the dst rect, and
+          extrapolating the corresponding part of the src rect.  */
+       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 (!maskRect.setIntersect (dstRect, maskRect))
+         /* There is no intersection between the clip mask and the
+            dest rect.  */
          return;
 
-       maskPaint.setXfermode (srcInAlu);
-       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       /* Finally, create a temporary bitmap that is the size of
+          maskRect.  */
+
+       maskBitmap
+         = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+                                Bitmap.Config.ARGB_8888);
+
+       /* Draw the mask onto the maskBitmap.  */
        maskCanvas = new Canvas (maskBitmap);
-       srcRect = new Rect (0, 0, maskBitmap.getWidth (),
-                           maskBitmap.getHeight ());
-       maskCanvas.drawRect (srcRect, maskPaint);
-       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+       maskRect.offset (-gc.clip_x_origin,
+                        -gc.clip_y_origin);
+       maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+                              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));
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawable.java 
b/java/org/gnu/emacs/EmacsDrawable.java
index 1906213721..6a6199ff21 100644
--- a/java/org/gnu/emacs/EmacsDrawable.java
+++ b/java/org/gnu/emacs/EmacsDrawable.java
@@ -26,7 +26,6 @@ import android.graphics.Canvas;
 public interface EmacsDrawable
 {
   public Canvas lockCanvas ();
-  public void unlockCanvas ();
   public void damageRect (Rect damageRect);
   public Bitmap getBitmap ();
   public boolean isDestroyed ();
diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java 
b/java/org/gnu/emacs/EmacsFillPolygon.java
index 3198c7f07c..42b73886df 100644
--- a/java/org/gnu/emacs/EmacsFillPolygon.java
+++ b/java/org/gnu/emacs/EmacsFillPolygon.java
@@ -26,34 +26,35 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Xfermode;
 
-public class EmacsFillPolygon implements EmacsPaintReq
+public class EmacsFillPolygon
 {
-  private EmacsDrawable drawable;
-  private EmacsGC immutableGC;
-  private Path path;
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc, Point points[])
+  {
+    Canvas canvas;
+    Path path;
+    Paint paint;
+    Rect rect;
+    RectF rectF;
+    int i;
 
-  private static Xfermode xorAlu, srcInAlu;
+    canvas = drawable.lockCanvas ();
 
-  static
-  {
-    xorAlu = new PorterDuffXfermode (Mode.XOR);
-    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
-  };
+    if (canvas == null)
+      return;
 
-  public
-  EmacsFillPolygon (EmacsDrawable drawable, Point points[],
-                   EmacsGC immutableGC)
-  {
-    int i;
+    paint = gc.gcPaint;
+
+    canvas.save ();
 
-    this.drawable = drawable;
-    this.immutableGC = immutableGC;
+    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.  */
     path = new Path ();
@@ -67,84 +68,25 @@ public class EmacsFillPolygon implements EmacsPaintReq
 
        path.close ();
       }
-  }
-
-  @Override
-  public Rect
-  getRect ()
-  {
-    RectF rect;
 
-    rect = new RectF (0, 0, 0, 0);
-    path.computeBounds (rect, true);
+    /* Compute the damage rectangle.  */
+    rectF = new RectF (0, 0, 0, 0);
+    path.computeBounds (rectF, true);
 
-    return new Rect ((int) Math.floor (rect.left),
-                    (int) Math.floor (rect.top),
-                    (int) Math.ceil (rect.right),
-                    (int) Math.ceil (rect.bottom));
-  }
+    rect = new Rect ((int) Math.floor (rectF.left),
+                    (int) Math.floor (rectF.top),
+                    (int) Math.ceil (rectF.right),
+                    (int) Math.ceil (rectF.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 ();
+    paint.setStyle (Paint.Style.FILL);
 
-    if (alu == EmacsGC.GC_COPY)
-      paint.setXfermode (null);
-    else
-      paint.setXfermode (xorAlu);
+    if (gc.clip_mask == null)
+      canvas.drawPath (path, paint);
 
-    paint.setStyle (Paint.Style.FILL);
+    canvas.restore ();
+    drawable.damageRect (rect);
 
-    if (immutableGC.clip_mask == null)
-      {
-       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)
-         return;
-
-       maskPaint.setXfermode (srcInAlu);
-       maskPaint.setColor (immutableGC.foreground | 0xff000000);
-       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);
-      }
+    /* FillPolygon with clip mask not implemented; it is not used by
+       Emacs.  */
   }
 }
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java 
b/java/org/gnu/emacs/EmacsFillRectangle.java
index 7246c13de7..b733b417d6 100644
--- a/java/org/gnu/emacs/EmacsFillRectangle.java
+++ b/java/org/gnu/emacs/EmacsFillRectangle.java
@@ -22,108 +22,102 @@ package org.gnu.emacs;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 
 import android.util.Log;
 
-public class EmacsFillRectangle implements EmacsPaintReq
+public class EmacsFillRectangle
 {
-  private int x, y, width, height;
-  private EmacsDrawable drawable;
-  private EmacsGC immutableGC;
-  private static Xfermode xorAlu, srcInAlu;
-
-  static
-  {
-    xorAlu = new PorterDuffXfermode (Mode.XOR);
-    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
-  };
-
-  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 ()
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int width, int height)
   {
-    return drawable;
-  }
-
-  @Override
-  public EmacsGC
-  getGC ()
-  {
-    return immutableGC;
-  }
-
-  @Override
-  public void
-  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
-  {
-    int alu;
-    Paint maskPaint;
+    int i;
+    Paint maskPaint, paint;
     Canvas maskCanvas;
     Bitmap maskBitmap;
-    Rect rect, srcRect;
+    Rect rect;
+    Rect maskRect, dstRect;
+    Canvas canvas;
+    Bitmap clipBitmap;
 
     /* TODO implement stippling.  */
-    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       return;
 
-    alu = immutableGC.function;
-    rect = getRect ();
+    canvas = drawable.lockCanvas ();
 
-    paint.setStyle (Paint.Style.FILL);
+    if (canvas == null)
+      return;
 
-    if (alu == EmacsGC.GC_COPY)
-      paint.setXfermode (null);
-    else
-      paint.setXfermode (xorAlu);
+    canvas.save ();
 
-    if (immutableGC.clip_mask == null)
+    if (gc.real_clip_rects != null)
       {
-        paint.setColor (immutableGC.foreground | 0xff000000);
-       canvas.drawRect (rect, paint);
+       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);
+
+    if (gc.clip_mask == null)
+      canvas.drawRect (rect, paint);
     else
       {
-       maskPaint = new Paint ();
-       maskBitmap
-         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
-                                              true);
-
-       if (maskBitmap == null)
+       /* Drawing with a clip mask involves calculating the
+          intersection of the clip mask with the dst rect, and
+          extrapolating the corresponding part of the src rect.  */
+       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 (!maskRect.setIntersect (dstRect, maskRect))
+         /* There is no intersection between the clip mask and the
+            dest rect.  */
          return;
 
-       maskPaint.setXfermode (srcInAlu);
-       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       /* Finally, create a temporary bitmap that is the size of
+          maskRect.  */
+
+       maskBitmap
+         = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+                                Bitmap.Config.ARGB_8888);
+
+       /* Draw the mask onto the maskBitmap.  */
        maskCanvas = new Canvas (maskBitmap);
-       srcRect = new Rect (0, 0, maskBitmap.getWidth (),
-                           maskBitmap.getHeight ());
-       maskCanvas.drawRect (srcRect, maskPaint);
-       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+       maskRect.offset (-gc.clip_x_origin,
+                        -gc.clip_y_origin);
+       maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+                              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);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
index 9f40aa04c4..1d1e6f7b33 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -164,7 +164,7 @@ public abstract class EmacsFontDriver
   public static EmacsFontDriver
   createFontDriver ()
   {
-    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
       return new EmacsSdk23FontDriver ();
 
     return new EmacsSdk7FontDriver ();
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java
index 0becb04519..caa5c91edd 100644
--- a/java/org/gnu/emacs/EmacsGC.java
+++ b/java/org/gnu/emacs/EmacsGC.java
@@ -20,6 +20,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 package org.gnu.emacs;
 
 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
    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_OPAQUE_STIPPLED      = 1;
 
+  public static final Xfermode xorAlu, srcInAlu;
+
   public int function, fill_style;
   public int foreground, background;
   public int clip_x_origin, clip_y_origin;
   public int ts_origin_x, ts_origin_y;
-  public Rect clip_rects[];
+  public Rect clip_rects[], real_clip_rects[];
   public EmacsPixmap clip_mask, stipple;
-  private boolean dirty;
-  private EmacsGC immutableGC;
+  public Paint gcPaint;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  }
 
   /* The following fields are only set on immutable GCs.  */
 
@@ -55,62 +67,41 @@ public class EmacsGC extends EmacsHandleObject
     fill_style = GC_FILL_SOLID;
     function = GC_COPY;
     foreground = 0;
-    background = 0xffffffff;
+    background = 0xffffff;
+    gcPaint = new Paint ();
   }
 
-  public
-  EmacsGC (EmacsGC source)
-  {
-    super ((short) 0);
+  /* Mark this GC as dirty.  Apply parameters to the paint and
+     recompute real_clip_rects.  */
 
+  public void
+  markDirty ()
+  {
     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];
+       real_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);
+           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;
 
-  /* Mark this GC as dirty.  This means immutableGC will return a new
-     copy of this GC the next time it is called.  */
+    gcPaint.setColor (foreground | 0xff000000);
+    gcPaint.setXfermode (function == GC_XOR
+                        ? xorAlu : srcInAlu);
+  }
 
   public void
-  markDirty ()
+  resetXfermode ()
   {
-    dirty = true;
+    gcPaint.setXfermode (function == GC_XOR
+                        ? xorAlu : srcInAlu);
   }
-
-  public EmacsGC
-  immutableGC ()
-  {
-    if (immutableGC == null || dirty)
-      {
-       immutableGC = new EmacsGC (this);
-       dirty = false;
-      }
-
-    return immutableGC;
-  };
 };
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index c80339031a..ae48ce3040 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -79,6 +79,28 @@ public class EmacsNative
   /* Send an ANDROID_WINDOW_ACTION event.  */
   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
   {
     System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
index 897902c5b5..15452f007c 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -93,6 +93,8 @@ public class EmacsPixmap extends EmacsHandleObject
        break;
       }
 
+    bitmap.eraseColor (0xff000000);
+
     this.width = width;
     this.height = height;
     this.depth = depth;
@@ -108,13 +110,6 @@ public class EmacsPixmap extends EmacsHandleObject
     return canvas;
   }
 
-  @Override
-  public void
-  unlockCanvas ()
-  {
-
-  }
-
   @Override
   public void
   damageRect (Rect damageRect)
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
index 34e2b1803a..11e128d576 100644
--- a/java/org/gnu/emacs/EmacsSdk23FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -20,9 +20,80 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 package org.gnu.emacs;
 
 import android.graphics.Paint;
+import android.graphics.Rect;
 
 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
   public int
   hasChar (FontSpec font, char charCode)
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 437f38e62d..8a9426050a 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -243,93 +243,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     }
   };
 
-  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 Sdk7Typeface[] typefaceList;
   private Sdk7Typeface fallbackTypeface;
@@ -498,14 +411,11 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
                Paint paint, Rect bounds)
   {
     char[] text;
-    float[] width;
 
     text = new char[1];
     text[0] = (char) code;
 
     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.
        Translate these into XCharStruct values.
@@ -526,7 +436,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     metrics.rbearing = (short) bounds.right;
     metrics.ascent = (short) -bounds.top;
     metrics.descent = (short) bounds.bottom;
-    metrics.width = (short) Math.round (width[0]);
+    metrics.width = (short) paint.measureText ("" + text[0]);
   }
 
   @Override
@@ -563,7 +473,8 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
        for (i = 0; i < code.length; ++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);
 
        fontMetrics.lbearing = (short) boundsCache.left;
@@ -587,11 +498,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
        int[] chars, int x, int y, int backgroundWidth,
        boolean withBackground)
   {
-    Rect backgroundRect;
+    Rect backgroundRect, bounds;
     Sdk7FontObject sdk7FontObject;
-    Sdk7DrawString op;
     char[] charsArray;
     int i;
+    Canvas canvas;
+    Paint paint;
 
     sdk7FontObject = (Sdk7FontObject) fontObject;
     charsArray = new char[chars.length];
@@ -605,12 +517,41 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     backgroundRect.right = x + backgroundWidth;
     backgroundRect.bottom = y + sdk7FontObject.descent;
 
-    op = new Sdk7DrawString (sdk7FontObject, charsArray,
-                            gc.immutableGC (), drawable,
-                            withBackground,
-                            backgroundRect, x, y);
+    canvas = drawable.lockCanvas ();
+
+    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);
+      }
 
-    EmacsService.SERVICE.appendPaintOperation (op);
+    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;
   }
 };
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 41a45b0bd8..4444b7f1c5 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -19,7 +19,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
-import java.lang.Runnable;
 import java.io.IOException;
 import java.util.List;
 import java.util.ArrayList;
@@ -59,7 +58,6 @@ public class EmacsService extends Service
 
   private EmacsThread thread;
   private Handler handler;
-  private EmacsPaintQueue paintQueue;
 
   /* Display metrics used by font backends.  */
   public DisplayMetrics metrics;
@@ -191,126 +189,42 @@ public class EmacsService extends Service
     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
   fillRectangle (EmacsDrawable drawable, EmacsGC gc,
                 int x, int y, int width, int height)
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsFillRectangle (drawable, x, y,
-                                 width, height,
-                                 gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsFillRectangle.perform (drawable, gc, x, y,
+                               width, height);
   }
 
   public void
   fillPolygon (EmacsDrawable drawable, EmacsGC gc,
               Point points[])
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsFillPolygon (drawable, points,
-                               gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsFillPolygon.perform (drawable, gc, points);
   }
 
   public void
   drawRectangle (EmacsDrawable drawable, EmacsGC gc,
                 int x, int y, int width, int height)
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsDrawRectangle (drawable, x, y,
-                                 width, height,
-                                 gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsDrawRectangle.perform (drawable, gc, x, y,
+                               width, height);
   }
 
   public void
   drawLine (EmacsDrawable drawable, EmacsGC gc,
            int x, int y, int x2, int y2)
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsDrawLine (drawable, x, y,
-                            x2, y2,
-                            gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsDrawLine.perform (drawable, gc, x, y,
+                          x2, y2);
   }
 
   public void
   drawPoint (EmacsDrawable drawable, EmacsGC gc,
             int x, int y)
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsDrawPoint (drawable, x, y,
-                             gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsDrawPoint.perform (drawable, gc, x, y);
   }
 
   public void
@@ -319,15 +233,9 @@ public class EmacsService extends Service
            int srcX, int srcY, int width, int height, int destX,
            int destY)
   {
-    EmacsPaintReq req;
-
-    ensurePaintQueue ();
-
-    req = new EmacsCopyArea (srcDrawable, dstDrawable,
-                            srcX, srcY, width, height, destX,
-                            destY, gc.immutableGC ());
-    paintQueue.appendPaintOperation (req);
-    checkFlush ();
+    EmacsCopyArea.perform (srcDrawable, gc, dstDrawable,
+                          srcX, srcY, width, height, destX,
+                          destY);
   }
 
   public void
@@ -342,12 +250,4 @@ public class EmacsService extends Service
   {
     window.clearArea (x, y, width, height);
   }
-
-  public void
-  appendPaintOperation (EmacsPaintReq op)
-  {
-    ensurePaintQueue ();
-    paintQueue.appendPaintOperation (op);
-    checkFlush ();
-  }
 };
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index b8b828e482..5efb888226 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -29,6 +29,7 @@ import android.graphics.Rect;
 
 public class EmacsSurfaceView extends SurfaceView
 {
+  public Object surfaceChangeLock;
   private boolean created;
 
   public
@@ -36,33 +37,36 @@ public class EmacsSurfaceView extends SurfaceView
   {
     super (view.getContext ());
 
+    surfaceChangeLock = new Object ();
+
     getHolder ().addCallback (new SurfaceHolder.Callback () {
        @Override
        public void
        surfaceChanged (SurfaceHolder holder, int format,
                        int width, int height)
        {
-         /* Force a buffer swap now to get the contents of the Emacs
-            view on screen.  */
-         view.swapBuffers (true);
+         view.swapBuffers ();
        }
 
        @Override
        public void
        surfaceCreated (SurfaceHolder holder)
        {
-         created = true;
-
-         /* Force a buffer swap now to get the contents of the Emacs
-            view on screen.  */
-         view.swapBuffers (true);
+         synchronized (surfaceChangeLock)
+           {
+             created = true;
+             view.swapBuffers ();
+           }
        }
 
        @Override
        public void
        surfaceDestroyed (SurfaceHolder holder)
        {
-         created = false;
+         synchronized (surfaceChangeLock)
+           {
+             created = false;
+           }
        }
       });
   }
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 7b48eaf0aa..169a1e42ee 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -23,6 +23,7 @@ import android.content.res.ColorStateList;
 
 import android.view.View;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.ViewGroup;
 
 import android.graphics.Bitmap;
@@ -64,6 +65,14 @@ public class EmacsView extends ViewGroup
   /* The associated surface view.  */
   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
   EmacsView (EmacsWindow window)
   {
@@ -81,6 +90,43 @@ public class EmacsView extends ViewGroup
     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
   protected void
   onMeasure (int widthMeasureSpec, int heightMeasureSpec)
@@ -114,7 +160,7 @@ public class EmacsView extends ViewGroup
   }
 
   @Override
-  protected void
+  protected synchronized void
   onLayout (boolean changed, int left, int top, int right,
            int bottom)
   {
@@ -122,19 +168,15 @@ public class EmacsView extends ViewGroup
     View child;
     Rect windowRect;
 
-    if (changed)
+    if (changed || mustReportLayout)
       {
+       mustReportLayout = false;
        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 ();
 
     for (i = 0; i < count; ++i)
@@ -159,47 +201,60 @@ public class EmacsView extends ViewGroup
       }
   }
 
-  public void
+  public synchronized void
   damageRect (Rect 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)
   {
-    Bitmap back;
     Canvas canvas;
     Rect damageRect;
+    Bitmap bitmap;
 
     if (damageRegion.isEmpty ())
       return;
 
-    if (!surfaceView.isCreated ())
-      return;
+    bitmap = getBitmap ();
 
-    if (bitmap == null)
-      return;
+    /* 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)
+      {
+       damageRect = damageRegion.getBounds ();
 
-    /* Lock the canvas with the specified damage.  */
-    damageRect = damageRegion.getBounds ();
-    canvas = surfaceView.lockCanvas (damageRect);
+       if (!surfaceView.isCreated ())
+         return;
 
-    /* Return if locking the canvas failed.  */
-    if (canvas == null)
-      return;
+       if (bitmap == null)
+         return;
+
+       /* Lock the canvas with the specified damage.  */
+       canvas = surfaceView.lockCanvas (damageRect);
 
-    /* Copy from the back buffer to the canvas.  If damageRect was
-       made empty, then draw the entire back buffer.  */
+       /* Return if locking the canvas failed.  */
+       if (canvas == null)
+         return;
 
-    if (damageRect.isEmpty ())
-      canvas.drawBitmap (bitmap, 0f, 0f, paint);
-    else
-      canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+       /* Copy from the back buffer to the canvas.  If damageRect was
+          made empty, then draw the entire back buffer.  */
 
-    /* Unlock the canvas and clear the damage.  */
-    surfaceView.unlockCanvasAndPost (canvas);
-    damageRegion.setEmpty ();
+       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
@@ -241,4 +296,18 @@ public class EmacsView extends ViewGroup
     super.onFocusChanged (gainFocus, direction,
                          previouslyFocusedRect);
   }
+
+  @Override
+  public boolean
+  onGenericMotionEvent (MotionEvent motion)
+  {
+    return window.onSomeKindOfMotionEvent (motion);
+  }
+
+  @Override
+  public boolean
+  onTouchEvent (MotionEvent motion)
+  {
+    return window.onSomeKindOfMotionEvent (motion);
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index 26e788a20a..a9a24b6186 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -31,8 +31,13 @@ import android.graphics.Point;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.InputDevice;
 
 import android.content.Intent;
+import android.util.Log;
+
+import android.os.Build;
 
 /* This defines a window, which is a handle.  Windows represent a
    rectangular subset of the screen with their own contents.
@@ -68,6 +73,10 @@ public class EmacsWindow extends EmacsHandleObject
      window background.  */
   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
   EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
               int width, int height)
@@ -212,6 +221,7 @@ public class EmacsWindow extends EmacsHandleObject
        public void
        run ()
        {
+         view.mustReportLayout = true;
          view.requestLayout ();
        }
       });
@@ -224,6 +234,8 @@ public class EmacsWindow extends EmacsHandleObject
       {
        rect.right = rect.left + width;
        rect.bottom = rect.top + height;
+
+       requestViewLayout ();
       }
   }
 
@@ -279,17 +291,7 @@ public class EmacsWindow extends EmacsHandleObject
   public Canvas
   lockCanvas ()
   {
-    if (view.canvas != null)
-      return view.canvas;
-
-    return null;
-  }
-
-  @Override
-  public void
-  unlockCanvas ()
-  {
-
+    return view.getCanvas ();
   }
 
   @Override
@@ -302,17 +304,7 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   swapBuffers ()
   {
-    /* Before calling swapBuffers, make sure to flush the paint
-       queue.  */
-    EmacsService.SERVICE.flushPaintQueue ();
-    view.post (new Runnable () {
-       @Override
-       public void
-       run ()
-       {
-         view.swapBuffers ();
-       }
-      });
+    view.swapBuffers ();
   }
 
   public void
@@ -337,7 +329,7 @@ public class EmacsWindow extends EmacsHandleObject
   public Bitmap
   getBitmap ()
   {
-    return view.bitmap;
+    return view.getBitmap ();
   }
 
   public void
@@ -357,6 +349,7 @@ public class EmacsWindow extends EmacsHandleObject
                                 recognized as an ASCII key press
                                 event.  */
                              event.getUnicodeChar (state));
+    lastModifiers = event.getModifiers ();
   }
 
   public void
@@ -372,6 +365,7 @@ public class EmacsWindow extends EmacsHandleObject
                                event.getModifiers (),
                                keyCode,
                                event.getUnicodeChar (state));
+    lastModifiers = event.getModifiers ();
   }
 
   public void
@@ -386,4 +380,105 @@ public class EmacsWindow extends EmacsHandleObject
     /* Destroy the associated frame when the activity is detached.  */
     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;
+  }
 };



reply via email to

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