1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-27 07:37:33 +00:00

Update Android port

* configure.ac (ANDROID_MIN_SDK): New variable.
(DX): Remove and replace with D8.
(XCONFIGURE): Check for the minimum version of Android the cross
compiler compiles for.  Generate java/AndroidManifest.xml from
java/AndroidManifest.xml.in.  Allow using Zlib on Android.

* java/AndroidManifest.xml.in: New file.  Use the minimum SDK
detected by configure.

* java/Makefile.in (top_srcdir, version): New variables.
(DX, D8): Replace with D8.
(ANDROID_MIN_SDK, APK_NAME): New variables.
(.PHONY):
(.PRECIOUS):
(classes.dex):
(emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'.

* java/debug.sh: New option --attach-existing.  Attach to an
existing Emacs instance when specified.

* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
field `isPaused'.
(invalidateFocus1): Fix infinite recursion.
(detachWindow): Deiconify window.
(attachWindow): Iconify the window if the activity is paused.
(onCreate): Use the ``no title bar'' theme.
(onPause, onResume): New functions.
* java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown)
(sendTouchMove, sendWheel, sendIconified, sendDeiconified): New
functions.
* java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface):
(list): Remove logging for code that is mostly going to be unused.
* java/org/gnu/emacs/EmacsService.java (ringBell, queryTree)
(getScreenWidth, getScreenHeight, detectMouse): New functions.
* java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
(surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra
debug logging.  Avoid deadlock in surfaceCreated.

* java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard
to make the SurfaceView respect Z order.  It didn't work.
(handleDirtyBitmap): Copy over the contents from the old bitmap.
(explicitlyDirtyBitmap): New function.
(onLayout): Don't dirty bitmap if unnecessary.
(damageRect, swapBuffers): Don't synchronize so hard.
(onTouchEvent): Call window.onTouchEvent instead.
(moveChildToBack, raise, lower): New functions.

* java/org/gnu/emacs/EmacsWindow.java (Coordinate): New
subclass.
(pointerMap, isMapped, isIconified, dontFocusOnMap)
(dontAcceptFocus): New fields.
(EmacsWindow): Don't immediately register unmapped window.
(viewLayout): Send configure event outside the lock.
(requestViewLayout): Explicitly dirty the bitmap.
(mapWindow): Register the window now.  Respect dontFocusOnMap.
(unmapWindow): Unregister the window now.
(figureChange, onTouchEvent): New functions.
(onSomeKindOfMotionEvent): Handle scroll wheel events.
(reparentTo, makeInputFocus, raise, lower, getWindowGeometry)
(noticeIconified, noticeDeiconified, setDontAcceptFocus)
(setDontFocusOnMap, getDontFocusOnMap): New functions.
* java/org/gnu/emacs/EmacsWindowAttachmentManager.java
(registerWindow, detachWindow): Synchronize.
(noticeIconified, noticeDeiconified): New functions.
(copyWindows): New function.

* lisp/frame.el (frame-geometry, frame-edges)
(mouse-absolute-pixel-position, set-mouse-absolute-pixel-position)
(frame-list-z-order, frame-restack, display-mouse-p)
(display-monitor-attributes-list): Implement on Android.

* lisp/mwheel.el (mouse-wheel-down-event):
(mouse-wheel-up-event):
(mouse-wheel-left-event):
(mouse-wheel-right-event): Define on Android.

* src/android.c (struct android_emacs_service): New methods
`ringBell', `queryTree', `getScreenWidth', `getScreenHeight',
and `detectMouse'.
(struct android_event_queue, android_init_events)
(android_next_event, android_write_event): Remove write limit.
(android_file_access_p): Handle directories correcty.
(android_close): Fix coding style.
(android_fclose): New function.
(android_init_emacs_service): Initialize new methods.
(android_reparent_window): Implement function.
(android_bell, android_set_input_focus, android_raise_window)
(android_lower_window, android_query_tree, android_get_geometry)
(android_get_screen_width, android_get_screen_height)
(android_get_mm_width, android_get_mm_height, android_detect_mouse)
(android_set_dont_focus_on_map, android_set_dont_accept_focus):
New functions.
(struct android_dir): New structure.
(android_opendir, android_readdir, android_closedir): New
functions.
(emacs_abort): Implement here on Android and poke debuggerd into
generating a tombstone.

* src/android.h: Update prototypes.

* src/androidfns.c (android_set_parent_frame): New function.
(android_default_font_parameter): Use sane font size by default.
(Fx_display_pixel_width, Fx_display_pixel_height)
(Fx_display_mm_width, Fx_display_mm_height)
(Fx_display_monitor_attributes_list): Rename to start with
`android-'.  Implement.  Fiddle with documentation to introduce
Android specific nuances.
(Fandroid_display_monitor_attributes_list): New function.
(Fx_frame_geometry, frame_geometry): New function.
(Fandroid_frame_geometry): Implement correctly.
(Fx_frame_list_z_order): Rename to start with `android-'.
(android_frame_list_z_order, Fandroid_frame_list_z_order):
Implement.
(Fx_frame_restack): Rename to start with `android-'.
(Fandroid_frame_restack): ``Implement''.
(Fx_mouse_absolute_pixel_position): Rename to start with
`android-'.
(Fandroid_mouse_absolute_pixel_position): ``Implement''.
(Fx_set_mouse_absolute_pixel_position): Rename to start with
`android-'.
(Fandroid_set_mouse_absolute_pixel_position): ``Implement''.
(Fandroid_detect_mouse): New function.
(android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when
clearing area.
(android_set_no_focus_on_map, android_set_no_accept_focus): New
functions.
(android_frame_parm_handlers): Register new frame parameter
handlers.
(syms_of_androidfns): Update appropriately.

* src/androidfont.c (androidfont_draw): Use
FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.

* src/androidgui.h (enum android_event_type): New events.
(struct android_touch_event, struct android_wheel_event)
(struct android_iconify_event): New structures.
(union android_event): Add new events.

* src/androidterm.c (android_clear_frame): Use
FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
(android_flash, android_ring_bell): Implement bell ringing.
(android_toggle_invisible_pointer): Don't TODO function that
can't be implemented.
(show_back_buffer, android_flush_dirty_back_buffer_on): Check if
a buffer flip is required before doing the flip.
(android_lower_frame, android_raise_frame): Implement functions.
(android_update_tools, android_find_tool): New functions.
(handle_one_android_event): Handle new iconification, wheel and
touch events.
(android_read_socket): Implement pending-autoraise-frames.
(android_frame_up_to_date): Implement bell ringing.
(android_buffer_flipping_unblocked_hook): Check if a buffer flip
is required before doing the flip.
(android_focus_frame, android_frame_highlight)
(android_frame_unhighlight): New function.
(android_frame_rehighlight): Implement functions.
(android_iconify_frame): Always display error.
(android_set_alpha): Update commentary.
(android_free_frame_resources): Free frame touch points.
(android_scroll_run, android_flip_and_flush)
(android_clear_rectangle, android_draw_fringe_bitmap)
(android_draw_glyph_string_background, android_fill_triangle)
(android_clear_point, android_draw_relief_rect)
(android_draw_box_rect, android_draw_glyph_string_bg_rect)
(android_draw_image_foreground, android_draw_stretch_glyph_string)
(android_draw_underwave, android_draw_glyph_string_foreground)
(android_draw_composite_glyph_string_foreground)
(android_draw_glyphless_glyph_string_foreground)
(android_draw_glyph_string, android_clear_frame_area)
(android_clear_under_internal_border, android_draw_hollow_cursor)
(android_draw_bar_cursor, android_draw_vertical_window_border)
(android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE
instead of FRAME_ANDROID_WINDOW for drawing operations.

* src/androidterm.h (struct android_touch_point): New structure.
(struct android_output): New fields.
(FRAME_ANDROID_NEED_BUFFER_FLIP): New macro.

* src/dired.c (emacs_readdir, open_directory)
(directory_files_internal_unwind, read_dirent)
(directory_files_internal, file_name_completion): Add
indirection over readdir and opendir.  Use android variants on
Android.

* src/dispnew.c (Fopen_termscript):
* src/fileio.c (fclose_unwind): Use emacs_fclose.
(Faccess_file): Call android_file_access_p.
(file_accessible_directory_p): Append right suffix to Android
assets directory.
(do_auto_save_unwind): Use emacs_fclose.
* src/keyboard.c (lispy_function_keys): Use right function key
for page up and page down.
(Fopen_dribble_file): Use emacs_fclose.

* src/lisp.h: New prototype emacs_fclose.

* src/lread.c (close_infile_unwind): Use emacs_fclose.

* src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation.
(sfnt_prepare_raster): Compute raster width and height
consistently with outline building.
(sfnt_build_outline_edges): Use the same offsets used to set
offy and offx.
(main): Adjust debug code.

* src/sfntfont-android.c (sfntfont_android_saturate32): Delete
function.
(sfntfont_android_blend, sfntfont_android_blendrgb): Remove
unnecessary debug code.
(sfntfont_android_composite_bitmap): Prevent out of bounds
write.
(sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE.
(init_sfntfont_android): Initialize Monospace Serif font to
something sensible.
* src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics
before summing up pcm.
(sfntfont_draw): Use s->font instead of s->face->font.

* src/sysdep.c (emacs_fclose): Wrap around android_fclose on
android.

* src/term.c (Fsuspend_tty):
(delete_tty): Use emacs_fclose.
* src/verbose.mk.in (AM_V_DX): Replace with D8 version.
This commit is contained in:
Po Lu 2023-01-13 15:53:08 +08:00
parent 2fa5583d96
commit f9732131cf
33 changed files with 2815 additions and 351 deletions

View File

@ -816,8 +816,8 @@ a valid path to android.jar. See config.log for more details.])
Please verify that the path to the SDK build tools you specified is correct])
fi
AC_PATH_PROGS([DX], [dx d8], [], "${SDK_BUILD_TOOLS}:$PATH")
if test "DX" = ""; then
AC_PATH_PROGS([D8], [d8], [], "${SDK_BUILD_TOOLS}:$PATH")
if test "D8" = ""; then
AC_MSG_ERROR([The Android dexer was not found.
Please verify that the path to the SDK build tools you specified is correct])
fi
@ -867,6 +867,25 @@ in the ANDROID_CC variable when you ran configure.])
ANDROID_ABI=$android_abi
dnl Obtain the minimum SDK version of the resulting Emacs binary
dnl built with this NDK.
ANDROID_MIN_SDK=8
AC_MSG_CHECKING([for the lowest Android version Emacs can run on])
[android_sdk=`echo "$cc_target" | grep -oE 'android([0-9][0-9]?)'`]
if test -n "$android_sdk"; then
android_sdk=`echo "$android_sdk" | sed -n 's/android//p'`
AC_MSG_RESULT([$android_sdk])
ANDROID_MIN_SDK=$android_sdk
else
AC_MSG_RESULT([unknown ($cc_target); assuming 8])
AC_MSG_WARN([configure could not determine the versions of Android \
a binary built with this compiler will run on. The generated application \
package will likely install on older systems but crash on startup.])
fi
AC_SUBST([ANDROID_MIN_SDK])
# Save confdefs.h and config.log for now.
mv -f confdefs.h _confdefs.h
mv -f config.log _config.log
@ -899,7 +918,7 @@ fi
AC_SUBST([ANDROID])
AC_SUBST([JAVAC])
AC_SUBST([AAPT])
AC_SUBST([DX])
AC_SUBST([D8])
AC_SUBST([ZIPALIGN])
AC_SUBST([ANDROID_JAR])
AC_SUBST([ANDROID_ABI])
@ -914,7 +933,7 @@ fi
AC_SUBST([XCONFIGURE])
if test "$ANDROID" = "yes"; then
# When --with-android is specified, all build options must be
# When --with-android is specified, almost all build options must be
# disabled, both within the recursive invocation of configure and
# outside.
with_xpm=no
@ -938,11 +957,12 @@ if test "$ANDROID" = "yes"; then
with_gpm=no
with_dbus=no
with_gsettings=no
with_selinx=no
with_selinux=no
with_gnutls=no
with_zlib=no
with_modules=no
with_threads=no
# zlib is available in android.
fi
dnl This used to use changequote, but, apart from 'changequote is evil'
@ -7211,6 +7231,9 @@ fi
ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile])
# Make java/AndroidManifest.xml
ARCH_INDEPENDENT_CONFIG_FILES([java/AndroidManifest.xml])
AC_OUTPUT
if test ! "$with_mailutils"; then

View File

@ -0,0 +1,84 @@
<!-- @configure_input@
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/>. -->
<!-- targetSandboxVersion must be 1. Otherwise, fascist security
restrictions prevent Emacs from making HTTP connections. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.gnu.emacs"
android:targetSandboxVersion="1"
android:installLocation="auto"
android:versionName="@version@">
<!-- Paste in every permission in existence so Emacs can do
anything. -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.TRANSMIT_IR" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
android:targetSdkVersion="28"/>
<application android:name="org.gnu.emacs.EmacsApplication"
android:label="Emacs"
android:hardwareAccelerated="true"
android:supportsRtl="true"
android:theme="@android:style/Theme"
android:debuggable="true"
android:extractNativeLibs="true">
<activity android:name="org.gnu.emacs.EmacsActivity"
android:launchMode="singleTop"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
<service android:name="org.gnu.emacs.EmacsService"
android:directBootAware="false"
android:enabled="true"
android:exported="false"
android:label="GNU Emacs service"/>
</application>
</manifest>

View File

