* 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.
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
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
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])
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
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'
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
--- /dev/null
+<!-- @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>
# 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@
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
# 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 \
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) $<
# 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:
.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"
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
gdb_port=5039
jdb_port=64013
jdb=no
+attach_existing=no
while [ $# -gt 0 ]; do
case "$1" in
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:"
"--port" )
gdb_port=$1
;;
+ "--attach-existing" )
+ attach_existing=yes
+ ;;
"--" )
shift
gdbargs=$@
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
-
-# 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
+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"
+
+ # 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"`
/* The currently focused window. */
public static EmacsWindow focusedWindow;
+ /* Whether or not this activity is paused. */
+ private boolean isPaused;
+
static
{
focusedActivities = new ArrayList<EmacsActivity> ();
focusedWindow = window;
for (EmacsWindow child : window.children)
- invalidateFocus1 (window);
+ invalidateFocus1 (child);
}
public static void
/* 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;
public void
attachWindow (EmacsWindow child)
{
+ Log.d (TAG, "attachWindow: " + child);
+
if (window != null)
throw new IllegalStateException ("trying to attach window when one"
+ " already exists");
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 ();
}
{
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);
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 ();
+ }
};
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");
}
else
familyName = fileName;
-
- Log.d (TAG, "Initialized new typeface " + familyName);
}
@Override
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]);
}
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;
{
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;
+ }
};
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;
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
+ Log.d (TAG, "surfaceChanged: " + view);
view.swapBuffers ();
}
{
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
{
synchronized (surfaceChangeLock)
{
+ Log.d (TAG, "surfaceDestroyed: " + view);
created = false;
}
}
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. */
/* 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 (),
/* 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 ()
{
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))
}
}
- 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 ();
+ synchronized (damageRegion)
+ {
+ if (damageRegion.isEmpty ())
+ return;
- /* 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. */
+ bitmap = getBitmap ();
- synchronized (surfaceView.surfaceChangeLock)
- {
- damageRect = damageRegion.getBounds ();
+ /* 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. */
- if (!surfaceView.isCreated ())
- return;
+ synchronized (surfaceView.surfaceChangeLock)
+ {
+ damageRect = damageRegion.getBounds ();
- if (bitmap == null)
- return;
+ if (!surfaceView.isCreated ())
+ return;
- /* Lock the canvas with the specified damage. */
- canvas = surfaceView.lockCanvas (damageRect);
+ if (bitmap == null)
+ return;
- /* Return if locking the canvas failed. */
- if (canvas == null)
- return;
+ /* Lock the canvas with the specified damage. */
+ canvas = surfaceView.lockCanvas (damageRect);
- /* Copy from the back buffer to the canvas. If damageRect was
- made empty, then draw the entire back buffer. */
+ /* Return if locking the canvas failed. */
+ if (canvas == null)
+ return;
- if (damageRect.isEmpty ())
- canvas.drawBitmap (bitmap, 0f, 0f, paint);
- else
- canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+ /* Copy from the back buffer to the canvas. If damageRect was
+ made empty, then draw the entire back buffer. */
- /* Unlock the canvas and clear the damage. */
- surfaceView.unlockCanvasAndPost (canvas);
- damageRegion.setEmpty ();
+ 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 ();
+ }
}
}
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);
}
};
import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
+import java.util.HashMap;
import android.graphics.Rect;
import android.graphics.Canvas;
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;
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;
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)
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. */
if (parent != null)
{
parent.children.add (this);
- parent.view.post (new Runnable () {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
}
});
}
- 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);
}
+ "children!");
/* Remove the view from its parent and make it invisible. */
- view.post (new Runnable () {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
public void
run ()
{
parent = (View) view.getParent ();
- if (parent != null && attached == null)
+ if (parent != null)
((ViewGroup) parent).removeView (view);
manager.detachWindow (EmacsWindow.this);
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 ()
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);
}
});
}
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)
{
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;
+ }
};
package org.gnu.emacs;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
};
private List<WindowConsumer> consumers;
- private List<EmacsWindow> windows;
+ public List<EmacsWindow> windows;
public
EmacsWindowAttachmentManager ()
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)
consumers.remove (consumer);
}
- public void
+ public synchronized void
detachWindow (EmacsWindow window)
{
WindowConsumer consumer;
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);
}
};
(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.
(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)
(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.
(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))))))
(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.
(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)))))
(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).
((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.
;; 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.
;; (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.
((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)
(> 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)
(&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.
(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))))
(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."
(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."
(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.")
(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.")
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
/* 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;
"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",
/* 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);
}
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;
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
{
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;
}
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. */
"(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
}
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
\f
}
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
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);
+}
+
\f
/* Low level drawing primitives. */
\f
+/* 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);
+}
+
+\f
+
#undef faccessat
/* Replace the system faccessat with one which understands AT_EACCESS.
return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
}
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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. */
#ifndef ANDROID_STUBIFY
#include <jni.h>
#include <pwd.h>
+
#include <sys/stat.h>
+#include <dirent.h>
#include <android/bitmap.h>
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;
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);
+
+\f
+
+/* 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 *);
+
+\f
#endif
#include <math.h>
#include "lisp.h"
+#include "android.h"
#include "androidterm.h"
#include "blockinput.h"
#include "keyboard.h"
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)
if (! FONTP (font) && ! STRINGP (font))
{
const char *names[] = {
- "Droid Sans Mono",
- "monospace",
- "DroidSansMono",
+ "Droid Sans Mono-12",
+ "Monospace-12",
+ "DroidSansMono-12",
NULL
};
int i;
error ("Android cross-compilation stub called!");
return Qnil;
#else
- error ("Not implemented");
- return Qnil;
+ return make_fixnum (android_get_screen_width ());
#endif
}
error ("Android cross-compilation stub called!");
return Qnil;
#else
- error ("Not implemented");
- return Qnil;
+ return make_fixnum (android_get_screen_height ());
#endif
}
error ("Android cross-compilation stub called!");
return Qnil;
#else
- error ("Not implemented");
- return Qnil;
+ return make_fixnum (android_get_mm_width ());
#endif
}
error ("Android cross-compilation stub called!");
return Qnil;
#else
- error ("Not implemented");
- return Qnil;
+ return make_fixnum (android_get_mm_height ());
#endif
}
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");
- return Qnil;
+ 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
}
-DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry,
- 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
- (Lisp_Object terminal)
+#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
- error ("Not implemented");
+ 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-list-z-order", Fx_frame_list_z_order,
- Sx_frame_list_z_order, 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-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0,
- doc: /* SKIP: real doc in xfns.c. */)
+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.
+
+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;
}
#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
+}
+
\f
#ifndef ANDROID_STUBIFY
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 ();
}
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 ();
}
}
}
+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,
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
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);
}
-/* Communication module for Android terminals.
+/* Android fallback font driver.
Copyright (C) 2023 Free Software Foundation, Inc.
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"
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);
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
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;
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 *);
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
\f
#include <config.h>
#include <stdio.h>
+#include <math.h>
#include "lisp.h"
#include "androidterm.h"
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,
/* 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
{
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. */
|| 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);
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
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,
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
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? */
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;
}
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;
}
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. */
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 ();
}
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
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
}
}
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
static void
android_set_alpha (struct frame *f)
{
- /* TODO */
+ /* Not supported on Android. */
}
static Lisp_Object
{
struct android_display_info *dpyinfo;
Mouse_HLInfo *hlinfo;
+ struct android_touch_point *last, *next;
dpyinfo = FRAME_DISPLAY_INFO (f);
hlinfo = &dpyinfo->mouse_highlight;
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 ();
}
/* 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);
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. */
| 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);
}
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);
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;
{
/* 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);
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);
}
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);
}
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);
/* 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)
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);
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
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);
{
/* 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);
}
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);
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);
}
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);
}
{
/* 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);
| 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);
}
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;
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);
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);
}
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);
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);
}
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
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);
}
}
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);
}
}
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);
}
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);
}
}
}
/* 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);
}
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);
}
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);
}
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);
}
{
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);
}
{
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);
}
/* 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);
}
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. */
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;
/* 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
#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) \
#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
#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);
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.
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))
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
list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
}
- closedir (d);
+ emacs_closedir (d);
#ifdef WINDOWSNT
if (attrs)
Vw32_get_true_file_attributes = w32_save;
}
}
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. */
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;
}
if (tty->termscript != 0)
{
block_input ();
- fclose (tty->termscript);
+ emacs_fclose (tty->termscript);
tty->termscript = 0;
unblock_input ();
}
fclose_unwind (void *arg)
{
FILE *stream = arg;
- fclose (stream);
+ emacs_fclose (stream);
}
/* Restore point, having saved it as a marker. */
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);
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
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);
if (stream != NULL)
{
block_input ();
- fclose (stream);
+ emacs_fclose (stream);
unblock_input ();
}
}
[66] = "return",
[67] = "backspace",
[82] = "menu",
- [92] = "page-up",
- [93] = "page-down",
+ [92] = "prior",
+ [93] = "next",
};
#elif defined HAVE_NTGUI
if (dribble)
{
block_input ();
- fclose (dribble);
+ emacs_fclose (dribble);
unblock_input ();
dribble = 0;
}
#include <float.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdio.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
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);
{
struct infile *prev_infile = arg;
eassert (infile && infile != prev_infile);
- fclose (infile->stream);
+ emacs_fclose (infile->stream);
infile = prev_infile;
}
/* 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);
}
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. */
}
/* 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,
{
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.
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);
/* 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);
if (hmtx && head)
{
- if (!sfnt_lookup_glyph_metrics (code, 36,
+ if (!sfnt_lookup_glyph_metrics (code, 12,
&metrics,
hmtx, hhea,
head, maxp))
\f
-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.
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))
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
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));
}
/* 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
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. */
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")));
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)
{
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);
}
}
\f
-#ifndef HAVE_NTGUI
+#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY)
void
emacs_abort (void)
{
}
}
+/* 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>.
#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;
{
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);
# These are used for the Android port.
AM_V_JAVAC = @$(info $ JAVAC $@)
-AM_V_DX = @$(info $ DX $@)
+AM_V_D8 = @$(info $ D8 $@)
endif