From 0198b8cffd82893412c738dae8e50c45a99286f1 Mon Sep 17 00:00:00 2001
From: Po Lu
Date: Sun, 12 Feb 2023 20:32:25 +0800
Subject: Update Android port
* doc/emacs/android.texi (Android Environment): Document
notifications permission.
* java/org/gnu/emacs/EmacsEditable.java (EmacsEditable):
* java/org/gnu/emacs/EmacsInputConnection.java
(EmacsInputConnection): New files.
* java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load
library dependencies in a less verbose fashion.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Make imManager
public.
(onCreateInputConnection): Set InputType to TYPE_NULL for now.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown)
(onKeyUp, getEventUnicodeChar): Correctly handle key events with
strings.
* lisp/term/android-win.el (android-clear-preedit-text)
(android-preedit-text): New special event handlers.
* src/android.c (struct android_emacs_window): Add function
lookup_string.
(android_init_emacs_window): Adjust accordingly.
(android_wc_lookup_string): New function.
* src/androidgui.h (struct android_key_event): Improve
commentary.
(enum android_lookup_status): New enum.
* src/androidterm.c (handle_one_android_event): Synchronize IM
lookup code with X.
* src/coding.c (from_unicode_buffer): Implement on Android.
* src/coding.h:
* src/sfnt.c: Fix commentary.
---
java/org/gnu/emacs/EmacsEditable.java | 300 +++++++++++++++++++++++++++
java/org/gnu/emacs/EmacsInputConnection.java | 175 ++++++++++++++++
java/org/gnu/emacs/EmacsNative.java | 166 +++------------
java/org/gnu/emacs/EmacsView.java | 7 +-
java/org/gnu/emacs/EmacsWindow.java | 96 ++++++++-
5 files changed, 588 insertions(+), 156 deletions(-)
create mode 100644 java/org/gnu/emacs/EmacsEditable.java
create mode 100644 java/org/gnu/emacs/EmacsInputConnection.java
(limited to 'java/org/gnu')
diff --git a/java/org/gnu/emacs/EmacsEditable.java b/java/org/gnu/emacs/EmacsEditable.java
new file mode 100644
index 00000000000..79af65a6ccd
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsEditable.java
@@ -0,0 +1,300 @@
+/* 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 . */
+
+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;
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java
new file mode 100644
index 00000000000..897a393b984
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -0,0 +1,175 @@
+/* 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 . */
+
+package org.gnu.emacs;
+
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.SurroundingText;
+import android.view.KeyEvent;
+
+import android.text.Editable;
+
+import android.util.Log;
+
+/* Android input methods, take number six.
+
+ See EmacsEditable for more details. */
+
+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;
+
+ public
+ EmacsInputConnection (EmacsView view)
+ {
+ super (view, false);
+ this.view = view;
+ this.editable = new EmacsEditable (this);
+ }
+
+ @Override
+ public Editable
+ getEditable ()
+ {
+ return editable;
+ }
+
+ @Override
+ public boolean
+ setComposingText (CharSequence text, int newCursorPosition)
+ {
+ editable.compositionStart ();
+ super.setComposingText (text, newCursorPosition);
+ return true;
+ }
+
+ @Override
+ 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 + "\"");
+
+ 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 ()
+ {
+ switch (currentLargeOffset)
+ {
+ case 0:
+ currentLargeOffset = 100000;
+ return 100000;
+
+ case 100000:
+ currentLargeOffset = 30000;
+ return 30000;
+
+ case 30000:
+ currentLargeOffset = 0;
+ return 0;
+ }
+
+ currentLargeOffset = 0;
+ return -1;
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 939348ba420..f0219843d35 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -25,6 +25,10 @@ import android.content.res.AssetManager;
public class EmacsNative
{
+ /* List of native libraries that must be loaded during class
+ initialization. */
+ private static final String[] libraryDeps;
+
/* Obtain the fingerprint of this build of Emacs. The fingerprint
can be used to determine the dump file name. */
public static native String getFingerprint ();
@@ -167,148 +171,26 @@ public class EmacsNative
Every time you add a new shared library dependency to Emacs,
please add it here as well. */
- try
- {
- System.loadLibrary ("png_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("selinux_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("crypto_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("pcre_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("packagelistparser_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("gnutls_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("gmp_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("nettle_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("p11-kit_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("tasn1_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("hogweed_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("jansson_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("jpeg_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("tiff_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("xml2_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
-
- try
- {
- System.loadLibrary ("icuuc_emacs");
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
+ libraryDeps = new String[] { "png_emacs", "selinux_emacs",
+ "crypto_emacs", "pcre_emacs",
+ "packagelistparser_emacs",
+ "gnutls_emacs", "gmp_emacs",
+ "nettle_emacs", "p11-kit_emacs",
+ "tasn1_emacs", "hogweed_emacs",
+ "jansson_emacs", "jpeg_emacs",
+ "tiff_emacs", "xml2_emacs",
+ "icuuc_emacs", };
+
+ for (String dependency : libraryDeps)
+ {
+ try
+ {
+ System.loadLibrary (dependency);
+ }
+ catch (UnsatisfiedLinkError exception)
+ {
+ /* Ignore this exception. */
+ }
}
System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index 4fc8104e31f..bc3716f6da8 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -94,7 +94,7 @@ public class EmacsView extends ViewGroup
private long lastClipSerial;
/* The InputMethodManager for this view's context. */
- private InputMethodManager imManager;
+ public InputMethodManager imManager;
/* Whether or not this view is attached to a window. */
public boolean isAttachedToWindow;
@@ -558,8 +558,9 @@ public class EmacsView extends ViewGroup
box that obscures Emacs. */
info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
- /* But don't return an InputConnection, in order to force the on
- screen keyboard to work correctly. */
+ /* Set a reasonable inputType. */
+ info.inputType = InputType.TYPE_NULL;
+
return null;
}
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index 9e2f2f53270..0eca35cec61 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -23,6 +23,8 @@ import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
import android.content.Context;
@@ -100,7 +102,7 @@ public class EmacsWindow extends EmacsHandleObject
/* The button state and keyboard modifier mask at the time of the
last button press or release event. */
- private int lastButtonState, lastModifiers;
+ public int lastButtonState, lastModifiers;
/* Whether or not the window is mapped, and whether or not it is
deiconified. */
@@ -122,6 +124,10 @@ public class EmacsWindow extends EmacsHandleObject
to quit Emacs. */
private long lastVolumeButtonRelease;
+ /* Linked list of character strings which were recently sent as
+ events. */
+ public LinkedHashMap eventStrings;
+
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height, boolean overrideRedirect)
@@ -155,6 +161,19 @@ public class EmacsWindow extends EmacsHandleObject
}
scratchGC = new EmacsGC ((short) 0);
+
+ /* Create the map of input method-committed strings. Keep at most
+ ten strings in the map. */
+
+ eventStrings
+ = new LinkedHashMap () {
+ @Override
+ protected boolean
+ removeEldestEntry (Map.Entry entry)
+ {
+ return size () > 10;
+ }
+ };
}
public void
@@ -507,10 +526,40 @@ public class EmacsWindow extends EmacsHandleObject
return view.getBitmap ();
}
+ /* event.getCharacters is used because older input methods still
+ require it. */
+ @SuppressWarnings ("deprecation")
+ public int
+ getEventUnicodeChar (KeyEvent event, int state)
+ {
+ String characters;
+
+ if (event.getUnicodeChar (state) != 0)
+ return event.getUnicodeChar (state);
+
+ characters = event.getCharacters ();
+
+ if (characters != null && characters.length () == 1)
+ return characters.charAt (0);
+
+ return characters == null ? 0 : -1;
+ }
+
+ public void
+ saveUnicodeString (int serial, String string)
+ {
+ eventStrings.put (serial, string);
+ }
+
+ /* event.getCharacters is used because older input methods still
+ require it. */
+ @SuppressWarnings ("deprecation")
public void
onKeyDown (int keyCode, KeyEvent event)
{
int state, state_1;
+ long serial;
+ String characters;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
state = event.getModifiers ();
@@ -537,18 +586,28 @@ public class EmacsWindow extends EmacsHandleObject
state_1
= state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
- EmacsNative.sendKeyPress (this.handle,
- event.getEventTime (),
- state, keyCode,
- event.getUnicodeChar (state_1));
- lastModifiers = state;
+ synchronized (eventStrings)
+ {
+ serial
+ = EmacsNative.sendKeyPress (this.handle,
+ event.getEventTime (),
+ state, keyCode,
+ getEventUnicodeChar (event,
+ state_1));
+ lastModifiers = state;
+
+ characters = event.getCharacters ();
+
+ if (characters != null && characters.length () > 1)
+ saveUnicodeString ((int) serial, characters);
+ }
}
public void
onKeyUp (int keyCode, KeyEvent event)
{
int state, state_1;
- long time;
+ long time, serial;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
state = event.getModifiers ();
@@ -575,10 +634,12 @@ public class EmacsWindow extends EmacsHandleObject
state_1
= state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
- EmacsNative.sendKeyRelease (this.handle,
- event.getEventTime (),
- state, keyCode,
- event.getUnicodeChar (state_1));
+ serial
+ = EmacsNative.sendKeyRelease (this.handle,
+ event.getEventTime (),
+ state, keyCode,
+ getEventUnicodeChar (event,
+ state_1));
lastModifiers = state;
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
@@ -1105,4 +1166,17 @@ public class EmacsWindow extends EmacsHandleObject
}
});
}
+
+ public String
+ lookupString (int eventSerial)
+ {
+ String any;
+
+ synchronized (eventStrings)
+ {
+ any = eventStrings.remove (eventSerial);
+ }
+
+ return any;
+ }
};
--
cgit v1.2.1