@ -18,13 +18,15 @@
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
version = @version@
-include ${top_builddir}/src/verbose.mk
SHELL = @SHELL@
JAVAC = @JAVAC@
AAPT = @AAPT@
DX = @DX@
D8 = @D8@
ZIPALIGN = @ZIPALIGN@
JARSIGNER = @JARSIGNER@
ANDROID_JAR = @ANDROID_JAR@
@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
JAVA_FILES = $(shell find . -type f -name *.java)
CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
# Compute the name for the Emacs application package. This should be:
# emacs-<version>-<min-sdk>-<abi>.apk
ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
APK_NAME = emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
# How this stuff works.
# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
@ -55,7 +63,7 @@ CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
# assets/lisp/
.PHONY: emacs.apk-in all
all: emacs.apk
all: $(APK_NAME)
# Binaries to cross-compile.
CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
@ -118,6 +126,18 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
rm -rf install_temp
# Makefile itself.
.PRECIOUS: ../config.status Makefile
../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
$(MAKE) -C $(dir $@) $(notdir $@)
Makefile: ../config.status $(top_builddir)/java/Makefile.in
$(MAKE) -C .. java/$@
# AndroidManifest.xml:
AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
AndroidManifest.xml.in
pushd ..; ./config.status java/AndroidManifest.xml; popd
.SUFFIXES: .java .class
.java.class &:
$(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
@ -126,7 +146,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
# nested classes.
classes.dex: $(CLASS_FILES)
$(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \
$(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
$(subst $$,\$$,$(shell find . -type f -name *.class))
# When emacs.keystore expires, regenerate it with:
@ -136,7 +156,7 @@ classes.dex: $(CLASS_FILES)
.PHONY: clean maintainer-clean
emacs.apk: classes.dex emacs.apk-in emacs.keystore
$(APK_NAME): classes.dex emacs.apk-in emacs.keystore
cp -f emacs.apk-in $@.unaligned
$(AAPT) add $@.unaligned classes.dex
$(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
@ -144,7 +164,7 @@ emacs.apk: classes.dex emacs.apk-in emacs.keystore
rm -f $@.unaligned
clean:
rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class
rm -f *.apk emacs.apk-in *.dex *.unaligned *.class
rm -rf install-temp
find . -name '*.class' -delete

View File

@ -30,6 +30,7 @@ activity=org.gnu.emacs.EmacsActivity
gdb_port=5039
jdb_port=64013
jdb=no
attach_existing=no
while [ $# -gt 0 ]; do
case "$1" in
@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do
echo " --port PORT run the GDB server on a specific port"
echo " --jdb-port PORT run the JDB server on a specific port"
echo " --jdb run JDB instead of GDB"
echo " --attach-existing attach to an existing process"
echo " --help print this message"
echo ""
echo "Available devices:"
@ -63,6 +65,9 @@ while [ $# -gt 0 ]; do
"--port" )
gdb_port=$1
;;
"--attach-existing" )
attach_existing=yes
;;
"--" )
shift
gdbargs=$@
@ -120,30 +125,32 @@ package_pids=`awk -- '{
print $1
}' <<< $package_pids`
# Finally, kill each existing process.
for pid in $package_pids; do
echo "Killing existing process $pid..."
adb -s $device shell run-as $package kill -9 $pid &> /dev/null
done
if [ "$attach_existing" != "yes" ]; then
# Finally, kill each existing process.
for pid in $package_pids; do
echo "Killing existing process $pid..."
adb -s $device shell run-as $package kill -9 $pid &> /dev/null
done
# Now run the main activity. This must be done as the adb user and
# not as the package user.
echo "Starting activity $activity and attaching debugger"
# Now run the main activity. This must be done as the adb user and
# not as the package user.
echo "Starting activity $activity and attaching debugger"
# Exit if the activity could not be started.
adb -s $device shell am start -D "$package/$activity"
if [ ! $? ]; then
exit 1;
fi
# Exit if the activity could not be started.
adb -s $device shell am start -D "$package/$activity"
if [ ! $? ]; then
exit 1;
fi
# Now look for processes matching the package again.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Now look for processes matching the package again.
package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD`
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
# Next, remove lines matching "ps" itself.
package_pids=`awk -- '{
if (!match ($0, /(PID|ps)/))
print $1
}' <<< $package_pids`
fi
pid=$package_pids
num_pids=`wc -w <<< "$package_pids"`

View File

@ -48,6 +48,9 @@ public class EmacsActivity extends Activity
/* The currently focused window. */
public static EmacsWindow focusedWindow;
/* Whether or not this activity is paused. */
private boolean isPaused;
static
{
focusedActivities = new ArrayList<EmacsActivity> ();
@ -60,7 +63,7 @@ public class EmacsActivity extends Activity
focusedWindow = window;
for (EmacsWindow child : window.children)
invalidateFocus1 (window);
invalidateFocus1 (child);
}
public static void
@ -103,6 +106,9 @@ public class EmacsActivity extends Activity
/* Clear the window's pointer to this activity and remove the
window's view. */
window.setConsumer (null);
/* The window can't be iconified any longer. */
window.noticeDeiconified ();
layout.removeView (window.view);
window = null;
@ -114,6 +120,8 @@ public class EmacsActivity extends Activity
public void
attachWindow (EmacsWindow child)
{
Log.d (TAG, "attachWindow: " + child);
if (window != null)
throw new IllegalStateException ("trying to attach window when one"
+ " already exists");
@ -124,6 +132,10 @@ public class EmacsActivity extends Activity
layout.addView (window.view);
child.setConsumer (this);
/* If the activity is iconified, send that to the window. */
if (isPaused)
window.noticeIconified ();
/* Invalidate the focus. */
invalidateFocus ();
}
@ -148,6 +160,9 @@ public class EmacsActivity extends Activity
{
FrameLayout.LayoutParams params;
/* Set the theme to one without a title bar. */
setTheme (android.R.style.Theme_NoTitleBar);
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
@ -192,4 +207,24 @@ public class EmacsActivity extends Activity
invalidateFocus ();
}
@Override
public void
onPause ()
{
isPaused = true;
EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
super.onResume ();
}
@Override
public void
onResume ()
{
isPaused = false;
EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
super.onResume ();
}
};

View File

@ -96,11 +96,34 @@ public static native void sendButtonPress (short window, int x, int y,
long time, int state,
int button);
/* Send an ANDROID_BUTTON_RELEASE event. */
/* Send an ANDROID_BUTTON_RELEASE event. */
public static native void 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,
long time, int pointerID);
/* Send an ANDROID_TOUCH_UP event. */
public static native void 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,
long time, int pointerID);
/* Send an ANDROID_WHEEL event. */
public static native void 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);
/* Send an ANDROID_DEICONIFIED event. */
public static native void sendDeiconified (short window);
static
{
System.loadLibrary ("emacs");

View File

@ -149,8 +149,6 @@ else if (style.contains ("Expanded"))
}
else
familyName = fileName;
Log.d (TAG, "Initialized new typeface " + familyName);
}
@Override
@ -321,17 +319,12 @@ protected class Sdk7FontObject extends FontObject
list = new LinkedList<FontEntity> ();
Log.d (TAG, ("Looking for fonts matching font spec: "
+ fontSpec.toString ()));
for (i = 0; i < typefaceList.length; ++i)
{
if (checkMatch (typefaceList[i], fontSpec))
list.add (new Sdk7FontEntity (typefaceList[i]));
}
Log.d (TAG, "Found font entities: " + list.toString ());
return (FontEntity[]) list.toArray (new FontEntity[0]);
}

View File

@ -28,20 +28,27 @@
import android.graphics.Point;
import android.view.View;
import android.view.InputDevice;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Looper;
import android.os.IBinder;
import android.os.Handler;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.os.VibrationEffect;
import android.util.Log;
import android.util.DisplayMetrics;
import android.hardware.input.InputManager;
class Holder<T>
{
T thing;
@ -250,4 +257,113 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT)
{
window.clearArea (x, y, width, height);
}
@SuppressWarnings ("deprecation")
public void
ringBell ()
{
Vibrator vibrator;
VibrationEffect effect;
VibratorManager vibratorManager;
Object tem;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
vibratorManager = (VibratorManager) tem;
vibrator = vibratorManager.getDefaultVibrator ();
}
else
vibrator
= (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
effect
= VibrationEffect.createOneShot (50,
VibrationEffect.DEFAULT_AMPLITUDE);
vibrator.vibrate (effect);
}
else
vibrator.vibrate (50);
}
public short[]
queryTree (EmacsWindow window)
{
short[] array;
List<EmacsWindow> windowList;
int i;
if (window == null)
/* Just return all the windows without a parent. */
windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
else
windowList = window.children;
array = new short[windowList.size () + 1];
i = 1;
array[0] = window.parent != null ? 0 : window.parent.handle;
for (EmacsWindow treeWindow : windowList)
array[i++] = treeWindow.handle;
return array;
}
public int
getScreenWidth (boolean mmWise)
{
DisplayMetrics metrics;
metrics = getResources ().getDisplayMetrics ();
if (!mmWise)
return metrics.widthPixels;
else
return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
}
public int
getScreenHeight (boolean mmWise)
{
DisplayMetrics metrics;
metrics = getResources ().getDisplayMetrics ();
if (!mmWise)
return metrics.heightPixels;
else
return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
}
public boolean
detectMouse ()
{
InputManager manager;
InputDevice device;
int[] ids;
int i;
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.JELLY_BEAN)
return false;
manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
ids = manager.getInputDeviceIds ();
for (i = 0; i < ids.length; ++i)
{
device = manager.getInputDevice (ids[i]);
if (device == null)
continue;
if (device.supportsSource (InputDevice.SOURCE_MOUSE))
return true;
}
return false;
}
};

View File

@ -27,8 +27,12 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;
public class EmacsSurfaceView extends SurfaceView
{
private static final String TAG = "EmacsSurfaceView";
public Object surfaceChangeLock;
private boolean created;
@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
Log.d (TAG, "surfaceChanged: " + view);
view.swapBuffers ();
}
@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView
{
synchronized (surfaceChangeLock)
{
Log.d (TAG, "surfaceCreated: " + view);
created = true;
view.swapBuffers ();
}
/* Drop the lock when doing this, or a deadlock can
result. */
view.swapBuffers ();
}
@Override
@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView
{
synchronized (surfaceChangeLock)
{
Log.d (TAG, "surfaceDestroyed: " + view);
created = false;
}
}
@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView
return holder.lockCanvas (damage);
}
@Override
protected void
onLayout (boolean changed, int left, int top, int right,
int bottom)
{
Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
+ " " + bottom + " -- " + changed + " visibility "
+ getVisibility ()));
}
/* This method is only used during debugging when it seems damage
isn't working correctly. */

View File

