mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-10 21:50:37 -08:00
Update Android port
* configure.ac (HAVE_TEXT_CONVERSION): Define on Android. * doc/emacs/input.texi (On-Screen Keyboards): Document ``text conversion'' slightly. * doc/lispref/commands.texi (Misc Events): Document new `text-conversion' event. * java/org/gnu/emacs/EmacsContextMenu.java (display): Use `syncRunnable'. * java/org/gnu/emacs/EmacsDialog.java (display): Likewise. * java/org/gnu/emacs/EmacsEditable.java: Delete file. * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): Reimplement from scratch. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Add new functions. * java/org/gnu/emacs/EmacsService.java (EmacsService, getEmacsView) (getLocationOnScreen, sync, getClipboardManager, restartEmacs): Use syncRunnable. (syncRunnable): New function. (updateIC, resetIC): New functions. * java/org/gnu/emacs/EmacsView.java (EmacsView): New field `inputConnection' and `icMode'. (onCreateInputConnection): Update accordingly. (setICMode, getICMode): New functions. * lisp/bindings.el (global-map): Ignore text conversion events. * src/alloc.c (mark_frame): Mark text conversion data. * src/android.c (struct android_emacs_service): New fields `update_ic' and `reset_ic'. (event_serial): Export. (android_query_sem): New function. (android_init_events): Initialize new semaphore. (android_write_event): Export. (android_select): Check for UI thread code. (setEmacsParams, android_init_emacs_service): Initialize new methods. (android_check_query, android_begin_query, android_end_query) (android_run_in_emacs_thread): (android_update_ic, android_reset_ic): New functions for managing synchronous queries from one thread to another. * src/android.h: Export new functions. * src/androidgui.h (enum android_event_type): Add input method events. (enum android_ime_operation, struct android_ime_event) (union android_event, enum android_ic_mode): New structs and enums. * src/androidterm.c (android_window_to_frame): Allow DPYINFO to be NULL. (android_decode_utf16, android_handle_ime_event) (handle_one_android_event, android_sync_edit) (android_copy_java_string, beginBatchEdit, endBatchEdit) (commitCompletion, deleteSurroundingText, finishComposingText) (getSelectedtext, getTextAfterCursor, getTextBeforeCursor) (setComposingText, setComposingRegion, setSelection, getSelection) (performEditorAction, getExtractedText): New functions. (struct android_conversion_query_context): (android_perform_conversion_query): (android_text_to_string): (struct android_get_selection_context): (android_get_selection): (struct android_get_extracted_text_context): (android_get_extracted_text): (struct android_extracted_text_request_class): (struct android_extracted_text_class): (android_update_selection): (android_reset_conversion): (android_set_point): (android_compose_region_changed): (android_notify_conversion): (text_conversion_interface): New functions and structures. (android_term_init): Initialize text conversion. * src/coding.c (syms_of_coding): Define Qutf_16le on Android. * src/frame.c (make_frame): Clear conversion data. (delete_frame): Reset conversion state. * src/frame.h (enum text_conversion_operation) (struct text_conversion_action, struct text_conversion_state) (GCALIGNED_STRUCT): Update structures. * src/keyboard.c (read_char, readable_events, kbd_buffer_get_event) (syms_of_keyboard): Handle text conversion events. * src/lisp.h: * src/process.c: Fix includes. * src/textconv.c (enum textconv_batch_edit_flags, textconv_query) (reset_frame_state, detect_conversion_events) (restore_selected_window, really_commit_text) (really_finish_composing_text, really_set_composing_text) (really_set_composing_region, really_delete_surrounding_text) (really_set_point, complete_edit) (handle_pending_conversion_events_1) (handle_pending_conversion_events, start_batch_edit) (end_batch_edit, commit_text, finish_composing_text) (set_composing_text, set_composing_region, textconv_set_point) (delete_surrounding_text, get_extracted_text) (report_selected_window_change, report_point_change) (register_texconv_interface): New functions. * src/textconv.h (struct textconv_interface) (TEXTCONV_SKIP_CONVERSION_REGION): Update prototype. * src/xdisp.c (mark_window_display_accurate_1): * src/xfns.c (xic_string_conversion_callback): * src/xterm.c (init_xterm): Adjust accordingly.
This commit is contained in:
parent
5a7855e84a
commit
a158c1d5b9
27 changed files with 2806 additions and 525 deletions
|
|
@ -279,20 +279,7 @@ public class EmacsContextMenu
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
EmacsService.SERVICE.runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
|
||||
EmacsService.syncRunnable (runnable);
|
||||
return rc.thing;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -317,20 +317,7 @@ public class EmacsDialog implements DialogInterface.OnDismissListener
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
EmacsService.SERVICE.runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
|
||||
EmacsService.syncRunnable (runnable);
|
||||
return rc.thing;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,300 +0,0 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.SpanWatcher;
|
||||
import android.text.Selection;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
|
||||
import android.text.Spannable;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/* Android input methods insist on having access to buffer contents.
|
||||
Since Emacs is not designed like ``any other Android text editor'',
|
||||
that is not possible.
|
||||
|
||||
This file provides a fake editing buffer that is designed to weasel
|
||||
as much information as possible out of an input method, without
|
||||
actually providing buffer contents to Emacs.
|
||||
|
||||
The basic idea is to have the fake editing buffer be initially
|
||||
empty.
|
||||
|
||||
When the input method inserts composed text, it sets a flag.
|
||||
Updates to the buffer while the flag is set are sent to Emacs to be
|
||||
displayed as ``preedit text''.
|
||||
|
||||
Once some heuristics decide that composition has been completed,
|
||||
the composed text is sent to Emacs, and the text that was inserted
|
||||
in this editing buffer is erased. */
|
||||
|
||||
public class EmacsEditable extends SpannableStringBuilder
|
||||
implements SpanWatcher
|
||||
{
|
||||
private static final String TAG = "EmacsEditable";
|
||||
|
||||
/* Whether or not composition is currently in progress. */
|
||||
private boolean isComposing;
|
||||
|
||||
/* The associated input connection. */
|
||||
private EmacsInputConnection connection;
|
||||
|
||||
/* The associated IM manager. */
|
||||
private InputMethodManager imManager;
|
||||
|
||||
/* Any extracted text an input method may be monitoring. */
|
||||
private ExtractedText extractedText;
|
||||
|
||||
/* The corresponding text request. */
|
||||
private ExtractedTextRequest extractRequest;
|
||||
|
||||
/* The number of nested batch edits. */
|
||||
private int batchEditCount;
|
||||
|
||||
/* Whether or not invalidateInput should be called upon batch edits
|
||||
ending. */
|
||||
private boolean pendingInvalidate;
|
||||
|
||||
/* The ``composing span'' indicating the bounds of an ongoing
|
||||
character composition. */
|
||||
private Object composingSpan;
|
||||
|
||||
public
|
||||
EmacsEditable (EmacsInputConnection connection)
|
||||
{
|
||||
/* Initialize the editable with one initial space, so backspace
|
||||
always works. */
|
||||
super ();
|
||||
|
||||
Object tem;
|
||||
Context context;
|
||||
|
||||
this.connection = connection;
|
||||
|
||||
context = connection.view.getContext ();
|
||||
tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
|
||||
imManager = (InputMethodManager) tem;
|
||||
|
||||
/* To watch for changes to text properties on Android, you
|
||||
add... a text property. */
|
||||
setSpan (this, 0, 0, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
}
|
||||
|
||||
public void
|
||||
endBatchEdit ()
|
||||
{
|
||||
if (batchEditCount < 1)
|
||||
return;
|
||||
|
||||
if (--batchEditCount == 0 && pendingInvalidate)
|
||||
invalidateInput ();
|
||||
}
|
||||
|
||||
public void
|
||||
beginBatchEdit ()
|
||||
{
|
||||
++batchEditCount;
|
||||
}
|
||||
|
||||
public void
|
||||
setExtractedTextAndRequest (ExtractedText text,
|
||||
ExtractedTextRequest request,
|
||||
boolean monitor)
|
||||
{
|
||||
/* Extract the text. If monitor is set, also record it as the
|
||||
text that is currently being extracted. */
|
||||
|
||||
text.startOffset = 0;
|
||||
text.selectionStart = Selection.getSelectionStart (this);
|
||||
text.selectionEnd = Selection.getSelectionStart (this);
|
||||
text.text = this;
|
||||
|
||||
if (monitor)
|
||||
{
|
||||
extractedText = text;
|
||||
extractRequest = request;
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
compositionStart ()
|
||||
{
|
||||
isComposing = true;
|
||||
}
|
||||
|
||||
public void
|
||||
compositionEnd ()
|
||||
{
|
||||
isComposing = false;
|
||||
sendComposingText (null);
|
||||
}
|
||||
|
||||
private void
|
||||
sendComposingText (String string)
|
||||
{
|
||||
EmacsWindow window;
|
||||
long time, serial;
|
||||
|
||||
window = connection.view.window;
|
||||
|
||||
if (window.isDestroyed ())
|
||||
return;
|
||||
|
||||
time = System.currentTimeMillis ();
|
||||
|
||||
/* A composition event is simply a special key event with a
|
||||
keycode of -1. */
|
||||
|
||||
synchronized (window.eventStrings)
|
||||
{
|
||||
serial
|
||||
= EmacsNative.sendKeyPress (window.handle, time, 0, -1, -1);
|
||||
|
||||
/* Save the string so that android_lookup_string can find
|
||||
it. */
|
||||
if (string != null)
|
||||
window.saveUnicodeString ((int) serial, string);
|
||||
}
|
||||
}
|
||||
|
||||
private void
|
||||
invalidateInput ()
|
||||
{
|
||||
int start, end, composingSpanStart, composingSpanEnd;
|
||||
|
||||
if (batchEditCount > 0)
|
||||
{
|
||||
Log.d (TAG, "invalidateInput: deferring for batch edit");
|
||||
pendingInvalidate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
pendingInvalidate = false;
|
||||
|
||||
start = Selection.getSelectionStart (this);
|
||||
end = Selection.getSelectionEnd (this);
|
||||
|
||||
if (composingSpan != null)
|
||||
{
|
||||
composingSpanStart = getSpanStart (composingSpan);
|
||||
composingSpanEnd = getSpanEnd (composingSpan);
|
||||
}
|
||||
else
|
||||
{
|
||||
composingSpanStart = -1;
|
||||
composingSpanEnd = -1;
|
||||
}
|
||||
|
||||
Log.d (TAG, "invalidateInput: now " + start + ", " + end);
|
||||
|
||||
/* Tell the input method that the cursor changed. */
|
||||
imManager.updateSelection (connection.view, start, end,
|
||||
composingSpanStart,
|
||||
composingSpanEnd);
|
||||
|
||||
/* If there is any extracted text, tell the IME that it has
|
||||
changed. */
|
||||
if (extractedText != null)
|
||||
imManager.updateExtractedText (connection.view,
|
||||
extractRequest.token,
|
||||
extractedText);
|
||||
}
|
||||
|
||||
public SpannableStringBuilder
|
||||
replace (int start, int end, CharSequence tb, int tbstart,
|
||||
int tbend)
|
||||
{
|
||||
super.replace (start, end, tb, tbstart, tbend);
|
||||
|
||||
/* If a change happens during composition, perform the change and
|
||||
then send the text being composed. */
|
||||
|
||||
if (isComposing)
|
||||
sendComposingText (toString ());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean
|
||||
isSelectionSpan (Object span)
|
||||
{
|
||||
return ((Selection.SELECTION_START == span
|
||||
|| Selection.SELECTION_END == span)
|
||||
&& (getSpanFlags (span)
|
||||
& Spanned.SPAN_INTERMEDIATE) == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onSpanAdded (Spannable text, Object what, int start, int end)
|
||||
{
|
||||
Log.d (TAG, "onSpanAdded: " + text + " " + what + " "
|
||||
+ start + " " + end);
|
||||
|
||||
/* Try to find the composing span. This isn't a public API. */
|
||||
|
||||
if (what.getClass ().getName ().contains ("ComposingText"))
|
||||
composingSpan = what;
|
||||
|
||||
if (isSelectionSpan (what))
|
||||
invalidateInput ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onSpanChanged (Spannable text, Object what, int ostart,
|
||||
int oend, int nstart, int nend)
|
||||
{
|
||||
Log.d (TAG, "onSpanChanged: " + text + " " + what + " "
|
||||
+ nstart + " " + nend);
|
||||
|
||||
if (isSelectionSpan (what))
|
||||
invalidateInput ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onSpanRemoved (Spannable text, Object what,
|
||||
int start, int end)
|
||||
{
|
||||
Log.d (TAG, "onSpanRemoved: " + text + " " + what + " "
|
||||
+ start + " " + end);
|
||||
|
||||
if (isSelectionSpan (what))
|
||||
invalidateInput ();
|
||||
}
|
||||
|
||||
public boolean
|
||||
isInBatchEdit ()
|
||||
{
|
||||
return batchEditCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import android.view.inputmethod.ExtractedText;
|
|||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
import android.view.inputmethod.TextSnapshot;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import android.text.Editable;
|
||||
|
|
@ -38,35 +39,115 @@ import android.util.Log;
|
|||
public class EmacsInputConnection extends BaseInputConnection
|
||||
{
|
||||
private static final String TAG = "EmacsInputConnection";
|
||||
public EmacsView view;
|
||||
private EmacsEditable editable;
|
||||
|
||||
/* The length of the last string to be committed. */
|
||||
private int lastCommitLength;
|
||||
|
||||
int currentLargeOffset;
|
||||
private EmacsView view;
|
||||
private short windowHandle;
|
||||
|
||||
public
|
||||
EmacsInputConnection (EmacsView view)
|
||||
{
|
||||
super (view, false);
|
||||
super (view, true);
|
||||
|
||||
this.view = view;
|
||||
this.editable = new EmacsEditable (this);
|
||||
this.windowHandle = view.window.handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable
|
||||
getEditable ()
|
||||
public boolean
|
||||
beginBatchEdit ()
|
||||
{
|
||||
return editable;
|
||||
Log.d (TAG, "beginBatchEdit");
|
||||
EmacsNative.beginBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
endBatchEdit ()
|
||||
{
|
||||
Log.d (TAG, "endBatchEdit");
|
||||
EmacsNative.endBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
commitCompletion (CompletionInfo info)
|
||||
{
|
||||
Log.d (TAG, "commitCompletion: " + info);
|
||||
EmacsNative.commitCompletion (windowHandle,
|
||||
info.getText ().toString (),
|
||||
info.getPosition ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
commitText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
|
||||
EmacsNative.commitText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
deleteSurroundingText (int leftLength, int rightLength)
|
||||
{
|
||||
Log.d (TAG, ("deleteSurroundingText: "
|
||||
+ leftLength + " " + rightLength));
|
||||
EmacsNative.deleteSurroundingText (windowHandle, leftLength,
|
||||
rightLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
finishComposingText ()
|
||||
{
|
||||
Log.d (TAG, "finishComposingText");
|
||||
|
||||
EmacsNative.finishComposingText (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getSelectedText (int flags)
|
||||
{
|
||||
Log.d (TAG, "getSelectedText: " + flags);
|
||||
|
||||
return EmacsNative.getSelectedText (windowHandle, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getTextAfterCursor (int length, int flags)
|
||||
{
|
||||
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
|
||||
|
||||
return EmacsNative.getTextAfterCursor (windowHandle, length,
|
||||
flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getTextBeforeCursor (int length, int flags)
|
||||
{
|
||||
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
|
||||
|
||||
return EmacsNative.getTextBeforeCursor (windowHandle, length,
|
||||
flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
setComposingText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
editable.compositionStart ();
|
||||
super.setComposingText (text, newCursorPosition);
|
||||
Log.d (TAG, "setComposingText: " + newCursorPosition);
|
||||
|
||||
EmacsNative.setComposingText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -74,102 +155,40 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
setComposingRegion (int start, int end)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (lastCommitLength != 0)
|
||||
{
|
||||
Log.d (TAG, "Restarting composition for: " + lastCommitLength);
|
||||
|
||||
for (i = 0; i < lastCommitLength; ++i)
|
||||
sendKeyEvent (new KeyEvent (KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_DEL));
|
||||
|
||||
lastCommitLength = 0;
|
||||
}
|
||||
|
||||
editable.compositionStart ();
|
||||
super.setComposingRegion (start, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
finishComposingText ()
|
||||
{
|
||||
editable.compositionEnd ();
|
||||
return super.finishComposingText ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
beginBatchEdit ()
|
||||
{
|
||||
editable.beginBatchEdit ();
|
||||
return super.beginBatchEdit ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
endBatchEdit ()
|
||||
{
|
||||
editable.endBatchEdit ();
|
||||
return super.endBatchEdit ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
commitText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
editable.compositionEnd ();
|
||||
super.commitText (text, newCursorPosition);
|
||||
|
||||
/* An observation is that input methods rarely recompose trailing
|
||||
spaces. Avoid re-setting the commit length in that case. */
|
||||
|
||||
if (text.toString ().equals (" "))
|
||||
lastCommitLength += 1;
|
||||
else
|
||||
/* At this point, the editable is now empty.
|
||||
|
||||
The input method may try to cancel the edit upon a subsequent
|
||||
backspace by calling setComposingRegion with a region that is
|
||||
the length of TEXT.
|
||||
|
||||
Record this length in order to be able to send backspace
|
||||
events to ``delete'' the text in that case. */
|
||||
lastCommitLength = text.length ();
|
||||
|
||||
Log.d (TAG, "commitText: \"" + text + "\"");
|
||||
Log.d (TAG, "setComposingRegion: " + start + " " + end);
|
||||
|
||||
EmacsNative.setComposingRegion (windowHandle, start, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return a large offset, cycling through 100000, 30000, 0.
|
||||
The offset is typically used to force the input method to update
|
||||
its notion of ``surrounding text'', bypassing any caching that
|
||||
it might have in progress.
|
||||
|
||||
There must be another way to do this, but I can't find it. */
|
||||
|
||||
public int
|
||||
largeSelectionOffset ()
|
||||
@Override
|
||||
public boolean
|
||||
performEditorAction (int editorAction)
|
||||
{
|
||||
switch (currentLargeOffset)
|
||||
{
|
||||
case 0:
|
||||
currentLargeOffset = 100000;
|
||||
return 100000;
|
||||
Log.d (TAG, "performEditorAction: " + editorAction);
|
||||
|
||||
case 100000:
|
||||
currentLargeOffset = 30000;
|
||||
return 30000;
|
||||
EmacsNative.performEditorAction (windowHandle, editorAction);
|
||||
return true;
|
||||
}
|
||||
|
||||
case 30000:
|
||||
currentLargeOffset = 0;
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public ExtractedText
|
||||
getExtractedText (ExtractedTextRequest request, int flags)
|
||||
{
|
||||
Log.d (TAG, "getExtractedText: " + request + " " + flags);
|
||||
|
||||
currentLargeOffset = 0;
|
||||
return -1;
|
||||
return EmacsNative.getExtractedText (windowHandle, request,
|
||||
flags);
|
||||
}
|
||||
|
||||
|
||||
/* Override functions which are not implemented. */
|
||||
|
||||
@Override
|
||||
public TextSnapshot
|
||||
takeSnapshot ()
|
||||
{
|
||||
Log.d (TAG, "takeSnapshot");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ package org.gnu.emacs;
|
|||
import java.lang.System;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
|
||||
public class EmacsNative
|
||||
{
|
||||
|
|
@ -161,6 +163,50 @@ public class EmacsNative
|
|||
descriptor, or NULL if there is none. */
|
||||
public static native byte[] getProcName (int fd);
|
||||
|
||||
/* Notice that the Emacs thread will now start waiting for the main
|
||||
thread's looper to respond. */
|
||||
public static native void beginSynchronous ();
|
||||
|
||||
/* Notice that the Emacs thread will has finished waiting for the
|
||||
main thread's looper to respond. */
|
||||
public static native void endSynchronous ();
|
||||
|
||||
|
||||
|
||||
/* Input connection functions. These mostly correspond to their
|
||||
counterparts in Android's InputConnection. */
|
||||
|
||||
public static native void beginBatchEdit (short window);
|
||||
public static native void endBatchEdit (short window);
|
||||
public static native void commitCompletion (short window, String text,
|
||||
int position);
|
||||
public static native void commitText (short window, String text,
|
||||
int position);
|
||||
public static native void deleteSurroundingText (short window,
|
||||
int leftLength,
|
||||
int rightLength);
|
||||
public static native void finishComposingText (short window);
|
||||
public static native String getSelectedText (short window, int flags);
|
||||
public static native String getTextAfterCursor (short window, int length,
|
||||
int flags);
|
||||
public static native String getTextBeforeCursor (short window, int length,
|
||||
int flags);
|
||||
public static native void setComposingText (short window, String text,
|
||||
int newCursorPosition);
|
||||
public static native void setComposingRegion (short window, int start,
|
||||
int end);
|
||||
public static native void setSelection (short window, int start, int end);
|
||||
public static native void performEditorAction (short window,
|
||||
int editorAction);
|
||||
public static native ExtractedText getExtractedText (short window,
|
||||
ExtractedTextRequest req,
|
||||
int flags);
|
||||
|
||||
|
||||
/* Return the current value of the selection, or -1 upon
|
||||
failure. */
|
||||
public static native int getSelection (short window);
|
||||
|
||||
static
|
||||
{
|
||||
/* Older versions of Android cannot link correctly with shared
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ public class EmacsService extends Service
|
|||
private EmacsThread thread;
|
||||
private Handler handler;
|
||||
|
||||
/* Keep this in synch with androidgui.h. */
|
||||
public static final int IC_MODE_NULL = 0;
|
||||
public static final int IC_MODE_ACTION = 1;
|
||||
public static final int IC_MODE_TEXT = 2;
|
||||
|
||||
/* Display metrics used by font backends. */
|
||||
public DisplayMetrics metrics;
|
||||
|
||||
|
|
@ -258,20 +263,7 @@ public class EmacsService extends Service
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
|
||||
syncRunnable (runnable);
|
||||
return view.thing;
|
||||
}
|
||||
|
||||
|
|
@ -292,19 +284,7 @@ public class EmacsService extends Service
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
syncRunnable (runnable);
|
||||
}
|
||||
|
||||
public void
|
||||
|
|
@ -502,19 +482,7 @@ public class EmacsService extends Service
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
syncRunnable (runnable);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -594,20 +562,7 @@ public class EmacsService extends Service
|
|||
}
|
||||
};
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
runOnUiThread (runnable);
|
||||
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
}
|
||||
}
|
||||
|
||||
syncRunnable (runnable);
|
||||
return manager.thing;
|
||||
}
|
||||
|
||||
|
|
@ -622,4 +577,58 @@ public class EmacsService extends Service
|
|||
startActivity (intent);
|
||||
System.exit (0);
|
||||
}
|
||||
|
||||
/* Wait synchronously for the specified RUNNABLE to complete in the
|
||||
UI thread. Must be called from the Emacs thread. */
|
||||
|
||||
public static void
|
||||
syncRunnable (Runnable runnable)
|
||||
{
|
||||
EmacsNative.beginSynchronous ();
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
SERVICE.runOnUiThread (runnable);
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
break;
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmacsNative.endSynchronous ();
|
||||
}
|
||||
|
||||
public void
|
||||
updateIC (EmacsWindow window, int newSelectionStart,
|
||||
int newSelectionEnd, int composingRegionStart,
|
||||
int composingRegionEnd)
|
||||
{
|
||||
Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
|
||||
+ " " + newSelectionEnd + " "
|
||||
+ composingRegionStart + " "
|
||||
+ composingRegionEnd));
|
||||
window.view.imManager.updateSelection (window.view,
|
||||
newSelectionStart,
|
||||
newSelectionEnd,
|
||||
composingRegionStart,
|
||||
composingRegionEnd);
|
||||
}
|
||||
|
||||
public void
|
||||
resetIC (EmacsWindow window, int icMode)
|
||||
{
|
||||
Log.d (TAG, "resetIC: " + window);
|
||||
|
||||
window.view.setICMode (icMode);
|
||||
window.view.imManager.restartInput (window.view);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -103,6 +103,13 @@ public class EmacsView extends ViewGroup
|
|||
displayed whenever possible. */
|
||||
public boolean isCurrentlyTextEditor;
|
||||
|
||||
/* The associated input connection. */
|
||||
private EmacsInputConnection inputConnection;
|
||||
|
||||
/* The current IC mode. See `android_reset_ic' for more
|
||||
details. */
|
||||
private int icMode;
|
||||
|
||||
public
|
||||
EmacsView (EmacsWindow window)
|
||||
{
|
||||
|
|
@ -554,14 +561,46 @@ public class EmacsView extends ViewGroup
|
|||
public InputConnection
|
||||
onCreateInputConnection (EditorInfo info)
|
||||
{
|
||||
int selection, mode;
|
||||
|
||||
/* Figure out what kind of IME behavior Emacs wants. */
|
||||
mode = getICMode ();
|
||||
|
||||
/* Make sure the input method never displays a full screen input
|
||||
box that obscures Emacs. */
|
||||
info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||
|
||||
/* Set a reasonable inputType. */
|
||||
info.inputType = InputType.TYPE_NULL;
|
||||
info.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
|
||||
return null;
|
||||
/* Obtain the current position of point and set it as the
|
||||
selection. */
|
||||
selection = EmacsNative.getSelection (window.handle);
|
||||
|
||||
Log.d (TAG, "onCreateInputConnection: current selection is: " + selection);
|
||||
|
||||
/* If this fails or ANDROID_IC_MODE_NULL was requested, then don't
|
||||
initialize the input connection. */
|
||||
if (selection == -1 || mode == EmacsService.IC_MODE_NULL)
|
||||
{
|
||||
info.inputType = InputType.TYPE_NULL;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mode == EmacsService.IC_MODE_ACTION)
|
||||
info.imeOptions |= EditorInfo.IME_ACTION_DONE;
|
||||
|
||||
/* Set the initial selection fields. */
|
||||
info.initialSelStart = selection;
|
||||
info.initialSelEnd = selection;
|
||||
|
||||
/* Create the input connection if necessary. */
|
||||
|
||||
if (inputConnection == null)
|
||||
inputConnection = new EmacsInputConnection (this);
|
||||
|
||||
/* Return the input connection. */
|
||||
return inputConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -572,4 +611,16 @@ public class EmacsView extends ViewGroup
|
|||
keyboard. */
|
||||
return isCurrentlyTextEditor;
|
||||
}
|
||||
|
||||
public synchronized void
|
||||
setICMode (int icMode)
|
||||
{
|
||||
this.icMode = icMode;
|
||||
}
|
||||
|
||||
public synchronized int
|
||||
getICMode ()
|
||||
{
|
||||
return icMode;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue