aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-02-10 18:57:51 +0800
committerPo Lu2023-02-10 18:57:51 +0800
commita1941cd7a7dc9a6f6b7239ec7d4bd3bdf5d55fc9 (patch)
treeabbb940a86dfc835ff22ec40d5387ea4a20a9e55 /java
parent60270d8ee30ee15b5ed74ac211b7cb35c58ad3f3 (diff)
downloademacs-a1941cd7a7dc9a6f6b7239ec7d4bd3bdf5d55fc9.tar.gz
emacs-a1941cd7a7dc9a6f6b7239ec7d4bd3bdf5d55fc9.zip
Update Android port
* doc/emacs/android.texi (Android Windowing): Remove yet another limitation. * java/debug.sh: Make this work on systems which prohibit attaching to app processes from adbd. * java/org/gnu/emacs/EmacsCopyArea.java (perform): Avoid creating copies whenever possible. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): Remove SurfaceView based implementation and use manual double buffering with invalidate instead. * java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap) (raise, lower, onDetachedFromWindow): Adjust accordingly. * java/org/gnu/emacs/EmacsWindow.java (windowUpdated): Remove function. * src/sfntfont.c (sfntfont_open): Set font->max_width correctly.
Diffstat (limited to 'java')
-rwxr-xr-xjava/debug.sh13
-rw-r--r--java/org/gnu/emacs/EmacsCopyArea.java24
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java159
-rw-r--r--java/org/gnu/emacs/EmacsView.java93
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java23
5 files changed, 112 insertions, 200 deletions
diff --git a/java/debug.sh b/java/debug.sh
index 7008664c049..2e95f9738c7 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -267,10 +267,14 @@ if [ -z "$gdbserver" ]; then
267 gdbserver_bin=/system/bin/gdbserver 267 gdbserver_bin=/system/bin/gdbserver
268else 268else
269 gdbserver_bin=/data/local/tmp/gdbserver 269 gdbserver_bin=/data/local/tmp/gdbserver
270 gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
271 \"tee gdbserver > /dev/null\""
270 272
271 # Upload the specified gdbserver binary to the device. 273 # Upload the specified gdbserver binary to the device.
272 adb -s $device push "$gdbserver" "$gdbserver_bin" 274 adb -s $device push "$gdbserver" "$gdbserver_bin"
273 adb -s $device shell chmod +x "$gdbserver_bin" 275 # Copy it to the user directory.
276 adb -s $device shell "$gdbserver_cat"
277 adb -s $device shell "run-as $package chmod +x gdbserver"
274fi 278fi
275 279
276# Now start gdbserver on the device asynchronously. 280# Now start gdbserver on the device asynchronously.
@@ -286,10 +290,9 @@ if [ -z "$gdbserver" ]; then
286else 290else
287 # Normally the program cannot access $gdbserver_bin when it is 291 # Normally the program cannot access $gdbserver_bin when it is
288 # placed in /data/local/tmp. 292 # placed in /data/local/tmp.
289 adb -s $device shell $gdbserver_bin --once \ 293 adb -s $device shell run-as $package "./gdbserver" --once \
290 "+/data/local/tmp/debug.$package.socket" \ 294 "0.0.0.0:7654" --attach $pid >&5 &
291 --attach $pid >&5 & 295 gdb_socket="tcp:7654"
292 gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
293fi 296fi
294 297
295# Wait until gdbserver successfully runs. 298# Wait until gdbserver successfully runs.
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java
index f8974e17c2e..11dc22e0456 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -110,11 +110,25 @@ public class EmacsCopyArea
110 110
111 if (gc.clip_mask == null) 111 if (gc.clip_mask == null)
112 { 112 {
113 bitmap = Bitmap.createBitmap (srcBitmap, 113 if (source == destination)
114 src_x, src_y, width, 114 {
115 height); 115 /* Create a copy of the bitmap, since Android can't handle
116 canvas.drawBitmap (bitmap, null, rect, paint); 116 overlapping copies. */
117 bitmap.recycle (); 117 bitmap = Bitmap.createBitmap (srcBitmap,
118 src_x, src_y, width,
119 height);
120 canvas.drawBitmap (bitmap, null, rect, paint);
121 bitmap.recycle ();
122 }
123 else
124 {
125 /* But here the bitmaps are known to not overlap, so avoid
126 that extra consing overhead. */
127
128 srcRect = new Rect (src_x, src_y, src_x + width,
129 src_y + height);
130 canvas.drawBitmap (srcBitmap, null, rect, paint);
131 }
118 } 132 }
119 else 133 else
120 { 134 {
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java
index f6cb77bb2b8..e9bae623930 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -19,127 +19,114 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19 19
20package org.gnu.emacs; 20package org.gnu.emacs;
21 21
22import android.view.SurfaceView; 22import android.view.View;
23import android.view.SurfaceHolder;
24 23
25import android.os.Build; 24import android.os.Build;
26 25
26import android.graphics.Bitmap;
27import android.graphics.Canvas; 27import android.graphics.Canvas;
28import android.graphics.Rect; 28import android.graphics.Rect;
29import android.graphics.Paint;
29 30
30import android.util.Log; 31/* This originally extended SurfaceView. However, doing so proved to
32 be too slow, and Android's surface view keeps up to three of its
33 own back buffers, which use too much memory (up to 96 MB for a
34 single frame.) */
31 35
32public class EmacsSurfaceView extends SurfaceView 36public class EmacsSurfaceView extends View
33{ 37{
34 private static final String TAG = "EmacsSurfaceView"; 38 private static final String TAG = "EmacsSurfaceView";
35 public Object surfaceChangeLock;
36 private boolean created;
37 private EmacsView view; 39 private EmacsView view;
38 40 private Bitmap frontBuffer;
39 /* This is the callback used on Android 8 to 25. */ 41 private Canvas bitmapCanvas;
40 42 private Bitmap bitmap;
41 private class Callback implements SurfaceHolder.Callback 43 private Paint bitmapPaint;
42 {
43 @Override
44 public void
45 surfaceChanged (SurfaceHolder holder, int format,
46 int width, int height)
47 {
48 Canvas canvas;
49
50 Log.d (TAG, "surfaceChanged: " + view + ", ");
51
52 view.swapBuffers (true);
53 }
54
55 @Override
56 public void
57 surfaceCreated (SurfaceHolder holder)
58 {
59 synchronized (surfaceChangeLock)
60 {
61 Log.d (TAG, "surfaceCreated: " + view);
62 created = true;
63 }
64
65 /* Drop the lock when doing this, or a deadlock can
66 result. */
67 view.swapBuffers (true);
68 }
69
70 @Override
71 public void
72 surfaceDestroyed (SurfaceHolder holder)
73 {
74 synchronized (surfaceChangeLock)
75 {
76 Log.d (TAG, "surfaceDestroyed: " + view);
77 created = false;
78 }
79 }
80 }
81 44
82 public 45 public
83 EmacsSurfaceView (final EmacsView view) 46 EmacsSurfaceView (final EmacsView view)
84 { 47 {
85 super (view.getContext ()); 48 super (view.getContext ());
86 49
87 this.surfaceChangeLock = new Object ();
88 this.view = view; 50 this.view = view;
89 51 this.bitmapPaint = new Paint ();
90 getHolder ().addCallback (new Callback ());
91 } 52 }
92 53
93 public boolean 54 private void
94 isCreated () 55 copyToFrontBuffer (Rect damageRect)
95 { 56 {
96 return created; 57 if (damageRect != null)
58 bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect,
59 bitmapPaint);
60 else
61 bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint);
97 } 62 }
98 63
99 public Canvas 64 private void
100 lockCanvas (Rect damage) 65 reconfigureFrontBuffer (Bitmap bitmap)
101 { 66 {
102 SurfaceHolder holder; 67 /* First, remove the old front buffer. */
103
104 holder = getHolder ();
105 68
106 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 69 if (frontBuffer != null)
107 { 70 {
108 damage.setEmpty (); 71 frontBuffer.recycle ();
109 return holder.lockHardwareCanvas (); 72 frontBuffer = null;
73 bitmapCanvas = null;
110 } 74 }
111 75
112 return holder.lockCanvas (damage); 76 this.bitmap = bitmap;
113 }
114 77
115 @Override 78 /* Next, create the new front buffer if necessary. */
116 protected void
117 onLayout (boolean changed, int left, int top, int right,
118 int bottom)
119 {
120 Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
121 + " " + bottom + " -- " + changed + " visibility "
122 + getVisibility ()));
123 }
124 79
125 /* This method is only used during debugging when it seems damage 80 if (bitmap != null && frontBuffer == null)
126 isn't working correctly. */ 81 {
82 frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
83 bitmap.getHeight (),
84 Bitmap.Config.ARGB_8888,
85 false);
86 bitmapCanvas = new Canvas (frontBuffer);
87
88 /* And copy over the bitmap contents. */
89 copyToFrontBuffer (null);
90 }
91 else if (bitmap != null)
92 /* Just copy over the bitmap contents. */
93 copyToFrontBuffer (null);
94 }
127 95
128 public Canvas 96 public synchronized void
129 lockCanvas () 97 setBitmap (Bitmap bitmap, Rect damageRect)
130 { 98 {
131 SurfaceHolder holder; 99 if (bitmap != this.bitmap)
100 reconfigureFrontBuffer (bitmap);
101 else if (bitmap != null)
102 copyToFrontBuffer (damageRect);
132 103
133 holder = getHolder (); 104 if (bitmap != null)
134 return holder.lockCanvas (); 105 {
106 /* In newer versions of Android, the invalid rectangle is
107 supposedly internally calculated by the system. How that
108 is done is unknown, but calling `invalidateRect' is now
109 deprecated.
110
111 Fortunately, nobody has deprecated the version of
112 `postInvalidate' that accepts a dirty rectangle. */
113
114 if (damageRect != null)
115 postInvalidate (damageRect.left, damageRect.top,
116 damageRect.right, damageRect.bottom);
117 else
118 postInvalidate ();
119 }
135 } 120 }
136 121
137 public void 122 @Override
138 unlockCanvasAndPost (Canvas canvas) 123 public synchronized void
124 onDraw (Canvas canvas)
139 { 125 {
140 SurfaceHolder holder; 126 /* Paint the view's bitmap; the bitmap might be recycled right
127 now. */
141 128
142 holder = getHolder (); 129 if (frontBuffer != null)
143 holder.unlockCanvasAndPost (canvas); 130 canvas.drawBitmap (frontBuffer, 0f, 0f, bitmapPaint);
144 } 131 }
145}; 132};
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index fac11870ebf..0416301101c 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -103,14 +103,6 @@ public class EmacsView extends ViewGroup
103 displayed whenever possible. */ 103 displayed whenever possible. */
104 public boolean isCurrentlyTextEditor; 104 public boolean isCurrentlyTextEditor;
105 105
106 /* An empty rectangle. */
107 public static final Rect emptyRect;
108
109 static
110 {
111 emptyRect = new Rect ();
112 };
113
114 public 106 public
115 EmacsView (EmacsWindow window) 107 EmacsView (EmacsWindow window)
116 { 108 {
@@ -127,14 +119,8 @@ public class EmacsView extends ViewGroup
127 119
128 /* Create the surface view. */ 120 /* Create the surface view. */
129 this.surfaceView = new EmacsSurfaceView (this); 121 this.surfaceView = new EmacsSurfaceView (this);
130 this.surfaceView.setZOrderMediaOverlay (true);
131 addView (this.surfaceView); 122 addView (this.surfaceView);
132 123
133 /* Not sure exactly what this does but it makes things magically
134 work. Why is something as simple as XRaiseWindow so involved
135 on Android? */
136 setChildrenDrawingOrderEnabled (true);
137
138 /* Get rid of the default focus highlight. */ 124 /* Get rid of the default focus highlight. */
139 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) 125 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
140 setDefaultFocusHighlightEnabled (false); 126 setDefaultFocusHighlightEnabled (false);
@@ -191,8 +177,12 @@ public class EmacsView extends ViewGroup
191 bitmapDirty = false; 177 bitmapDirty = false;
192 178
193 /* Explicitly free the old bitmap's memory. */ 179 /* Explicitly free the old bitmap's memory. */
180
194 if (oldBitmap != null) 181 if (oldBitmap != null)
195 oldBitmap.recycle (); 182 {
183 oldBitmap.recycle ();
184 surfaceView.setBitmap (null, null);
185 }
196 186
197 /* Some Android versions still don't free the bitmap until the 187 /* Some Android versions still don't free the bitmap until the
198 next GC. */ 188 next GC. */
@@ -342,67 +332,27 @@ public class EmacsView extends ViewGroup
342 thread. */ 332 thread. */
343 333
344 public void 334 public void
345 swapBuffers (boolean force) 335 swapBuffers ()
346 { 336 {
347 Canvas canvas; 337 Canvas canvas;
348 Rect damageRect; 338 Rect damageRect;
349 Bitmap bitmap; 339 Bitmap bitmap;
350 340
351 /* Code must always take damageRegion, and then surfaceChangeLock, 341 damageRect = null;
352 never the other way around! */
353 342
354 synchronized (damageRegion) 343 synchronized (damageRegion)
355 { 344 {
356 if (!force && damageRegion.isEmpty ()) 345 if (damageRegion.isEmpty ())
357 return; 346 return;
358 347
359 bitmap = getBitmap (); 348 bitmap = getBitmap ();
360 349
361 /* Emacs must take the following lock to ensure the access to the 350 /* Transfer the bitmap to the surface view, then invalidate
362 canvas occurs with the surface created. Otherwise, Android 351 it. */
363 will throttle calls to lockCanvas. */ 352 surfaceView.setBitmap (bitmap, damageRect);
364
365 synchronized (surfaceView.surfaceChangeLock)
366 {
367 if (!force)
368 damageRect = damageRegion.getBounds ();
369 else
370 damageRect = emptyRect;
371
372 if (!surfaceView.isCreated ())
373 return;
374
375 if (bitmap == null)
376 return;
377
378 /* Lock the canvas with the specified damage. */
379 canvas = surfaceView.lockCanvas (damageRect);
380
381 /* Return if locking the canvas failed. */
382 if (canvas == null)
383 return;
384
385 /* Copy from the back buffer to the canvas. If damageRect was
386 made empty, then draw the entire back buffer. */
387
388 if (damageRect.isEmpty ())
389 canvas.drawBitmap (bitmap, 0f, 0f, paint);
390 else
391 canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
392
393 /* Unlock the canvas and clear the damage. */
394 surfaceView.unlockCanvasAndPost (canvas);
395 damageRegion.setEmpty ();
396 }
397 } 353 }
398 } 354 }
399 355
400 public void
401 swapBuffers ()
402 {
403 swapBuffers (false);
404 }
405
406 @Override 356 @Override
407 public boolean 357 public boolean
408 onKeyDown (int keyCode, KeyEvent event) 358 onKeyDown (int keyCode, KeyEvent event)
@@ -486,16 +436,6 @@ public class EmacsView extends ViewGroup
486 return; 436 return;
487 437
488 parent.bringChildToFront (this); 438 parent.bringChildToFront (this);
489
490 /* Yes, all of this is really necessary! */
491 parent.requestLayout ();
492 parent.invalidate ();
493 requestLayout ();
494 invalidate ();
495
496 /* The surface view must be destroyed and recreated. */
497 removeView (surfaceView);
498 addView (surfaceView, 0);
499 } 439 }
500 440
501 public void 441 public void
@@ -511,16 +451,6 @@ public class EmacsView extends ViewGroup
511 return; 451 return;
512 452
513 parent.moveChildToBack (this); 453 parent.moveChildToBack (this);
514
515 /* Yes, all of this is really necessary! */
516 parent.requestLayout ();
517 parent.invalidate ();
518 requestLayout ();
519 invalidate ();
520
521 /* The surface view must be removed and attached again. */
522 removeView (surfaceView);
523 addView (surfaceView, 0);
524 } 454 }
525 455
526 @Override 456 @Override
@@ -574,6 +504,7 @@ public class EmacsView extends ViewGroup
574 bitmap.recycle (); 504 bitmap.recycle ();
575 bitmap = null; 505 bitmap = null;
576 canvas = null; 506 canvas = null;
507 surfaceView.setBitmap (null, null);
577 508
578 /* Collect the bitmap storage; it could be large. */ 509 /* Collect the bitmap storage; it could be large. */
579 Runtime.getRuntime ().gc (); 510 Runtime.getRuntime ().gc ();
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index e921b972c2c..9e2f2f53270 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -57,12 +57,6 @@ import android.os.Build;
57 Views are also drawables, meaning they can accept drawing 57 Views are also drawables, meaning they can accept drawing
58 requests. */ 58 requests. */
59 59
60/* Help wanted. What does not work includes `EmacsView.raise',
61 `EmacsView.lower', reparenting a window onto another window.
62
63 All three are likely undocumented restrictions within
64 EmacsSurface. */
65
66public class EmacsWindow extends EmacsHandleObject 60public class EmacsWindow extends EmacsHandleObject
67 implements EmacsDrawable 61 implements EmacsDrawable
68{ 62{
@@ -1111,21 +1105,4 @@ public class EmacsWindow extends EmacsHandleObject
1111 } 1105 }
1112 }); 1106 });
1113 } 1107 }
1114
1115 /* Notice that outstanding configure events have been processed.
1116 SERIAL is checked in the UI thread to verify that no new
1117 configure events have been generated in the mean time. */
1118
1119 public void
1120 windowUpdated (final long serial)
1121 {
1122 EmacsService.SERVICE.runOnUiThread (new Runnable () {
1123 @Override
1124 public void
1125 run ()
1126 {
1127 view.windowUpdated (serial);
1128 }
1129 });
1130 }
1131}; 1108};