@ -87,12 +87,27 @@ public class EmacsView extends ViewGroup
/* Create the surface view. */
this.surfaceView = new EmacsSurfaceView (this);
this.surfaceView.setZOrderMediaOverlay (true);
addView (this.surfaceView);
/* Not sure exactly what this does but it makes things magically
work. Why is something as simple as XRaiseWindow so involved
on Android? */
setChildrenDrawingOrderEnabled (true);
/* Get rid of the foreground and background tint. */
setBackgroundTintList (null);
setForegroundTintList (null);
}
private void
handleDirtyBitmap ()
{
Bitmap oldBitmap;
/* Save the old bitmap. */
oldBitmap = bitmap;
/* Recreate the front and back buffer bitmaps. */
bitmap
= Bitmap.createBitmap (bitmapDirty.width (),
@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup
/* And canvases. */
canvas = new Canvas (bitmap);
/* If Emacs is drawing to the bitmap right now from the
main thread, the image contents are lost until the next
ConfigureNotify and complete garbage. Sorry! */
/* Copy over the contents of the old bitmap. */
if (oldBitmap != null)
canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
bitmapDirty = null;
}
public synchronized void
explicitlyDirtyBitmap (Rect rect)
{
if (bitmapDirty == null
&& (bitmap == null
|| rect.width () != bitmap.getWidth ()
|| rect.height () != bitmap.getHeight ()))
bitmapDirty = rect;
}
public synchronized Bitmap
getBitmap ()
{
@ -168,25 +194,31 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
View child;
Rect windowRect;
count = getChildCount ();
if (changed || mustReportLayout)
{
mustReportLayout = false;
window.viewLayout (left, top, right, bottom);
}
if (changed)
if (changed
/* Check that a change has really happened. */
&& (bitmapDirty == null
|| bitmapDirty.width () != right - left
|| bitmapDirty.height () != bottom - top))
bitmapDirty = new Rect (left, top, right, bottom);
count = getChildCount ();
for (i = 0; i < count; ++i)
{
child = getChildAt (i);
Log.d (TAG, "onLayout: " + child);
if (child == surfaceView)
/* The child is the surface view, so give it the entire
view. */
child.layout (left, top, right, bottom);
child.layout (0, 0, right - left, bottom - top);
else if (child.getVisibility () != GONE)
{
if (!(child instanceof EmacsView))
@ -201,59 +233,68 @@ else if (child.getVisibility () != GONE)
}
}
public synchronized void
public void
damageRect (Rect damageRect)
{
damageRegion.union (damageRect);
synchronized (damageRegion)
{
damageRegion.union (damageRect);
}
}
/* This method is called from both the UI thread and the Emacs
thread. */
public synchronized void
public void
swapBuffers (boolean force)
{
Canvas canvas;
Rect damageRect;
Bitmap bitmap;
if (damageRegion.isEmpty ())
return;
/* Code must always take damageRegion, and then surfaceChangeLock,
never the other way around! */
bitmap = getBitmap ();
/* Emacs must take the following lock to ensure the access to the
canvas occurs with the surface created. Otherwise, Android
will throttle calls to lockCanvas. */
synchronized (surfaceView.surfaceChangeLock)
synchronized (damageRegion)
{
damageRect = damageRegion.getBounds ();
if (!surfaceView.isCreated ())
if (damageRegion.isEmpty ())
return;
if (bitmap == null)
return;
bitmap = getBitmap ();
/* Lock the canvas with the specified damage. */
canvas = surfaceView.lockCanvas (damageRect);
/* Emacs must take the following lock to ensure the access to the
canvas occurs with the surface created. Otherwise, Android
will throttle calls to lockCanvas. */
/* Return if locking the canvas failed. */
if (canvas == null)
return;
synchronized (surfaceView.surfaceChangeLock)
{
damageRect = damageRegion.getBounds ();
/* Copy from the back buffer to the canvas. If damageRect was
made empty, then draw the entire back buffer. */
if (!surfaceView.isCreated ())
return;
if (damageRect.isEmpty ())
canvas.drawBitmap (bitmap, 0f, 0f, paint);
else
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
if (bitmap == null)
return;
/* Unlock the canvas and clear the damage. */
surfaceView.unlockCanvasAndPost (canvas);
damageRegion.setEmpty ();
/* Lock the canvas with the specified damage. */
canvas = surfaceView.lockCanvas (damageRect);
/* Return if locking the canvas failed. */
if (canvas == null)
return;
/* Copy from the back buffer to the canvas. If damageRect was
made empty, then draw the entire back buffer. */
if (damageRect.isEmpty ())
canvas.drawBitmap (bitmap, 0f, 0f, paint);
else
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
/* Unlock the canvas and clear the damage. */
surfaceView.unlockCanvasAndPost (canvas);
damageRegion.setEmpty ();
}
}
}
@ -308,6 +349,78 @@ else if (child.getVisibility () != GONE)
public boolean
onTouchEvent (MotionEvent motion)
{
return window.onSomeKindOfMotionEvent (motion);
return window.onTouchEvent (motion);
}
private void
moveChildToBack (View child)
{
int index;
index = indexOfChild (child);
if (index > 0)
{
detachViewFromParent (index);
/* The view at 0 is the surface view. */
attachViewToParent (child, 1,
child.getLayoutParams());
}
}
/* The following two functions must not be called if the view has no
parent, or is parented to an activity. */
public void
raise ()
{
EmacsView parent;
parent = (EmacsView) getParent ();
Log.d (TAG, "raise: parent " + parent);
if (parent.indexOfChild (this)
== parent.getChildCount () - 1)
return;
parent.bringChildToFront (this);
/* Yes, all of this is really necessary! */
parent.requestLayout ();
parent.invalidate ();
requestLayout ();
invalidate ();
/* The surface view must be destroyed and recreated. */
removeView (surfaceView);
addView (surfaceView, 0);
}
public void
lower ()
{
EmacsView parent;
parent = (EmacsView) getParent ();
Log.d (TAG, "lower: parent " + parent);
if (parent.indexOfChild (this) == 1)
return;
parent.moveChildToBack (this);
/* Yes, all of this is really necessary! */
parent.requestLayout ();
parent.invalidate ();
requestLayout ();
invalidate ();
/* The surface view must be removed and attached again. */
removeView (surfaceView);
addView (surfaceView, 0);
}
};

View File

@ -22,6 +22,7 @@
import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import android.graphics.Rect;
import android.graphics.Canvas;
@ -50,9 +51,29 @@ their views are attached to the parent activity (if any), else
Views are also drawables, meaning they can accept drawing
requests. */
/* Help wanted. What does not work includes `EmacsView.raise',
`EmacsView.lower', reparenting a window onto another window.
All three are likely undocumented restrictions within
EmacsSurface. */
public class EmacsWindow extends EmacsHandleObject
implements EmacsDrawable
{
private static final String TAG = "EmacsWindow";
private class Coordinate
{
/* Integral coordinate. */
int x, y;
Coordinate (int x, int y)
{
this.x = x;
this.y = y;
}
};
/* The view associated with the window. */
public EmacsView view;
@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject
private Rect rect;
/* The parent window, or null if it is the root window. */
private EmacsWindow parent;
public EmacsWindow parent;
/* List of all children in stacking order. This must be kept
consistent! */
public ArrayList<EmacsWindow> children;
/* Map between pointer identifiers and last known position. Used to
compute which pointer changed upon a touch event. */
private HashMap<Integer, Coordinate> pointerMap;
/* The window consumer currently attached, if it exists. */
private EmacsWindowAttachmentManager.WindowConsumer attached;
@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject
last button press or release event. */
private int lastButtonState, lastModifiers;
/* Whether or not the window is mapped, and whether or not it is
deiconified. */
private boolean isMapped, isIconified;
/* Whether or not to ask for focus upon being mapped, and whether or
not the window should be focusable. */
private boolean dontFocusOnMap, dontAcceptFocus;
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height)
@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject
super (handle);
rect = new Rect (x, y, x + width, y + height);
pointerMap = new HashMap<Integer, Coordinate> ();
/* Create the view from the context's UI thread. The window is
unmapped, so the view is GONE. */
@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject
if (parent != null)
{
parent.children.add (this);
parent.view.post (new Runnable () {
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject
}
});
}
else
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
manager = EmacsWindowAttachmentManager.MANAGER;
/* If parent is the root window, notice that there are new
children available for interested activites to pick
up. */
manager.registerWindow (EmacsWindow.this);
}
});
scratchGC = new EmacsGC ((short) 0);
}
@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject
+ "children!");
/* Remove the view from its parent and make it invisible. */
view.post (new Runnable () {
EmacsService.SERVICE.runOnUiThread (new Runnable () {
public void
run ()
{
@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject
parent = (View) view.getParent ();
if (parent != null && attached == null)
if (parent != null)
((ViewGroup) parent).removeView (view);
manager.detachWindow (EmacsWindow.this);
@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject
public void
viewLayout (int left, int top, int right, int bottom)
{
int rectWidth, rectHeight;
synchronized (this)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
left, top, rect.width (),
rect.height ());
}
rectWidth = right - left;
rectHeight = bottom - top;
EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
left, top, rectWidth,
rectHeight);
}
public void
requestViewLayout ()
{
view.post (new Runnable () {
/* This is necessary because otherwise subsequent drawing on the
Emacs thread may be lost. */
view.explicitlyDirtyBitmap (rect);
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject
public void
mapWindow ()
{
view.post (new Runnable () {
@Override
public void
run ()
{
if (isMapped)
return;
view.setVisibility (View.VISIBLE);
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
});
isMapped = true;
if (parent == null)
{
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
/* Make the view visible, first of all. */
view.setVisibility (View.VISIBLE);
manager = EmacsWindowAttachmentManager.MANAGER;
/* If parent is the root window, notice that there are new
children available for interested activites to pick
up. */
manager.registerWindow (EmacsWindow.this);
if (!getDontFocusOnMap ())
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
});
}
else
{
/* Do the same thing as above, but don't register this
window. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.setVisibility (View.VISIBLE);
if (!getDontFocusOnMap ())
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
});
}
}
public void
unmapWindow ()
{
if (!isMapped)
return;
isMapped = false;
view.post (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
manager = EmacsWindowAttachmentManager.MANAGER;
view.setVisibility (View.GONE);
/* Now that the window is unmapped, unregister it as
well. */
manager.detachWindow (EmacsWindow.this);
}
});
}
@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject
return 4;
}
/* Return the ID of the pointer which changed in EVENT. Value is -1
if it could not be determined, else the pointer that changed, or
-2 if -1 would have been returned, but there is also a pointer
that is a mouse. */
private int
figureChange (MotionEvent event)
{
int pointerID, i, truncatedX, truncatedY, pointerIndex;
Coordinate coordinate;
boolean mouseFlag;
/* pointerID is always initialized but the Java compiler is too
dumb to know that. */
pointerID = -1;
mouseFlag = false;
switch (event.getActionMasked ())
{
case MotionEvent.ACTION_DOWN:
/* Primary pointer pressed with index 0. */
/* Detect mice. If this is a mouse event, give it to
onSomeKindOfMotionEvent. */
if ((Build.VERSION.SDK_INT
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
&& event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
return -2;
pointerID = event.getPointerId (0);
pointerMap.put (pointerID,
new Coordinate ((int) event.getX (0),
(int) event.getY (0)));
break;
case MotionEvent.ACTION_UP:
/* Primary pointer released with index 0. */
pointerID = event.getPointerId (0);
pointerMap.remove (pointerID);
break;
case MotionEvent.ACTION_POINTER_DOWN:
/* New pointer. Find the pointer ID from the index and place
it in the map. */
pointerIndex = event.getActionIndex ();
pointerID = event.getPointerId (pointerIndex);
pointerMap.put (pointerID,
new Coordinate ((int) event.getX (pointerID),
(int) event.getY (pointerID)));
break;
case MotionEvent.ACTION_POINTER_UP:
/* Pointer removed. Remove it from the map. */
pointerIndex = event.getActionIndex ();
pointerID = event.getPointerId (pointerIndex);
pointerMap.remove (pointerID);
break;
default:
/* Loop through each pointer in the event. */
for (i = 0; i < event.getPointerCount (); ++i)
{
pointerID = event.getPointerId (i);
/* Look up that pointer in the map. */
coordinate = pointerMap.get (pointerID);
if (coordinate != null)
{
/* See if coordinates have changed. */
truncatedX = (int) event.getX (i);
truncatedY = (int) event.getY (i);
if (truncatedX != coordinate.x
|| truncatedY != coordinate.y)
{
/* The pointer changed. Update the coordinate and
break out of the loop. */
coordinate.x = truncatedX;
coordinate.y = truncatedY;
break;
}
}
/* See if this is a mouse. If so, set the mouseFlag. */
if ((Build.VERSION.SDK_INT
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
&& event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
mouseFlag = true;
}
/* Set the pointer ID to -1 if the loop failed to find any
changed pointer. If a mouse pointer was found, set it to
-2. */
if (i == event.getPointerCount ())
pointerID = (mouseFlag ? -2 : -1);
}
/* Return the pointer ID. */
return pointerID;
}
public boolean
onTouchEvent (MotionEvent event)
{
int pointerID, index;
/* Extract the ``touch ID'' (or in Android, the ``pointer
ID''.) */
pointerID = figureChange (event);
if (pointerID < 0)
{
/* If this is a mouse event, give it to
onSomeKindOfMotionEvent. */
if (pointerID == -2)
return onSomeKindOfMotionEvent (event);
return false;
}
/* Find the pointer index corresponding to the event. */
index = event.findPointerIndex (pointerID);
switch (event.getActionMasked ())
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
/* Touch down event. */
EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
/* Touch up event. */
EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
case MotionEvent.ACTION_MOVE:
/* Pointer motion event. */
EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
}
return false;
}
public boolean
onSomeKindOfMotionEvent (MotionEvent event)
{
@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
/* Emacs must return true even though touch events are not yet
handled, because the value of this function is used by the
system to decide whether or not Emacs gets ACTION_MOVE
/* Emacs must return true even though touch events are not
handled here, because the value of this function is used by
the system to decide whether or not Emacs gets ACTION_MOVE
events. */
return true;
case MotionEvent.ACTION_SCROLL:
/* Send a scroll event with the specified deltas. */
EmacsNative.sendWheel (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
event.getAxisValue (MotionEvent.AXIS_HSCROLL),
event.getAxisValue (MotionEvent.AXIS_VSCROLL));
return true;
}
return false;
}
public void
reparentTo (final EmacsWindow otherWindow, int x, int y)
{
int width, height;
/* Reparent this window to the other window. */
if (parent != null)
parent.children.remove (this);
if (otherWindow != null)
otherWindow.children.add (this);
parent = otherWindow;
/* Move this window to the new location. */
synchronized (this)
{
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
}
/* Now do the work necessary on the UI thread to reparent the
window. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
View parent;
/* First, detach this window if necessary. */
manager = EmacsWindowAttachmentManager.MANAGER;
manager.detachWindow (EmacsWindow.this);
/* Also unparent this view. */
parent = (View) view.getParent ();
if (parent != null)
((ViewGroup) parent).removeView (view);
/* Next, either add this window as a child of the new
parent's view, or make it available again. */
if (otherWindow != null)
otherWindow.view.addView (view);
else if (EmacsWindow.this.isMapped)
manager.registerWindow (EmacsWindow.this);
/* Request relayout. */
view.requestLayout ();
}
});
}
public void
makeInputFocus (long time)
{
/* TIME is currently ignored. Request the input focus now. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.requestFocus ();
}
});
}
public void
raise ()
{
/* This does nothing here. */
if (parent == null)
return;
/* Remove and add this view again. */
parent.children.remove (this);
parent.children.add (this);
/* Request a relayout. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.raise ();
}
});
}
public void
lower ()
{
/* This does nothing here. */
if (parent == null)
return;
/* Remove and add this view again. */
parent.children.remove (this);
parent.children.add (this);
/* Request a relayout. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.lower ();
}
});
}
public int[]
getWindowGeometry ()
{
int[] array;
Rect rect;
array = new int[4];
rect = getGeometry ();
array[0] = rect.left;
array[1] = rect.top;
array[2] = rect.width ();
array[3] = rect.height ();
return array;
}
public void
noticeIconified ()
{
isIconified = true;
EmacsNative.sendIconified (this.handle);
}
public void
noticeDeiconified ()
{
isIconified = false;
EmacsNative.sendDeiconified (this.handle);
}
public synchronized void
setDontAcceptFocus (final boolean dontAcceptFocus)
{
this.dontAcceptFocus = dontAcceptFocus;
/* Update the view's focus state. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.setFocusable (!dontAcceptFocus);
view.setFocusableInTouchMode (!dontAcceptFocus);
}
});
}
public synchronized void
setDontFocusOnMap (final boolean dontFocusOnMap)
{
this.dontFocusOnMap = dontFocusOnMap;
}
public synchronized boolean
getDontFocusOnMap ()
{
return dontFocusOnMap;
}
};

View File

@ -19,6 +19,7 @@
package org.gnu.emacs;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -68,7 +69,7 @@ public interface WindowConsumer
};
private List<WindowConsumer> consumers;
private List<EmacsWindow> windows;
public List<EmacsWindow> windows;
public
EmacsWindowAttachmentManager ()
@ -98,12 +99,19 @@ public interface WindowConsumer
EmacsNative.sendWindowAction ((short) 0, 0);
}
public void
public synchronized void
registerWindow (EmacsWindow window)
{
Intent intent;
Log.d (TAG, "registerWindow " + window);
Log.d (TAG, "registerWindow (maybe): " + window);
if (windows.contains (window))
/* The window is already registered. */
return;
Log.d (TAG, "registerWindow: " + window);
windows.add (window);
for (WindowConsumer consumer : consumers)
@ -146,7 +154,7 @@ public interface WindowConsumer
consumers.remove (consumer);
}
public void
public synchronized void
detachWindow (EmacsWindow window)
{
WindowConsumer consumer;
@ -162,5 +170,43 @@ public interface WindowConsumer
consumers.remove (consumer);
consumer.destroy ();
}
windows.remove (window);
}
public void
noticeIconified (WindowConsumer consumer)
{
EmacsWindow window;
Log.d (TAG, "noticeIconified " + consumer);
/* If a window is attached, send the appropriate iconification
events. */
window = consumer.getAttachedWindow ();
if (window != null)
window.noticeIconified ();
}
public void
noticeDeiconified (WindowConsumer consumer)
{
EmacsWindow window;
Log.d (TAG, "noticeDeiconified " + consumer);
/* If a window is attached, send the appropriate iconification
events. */
window = consumer.getAttachedWindow ();
if (window != null)
window.noticeDeiconified ();
}
public synchronized List<EmacsWindow>
copyWindows ()
{
return new ArrayList<EmacsWindow> (windows);
}
};

View File

