@@ -88,15 +88,15 @@ public final class EmacsView extends ViewGroup
88
88
/* Whether or not a popup is active. */
89
89
private boolean popupActive ;
90
90
91
+ /* Whether the back buffer has been updated since the last swap. */
92
+ private boolean unswapped ;
93
+
91
94
/* The current context menu. */
92
95
private EmacsContextMenu contextMenu ;
93
96
94
97
/* The last measured width and height. */
95
98
private int measuredWidth , measuredHeight ;
96
99
97
- /* Object acting as a lock for those values. */
98
- private Object dimensionsLock ;
99
-
100
100
/* The serial of the last clip rectangle change. */
101
101
private long lastClipSerial ;
102
102
@@ -153,9 +153,6 @@ public final class EmacsView extends ViewGroup
153
153
154
154
/* Add this view as its own global layout listener. */
155
155
getViewTreeObserver ().addOnGlobalLayoutListener (this );
156
-
157
- /* Create an object used as a lock. */
158
- this .dimensionsLock = new Object ();
159
156
}
160
157
161
158
private void
@@ -164,12 +161,9 @@ public final class EmacsView extends ViewGroup
164
161
Bitmap oldBitmap ;
165
162
int measuredWidth , measuredHeight ;
166
163
167
- synchronized (dimensionsLock )
168
- {
169
- /* Load measuredWidth and measuredHeight. */
170
- measuredWidth = this .measuredWidth ;
171
- measuredHeight = this .measuredHeight ;
172
- }
164
+ /* Load measuredWidth and measuredHeight. */
165
+ measuredWidth = this .measuredWidth ;
166
+ measuredHeight = this .measuredHeight ;
173
167
174
168
if (measuredWidth == 0 || measuredHeight == 0 )
175
169
return ;
@@ -231,8 +225,14 @@ public final class EmacsView extends ViewGroup
231
225
public synchronized Bitmap
232
226
getBitmap ()
233
227
{
234
- if (bitmapDirty || bitmap == null )
228
+ /* Never alter the bitmap if modifications have been received that
229
+ are still to be copied to the front buffer, as this indicates
230
+ that redisplay is in the process of copying matrix contents to
231
+ the glass, and such events as generally prompt a complete
232
+ regeneration of the frame's contents might not be processed. */
233
+ if (!unswapped && (bitmapDirty || bitmap == null ))
235
234
handleDirtyBitmap ();
235
+ unswapped = true ;
236
236
237
237
return bitmap ;
238
238
}
@@ -242,11 +242,12 @@ public final class EmacsView extends ViewGroup
242
242
{
243
243
int i ;
244
244
245
- if (bitmapDirty || bitmap == null )
245
+ if (! unswapped && ( bitmapDirty || bitmap == null ) )
246
246
handleDirtyBitmap ();
247
247
248
248
if (canvas == null )
249
249
return null ;
250
+ unswapped = true ;
250
251
251
252
/* Update clip rectangles if necessary. */
252
253
if (gc .clipRectID != lastClipSerial )
@@ -266,14 +267,11 @@ public final class EmacsView extends ViewGroup
266
267
return canvas ;
267
268
}
268
269
269
- public void
270
+ public synchronized void
270
271
prepareForLayout (int wantedWidth , int wantedHeight )
271
272
{
272
- synchronized (dimensionsLock )
273
- {
274
- measuredWidth = wantedWidth ;
275
- measuredHeight = wantedWidth ;
276
- }
273
+ measuredWidth = wantedWidth ;
274
+ measuredHeight = wantedWidth ;
277
275
}
278
276
279
277
@ Override
@@ -329,8 +327,8 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
329
327
}
330
328
331
329
/* Note that the monitor lock for the window must never be held from
332
- within the lock for the view, because the window also locks the
333
- other way around . */
330
+ within that for the view, because the window acquires locks in the
331
+ opposite direction . */
334
332
335
333
@ Override
336
334
protected void
@@ -346,7 +344,7 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
346
344
count = getChildCount ();
347
345
needExpose = false ;
348
346
349
- synchronized (dimensionsLock )
347
+ synchronized (this )
350
348
{
351
349
/* Load measuredWidth and measuredHeight. */
352
350
oldMeasuredWidth = measuredWidth ;
@@ -355,48 +353,48 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
355
353
/* Set measuredWidth and measuredHeight. */
356
354
measuredWidth = right - left ;
357
355
measuredHeight = bottom - top ;
358
- }
359
356
360
- /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set changed
361
- to true as well. */
357
+ /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set
358
+ changed to true as well. */
362
359
363
- if (right - left != oldMeasuredWidth
364
- || bottom - top != oldMeasuredHeight )
365
- changed = true ;
360
+ if (right - left != oldMeasuredWidth
361
+ || bottom - top != oldMeasuredHeight )
362
+ changed = true ;
366
363
367
- /* Dirty the back buffer if the layout change resulted in the view
368
- being resized. */
369
-
370
- if (changed )
371
- {
372
- /* Expose the window upon a change in the view's size that
373
- prompts the creation of a new bitmap. */
374
- explicitlyDirtyBitmap ();
375
- needExpose = true ;
364
+ /* Dirty the back buffer if the layout change resulted in the view
365
+ being resized. */
376
366
377
- /* This might return NULL if this view is not attached. */
378
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R )
367
+ if (changed )
379
368
{
380
- /* If a toplevel view is focused and isCurrentlyTextEditor
381
- is enabled when the IME is hidden, clear
382
- isCurrentlyTextEditor so it isn't shown again if the
383
- user dismisses Emacs before returning. */
384
- rootWindowInsets = getRootWindowInsets ();
385
-
386
- if (isCurrentlyTextEditor
387
- && rootWindowInsets != null
388
- && isAttachedToWindow
389
- && !rootWindowInsets .isVisible (WindowInsets .Type .ime ())
390
- /* N.B. that the keyboard is dismissed during gesture
391
- navigation under Android 30, but the system is
392
- quite temperamental regarding whether the window is
393
- focused at that point. Ideally
394
- isCurrentlyTextEditor shouldn't be reset in that
395
- case, but detecting that situation appears to be
396
- impossible. Sigh. */
397
- && (window == EmacsActivity .focusedWindow
398
- && hasWindowFocus ()))
399
- isCurrentlyTextEditor = false ;
369
+ /* Expose the window upon a change in the view's size that
370
+ prompts the creation of a new bitmap. */
371
+ bitmapDirty = needExpose = true ;
372
+
373
+ /* This might return NULL if this view is not attached. */
374
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R )
375
+ {
376
+ /* If a toplevel view is focused and
377
+ isCurrentlyTextEditor is enabled when the IME is
378
+ hidden, clear isCurrentlyTextEditor so it isn't shown
379
+ again if the user dismisses Emacs before
380
+ returning. */
381
+ rootWindowInsets = getRootWindowInsets ();
382
+
383
+ if (isCurrentlyTextEditor
384
+ && rootWindowInsets != null
385
+ && isAttachedToWindow
386
+ && !rootWindowInsets .isVisible (WindowInsets .Type .ime ())
387
+ /* N.B. that the keyboard is dismissed during
388
+ gesture navigation under Android 30, but the
389
+ system is quite temperamental regarding whether
390
+ the window is focused at that point. Ideally
391
+ isCurrentlyTextEditor shouldn't be reset in that
392
+ case, but detecting that situation appears to be
393
+ impossible. Sigh. */
394
+ && (window == EmacsActivity .focusedWindow
395
+ && hasWindowFocus ()))
396
+ isCurrentlyTextEditor = false ;
397
+ }
400
398
}
401
399
}
402
400
@@ -449,6 +447,33 @@ else if (child.getVisibility () != GONE)
449
447
damageRegion .op (left , top , right , bottom , Region .Op .UNION );
450
448
}
451
449
450
+ /* Complete deferred reconfiguration of the front buffer after a
451
+ buffer swap completes, and generate Expose events for the same. */
452
+
453
+ private void
454
+ postSwapBuffers ()
455
+ {
456
+ if (!unswapped )
457
+ return ;
458
+
459
+ unswapped = false ;
460
+
461
+ /* If the bitmap is dirty, reconfigure the bitmap and
462
+ generate an Expose event to produce its contents. */
463
+
464
+ if ((bitmapDirty || bitmap == null )
465
+ /* Do not generate Expose events if handleDirtyBitmap will
466
+ not create a valid bitmap, or the consequent buffer swap
467
+ will produce another event, ad infinitum. */
468
+ && isAttachedToWindow && measuredWidth != 0
469
+ && measuredHeight != 0 )
470
+ {
471
+ handleDirtyBitmap ();
472
+ EmacsNative .sendExpose (this .window .handle , 0 , 0 ,
473
+ measuredWidth , measuredHeight );
474
+ }
475
+ }
476
+
452
477
/* This method is called from both the UI thread and the Emacs
453
478
thread. */
454
479
@@ -467,7 +492,10 @@ else if (child.getVisibility () != GONE)
467
492
/* Now see if there is a damage region. */
468
493
469
494
if (damageRegion .isEmpty ())
470
- return ;
495
+ {
496
+ postSwapBuffers ();
497
+ return ;
498
+ }
471
499
472
500
/* And extract and clear the damage region. */
473
501
@@ -479,6 +507,7 @@ else if (child.getVisibility () != GONE)
479
507
/* Transfer the bitmap to the surface view, then invalidate
480
508
it. */
481
509
surfaceView .setBitmap (bitmap , damageRect );
510
+ postSwapBuffers ();
482
511
}
483
512
}
484
513
@@ -758,13 +787,9 @@ else if (child.getVisibility () != GONE)
758
787
was called. */
759
788
bitmapDirty = true ;
760
789
761
- synchronized (dimensionsLock )
762
- {
763
- /* Now expose the view contents again. */
764
- EmacsNative .sendExpose (this .window .handle , 0 , 0 ,
765
- measuredWidth , measuredHeight );
766
- }
767
-
790
+ /* Now expose the view contents again. */
791
+ EmacsNative .sendExpose (this .window .handle , 0 , 0 ,
792
+ measuredWidth , measuredHeight );
768
793
super .onAttachedToWindow ();
769
794
}
770
795
0 commit comments