diff options
| author | Po Lu | 2024-06-17 17:43:24 +0800 |
|---|---|---|
| committer | Po Lu | 2024-06-17 17:45:48 +0800 |
| commit | 82f0014273193d27c71a1fcb9be778c85cfa5e65 (patch) | |
| tree | 62792993251680cebe47bf1d50775a1f4e8a8d46 /java | |
| parent | 6aa5068ac71cb1b8e46c299138f99fea44319146 (diff) | |
| download | emacs-82f0014273193d27c71a1fcb9be778c85cfa5e65.tar.gz emacs-82f0014273193d27c71a1fcb9be778c85cfa5e65.zip | |
Reinforce bitmap reconfiguration on Android
* java/org/gnu/emacs/EmacsView.java (EmacsView) <unswapped>: New
field in which to record whether the back buffer has received
contents beyond those present at the last buffer swap.
<dimensionsLock>: Delete field.
(onAttachedToWindow, onLayout, handleDirtyBitmap)
(prepareForLayout): Rather, synchronize window dimensions with
the view.
(getCanvas, getBitmap): Do not reconfigure the back buffer
bitmap if such outstanding data exists.
(postSwapBuffers): New function.
(swapBuffers): If such outstanding data exists and the back
bufferis pending reconfiguration, recreate the back buffer and
report exposure.
* src/androidterm.c (handle_one_android_event): Fix indentation.
Diffstat (limited to 'java')
| -rw-r--r-- | java/org/gnu/emacs/EmacsView.java | 161 |
1 files changed, 93 insertions, 68 deletions
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 78d1ef785da..2ea54217837 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java | |||
| @@ -88,15 +88,15 @@ public final class EmacsView extends ViewGroup | |||
| 88 | /* Whether or not a popup is active. */ | 88 | /* Whether or not a popup is active. */ |
| 89 | private boolean popupActive; | 89 | private boolean popupActive; |
| 90 | 90 | ||
| 91 | /* Whether the back buffer has been updated since the last swap. */ | ||
| 92 | private boolean unswapped; | ||
| 93 | |||
| 91 | /* The current context menu. */ | 94 | /* The current context menu. */ |
| 92 | private EmacsContextMenu contextMenu; | 95 | private EmacsContextMenu contextMenu; |
| 93 | 96 | ||
| 94 | /* The last measured width and height. */ | 97 | /* The last measured width and height. */ |
| 95 | private int measuredWidth, measuredHeight; | 98 | private int measuredWidth, measuredHeight; |
| 96 | 99 | ||
| 97 | /* Object acting as a lock for those values. */ | ||
| 98 | private Object dimensionsLock; | ||
| 99 | |||
| 100 | /* The serial of the last clip rectangle change. */ | 100 | /* The serial of the last clip rectangle change. */ |
| 101 | private long lastClipSerial; | 101 | private long lastClipSerial; |
| 102 | 102 | ||
| @@ -153,9 +153,6 @@ public final class EmacsView extends ViewGroup | |||
| 153 | 153 | ||
| 154 | /* Add this view as its own global layout listener. */ | 154 | /* Add this view as its own global layout listener. */ |
| 155 | getViewTreeObserver ().addOnGlobalLayoutListener (this); | 155 | getViewTreeObserver ().addOnGlobalLayoutListener (this); |
| 156 | |||
| 157 | /* Create an object used as a lock. */ | ||
| 158 | this.dimensionsLock = new Object (); | ||
| 159 | } | 156 | } |
| 160 | 157 | ||
| 161 | private void | 158 | private void |
| @@ -164,12 +161,9 @@ public final class EmacsView extends ViewGroup | |||
| 164 | Bitmap oldBitmap; | 161 | Bitmap oldBitmap; |
| 165 | int measuredWidth, measuredHeight; | 162 | int measuredWidth, measuredHeight; |
| 166 | 163 | ||
| 167 | synchronized (dimensionsLock) | 164 | /* Load measuredWidth and measuredHeight. */ |
| 168 | { | 165 | measuredWidth = this.measuredWidth; |
| 169 | /* Load measuredWidth and measuredHeight. */ | 166 | measuredHeight = this.measuredHeight; |
| 170 | measuredWidth = this.measuredWidth; | ||
| 171 | measuredHeight = this.measuredHeight; | ||
| 172 | } | ||
| 173 | 167 | ||
| 174 | if (measuredWidth == 0 || measuredHeight == 0) | 168 | if (measuredWidth == 0 || measuredHeight == 0) |
| 175 | return; | 169 | return; |
| @@ -231,8 +225,14 @@ public final class EmacsView extends ViewGroup | |||
| 231 | public synchronized Bitmap | 225 | public synchronized Bitmap |
| 232 | getBitmap () | 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 | handleDirtyBitmap (); | 234 | handleDirtyBitmap (); |
| 235 | unswapped = true; | ||
| 236 | 236 | ||
| 237 | return bitmap; | 237 | return bitmap; |
| 238 | } | 238 | } |
| @@ -242,11 +242,12 @@ public final class EmacsView extends ViewGroup | |||
| 242 | { | 242 | { |
| 243 | int i; | 243 | int i; |
| 244 | 244 | ||
| 245 | if (bitmapDirty || bitmap == null) | 245 | if (!unswapped && (bitmapDirty || bitmap == null)) |
| 246 | handleDirtyBitmap (); | 246 | handleDirtyBitmap (); |
| 247 | 247 | ||
| 248 | if (canvas == null) | 248 | if (canvas == null) |
| 249 | return null; | 249 | return null; |
| 250 | unswapped = true; | ||
| 250 | 251 | ||
| 251 | /* Update clip rectangles if necessary. */ | 252 | /* Update clip rectangles if necessary. */ |
| 252 | if (gc.clipRectID != lastClipSerial) | 253 | if (gc.clipRectID != lastClipSerial) |
| @@ -266,14 +267,11 @@ public final class EmacsView extends ViewGroup | |||
| 266 | return canvas; | 267 | return canvas; |
| 267 | } | 268 | } |
| 268 | 269 | ||
| 269 | public void | 270 | public synchronized void |
| 270 | prepareForLayout (int wantedWidth, int wantedHeight) | 271 | prepareForLayout (int wantedWidth, int wantedHeight) |
| 271 | { | 272 | { |
| 272 | synchronized (dimensionsLock) | 273 | measuredWidth = wantedWidth; |
| 273 | { | 274 | measuredHeight = wantedWidth; |
| 274 | measuredWidth = wantedWidth; | ||
| 275 | measuredHeight = wantedWidth; | ||
| 276 | } | ||
| 277 | } | 275 | } |
| 278 | 276 | ||
| 279 | @Override | 277 | @Override |
| @@ -329,8 +327,8 @@ public final class EmacsView extends ViewGroup | |||
| 329 | } | 327 | } |
| 330 | 328 | ||
| 331 | /* Note that the monitor lock for the window must never be held from | 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 | 330 | within that for the view, because the window acquires locks in the |
| 333 | other way around. */ | 331 | opposite direction. */ |
| 334 | 332 | ||
| 335 | @Override | 333 | @Override |
| 336 | protected void | 334 | protected void |
| @@ -346,7 +344,7 @@ public final class EmacsView extends ViewGroup | |||
| 346 | count = getChildCount (); | 344 | count = getChildCount (); |
| 347 | needExpose = false; | 345 | needExpose = false; |
| 348 | 346 | ||
| 349 | synchronized (dimensionsLock) | 347 | synchronized (this) |
| 350 | { | 348 | { |
| 351 | /* Load measuredWidth and measuredHeight. */ | 349 | /* Load measuredWidth and measuredHeight. */ |
| 352 | oldMeasuredWidth = measuredWidth; | 350 | oldMeasuredWidth = measuredWidth; |
| @@ -355,48 +353,48 @@ public final class EmacsView extends ViewGroup | |||
| 355 | /* Set measuredWidth and measuredHeight. */ | 353 | /* Set measuredWidth and measuredHeight. */ |
| 356 | measuredWidth = right - left; | 354 | measuredWidth = right - left; |
| 357 | measuredHeight = bottom - top; | 355 | measuredHeight = bottom - top; |
| 358 | } | ||
| 359 | 356 | ||
| 360 | /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set changed | 357 | /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set |
| 361 | to true as well. */ | 358 | changed to true as well. */ |
| 362 | 359 | ||
| 363 | if (right - left != oldMeasuredWidth | 360 | if (right - left != oldMeasuredWidth |
| 364 | || bottom - top != oldMeasuredHeight) | 361 | || bottom - top != oldMeasuredHeight) |
| 365 | changed = true; | 362 | changed = true; |
| 366 | 363 | ||
| 367 | /* Dirty the back buffer if the layout change resulted in the view | 364 | /* Dirty the back buffer if the layout change resulted in the view |
| 368 | being resized. */ | 365 | 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; | ||
| 376 | 366 | ||
| 377 | /* This might return NULL if this view is not attached. */ | 367 | if (changed) |
| 378 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) | ||
| 379 | { | 368 | { |
| 380 | /* If a toplevel view is focused and isCurrentlyTextEditor | 369 | /* Expose the window upon a change in the view's size that |
| 381 | is enabled when the IME is hidden, clear | 370 | prompts the creation of a new bitmap. */ |
| 382 | isCurrentlyTextEditor so it isn't shown again if the | 371 | bitmapDirty = needExpose = true; |
| 383 | user dismisses Emacs before returning. */ | 372 | |
| 384 | rootWindowInsets = getRootWindowInsets (); | 373 | /* This might return NULL if this view is not attached. */ |
| 385 | 374 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) | |
| 386 | if (isCurrentlyTextEditor | 375 | { |
| 387 | && rootWindowInsets != null | 376 | /* If a toplevel view is focused and |
| 388 | && isAttachedToWindow | 377 | isCurrentlyTextEditor is enabled when the IME is |
| 389 | && !rootWindowInsets.isVisible (WindowInsets.Type.ime ()) | 378 | hidden, clear isCurrentlyTextEditor so it isn't shown |
| 390 | /* N.B. that the keyboard is dismissed during gesture | 379 | again if the user dismisses Emacs before |
| 391 | navigation under Android 30, but the system is | 380 | returning. */ |
| 392 | quite temperamental regarding whether the window is | 381 | rootWindowInsets = getRootWindowInsets (); |
| 393 | focused at that point. Ideally | 382 | |
| 394 | isCurrentlyTextEditor shouldn't be reset in that | 383 | if (isCurrentlyTextEditor |
| 395 | case, but detecting that situation appears to be | 384 | && rootWindowInsets != null |
| 396 | impossible. Sigh. */ | 385 | && isAttachedToWindow |
| 397 | && (window == EmacsActivity.focusedWindow | 386 | && !rootWindowInsets.isVisible (WindowInsets.Type.ime ()) |
| 398 | && hasWindowFocus ())) | 387 | /* N.B. that the keyboard is dismissed during |
| 399 | isCurrentlyTextEditor = false; | 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 @@ public final class EmacsView extends ViewGroup | |||
| 449 | damageRegion.op (left, top, right, bottom, Region.Op.UNION); | 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 | /* This method is called from both the UI thread and the Emacs | 477 | /* This method is called from both the UI thread and the Emacs |
| 453 | thread. */ | 478 | thread. */ |
| 454 | 479 | ||
| @@ -467,7 +492,10 @@ public final class EmacsView extends ViewGroup | |||
| 467 | /* Now see if there is a damage region. */ | 492 | /* Now see if there is a damage region. */ |
| 468 | 493 | ||
| 469 | if (damageRegion.isEmpty ()) | 494 | if (damageRegion.isEmpty ()) |
| 470 | return; | 495 | { |
| 496 | postSwapBuffers (); | ||
| 497 | return; | ||
| 498 | } | ||
| 471 | 499 | ||
| 472 | /* And extract and clear the damage region. */ | 500 | /* And extract and clear the damage region. */ |
| 473 | 501 | ||
| @@ -479,6 +507,7 @@ public final class EmacsView extends ViewGroup | |||
| 479 | /* Transfer the bitmap to the surface view, then invalidate | 507 | /* Transfer the bitmap to the surface view, then invalidate |
| 480 | it. */ | 508 | it. */ |
| 481 | surfaceView.setBitmap (bitmap, damageRect); | 509 | surfaceView.setBitmap (bitmap, damageRect); |
| 510 | postSwapBuffers (); | ||
| 482 | } | 511 | } |
| 483 | } | 512 | } |
| 484 | 513 | ||
| @@ -758,13 +787,9 @@ public final class EmacsView extends ViewGroup | |||
| 758 | was called. */ | 787 | was called. */ |
| 759 | bitmapDirty = true; | 788 | bitmapDirty = true; |
| 760 | 789 | ||
| 761 | synchronized (dimensionsLock) | 790 | /* Now expose the view contents again. */ |
| 762 | { | 791 | EmacsNative.sendExpose (this.window.handle, 0, 0, |
| 763 | /* Now expose the view contents again. */ | 792 | measuredWidth, measuredHeight); |
| 764 | EmacsNative.sendExpose (this.window.handle, 0, 0, | ||
| 765 | measuredWidth, measuredHeight); | ||
| 766 | } | ||
| 767 | |||
| 768 | super.onAttachedToWindow (); | 793 | super.onAttachedToWindow (); |
| 769 | } | 794 | } |
| 770 | 795 | ||