Update Android port

* doc/lispref/frames.texi (On-Screen Keyboards): Describe return
value of `frame-toggle-on-screen-keyboard'.
* java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged)
(surfaceCreated, EmacsSurfaceView): Remove unuseful
synchronization code.  The framework doesn't seem to look at
this at all.

* java/org/gnu/emacs/EmacsView.java (EmacsView):
(onLayout): Lay out the window after children.
(swapBuffers): Properly implement `force'.
(windowUpdated): Delete function.

* lisp/frame.el (frame-toggle-on-screen-keyboard): Return
whether or not the on screen keyboard might've been displayed.

* lisp/minibuffer.el (minibuffer-on-screen-keyboard-timer):
(minibuffer-on-screen-keyboard-displayed):
(minibuffer-setup-on-screen-keyboard):
(minibuffer-exit-on-screen-keyboard): Improve OSK dismissal when
there are consecutive minibuffers.

* lisp/touch-screen.el (touch-screen-window-selection-changed):
New function.
(touch-screen-handle-point-up): Register it as a window
selection changed function.

* src/android.c (struct android_emacs_window)
(android_init_emacs_window): Remove references to
`windowUpdated'.
(android_window_updated): Delete function.
* src/android.h (struct android_output): Remove
`last_configure_serial'.
* src/androidterm.c (handle_one_android_event)
(android_frame_up_to_date):
* src/androidterm.h (struct android_output): Remove frame
synchronization, as that does not work on Android.
This commit is contained in:
Po Lu 2023-02-08 22:40:10 +08:00
parent 7fb0df0ce2
commit 0bd4b7fdab
10 changed files with 94 additions and 135 deletions

View file

@ -3828,6 +3828,10 @@ This function displays or hides the on-screen keyboard on behalf of
the frame @var{frame}. If @var{hide} is non-@code{nil}, then the
on-screen keyboard is hidden; otherwise, it is displayed.
It returns whether or not the on screen keyboard @strong{may} have
been displayed, which should be used to determine whether or not to
hide the on-screen keyboard later.
This has no effect if the system automatically detects when to display
the on-screen keyboard, or when it does not provide any on-screen
keyboard.

View file

@ -45,14 +45,11 @@ private class Callback implements SurfaceHolder.Callback
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
Log.d (TAG, "surfaceChanged: " + view + ", " + view.pendingConfigure);
Canvas canvas;
/* Make sure not to swap buffers if there is pending
configuration, because otherwise the redraw callback will not
run correctly. */
Log.d (TAG, "surfaceChanged: " + view + ", ");
if (view.pendingConfigure == 0)
view.swapBuffers ();
view.swapBuffers (true);
}
@Override
@ -67,7 +64,7 @@ private class Callback implements SurfaceHolder.Callback
/* Drop the lock when doing this, or a deadlock can
result. */
view.swapBuffers ();
view.swapBuffers (true);
}
@Override
@ -82,44 +79,6 @@ private class Callback implements SurfaceHolder.Callback
}
}
/* And this is the callback used on Android 26 and later. It is
used because it can tell the system when drawing completes. */
private class Callback2 extends Callback implements SurfaceHolder.Callback2
{
@Override
public void
surfaceRedrawNeeded (SurfaceHolder holder)
{
/* This version is not supported. */
return;
}
@Override
public void
surfaceRedrawNeededAsync (SurfaceHolder holder,
Runnable drawingFinished)
{
Runnable old;
Log.d (TAG, "surfaceRedrawNeededAsync: " + view.pendingConfigure);
/* The system calls this function when it wants to know whether
or not Emacs is still configuring itself in response to a
resize.
If the view did not send an outstanding ConfigureNotify
event, then call drawingFinish immediately. Else, give it to
the view to execute after drawing completes. */
if (view.pendingConfigure == 0)
drawingFinished.run ();
else
/* And set this runnable to run once drawing completes. */
view.drawingFinished = drawingFinished;
}
}
public
EmacsSurfaceView (final EmacsView view)
{
@ -128,10 +87,7 @@ private class Callback2 extends Callback implements SurfaceHolder.Callback2
this.surfaceChangeLock = new Object ();
this.view = view;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
getHolder ().addCallback (new Callback ());
else
getHolder ().addCallback (new Callback2 ());
getHolder ().addCallback (new Callback ());
}
public boolean

View file