@ -1647,6 +1647,7 @@ live frame and defaults to the selected one."
(declare-function ns-frame-geometry "nsfns.m" (&optional frame))
(declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
(declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
(declare-function android-frame-geometry "androidfns.c" (&optional frame))
(defun frame-geometry (&optional frame)
"Return geometric attributes of FRAME.
@ -1700,6 +1701,8 @@ and width values are in pixels.
(pgtk-frame-geometry frame))
((eq frame-type 'haiku)
(haiku-frame-geometry frame))
((eq frame-type 'android)
(android-frame-geometry frame))
(t
(list
'(outer-position 0 . 0)
@ -1826,6 +1829,7 @@ of frames like calls to map a frame or change its visibility."
(declare-function ns-frame-edges "nsfns.m" (&optional frame type))
(declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
(declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
(declare-function android-frame-edges "androidfns.c" (&optional frame type))
(defun frame-edges (&optional frame type)
"Return coordinates of FRAME's edges.
@ -1853,6 +1857,8 @@ FRAME."
(pgtk-frame-edges frame type))
((eq frame-type 'haiku)
(haiku-frame-edges frame type))
((eq frame-type 'android)
(android-frame-edges frame type))
(t
(list 0 0 (frame-width frame) (frame-height frame))))))
@ -1861,6 +1867,7 @@ FRAME."
(declare-function ns-mouse-absolute-pixel-position "nsfns.m")
(declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
(declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
(declare-function android-mouse-absolute-pixel-position "androidfns.c")
(defun mouse-absolute-pixel-position ()
"Return absolute position of mouse cursor in pixels.
@ -1879,6 +1886,8 @@ position (0, 0) of the selected frame's terminal."
(pgtk-mouse-absolute-pixel-position))
((eq frame-type 'haiku)
(haiku-mouse-absolute-pixel-position))
((eq frame-type 'android)
(android-mouse-absolute-pixel-position))
(t
(cons 0 0)))))
@ -1887,6 +1896,8 @@ position (0, 0) of the selected frame's terminal."
(declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
(declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
(declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y))
(declare-function android-set-mouse-absolute-pixel-position
"androidfns.c" (x y))
(defun set-mouse-absolute-pixel-position (x y)
"Move mouse pointer to absolute pixel position (X, Y).
@ -1903,7 +1914,9 @@ position (0, 0) of the selected frame's terminal."
((eq frame-type 'w32)
(w32-set-mouse-absolute-pixel-position x y))
((eq frame-type 'haiku)
(haiku-set-mouse-absolute-pixel-position x y)))))
(haiku-set-mouse-absolute-pixel-position x y))
((eq frame-type 'android)
(android-set-mouse-absolute-pixel-position x y)))))
(defun frame-monitor-attributes (&optional frame)
"Return the attributes of the physical monitor dominating FRAME.
@ -1999,6 +2012,7 @@ workarea attribute."
;; TODO: implement this on PGTK.
;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
(declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
(declare-function android-frame-list-z-order "androidfns.c" (&optional display))
(defun frame-list-z-order (&optional display)
"Return list of Emacs' frames, in Z (stacking) order.
@ -2024,13 +2038,17 @@ Return nil if DISPLAY contains no Emacs frame."
;; (pgtk-frame-list-z-order display)
nil)
((eq frame-type 'haiku)
(haiku-frame-list-z-order display)))))
(haiku-frame-list-z-order display))
((eq frame-type 'android)
(android-frame-list-z-order display)))))
(declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
(declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
(declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
(declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional above))
(declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional above))
(declare-function android-frame-restack "androidfns.c" (frame1 frame2
&optional above))
(defun frame-restack (frame1 frame2 &optional above)
"Restack FRAME1 below FRAME2.
@ -2064,7 +2082,9 @@ Some window managers may refuse to restack windows."
((eq frame-type 'haiku)
(haiku-frame-restack frame1 frame2 above))
((eq frame-type 'pgtk)
(pgtk-frame-restack frame1 frame2 above))))
(pgtk-frame-restack frame1 frame2 above))
((eq frame-type 'android)
(android-frame-restack frame1 frame2 above))))
(error "Cannot restack frames")))
(defun frame-size-changed-p (&optional frame)
@ -2113,6 +2133,8 @@ frame's display)."
(> w32-num-mouse-buttons 0)))
((memq frame-type '(x ns haiku pgtk))
t) ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing device
((eq frame-type 'android)
(android-detect-mouse))
(t
(or (and (featurep 'xt-mouse)
xterm-mouse-mode)
@ -2396,6 +2418,8 @@ If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(&optional terminal))
(declare-function haiku-display-monitor-attributes-list "haikufns.c"
(&optional terminal))
(declare-function android-display-monitor-attributes-list "androidfns.c"
(&optional terminal))
(defun display-monitor-attributes-list (&optional display)
"Return a list of physical monitor attributes on DISPLAY.
@ -2449,6 +2473,8 @@ monitors."
(pgtk-display-monitor-attributes-list display))
((eq frame-type 'haiku)
(haiku-display-monitor-attributes-list display))
((eq frame-type 'android)
(android-display-monitor-attributes-list display))
(t
(let ((geometry (list 0 0 (display-pixel-width display)
(display-pixel-height display))))

View File

@ -58,7 +58,8 @@
(defcustom mouse-wheel-down-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
(featurep 'haiku-win) (featurep 'pgtk-win))
(featurep 'haiku-win) (featurep 'pgtk-win)
(featurep 'android-win))
'wheel-up
'mouse-4)
"Event used for scrolling down."
@ -79,7 +80,8 @@
(defcustom mouse-wheel-up-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
(featurep 'haiku-win) (featurep 'pgtk-win))
(featurep 'haiku-win) (featurep 'pgtk-win)
(featurep 'android-win))
'wheel-down
'mouse-5)
"Event used for scrolling up."
@ -254,7 +256,8 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-left-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
(featurep 'haiku-win) (featurep 'pgtk-win))
(featurep 'haiku-win) (featurep 'pgtk-win)
(featurep 'android-win))
'wheel-left
'mouse-6)
"Event used for scrolling left.")
@ -268,7 +271,8 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-right-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
(featurep 'haiku-win) (featurep 'pgtk-win))
(featurep 'haiku-win) (featurep 'pgtk-win)
(featurep 'android-win))
'wheel-right
'mouse-7)
"Event used for scrolling right.")

View File

@ -82,6 +82,11 @@ struct android_emacs_service
jmethodID copy_area;
jmethodID clear_window;
jmethodID clear_area;
jmethodID ring_bell;
jmethodID query_tree;
jmethodID get_screen_width;
jmethodID get_screen_height;
jmethodID detect_mouse;
};
struct android_emacs_pixmap
@ -196,9 +201,6 @@ struct android_event_queue
/* The thread used to run select. */
pthread_t select_thread;
/* Condition variable for the writing side. */
pthread_cond_t write_var;
/* Condition variables for the reading side. */
pthread_cond_t read_var;
@ -314,11 +316,6 @@ android_init_events (void)
"pthread_mutex_init: %s",
strerror (errno));
if (pthread_cond_init (&event_queue.write_var, NULL))
__android_log_print (ANDROID_LOG_FATAL, __func__,
"pthread_cond_init: %s",
strerror (errno));
if (pthread_cond_init (&event_queue.read_var, NULL))
__android_log_print (ANDROID_LOG_FATAL, __func__,
"pthread_cond_init: %s",
@ -387,8 +384,7 @@ android_next_event (union android_event *event_return)
/* Free the container. */
free (container);
/* Signal that events can now be written. */
pthread_cond_signal (&event_queue.write_var);
/* Unlock the queue. */
pthread_mutex_unlock (&event_queue.mutex);
}
@ -407,12 +403,6 @@ android_write_event (union android_event *event)
return;
pthread_mutex_lock (&event_queue.mutex);
/* The event queue is full, wait for events to be read. */
if (event_queue.num_events >= 1024)
pthread_cond_wait (&event_queue.write_var,
&event_queue.mutex);
container->next = event_queue.events.next;
container->last = &event_queue.events;
container->next->last = container;
@ -421,6 +411,10 @@ android_write_event (union android_event *event)
event_queue.num_events++;
pthread_cond_signal (&event_queue.read_var);
pthread_mutex_unlock (&event_queue.mutex);
/* Now set pending_signals to true. This allows C-g to be handled
immediately even without SIGIO. */
pending_signals = true;
}
int
@ -604,31 +598,60 @@ android_file_access_p (const char *name, int amode)
{
AAsset *asset;
AAssetDir *directory;
int length;
if (!asset_manager)
return false;
if (!(amode & W_OK) && (name = android_get_asset_name (name)))
{
if (!strcmp (name, "") || !strcmp (name, "/"))
/* /assets always exists. */
return true;
/* Check if the asset exists by opening it. Suboptimal! */
asset = AAssetManager_open (asset_manager, name,
AASSET_MODE_UNKNOWN);
if (!asset)
{
/* See if it's a directory also. */
/* See if it's a directory as well. To open a directory
with the asset manager, the trailing slash (if specified)
must be removed. */
directory = AAssetManager_openDir (asset_manager, name);
if (directory)
{
/* Make sure the directory actually has files in it. */
if (!AAssetDir_getNextFileName (directory))
{
AAssetDir_close (directory);
errno = ENOENT;
return false;
}
AAssetDir_close (directory);
return true;
}
errno = ENOENT;
return false;
}
AAsset_close (asset);
/* If NAME is a directory name, but it was a regular file, set
errno to ENOTDIR and return false. This is to behave like
faccessat. */
length = strlen (name);
if (name[length - 1] == '/')
{
errno = ENOTDIR;
return false;
}
return true;
}
@ -819,12 +842,31 @@ int
android_close (int fd)
{
if (fd < ANDROID_MAX_ASSET_FD
&& (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
&& (android_table[fd].flags
& ANDROID_FD_TABLE_ENTRY_IS_VALID))
android_table[fd].flags = 0;
return close (fd);
}
/* Like fclose. However, remove any information associated with
FILE's file descriptor from the asset table as well. */
int
android_fclose (FILE *stream)
{
int fd;
fd = fileno (stream);
if (fd != -1 && fd < ANDROID_MAX_ASSET_FD
&& (android_table[fd].flags
& ANDROID_FD_TABLE_ENTRY_IS_VALID))
android_table[fd].flags = 0;
return fclose (stream);
}
/* Return the current user's ``home'' directory, which is actually the
app data directory on Android. */
@ -1010,7 +1052,12 @@ android_init_emacs_service (void)
"(Lorg/gnu/emacs/EmacsWindow;)V");
FIND_METHOD (clear_area, "clearArea",
"(Lorg/gnu/emacs/EmacsWindow;IIII)V");
FIND_METHOD (ring_bell, "ringBell", "()V");
FIND_METHOD (query_tree, "queryTree",
"(Lorg/gnu/emacs/EmacsWindow;)[S");
FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
FIND_METHOD (detect_mouse, "detectMouse", "()Z");
#undef FIND_METHOD
}
@ -1342,6 +1389,97 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
android_write_event (&event);
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
jshort window, jint x, jint y,
jlong time, jint pointer_id)
{
union android_event event;
event.touch.type = ANDROID_TOUCH_DOWN;
event.touch.window = window;
event.touch.x = x;
event.touch.y = y;
event.touch.time = time;
event.touch.pointer_id = pointer_id;
android_write_event (&event);
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
jshort window, jint x, jint y,
jlong time, jint pointer_id)
{
union android_event event;
event.touch.type = ANDROID_TOUCH_UP;
event.touch.window = window;
event.touch.x = x;
event.touch.y = y;
event.touch.time = time;
event.touch.pointer_id = pointer_id;
android_write_event (&event);
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
jshort window, jint x, jint y,
jlong time, jint pointer_id)
{
union android_event event;
event.touch.type = ANDROID_TOUCH_MOVE;
event.touch.window = window;
event.touch.x = x;
event.touch.y = y;
event.touch.time = time;
event.touch.pointer_id = pointer_id;
android_write_event (&event);
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
jshort window, jint x, jint y,
jlong time, jint state,
jfloat x_delta, jfloat y_delta)
{
union android_event event;
event.wheel.type = ANDROID_WHEEL;
event.wheel.window = window;
event.wheel.x = x;
event.wheel.y = y;
event.wheel.time = time;
event.wheel.state = state;
event.wheel.x_delta = x_delta;
event.wheel.y_delta = y_delta;
android_write_event (&event);
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
jshort window)
{
union android_event event;
event.iconified.type = ANDROID_ICONIFIED;
event.iconified.window = window;
}
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
jshort window)
{
union android_event event;
event.iconified.type = ANDROID_DEICONIFIED;
event.iconified.window = window;
}
#pragma clang diagnostic pop
@ -1979,10 +2117,22 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
}
void
android_reparent_window (android_window w, android_window parent,
android_reparent_window (android_window w, android_window parent_handle,
int x, int y)
{
/* TODO */
jobject window, parent;
jmethodID method;
window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW);
parent = android_resolve_handle (parent_handle,
ANDROID_HANDLE_WINDOW);
method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"reparentTo",
"(Lorg/gnu/emacs/EmacsWindow;II)V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
method,
parent, (jint) x, (jint) y);
}
/* Look up the method with SIGNATURE by NAME in CLASS. Abort if it
@ -2904,6 +3054,163 @@ android_put_image (android_pixmap handle, struct android_image *image)
ANDROID_DELETE_LOCAL_REF (bitmap);
}
void
android_bell (void)
{
(*android_java_env)->CallVoidMethod (android_java_env,
emacs_service,
service_class.ring_bell);
}
void
android_set_input_focus (android_window handle, unsigned long time)
{
jobject window;
jmethodID make_input_focus;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
make_input_focus = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"makeInputFocus", "(J)V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
make_input_focus, (jlong) time);
}
void
android_raise_window (android_window handle)
{
jobject window;
jmethodID raise;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
raise = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"raise", "()V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
raise);
}
void
android_lower_window (android_window handle)
{
jobject window;
jmethodID lower;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
lower = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"lower", "()V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
lower);
}
int
android_query_tree (android_window handle, android_window *root_return,
android_window *parent_return,
android_window **children_return,
unsigned int *nchildren_return)
{
jobject window, array;
jsize nelements, i;
android_window *children;
jshort *shorts;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
/* window can be NULL, so this is a service method. */
array
= (*android_java_env)->CallObjectMethod (android_java_env,
emacs_service,
service_class.query_tree,
window);
if (!array)
{
(*android_java_env)->ExceptionClear (android_java_env);
memory_full (0);
}
/* The first element of the array is the parent window. The rest
are the children. */
nelements = (*android_java_env)->GetArrayLength (android_java_env,
array);
eassert (nelements);
/* Now fill in the children. */
children = xnmalloc (nelements - 1, sizeof *children);
shorts
= (*android_java_env)->GetShortArrayElements (android_java_env, array,
NULL);
for (i = 1; i < nelements; ++i)
children[i] = shorts[i];
/* Finally, return the parent and other values. */
*root_return = 0;
*parent_return = shorts[0];
*children_return = children;
/* Release the array contents. */
(*android_java_env)->ReleaseShortArrayElements (android_java_env, array,
shorts, JNI_ABORT);
ANDROID_DELETE_LOCAL_REF (array);
return 1;
}
void
android_get_geometry (android_window handle,
android_window *root_return,
int *x_return, int *y_return,
unsigned int *width_return,
unsigned int *height_return,
unsigned int *border_width_return)
{
jobject window;
jarray window_geometry;
jmethodID get_geometry;
jint *ints;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
get_geometry = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"getWindowGeometry", "()[I");
window_geometry
= (*android_java_env)->CallObjectMethod (android_java_env,
window,
get_geometry);
if (!window_geometry)
{
(*android_java_env)->ExceptionClear (android_java_env);
memory_full (0);
}
/* window_geometry is an array containing x, y, width and
height. border_width is always 0 on Android. */
eassert ((*android_java_env)->GetArrayLength (android_java_env,
window_geometry)
== 4);
*root_return = 0;
*border_width_return = 0;
ints
= (*android_java_env)->GetIntArrayElements (android_java_env,
window_geometry,
NULL);
*x_return = ints[0];
*y_return = ints[1];
*width_return = ints[2];
*height_return = ints[3];
(*android_java_env)->ReleaseIntArrayElements (android_java_env,
window_geometry,
ints, JNI_ABORT);
/* Now free the local reference. */
ANDROID_DELETE_LOCAL_REF (window_geometry);
}
/* Low level drawing primitives. */
@ -2998,6 +3305,86 @@ android_damage_window (android_drawable handle,
/* Other misc system routines. */
int
android_get_screen_width (void)
{
return (*android_java_env)->CallIntMethod (android_java_env,
emacs_service,
service_class.get_screen_width,
(jboolean) false);
}
int
android_get_screen_height (void)
{
return (*android_java_env)->CallIntMethod (android_java_env,
emacs_service,
service_class.get_screen_height,
(jboolean) false);
}
int
android_get_mm_width (void)
{
return (*android_java_env)->CallIntMethod (android_java_env,
emacs_service,
service_class.get_screen_width,
(jboolean) true);
}
int
android_get_mm_height (void)
{
return (*android_java_env)->CallIntMethod (android_java_env,
emacs_service,
service_class.get_screen_height,
(jboolean) true);
}
bool
android_detect_mouse (void)
{
return (*android_java_env)->CallBooleanMethod (android_java_env,
emacs_service,
service_class.detect_mouse);
}
void
android_set_dont_focus_on_map (android_window handle,
bool no_focus_on_map)
{
jmethodID method;
jobject window;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"setDontFocusOnMap", "(Z)V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
method,
(jboolean) no_focus_on_map);
}
void
android_set_dont_accept_focus (android_window handle,
bool no_accept_focus)
{
jmethodID method;
jobject window;
window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
"setDontAcceptFocus", "(Z)V");
(*android_java_env)->CallVoidMethod (android_java_env, window,
method,
(jboolean) no_accept_focus);
}
#undef faccessat
/* Replace the system faccessat with one which understands AT_EACCESS.
@ -3015,6 +3402,128 @@ faccessat (int dirfd, const char *pathname, int mode, int flags)
return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
}
/* Directory listing emulation. */
struct android_dir
{
/* The real DIR *, if it exists. */
DIR *dir;
/* Otherwise, the AAssetDir. */
void *asset_dir;
};
/* Like opendir. However, return an asset directory if NAME points to
an asset. */
struct android_dir *
android_opendir (const char *name)
{
struct android_dir *dir;
AAssetDir *asset_dir;
const char *asset_name;
asset_name = android_get_asset_name (name);
/* If the asset manager exists and NAME is an asset, return an asset
directory. */
if (asset_manager && asset_name)
{
asset_dir = AAssetManager_openDir (asset_manager,
asset_name);
if (!asset_dir)
{
errno = ENOENT;
return NULL;
}
dir = xmalloc (sizeof *dir);
dir->dir = NULL;
dir->asset_dir = asset_dir;
return dir;
}
/* Otherwise, open the directory normally. */
dir = xmalloc (sizeof *dir);
dir->asset_dir = NULL;
dir->dir = opendir (name);
if (!dir->dir)
{
xfree (dir);
return NULL;
}
return dir;
}
/* Like readdir, except it understands asset directories. */
struct dirent *
android_readdir (struct android_dir *dir)
{
static struct dirent dirent;
const char *filename;
if (dir->asset_dir)
{
filename = AAssetDir_getNextFileName (dir->asset_dir);
errno = 0;
if (!filename)
return NULL;
memset (&dirent, 0, sizeof dirent);
dirent.d_ino = 0;
dirent.d_off = 0;
dirent.d_reclen = sizeof dirent;
dirent.d_type = DT_UNKNOWN;
strncpy (dirent.d_name, filename,
sizeof dirent.d_name - 1);
return &dirent;
}
return readdir (dir->dir);
}
/* Like closedir, but it also closes asset manager directories. */
void
android_closedir (struct android_dir *dir)
{
if (dir->dir)
closedir (dir->dir);
else
AAssetDir_close (dir->asset_dir);
xfree (dir);
}
/* emacs_abort implementation for Android. This logs a stack
trace. */
void
emacs_abort (void)
{
volatile char *foo;
__android_log_print (ANDROID_LOG_FATAL, __func__,
"emacs_abort called, please review the ensuing"
" stack trace");
/* Cause a NULL pointer dereference to make debuggerd generate a
tombstone. */
foo = NULL;
*foo = '\0';
abort ();
}
#else /* ANDROID_STUBIFY */
/* X emulation functions for Android. */

View File

@ -27,7 +27,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifndef ANDROID_STUBIFY
#include <jni.h>
#include <pwd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <android/bitmap.h>
@ -53,6 +55,7 @@ extern int android_fstat (int, struct stat *);
extern int android_fstatat (int, const char *restrict,
struct stat *restrict, int);
extern int android_close (int);
extern int android_fclose (FILE *);
extern const char *android_get_home_directory (void);
extern double android_pixel_density_x, android_pixel_density_y;
@ -71,6 +74,26 @@ extern unsigned char *android_lock_bitmap (android_window,
jobject *);
extern void android_damage_window (android_window,
struct android_rectangle *);
extern int android_get_screen_width (void);
extern int android_get_screen_height (void);
extern int android_get_mm_width (void);
extern int android_get_mm_height (void);
extern bool android_detect_mouse (void);
extern void android_set_dont_focus_on_map (android_window, bool);
extern void android_set_dont_accept_focus (android_window, bool);
/* Directory listing emulation. */
struct android_dir;
extern struct android_dir *android_opendir (const char *);
extern struct dirent *android_readdir (struct android_dir *);
extern void android_closedir (struct android_dir *);
#endif

View File

