/* Whether or not a popup is active. */
private boolean popupActive;
+ /* Whether the back buffer has been updated since the last swap. */
+ private boolean unswapped;
+
/* The current context menu. */
private EmacsContextMenu contextMenu;
/* The last measured width and height. */
private int measuredWidth, measuredHeight;
- /* Object acting as a lock for those values. */
- private Object dimensionsLock;
-
/* The serial of the last clip rectangle change. */
private long lastClipSerial;
/* Add this view as its own global layout listener. */
getViewTreeObserver ().addOnGlobalLayoutListener (this);
-
- /* Create an object used as a lock. */
- this.dimensionsLock = new Object ();
}
private void
Bitmap oldBitmap;
int measuredWidth, measuredHeight;
- synchronized (dimensionsLock)
- {
- /* Load measuredWidth and measuredHeight. */
- measuredWidth = this.measuredWidth;
- measuredHeight = this.measuredHeight;
- }
+ /* Load measuredWidth and measuredHeight. */
+ measuredWidth = this.measuredWidth;
+ measuredHeight = this.measuredHeight;
if (measuredWidth == 0 || measuredHeight == 0)
return;
public synchronized Bitmap
getBitmap ()
{
- if (bitmapDirty || bitmap == null)
+ /* Never alter the bitmap if modifications have been received that
+ are still to be copied to the front buffer, as this indicates
+ that redisplay is in the process of copying matrix contents to
+ the glass, and such events as generally prompt a complete
+ regeneration of the frame's contents might not be processed. */
+ if (!unswapped && (bitmapDirty || bitmap == null))
handleDirtyBitmap ();
+ unswapped = true;
return bitmap;
}
{
int i;
- if (bitmapDirty || bitmap == null)
+ if (!unswapped && (bitmapDirty || bitmap == null))
handleDirtyBitmap ();
if (canvas == null)
return null;
+ unswapped = true;
/* Update clip rectangles if necessary. */
if (gc.clipRectID != lastClipSerial)
return canvas;
}
- public void
+ public synchronized void
prepareForLayout (int wantedWidth, int wantedHeight)
{
- synchronized (dimensionsLock)
- {
- measuredWidth = wantedWidth;
- measuredHeight = wantedWidth;
- }
+ measuredWidth = wantedWidth;
+ measuredHeight = wantedWidth;
}
@Override
}
/* Note that the monitor lock for the window must never be held from
- within the lock for the view, because the window also locks the
- other way around. */
+ within that for the view, because the window acquires locks in the
+ opposite direction. */
@Override
protected void
count = getChildCount ();
needExpose = false;
- synchronized (dimensionsLock)
+ synchronized (this)
{
/* Load measuredWidth and measuredHeight. */
oldMeasuredWidth = measuredWidth;
/* Set measuredWidth and measuredHeight. */
measuredWidth = right - left;
measuredHeight = bottom - top;
- }
- /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set changed
- to true as well. */
+ /* If oldMeasuredHeight or oldMeasuredWidth are wrong, set
+ changed to true as well. */
- if (right - left != oldMeasuredWidth
- || bottom - top != oldMeasuredHeight)
- changed = true;
+ if (right - left != oldMeasuredWidth
+ || bottom - top != oldMeasuredHeight)
+ changed = true;
- /* Dirty the back buffer if the layout change resulted in the view
- being resized. */
-
- if (changed)
- {
- /* Expose the window upon a change in the view's size that
- prompts the creation of a new bitmap. */
- explicitlyDirtyBitmap ();
- needExpose = true;
+ /* Dirty the back buffer if the layout change resulted in the view
+ being resized. */
- /* This might return NULL if this view is not attached. */
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+ if (changed)
{
- /* If a toplevel view is focused and isCurrentlyTextEditor
- is enabled when the IME is hidden, clear
- isCurrentlyTextEditor so it isn't shown again if the
- user dismisses Emacs before returning. */
- rootWindowInsets = getRootWindowInsets ();
-
- if (isCurrentlyTextEditor
- && rootWindowInsets != null
- && isAttachedToWindow
- && !rootWindowInsets.isVisible (WindowInsets.Type.ime ())
- /* N.B. that the keyboard is dismissed during gesture
- navigation under Android 30, but the system is
- quite temperamental regarding whether the window is
- focused at that point. Ideally
- isCurrentlyTextEditor shouldn't be reset in that
- case, but detecting that situation appears to be
- impossible. Sigh. */
- && (window == EmacsActivity.focusedWindow
- && hasWindowFocus ()))
- isCurrentlyTextEditor = false;
+ /* Expose the window upon a change in the view's size that
+ prompts the creation of a new bitmap. */
+ bitmapDirty = needExpose = true;
+
+ /* This might return NULL if this view is not attached. */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+ {
+ /* If a toplevel view is focused and
+ isCurrentlyTextEditor is enabled when the IME is
+ hidden, clear isCurrentlyTextEditor so it isn't shown
+ again if the user dismisses Emacs before
+ returning. */
+ rootWindowInsets = getRootWindowInsets ();
+
+ if (isCurrentlyTextEditor
+ && rootWindowInsets != null
+ && isAttachedToWindow
+ && !rootWindowInsets.isVisible (WindowInsets.Type.ime ())
+ /* N.B. that the keyboard is dismissed during
+ gesture navigation under Android 30, but the
+ system is quite temperamental regarding whether
+ the window is focused at that point. Ideally
+ isCurrentlyTextEditor shouldn't be reset in that
+ case, but detecting that situation appears to be
+ impossible. Sigh. */
+ && (window == EmacsActivity.focusedWindow
+ && hasWindowFocus ()))
+ isCurrentlyTextEditor = false;
+ }
}
}
damageRegion.op (left, top, right, bottom, Region.Op.UNION);
}
+ /* Complete deferred reconfiguration of the front buffer after a
+ buffer swap completes, and generate Expose events for the same. */
+
+ private void
+ postSwapBuffers ()
+ {
+ if (!unswapped)
+ return;
+
+ unswapped = false;
+
+ /* If the bitmap is dirty, reconfigure the bitmap and
+ generate an Expose event to produce its contents. */
+
+ if ((bitmapDirty || bitmap == null)
+ /* Do not generate Expose events if handleDirtyBitmap will
+ not create a valid bitmap, or the consequent buffer swap
+ will produce another event, ad infinitum. */
+ && isAttachedToWindow && measuredWidth != 0
+ && measuredHeight != 0)
+ {
+ handleDirtyBitmap ();
+ EmacsNative.sendExpose (this.window.handle, 0, 0,
+ measuredWidth, measuredHeight);
+ }
+ }
+
/* This method is called from both the UI thread and the Emacs
thread. */
/* Now see if there is a damage region. */
if (damageRegion.isEmpty ())
- return;
+ {
+ postSwapBuffers ();
+ return;
+ }
/* And extract and clear the damage region. */
/* Transfer the bitmap to the surface view, then invalidate
it. */
surfaceView.setBitmap (bitmap, damageRect);
+ postSwapBuffers ();
}
}
was called. */
bitmapDirty = true;
- synchronized (dimensionsLock)
- {
- /* Now expose the view contents again. */
- EmacsNative.sendExpose (this.window.handle, 0, 0,
- measuredWidth, measuredHeight);
- }
-
+ /* Now expose the view contents again. */
+ EmacsNative.sendExpose (this.window.handle, 0, 0,
+ measuredWidth, measuredHeight);
super.onAttachedToWindow ();
}