@ -96,13 +96,6 @@ public class EmacsView extends ViewGroup
/* The InputMethodManager for this view's context. */
private InputMethodManager imManager;
/* Runnable that will run once drawing completes. */
public Runnable drawingFinished;
/* Serial of the last ConfigureNotify event sent that Emacs has not
yet responded to. 0 if there is no such outstanding event. */
public long pendingConfigure;
/* Whether or not this view is attached to a window. */
public boolean isAttachedToWindow;
@ -110,6 +103,14 @@ public class EmacsView extends ViewGroup
displayed whenever possible. */
public boolean isCurrentlyTextEditor;
/* An empty rectangle. */
public static final Rect emptyRect;
static
{
emptyRect = new Rect ();
};
public
EmacsView (EmacsWindow window)
{
@ -286,16 +287,10 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
int count, i;
View child;
Rect windowRect;
int wantedWidth, wantedHeight;
count = getChildCount ();
if (changed || mustReportLayout)
{
mustReportLayout = false;
pendingConfigure
= window.viewLayout (left, top, right, bottom);
}
measuredWidth = right - left;
measuredHeight = bottom - top;
@ -311,8 +306,6 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
Log.d (TAG, "onLayout: " + child);
if (child == surfaceView)
/* The child is the surface view, so give it the entire
view. */
child.layout (0, 0, right - left, bottom - top);
else if (child.getVisibility () != GONE)
{
@ -326,6 +319,14 @@ else if (child.getVisibility () != GONE)
windowRect.right, windowRect.bottom);
}
}
/* Now report the layout change to the window. */
if (changed || mustReportLayout)
{
mustReportLayout = false;
window.viewLayout (left, top, right, bottom);
}
}
public void
@ -352,7 +353,7 @@ else if (child.getVisibility () != GONE)
synchronized (damageRegion)
{
if (damageRegion.isEmpty ())
if (!force && damageRegion.isEmpty ())
return;
bitmap = getBitmap ();
@ -363,7 +364,10 @@ else if (child.getVisibility () != GONE)
synchronized (surfaceView.surfaceChangeLock)
{
damageRect = damageRegion.getBounds ();
if (!force)
damageRect = damageRegion.getBounds ();
else
damageRect = emptyRect;
if (!surfaceView.isCreated ())
return;
@ -612,24 +616,6 @@ else if (child.getVisibility () != GONE)
isCurrentlyTextEditor = false;
}
public void
windowUpdated (long serial)
{
Log.d (TAG, "windowUpdated: serial is " + serial);
if (pendingConfigure <= serial
/* Detect wraparound. */
|| pendingConfigure - serial >= 0x7fffffff)
{
pendingConfigure = 0;
if (drawingFinished != null)
drawingFinished.run ();
drawingFinished = null;
}
}
@Override
public InputConnection
onCreateInputConnection (EditorInfo info)

View file

@ -2564,11 +2564,16 @@ On systems with an on-screen keyboard, display the on screen
keyboard on behalf of the frame FRAME if HIDE is nil. Else, hide
the on screen keyboard.
Return whether or not the on screen keyboard may have been
displayed; that is, return t on systems with an on screen
keyboard, and nil on those without.
FRAME must already have the input focus for this to work
reliably."
(let ((frame-type (framep-on-display frame)))
(cond ((eq frame-type 'android)
(android-toggle-on-screen-keyboard frame hide)))))
(android-toggle-on-screen-keyboard frame hide) t)
(t nil))))
;;;; Frame geometry values

View file