@ -21,6 +21,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <math.h>
#include "lisp.h"
#include "android.h"
#include "androidterm.h"
#include "blockinput.h"
#include "keyboard.h"
@ -151,6 +152,36 @@ android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
signal_error ("Undefined color", color_name);
}
static void
android_set_parent_frame (struct frame *f, Lisp_Object new_value,
Lisp_Object old_value)
{
struct frame *p;
p = NULL;
if (!NILP (new_value)
&& (!FRAMEP (new_value)
|| !FRAME_LIVE_P (p = XFRAME (new_value))
|| !FRAME_ANDROID_P (p)))
{
store_frame_param (f, Qparent_frame, old_value);
error ("Invalid specification of `parent-frame'");
}
if (p != FRAME_PARENT_FRAME (f))
{
block_input ();
android_reparent_window (FRAME_ANDROID_WINDOW (f),
(p ? FRAME_ANDROID_WINDOW (p)
: FRAME_DISPLAY_INFO (f)->root_window),
f->left_pos, f->top_pos);
unblock_input ();
fset_parent_frame (f, new_value);
}
}
void
android_implicitly_set_name (struct frame *f, Lisp_Object arg,
Lisp_Object oldval)
@ -531,9 +562,9 @@ android_default_font_parameter (struct frame *f, Lisp_Object parms)
if (! FONTP (font) && ! STRINGP (font))
{
const char *names[] = {
"Droid Sans Mono",
"monospace",
"DroidSansMono",
"Droid Sans Mono-12",
"Monospace-12",
"DroidSansMono-12",
NULL
};
int i;
@ -1119,8 +1150,7 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width,
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
return make_fixnum (android_get_screen_width ());
#endif
}
@ -1133,8 +1163,7 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
return make_fixnum (android_get_screen_height ());
#endif
}
@ -1185,8 +1214,7 @@ DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width,
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
return make_fixnum (android_get_mm_width ());
#endif
}
@ -1198,8 +1226,7 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height,
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
return make_fixnum (android_get_mm_height ());
#endif
}
@ -1225,77 +1252,373 @@ DEFUN ("x-display-visual-class", Fx_display_visual_class,
return Qtrue_color;
}
DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
Sx_display_monitor_attributes_list,
DEFUN ("android-display-monitor-attributes-list",
Fandroid_display_monitor_attributes_list,
Sandroid_display_monitor_attributes_list,
0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
doc: /* Return a list of physical monitor attributes on the X display TERMINAL.
The optional argument TERMINAL specifies which display to ask about.
TERMINAL should be a terminal object, a frame or a display name (a string).
If omitted or nil, that stands for the selected frame's display.
Internal use only, use `display-monitor-attributes-list' instead. */)
(Lisp_Object terminal)
{
#ifdef ANDROID_STUBIFY
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
struct MonitorInfo monitor;
memset (&monitor, 0, sizeof monitor);
monitor.geom.width = android_get_screen_width ();
monitor.geom.height = android_get_screen_height ();
monitor.mm_width = android_get_mm_width ();
monitor.mm_height = android_get_mm_height ();
monitor.work = monitor.geom;
monitor.name = (char *) "Android device monitor";
/* What to do about monitor_frames? */
return make_monitor_attribute_list (&monitor, 1,
0, Qnil, NULL);
#endif
}
#ifndef ANDROID_STUBIFY
static Lisp_Object
frame_geometry (Lisp_Object frame, Lisp_Object attribute)
{
struct frame *f = decode_live_frame (frame);
android_window rootw;
unsigned int native_width, native_height, x_border_width = 0;
int x_native = 0, y_native = 0, xptr = 0, yptr = 0;
int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0;
int outer_left, outer_top, outer_right, outer_bottom;
int native_left, native_top, native_right, native_bottom;
int inner_left, inner_top, inner_right, inner_bottom;
int internal_border_width;
bool menu_bar_external = false, tool_bar_external = false;
int menu_bar_height = 0, menu_bar_width = 0;
int tab_bar_height = 0, tab_bar_width = 0;
int tool_bar_height = 0, tool_bar_width = 0;
if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f)
|| !FRAME_ANDROID_WINDOW (f))
return Qnil;
block_input ();
android_get_geometry (FRAME_ANDROID_WINDOW (f),
&rootw, &x_native, &y_native,
&native_width, &native_height, &x_border_width);
unblock_input ();
if (FRAME_PARENT_FRAME (f))
{
Lisp_Object parent, edges;
XSETFRAME (parent, FRAME_PARENT_FRAME (f));
edges = Fandroid_frame_edges (parent, Qnative_edges);
if (!NILP (edges))
{
x_native += XFIXNUM (Fnth (make_fixnum (0), edges));
y_native += XFIXNUM (Fnth (make_fixnum (1), edges));
}
outer_left = x_native;
outer_top = y_native;
outer_right = outer_left + native_width + 2 * x_border_width;
outer_bottom = outer_top + native_height + 2 * x_border_width;
native_left = x_native + x_border_width;
native_top = y_native + x_border_width;
native_right = native_left + native_width;
native_bottom = native_top + native_height;
}
else
{
outer_left = xptr;
outer_top = yptr;
outer_right = outer_left + left_off + native_width + right_off;
outer_bottom = outer_top + top_off + native_height + bottom_off;
native_left = outer_left + left_off;
native_top = outer_top + top_off;
native_right = native_left + native_width;
native_bottom = native_top + native_height;
}
internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
inner_left = native_left + internal_border_width;
inner_top = native_top + internal_border_width;
inner_right = native_right - internal_border_width;
inner_bottom = native_bottom - internal_border_width;
menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
inner_top += menu_bar_height;
menu_bar_width = menu_bar_height ? native_width : 0;
tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
tab_bar_width = (tab_bar_height
? native_width - 2 * internal_border_width
: 0);
inner_top += tab_bar_height;
tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
tool_bar_width = (tool_bar_height
? native_width - 2 * internal_border_width
: 0);
inner_top += tool_bar_height;
/* Construct list. */
if (EQ (attribute, Qouter_edges))
return list4i (outer_left, outer_top, outer_right, outer_bottom);
else if (EQ (attribute, Qnative_edges))
return list4i (native_left, native_top, native_right, native_bottom);
else if (EQ (attribute, Qinner_edges))
return list4i (inner_left, inner_top, inner_right, inner_bottom);
else
return
list (Fcons (Qouter_position,
Fcons (make_fixnum (outer_left),
make_fixnum (outer_top))),
Fcons (Qouter_size,
Fcons (make_fixnum (outer_right - outer_left),
make_fixnum (outer_bottom - outer_top))),
/* Approximate. */
Fcons (Qexternal_border_size,
Fcons (make_fixnum (right_off),
make_fixnum (bottom_off))),
Fcons (Qouter_border_width, make_fixnum (x_border_width)),
/* Approximate. */
Fcons (Qtitle_bar_size,
Fcons (make_fixnum (0),
make_fixnum (top_off - bottom_off))),
Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil),
Fcons (Qmenu_bar_size,
Fcons (make_fixnum (menu_bar_width),
make_fixnum (menu_bar_height))),
Fcons (Qtab_bar_size,
Fcons (make_fixnum (tab_bar_width),
make_fixnum (tab_bar_height))),
Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
Fcons (Qtool_bar_size,
Fcons (make_fixnum (tool_bar_width),
make_fixnum (tool_bar_height))),
Fcons (Qinternal_border_width,
make_fixnum (internal_border_width)));
}
#endif
DEFUN ("android-frame-geometry", Fandroid_frame_geometry,
Sandroid_frame_geometry,
0, 1, 0,
doc: /* Return geometric attributes of FRAME.
FRAME must be a live frame and defaults to the selected one. The return
value is an association list of the attributes listed below. All height
and width values are in pixels.
`outer-position' is a cons of the outer left and top edges of FRAME
relative to the origin - the position (0, 0) - of FRAME's display.
`outer-size' is a cons of the outer width and height of FRAME. The
outer size includes the title bar and the external borders as well as
any menu and/or tool bar of frame.
`external-border-size' is a cons of the horizontal and vertical width of
FRAME's external borders as supplied by the window manager.
`title-bar-size' is a cons of the width and height of the title bar of
FRAME as supplied by the window manager. If both of them are zero,
FRAME has no title bar. If only the width is zero, Emacs was not
able to retrieve the width information.
`menu-bar-external', if non-nil, means the menu bar is external (never
included in the inner edges of FRAME).
`menu-bar-size' is a cons of the width and height of the menu bar of
FRAME.
`tool-bar-external', if non-nil, means the tool bar is external (never
included in the inner edges of FRAME).
`tool-bar-position' tells on which side the tool bar on FRAME is and can
be one of `left', `top', `right' or `bottom'. If this is nil, FRAME
has no tool bar.
`tool-bar-size' is a cons of the width and height of the tool bar of
FRAME.
`internal-border-width' is the width of the internal border of
FRAME. */)
(Lisp_Object frame)
{
#ifdef ANDROID_STUBIFY
error ("Android cross-compilation stub called!");
return Qnil;
#else
return frame_geometry (frame, Qnil);
#endif
}
DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 2, 0,
doc: /* Return edge coordinates of FRAME.
FRAME must be a live frame and defaults to the selected one. The return
value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are
in pixels relative to the origin - the position (0, 0) - of FRAME's
display.
If optional argument TYPE is the symbol `outer-edges', return the outer
edges of FRAME. The outer edges comprise the decorations of the window
manager (like the title bar or external borders) as well as any external
menu or tool bar of FRAME. If optional argument TYPE is the symbol
`native-edges' or nil, return the native edges of FRAME. The native
edges exclude the decorations of the window manager and any external
menu or tool bar of FRAME. If TYPE is the symbol `inner-edges', return
the inner edges of FRAME. These edges exclude title bar, any borders,
menu bar or tool bar of FRAME. */)
(Lisp_Object frame, Lisp_Object type)
{
#ifndef ANDROID_STUBIFY
return frame_geometry (frame, ((EQ (type, Qouter_edges)
|| EQ (type, Qinner_edges))
? type
: Qnative_edges));
#else
return Qnil;
#endif
}
DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry,
0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
#ifndef ANDROID_STUBIFY
static Lisp_Object
android_frame_list_z_order (struct android_display_info *dpyinfo,
android_window window)
{
android_window root, parent, *children;
unsigned int nchildren;
unsigned long i;
Lisp_Object frames;
frames = Qnil;
if (android_query_tree (window, &root, &parent,
&children, &nchildren))
{
for (i = 0; i < nchildren; i++)
{
Lisp_Object frame, tail;
FOR_EACH_FRAME (tail, frame)
{
struct frame *cf = XFRAME (frame);
if (FRAME_ANDROID_P (cf)
&& (FRAME_ANDROID_WINDOW (cf) == children[i]))
frames = Fcons (frame, frames);
}
}
if (children)
xfree (children);
}
return frames;
}
#endif
DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order,
Sandroid_frame_list_z_order, 0, 1, 0,
doc: /* Return list of Emacs' frames, in Z (stacking) order.
The optional argument TERMINAL specifies which display to ask about.
TERMINAL should be either a frame or a display name (a string). If
omitted or nil, that stands for the selected frame's display. Return
nil if TERMINAL contains no Emacs frame.
As a special case, if TERMINAL is non-nil and specifies a live frame,
return the child frames of that frame in Z (stacking) order.
Frames are listed from topmost (first) to bottommost (last).
On Android, the order of the frames returned is undefined unless
TERMINAL is a frame. */)
(Lisp_Object terminal)
{
#ifdef ANDROID_STUBIFY
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
struct android_display_info *dpyinfo;
android_window window;
dpyinfo = check_android_display_info (terminal);
if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
window = FRAME_ANDROID_WINDOW (XFRAME (terminal));
else
window = dpyinfo->root_window;
return android_frame_list_z_order (dpyinfo, window);
#endif
}
DEFUN ("x-frame-list-z-order", Fx_frame_list_z_order,
Sx_frame_list_z_order, 0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
#ifdef ANDROID_STUBIFY
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
return Qnil;
#endif
}
DEFUN ("android-frame-restack", Fandroid_frame_restack,
Sandroid_frame_restack, 2, 3, 0,
doc: /* Restack FRAME1 below FRAME2.
This means that if both frames are visible and the display areas of
these frames overlap, FRAME2 (partially) obscures FRAME1. If optional
third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This
means that if both frames are visible and the display areas of these
frames overlap, FRAME1 (partially) obscures FRAME2.
DEFUN ("x-frame-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0,
doc: /* SKIP: real doc in xfns.c. */)
This may be thought of as an atomic action performed in two steps: The
first step removes FRAME1's window-step window from the display. The
second step reinserts FRAME1's window below (above if ABOVE is true)
that of FRAME2. Hence the position of FRAME2 in its display's Z
\(stacking) order relative to all other frames excluding FRAME1 remains
unaltered.
The Android system refuses to restack windows, so this does not
work. */)
(Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
{
#ifdef ANDROID_STUBIFY
error ("Android cross-compilation stub called!");
return Qnil;
#else
error ("Not implemented");
/* This is not supported on Android because of limitations in the
platform that prevent ViewGroups from restacking
SurfaceViews. */
return Qnil;
#endif
}
DEFUN ("x-mouse-absolute-pixel-position", Fx_mouse_absolute_pixel_position,
Sx_mouse_absolute_pixel_position, 0, 0, 0,
doc: /* SKIP: real doc in xfns.c. */)
DEFUN ("android-mouse-absolute-pixel-position",
Fandroid_mouse_absolute_pixel_position,
Sandroid_mouse_absolute_pixel_position, 0, 0, 0,
doc: /* Return absolute position of mouse cursor in pixels.
The position is returned as a cons cell (X . Y) of the coordinates of
the mouse cursor position in pixels relative to a position (0, 0) of the
selected frame's display. This does not work on Android. */)
(void)
{
/* TODO: figure out how to implement this. */
/* This cannot be implemented on Android. */
return Qnil;
}
DEFUN ("x-set-mouse-absolute-pixel-position",
Fx_set_mouse_absolute_pixel_position,
Sx_set_mouse_absolute_pixel_position, 2, 2, 0,
doc: /* SKIP: real doc in xfns.c. */)
DEFUN ("android-set-mouse-absolute-pixel-position",
Fandroid_set_mouse_absolute_pixel_position,
Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0,
doc: /* Move mouse pointer to a pixel position at (X, Y). The
coordinates X and Y are interpreted to start from the top-left corner
of the screen. This does not work on Android. */)
(Lisp_Object x, Lisp_Object y)
{
/* TODO: figure out how to implement this. */
/* This cannot be implemented on Android. */
return Qnil;
}
@ -1364,6 +1687,20 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
#endif
}
DEFUN ("android-detect-mouse", Fandroid_detect_mouse,
Sandroid_detect_mouse, 0, 0, 0,
doc: /* Figure out whether or not there is a mouse.
Return non-nil if a mouse is connected to this computer, and nil if
there is no mouse. */)
(void)
{
#ifndef ANDROID_STUBIFY
return android_detect_mouse () ? Qt : Qnil;
#else
return Qnil;
#endif
}
#ifndef ANDROID_STUBIFY
@ -1579,7 +1916,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object value,
y = FRAME_TOP_MARGIN_HEIGHT (f);
block_input ();
android_clear_area (FRAME_ANDROID_WINDOW (f),
android_clear_area (FRAME_ANDROID_DRAWABLE (f),
0, y, width, height);
unblock_input ();
}
@ -1590,7 +1927,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object value,
height = nlines * FRAME_LINE_HEIGHT (f) - y;
block_input ();
android_clear_area (FRAME_ANDROID_WINDOW (f), 0, y,
android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y,
width, height);
unblock_input ();
}
@ -1682,6 +2019,30 @@ android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
}
}
static void
android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
Lisp_Object old_value)
{
if (!EQ (new_value, old_value))
{
android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f),
new_value);
FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
}
}
static void
android_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
Lisp_Object old_value)
{
if (!EQ (new_value, old_value))
{
android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f),
new_value);
FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
}
}
frame_parm_handler android_frame_parm_handlers[] =
{
gui_set_autoraise,
@ -1724,16 +2085,16 @@ frame_parm_handler android_frame_parm_handlers[] =
NULL,
NULL,
NULL,
NULL, /* x_set_undecorated, */
NULL, /* x_set_parent_frame, */
NULL, /* x_set_skip_taskbar, */
NULL, /* x_set_no_focus_on_map, */
NULL, /* x_set_no_accept_focus, */
NULL, /* x_set_z_group, */
NULL, /* x_set_override_redirect, */
NULL,
android_set_parent_frame,
NULL,
android_set_no_focus_on_map,
android_set_no_accept_focus,
NULL,
NULL,
gui_set_no_special_glyphs,
NULL, /* x_set_alpha_background, */
NULL, /* x_set_use_frame_synchronization, */
NULL,
NULL,
};
#endif
@ -1766,14 +2127,16 @@ syms_of_androidfns (void)
defsubr (&Sx_display_mm_height);
defsubr (&Sx_display_backing_store);
defsubr (&Sx_display_visual_class);
defsubr (&Sx_display_monitor_attributes_list);
defsubr (&Sx_frame_geometry);
defsubr (&Sx_frame_list_z_order);
defsubr (&Sx_frame_restack);
defsubr (&Sx_mouse_absolute_pixel_position);
defsubr (&Sx_set_mouse_absolute_pixel_position);
defsubr (&Sandroid_display_monitor_attributes_list);
defsubr (&Sandroid_frame_geometry);
defsubr (&Sandroid_frame_edges);
defsubr (&Sandroid_frame_list_z_order);
defsubr (&Sandroid_frame_restack);
defsubr (&Sandroid_mouse_absolute_pixel_position);
defsubr (&Sandroid_set_mouse_absolute_pixel_position);
defsubr (&Sandroid_get_connection);
defsubr (&Sx_display_list);
defsubr (&Sx_show_tip);
defsubr (&Sx_hide_tip);
defsubr (&Sandroid_detect_mouse);
}

