mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-21 06:55:39 +00:00
Update Android port
* .gitignore: Don't ignore verbose.mk.android. * doc/emacs/Makefile.in (EMACSSOURCES): Add android.texi and input.texi. * doc/emacs/android.texi (Android): Document support for the on-screen keyboard. (Android Startup): Document how to start Emacs with -Q on Android. (Android Environment): Document how Emacs works around the system ``task killer''. Document changes to frame deletion behavior. * doc/emacs/emacs.texi (Top): * doc/emacs/input.texi (Other Input Devices, On-Screen Keyboards): Document how to use Emacs with virtual keyboards. * doc/lispref/commands.texi (Touchscreen Events): Document changes to `touch-screen-track-drag'. * doc/lispref/frames.texi (Frames, On-Screen Keyboards): New node. * java/AndroidManifest.xml.in: Add settings activity and appropriate OSK adjustment mode. * java/org/gnu/emacs/EmacsActivity.java (onCreate): Allow creating Emacs with -Q. (onDestroy): Don't remove if killed by the system. * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems): Fix context menus again. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Make all event sending functions return long. * java/org/gnu/emacs/EmacsPreferencesActivity.java (EmacsPreferencesActivity): New class. * java/org/gnu/emacs/EmacsService.java (EmacsService) (onStartCommand, onCreate, startEmacsService): Start as a foreground service if necessary to bypass system restrictions. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout) (onDetachedFromWindow): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, viewLayout): Implement frame resize synchronization.. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager, removeWindowConsumer): Adjust accordingly for changes to frame deletion behavior. * lisp/frame.el (android-toggle-on-screen-keyboard) (frame-toggle-on-screen-keyboard): New function. * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard) (minibuffer-exit-on-screen-keyboard): New functions. (minibuffer-setup-hook, minibuffer-exit-hook): Add new functions to hooks. * lisp/touch-screen.el (touch-screen-relative-xy): Accept new value of window `frame'. Return frame coordinates in that case. (touch-screen-set-point-commands): New variable. (touch-screen-handle-point-up): Respect that variable. (touch-screen-track-drag): Return `no-drag' where appropriate. (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line): Refactor to use `no-drag'. * src/android.c (struct android_emacs_window): New methods. Make all event sending functions return the event serial. (android_toggle_on_screen_keyboard, android_window_updated): New functions. * src/android.h: Update prototypes. * src/androidfns.c (Fandroid_toggle_on_screen_keyboard) (syms_of_androidfns): New function. * src/androidgui.h (struct android_any_event) (struct android_key_event, struct android_configure_event) (struct android_focus_event, struct android_window_action_event) (struct android_crossing_event, struct android_motion_event) (struct android_button_event, struct android_touch_event) (struct android_wheel_event, struct android_iconify_event) (struct android_menu_event): Add `serial' fields. * src/androidterm.c (handle_one_android_event) (android_frame_up_to_date): * src/androidterm.h (struct android_output): Implement frame resize synchronization.
This commit is contained in:
parent
e07b58dc35
commit
d44b60c2f0
1
.gitignore
vendored
1
.gitignore
vendored
@ -55,6 +55,7 @@ src/emacs-module.h
|
||||
# Built by recursive call to `configure'.
|
||||
*.android
|
||||
!INSTALL.android
|
||||
!verbose.mk.android
|
||||
|
||||
# Built by `java'.
|
||||
java/install_temp/*
|
||||
|
@ -146,6 +146,8 @@ EMACSSOURCES= \
|
||||
${srcdir}/glossary.texi \
|
||||
${srcdir}/ack.texi \
|
||||
${srcdir}/kmacro.texi \
|
||||
${srcdir}/android.texi \
|
||||
${srcdir}/input.texi \
|
||||
$(EMACS_XTRA)
|
||||
|
||||
## Disable implicit rules.
|
||||
|
@ -10,8 +10,8 @@ Alliance. This section describes the peculiarities of using Emacs on
|
||||
an Android device running Android 2.2 or later.
|
||||
|
||||
Android devices commonly rely on user input through a touch screen
|
||||
or digitizer device. For more information about using them with
|
||||
Emacs, @pxref{other Input Devices}.
|
||||
or digitizer device and on-screen keyboard. For more information
|
||||
about using such devices with Emacs, @pxref{Other Input Devices}.
|
||||
|
||||
@menu
|
||||
* What is Android?:: Preamble.
|
||||
@ -82,6 +82,16 @@ command on that other system:
|
||||
$ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
|
||||
@end example
|
||||
|
||||
@cindex emacs -Q, android
|
||||
Since Android has no command line, there is normally no way to specify
|
||||
command-line arguments. However, Emacs can be started with the
|
||||
equivalent of the @code{--quick} option (@pxref{Initial Options})
|
||||
through a special preferences screen, which can be accessed through
|
||||
the Emacs ``app info'' page in the system settings application.
|
||||
|
||||
Consult the manufacturer of your device for more details, as how to do
|
||||
this varies by device.
|
||||
|
||||
@node Android File System
|
||||
@section What files Emacs can access under Android
|
||||
@cindex /assets directory, android
|
||||
@ -149,7 +159,7 @@ which is the app data directory (@pxref{Android File System}.)
|
||||
directories, and the app data directories of other applications. In
|
||||
recent versions of Android, the system also prohibits, for security
|
||||
reasons, even Emacs itself from running executables inside the app
|
||||
data directory!
|
||||
data directory.
|
||||
|
||||
Emacs comes with several binaries. While being executable files,
|
||||
they are packaged as libraries in the library directory, because
|
||||
@ -173,9 +183,28 @@ within itself.
|
||||
Application processes are treated as disposable entities by the
|
||||
system. When all Emacs frames move to the background, Emacs is liable
|
||||
to be killed by the system at any time, for the purpose of saving
|
||||
resources. There is currently no easy way to bypass these
|
||||
restrictions, aside from keeping Emacs constantly running in the
|
||||
foreground.
|
||||
system resources.
|
||||
|
||||
On Android 7.1 and earlier, Emacs tells the system to treat it as a
|
||||
``background service''. The system will try to avoid killing Emacs
|
||||
unless the device is under memory stress.
|
||||
|
||||
Android 8.0 removed the ability for background services to receive
|
||||
such special treatment. However, Emacs applies a workaround: the
|
||||
system considers applications that create a permanent notification to
|
||||
be performing active work, and will avoid killing such applications.
|
||||
Thus, on those systems, Emacs displays a permanant notification for as
|
||||
long as it is running. Once the notification is displayed, it can be
|
||||
safely hidden through the system settings without resulting in Emacs
|
||||
being killed.
|
||||
|
||||
However, it is not guaranteed that the system will not kill Emacs,
|
||||
even if the notification is being displayed. While the Open Handset
|
||||
Alliance's sample implementation of Android behaves correctly, many
|
||||
manufacturers place additional restrictions on program execution in
|
||||
the background in their proprietary versions of Android. There is a
|
||||
list of such troublesome manufacturers and sometimes workarounds, at
|
||||
@url{https://dontkillmyapp.com/}.
|
||||
|
||||
@section Android permissions
|
||||
@cindex external storage, android
|
||||
@ -272,14 +301,14 @@ maximized or full-screen, and only one window can be displayed at a
|
||||
time. On larger devices, the system allows up to four windows to be
|
||||
tiled on the screen at any time.
|
||||
|
||||
Windows on Android do not continue to exist indefinitely after they
|
||||
Windows on Android do not continue to exist indefinitely after they
|
||||
are created. Instead, the system may choose to terminate windows that
|
||||
are not on screen in order to save memory, with the assumption that
|
||||
the program will save its contents to disk and restore them later,
|
||||
when the user asks to open it again. As this is obvious not possible
|
||||
with Emacs, Emacs separates a frame from a system window.
|
||||
|
||||
Each system window created (including the initial window created
|
||||
Each system window created (including the initial window created
|
||||
during Emacs startup) is appended to a list of windows that do not
|
||||
have associated frames. When a frame is created, Emacs looks up any
|
||||
window within that list, and displays the contents of the frame
|
||||
@ -287,11 +316,28 @@ within; if there is no window at all, then one is created. Likewise,
|
||||
when a new window is created by the system, Emacs places the contents
|
||||
of any frame that is not already displayed within a window inside.
|
||||
When a frame is closed, the corresponding system window is also
|
||||
closed.
|
||||
closed. Upon startup, the system creates a window itself (within
|
||||
which Emacs displays the first window system frame shortly
|
||||
thereafter.) Emacs differentiates between that window and windows
|
||||
created on behalf of other frames to determine what to do when the
|
||||
system window associated with a frame is closed:
|
||||
|
||||
This strategy works as long as one window is in the foreground.
|
||||
Otherwise, Emacs can only run in the background for a limited amount
|
||||
of time before the process is killed completely.
|
||||
@itemize @bullet
|
||||
@item
|
||||
When the system closes the window created during application startup
|
||||
in order to save memory, Emacs retains the frame for when that window
|
||||
is created later.
|
||||
|
||||
@item
|
||||
When the user closes the window created during application startup,
|
||||
and the window was not previously closed by the system in order to
|
||||
save resources, Emacs deletes any frame displayed within that window.
|
||||
|
||||
@item
|
||||
When the user or the system closes any window created by Emacs on
|
||||
behalf of a specific frame, Emacs deletes the frame displayed within
|
||||
that window.
|
||||
@end itemize
|
||||
|
||||
@cindex windowing limitations, android
|
||||
@cindex frame parameters, android
|
||||
|
@ -1269,6 +1269,7 @@ Emacs and Android
|
||||
Emacs and unconventional input devices
|
||||
|
||||
* Touchscreens:: Using Emacs on touchscreens.
|
||||
* On-Screen Keyboards:: Using Emacs with virtual keyboards.
|
||||
|
||||
Emacs and Microsoft Windows/MS-DOS
|
||||
|
||||
|
@ -16,6 +16,7 @@ input devices, which is detailed here.
|
||||
|
||||
@menu
|
||||
* Touchscreens:: Using Emacs on touchscreens.
|
||||
* On-Screen Keyboards:: Using Emacs with virtual keyboards.
|
||||
@end menu
|
||||
|
||||
@node Touchscreens
|
||||
@ -58,3 +59,38 @@ were to be held down. @xref{Mouse Commands}.
|
||||
By default, Emacs considers a tool as having been left on the
|
||||
display for a while after 0.7 seconds, but this can be changed by
|
||||
customizing the variable @code{touch-screen-delay}.
|
||||
|
||||
@node On-Screen Keyboards
|
||||
@section Using Emacs with virtual keyboards
|
||||
@cindex virtual keyboards
|
||||
@cindex on-screen keyboards
|
||||
|
||||
When there is no physical keyboard attached to a system, the
|
||||
windowing system typically provides an on-screen keyboard, more often
|
||||
known as a ``virtual keyboard'', containing rows of clickable buttons
|
||||
that send keyboard input to the application, much like a real keyboard
|
||||
would. This virtual keyboard is hidden by default, as it uses up
|
||||
valuable on-screen real estate, and must be opened once the program
|
||||
being used is ready to accept keyboard input.
|
||||
|
||||
Under the X Window System, the client that provides the on-screen
|
||||
keyboard typically detects when the application is ready to accept
|
||||
keyboard input through a set of complex heuristics, and automatically
|
||||
displays the keyboard when necessary.
|
||||
|
||||
On other systems such as Android, Emacs must tell the system when it
|
||||
is ready to accept keyboard input. Typically, this is done in
|
||||
response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or
|
||||
once to the minibuffer becomes in use (@pxref{Minibuffer}.)
|
||||
|
||||
@vindex touch-screen-set-point-commands
|
||||
When a ``tap'' gesture results in a command being executed, Emacs
|
||||
checks to see whether or not the command is supposed to set the point
|
||||
by looking for it in the list @code{touch-screen-set-point-commands}.
|
||||
If it is, then Emacs looks up whether or not the text under the point
|
||||
is read-only; if not, it activates the on-screen keyboard, assuming
|
||||
that the user is about to enter text in to the current buffer.
|
||||
|
||||
Emacs also provides a set of functions to show or hide the on-screen
|
||||
keyboard. For more details, @pxref{On-Screen Keyboards,,, elisp, The
|
||||
Emacs Lisp Reference Manual}.
|
||||
|
@ -2055,9 +2055,11 @@ The caller should not perform any action in that case.
|
||||
|
||||
@defun touch-screen-track-drag event update &optional data
|
||||
This function is used to track a single ``drag'' gesture originating
|
||||
from the @code{touchscreen-begin} event @code{event}. Currently, it
|
||||
behaves identically to @code{touch-screen-track-tap}, but differences
|
||||
are anticipated in the future.
|
||||
from the @code{touchscreen-begin} event @code{event}.
|
||||
|
||||
It behaves like @code{touch-screen-track-tap}, except that it returns
|
||||
@code{no-drag} if the touchpoint in @code{event} did not move far
|
||||
enough to qualify as an actual drag.
|
||||
@end defun
|
||||
|
||||
@node Focus Events
|
||||
|
@ -104,6 +104,7 @@ window of another Emacs frame. @xref{Child Frames}.
|
||||
* Mouse Tracking:: Getting events that say when the mouse moves.
|
||||
* Mouse Position:: Asking where the mouse is, or moving it.
|
||||
* Pop-Up Menus:: Displaying a menu for the user to select from.
|
||||
* On-Screen Keyboards:: Displaying the virtual keyboard.
|
||||
* Dialog Boxes:: Displaying a box to ask yes or no.
|
||||
* Pointer Shape:: Specifying the shape of the mouse pointer.
|
||||
* Window System Selections:: Transferring text to and from other X clients.
|
||||
@ -3812,6 +3813,26 @@ keymap. It won't be called if @code{x-popup-menu} returns for some
|
||||
other reason without displaying a pop-up menu.
|
||||
@end defvar
|
||||
|
||||
@node On-Screen Keyboards
|
||||
@section On-Screen Keyboards
|
||||
|
||||
An on-screen keyboard is a special kind of pop up provided by the
|
||||
system, with rows of clickable buttons that act as a real keyboard.
|
||||
|
||||
On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs
|
||||
Manual}), Emacs is supposed to display and hide the on screen keyboard
|
||||
depending on whether or not the user is about to type something.
|
||||
|
||||
@defun frame-toggle-on-screen-keyboard frame hide
|
||||
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.
|
||||
|
||||
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.
|
||||
@end defun
|
||||
|
||||
@node Dialog Boxes
|
||||
@section Dialog Boxes
|
||||
@cindex dialog boxes
|
||||
|
@ -62,8 +62,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
|
||||
android:theme="@android:style/Theme"
|
||||
android:debuggable="true"
|
||||
android:extractNativeLibs="true">
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -73,8 +75,18 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
|
||||
|
||||
<activity android:autoRemoveFromRecents="true"
|
||||
android:label="Emacs options"
|
||||
android:name=".EmacsPreferencesActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="org.gnu.emacs.EmacsService"
|
||||
android:directBootAware="false"
|
||||
android:enabled="true"
|
||||
|
@ -161,6 +161,13 @@ public class EmacsActivity extends Activity
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
FrameLayout.LayoutParams params;
|
||||
Intent intent;
|
||||
|
||||
/* See if Emacs should be started with -Q. */
|
||||
intent = getIntent ();
|
||||
EmacsService.needDashQ
|
||||
= intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
|
||||
false);
|
||||
|
||||
/* Set the theme to one without a title bar. */
|
||||
|
||||
@ -179,9 +186,8 @@ public class EmacsActivity extends Activity
|
||||
/* Set it as the content view. */
|
||||
setContentView (layout);
|
||||
|
||||
if (EmacsService.SERVICE == null)
|
||||
/* Start the Emacs service now. */
|
||||
startService (new Intent (this, EmacsService.class));
|
||||
/* Maybe start the Emacs service if necessary. */
|
||||
EmacsService.startEmacsService (this);
|
||||
|
||||
/* Add this activity to the list of available activities. */
|
||||
EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
|
||||
@ -193,10 +199,16 @@ public class EmacsActivity extends Activity
|
||||
public void
|
||||
onDestroy ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
boolean isMultitask;
|
||||
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
|
||||
/* The activity will die shortly hereafter. If there is a window
|
||||
attached, close it now. */
|
||||
Log.d (TAG, "onDestroy " + this);
|
||||
EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this);
|
||||
isMultitask = this instanceof EmacsMultitaskActivity;
|
||||
manager.removeWindowConsumer (this, isMultitask || isFinishing ());
|
||||
focusedActivities.remove (this);
|
||||
invalidateFocus ();
|
||||
super.onDestroy ();
|
||||
|
@ -174,6 +174,7 @@ private class Item implements MenuItem.OnMenuItemClickListener
|
||||
support doing so, create the submenu and add the
|
||||
contents of the menu to it. */
|
||||
submenu = menu.addSubMenu (item.itemName);
|
||||
item.subMenu.inflateMenuItems (submenu);
|
||||
}
|
||||
catch (UnsupportedOperationException exception)
|
||||
{
|
||||
|
@ -61,75 +61,76 @@ public static native void setEmacsParams (AssetManager assetManager,
|
||||
/* Abort and generate a native core dump. */
|
||||
public static native void emacsAbort ();
|
||||
|
||||
/* Send an ANDROID_CONFIGURE_NOTIFY event. */
|
||||
public static native void sendConfigureNotify (short window, long time,
|
||||
/* Send an ANDROID_CONFIGURE_NOTIFY event. The values of all the
|
||||
functions below are the serials of the events sent. */
|
||||
public static native long sendConfigureNotify (short window, long time,
|
||||
int x, int y, int width,
|
||||
int height);
|
||||
|
||||
/* Send an ANDROID_KEY_PRESS event. */
|
||||
public static native void sendKeyPress (short window, long time, int state,
|
||||
public static native long sendKeyPress (short window, long time, int state,
|
||||
int keyCode, int unicodeChar);
|
||||
|
||||
/* Send an ANDROID_KEY_RELEASE event. */
|
||||
public static native void sendKeyRelease (short window, long time, int state,
|
||||
public static native long sendKeyRelease (short window, long time, int state,
|
||||
int keyCode, int unicodeChar);
|
||||
|
||||
/* Send an ANDROID_FOCUS_IN event. */
|
||||
public static native void sendFocusIn (short window, long time);
|
||||
public static native long sendFocusIn (short window, long time);
|
||||
|
||||
/* Send an ANDROID_FOCUS_OUT event. */
|
||||
public static native void sendFocusOut (short window, long time);
|
||||
public static native long sendFocusOut (short window, long time);
|
||||
|
||||
/* Send an ANDROID_WINDOW_ACTION event. */
|
||||
public static native void sendWindowAction (short window, int action);
|
||||
public static native long sendWindowAction (short window, int action);
|
||||
|
||||
/* Send an ANDROID_ENTER_NOTIFY event. */
|
||||
public static native void sendEnterNotify (short window, int x, int y,
|
||||
public static native long sendEnterNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_LEAVE_NOTIFY event. */
|
||||
public static native void sendLeaveNotify (short window, int x, int y,
|
||||
public static native long sendLeaveNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_MOTION_NOTIFY event. */
|
||||
public static native void sendMotionNotify (short window, int x, int y,
|
||||
public static native long sendMotionNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_BUTTON_PRESS event. */
|
||||
public static native void sendButtonPress (short window, int x, int y,
|
||||
public static native long sendButtonPress (short window, int x, int y,
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_BUTTON_RELEASE event. */
|
||||
public static native void sendButtonRelease (short window, int x, int y,
|
||||
public static native long sendButtonRelease (short window, int x, int y,
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_TOUCH_DOWN event. */
|
||||
public static native void sendTouchDown (short window, int x, int y,
|
||||
public static native long sendTouchDown (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_UP event. */
|
||||
public static native void sendTouchUp (short window, int x, int y,
|
||||
public static native long sendTouchUp (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_MOVE event. */
|
||||
public static native void sendTouchMove (short window, int x, int y,
|
||||
public static native long sendTouchMove (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_WHEEL event. */
|
||||
public static native void sendWheel (short window, int x, int y,
|
||||
public static native long sendWheel (short window, int x, int y,
|
||||
long time, int state,
|
||||
float xDelta, float yDelta);
|
||||
|
||||
/* Send an ANDROID_ICONIFIED event. */
|
||||
public static native void sendIconified (short window);
|
||||
public static native long sendIconified (short window);
|
||||
|
||||
/* Send an ANDROID_DEICONIFIED event. */
|
||||
public static native void sendDeiconified (short window);
|
||||
public static native long sendDeiconified (short window);
|
||||
|
||||
/* Send an ANDROID_CONTEXT_MENU event. */
|
||||
public static native void sendContextMenu (short window, int menuEventID);
|
||||
public static native long sendContextMenu (short window, int menuEventID);
|
||||
|
||||
static
|
||||
{
|
||||
|
98
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
98
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
@ -0,0 +1,98 @@
|
||||
/* 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.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.R;
|
||||
|
||||
/* This module provides a ``preferences'' display for Emacs. It is
|
||||
supposed to be launched from inside the Settings application to
|
||||
perform various actions, such as starting Emacs with the ``-Q''
|
||||
option, which would not be possible otherwise, as there is no
|
||||
command line on Android. */
|
||||
|
||||
public class EmacsPreferencesActivity extends Activity
|
||||
{
|
||||
/* The linear layout associated with the activity. */
|
||||
private LinearLayout layout;
|
||||
|
||||
/* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and
|
||||
tell the system to EmacsActivity with some parameters later. */
|
||||
|
||||
private void
|
||||
startEmacsQ ()
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
intent = new Intent (this, EmacsActivity.class);
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
intent.putExtra ("org.gnu.emacs.START_DASH_Q", true);
|
||||
startActivity (intent);
|
||||
System.exit (0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
LinearLayout layout;
|
||||
TextView textView;
|
||||
LinearLayout.LayoutParams params;
|
||||
int resid;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
setTheme (R.style.Theme_DeviceDefault_Settings);
|
||||
else if (Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
setTheme (R.style.Theme_DeviceDefault);
|
||||
|
||||
layout = new LinearLayout (this);
|
||||
layout.setOrientation (LinearLayout.VERTICAL);
|
||||
setContentView (layout);
|
||||
|
||||
textView = new TextView (this);
|
||||
textView.setPadding (8, 20, 20, 8);
|
||||
|
||||
params = new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
textView.setLayoutParams (params);
|
||||
textView.setText ("(Re)start Emacs with -Q");
|
||||
textView.setOnClickListener (new View.OnClickListener () {
|
||||
@Override
|
||||
public void
|
||||
onClick (View view)
|
||||
{
|
||||
startEmacsQ ();
|
||||
}
|
||||
});
|
||||
layout.addView (textView);
|
||||
|
||||
super.onCreate (savedInstanceState);
|
||||
}
|
||||
};
|
@ -32,7 +32,13 @@
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
@ -63,6 +69,7 @@ public class EmacsService extends Service
|
||||
public static final String TAG = "EmacsService";
|
||||
public static final int MAX_PENDING_REQUESTS = 256;
|
||||
public static volatile EmacsService SERVICE;
|
||||
public static boolean needDashQ;
|
||||
|
||||
private EmacsThread thread;
|
||||
private Handler handler;
|
||||
@ -74,6 +81,31 @@ public class EmacsService extends Service
|
||||
public int
|
||||
onStartCommand (Intent intent, int flags, int startId)
|
||||
{
|
||||
Notification notification;
|
||||
NotificationManager manager;
|
||||
NotificationChannel channel;
|
||||
String infoBlurb;
|
||||
Object tem;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
tem = getSystemService (Context.NOTIFICATION_SERVICE);
|
||||
manager = (NotificationManager) tem;
|
||||
infoBlurb = ("See (emacs)Android Environment for more"
|
||||
+ " details about this notification.");
|
||||
channel
|
||||
= new NotificationChannel ("emacs", "Emacs persistent notification",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
manager.createNotificationChannel (channel);
|
||||
notification = (new Notification.Builder (this, "emacs")
|
||||
.setContentTitle ("Emacs")
|
||||
.setContentText (infoBlurb)
|
||||
.setSmallIcon (android.R.drawable.sym_def_app_icon)
|
||||
.build ());
|
||||
manager.notify (1, notification);
|
||||
startForeground (1, notification);
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@ -137,7 +169,7 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
|
||||
this);
|
||||
|
||||
/* Start the thread that runs Emacs. */
|
||||
thread = new EmacsThread (this);
|
||||
thread = new EmacsThread (this, needDashQ);
|
||||
thread.start ();
|
||||
}
|
||||
catch (IOException exception)
|
||||
@ -444,4 +476,32 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Start the Emacs service if necessary. On Android 26 and up,
|
||||
start Emacs as a foreground service with a notification, to avoid
|
||||
it being killed by the system.
|
||||
|
||||
On older systems, simply start it as a normal background
|
||||
service. */
|
||||
|
||||
public static void
|
||||
startEmacsService (Context context)
|
||||
{
|
||||
PendingIntent intent;
|
||||
|
||||
if (EmacsService.SERVICE == null)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
/* Start the Emacs service now. */
|
||||
context.startService (new Intent (context,
|
||||
EmacsService.class));
|
||||
else
|
||||
/* Display the permanant notification and start Emacs as a
|
||||
foreground service. */
|
||||
context.startForegroundService (new Intent (context,
|
||||
EmacsService.class));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -32,53 +32,106 @@
|
||||
public class EmacsSurfaceView extends SurfaceView
|
||||
{
|
||||
private static final String TAG = "EmacsSurfaceView";
|
||||
|
||||
public Object surfaceChangeLock;
|
||||
private boolean created;
|
||||
private EmacsView view;
|
||||
|
||||
/* This is the callback used on Android 8 to 25. */
|
||||
|
||||
private class Callback implements SurfaceHolder.Callback
|
||||
{
|
||||
@Override
|
||||
public void
|
||||
surfaceChanged (SurfaceHolder holder, int format,
|
||||
int width, int height)
|
||||
{
|
||||
Log.d (TAG, "surfaceChanged: " + view + ", " + view.pendingConfigure);
|
||||
|
||||
/* Make sure not to swap buffers if there is pending
|
||||
configuration, because otherwise the redraw callback will not
|
||||
run correctly. */
|
||||
|
||||
if (view.pendingConfigure == 0)
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceCreated (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceCreated: " + view);
|
||||
created = true;
|
||||
}
|
||||
|
||||
/* Drop the lock when doing this, or a deadlock can
|
||||
result. */
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceDestroyed (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceDestroyed: " + view);
|
||||
created = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
super (view.getContext ());
|
||||
|
||||
surfaceChangeLock = new Object ();
|
||||
this.surfaceChangeLock = new Object ();
|
||||
this.view = view;
|
||||
|
||||
getHolder ().addCallback (new SurfaceHolder.Callback () {
|
||||
@Override
|
||||
public void
|
||||
surfaceChanged (SurfaceHolder holder, int format,
|
||||
int width, int height)
|
||||
{
|
||||
Log.d (TAG, "surfaceChanged: " + view);
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceCreated (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceCreated: " + view);
|
||||
created = true;
|
||||
}
|
||||
|
||||
/* Drop the lock when doing this, or a deadlock can
|
||||
result. */
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
surfaceDestroyed (SurfaceHolder holder)
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceDestroyed: " + view);
|
||||
created = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
getHolder ().addCallback (new Callback ());
|
||||
else
|
||||
getHolder ().addCallback (new Callback2 ());
|
||||
}
|
||||
|
||||
public boolean
|
||||
|
@ -23,12 +23,13 @@
|
||||
|
||||
public class EmacsThread extends Thread
|
||||
{
|
||||
EmacsService context;
|
||||
/* Whether or not Emacs should be started -Q. */
|
||||
private boolean startDashQ;
|
||||
|
||||
public
|
||||
EmacsThread (EmacsService service)
|
||||
EmacsThread (EmacsService service, boolean startDashQ)
|
||||
{
|
||||
context = service;
|
||||
this.startDashQ = startDashQ;
|
||||
}
|
||||
|
||||
public void
|
||||
@ -36,7 +37,10 @@ public class EmacsThread extends Thread
|
||||
{
|
||||
String args[];
|
||||
|
||||
args = new String[] { "libandroid-emacs.so", };
|
||||
if (!startDashQ)
|
||||
args = new String[] { "libandroid-emacs.so", };
|
||||
else
|
||||
args = new String[] { "libandroid-emacs.so", "-Q", };
|
||||
|
||||
/* Run the native code now. */
|
||||
EmacsNative.initEmacs (args);
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
|
||||
import android.view.ContextMenu;
|
||||
@ -27,6 +28,8 @@
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
@ -86,11 +89,23 @@ public class EmacsView extends ViewGroup
|
||||
/* The serial of the last clip rectangle change. */
|
||||
private long lastClipSerial;
|
||||
|
||||
/* 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;
|
||||
|
||||
public
|
||||
EmacsView (EmacsWindow window)
|
||||
{
|
||||
super (EmacsService.SERVICE);
|
||||
|
||||
Object tem;
|
||||
|
||||
this.window = window;
|
||||
this.damageRegion = new Region ();
|
||||
this.paint = new Paint ();
|
||||
@ -111,6 +126,10 @@ public class EmacsView extends ViewGroup
|
||||
/* Get rid of the default focus highlight. */
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
|
||||
setDefaultFocusHighlightEnabled (false);
|
||||
|
||||
/* Obtain the input method manager. */
|
||||
tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE);
|
||||
imManager = (InputMethodManager) tem;
|
||||
}
|
||||
|
||||
private void
|
||||
@ -259,7 +278,8 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
|
||||
if (changed || mustReportLayout)
|
||||
{
|
||||
mustReportLayout = false;
|
||||
window.viewLayout (left, top, right, bottom);
|
||||
pendingConfigure
|
||||
= window.viewLayout (left, top, right, bottom);
|
||||
}
|
||||
|
||||
measuredWidth = right - left;
|
||||
@ -538,4 +558,37 @@ else if (child.getVisibility () != GONE)
|
||||
Runtime.getRuntime ().gc ();
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
showOnScreenKeyboard ()
|
||||
{
|
||||
/* Specifying no flags at all tells the system the user asked for
|
||||
the input method to be displayed. */
|
||||
imManager.showSoftInput (this, 0);
|
||||
}
|
||||
|
||||
public void
|
||||
hideOnScreenKeyboard ()
|
||||
{
|
||||
imManager.hideSoftInputFromWindow (this.getWindowToken (),
|
||||
0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -233,7 +233,7 @@ private class Coordinate
|
||||
return attached;
|
||||
}
|
||||
|
||||
public void
|
||||
public long
|
||||
viewLayout (int left, int top, int right, int bottom)
|
||||
{
|
||||
int rectWidth, rectHeight;
|
||||
@ -249,10 +249,10 @@ private class Coordinate
|
||||
rectWidth = right - left;
|
||||
rectHeight = bottom - top;
|
||||
|
||||
EmacsNative.sendConfigureNotify (this.handle,
|
||||
System.currentTimeMillis (),
|
||||
left, top, rectWidth,
|
||||
rectHeight);
|
||||
return EmacsNative.sendConfigureNotify (this.handle,
|
||||
System.currentTimeMillis (),
|
||||
left, top, rectWidth,
|
||||
rectHeight);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -589,11 +589,20 @@ private class Coordinate
|
||||
EmacsActivity.invalidateFocus ();
|
||||
}
|
||||
|
||||
/* Notice that the activity has been detached or destroyed.
|
||||
|
||||
ISFINISHING is set if the activity is not the main activity, or
|
||||
if the activity was not destroyed in response to explicit user
|
||||
action. */
|
||||
|
||||
public void
|
||||
onActivityDetached ()
|
||||
onActivityDetached (boolean isFinishing)
|
||||
{
|
||||
/* Destroy the associated frame when the activity is detached. */
|
||||
EmacsNative.sendWindowAction (this.handle, 0);
|
||||
/* Destroy the associated frame when the activity is detached in
|
||||
response to explicit user action. */
|
||||
|
||||
if (isFinishing)
|
||||
EmacsNative.sendWindowAction (this.handle, 0);
|
||||
}
|
||||
|
||||
/* Look through the button state to determine what button EVENT was
|
||||
@ -1064,4 +1073,37 @@ else if (EmacsWindow.this.isMapped)
|
||||
/* Return the resulting coordinates. */
|
||||
return array;
|
||||
}
|
||||
|
||||
public void
|
||||
toggleOnScreenKeyboard (final boolean on)
|
||||
{
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
if (on)
|
||||
view.showOnScreenKeyboard ();
|
||||
else
|
||||
view.hideOnScreenKeyboard ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Notice that outstanding configure events have been processed.
|
||||
SERIAL is checked in the UI thread to verify that no new
|
||||
configure events have been generated in the mean time. */
|
||||
|
||||
public void
|
||||
windowUpdated (final long serial)
|
||||
{
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.windowUpdated (serial);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ public interface WindowConsumer
|
||||
}
|
||||
|
||||
public void
|
||||
removeWindowConsumer (WindowConsumer consumer)
|
||||
removeWindowConsumer (WindowConsumer consumer, boolean isFinishing)
|
||||
{
|
||||
EmacsWindow window;
|
||||
|
||||
@ -147,7 +147,7 @@ public interface WindowConsumer
|
||||
Log.d (TAG, "removeWindowConsumer: detaching " + window);
|
||||
|
||||
consumer.detachWindow ();
|
||||
window.onActivityDetached ();
|
||||
window.onActivityDetached (isFinishing);
|
||||
}
|
||||
|
||||
Log.d (TAG, "removeWindowConsumer: removing " + consumer);
|
||||
|
@ -2553,6 +2553,23 @@ symbols."
|
||||
((string= name "Virtual core keyboard")
|
||||
'core-keyboard))))))
|
||||
|
||||
|
||||
;;;; On-screen keyboard management.
|
||||
|
||||
(declare-function android-toggle-on-screen-keyboard "androidfns.c")
|
||||
|
||||
(defun frame-toggle-on-screen-keyboard (frame hide)
|
||||
"Display or hide the on-screen keyboard.
|
||||
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.
|
||||
|
||||
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)))))
|
||||
|
||||
|
||||
;;;; Frame geometry values
|
||||
|
||||
|
@ -4576,6 +4576,29 @@ is included in the return value."
|
||||
default)))
|
||||
": "))
|
||||
|
||||
|
||||
;;; On screen keyboard support.
|
||||
;; Try to display the on screen keyboard whenever entering the
|
||||
;; mini-buffer, and hide it whenever leaving.
|
||||
|
||||
(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."
|
||||
(when (not (memq (device-class last-event-frame
|
||||
last-event-device)
|
||||
'(keyboard core-keyboard)))
|
||||
(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))
|
||||
|
||||
(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
|
||||
(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)
|
||||
|
||||
(provide 'minibuffer)
|
||||
|
||||
;;; minibuffer.el ends here
|
||||
|
@ -38,6 +38,12 @@ touch point, and the initial position of the touchpoint. See
|
||||
`touch-screen-handle-point-update' for the meanings of the fourth
|
||||
element.")
|
||||
|
||||
(defvar touch-screen-set-point-commands '(mouse-set-point)
|
||||
"List of commands known to set the point.
|
||||
This is used to determine whether or not to display the on-screen
|
||||
keyboard after a mouse command is executed in response to a
|
||||
`touchscreen-end' event.")
|
||||
|
||||
(defvar touch-screen-current-timer nil
|
||||
"Timer used to track long-presses.
|
||||
This is always cleared upon any significant state change.")
|
||||
@ -54,20 +60,28 @@ However, return the coordinates relative to WINDOW.
|
||||
|
||||
If (posn-window posn) is the same as window, simply return the
|
||||
coordinates in POSN. Otherwise, convert them to the frame, and
|
||||
then back again."
|
||||
(if (eq (posn-window posn) window)
|
||||
then back again.
|
||||
|
||||
If WINDOW is the symbol `frame', simply convert the coordinates
|
||||
to the frame that they belong in."
|
||||
(if (or (eq (posn-window posn) window)
|
||||
(and (eq window 'frame)
|
||||
(framep (posn-window posn))))
|
||||
(posn-x-y posn)
|
||||
(let ((xy (posn-x-y posn))
|
||||
(edges (window-inside-pixel-edges window)))
|
||||
(edges (and (windowp window)
|
||||
(window-inside-pixel-edges window))))
|
||||
;; Make the X and Y positions frame relative.
|
||||
(when (windowp (posn-window posn))
|
||||
(let ((edges (window-inside-pixel-edges
|
||||
(posn-window posn))))
|
||||
(setq xy (cons (+ (car xy) (car edges))
|
||||
(+ (cdr xy) (cadr edges))))))
|
||||
;; Make the X and Y positions window relative again.
|
||||
(cons (- (car xy) (car edges))
|
||||
(- (cdr xy) (cadr edges))))))
|
||||
(if (eq window 'frame)
|
||||
xy
|
||||
;; Make the X and Y positions window relative again.
|
||||
(cons (- (car xy) (car edges))
|
||||
(- (cdr xy) (cadr edges)))))))
|
||||
|
||||
(defun touch-screen-handle-scroll (dx dy)
|
||||
"Scroll the display assuming that a touch point has moved by DX and DY."
|
||||
@ -252,7 +266,12 @@ If the fourth argument of `touch-screen-current-tool' is nil,
|
||||
move point to the position of POINT, selecting the window under
|
||||
POINT as well, and deactivate the mark; if there is a button or
|
||||
link at POINT, call the command bound to `mouse-2' there.
|
||||
Otherwise, call the command bound to `mouse-1'."
|
||||
Otherwise, call the command bound to `mouse-1'.
|
||||
|
||||
If the command being executed is listed in
|
||||
`touch-screen-set-point-commands' also display the on-screen
|
||||
keyboard if the current buffer and the character at the new point
|
||||
is not read-only."
|
||||
(let ((what (nth 3 touch-screen-current-tool)))
|
||||
(cond ((null what)
|
||||
(when (windowp (posn-window (cdr point)))
|
||||
@ -283,9 +302,17 @@ Otherwise, call the command bound to `mouse-1'."
|
||||
(deactivate-mark)
|
||||
;; This is necessary for following links.
|
||||
(goto-char (posn-point (cdr point)))
|
||||
;; Figure out if the on screen keyboard needs to be
|
||||
;; displayed.
|
||||
(when command
|
||||
(call-interactively command nil
|
||||
(vector event)))))))))
|
||||
(vector event))
|
||||
(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)
|
||||
;; Otherwise, hide the on screen keyboard now.
|
||||
(frame-toggle-on-screen-keyboard (selected-frame) t))))))))))
|
||||
|
||||
(defun touch-screen-handle-touch (event)
|
||||
"Handle a single touch EVENT, and perform associated actions.
|
||||
@ -379,22 +406,34 @@ touch point with the same ID as in EVENT, call UPDATE with the
|
||||
touch point in event and DATA.
|
||||
|
||||
Return nil immediately if any other kind of event is received;
|
||||
otherwise, return t once the `touchscreen-end' event arrives."
|
||||
(catch 'finish
|
||||
(while t
|
||||
(let ((new-event (read-event)))
|
||||
(cond
|
||||
((eq (car-safe new-event) 'touchscreen-update)
|
||||
(let ((tool (assq (caadr event) (nth 1 new-event))))
|
||||
(when (and update tool)
|
||||
(funcall update new-event data))))
|
||||
((eq (car-safe new-event) 'touchscreen-end)
|
||||
(throw 'finish
|
||||
;; Now determine whether or not the `touchscreen-end'
|
||||
;; event has the same ID as EVENT. If it doesn't,
|
||||
;; then this is another touch, so return nil.
|
||||
(eq (caadr event) (caadr new-event))))
|
||||
(t (throw 'finish nil)))))))
|
||||
otherwise, return either t or `no-drag' once the
|
||||
`touchscreen-end' event arrives; return `no-drag' returned if the
|
||||
touch point in EVENT did not move significantly, and t otherwise."
|
||||
(let ((return-value 'no-drag)
|
||||
(start-xy (touch-screen-relative-xy (cdadr event)
|
||||
'frame)))
|
||||
(catch 'finish
|
||||
(while t
|
||||
(let ((new-event (read-event)))
|
||||
(cond
|
||||
((eq (car-safe new-event) 'touchscreen-update)
|
||||
(when-let* ((tool (assq (caadr event) (nth 1 new-event)))
|
||||
(xy (touch-screen-relative-xy (cdr tool) 'frame)))
|
||||
(when (or (> (- (car xy) (car start-xy)) 5)
|
||||
(< (- (car xy) (car start-xy)) -5)
|
||||
(> (- (cdr xy) (cdr start-xy)) 5)
|
||||
(< (- (cdr xy) (cdr start-xy)) -5))
|
||||
(setq return-value t))
|
||||
(when (and update tool)
|
||||
(funcall update new-event data))))
|
||||
((eq (car-safe new-event) 'touchscreen-end)
|
||||
(throw 'finish
|
||||
;; Now determine whether or not the `touchscreen-end'
|
||||
;; event has the same ID as EVENT. If it doesn't,
|
||||
;; then this is another touch, so return nil.
|
||||
(and (eq (caadr event) (caadr new-event))
|
||||
return-value)))
|
||||
(t (throw 'finish nil))))))))
|
||||
|
||||
|
||||
|
||||
@ -402,45 +441,8 @@ otherwise, return t once the `touchscreen-end' event arrives."
|
||||
|
||||
(defun touch-screen-drag-mode-line-1 (event)
|
||||
"Internal helper for `touch-screen-drag-mode-line'.
|
||||
This is called when that function determines it need not execute
|
||||
any keymaps on the mode line at that particular spot."
|
||||
;; Find the window that should be dragged and the starting position.
|
||||
(let* ((window (posn-window (cdadr event)))
|
||||
(relative-xy (touch-screen-relative-xy
|
||||
(cdadr event) window))
|
||||
(last-position (cdr relative-xy)))
|
||||
(when (window-resizable window 0)
|
||||
(touch-screen-track-drag
|
||||
event (lambda (new-event &optional _data)
|
||||
;; Find the position of the touchpoint in NEW-EVENT.
|
||||
(let* ((touchpoint (assq (caadr event) (cadr new-event)))
|
||||
(new-relative-xy
|
||||
(touch-screen-relative-xy (cdr touchpoint)
|
||||
window))
|
||||
(position (cdr new-relative-xy))
|
||||
growth)
|
||||
;; Now set the new height of the window.
|
||||
;; If new-relative-y is above relative-xy, then
|
||||
;; make the window that much shorter. Otherwise,
|
||||
;; make it bigger.
|
||||
(unless (or (zerop (setq growth (- position last-position)))
|
||||
(and (> growth 0)
|
||||
(< position (+ (window-pixel-top window)
|
||||
(window-pixel-height window))))
|
||||
(and (< growth 0)
|
||||
(> position (+ (window-pixel-top window)
|
||||
(window-pixel-height window)))))
|
||||
(adjust-window-trailing-edge window growth nil t))
|
||||
(setq last-position position)))))))
|
||||
|
||||
(defun touch-screen-drag-mode-line (event)
|
||||
"Begin dragging the mode line in response to a touch EVENT.
|
||||
If EVENT lies on top of text with a mouse command bound, run
|
||||
that command instead.
|
||||
|
||||
Change the height of the window based on where the touch point
|
||||
in EVENT moves."
|
||||
(interactive "e")
|
||||
This is called when that function determines that no drag really
|
||||
happened. EVENT is the same as in `touch-screen-drag-mode-line'."
|
||||
;; If there is an object at EVENT, then look either a keymap bound
|
||||
;; to [down-mouse-1] or a command bound to [mouse-1]. Then, if a
|
||||
;; keymap was found, pop it up as a menu. Otherwise, wait for a tap
|
||||
@ -457,21 +459,66 @@ in EVENT moves."
|
||||
(keymap (lookup-key object-keymap [mode-line down-mouse-1]))
|
||||
(command (or (lookup-key object-keymap [mode-line mouse-1])
|
||||
keymap)))
|
||||
(if (or (keymapp keymap) command)
|
||||
(if (keymapp keymap)
|
||||
(when (touch-screen-track-tap event)
|
||||
(when-let* ((command (x-popup-menu event keymap))
|
||||
(tem (lookup-key keymap
|
||||
(if (consp command)
|
||||
(apply #'vector command)
|
||||
(vector command))
|
||||
t)))
|
||||
(call-interactively tem)))
|
||||
(when (and (commandp command)
|
||||
(touch-screen-track-tap event))
|
||||
(call-interactively command nil
|
||||
(vector (list 'mouse-1 (cdadr event))))))
|
||||
(touch-screen-drag-mode-line-1 event))))
|
||||
(when (or (keymapp keymap) command)
|
||||
(if (keymapp keymap)
|
||||
(when-let* ((command (x-popup-menu event keymap))
|
||||
(tem (lookup-key keymap
|
||||
(if (consp command)
|
||||
(apply #'vector command)
|
||||
(vector command))
|
||||
t)))
|
||||
(call-interactively tem))
|
||||
(when (commandp command)
|
||||
(call-interactively command nil
|
||||
(vector (list 'mouse-1 (cdadr event)))))))))
|
||||
|
||||
(defun touch-screen-drag-mode-line (event)
|
||||
"Begin dragging the mode line in response to a touch EVENT.
|
||||
Change the height of the window based on where the touch point in
|
||||
EVENT moves.
|
||||
|
||||
If it does not actually move anywhere and the touch point is
|
||||
removed, and EVENT lies on top of text with a mouse command
|
||||
bound, run that command instead."
|
||||
(interactive "e")
|
||||
;; Find the window that should be dragged and the starting position.
|
||||
(let* ((window (posn-window (cdadr event)))
|
||||
(relative-xy (touch-screen-relative-xy
|
||||
(cdadr event) window))
|
||||
(last-position (cdr relative-xy)))
|
||||
(when (window-resizable window 0)
|
||||
(when (eq
|
||||
(touch-screen-track-drag
|
||||
event (lambda (new-event &optional _data)
|
||||
;; Find the position of the touchpoint in
|
||||
;; NEW-EVENT.
|
||||
(let* ((touchpoint (assq (caadr event)
|
||||
(cadr new-event)))
|
||||
(new-relative-xy
|
||||
(touch-screen-relative-xy (cdr touchpoint)
|
||||
window))
|
||||
(position (cdr new-relative-xy))
|
||||
growth)
|
||||
;; Now set the new height of the window. If
|
||||
;; new-relative-y is above relative-xy, then
|
||||
;; make the window that much shorter.
|
||||
;; Otherwise, make it bigger.
|
||||
(unless (or (zerop (setq growth
|
||||
(- position last-position)))
|
||||
(and (> growth 0)
|
||||
(< position
|
||||
(+ (window-pixel-top window)
|
||||
(window-pixel-height window))))
|
||||
(and (< growth 0)
|
||||
(> position
|
||||
(+ (window-pixel-top window)
|
||||
(window-pixel-height window)))))
|
||||
(adjust-window-trailing-edge window growth nil t))
|
||||
(setq last-position position))))
|
||||
'no-drag)
|
||||
;; Dragging did not actually happen, so try to run any command
|
||||
;; necessary.
|
||||
(touch-screen-drag-mode-line-1 event)))))
|
||||
|
||||
(global-set-key [mode-line touchscreen-begin]
|
||||
#'touch-screen-drag-mode-line)
|
||||
|
147
src/android.c
147
src/android.c
@ -121,6 +121,8 @@ struct android_emacs_window
|
||||
{
|
||||
jclass class;
|
||||
jmethodID swap_buffers;
|
||||
jmethodID toggle_on_screen_keyboard;
|
||||
jmethodID window_updated;
|
||||
};
|
||||
|
||||
/* The asset manager being used. */
|
||||
@ -193,6 +195,10 @@ static struct android_emacs_drawable drawable_class;
|
||||
/* Various methods associated with the EmacsWindow class. */
|
||||
static struct android_emacs_window window_class;
|
||||
|
||||
/* The last event serial used. This is a 32 bit value, but it is
|
||||
stored in unsigned long to be consistent with X. */
|
||||
static unsigned int event_serial;
|
||||
|
||||
|
||||
|
||||
/* Event handling functions. Events are stored on a (circular) queue
|
||||
@ -1435,6 +1441,9 @@ android_init_emacs_window (void)
|
||||
assert (window_class.c_name);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -1497,7 +1506,7 @@ NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
|
||||
emacs_abort ();
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
|
||||
jshort window, jlong time,
|
||||
jint x, jint y, jint width,
|
||||
@ -1506,6 +1515,7 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
|
||||
event.xconfigure.serial = ++event_serial;
|
||||
event.xconfigure.window = window;
|
||||
event.xconfigure.time = time;
|
||||
event.xconfigure.x = x;
|
||||
@ -1514,9 +1524,10 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
|
||||
event.xconfigure.height = height;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
|
||||
jshort window, jlong time,
|
||||
jint state, jint keycode,
|
||||
@ -1525,6 +1536,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xkey.type = ANDROID_KEY_PRESS;
|
||||
event.xkey.serial = ++event_serial;
|
||||
event.xkey.window = window;
|
||||
event.xkey.time = time;
|
||||
event.xkey.state = state;
|
||||
@ -1532,9 +1544,10 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
|
||||
event.xkey.unicode_char = unicode_char;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
|
||||
jshort window, jlong time,
|
||||
jint state, jint keycode,
|
||||
@ -1543,6 +1556,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xkey.type = ANDROID_KEY_RELEASE;
|
||||
event.xkey.serial = ++event_serial;
|
||||
event.xkey.window = window;
|
||||
event.xkey.time = time;
|
||||
event.xkey.state = state;
|
||||
@ -1550,48 +1564,55 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
|
||||
event.xkey.unicode_char = unicode_char;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
|
||||
jshort window, jlong time)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.xkey.type = ANDROID_FOCUS_IN;
|
||||
event.xkey.window = window;
|
||||
event.xkey.time = time;
|
||||
event.xfocus.type = ANDROID_FOCUS_IN;
|
||||
event.xfocus.serial = ++event_serial;
|
||||
event.xfocus.window = window;
|
||||
event.xfocus.time = time;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
|
||||
jshort window, jlong time)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.xkey.type = ANDROID_FOCUS_OUT;
|
||||
event.xkey.window = window;
|
||||
event.xkey.time = time;
|
||||
event.xfocus.type = ANDROID_FOCUS_OUT;
|
||||
event.xfocus.serial = ++event_serial;
|
||||
event.xfocus.window = window;
|
||||
event.xfocus.time = time;
|
||||
|
||||
android_write_event (&event);
|
||||
return ++event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
|
||||
jshort window, jint action)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.xaction.type = ANDROID_WINDOW_ACTION;
|
||||
event.xaction.serial = ++event_serial;
|
||||
event.xaction.window = window;
|
||||
event.xaction.action = action;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time)
|
||||
@ -1599,15 +1620,17 @@ NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xcrossing.type = ANDROID_ENTER_NOTIFY;
|
||||
event.xcrossing.serial = ++event_serial;
|
||||
event.xcrossing.window = window;
|
||||
event.xcrossing.x = x;
|
||||
event.xcrossing.y = y;
|
||||
event.xcrossing.time = time;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time)
|
||||
@ -1615,15 +1638,17 @@ NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xcrossing.type = ANDROID_LEAVE_NOTIFY;
|
||||
event.xcrossing.serial = ++event_serial;
|
||||
event.xcrossing.window = window;
|
||||
event.xcrossing.x = x;
|
||||
event.xcrossing.y = y;
|
||||
event.xcrossing.time = time;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time)
|
||||
@ -1631,15 +1656,17 @@ NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xmotion.type = ANDROID_MOTION_NOTIFY;
|
||||
event.xmotion.serial = ++event_serial;
|
||||
event.xmotion.window = window;
|
||||
event.xmotion.x = x;
|
||||
event.xmotion.y = y;
|
||||
event.xmotion.time = time;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint state,
|
||||
@ -1648,6 +1675,7 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xbutton.type = ANDROID_BUTTON_PRESS;
|
||||
event.xbutton.serial = ++event_serial;
|
||||
event.xbutton.window = window;
|
||||
event.xbutton.x = x;
|
||||
event.xbutton.y = y;
|
||||
@ -1656,9 +1684,10 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
|
||||
event.xbutton.button = button;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint state,
|
||||
@ -1667,6 +1696,7 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.xbutton.type = ANDROID_BUTTON_RELEASE;
|
||||
event.xbutton.serial = ++event_serial;
|
||||
event.xbutton.window = window;
|
||||
event.xbutton.x = x;
|
||||
event.xbutton.y = y;
|
||||
@ -1675,9 +1705,10 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
|
||||
event.xbutton.button = button;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint pointer_id)
|
||||
@ -1685,6 +1716,7 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.touch.type = ANDROID_TOUCH_DOWN;
|
||||
event.touch.serial = ++event_serial;
|
||||
event.touch.window = window;
|
||||
event.touch.x = x;
|
||||
event.touch.y = y;
|
||||
@ -1692,9 +1724,10 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
|
||||
event.touch.pointer_id = pointer_id;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint pointer_id)
|
||||
@ -1702,6 +1735,7 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.touch.type = ANDROID_TOUCH_UP;
|
||||
event.touch.serial = ++event_serial;
|
||||
event.touch.window = window;
|
||||
event.touch.x = x;
|
||||
event.touch.y = y;
|
||||
@ -1709,9 +1743,10 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
|
||||
event.touch.pointer_id = pointer_id;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint pointer_id)
|
||||
@ -1719,6 +1754,7 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.touch.type = ANDROID_TOUCH_MOVE;
|
||||
event.touch.serial = ++event_serial;
|
||||
event.touch.window = window;
|
||||
event.touch.x = x;
|
||||
event.touch.y = y;
|
||||
@ -1726,9 +1762,10 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
|
||||
event.touch.pointer_id = pointer_id;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
|
||||
jshort window, jint x, jint y,
|
||||
jlong time, jint state,
|
||||
@ -1737,6 +1774,7 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
|
||||
union android_event event;
|
||||
|
||||
event.wheel.type = ANDROID_WHEEL;
|
||||
event.wheel.serial = ++event_serial;
|
||||
event.wheel.window = window;
|
||||
event.wheel.x = x;
|
||||
event.wheel.y = y;
|
||||
@ -1746,43 +1784,50 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
|
||||
event.wheel.y_delta = y_delta;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
|
||||
jshort window)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.iconified.type = ANDROID_ICONIFIED;
|
||||
event.iconified.serial = ++event_serial;
|
||||
event.iconified.window = window;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
|
||||
jshort window)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.iconified.type = ANDROID_DEICONIFIED;
|
||||
event.iconified.serial = ++event_serial;
|
||||
event.iconified.window = window;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
extern JNIEXPORT void JNICALL
|
||||
extern JNIEXPORT jlong JNICALL
|
||||
NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object,
|
||||
jshort window, jint menu_event_id)
|
||||
{
|
||||
union android_event event;
|
||||
|
||||
event.menu.type = ANDROID_CONTEXT_MENU;
|
||||
event.menu.serial = ++event_serial;
|
||||
event.menu.window = window;
|
||||
event.menu.menu_event_id = menu_event_id;
|
||||
|
||||
android_write_event (&event);
|
||||
return event_serial;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
@ -3599,6 +3644,15 @@ android_translate_coordinates (android_window src, int x,
|
||||
ANDROID_DELETE_LOCAL_REF (coordinates);
|
||||
}
|
||||
|
||||
void
|
||||
android_sync (void)
|
||||
{
|
||||
(*android_java_env)->CallVoidMethod (android_java_env,
|
||||
emacs_service,
|
||||
service_class.sync);
|
||||
android_exception_check ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Low level drawing primitives. */
|
||||
@ -3795,12 +3849,45 @@ android_get_keysym_name (int keysym, char *name_return, size_t size)
|
||||
ANDROID_DELETE_LOCAL_REF (string);
|
||||
}
|
||||
|
||||
/* Display the on screen keyboard on window WINDOW, or hide it if SHOW
|
||||
is false. Ask the system to bring up or hide the on-screen
|
||||
keyboard on behalf of WINDOW. The request may be rejected by the
|
||||
system, especially when the window does not have the input
|
||||
focus. */
|
||||
|
||||
void
|
||||
android_sync (void)
|
||||
android_toggle_on_screen_keyboard (android_window window, bool show)
|
||||
{
|
||||
(*android_java_env)->CallVoidMethod (android_java_env,
|
||||
emacs_service,
|
||||
service_class.sync);
|
||||
jobject object;
|
||||
jmethodID method;
|
||||
|
||||
object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
|
||||
method = window_class.toggle_on_screen_keyboard;
|
||||
|
||||
/* Now display the on screen keyboard. */
|
||||
(*android_java_env)->CallVoidMethod (android_java_env, object,
|
||||
method, (jboolean) show);
|
||||
|
||||
/* Check for out of memory errors. */
|
||||
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 ();
|
||||
}
|
||||
|
||||
@ -3811,7 +3898,7 @@ android_sync (void)
|
||||
#undef faccessat
|
||||
|
||||
/* Replace the system faccessat with one which understands AT_EACCESS.
|
||||
Android's faccessat simply fails upon using AT_EACCESS, so repalce
|
||||
Android's faccessat simply fails upon using AT_EACCESS, so replace
|
||||
it with zero here. This isn't caught during configuration.
|
||||
|
||||
This replacement is only done when building for Android 17 or
|
||||
@ -3829,7 +3916,7 @@ faccessat (int dirfd, const char *pathname, int mode, int flags)
|
||||
return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
|
||||
}
|
||||
|
||||
#endif /* __ANDROID_API__ < 16 */
|
||||
#endif /* __ANDROID_API__ >= 17 */
|
||||
|
||||
|
||||
|
||||
|
@ -91,6 +91,8 @@ 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);
|
||||
|
||||
|
||||
|
||||
|
@ -2332,6 +2332,30 @@ there is no mouse. */)
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFUN ("android-toggle-on-screen-keyboard",
|
||||
Fandroid_toggle_on_screen_keyboard,
|
||||
Sandroid_toggle_on_screen_keyboard, 2, 2, 0,
|
||||
doc: /* Display or hide the on-screen keyboard.
|
||||
If HIDE is non-nil, hide the on screen keyboard if it is currently
|
||||
being displayed. Else, request that the system display it on behalf
|
||||
of FRAME. This request may be rejected if FRAME does not have the
|
||||
input focus. */)
|
||||
(Lisp_Object frame, Lisp_Object hide)
|
||||
{
|
||||
#ifndef ANDROID_STUBIFY
|
||||
struct frame *f;
|
||||
|
||||
f = decode_window_system_frame (frame);
|
||||
|
||||
block_input ();
|
||||
android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f),
|
||||
NILP (hide));
|
||||
unblock_input ();
|
||||
#endif
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef ANDROID_STUBIFY
|
||||
@ -2781,6 +2805,7 @@ syms_of_androidfns (void)
|
||||
defsubr (&Sx_show_tip);
|
||||
defsubr (&Sx_hide_tip);
|
||||
defsubr (&Sandroid_detect_mouse);
|
||||
defsubr (&Sandroid_toggle_on_screen_keyboard);
|
||||
|
||||
#ifndef ANDROID_STUBIFY
|
||||
tip_timer = Qnil;
|
||||
|
@ -239,6 +239,7 @@ enum android_event_type
|
||||
struct android_any_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
};
|
||||
|
||||
@ -252,6 +253,7 @@ enum android_modifier_mask
|
||||
struct android_key_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
android_time time;
|
||||
unsigned int state;
|
||||
@ -271,6 +273,7 @@ struct android_key_event
|
||||
struct android_configure_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
android_time time;
|
||||
int x, y;
|
||||
@ -280,6 +283,7 @@ struct android_configure_event
|
||||
struct android_focus_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
android_time time;
|
||||
};
|
||||
@ -287,6 +291,7 @@ struct android_focus_event
|
||||
struct android_window_action_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
|
||||
/* The window handle. This can be ANDROID_NONE. */
|
||||
android_window window;
|
||||
@ -301,6 +306,7 @@ struct android_window_action_event
|
||||
struct android_crossing_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
int x, y;
|
||||
unsigned long time;
|
||||
@ -309,6 +315,7 @@ struct android_crossing_event
|
||||
struct android_motion_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
int x, y;
|
||||
unsigned long time;
|
||||
@ -317,6 +324,7 @@ struct android_motion_event
|
||||
struct android_button_event
|
||||
{
|
||||
enum android_event_type type;
|
||||
unsigned long serial;
|
||||
android_window window;
|
||||
int x, y;
|
||||
unsigned long time;
|
||||
@ -329,6 +337,9 @@ struct android_touch_event
|
||||
/* Type of the event. */
|
||||
enum android_event_type type;
|
||||
|
||||
/* Serial identifying the event. */
|
||||
unsigned long serial;
|
||||
|
||||
/* Window associated with the event. */
|
||||
android_window window;
|
||||
|
||||
@ -347,6 +358,9 @@ struct android_wheel_event
|
||||
/* Type of the event. */
|
||||
enum android_event_type type;
|
||||
|
||||
/* Serial identifying the event. */
|
||||
unsigned long serial;
|
||||
|
||||
/* Window associated with the event. */
|
||||
android_window window;
|
||||
|
||||
@ -368,6 +382,9 @@ struct android_iconify_event
|
||||
/* Type of the event. */
|
||||
enum android_event_type type;
|
||||
|
||||
/* Serial identifying the event. */
|
||||
unsigned long serial;
|
||||
|
||||
/* Window associated with the event. */
|
||||
android_window window;
|
||||
};
|
||||
@ -377,6 +394,9 @@ struct android_menu_event
|
||||
/* Type of the event. */
|
||||
enum android_event_type type;
|
||||
|
||||
/* Serial identifying the event. */
|
||||
unsigned long serial;
|
||||
|
||||
/* Window associated with the event. Always None. */
|
||||
android_window window;
|
||||
|
||||
|
@ -591,7 +591,17 @@ 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;
|
||||
|
||||
@ -1194,6 +1204,9 @@ handle_one_android_event (struct android_display_info *dpyinfo,
|
||||
/* Iconification. This is vastly simpler than on X. */
|
||||
case ANDROID_ICONIFIED:
|
||||
|
||||
if (!any)
|
||||
goto OTHER;
|
||||
|
||||
if (FRAME_ICONIFIED_P (any))
|
||||
goto OTHER;
|
||||
|
||||
@ -1206,6 +1219,9 @@ handle_one_android_event (struct android_display_info *dpyinfo,
|
||||
|
||||
case ANDROID_DEICONIFIED:
|
||||
|
||||
if (!any)
|
||||
goto OTHER;
|
||||
|
||||
if (!FRAME_ICONIFIED_P (any))
|
||||
goto OTHER;
|
||||
|
||||
@ -1311,6 +1327,14 @@ 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 ();
|
||||
|
@ -241,6 +241,11 @@ 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
|
||||
|
Loading…
Reference in New Issue
Block a user