@ -4577,20 +4577,49 @@ is included in the return value."
;; Try to display the on screen keyboard whenever entering the
;; mini-buffer, and hide it whenever leaving.
(defvar minibuffer-on-screen-keyboard-timer nil
"Timer run upon exiting the minibuffer.
It will hide the on screen keyboard when necessary.")
(defvar minibuffer-on-screen-keyboard-displayed nil
"Whether or not the on-screen keyboard has been displayed.
Set inside `minibuffer-setup-on-screen-keyboard'.")
(defun minibuffer-setup-on-screen-keyboard ()
"Maybe display the on-screen keyboard in the current frame.
Display the on-screen keyboard in the current frame if the
last device to have sent an input event is not a keyboard.
This is run upon minibuffer setup."
;; Don't hide the on screen keyboard later on.
(when minibuffer-on-screen-keyboard-timer
(cancel-timer minibuffer-on-screen-keyboard-timer)
(setq minibuffer-on-screen-keyboard-timer nil))
(setq minibuffer-on-screen-keyboard-displayed nil)
(when (not (memq (device-class last-event-frame
last-event-device)
'(keyboard core-keyboard)))
(frame-toggle-on-screen-keyboard (selected-frame) nil)))
(setq minibuffer-on-screen-keyboard-displayed
(frame-toggle-on-screen-keyboard (selected-frame) nil))))
(defun minibuffer-exit-on-screen-keyboard ()
"Hide the on-screen keyboard if it was displayed.
This is run upon minibuffer exit."
(frame-toggle-on-screen-keyboard (selected-frame) t))
Hide the on-screen keyboard in a timer set to run in 0.1 seconds.
It will be cancelled if the minibuffer is displayed again within
that timeframe.
Do not hide the on screen keyboard inside a recursive edit.
Likewise, do not hide the on screen keyboard if point in the
window that will be selected after exiting the minibuffer is not
on read-only text.
The latter is implemented in `touch-screen.el'."
(unless (or (not minibuffer-on-screen-keyboard-displayed)
(> (recursion-depth) 1))
(when minibuffer-on-screen-keyboard-timer
(cancel-timer minibuffer-on-screen-keyboard-timer))
(setq minibuffer-on-screen-keyboard-timer
(run-with-timer 0.1 nil #'frame-toggle-on-screen-keyboard
(selected-frame) t))))
(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)

View file

@ -351,6 +351,21 @@ then move point to the position of POINT."
(goto-char (1+ (window-end nil t))))
(redisplay))))))))))))
(defun touch-screen-window-selection-changed (frame)
"Notice that FRAME's selected window has changed.
If point is now on read only text, hide the on screen keyboard.
Otherwise, cancel any timer that is supposed to hide the keyboard
in response to the minibuffer being closed."
(with-selected-frame frame
(if (or buffer-read-only
(get-text-property (point) 'read-only))
(frame-toggle-on-screen-keyboard (selected-frame) t)
;; Prevent hiding the minibuffer from hiding the on screen
;; keyboard.
(when minibuffer-on-screen-keyboard-timer
(cancel-timer minibuffer-on-screen-keyboard-timer)
(setq minibuffer-on-screen-keyboard-timer nil)))))
(defun touch-screen-handle-point-up (point)
"Notice that POINT has been removed from the screen.
POINT should be the point currently tracked as
@ -404,7 +419,16 @@ is not read-only."
(when (memq command touch-screen-set-point-commands)
(if (not (or buffer-read-only
(get-text-property (point) 'read-only)))
(frame-toggle-on-screen-keyboard (selected-frame) nil)
;; Once the on-screen keyboard has been opened,
;; add `touch-screen-window-selection-changed'
;; as a window selection change function This
;; allows the on screen keyboard to be hidden
;; if the selected window's point becomes read
;; only at some point in the future.
(progn
(add-hook 'window-selection-change-functions
#'touch-screen-window-selection-changed)
(frame-toggle-on-screen-keyboard (selected-frame) nil))
;; Otherwise, hide the on screen keyboard now.
(frame-toggle-on-screen-keyboard (selected-frame) t))))))))))

View file

@ -125,7 +125,6 @@ struct android_emacs_window
jclass class;
jmethodID swap_buffers;
jmethodID toggle_on_screen_keyboard;
jmethodID window_updated;
};
/* The API level of the current device. */
@ -1830,7 +1829,6 @@ android_init_emacs_window (void)
FIND_METHOD (swap_buffers, "swapBuffers", "()V");
FIND_METHOD (toggle_on_screen_keyboard,
"toggleOnScreenKeyboard", "(Z)V");
FIND_METHOD (window_updated, "windowUpdated", "(J)V");
#undef FIND_METHOD
}
@ -4325,25 +4323,6 @@ android_toggle_on_screen_keyboard (android_window window, bool show)
android_exception_check ();
}
/* Tell the window system that all configure events sent to WINDOW
have been fully processed, and that it is now okay to display its
new contents. SERIAL is the serial of the last configure event
processed. */
void
android_window_updated (android_window window, unsigned long serial)
{
jobject object;
jmethodID method;
object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
method = window_class.window_updated;
(*android_java_env)->CallVoidMethod (android_java_env, object,
method, (jlong) serial);
android_exception_check ();
}
/* When calling the system's faccessat, make sure to clear the flag

View file

@ -88,7 +88,6 @@ extern void android_exception_check (void);
extern void android_get_keysym_name (int, char *, size_t);
extern void android_wait_event (void);
extern void android_toggle_on_screen_keyboard (android_window, bool);
extern void android_window_updated (android_window, unsigned long);
extern _Noreturn void android_restart_emacs (void);
extern int android_get_current_api_level (void);

View file

@ -591,17 +591,7 @@ handle_one_android_event (struct android_display_info *dpyinfo,
android_clear_under_internal_border (f);
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
/* Now stash the serial of this configure event somewhere,
and call android_window_updated with it once the redraw
completes. */
FRAME_OUTPUT_DATA (f)->last_configure_serial
= configureEvent.xconfigure.serial;
}
else
/* Reply to this ConfigureNotify event immediately. */
android_window_updated (FRAME_ANDROID_WINDOW (f),
configureEvent.xconfigure.serial);
goto OTHER;
@ -1352,14 +1342,6 @@ android_frame_up_to_date (struct frame *f)
/* The frame is now complete, as its contents have been drawn. */
FRAME_ANDROID_COMPLETE_P (f) = true;
/* If there was an outstanding configure event, then tell system
that the update has finished and the new contents can now be
displayed. */
if (FRAME_OUTPUT_DATA (f)->last_configure_serial)
android_window_updated (FRAME_ANDROID_WINDOW (f),
FRAME_OUTPUT_DATA (f)->last_configure_serial);
FRAME_OUTPUT_DATA (f)->last_configure_serial = 0;
/* Shrink the scanline buffer used by the font backend. */
sfntfont_android_shrink_scanline_buffer ();
unblock_input ();

View file

@ -241,11 +241,6 @@ struct android_output
/* List of all tools (either styluses or fingers) pressed onto the
frame. */
struct android_touch_point *touch_points;
/* Event serial of the last ConfigureNotify event received that has
not yet been drawn. This is used to synchronize resize with the
window system. 0 if no such outstanding event exists. */
unsigned long last_configure_serial;
};
enum