View File

@ -1,4 +1,4 @@
/* Communication module for Android terminals.
/* Android fallback font driver.
Copyright (C) 2023 Free Software Foundation, Inc.
@ -17,6 +17,10 @@ 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/>. */
/* Due to the terrible nature of the Android Typeface subsystems, this
font driver is only used as a fallback when sfntfont-android.c
fails to enumerate any fonts at all. */
#include <config.h>
#include "lisp.h"
@ -636,7 +640,7 @@ androidfont_draw (struct glyph_string *s, int from, int to,
gcontext = android_resolve_handle (s->gc->gcontext,
ANDROID_HANDLE_GCONTEXT);
drawable = android_resolve_handle (FRAME_ANDROID_WINDOW (s->f),
drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f),
ANDROID_HANDLE_WINDOW);
chars = (*android_java_env)->NewIntArray (android_java_env,
to - from);

View File

@ -222,6 +222,12 @@ enum android_event_type
ANDROID_MOTION_NOTIFY,
ANDROID_BUTTON_PRESS,
ANDROID_BUTTON_RELEASE,
ANDROID_TOUCH_DOWN,
ANDROID_TOUCH_UP,
ANDROID_TOUCH_MOVE,
ANDROID_WHEEL,
ANDROID_ICONIFIED,
ANDROID_DEICONIFIED,
};
struct android_any_event
@ -312,6 +318,54 @@ struct android_button_event
unsigned int button;
};
struct android_touch_event
{
/* Type of the event. */
enum android_event_type type;
/* Window associated with the event. */
android_window window;
/* X and Y coordinates of the event. */
int x, y;
/* Time of the event, and the pointer identifier. */
unsigned long time;
/* Index of the pointer being tracked. */
unsigned int pointer_id;
};
struct android_wheel_event
{
/* Type of the event. */
enum android_event_type type;
/* Window associated with the event. */
android_window window;
/* X and Y coordinates of the event. */
int x, y;
/* Time of the event, and the pointer identifier. */
unsigned long time;
/* Modifier state at the time of the event. */
int state;
/* Motion alongside the X and Y axes. */
double x_delta, y_delta;
};
struct android_iconify_event
{
/* Type of the event. */
enum android_event_type type;
/* Window associated with the event. */
android_window window;
};
union android_event
{
enum android_event_type type;
@ -323,8 +377,26 @@ union android_event
struct android_crossing_event xcrossing;
struct android_motion_event xmotion;
struct android_button_event xbutton;
/* This has no parallel in X, since the X model of having
monotonically increasing touch IDs can't work on Android. */
struct android_touch_event touch;
/* This has no parallel in X outside the X Input Extension, and
emulating the input extension interface would be awfully
complicated. */
struct android_wheel_event wheel;
/* This has no parallel in X because Android doesn't have window
properties. */
struct android_iconify_event iconified;
};
enum
{
ANDROID_CURRENT_TIME = 0L,
};
extern int android_pending (void);
extern void android_next_event (union android_event *);
@ -396,6 +468,17 @@ extern void android_clear_area (android_window, int, int, unsigned int,
extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
unsigned int);
extern void android_bell (void);
extern void android_set_input_focus (android_window, unsigned long);
extern void android_raise_window (android_window);
extern void android_lower_window (android_window);
extern int android_query_tree (android_window, android_window *,
android_window *, android_window **,
unsigned int *);
extern void android_get_geometry (android_window, android_window *,
int *, int *, unsigned int *,
unsigned int *, unsigned int *);
#endif

View File

