Update Android port
* java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit) (endBatchEdit, commitCompletion, commitText, deleteSurroundingText) (finishComposingText, getSelectedText, getTextAfterCursor) (getTextBeforeCursor, setComposingText, setComposingRegion) (performEditorAction, performContextMenuAction, getExtractedText) (setSelection, sendKeyEvent, deleteSurroundingTextInCodePoints) (requestCursorUpdates): Ensure that the input connection is up to date. (getSurroundingText): New function. * java/org/gnu/emacs/EmacsNative.java (getSurroundingText): Export new C function. * java/org/gnu/emacs/EmacsService.java (resetIC): Invalidate previously created input connections. * java/org/gnu/emacs/EmacsView.java (EmacsView) (onCreateInputConnection): Signify that input connections are now up to date. * src/androidterm.c (struct android_get_surrounding_text_context): New structure. (android_get_surrounding_text, NATIVE_NAME): * src/textconv.c (get_surrounding_text): * src/textconv.h: New functions.
This commit is contained in:
parent
9a68041f2c
commit
63339a9577
7 changed files with 370 additions and 0 deletions
|
@ -23,7 +23,9 @@
|
|||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
import android.view.inputmethod.TextSnapshot;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import android.os.Build;
|
||||
|
@ -88,6 +90,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
beginBatchEdit ()
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "beginBatchEdit");
|
||||
|
||||
|
@ -99,6 +105,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
endBatchEdit ()
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "endBatchEdit");
|
||||
|
||||
|
@ -110,6 +120,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
commitCompletion (CompletionInfo info)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitCompletion: " + info);
|
||||
|
||||
|
@ -125,6 +139,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
{
|
||||
int[] selection;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
|
||||
|
||||
|
@ -156,6 +174,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
deleteSurroundingText (int leftLength, int rightLength)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("deleteSurroundingText: "
|
||||
+ leftLength + " " + rightLength));
|
||||
|
@ -169,6 +191,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
finishComposingText ()
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "finishComposingText");
|
||||
|
||||
|
@ -180,6 +206,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public String
|
||||
getSelectedText (int flags)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return null;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getSelectedText: " + flags);
|
||||
|
||||
|
@ -192,6 +222,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
{
|
||||
String string;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return null;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
|
||||
|
||||
|
@ -210,6 +244,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
{
|
||||
String string;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return null;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
|
||||
|
||||
|
@ -226,6 +264,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
setComposingText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("setComposingText: "
|
||||
+ text + " ## " + newCursorPosition));
|
||||
|
@ -239,6 +281,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
setComposingRegion (int start, int end)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setComposingRegion: " + start + " " + end);
|
||||
|
||||
|
@ -250,6 +296,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
performEditorAction (int editorAction)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "performEditorAction: " + editorAction);
|
||||
|
||||
|
@ -263,6 +313,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
{
|
||||
int action;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "performContextMenuAction: " + contextMenuAction);
|
||||
|
||||
|
@ -310,6 +364,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
ExtractedText text;
|
||||
int[] selection;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return null;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
|
||||
+ request.hintMaxLines + " " + flags);
|
||||
|
@ -360,6 +418,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
setSelection (int start, int end)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setSelection: " + start + " " + end);
|
||||
|
||||
|
@ -371,6 +433,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
sendKeyEvent (KeyEvent key)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "sendKeyEvent: " + key);
|
||||
|
||||
|
@ -381,6 +447,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
/* This can be implemented the same way as
|
||||
deleteSurroundingText. */
|
||||
return this.deleteSurroundingText (beforeLength, afterLength);
|
||||
|
@ -390,6 +460,10 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
requestCursorUpdates (int cursorUpdateMode)
|
||||
{
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return false;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
|
||||
|
||||
|
@ -397,6 +471,37 @@ public final class EmacsInputConnection extends BaseInputConnection
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurroundingText
|
||||
getSurroundingText (int beforeLength, int afterLength,
|
||||
int flags)
|
||||
{
|
||||
SurroundingText text;
|
||||
|
||||
/* Return if the input connection is out of date. */
|
||||
if (view.icSerial < view.icGeneration)
|
||||
return null;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("getSurroundingText: " + beforeLength + ", "
|
||||
+ afterLength));
|
||||
|
||||
text = EmacsNative.getSurroundingText (windowHandle, beforeLength,
|
||||
afterLength, flags);
|
||||
|
||||
if (text != null)
|
||||
Log.d (TAG, ("getSurroundingText: "
|
||||
+ text.getSelectionStart ()
|
||||
+ ","
|
||||
+ text.getSelectionEnd ()
|
||||
+ "+"
|
||||
+ text.getOffset ()
|
||||
+ ": "
|
||||
+ text.getText ()));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
/* Override functions which are not implemented. */
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
|
||||
public final class EmacsNative
|
||||
{
|
||||
|
@ -222,6 +223,9 @@ public static native ExtractedText getExtractedText (short window,
|
|||
public static native void requestSelectionUpdate (short window);
|
||||
public static native void requestCursorUpdates (short window, int mode);
|
||||
public static native void clearInputFlags (short window);
|
||||
public static native SurroundingText getSurroundingText (short window,
|
||||
int left, int right,
|
||||
int flags);
|
||||
|
||||
|
||||
/* Return the current value of the selection, or -1 upon
|
||||
|
|
|
@ -748,6 +748,7 @@ invocation of app_process (through android-emacs) can
|
|||
window.view.setICMode (icMode);
|
||||
|
||||
icBeginSynchronous ();
|
||||
window.view.icGeneration++;
|
||||
window.view.imManager.restartInput (window.view);
|
||||
icEndSynchronous ();
|
||||
}
|
||||
|
|
|
@ -111,6 +111,13 @@ public final class EmacsView extends ViewGroup
|
|||
details. */
|
||||
private int icMode;
|
||||
|
||||
/* The number of calls to `resetIC' to have taken place the last
|
||||
time an InputConnection was created. */
|
||||
public long icSerial;
|
||||
|
||||
/* The number of calls to `recetIC' that have taken place. */
|
||||
public volatile long icGeneration;
|
||||
|
||||
public
|
||||
EmacsView (EmacsWindow window)
|
||||
{
|
||||
|
@ -627,6 +634,12 @@ else if (child.getVisibility () != GONE)
|
|||
return null;
|
||||
}
|
||||
|
||||
/* Set icSerial. If icSerial < icGeneration, the input connection
|
||||
has been reset, and future input should be ignored until a new
|
||||
connection is created. */
|
||||
|
||||
icSerial = icGeneration;
|
||||
|
||||
/* Reset flags set by the previous input method. */
|
||||
|
||||
EmacsNative.clearInputFlags (window.handle);
|
||||
|
|
|
@ -5636,6 +5636,149 @@ NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject object,
|
|||
android_write_event (&event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Context for a call to `getSurroundingText'. */
|
||||
|
||||
struct android_get_surrounding_text_context
|
||||
{
|
||||
/* Number of characters before the region to return. */
|
||||
int before_length;
|
||||
|
||||
/* Number of characters after the region to return. */
|
||||
int after_length;
|
||||
|
||||
/* The returned text, or NULL. */
|
||||
char *text;
|
||||
|
||||
/* The size of that text in characters and bytes. */
|
||||
ptrdiff_t length, bytes;
|
||||
|
||||
/* Offsets into that text. */
|
||||
ptrdiff_t offset, start, end;
|
||||
|
||||
/* The window. */
|
||||
android_window window;
|
||||
};
|
||||
|
||||
/* Return the surrounding text in the surrounding text context
|
||||
specified by DATA. */
|
||||
|
||||
static void
|
||||
android_get_surrounding_text (void *data)
|
||||
{
|
||||
struct android_get_surrounding_text_context *request;
|
||||
struct frame *f;
|
||||
ptrdiff_t temp;
|
||||
|
||||
request = data;
|
||||
|
||||
/* Find the frame associated with the window. */
|
||||
f = android_window_to_frame (NULL, request->window);
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
/* Now get the surrounding text. */
|
||||
request->text
|
||||
= get_surrounding_text (f, request->before_length,
|
||||
request->after_length, &request->length,
|
||||
&request->bytes, &request->offset,
|
||||
&request->start, &request->end);
|
||||
|
||||
/* Sort request->start and request->end for compatibility with some
|
||||
bad input methods. */
|
||||
|
||||
if (request->end < request->start)
|
||||
{
|
||||
temp = request->start;
|
||||
request->start = request->end;
|
||||
request->end = temp;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object,
|
||||
jshort window, jint before_length,
|
||||
jint after_length, jint flags)
|
||||
{
|
||||
JNI_STACK_ALIGNMENT_PROLOGUE;
|
||||
|
||||
static jclass class;
|
||||
static jmethodID constructor;
|
||||
|
||||
struct android_get_surrounding_text_context context;
|
||||
jstring string;
|
||||
jobject object;
|
||||
|
||||
/* Initialize CLASS if it has not yet been initialized. */
|
||||
|
||||
if (!class)
|
||||
{
|
||||
class
|
||||
= (*env)->FindClass (env, ("android/view/inputmethod"
|
||||
"/SurroundingText"));
|
||||
|
||||
#if __ANDROID_API__ < 31
|
||||
/* If CLASS cannot be found, the version of Android currently
|
||||
running is too old. */
|
||||
|
||||
if (!class)
|
||||
{
|
||||
(*env)->ExceptionClear (env);
|
||||
return NULL;
|
||||
}
|
||||
#else /* __ANDROID_API__ >= 31 */
|
||||
assert (class);
|
||||
#endif /* __ANDROID_API__ < 31 */
|
||||
|
||||
class = (*env)->NewGlobalRef (env, class);
|
||||
if (!class)
|
||||
return NULL;
|
||||
|
||||
/* Now look for its constructor. */
|
||||
constructor = (*env)->GetMethodID (env, class, "<init>",
|
||||
"(Ljava/lang/CharSequence;III)V");
|
||||
assert (constructor);
|
||||
}
|
||||
|
||||
context.before_length = before_length;
|
||||
context.after_length = after_length;
|
||||
context.window = window;
|
||||
context.text = NULL;
|
||||
|
||||
android_sync_edit ();
|
||||
if (android_run_in_emacs_thread (android_get_surrounding_text,
|
||||
&context))
|
||||
return NULL;
|
||||
|
||||
if (!context.text)
|
||||
return NULL;
|
||||
|
||||
/* Encode the returned text. */
|
||||
string = android_text_to_string (env, context.text, context.length,
|
||||
context.bytes);
|
||||
free (context.text);
|
||||
|
||||
if (!string)
|
||||
return NULL;
|
||||
|
||||
/* Create an SurroundingText object containing this information. */
|
||||
object = (*env)->NewObject (env, class, constructor, string,
|
||||
(jint) min (context.start,
|
||||
TYPE_MAXIMUM (jint)),
|
||||
(jint) min (context.end,
|
||||
TYPE_MAXIMUM (jint)),
|
||||
/* Adjust point offsets to fit into
|
||||
Android's 0-based indexing. */
|
||||
(jint) min (context.offset - 1,
|
||||
TYPE_MAXIMUM (jint)));
|
||||
if (!object)
|
||||
return NULL;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
|
|
100
src/textconv.c
100
src/textconv.c
|
@ -1732,6 +1732,106 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
|
|||
return buffer;
|
||||
}
|
||||
|
||||
/* Return the text between the positions PT - LEFT and PT + RIGHT. If
|
||||
the mark is active, return the range of text relative to the bounds
|
||||
of the region instead.
|
||||
|
||||
Set *LENGTH to the number of characters returned, *BYTES to the
|
||||
number of bytes returned, *OFFSET to the character position of the
|
||||
returned text, and *START_RETURN and *END_RETURN to the mark and
|
||||
point relative to that position. */
|
||||
|
||||
char *
|
||||
get_surrounding_text (struct frame *f, ptrdiff_t left,
|
||||
ptrdiff_t right, ptrdiff_t *length,
|
||||
ptrdiff_t *bytes, ptrdiff_t *offset,
|
||||
ptrdiff_t *start_return,
|
||||
ptrdiff_t *end_return)
|
||||
{
|
||||
specpdl_ref count;
|
||||
ptrdiff_t start, end, start_byte, end_byte, mark, temp;
|
||||
char *buffer;
|
||||
|
||||
if (!WINDOW_LIVE_P (f->old_selected_window))
|
||||
return NULL;
|
||||
|
||||
/* Save the excursion, as there will be extensive changes to the
|
||||
selected window. */
|
||||
count = SPECPDL_INDEX ();
|
||||
record_unwind_protect_excursion ();
|
||||
|
||||
/* Inhibit quitting. */
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
/* Temporarily switch to F's selected window at the time of the last
|
||||
redisplay. */
|
||||
select_window (f->old_selected_window, Qt);
|
||||
buffer = NULL;
|
||||
|
||||
/* Figure out the bounds of the text to return. */
|
||||
|
||||
/* First, obtain start and end. */
|
||||
end = get_mark ();
|
||||
start = PT;
|
||||
|
||||
/* If the mark is not active, make it start and end. */
|
||||
|
||||
if (end == -1)
|
||||
end = start;
|
||||
|
||||
/* Now sort start and end. */
|
||||
|
||||
if (end < start)
|
||||
{
|
||||
temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
}
|
||||
|
||||
/* And subtract left and right. */
|
||||
|
||||
if (INT_SUBTRACT_WRAPV (start, left, &start)
|
||||
|| INT_ADD_WRAPV (end, right, &end))
|
||||
goto finish;
|
||||
|
||||
start = max (start, BEGV);
|
||||
end = min (end, ZV);
|
||||
|
||||
/* Detect overflow. */
|
||||
|
||||
if (!(start <= PT && PT <= end))
|
||||
goto finish;
|
||||
|
||||
/* Convert the character positions to byte positions. */
|
||||
start_byte = CHAR_TO_BYTE (start);
|
||||
end_byte = CHAR_TO_BYTE (end);
|
||||
|
||||
/* Extract the text from the buffer. */
|
||||
buffer = xmalloc (end_byte - start_byte);
|
||||
copy_buffer (start, start_byte, end, end_byte,
|
||||
buffer);
|
||||
|
||||
/* Get the mark. If it's not active, use PT. */
|
||||
|
||||
mark = get_mark ();
|
||||
|
||||
if (mark == -1)
|
||||
mark = PT;
|
||||
|
||||
/* Return the offsets. Unlike `get_extracted_text', this need not
|
||||
sort mark and point. */
|
||||
|
||||
*offset = start;
|
||||
*start_return = mark - start;
|
||||
*end_return = PT - start;
|
||||
*length = end - start;
|
||||
*bytes = end_byte - start_byte;
|
||||
|
||||
finish:
|
||||
unbind_to (count, Qnil);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Return whether or not text conversion is temporarily disabled.
|
||||
`reset' should always call this to determine whether or not to
|
||||
disable the input method. */
|
||||
|
|
|
@ -143,6 +143,10 @@ extern void textconv_barrier (struct frame *, unsigned long);
|
|||
extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
|
||||
ptrdiff_t *, ptrdiff_t *, ptrdiff_t *,
|
||||
ptrdiff_t *, bool *);
|
||||
extern char *get_surrounding_text (struct frame *, ptrdiff_t,
|
||||
ptrdiff_t, ptrdiff_t *,
|
||||
ptrdiff_t *, ptrdiff_t *,
|
||||
ptrdiff_t *, ptrdiff_t *);
|
||||
extern bool conversion_disabled_p (void);
|
||||
|
||||
extern void register_textconv_interface (struct textconv_interface *);
|
||||
|
|
Loading…
Add table
Reference in a new issue