@ -19,6 +19,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include <math.h>
#include "lisp.h"
#include "androidterm.h"
@ -46,6 +47,11 @@ struct android_display_info *x_display_list;
static bool any_help_event_p;
/* Counters for tallying up scroll wheel events if
mwheel_coalesce_scroll_events is true. */
static double wheel_event_x, wheel_event_y;
enum
{
ANDROID_EVENT_NORMAL,
@ -83,19 +89,133 @@ android_clear_frame (struct frame *f)
/* Clearing the frame will erase any cursor, so mark them all as no
longer visible. */
mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
android_clear_window (FRAME_ANDROID_WINDOW (f));
android_clear_window (FRAME_ANDROID_DRAWABLE (f));
}
static void
android_flash (struct frame *f)
{
struct android_gc *gc;
struct android_gc_values values;
int rc;
fd_set fds;
block_input ();
values.function = ANDROID_GC_XOR;
values.foreground = (FRAME_FOREGROUND_PIXEL (f)
^ FRAME_BACKGROUND_PIXEL (f));
gc = android_create_gc ((ANDROID_GC_FUNCTION
| ANDROID_GC_FOREGROUND),
&values);
/* Get the height not including a menu bar widget. */
int height = FRAME_PIXEL_HEIGHT (f);
/* Height of each line to flash. */
int flash_height = FRAME_LINE_HEIGHT (f);
/* These will be the left and right margins of the rectangles. */
int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
int width = flash_right - flash_left;
/* If window is tall, flash top and bottom line. */
if (height > 3 * FRAME_LINE_HEIGHT (f))
{
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left,
(FRAME_INTERNAL_BORDER_WIDTH (f)
+ FRAME_TOP_MARGIN_HEIGHT (f)),
width, flash_height);
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left,
(height - flash_height
- FRAME_INTERNAL_BORDER_WIDTH (f)),
width, flash_height);
}
else
/* If it is short, flash it all. */
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
width, (height - 2
* FRAME_INTERNAL_BORDER_WIDTH (f)));
flush_frame (f);
struct timespec delay = make_timespec (0, 150 * 1000 * 1000);
struct timespec wakeup = timespec_add (current_timespec (), delay);
/* Keep waiting until past the time wakeup or any input gets
available. */
while (! detect_input_pending ())
{
struct timespec current = current_timespec ();
struct timespec timeout;
/* Break if result would not be positive. */
if (timespec_cmp (wakeup, current) <= 0)
break;
/* How long `select' should wait. */
timeout = make_timespec (0, 10 * 1000 * 1000);
/* Wait for some input to become available on the X
connection. */
FD_ZERO (&fds);
/* Try to wait that long--but we might wake up sooner. */
rc = pselect (0, &fds, NULL, NULL, &timeout, NULL);
/* Some input is available, exit the visible bell. */
if (rc >= 0)
break;
}
/* If window is tall, flash top and bottom line. */
if (height > 3 * FRAME_LINE_HEIGHT (f))
{
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left,
(FRAME_INTERNAL_BORDER_WIDTH (f)
+ FRAME_TOP_MARGIN_HEIGHT (f)),
width, flash_height);
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left,
(height - flash_height
- FRAME_INTERNAL_BORDER_WIDTH (f)),
width, flash_height);
}
else
/* If it is short, flash it all. */
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
width, (height - 2
* FRAME_INTERNAL_BORDER_WIDTH (f)));
android_free_gc (gc);
flush_frame (f);
unblock_input ();
}
static void
android_ring_bell (struct frame *f)
{
/* TODO */
if (visible_bell)
android_flash (f);
else
{
block_input ();
android_bell ();
unblock_input ();
}
}
static void
android_toggle_invisible_pointer (struct frame *f, bool invisible)
{
/* TODO */
}
/* Start an update of frame F. This function is installed as a hook
@ -127,10 +247,17 @@ show_back_buffer (struct frame *f)
{
struct android_swap_info swap_info;
/* Somehow Android frames can be swapped while garbaged. */
if (FRAME_GARBAGED_P (f))
return;
memset (&swap_info, 0, sizeof (swap_info));
swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
swap_info.swap_action = ANDROID_COPIED;
android_swap_buffers (&swap_info, 1);
/* Now the back buffer no longer needs to be flipped. */
FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false;
}
/* Flip back buffers on F if it has undrawn content. */
@ -142,7 +269,8 @@ android_flush_dirty_back_buffer_on (struct frame *f)
|| buffer_flipping_blocked_p ()
/* If the frame is not already up to date, do not flush buffers
on input, as that will result in flicker. */
|| !FRAME_ANDROID_COMPLETE_P (f))
|| !FRAME_ANDROID_COMPLETE_P (f)
&& FRAME_ANDROID_NEED_BUFFER_FLIP (f))
return;
show_back_buffer (f);
@ -174,13 +302,13 @@ static void android_frame_rehighlight (struct android_display_info *);
static void
android_lower_frame (struct frame *f)
{
/* TODO. */
android_lower_window (FRAME_ANDROID_WINDOW (f));
}
static void
android_raise_frame (struct frame *f)
{
/* TODO. */
android_raise_window (FRAME_ANDROID_WINDOW (f));
}
static void
@ -354,6 +482,46 @@ android_construct_mouse_click (struct input_event *result,
return Qnil;
}
/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME.
Return the event in IE. Do not set IE->timestamp, as that is left
to the caller. */
static void
android_update_tools (struct frame *f, struct input_event *ie)
{
struct android_touch_point *touchpoint;
ie->kind = TOUCHSCREEN_UPDATE_EVENT;
XSETFRAME (ie->frame_or_window, f);
ie->arg = Qnil;
/* Build the list of active touches. */
for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
touchpoint; touchpoint = touchpoint->next)
ie->arg = Fcons (list3i (touchpoint->x,
touchpoint->y,
touchpoint->tool_id),
ie->arg);
}
/* Find and return an existing tool pressed against FRAME, identified
by POINTER_ID. Return NULL if no tool by that ID was found. */
static struct android_touch_point *
android_find_tool (struct frame *f, int pointer_id)
{
struct android_touch_point *touchpoint;
for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
touchpoint; touchpoint = touchpoint->next)
{
if (touchpoint->tool_id == pointer_id)
return touchpoint;
}
return NULL;
}
static int
handle_one_android_event (struct android_display_info *dpyinfo,
union android_event *event, int *finish,
@ -364,6 +532,10 @@ handle_one_android_event (struct android_display_info *dpyinfo,
Mouse_HLInfo *hlinfo;
union buffered_input_event inev;
int modifiers, count, do_help;
struct android_touch_point *touchpoint, **last;
Lisp_Object window;
int scroll_height;
double scroll_unit;
/* It is okay for this to not resemble handle_one_xevent so much.
Differences in event handling code are much less nasty than
@ -633,6 +805,28 @@ handle_one_android_event (struct android_display_info *dpyinfo,
f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
if (f && event->xbutton.type == ANDROID_BUTTON_PRESS
&& !popup_activated ()
/* && !x_window_to_scroll_bar (event->xbutton.display, */
/* event->xbutton.window, 2) */
&& !FRAME_NO_ACCEPT_FOCUS (f))
{
/* When clicking into a child frame or when clicking
into a parent frame with the child frame selected and
`no-accept-focus' is not set, select the clicked
frame. */
struct frame *hf = dpyinfo->highlight_frame;
if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
{
android_set_input_focus (FRAME_ANDROID_WINDOW (f),
event->xbutton.time);
if (FRAME_PARENT_FRAME (f))
android_raise_window (FRAME_ANDROID_WINDOW (f));
}
}
if (f)
{
/* Is this in the tab-bar? */
@ -715,6 +909,223 @@ handle_one_android_event (struct android_display_info *dpyinfo,
goto OTHER;
/* Touch events. The events here don't parallel X so much. */
case ANDROID_TOUCH_DOWN:
if (!any)
goto OTHER;
/* This event is sent when a tool is put on the screen. X and Y
are the location of the finger, and pointer_id identifies the
tool for as long as it is still held down. First, see if the
touch point already exists and can be reused (this shouldn't
happen, but be safe.) */
touchpoint = android_find_tool (any, event->touch.pointer_id);
if (touchpoint)
{
/* Simply update the tool position and send an update. */
touchpoint->x = event->touch.x;
touchpoint->y = event->touch.x;
android_update_tools (any, &inev.ie);
inev.ie.timestamp = event->touch.time;
goto OTHER;
}
/* Otherwise, link a new touchpoint onto the output's list of
pressed tools. */
touchpoint = xmalloc (sizeof *touchpoint);
touchpoint->tool_id = event->touch.pointer_id;
touchpoint->x = event->touch.x;
touchpoint->y = event->touch.x;
touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
/* Now generate the Emacs event. */
inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
inev.ie.timestamp = event->touch.time;
XSETFRAME (inev.ie.frame_or_window, any);
XSETINT (inev.ie.x, event->touch.x);
XSETINT (inev.ie.y, event->touch.y);
XSETINT (inev.ie.arg, event->touch.pointer_id);
goto OTHER;
case ANDROID_TOUCH_MOVE:
if (!any)
goto OTHER;
/* Look for the tool that moved. */
touchpoint = android_find_tool (any, event->touch.pointer_id);
/* If it doesn't exist, skip processing this event. */
if (!touchpoint)
goto OTHER;
/* Otherwise, update the position and send the update event. */
touchpoint->x = event->touch.x;
touchpoint->y = event->touch.y;
android_update_tools (any, &inev.ie);
inev.ie.timestamp = event->touch.time;
goto OTHER;
case ANDROID_TOUCH_UP:
if (!any)
goto OTHER;
/* Now find and unlink the tool in question. */
last = &FRAME_OUTPUT_DATA (any)->touch_points;
while ((touchpoint = *last))
{
if (touchpoint->tool_id == event->touch.pointer_id)
{
*last = touchpoint->next;
/* The tool was unlinked. Free it and generate the
appropriate Emacs event. */
xfree (touchpoint);
inev.ie.kind = TOUCHSCREEN_END_EVENT;
inev.ie.timestamp = event->touch.time;
XSETFRAME (inev.ie.frame_or_window, any);
XSETINT (inev.ie.x, event->touch.x);
XSETINT (inev.ie.y, event->touch.y);
XSETINT (inev.ie.arg, event->touch.pointer_id);
/* Break out of the loop. */
goto OTHER;
}
else
last = &touchpoint->next;
}
/* No touch point was found. This shouldn't happen. */
goto OTHER;
/* Wheel motion. The events here don't parallel X because
Android doesn't have scroll valuators. */
case ANDROID_WHEEL:
if (!any)
goto OTHER;
if (fabs (event->wheel.x_delta) > 0
|| fabs (event->wheel.y_delta) > 0)
{
if (mwheel_coalesce_scroll_events)
{
if (signbit (event->wheel.x_delta)
!= signbit (wheel_event_x))
wheel_event_x = 0.0;
if (signbit (event->wheel.y_delta)
!= signbit (wheel_event_y))
wheel_event_y = 0.0;
/* Tally up deltas until one of them exceeds 1.0. */
wheel_event_x += event->wheel.x_delta;
wheel_event_y += event->wheel.y_delta;
if (fabs (wheel_event_x) < 1.0
&& fabs (wheel_event_y) < 1.0)
goto OTHER;
}
else
{
/* Use the deltas in the event. */
wheel_event_x = event->wheel.x_delta;
wheel_event_y = event->wheel.y_delta;
}
/* Determine what kind of event to send. */
inev.ie.kind = ((fabs (wheel_event_y)
>= fabs (wheel_event_x))
? WHEEL_EVENT : HORIZ_WHEEL_EVENT);
inev.ie.timestamp = event->wheel.time;
/* Set the event coordinates. */
XSETINT (inev.ie.x, event->wheel.x);
XSETINT (inev.ie.y, event->wheel.y);
/* Set the frame. */
XSETFRAME (inev.ie.frame_or_window, any);
/* Figure out the scroll direction. */
inev.ie.modifiers = (signbit ((fabs (wheel_event_x)
>= fabs (wheel_event_y))
? wheel_event_x
: wheel_event_y)
? down_modifier : up_modifier);
/* Figure out how much to scale the deltas by. */
window = window_from_coordinates (any, event->wheel.x,
event->wheel.y, NULL,
false, false);
if (WINDOWP (window))
scroll_height = XWINDOW (window)->pixel_height;
else
/* EVENT_X and EVENT_Y can be outside the
frame if F holds the input grab, so fall
back to the height of the frame instead. */
scroll_height = FRAME_PIXEL_HEIGHT (any);
scroll_unit = pow (scroll_height, 2.0 / 3.0);
/* Add the keyboard modifiers. */
inev.ie.modifiers
|= android_android_to_emacs_modifiers (dpyinfo,
event->wheel.state);
/* Finally include the scroll deltas. */
inev.ie.arg = list3 (Qnil,
make_float (wheel_event_x
* scroll_unit),
make_float (wheel_event_y
* scroll_unit));
wheel_event_x = 0.0;
wheel_event_y = 0.0;
}
goto OTHER;
/* Iconification. This is vastly simpler than on X. */
case ANDROID_ICONIFIED:
if (FRAME_ICONIFIED_P (any))
goto OTHER;
SET_FRAME_VISIBLE (any, false);
SET_FRAME_ICONIFIED (any, true);
inev.ie.kind = ICONIFY_EVENT;
XSETFRAME (inev.ie.frame_or_window, any);
goto OTHER;
case ANDROID_DEICONIFIED:
if (!FRAME_ICONIFIED_P (any))
goto OTHER;
SET_FRAME_VISIBLE (any, true);
SET_FRAME_ICONIFIED (any, false);
inev.ie.kind = DEICONIFY_EVENT;
XSETFRAME (inev.ie.frame_or_window, any);
goto OTHER;
default:
goto OTHER;
}
@ -781,8 +1192,7 @@ android_read_socket (struct terminal *terminal,
now. */
if (dpyinfo->pending_autoraise_frame)
{
/* android_raise_frame (dpyinfo->pending_autoraise_frame);
TODO */
android_raise_frame (dpyinfo->pending_autoraise_frame);
dpyinfo->pending_autoraise_frame = NULL;
}
@ -796,7 +1206,8 @@ android_frame_up_to_date (struct frame *f)
block_input ();
FRAME_MOUSE_UPDATE (f);
if (!buffer_flipping_blocked_p ())
if (!buffer_flipping_blocked_p ()
&& FRAME_ANDROID_NEED_BUFFER_FLIP (f))
show_back_buffer (f);
/* The frame is now complete, as its contents have been drawn. */
@ -808,7 +1219,10 @@ static void
android_buffer_flipping_unblocked_hook (struct frame *f)
{
block_input ();
show_back_buffer (f);
if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
show_back_buffer (f);
unblock_input ();
}
@ -935,7 +1349,25 @@ android_get_focus_frame (struct frame *f)
static void
android_focus_frame (struct frame *f, bool noactivate)
{
/* TODO */
/* Set the input focus to the frame's window. The system only lets
this work on child frames. */
android_set_input_focus (FRAME_ANDROID_WINDOW (f),
ANDROID_CURRENT_TIME);
}
/* The two procedures below only have to update the cursor on Android,
as there are no window borders there. */
static void
android_frame_highlight (struct frame *f)
{
gui_update_cursor (f, true);
}
static void
android_frame_unhighlight (struct frame *f)
{
gui_update_cursor (f, true);
}
static void
@ -963,12 +1395,10 @@ android_frame_rehighlight (struct android_display_info *dpyinfo)
if (dpyinfo->highlight_frame != old_highlight)
{
/* This is not yet required on Android. */
#if 0
if (old_highlight)
android_frame_unhighlight (old_highlight);
if (dpyinfo->highlight_frame)
android_frame_highlight (dpyinfo->highlight_frame);
#endif
}
}
@ -1027,7 +1457,8 @@ android_fullscreen_hook (struct frame *f)
void
android_iconify_frame (struct frame *f)
{
/* TODO */
/* This really doesn't work on Android. */
error ("Can't notify window manager of iconification");
}
static void
@ -1149,7 +1580,7 @@ android_set_offset (struct frame *f, int xoff, int yoff,
static void
android_set_alpha (struct frame *f)
{
/* TODO */
/* Not supported on Android. */
}
static Lisp_Object
@ -1215,6 +1646,7 @@ android_free_frame_resources (struct frame *f)
{
struct android_display_info *dpyinfo;
Mouse_HLInfo *hlinfo;
struct android_touch_point *last, *next;
dpyinfo = FRAME_DISPLAY_INFO (f);
hlinfo = &dpyinfo->mouse_highlight;
@ -1256,6 +1688,18 @@ android_free_frame_resources (struct frame *f)
if (f == dpyinfo->last_mouse_frame)
dpyinfo->last_mouse_frame = NULL;
/* Free all tool presses currently active on this frame. */
next = FRAME_OUTPUT_DATA (f)->touch_points;
while (next)
{
last = next;
next = next->next;
xfree (last);
}
/* Clear this in case unblock_input reads events. */
FRAME_OUTPUT_DATA (f)->touch_points = NULL;
unblock_input ();
}
@ -1316,8 +1760,8 @@ android_scroll_run (struct window *w, struct run *run)
/* Cursor off. Will be switched on again in gui_update_window_end. */
gui_clear_cursor (w);
android_copy_area (FRAME_ANDROID_WINDOW (f),
FRAME_ANDROID_WINDOW (f),
android_copy_area (FRAME_ANDROID_DRAWABLE (f),
FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x, from_y, width, height, x, to_y);
@ -1337,7 +1781,9 @@ static void
android_flip_and_flush (struct frame *f)
{
block_input ();
show_back_buffer (f);
if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
show_back_buffer (f);
/* The frame is complete again as its contents were just
flushed. */
@ -1355,7 +1801,7 @@ android_clear_rectangle (struct frame *f, struct android_gc *gc, int x,
| ANDROID_GC_FOREGROUND),
&xgcv);
android_set_foreground (gc, xgcv.background);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
x, y, width, height);
android_set_foreground (gc, xgcv.foreground);
}
@ -1405,7 +1851,7 @@ android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
if (face->stipple)
{
android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), face->gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc,
p->bx, p->by, p->nx, p->ny);
android_set_fill_style (face->gc, ANDROID_FILL_SOLID);
@ -1428,7 +1874,7 @@ android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
unsigned long background, cursor_pixel;
int depth;
drawable = FRAME_ANDROID_WINDOW (f);
drawable = FRAME_ANDROID_DRAWABLE (f);
clipmask = ANDROID_NONE;
background = face->background;
cursor_pixel = f->output_data.android->cursor_pixel;
@ -1697,7 +2143,7 @@ android_draw_glyph_string_background (struct glyph_string *s, bool force_p)
{
/* Fill background with a stipple pattern. */
android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
s->x, s->y + box_line_width,
s->background_width,
s->height - 2 * box_line_width);
@ -1734,7 +2180,7 @@ android_fill_triangle (struct frame *f, struct android_gc *gc,
abc[1] = point2;
abc[2] = point3;
android_fill_polygon (FRAME_ANDROID_WINDOW (f),
android_fill_polygon (FRAME_ANDROID_DRAWABLE (f),
gc, abc, 3, ANDROID_CONVEX,
ANDROID_COORD_MODE_ORIGIN);
}
@ -1776,7 +2222,7 @@ android_clear_point (struct frame *f, struct android_gc *gc,
android_get_gc_values (gc, ANDROID_GC_BACKGROUND | ANDROID_GC_FOREGROUND,
&xgcv);
android_set_foreground (gc, xgcv.background);
android_draw_point (FRAME_ANDROID_WINDOW (f), gc, x, y);
android_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y);
android_set_foreground (gc, xgcv.foreground);
}
@ -1798,7 +2244,7 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
black_gc = f->output_data.android->black_relief.gc;
normal_gc = f->output_data.android->normal_gc;
drawable = FRAME_ANDROID_WINDOW (f);
drawable = FRAME_ANDROID_DRAWABLE (f);
android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1);
android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1);
@ -1811,11 +2257,11 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
/* Draw lines. */
if (top_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
right_x - left_x + 1, hwidth);
if (left_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
vwidth, bottom_y - top_y + 1);
if (raised_p)
@ -1824,12 +2270,12 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
gc = white_gc;
if (bot_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x,
bottom_y - hwidth + 1,
right_x - left_x + 1, hwidth);
if (right_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
right_x - vwidth + 1,
top_y, vwidth, bottom_y - top_y + 1);
@ -1853,7 +2299,7 @@ android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
if (top_p && left_p && bot_p && right_p
&& hwidth > 1 && vwidth > 1)
android_draw_rectangle (FRAME_ANDROID_WINDOW (f),
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f),
black_gc, left_x, top_y,
right_x - left_x, bottom_y - top_y);
else
@ -1913,22 +2359,22 @@ android_draw_box_rect (struct glyph_string *s,
android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1);
/* Top. */
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
left_x, right_x - left_x + 1, hwidth);
/* Left. */
if (left_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
top_y, vwidth, bottom_y - top_y + 1);
/* Bottom. */
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
bottom_y - hwidth + 1, right_x - left_x + 1,
hwidth);
/* Right. */
if (right_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
right_x - vwidth + 1, top_y, vwidth,
bottom_y - top_y + 1);
@ -2153,7 +2599,7 @@ android_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y,
{
/* Fill background with a stipple pattern. */
android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x,
y, w, h);
android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
}
@ -2294,7 +2740,7 @@ android_draw_image_foreground (struct glyph_string *s)
if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
android_copy_area (s->img->pixmap,
FRAME_ANDROID_WINDOW (s->f),
FRAME_ANDROID_DRAWABLE (s->f),
s->gc, s->slice.x + r.x - x,
s->slice.y + r.y - y,
r.width, r.height, r.x, r.y);
@ -2307,7 +2753,7 @@ android_draw_image_foreground (struct glyph_string *s)
if (s->hl == DRAW_CURSOR && !s->img->mask)
{
int relief = eabs (s->img->relief);
android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
x - relief, y - relief,
s->slice.width + relief*2 - 1,
s->slice.height + relief*2 - 1);
@ -2317,7 +2763,7 @@ android_draw_image_foreground (struct glyph_string *s)
}
else
/* Draw a rectangle if image could not be loaded. */
android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, y,
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y,
s->slice.width - 1, s->slice.height - 1);
}
@ -2444,7 +2890,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s)
{
/* Fill background with a stipple pattern. */
android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
gc, x, y, w, h);
android_set_fill_style (gc, ANDROID_FILL_SOLID);
@ -2457,7 +2903,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s)
| ANDROID_GC_BACKGROUND),
&xgcv);
android_set_foreground (gc, xgcv.background);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
gc, x, y, w, h);
android_set_foreground (gc, xgcv.foreground);
}
@ -2536,7 +2982,7 @@ android_draw_underwave (struct glyph_string *s, int decoration_width)
while (x1 <= xmax)
{
android_draw_line (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
x1, y1, x2, y2);
x1 = x2, y1 = y2;
x2 += dx, y2 = y0 + odd*dy;
@ -2567,7 +3013,7 @@ android_draw_glyph_string_foreground (struct glyph_string *s)
for (i = 0; i < s->nchars; ++i)
{
struct glyph *g = s->first_glyph + i;
android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
s->gc, x, s->y,
g->pixel_width - 1,
s->height - 1);
@ -2618,7 +3064,7 @@ android_draw_composite_glyph_string_foreground (struct glyph_string *s)
if (s->font_not_found_p)
{
if (s->cmp_from == 0)
android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
s->gc, x, s->y,
s->width - 1, s->height - 1);
}
@ -2754,7 +3200,7 @@ android_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
false);
}
if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
x, s->ybase - glyph->ascent,
glyph->pixel_width - 1,
glyph->ascent + glyph->descent - 1);
@ -2987,14 +3433,14 @@ android_draw_glyph_string (struct glyph_string *s)
s->underline_position = position;
y = s->ybase + position;
if (s->face->underline_defaulted_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
s->x, y, decoration_width, thickness);
else
{
struct android_gc_values xgcv;
android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
android_set_foreground (s->gc, s->face->underline_color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
s->x, y, decoration_width, thickness);
android_set_foreground (s->gc, xgcv.foreground);
}
@ -3006,7 +3452,7 @@ android_draw_glyph_string (struct glyph_string *s)
unsigned long dy = 0, h = 1;
if (s->face->overline_color_defaulted_p)
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
s->gc, s->x, s->y + dy,
decoration_width, h);
else
@ -3014,8 +3460,8 @@ android_draw_glyph_string (struct glyph_string *s)
struct android_gc_values xgcv;
android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
android_set_foreground (s->gc, s->face->overline_color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, s->x,
s->y + dy, decoration_width, h);
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
s->x, s->y + dy, decoration_width, h);
android_set_foreground (s->gc, xgcv.foreground);
}
}
@ -3044,8 +3490,9 @@ android_draw_glyph_string (struct glyph_string *s)
struct android_gc_values xgcv;
android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
android_set_foreground (s->gc, s->face->strike_through_color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, s->x,
glyph_y + dy, decoration_width, h);
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
s->x, glyph_y + dy, decoration_width,
h);
android_set_foreground (s->gc, xgcv.foreground);
}
}
@ -3125,7 +3572,7 @@ static void
android_clear_frame_area (struct frame *f, int x, int y,
int width, int height)
{
android_clear_area (FRAME_ANDROID_WINDOW (f),
android_clear_area (FRAME_ANDROID_DRAWABLE (f),
x, y, width, height);
}
@ -3154,25 +3601,25 @@ android_clear_under_internal_border (struct frame *f)
struct android_gc *gc = f->output_data.android->normal_gc;
android_set_foreground (gc, color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, margin,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin,
width, border);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, 0,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0,
border, height);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, width - border,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - border,
0, border, height);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0,
height - border, width, border);
android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f));
}
else
{
android_clear_area (FRAME_ANDROID_WINDOW (f), 0, 0,
android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0,
border, height);
android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
margin, width, border);
android_clear_area (FRAME_ANDROID_WINDOW (f), width - border,
android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border,
0, border, height);
android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
height - border, width, border);
}
}
@ -3221,7 +3668,7 @@ android_draw_hollow_cursor (struct window *w, struct glyph_row *row)
}
/* Set clipping, draw the rectangle, and reset clipping again. */
android_clip_to_row (w, row, TEXT_AREA, gc);
android_draw_rectangle (FRAME_ANDROID_WINDOW (f), gc, x, y, wd, h - 1);
android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
android_reset_clip_rectangles (f, gc);
}
@ -3295,7 +3742,7 @@ android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width,
if ((cursor_glyph->resolved_level & 1) != 0)
x += cursor_glyph->pixel_width - width;
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
width, row->height);
}
@ -3318,7 +3765,7 @@ android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width,
if ((cursor_glyph->resolved_level & 1) != 0
&& cursor_glyph->pixel_width > w->phys_cursor_width - 1)
x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;
android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, x,
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
cursor_start_y,
w->phys_cursor_width - 1, width);
}
@ -3387,7 +3834,7 @@ android_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
android_set_foreground (f->output_data.android->normal_gc,
face->foreground);
android_draw_line (FRAME_ANDROID_WINDOW (f),
android_draw_line (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x, y0, x, y1);
}
@ -3415,17 +3862,17 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
{
android_set_foreground (f->output_data.android->normal_gc,
color_first);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0, y0, 1, y1 - y0);
android_set_foreground (f->output_data.android->normal_gc,
color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0 + 1, y0, x1 - x0 - 2, y1 - y0);
android_set_foreground (f->output_data.android->normal_gc,
color_last);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x1 - 1, y0, 1, y1 - y0);
}
@ -3435,16 +3882,16 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
{
android_set_foreground (f->output_data.android->normal_gc,
color_first);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0, y0, x1 - x0, 1);
android_set_foreground (f->output_data.android->normal_gc, color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0, y0 + 1, x1 - x0, y1 - y0 - 2);
android_set_foreground (f->output_data.android->normal_gc,
color_last);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0, y1 - 1, x1 - x0, 1);
}
@ -3453,7 +3900,7 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
/* In any other case do not draw the first and last pixels
differently. */
android_set_foreground (f->output_data.android->normal_gc, color);
android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
f->output_data.android->normal_gc,
x0, y0, x1 - x0, y1 - y0);
}

View File

@ -134,6 +134,18 @@ struct android_display_info
Time last_mouse_movement_time;
};
/* Structure representing a single tool (finger or stylus) pressed
onto a frame. */
struct android_touch_point
{
/* The next tool on this list. */
struct android_touch_point *next;
/* The tool ID and the last known X and Y positions. */
int tool_id, x, y;
};
struct android_output
{
/* Graphics contexts for the default font. */
@ -201,6 +213,10 @@ struct android_output
input. */
bool_bf complete : 1;
/* True that indicates whether or not a buffer flip is required
because the frame contents have been dirtied. */
bool_bf need_buffer_flip : 1;
/* Relief GCs, colors etc. */
struct relief {
struct android_gc *gc;
@ -214,6 +230,10 @@ struct android_output
/* Focus state. Only present for consistency with X; it is actually
a boolean. */
int focus_state;
/* List of all tools (either styluses or fingers) pressed onto the
frame. */
struct android_touch_point *touch_points;
};
enum
@ -240,6 +260,14 @@ enum
#define FRAME_ANDROID_NEED_BUFFER_FLIP(f) \
((f)->output_data.android->need_buffer_flip)
/* Return the drawable used for rendering to frame F and mark the
frame as needing a buffer flip later. There's no easy way to run
code after any drawing command, but code can be run whenever
someone asks for the handle necessary to draw. */
#define FRAME_ANDROID_DRAWABLE(f) \
(((f))->output_data.android->need_buffer_flip = true, \
FRAME_ANDROID_WINDOW ((f)))
/* Return whether or not the frame F has been completely drawn. Used
while handling async input. */
#define FRAME_ANDROID_COMPLETE_P(f) \

View File

@ -44,6 +44,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "msdos.h" /* for fstatat */
#endif
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
typedef DIR emacs_dir;
#define emacs_readdir readdir
#define emacs_closedir closedir
#else
#include "android.h"
/* The Android emulation of dirent stuff is required to be able to
list the /assets special directory. */
typedef struct android_dir emacs_dir;
#define emacs_readdir android_readdir
#define emacs_closedir android_closedir
#endif
#ifdef WINDOWSNT
extern int is_slow_fs (const char *);
#endif
@ -78,19 +93,30 @@ dirent_type (struct dirent *dp)
#endif
}
static DIR *
static emacs_dir *
open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
{
char *name = SSDATA (encoded_dirname);
DIR *d;
emacs_dir *d;
int fd, opendir_errno;
#ifdef DOS_NT
/* Directories cannot be opened. The emulation assumes that any
file descriptor other than AT_FDCWD corresponds to the most
recently opened directory. This hack is good enough for Emacs. */
#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
/* On DOS_NT, directories cannot be opened. The emulation assumes
that any file descriptor other than AT_FDCWD corresponds to the
most recently opened directory. This hack is good enough for
Emacs.
This code is also used on Android for a different reason: a
special `assets' directory outside the normal file system is used
to open assets inside the Android application package, and must
be listed using the opendir-like interface provided in
android.h. */
fd = 0;
#ifndef HAVE_ANDROID
d = opendir (name);
#else
d = android_opendir (name);
#endif
opendir_errno = errno;
#else
fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
@ -125,7 +151,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
static void
directory_files_internal_unwind (void *d)
{
closedir (d);
emacs_closedir (d);
}
/* Return the next directory entry from DIR; DIR's name is DIRNAME.
@ -133,12 +159,12 @@ directory_files_internal_unwind (void *d)
Signal any unrecoverable errors. */
static struct dirent *
read_dirent (DIR *dir, Lisp_Object dirname)
read_dirent (emacs_dir *dir, Lisp_Object dirname)
{
while (true)
{
errno = 0;
struct dirent *dp = readdir (dir);
struct dirent *dp = emacs_readdir (dir);
if (dp || errno == 0)
return dp;
if (! (errno == EAGAIN || errno == EINTR))
@ -190,7 +216,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
int fd;
DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd);
/* Unfortunately, we can now invoke expand-file-name and
file-attributes on filenames, both of which can throw, so we must
@ -300,7 +326,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
}
closedir (d);
emacs_closedir (d);
#ifdef WINDOWSNT
if (attrs)
Vw32_get_true_file_attributes = w32_save;
@ -514,7 +540,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
}
}
int fd;
DIR *d = open_directory (dirname, encoded_dir, &fd);
emacs_dir *d = open_directory (dirname, encoded_dir, &fd);
record_unwind_protect_ptr (directory_files_internal_unwind, d);
/* Loop reading directory entries. */

View File

@ -3175,6 +3175,7 @@ redraw_frame (struct frame *f)
its redisplay done. */
mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true);
f->garbaged = false;
}
@ -6053,7 +6054,7 @@ FILE = nil means just close any termscript file currently open. */)
if (tty->termscript != 0)
{
block_input ();
fclose (tty->termscript);
emacs_fclose (tty->termscript);
tty->termscript = 0;
unblock_input ();
}

View File

@ -277,7 +277,7 @@ void
fclose_unwind (void *arg)
{
FILE *stream = arg;
fclose (stream);
emacs_fclose (stream);
}
/* Restore point, having saved it as a marker. */
@ -2989,6 +2989,12 @@ If there is no error, returns nil. */)
encoded_filename = ENCODE_FILE (absname);
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
/* FILE may be some kind of special Android file. */
if (android_file_access_p (SSDATA (encoded_filename), R_OK))
return Qnil;
#endif
if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
report_file_error (SSDATA (string), filename);
@ -3205,7 +3211,11 @@ file_accessible_directory_p (Lisp_Object file)
There are three exceptions: "", "/", and "//". Leave "" alone,
as it's invalid. Append only "." to the other two exceptions as
"/" and "//" are distinct on some platforms, whereas "/", "///",
"////", etc. are all equivalent. */
"////", etc. are all equivalent.
Android has a special directory named "/assets". There is no "."
directory there, but appending a "/" is sufficient to check
whether or not it is a directory. */
if (! len)
dir = data;
else
@ -3215,11 +3225,27 @@ file_accessible_directory_p (Lisp_Object file)
special cases "/" and "//", and it's a safe optimization
here. After appending '.', append another '/' to work around
a macOS bug (Bug#30350). */
static char const appended[] = "/./";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
if (!strncmp ("/assets/", data,
sizeof "/assets" - 1))
{
static char const appended[] = "/";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
}
else
{
#endif
static char const appended[] = "/./";
char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
}
#endif
}
ok = file_access_p (dir, F_OK);
@ -5973,7 +5999,7 @@ do_auto_save_unwind (void *arg)
if (stream != NULL)
{
block_input ();
fclose (stream);
emacs_fclose (stream);
unblock_input ();
}
}

View File

@ -4956,8 +4956,8 @@ const char *const lispy_function_keys[] =
[66] = "return",
[67] = "backspace",
[82] = "menu",
[92] = "page-up",
[93] = "page-down",
[92] = "prior",
[93] = "next",
};
#elif defined HAVE_NTGUI
@ -11219,7 +11219,7 @@ This may include sensitive information such as passwords. */)
if (dribble)
{
block_input ();
fclose (dribble);
emacs_fclose (dribble);
unblock_input ();
dribble = 0;
}

View File

@ -28,6 +28,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@ -5077,6 +5078,7 @@ extern int emacs_open (const char *, int, int);
extern int emacs_open_noquit (const char *, int, int);
extern int emacs_pipe (int[2]);
extern int emacs_close (int);
extern int emacs_fclose (FILE *);
extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);

View File

@ -1141,7 +1141,7 @@ close_infile_unwind (void *arg)
{
struct infile *prev_infile = arg;
eassert (infile && infile != prev_infile);
fclose (infile->stream);
emacs_fclose (infile->stream);
infile = prev_infile;
}

View File

@ -3072,8 +3072,8 @@ sfnt_curve_is_flat (struct sfnt_point control0,
/* 2.0 is a constant describing the area covered at which point the
curve is considered "flat". */
return (abs (sfnt_mul_fixed (g.x, h.x)
- sfnt_mul_fixed (g.y, h.y))
return (abs (sfnt_mul_fixed (g.x, h.y)
- sfnt_mul_fixed (g.y, h.x))
<= 0400000);
}
@ -3261,9 +3261,11 @@ sfnt_prepare_raster (struct sfnt_raster *raster,
struct sfnt_glyph_outline *outline)
{
raster->width
= sfnt_ceil_fixed (outline->xmax - outline->xmin) >> 16;
= (sfnt_ceil_fixed (outline->xmax)
- sfnt_floor_fixed (outline->xmin)) >> 16;
raster->height
= sfnt_ceil_fixed (outline->ymax - outline->ymin) >> 16;
= (sfnt_ceil_fixed (outline->ymax)
- sfnt_floor_fixed (outline->ymin)) >> 16;
raster->refcount = 0;
/* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */
@ -3292,10 +3294,10 @@ sfnt_step_edge (struct sfnt_edge *edge)
}
/* Build a list of edges for each contour in OUTLINE, applying
OUTLINE->xmin and OUTLINE->ymin as the offset to each edge. Call
EDGE_PROC with DCONTEXT and the resulting edges as arguments. It
is OK to modify the edges given to EDGE_PROC. Align all edges to
the sub-pixel grid. */
OUTLINE->xmin and floor (OUTLINE->ymin) as the offset to each edge.
Call EDGE_PROC with DCONTEXT and the resulting edges as arguments.
It is OK to modify the edges given to EDGE_PROC. Align all edges
to the sub-pixel grid. */
static void
sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
@ -3303,13 +3305,18 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
{
struct sfnt_edge *edges;
size_t i, edge, next_vertex;
sfnt_fixed dx, dy, bot, step_x;
sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
int inc_x;
size_t top, bottom, y;
edges = alloca (outline->outline_used * sizeof *edges);
edge = 0;
/* ymin and xmin must be the same as the offset used to set offy and
offx in rasters. */
ymin = sfnt_floor_fixed (outline->ymin);
xmin = sfnt_floor_fixed (outline->xmin);
for (i = 0; i < outline->outline_used; ++i)
{
/* Set NEXT_VERTEX to the next point (vertex) in this contour.
@ -3356,12 +3363,12 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
top = next_vertex;
}
bot = (outline->outline[bottom].y - outline->ymin);
edges[edge].top = (outline->outline[top].y - outline->ymin);
bot = (outline->outline[bottom].y - ymin);
edges[edge].top = (outline->outline[top].y - ymin);
/* Record the edge. Rasterization happens from bottom to
up, so record the X at the bottom. */
edges[edge].x = (outline->outline[bottom].x - outline->xmin);
edges[edge].x = (outline->outline[bottom].x - xmin);
dx = (outline->outline[top].x - outline->outline[bottom].x);
dy = abs (outline->outline[top].y
- outline->outline[bottom].y);
@ -4585,7 +4592,7 @@ main (int argc, char **argv)
/* Time this important bit. */
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
outline = sfnt_build_glyph_outline (glyph, head,
45,
12,
sfnt_test_get_glyph,
sfnt_test_free_glyph,
&dcontext);
@ -4652,7 +4659,7 @@ main (int argc, char **argv)
if (hmtx && head)
{
if (!sfnt_lookup_glyph_metrics (code, 36,
if (!sfnt_lookup_glyph_metrics (code, 12,
&metrics,
hmtx, hhea,
head, maxp))

View File

@ -42,19 +42,6 @@ static Lisp_Object font_cache;
static unsigned int
sfntfont_android_saturate32 (unsigned int a, unsigned int b)
{
unsigned int c;
c = a + b;
if (c < a)
c = -1;
return c;
}
/* Scale each of the four packed bytes in P in the low 16 bits of P by
SCALE. Return the result.
@ -107,8 +94,9 @@ sfntfont_android_blend (unsigned int src, unsigned int dst)
src = src & ~0x00ff00ff;
src |= (src_rb >> 16 | src_rb << 16);
/* Saturating is unnecessary but helps find bugs. */
return sfntfont_android_saturate32 (both, src);
/* This addition need not be saturating because both has already
been multiplied by 255 - a. */
return both + src;
}
#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
@ -128,8 +116,9 @@ sfntfont_android_blendrgb (unsigned int src, unsigned int dst)
both = ag_part | rb_part;
/* Saturating is unnecessary but helps find bugs. */
return sfntfont_android_saturate32 (both, src);
/* This addition need not be saturating because both has already
been multiplied by 255 - a. */
return both + src;
}
/* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE
@ -162,6 +151,10 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
src_y = i + (rect->y - text_rectangle->y);
if (src_y > text_rectangle->height)
/* Huh? */
return;
src_row = (unsigned int *) ((buffer + src_y * stride));
dst_row = (unsigned int *) (dest + ((i + rect->y)
* bitmap_info->stride));
@ -343,7 +336,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
}
/* Lock the bitmap. It must be unlocked later. */
bitmap_data = android_lock_bitmap (FRAME_ANDROID_WINDOW (s->f),
bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f),
&bitmap_info, &bitmap);
/* If locking the bitmap fails, just discard the data that was
@ -385,7 +378,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
ANDROID_DELETE_LOCAL_REF (bitmap);
/* Damage the window by the text rectangle. */
android_damage_window (FRAME_ANDROID_WINDOW (s->f),
android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
&text_rectangle);
/* Release the temporary scanline buffer. */
@ -495,13 +488,19 @@ init_sfntfont_android (void)
version of Android the device is running. */
if (android_get_device_api_level () >= 15)
Vsfnt_default_family_alist
= list2 (Fcons (build_string ("Monospace"),
= list3 (Fcons (build_string ("Monospace"),
build_string ("Droid Sans Mono")),
/* Android doesn't come with a Monospace Serif font, so
this will have to do. */
Fcons (build_string ("Monospace Serif"),
build_string ("Droid Sans Mono")),
Fcons (build_string ("Sans Serif"),
build_string ("Roboto")));
else
Vsfnt_default_family_alist
= list2 (Fcons (build_string ("Monospace"),
= list3 (Fcons (build_string ("Monospace"),
build_string ("Droid Sans Mono")),
Fcons (build_string ("Monospace Serif"),
build_string ("Droid Sans Mono")),
Fcons (build_string ("Sans Serif"),
build_string ("Droid Sans")));

View File

@ -1982,6 +1982,9 @@ sfntfont_text_extents (struct font *font, const unsigned int *code,
total_width = 0;
/* First clear the metrics array. */
memset (metrics, 0, sizeof *metrics);
/* Get the metrcs one by one, then sum them up. */
for (i = 0; i < nglyphs; ++i)
{
@ -2059,7 +2062,7 @@ sfntfont_draw (struct glyph_string *s, int from, int to,
struct sfnt_glyph_metrics metrics;
length = to - from;
font = s->face->font;
font = s->font;
info = (struct sfnt_font_info *) font;
rasters = alloca (length * sizeof *rasters);

View File

@ -2335,7 +2335,8 @@ emacs_backtrace (int backtrace_limit)
}
}
#ifndef HAVE_NTGUI
#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID \
&& !defined ANDROID_STUBIFY)
void
emacs_abort (void)
{
@ -2568,6 +2569,20 @@ emacs_close (int fd)
}
}
/* Wrapper around fclose. On Android, this calls `android_fclose' to
clear information associated with the FILE's file descriptor if
necessary. */
int
emacs_fclose (FILE *stream)
{
#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return fclose (stream);
#else
return android_fclose (stream);
#endif
}
/* Maximum number of bytes to read or write in a single system call.
This works around a serious bug in Linux kernels before 2.6.16; see
<https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.

View File

@ -2354,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on it. */)
#ifndef MSDOS
if (f != t->display_info.tty->output)
fclose (t->display_info.tty->output);
fclose (f);
emacs_fclose (t->display_info.tty->output);
emacs_fclose (f);
#endif
t->display_info.tty->input = 0;
@ -4632,12 +4632,12 @@ delete_tty (struct terminal *terminal)
{
delete_keyboard_wait_descriptor (fileno (tty->input));
if (tty->input != stdin)
fclose (tty->input);
emacs_fclose (tty->input);
}
if (tty->output && tty->output != stdout && tty->output != tty->input)
fclose (tty->output);
emacs_fclose (tty->output);
if (tty->termscript)
fclose (tty->termscript);
emacs_fclose (tty->termscript);
xfree (tty->old_tty);
xfree (tty->Wcm);

View File

@ -83,5 +83,5 @@ AM_V_RC = @$(info $ RC $@)
# These are used for the Android port.
AM_V_JAVAC = @$(info $ JAVAC $@)
AM_V_DX = @$(info $ DX $@)
AM_V_D8 = @$(info $ D8 $@)
endif