summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2023-01-02 21:38:19 +0800
committerPo Lu <luangruo@yahoo.com>2023-01-02 21:38:19 +0800
commita32963e11f9f8e5d22b0d754d34a867f3b178ed2 (patch)
tree898f12248beb2feb63ed4295f9de40c89053ae19
parentfd074f3133a348dd1d3b7ee569f0fc046223efb9 (diff)
downloademacs-a32963e11f9f8e5d22b0d754d34a867f3b178ed2.tar.gz
Update Android port
* Makefile.in (java): Depend on info. (MAKEFILE_NAME): (config.status): Remove unneeded changes. * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a C++ compiler on Android. * java/AndroidManifest.xml: <EmacsActivity>: Set launchMode appropriately. <EmacsMultitaskActivity>: New activity. * java/Makefile.in (CROSS_BINS): Add EmacsClient. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity) (onCreate): Use the window attachment manager. * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea) (paintTo): Implement clip masks correctly. * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo): Fix damage tracking rectangles. * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString): New function. (FontMetrics, EmacsFontDriver): Fix signature of textExtents. * java/org/gnu/emacs/EmacsMultitaskActivity.java (EmacsMultitaskActivity): New file. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New functions sendFocusIn, sendFocusOut, sendWindowAction. * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping handling. * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add constructor for mutable pixmaps. * java/org/gnu/emacs/EmacsSdk23FontDriver.java (EmacsSdk23FontDriver): New file. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject) (checkMatch, hasChar, encodeChar): Implement text display and fix font metrics semantics. * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove availableChildren. (getLibraryDirectory, onCreate): Pass pixel density to Emacs. (clearArea): Fix arguments. Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged) (surfaceCreated): Flip buffers on surface attachment. * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): New argument FORCE. Always swap if it is true. (onKeyMultiple, onFocusChanged): New functions. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle) (run): Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): New file. * lisp/cus-edit.el (custom-button, custom-button-mouse) (custom-button-pressed): * lisp/faces.el (tool-bar): Define faces correctly on Android. * src/android.c (struct android_emacs_pixmap): Add mutable constructor. (struct android_emacs_drawable): New structure. (android_write_event): Check if event queue hasn't yet been initialized. (android_select): Set errno to EINTR if pselect fails. (android_close): Remove unused debugging code. (android_get_home_directory): New function. (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel density and compute game path. (android_init_emacs_drawable): New function. (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument `unicode_char'. Pass it in events. (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise. (Java_org_gnu_emacs_EmacsNative_sendFocusIn) (Java_org_gnu_emacs_EmacsNative_sendFocusOut) (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New functions. (android_resolve_handle): Export function. (android_change_gc): Clear clip rects under the right circumstances. Set right clip mask field. (android_create_pixmap_from_bitmap_data): Use correct alpha channels. (android_create_pixmap): Create mutable pixmap and avoid redundant color array allocation. (android_create_bitmap_from_data, android_create_image) (android_destroy_image, android_put_pixel, android_get_pixel) (android_get_image, android_put_image, faccessat): New functions. * src/android.h: Update prototypes. * src/androidfns.c (android_default_font_parameter): Prefer monospace to Droid Sans Mono. * src/androidfont.c (struct android_emacs_font_driver): New method `draw'. (struct android_emacs_font_spec): New field `dpi'. (struct androidfont_info): Add font metrics cache. (android_init_font_driver, android_init_font_spec): Adjust accordingly. (androidfont_from_lisp, androidfont_from_java): Handle new fields. (androidfont_draw): Implement function. (androidfont_open_font): Set pixel size correctly. (androidfont_close_font): Free metrics cache. (androidfont_cache_text_extents) (androidfont_check_cached_extents): New functions. (androidfont_text_extents): Cache glyph metrics somewhere for future use. (androidfont_list_family): Implement function. * src/androidgui.h (enum android_event_type): New focus and window action events. (enum android_modifier_mask): New masks. (struct android_key_event): New field `unicode_char'. (ANDROID_IS_MODIFIER_KEY): Newmacro. (struct android_focus_event, struct android_window_action_event): New structs. (union android_event): Add new fields. (enum android_image_format, struct android_image): New enums and structs. * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers, android_lower_frame) (android_raise_frame, android_new_focus_frame) (android_focus_changed, android_detect_focus_change): New functions. (handle_one_android_event): Implement focus and key event handling. (android_frame_rehighlight): New function. (android_frame_raise_lower): Implement accordingly. (android_make_frame_invisible): Clear highlight_frame if required. (android_free_frame_resources): Clear x_focus_event_frame if required. (android_draw_fringe_bitmap, android_draw_image_foreground) (android_draw_image_foreground_1) (android_draw_image_glyph_string): Remove unnecessary code. (android_create_terminal, android_term_init): Set the baud rate to something sensible. * src/androidterm.h (struct android_bitmap_record): Make structure the same as on X. (struct android_display_info): New focus tracking fields. (struct android_output): Likewise. * src/dispextern.h (struct image): Add ximg and mask_img on Android. * src/emacs.c (android_emacs_init): Fix argc sorting iteration. * src/fileio.c (user_homedir): (get_homedir): Implement correctly on Android. * src/font.h (PT_PER_INCH): Define correctly on Android. * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe bitmaps correctly on Android. * src/image.c (GET_PIXEL, image_create_bitmap_from_data) (image_create_bitmap_from_file, free_bitmap_record) (image_unget_x_image_or_dc, struct image_type) (prepare_image_for_display, image_clear_image_1) (image_size_in_bytes, x_check_image_size) (x_create_x_image_and_pixmap, x_destroy_x_image) (image_check_image_size, image_create_x_image_and_pixmap_1) (image_destroy_x_image, gui_put_x_image, image_put_x_image) (image_get_x_image, image_unget_x_image) (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross) (MaskForeground, image_types, syms_of_image): Implement all of the above on Android in terms of an API very similar to X. * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys): Define on Android to something sensible. * src/lread.c (build_load_history): Fix problem.
-rw-r--r--Makefile.in17
-rw-r--r--configure.ac20
-rw-r--r--java/AndroidManifest.xml9
-rw-r--r--java/Makefile.in3
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java153
-rw-r--r--java/org/gnu/emacs/EmacsCopyArea.java135
-rw-r--r--java/org/gnu/emacs/EmacsDrawRectangle.java8
-rw-r--r--java/org/gnu/emacs/EmacsFontDriver.java26
-rw-r--r--java/org/gnu/emacs/EmacsMultitaskActivity.java25
-rw-r--r--java/org/gnu/emacs/EmacsNative.java18
-rw-r--r--java/org/gnu/emacs/EmacsPaintQueue.java24
-rw-r--r--java/org/gnu/emacs/EmacsPixmap.java29
-rw-r--r--java/org/gnu/emacs/EmacsSdk23FontDriver.java43
-rw-r--r--java/org/gnu/emacs/EmacsSdk7FontDriver.java188
-rw-r--r--java/org/gnu/emacs/EmacsService.java83
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java37
-rw-r--r--java/org/gnu/emacs/EmacsView.java45
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java143
-rw-r--r--java/org/gnu/emacs/EmacsWindowAttachmentManager.java166
-rw-r--r--lisp/cus-edit.el6
-rw-r--r--lisp/faces.el2
-rw-r--r--src/android.c627
-rw-r--r--src/android.h15
-rw-r--r--src/androidfns.c5
-rw-r--r--src/androidfont.c270
-rw-r--r--src/androidgui.h81
-rw-r--r--src/androidterm.c544
-rw-r--r--src/androidterm.h30
-rw-r--r--src/dispextern.h11
-rw-r--r--src/emacs.c5
-rw-r--r--src/fileio.c9
-rw-r--r--src/font.h6
-rw-r--r--src/fringe.c23
-rw-r--r--src/image.c249
-rw-r--r--src/keyboard.c53
-rw-r--r--src/lread.c20
36 files changed, 2435 insertions, 693 deletions
diff --git a/Makefile.in b/Makefile.in
index d8c5ba555d3..6ca1b1dc382 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -536,7 +536,7 @@ lisp: src
lib lib-src lisp nt: Makefile
$(MAKE) -C $@ all
-java: lisp
+java: lisp info
$(MAKE) -C $@ all
xcompile: src
@@ -571,20 +571,10 @@ blessmail: Makefile src
# then attempts to build that file. This forces 'Makefile', 'lib/Makefile',
# etc. to be built without running into similar recursion problems.
MAKEFILE_NAME = Makefile
-ifeq ($(ANDROID),)
$(MAKEFILE_NAME): config.status $(srcdir)/configure \
$(srcdir)/lib/gnulib.mk.in \
$(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
MAKE='$(MAKE)' ./config.status
-else
-# Note that calling config.status is insufficient on Android due to
-# the recursive calls to configure.
-
-$(MAKEFILE_NAME): $(srcdir)/configure \
- $(srcdir)/lib/gnulib.mk.in \
- $(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
- $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS);
-endif
# Don't erase these files if make is interrupted while refreshing them.
.PRECIOUS: Makefile config.status
@@ -592,17 +582,12 @@ endif
# Note that calling config.status --recheck is insufficient on Android
# due to the recursive calls to configure.
-ifneq ($(ANDROID),)
-config.status: ${srcdir}/configure
- $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS)
-else
config.status: ${srcdir}/configure
if [ -x ./config.status ]; then \
$(CFG) ./config.status --recheck; \
else \
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
fi
-endif
$(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/m4/*.m4
cd $(srcdir) && ./autogen.sh autoconf
diff --git a/configure.ac b/configure.ac
index da8d753afca..70dbfa10b35 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,9 +31,8 @@ if test "$XCONFIGURE" = "android"; then
# Android!
AC_MSG_NOTICE([called to recursively configure Emacs \
for Android.])
- # Set CC to ANDROID_CC, and CXX to ANDROID_CXX.
+ # Set CC to ANDROID_CC.
CC=$ANDROID_CC
- CXX=$ANDROID_CXX
fi
dnl Set emacs_config_options to the options of 'configure', quoted for the shell,
@@ -147,7 +146,7 @@ if test "$XCONFIGURE" = "android"; then
;;
*) AC_MSG_ERROR([The cross compiler does not compile for Android.
Please verify that you specified the correct compiler in the ANDROID_CC
-and ANDROID_CXX variables when you ran configure.])
+variable when you ran configure.])
;;
esac
AC_MSG_RESULT([$host_alias])
@@ -759,8 +758,7 @@ tools such as aapt, dx, and aidl):
The cross-compiler should then be specified:
- ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang
- ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++])
+ ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang])
elif test "$with_android" = "no" || test "$with_android" = ""; then
ANDROID=no
else
@@ -833,12 +831,11 @@ Please verify that the path to the SDK build tools you specified is correct]);
dnl Now configure Emacs to generate binaries for Android. After the
dnl configuration completes, move the generated Makefiles.
- if test "$ANDROID_CC" = "" || test "$ANDROID_CXX" = ""; then
+ if test "$ANDROID_CC" = ""; then
AC_MSG_ERROR([Please specify the path to the Android cross-compiler
for your machine. For example:
ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang \\
- ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++ \\
./configure --with-android])
fi
@@ -863,7 +860,7 @@ for your machine. For example:
*) AC_MSG_ERROR([configure could not determine the type of Android \
binary Emacs is being configured for. Please port this configure script \
to your Android system, or verify that you specified the correct compiler \
-in the ANDROID_CC and ANDROID_CXX variables when you ran configure.])
+in the ANDROID_CC variable when you ran configure.])
;;
esac
AC_MSG_RESULT([$android_abi])
@@ -874,9 +871,8 @@ in the ANDROID_CC and ANDROID_CXX variables when you ran configure.])
mv -f confdefs.h _confdefs.h
mv -f config.log _config.log
- AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
- ANDROID_CXX="$ANDROID_CXX" $0], [], [AC_MSG_ERROR([Failed to cross-\
-configure Emacs for android.])])
+ AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" $0], [],
+ [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
# Now set ANDROID to yes.
ANDROID=yes
@@ -2254,7 +2250,7 @@ for Android, but all API calls need to be stubbed out])
ANDROID_CFLAGS="-fPIC -fvisibility=hidden"
# Link with libraries required for Android support.
- ANDROID_LIBS="-landroid -llog"
+ ANDROID_LIBS="-landroid -llog -ljnigraphics"
fi
fi
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 75aa5bdf409..06417017ae3 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -31,13 +31,15 @@
android:targetSdkVersion="28"/>
<application android:name="org.gnu.emacs.EmacsApplication"
- android:label="GNU Emacs"
+ 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">
+ <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" />
@@ -45,6 +47,9 @@
</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"
diff --git a/java/Makefile.in b/java/Makefile.in
index e9fcc625cb2..31bd22b5a2e 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -60,7 +60,7 @@ all: emacs.apk
# Binaries to cross-compile.
CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
- ../xcompile/lib-src/ebrowse
+ ../xcompile/lib-src/ebrowse ../xcompile/lib-src/emacsclient
# Libraries to cross-compile.
CROSS_LIBS = ../xcompile/src/libemacs.so
@@ -84,6 +84,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
# Install architecture independents to assets/etc and assets/lisp
cp -r $(top_builddir)/lisp install_temp/assets
cp -r $(top_builddir)/etc install_temp/assets
+ cp -r $(top_builddir)/info install_temp/assets
# Remove undesirable files from those directories.
for subdir in `find install_temp -type d -print`; do \
chmod a+rx $${subdir} ; \
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
index cacd5f13e60..67f411a38c3 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -32,65 +32,114 @@ import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
public class EmacsActivity extends Activity
+ implements EmacsWindowAttachmentManager.WindowConsumer
{
public static final String TAG = "EmacsActivity";
- /* List of all activities that do not have an associated
- EmacsWindow. */
- public static List<EmacsActivity> availableActivities;
-
/* The currently attached EmacsWindow, or null if none. */
private EmacsWindow window;
/* The frame layout associated with the activity. */
private FrameLayout layout;
+ /* List of activities with focus. */
+ private static List<EmacsActivity> focusedActivities;
+
+ /* The currently focused window. */
+ public static EmacsWindow focusedWindow;
+
static
{
- /* Set up the list of available activities. */
- availableActivities = new ArrayList<EmacsActivity> ();
+ focusedActivities = new ArrayList<EmacsActivity> ();
};
+ public static void
+ invalidateFocus1 (EmacsWindow window)
+ {
+ if (window.view.isFocused ())
+ focusedWindow = window;
+
+ for (EmacsWindow child : window.children)
+ invalidateFocus1 (window);
+ }
+
+ public static void
+ invalidateFocus ()
+ {
+ EmacsWindow oldFocus;
+
+ /* Walk through each focused activity and assign the window focus
+ to the bottom-most focused window within. Record the old focus
+ as well. */
+ oldFocus = focusedWindow;
+ focusedWindow = null;
+
+ for (EmacsActivity activity : focusedActivities)
+ {
+ if (activity.window != null)
+ invalidateFocus1 (activity.window);
+ }
+
+ /* Send focus in- and out- events to the previous and current
+ focus. */
+
+ if (oldFocus != null)
+ EmacsNative.sendFocusOut (oldFocus.handle,
+ System.currentTimeMillis ());
+
+ if (focusedWindow != null)
+ EmacsNative.sendFocusIn (focusedWindow.handle,
+ System.currentTimeMillis ());
+ }
+
+ @Override
public void
- attachChild (EmacsWindow child)
+ detachWindow ()
+ {
+ if (window == null)
+ Log.w (TAG, "detachWindow called, but there is no window");
+ else
+ {
+ /* Clear the window's pointer to this activity and remove the
+ window's view. */
+ window.setConsumer (null);
+ layout.removeView (window.view);
+ window = null;
+
+ invalidateFocus ();
+ }
+ }
+
+ @Override
+ public void
+ attachWindow (EmacsWindow child)
{
if (window != null)
throw new IllegalStateException ("trying to attach window when one"
+ " already exists");
/* Record and attach the view. */
+
window = child;
layout.addView (window.view);
+ child.setConsumer (this);
- /* Remove the objects from the lists of what is available. */
- EmacsService.availableChildren.remove (child);
- availableActivities.remove (this);
-
- /* Now set child->activity. */
- child.setActivity (this);
+ /* Invalidate the focus. */
+ invalidateFocus ();
}
- /* Make this activity available for future windows to attach
- again. */
-
+ @Override
public void
- makeAvailable ()
+ destroy ()
{
- window = null;
-
- for (EmacsWindow iterWindow
- : EmacsService.availableChildren)
- {
- synchronized (iterWindow)
- {
- if (!iterWindow.isDestroyed ())
- attachChild (iterWindow);
-
- return;
- }
- }
+ finish ();
+ }
- availableActivities.add (this);
+ @Override
+ public EmacsWindow
+ getAttachedWindow ()
+ {
+ return window;
}
@Override
@@ -109,38 +158,38 @@ public class EmacsActivity extends Activity
/* Set it as the content view. */
setContentView (layout);
- /* Make the activity available before starting the
- service. */
- makeAvailable ();
-
if (EmacsService.SERVICE == null)
/* Start the Emacs service now. */
startService (new Intent (this, EmacsService.class));
+ /* Add this activity to the list of available activities. */
+ EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
super.onCreate (savedInstanceState);
}
@Override
public void
- onStop ()
+ onDestroy ()
{
- /* The activity is no longer visible. If there is a window
- attached, detach it. */
-
- if (window != null)
- {
- layout.removeView (window.view);
+ /* The activity will die shortly hereafter. If there is a window
+ attached, close it now. */
+ Log.d (TAG, "onDestroy " + this);
+ EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this);
+ focusedActivities.remove (this);
+ invalidateFocus ();
+ super.onDestroy ();
+ }
- /* Notice that the window is already available too. But do
- not call noticeAvailableChild; that might assign it to some
- other activity, which behaves badly. */
- EmacsService.availableChildren.add (window);
- window = null;
- }
+ @Override
+ public void
+ onWindowFocusChanged (boolean isFocused)
+ {
+ if (isFocused && !focusedActivities.contains (this))
+ focusedActivities.add (this);
+ else
+ focusedActivities.remove (this);
- /* Finally, remove this activity from the list of available
- activities. */
- availableActivities.remove (this);
- super.onStop ();
+ invalidateFocus ();
}
};
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java
index f34d1ecde01..0dd5b2c1fb6 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -32,19 +32,22 @@ public class EmacsCopyArea implements EmacsPaintReq
private int src_x, src_y, dest_x, dest_y, width, height;
private EmacsDrawable destination, source;
private EmacsGC immutableGC;
- private static Xfermode xorAlu, srcInAlu;
+ private static Xfermode xorAlu, srcInAlu, overAlu;
static
{
+ overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public
- EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source,
+ EmacsCopyArea (EmacsDrawable source, EmacsDrawable destination,
int src_x, int src_y, int width, int height,
int dest_x, int dest_y, EmacsGC immutableGC)
{
+ Bitmap bitmap;
+
this.destination = destination;
this.source = source;
this.src_x = src_x;
@@ -71,6 +74,16 @@ public class EmacsCopyArea implements EmacsPaintReq
return destination;
}
+ private void
+ insetRectBy (Rect rect, int left, int top, int right,
+ int bottom)
+ {
+ rect.left += left;
+ rect.top += top;
+ rect.right -= right;
+ rect.bottom -= bottom;
+ }
+
@Override
public EmacsGC
getGC ()
@@ -86,16 +99,45 @@ public class EmacsCopyArea implements EmacsPaintReq
Bitmap bitmap;
Paint maskPaint;
Canvas maskCanvas;
- Bitmap maskBitmap;
- Rect rect, srcRect;
+ Bitmap srcBitmap, maskBitmap, clipBitmap;
+ Rect rect, maskRect, srcRect, dstRect, maskDestRect;
+ boolean needFill;
/* TODO implement stippling. */
if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
return;
alu = immutableGC.function;
+
+ /* A copy must be created or drawBitmap could end up overwriting
+ itself. */
+ srcBitmap = source.getBitmap ();
+
+ /* If srcBitmap is out of bounds, then adjust the source rectangle
+ to be within bounds. Note that tiling on windows with
+ backgrounds is unimplemented. */
+
+ if (src_x < 0)
+ {
+ width += src_x;
+ dest_x -= src_x;
+ src_x = 0;
+ }
+
+ if (src_y < 0)
+ {
+ height += src_y;
+ dest_y -= src_y;
+ src_y = 0;
+ }
+
+ if (src_x + width > srcBitmap.getWidth ())
+ width = srcBitmap.getWidth () - src_x;
+
+ if (src_y + height > srcBitmap.getHeight ())
+ height = srcBitmap.getHeight () - src_y;
+
rect = getRect ();
- bitmap = source.getBitmap ();
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
@@ -103,29 +145,76 @@ public class EmacsCopyArea implements EmacsPaintReq
paint.setXfermode (xorAlu);
if (immutableGC.clip_mask == null)
- canvas.drawBitmap (bitmap, new Rect (src_x, src_y,
- src_x + width,
- src_y + height),
- rect, paint);
+ {
+ bitmap = Bitmap.createBitmap (srcBitmap,
+ src_x, src_y, width,
+ height);
+ canvas.drawBitmap (bitmap, null, rect, paint);
+ }
else
{
- maskPaint = new Paint ();
- srcRect = new Rect (0, 0, rect.width (),
- rect.height ());
- maskBitmap
- = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
- true);
-
- if (maskBitmap == null)
+ /* Drawing with a clip mask involves calculating the
+ intersection of the clip mask with the dst rect, and
+ extrapolating the corresponding part of the src rect. */
+ clipBitmap = immutableGC.clip_mask.bitmap;
+ dstRect = new Rect (dest_x, dest_y,
+ dest_x + width,
+ dest_y + height);
+ maskRect = new Rect (immutableGC.clip_x_origin,
+ immutableGC.clip_y_origin,
+ (immutableGC.clip_x_origin
+ + clipBitmap.getWidth ()),
+ (immutableGC.clip_y_origin
+ + clipBitmap.getHeight ()));
+ clipBitmap = immutableGC.clip_mask.bitmap;
+
+ if (!maskRect.setIntersect (dstRect, maskRect))
+ /* There is no intersection between the clip mask and the
+ dest rect. */
return;
- maskPaint.setXfermode (srcInAlu);
+ /* Now figure out which part of the source corresponds to
+ maskRect and return it relative to srcBitmap. */
+ srcRect = new Rect (src_x, src_y, src_x + width,
+ src_y + height);
+ insetRectBy (srcRect, maskRect.left - dstRect.left,
+ maskRect.top - dstRect.top,
+ maskRect.right - dstRect.right,
+ maskRect.bottom - dstRect.bottom);
+
+ /* Finally, create a temporary bitmap that is the size of
+ maskRect. */
+
+ maskBitmap
+ = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+ Bitmap.Config.ARGB_8888);
+
+ /* Draw the mask onto the maskBitmap. */
maskCanvas = new Canvas (maskBitmap);
- maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y,
- src_x + width,
- src_y + height),
- srcRect, maskPaint);
- canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+ maskRect.offset (-immutableGC.clip_x_origin,
+ -immutableGC.clip_y_origin);
+ maskCanvas.drawBitmap (immutableGC.clip_mask.bitmap,
+ maskRect, new Rect (0, 0,
+ maskRect.width (),
+ maskRect.height ()),
+ paint);
+ maskRect.offset (immutableGC.clip_x_origin,
+ immutableGC.clip_y_origin);
+
+ /* Set the transfer mode to SRC_IN to preserve only the parts
+ of the source that overlap with the mask. */
+ maskPaint = new Paint ();
+ maskPaint.setXfermode (srcInAlu);
+
+ /* Draw the source. */
+ maskDestRect = new Rect (0, 0, srcRect.width (),
+ srcRect.height ());
+ maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect,
+ maskPaint);
+
+ /* Finally, draw the mask bitmap to the destination. */
+ paint.setXfermode (overAlu);
+ canvas.drawBitmap (maskBitmap, null, maskRect, paint);
}
}
}
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java
index 462bf7c85b5..e3f28227146 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -57,7 +57,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
public Rect
getRect ()
{
- return new Rect (x, y, x + width, y + height);
+ /* Canvas.drawRect actually behaves exactly like PolyRectangle wrt
+ to where the lines are placed, so extend the width and height
+ by 1 in the damage rectangle. */
+ return new Rect (x, y, x + width + 1, y + height + 1);
}
@Override
@@ -89,9 +92,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
return;
alu = immutableGC.function;
- rect = getRect ();
+ rect = new Rect (x, y, x + width, y + height);
paint.setStyle (Paint.Style.STROKE);
+ paint.setStrokeWidth (1);
if (alu == EmacsGC.GC_COPY)
paint.setXfermode (null);
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java
index f419e71059d..9f40aa04c44 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -21,6 +21,8 @@ package org.gnu.emacs;
import java.util.List;
+import android.os.Build;
+
public abstract class EmacsFontDriver
{
/* Font weights. */
@@ -75,6 +77,7 @@ public abstract class EmacsFontDriver
public Integer size;
public Integer spacing;
public Integer avgwidth;
+ public Integer dpi;
@Override
public String
@@ -88,7 +91,8 @@ public abstract class EmacsFontDriver
+ " weight: " + weight
+ " slant: " + slant
+ " spacing: " + spacing
- + " avgwidth: " + avgwidth);
+ + " avgwidth: " + avgwidth
+ + " dpi: " + dpi);
}
};
@@ -99,6 +103,17 @@ public abstract class EmacsFontDriver
public short width;
public short ascent;
public short descent;
+
+ @Override
+ public String
+ toString ()
+ {
+ return ("lbearing " + lbearing
+ + " rbearing " + rbearing
+ + " width " + width
+ + " ascent " + ascent
+ + " descent " + descent);
+ }
}
public class FontEntity extends FontSpec
@@ -139,12 +154,19 @@ public abstract class EmacsFontDriver
public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
public abstract int hasChar (FontSpec font, char charCode);
public abstract void textExtents (FontObject font, int code[],
- FontMetrics fontMetrics[]);
+ FontMetrics fontMetrics);
public abstract int encodeChar (FontObject fontObject, char charCode);
+ public abstract int draw (FontObject fontObject, EmacsGC gc,
+ EmacsDrawable drawable, int[] chars,
+ int x, int y, int backgroundWidth,
+ boolean withBackground);
public static EmacsFontDriver
createFontDriver ()
{
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
+ return new EmacsSdk23FontDriver ();
+
return new EmacsSdk7FontDriver ();
}
};
diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java
new file mode 100644
index 00000000000..dbdc99a3559
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java
@@ -0,0 +1,25 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+package org.gnu.emacs;
+
+public class EmacsMultitaskActivity extends EmacsActivity
+{
+
+}
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 6550e6fa2a1..c80339031a8 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -38,10 +38,15 @@ public class EmacsNative
libDir must be the package's data storage location for native
libraries. It is used as PATH.
+ pixelDensityX and pixelDensityY are the DPI values that will be
+ used by Emacs.
+
emacsService must be the emacsService singleton. */
public static native void setEmacsParams (AssetManager assetManager,
String filesDir,
String libDir,
+ float pixelDensityX,
+ float pixelDensityY,
EmacsService emacsService);
/* Initialize Emacs with the argument array ARGV. Each argument
@@ -59,11 +64,20 @@ public class EmacsNative
/* Send an ANDROID_KEY_PRESS event. */
public static native void sendKeyPress (short window, long time, int state,
- int keyCode);
+ int keyCode, int unicodeChar);
/* Send an ANDROID_KEY_RELEASE event. */
public static native void sendKeyRelease (short window, long time, int state,
- int keyRelease);
+ int keyCode, int unicodeChar);
+
+ /* Send an ANDROID_FOCUS_IN event. */
+ public static native void sendFocusIn (short window, long time);
+
+ /* Send an ANDROID_FOCUS_OUT event. */
+ public static native void sendFocusOut (short window, long time);
+
+ /* Send an ANDROID_WINDOW_ACTION event. */
+ public static native void sendWindowAction (short window, int action);
static
{
diff --git a/java/org/gnu/emacs/EmacsPaintQueue.java b/java/org/gnu/emacs/EmacsPaintQueue.java
index 5af5868d3b9..f4840dbf5ae 100644
--- a/java/org/gnu/emacs/EmacsPaintQueue.java
+++ b/java/org/gnu/emacs/EmacsPaintQueue.java
@@ -47,7 +47,7 @@ public class EmacsPaintQueue
{
EmacsDrawable drawable, last;
Canvas canvas;
- EmacsGC gc, lastGC;
+ EmacsGC gc;
int i;
Paint paint;
Rect rect, offsetRect, copyRect;
@@ -60,45 +60,34 @@ public class EmacsPaintQueue
for (EmacsPaintReq req : paintOperations)
{
drawable = req.getDrawable ();
-
- synchronized (req)
- {
- /* Ignore graphics requests for drawables that have been
- destroyed. */
- if (drawable.isDestroyed ())
- continue;
- }
-
canvas = drawable.lockCanvas ();
if (canvas == null)
/* No canvas is currently available. */
continue;
- lastGC = gc;
gc = req.getGC ();
rect = req.getRect ();
+ drawable.damageRect (rect);
+
if (gc.clip_rects == null)
{
/* No clipping is applied. Just draw and continue. */
- canvas.save ();
req.paintTo (canvas, paint, gc);
- canvas.restore ();
- drawable.damageRect (rect);
continue;
}
if (gc.clip_rects != null && gc.clip_rects.length > 0)
{
- canvas.save ();
-
if (gc.clip_rects.length == 1)
{
/* There is only a single clip rect, which is simple
enough. */
+ canvas.save ();
canvas.clipRect (gc.clip_rects[0]);
req.paintTo (canvas, paint, gc);
+ canvas.restore ();
}
else
{
@@ -122,9 +111,6 @@ public class EmacsPaintQueue
}
}
}
-
- drawable.damageRect (rect);
- canvas.restore ();
}
}
}
diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java
index ccd5f1e0043..897902c5b57 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -69,6 +69,35 @@ public class EmacsPixmap extends EmacsHandleObject
this.depth = depth;
}
+ public
+ EmacsPixmap (short handle, int width, int height, int depth)
+ {
+ super (handle);
+
+ if (depth != 1 && depth != 24)
+ throw new IllegalArgumentException ("Invalid depth specified"
+ + " for pixmap: " + depth);
+
+ switch (depth)
+ {
+ case 1:
+ bitmap = Bitmap.createBitmap (width, height,
+ Bitmap.Config.ALPHA_8,
+ false);
+ break;
+
+ case 24:
+ bitmap = Bitmap.createBitmap (width, height,
+ Bitmap.Config.ARGB_8888,
+ false);
+ break;
+ }
+
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ }
+
@Override
public Canvas
lockCanvas ()
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
new file mode 100644
index 00000000000..34e2b1803a2
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -0,0 +1,43 @@
+/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Paint;
+
+public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
+{
+ @Override
+ public int
+ hasChar (FontSpec font, char charCode)
+ {
+ Sdk7FontObject fontObject;
+ Paint paint;
+
+ if (font instanceof Sdk7FontObject)
+ {
+ fontObject = (Sdk7FontObject) font;
+ paint = fontObject.typeface.typefacePaint;
+ }
+ else
+ paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+ return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 5a8cdbfc75b..437f38e62db 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -28,16 +28,19 @@ import java.util.List;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.Canvas;
import android.util.Log;
+import android.os.Build;
+
public class EmacsSdk7FontDriver extends EmacsFontDriver
{
private static final String TOFU_STRING = "\uDB3F\uDFFD";
private static final String EM_STRING = "m";
private static final String TAG = "EmacsSdk7FontDriver";
- private class Sdk7Typeface
+ protected class Sdk7Typeface
{
/* The typeface and paint. */
public Typeface typeface;
@@ -57,7 +60,10 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
width = UNSPECIFIED;
spacing = PROPORTIONAL;
+ this.typeface = typeface;
+
typefacePaint = new Paint ();
+ typefacePaint.setAntiAlias (true);
typefacePaint.setTypeface (typeface);
/* For the calls to measureText below. */
@@ -160,7 +166,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
}
};
- private class Sdk7FontEntity extends FontEntity
+ protected class Sdk7FontEntity extends FontEntity
{
/* The typeface. */
public Sdk7Typeface typeface;
@@ -177,19 +183,17 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
+ dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
this.typeface = typeface;
}
};
- private class Sdk7FontObject extends FontObject
+ protected class Sdk7FontObject extends FontObject
{
/* The typeface. */
public Sdk7Typeface typeface;
- /* The text size. */
- public int pixelSize;
-
public
Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
{
@@ -205,6 +209,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
+ dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
/* Compute the ascent and descent. */
typeface.typefacePaint.setTextSize (pixelSize);
@@ -238,6 +243,93 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
}
};
+ private class Sdk7DrawString implements EmacsPaintReq
+ {
+ private boolean drawBackground;
+ private Sdk7FontObject fontObject;
+ private char[] chars;
+ private EmacsGC immutableGC;
+ private EmacsDrawable drawable;
+ private Rect rect;
+ private int originX, originY;
+
+ public
+ Sdk7DrawString (Sdk7FontObject fontObject, char[] chars,
+ EmacsGC immutableGC, EmacsDrawable drawable,
+ boolean drawBackground, Rect rect,
+ int originX, int originY)
+ {
+ this.fontObject = fontObject;
+ this.chars = chars;
+ this.immutableGC = immutableGC;
+ this.drawable = drawable;
+ this.drawBackground = drawBackground;
+ this.rect = rect;
+ this.originX = originX;
+ this.originY = originY;
+ }
+
+ @Override
+ public EmacsDrawable
+ getDrawable ()
+ {
+ return drawable;
+ }
+
+ @Override
+ public EmacsGC
+ getGC ()
+ {
+ return immutableGC;
+ }
+
+ @Override
+ public void
+ paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+ {
+ int scratch;
+
+ paint.setStyle (Paint.Style.FILL);
+
+ if (drawBackground)
+ {
+ paint.setColor (immutableGC.background | 0xff000000);
+ canvas.drawRect (rect, paint);
+ }
+
+ paint.setTextSize (fontObject.pixelSize);
+ paint.setColor (immutableGC.foreground | 0xff000000);
+ paint.setTypeface (fontObject.typeface.typeface);
+ paint.setAntiAlias (true);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ /* Disable hinting as that leads to displayed text not
+ matching the computed metrics. */
+ paint.setHinting (Paint.HINTING_OFF);
+
+ canvas.drawText (chars, 0, chars.length, originX, originY, paint);
+ paint.setAntiAlias (false);
+ }
+
+ @Override
+ public Rect
+ getRect ()
+ {
+ Rect rect;
+
+ rect = new Rect ();
+
+ fontObject.typeface.typefacePaint.setTextSize (fontObject.pixelSize);
+ fontObject.typeface.typefacePaint.getTextBounds (chars, 0, chars.length,
+ rect);
+
+ /* Add the background rect to the damage as well. */
+ rect.union (this.rect);
+
+ return rect;
+ }
+ };
+
private String[] fontFamilyList;
private Sdk7Typeface[] typefaceList;
private Sdk7Typeface fallbackTypeface;
@@ -252,7 +344,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
systemFontsDirectory = new File ("/system/fonts");
fontFamilyList = systemFontsDirectory.list ();
- typefaceList = new Sdk7Typeface[fontFamilyList.length];
+ typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
/* It would be nice to avoid opening each and every font upon
startup. But that doesn't seem to be possible on
@@ -267,8 +359,18 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
typeface);
}
+ /* Initialize the default monospace and serif typefaces. */
fallbackTypeface = new Sdk7Typeface ("monospace",
Typeface.MONOSPACE);
+ typefaceList[fontFamilyList.length] = fallbackTypeface;
+
+ fallbackTypeface = new Sdk7Typeface ("Monospace",
+ Typeface.MONOSPACE);
+ typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
+
+ fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+ Typeface.DEFAULT);
+ typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
}
private boolean
@@ -278,11 +380,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
&& !fontSpec.family.equals (typeface.familyName))
return false;
-
- if (fontSpec.adstyle != null
- && !fontSpec.adstyle.isEmpty ())
- /* return false; */;
-
if (fontSpec.slant != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
@@ -393,7 +490,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
rect1);
paint.getTextBounds ("" + charCode, 0, 1, rect2);
- return rect1.equals (rect2) ? 1 : 0;
+ return rect1.equals (rect2) ? 0 : 1;
}
private void
@@ -434,21 +531,47 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
@Override
public void
- textExtents (FontObject font, int code[], FontMetrics fontMetrics[])
+ textExtents (FontObject font, int code[], FontMetrics fontMetrics)
{
int i;
Paint paintCache;
Rect boundsCache;
Sdk7FontObject fontObject;
+ char[] text;
+ float width;
fontObject = (Sdk7FontObject) font;
paintCache = fontObject.typeface.typefacePaint;
paintCache.setTextSize (fontObject.pixelSize);
boundsCache = new Rect ();
- for (i = 0; i < code.length; ++i)
- textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i],
+ if (code.length == 0)
+ {
+ fontMetrics.lbearing = 0;
+ fontMetrics.rbearing = 0;
+ fontMetrics.ascent = 0;
+ fontMetrics.descent = 0;
+ fontMetrics.width = 0;
+ }
+ else if (code.length == 1)
+ textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
paintCache, boundsCache);
+ else
+ {
+ text = new char[code.length];
+
+ for (i = 0; i < code.length; ++i)
+ text[i] = (char) code[i];
+
+ paintCache.getTextBounds (text, 0, 1, boundsCache);
+ width = paintCache.measureText (text, 0, code.length);
+
+ fontMetrics.lbearing = (short) boundsCache.left;
+ fontMetrics.rbearing = (short) boundsCache.right;
+ fontMetrics.ascent = (short) -boundsCache.top;
+ fontMetrics.descent = (short) boundsCache.bottom;
+ fontMetrics.width = (short) Math.round (width);
+ }
}
@Override
@@ -457,4 +580,37 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
{
return charCode;
}
+
+ @Override
+ public int
+ draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
+ int[] chars, int x, int y, int backgroundWidth,
+ boolean withBackground)
+ {
+ Rect backgroundRect;
+ Sdk7FontObject sdk7FontObject;
+ Sdk7DrawString op;
+ char[] charsArray;
+ int i;
+
+ sdk7FontObject = (Sdk7FontObject) fontObject;
+ charsArray = new char[chars.length];
+
+ for (i = 0; i < chars.length; ++i)
+ charsArray[i] = (char) chars[i];
+
+ backgroundRect = new Rect ();
+ backgroundRect.top = y - sdk7FontObject.ascent;
+ backgroundRect.left = x;
+ backgroundRect.right = x + backgroundWidth;
+ backgroundRect.bottom = y + sdk7FontObject.descent;
+
+ op = new Sdk7DrawString (sdk7FontObject, charsArray,
+ gc.immutableGC (), drawable,
+ withBackground,
+ backgroundRect, x, y);
+
+ EmacsService.SERVICE.appendPaintOperation (op);
+ return 1;
+ }
};
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index 311226e6f7e..41a45b0bd85 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -28,6 +28,8 @@ import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.view.View;
+
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
@@ -37,7 +39,9 @@ import android.os.Build;
import android.os.Looper;
import android.os.IBinder;
import android.os.Handler;
+
import android.util.Log;
+import android.util.DisplayMetrics;
class Holder<T>
{
@@ -57,14 +61,8 @@ public class EmacsService extends Service
private Handler handler;
private EmacsPaintQueue paintQueue;
- /* List of all EmacsWindows that are available to attach to an
- activity. */
- public static List<EmacsWindow> availableChildren;
-
- static
- {
- availableChildren = new ArrayList<EmacsWindow> ();
- };
+ /* Display metrics used by font backends. */
+ public DisplayMetrics metrics;
@Override
public int
@@ -88,7 +86,7 @@ public class EmacsService extends Service
Context context;
context = getApplicationContext ();
- apiLevel = android.os.Build.VERSION.SDK_INT;
+ apiLevel = Build.VERSION.SDK_INT;
if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
return context.getApplicationInfo().nativeLibraryDir;
@@ -105,11 +103,16 @@ public class EmacsService extends Service
AssetManager manager;
Context app_context;
String filesDir, libDir;
+ double pixelDensityX;
+ double pixelDensityY;
SERVICE = this;
handler = new Handler (Looper.getMainLooper ());
manager = getAssets ();
app_context = getApplicationContext ();
+ metrics = getResources ().getDisplayMetrics ();
+ pixelDensityX = metrics.xdpi;
+ pixelDensityY = metrics.ydpi;
try
{
@@ -122,6 +125,8 @@ public class EmacsService extends Service
+ " and libDir = " + libDir);
EmacsNative.setEmacsParams (manager, filesDir, libDir,
+ (float) pixelDensityX,
+ (float) pixelDensityY,
this);
/* Start the thread that runs Emacs. */
@@ -147,7 +152,8 @@ public class EmacsService extends Service
}
EmacsView
- getEmacsView (final EmacsWindow window)
+ getEmacsView (final EmacsWindow window, final int visibility,
+ final boolean isFocusedByDefault)
{
Runnable runnable;
final Holder<EmacsView> view;
@@ -161,6 +167,8 @@ public class EmacsService extends Service
synchronized (this)
{
view.thing = new EmacsView (window);
+ view.thing.setVisibility (visibility);
+ view.thing.setFocusedByDefault (isFocusedByDefault);
notify ();
}
}
@@ -183,48 +191,6 @@ public class EmacsService extends Service
return view.thing;
}
- /* Notice that a child of the root window named WINDOW is now
- available for attachment to a specific activity. */
-
- public void
- noticeAvailableChild (final EmacsWindow window)
- {
- Log.d (TAG, "A new child is available: " + window);
-
- handler.post (new Runnable () {
- public void
- run ()
- {
- for (EmacsActivity activity
- : EmacsActivity.availableActivities)
- {
- /* TODO: check if the activity matches. */
- activity.attachChild (window);
- break;
- }
-
- /* Nope, wait for an activity to become available. */
- availableChildren.add (window);
- }
- });
- }
-
- /* Notice that a child of the root window named WINDOW has been
- destroyed. */
-
- public void
- noticeChildDestroyed (final EmacsWindow child)
- {
- handler.post (new Runnable () {
- @Override
- public void
- run ()
- {
- availableChildren.remove (child);
- }
- });
- }
-
/* X drawing operations. These are quite primitive operations. The
drawing queue is kept on the Emacs thread, but is periodically
flushed to the application thread, upon buffers swaps and once it
@@ -311,11 +277,6 @@ public class EmacsService extends Service
ensurePaintQueue ();
- if (gc.clip_rects != null && gc.clip_rects.length >= 1)
- android.util.Log.d ("drawRectangle",
- gc.clip_rects[0].toString ()
- + " " + gc.toString ());
-
req = new EmacsDrawRectangle (drawable, x, y,
width, height,
gc.immutableGC ());
@@ -381,4 +342,12 @@ public class EmacsService extends Service
{
window.clearArea (x, y, width, height);
}
+
+ public void
+ appendPaintOperation (EmacsPaintReq op)
+ {
+ ensurePaintQueue ();
+ paintQueue.appendPaintOperation (op);
+ checkFlush ();
+ }
};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java
index 194f6ad37a3..b8b828e4820 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -22,6 +22,8 @@ package org.gnu.emacs;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
+import android.os.Build;
+
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -40,7 +42,9 @@ public class EmacsSurfaceView extends SurfaceView
surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
-
+ /* Force a buffer swap now to get the contents of the Emacs
+ view on screen. */
+ view.swapBuffers (true);
}
@Override
@@ -51,7 +55,7 @@ public class EmacsSurfaceView extends SurfaceView
/* Force a buffer swap now to get the contents of the Emacs
view on screen. */
- view.swapBuffers ();
+ view.swapBuffers (true);
}
@Override
@@ -72,12 +76,37 @@ public class EmacsSurfaceView extends SurfaceView
public Canvas
lockCanvas (Rect damage)
{
- return getHolder ().lockCanvas (damage);
+ SurfaceHolder holder;
+
+ holder = getHolder ();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ {
+ damage.setEmpty ();
+ return holder.lockHardwareCanvas ();
+ }
+
+ return holder.lockCanvas (damage);
+ }
+
+ /* This method is only used during debugging when it seems damage
+ isn't working correctly. */
+
+ public Canvas
+ lockCanvas ()
+ {
+ SurfaceHolder holder;
+
+ holder = getHolder ();
+ return holder.lockCanvas ();
}
public void
unlockCanvasAndPost (Canvas canvas)
{
- getHolder ().unlockCanvasAndPost (canvas);
+ SurfaceHolder holder;
+
+ holder = getHolder ();
+ holder.unlockCanvasAndPost (canvas);
}
};
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index 237946d6366..7b48eaf0aa6 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -19,17 +19,20 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
package org.gnu.emacs;
+import android.content.res.ColorStateList;
+
import android.view.View;
import android.view.KeyEvent;
import android.view.ViewGroup;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Paint;
-import android.util.Log;
import android.os.Build;
+import android.util.Log;
/* This is an Android view which has a back and front buffer. When
swapBuffers is called, the back buffer is swapped to the front
@@ -70,10 +73,11 @@ public class EmacsView extends ViewGroup
this.damageRegion = new Region ();
this.paint = new Paint ();
+ setFocusable (true);
+ setFocusableInTouchMode (true);
+
/* Create the surface view. */
this.surfaceView = new EmacsSurfaceView (this);
-
- setFocusable (FOCUSABLE);
addView (this.surfaceView);
}
@@ -162,7 +166,7 @@ public class EmacsView extends ViewGroup
}
public void
- swapBuffers ()
+ swapBuffers (boolean force)
{
Bitmap back;
Canvas canvas;
@@ -185,14 +189,25 @@ public class EmacsView extends ViewGroup
if (canvas == null)
return;
- /* Copy from the back buffer to the canvas. */
- 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. */
+
+ 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 void
+ swapBuffers ()
+ {
+ swapBuffers (false);
+ }
+
@Override
public boolean
onKeyDown (int keyCode, KeyEvent event)
@@ -203,9 +218,27 @@ public class EmacsView extends ViewGroup
@Override
public boolean
+ onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
+ {
+ window.onKeyDown (keyCode, event);
+ return true;
+ }
+
+ @Override
+ public boolean
onKeyUp (int keyCode, KeyEvent event)
{
window.onKeyUp (keyCode, event);
return true;
}
+
+ @Override
+ public void
+ onFocusChanged (boolean gainFocus, int direction,
+ Rect previouslyFocusedRect)
+ {
+ window.onFocusChanged (gainFocus);
+ super.onFocusChanged (gainFocus, direction,
+ previouslyFocusedRect);
+ }
};
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index 28db04a261d..26e788a20a8 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -32,6 +32,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.KeyEvent;
+import android.content.Intent;
+
/* This defines a window, which is a handle. Windows represent a
rectangular subset of the screen with their own contents.
@@ -57,10 +59,10 @@ public class EmacsWindow extends EmacsHandleObject
/* List of all children in stacking order. This must be kept
consistent! */
- private ArrayList<EmacsWindow> children;
+ public ArrayList<EmacsWindow> children;
- /* The EmacsActivity currently attached, if it exists. */
- private EmacsActivity attached;
+ /* The window consumer currently attached, if it exists. */
+ private EmacsWindowAttachmentManager.WindowConsumer attached;
/* The window background scratch GC. foreground is always the
window background. */
@@ -74,35 +76,44 @@ public class EmacsWindow extends EmacsHandleObject
rect = new Rect (x, y, x + width, y + height);
- /* Create the view from the context's UI thread. */
- view = EmacsService.SERVICE.getEmacsView (this);
+ /* Create the view from the context's UI thread. The window is
+ unmapped, so the view is GONE. */
+ view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
+ parent == null);
this.parent = parent;
- children = new ArrayList<EmacsWindow> ();
- /* The window is unmapped by default. */
- view.setVisibility (View.GONE);
+ /* Create the list of children. */
+ children = new ArrayList<EmacsWindow> ();
- /* If parent is the root window, notice that there are new
- children available for interested activites to pick up. */
- if (parent == null)
- EmacsService.SERVICE.noticeAvailableChild (this);
- else
+ if (parent != null)
{
- /* Otherwise, directly add this window as a child of that
- window's view. */
- synchronized (parent)
+ parent.children.add (this);
+ parent.view.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ parent.view.addView (view);
+ }
+ });
+ }
+ else
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
{
- parent.children.add (this);
- parent.view.post (new Runnable () {
- @Override
- public void
- run ()
- {
- parent.view.addView (view);
- }
- });
+ 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);
}
@@ -129,28 +140,35 @@ public class EmacsWindow extends EmacsHandleObject
public void
destroyHandle () throws IllegalStateException
{
- synchronized (this)
- {
- if (!children.isEmpty ())
- throw new IllegalStateException ("Trying to destroy window with "
- + "children!");
- }
+ if (parent != null)
+ parent.children.remove (this);
+
+ EmacsActivity.invalidateFocus ();
- /* Notice that the child has been destroyed. */
- EmacsService.SERVICE.noticeChildDestroyed (this);
+ if (!children.isEmpty ())
+ throw new IllegalStateException ("Trying to destroy window with "
+ + "children!");
/* Remove the view from its parent and make it invisible. */
view.post (new Runnable () {
public void
run ()
{
+ View parent;
+ EmacsWindowAttachmentManager manager;
+
+ if (EmacsActivity.focusedWindow == EmacsWindow.this)
+ EmacsActivity.focusedWindow = null;
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
view.setVisibility (View.GONE);
- if (view.getParent () != null)
- ((ViewGroup) view.getParent ()).removeView (view);
+ parent = (View) view.getParent ();
- if (attached != null)
- attached.makeAvailable ();
+ if (parent != null && attached == null)
+ ((ViewGroup) parent).removeView (view);
+
+ manager.detachWindow (EmacsWindow.this);
}
});
@@ -158,12 +176,15 @@ public class EmacsWindow extends EmacsHandleObject
}
public void
- setActivity (EmacsActivity activity)
+ setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
{
- synchronized (this)
- {
- activity = activity;
- }
+ attached = consumer;
+ }
+
+ public EmacsWindowAttachmentManager.WindowConsumer
+ getAttachedConsumer ()
+ {
+ return attached;
}
public void
@@ -233,7 +254,10 @@ public class EmacsWindow extends EmacsHandleObject
public void
run ()
{
+
view.setVisibility (View.VISIBLE);
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
}
});
}
@@ -319,18 +343,47 @@ public class EmacsWindow extends EmacsHandleObject
public void
onKeyDown (int keyCode, KeyEvent event)
{
+ int state;
+
+ state = event.getModifiers ();
+ state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+
EmacsNative.sendKeyPress (this.handle,
event.getEventTime (),
event.getModifiers (),
- keyCode);
+ keyCode,
+ /* Ignore meta-state understood by Emacs
+ for now, or Ctrl+C will not be
+ recognized as an ASCII key press
+ event. */
+ event.getUnicodeChar (state));
}
public void
onKeyUp (int keyCode, KeyEvent event)
{
+ int state;
+
+ state = event.getModifiers ();
+ state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+
EmacsNative.sendKeyRelease (this.handle,
event.getEventTime (),
event.getModifiers (),
- keyCode);
+ keyCode,
+ event.getUnicodeChar (state));
+ }
+
+ public void
+ onFocusChanged (boolean gainFocus)
+ {
+ EmacsActivity.invalidateFocus ();
+ }
+
+ public void
+ onActivityDetached ()
+ {
+ /* Destroy the associated frame when the activity is detached. */
+ EmacsNative.sendWindowAction (this.handle, 0);
}
};
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
new file mode 100644
index 00000000000..34be2ab8789
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -0,0 +1,166 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+package org.gnu.emacs;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.Intent;
+import android.util.Log;
+
+/* Code to paper over the differences in lifecycles between
+ "activities" and windows. There are four interfaces to an instance
+ of this class:
+
+ registerWindowConsumer (WindowConsumer)
+ registerWindow (EmacsWindow)
+ removeWindowConsumer (WindowConsumer)
+ removeWindow (EmacsWindow)
+
+ A WindowConsumer is expected to allow an EmacsWindow to be attached
+ to it, and be created or destroyed.
+
+ Every time a window is created, registerWindow checks the list of
+ window consumers. If a consumer exists and does not currently have
+ a window of its own attached, it gets the new window. Otherwise,
+ the window attachment manager starts a new consumer.
+
+ Every time a consumer is registered, registerWindowConsumer checks
+ the list of available windows. If a window exists and is not
+ currently attached to a consumer, then the consumer gets it.
+
+ Finally, every time a window is removed, the consumer is
+ destroyed. */
+
+public class EmacsWindowAttachmentManager
+{
+ public static EmacsWindowAttachmentManager MANAGER;
+ private final static String TAG = "EmacsWindowAttachmentManager";
+
+ static
+ {
+ MANAGER = new EmacsWindowAttachmentManager ();
+ };
+
+ public interface WindowConsumer
+ {
+ public void attachWindow (EmacsWindow window);
+ public EmacsWindow getAttachedWindow ();
+ public void detachWindow ();
+ public void destroy ();
+ };
+
+ private List<WindowConsumer> consumers;
+ private List<EmacsWindow> windows;
+
+ public
+ EmacsWindowAttachmentManager ()
+ {
+ consumers = new LinkedList<WindowConsumer> ();
+ windows = new LinkedList<EmacsWindow> ();
+ }
+
+ public void
+ registerWindowConsumer (WindowConsumer consumer)
+ {
+ Log.d (TAG, "registerWindowConsumer " + consumer);
+
+ consumers.add (consumer);
+
+ for (EmacsWindow window : windows)
+ {
+ if (window.getAttachedConsumer () == null)
+ {
+ Log.d (TAG, "registerWindowConsumer: attaching " + window);
+ consumer.attachWindow (window);
+ return;
+ }
+ }
+
+ Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0");
+ EmacsNative.sendWindowAction ((short) 0, 0);
+ }
+
+ public void
+ registerWindow (EmacsWindow window)
+ {
+ Intent intent;
+
+ Log.d (TAG, "registerWindow " + window);
+ windows.add (window);
+
+ for (WindowConsumer consumer : consumers)
+ {
+ if (consumer.getAttachedWindow () == null)
+ {
+ Log.d (TAG, "registerWindow: attaching " + consumer);
+ consumer.attachWindow (window);
+ return;
+ }
+ }
+
+ intent = new Intent (EmacsService.SERVICE,
+ EmacsMultitaskActivity.class);
+ intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ EmacsService.SERVICE.startActivity (intent);
+ Log.d (TAG, "registerWindow: startActivity");
+ }
+
+ public void
+ removeWindowConsumer (WindowConsumer consumer)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "removeWindowConsumer " + consumer);
+
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ {
+ Log.d (TAG, "removeWindowConsumer: detaching " + window);
+
+ consumer.detachWindow ();
+ window.onActivityDetached ();
+ }
+
+ Log.d (TAG, "removeWindowConsumer: removing " + consumer);
+ consumers.remove (consumer);
+ }
+
+ public void
+ detachWindow (EmacsWindow window)
+ {
+ WindowConsumer consumer;
+
+ Log.d (TAG, "detachWindow " + window);
+
+ if (window.getAttachedConsumer () != null)
+ {
+ consumer = window.getAttachedConsumer ();
+
+ Log.d (TAG, "detachWindow: removing" + consumer);
+
+ consumers.remove (consumer);
+ consumer.destroy ();
+ }
+ }
+};
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 65eb066a554..6e71bfa6db6 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2209,7 +2209,7 @@ and `face'."
;;; The `custom' Widget.
(defface custom-button
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2217,7 +2217,7 @@ and `face'."
:group 'custom-faces)
(defface custom-button-mouse
- '((((type x w32 ns haiku pgtk) (class color))
+ '((((type x w32 ns haiku pgtk android) (class color))
:box (:line-width 2 :style released-button)
:background "grey90" :foreground "black")
(t
@@ -2242,7 +2242,7 @@ and `face'."
(if custom-raised-buttons 'custom-button-mouse 'highlight))
(defface custom-button-pressed
- '((((type x w32 ns haiku pgtk) (class color))
+ '((((type x w32 ns haiku pgtk android) (class color))
:box (:line-width 2 :style pressed-button)
:background "lightgrey" :foreground "black")
(t :inverse-video t))
diff --git a/lisp/faces.el b/lisp/faces.el
index 0dd89be4738..2779bfc295f 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2911,7 +2911,7 @@ Note: Other faces cannot inherit from the cursor face."
(((type haiku))
:foreground "B_MENU_ITEM_TEXT_COLOR"
:background "B_MENU_BACKGROUND_COLOR")
- (((type x w32 ns pgtk) (class color))
+ (((type x w32 ns pgtk android) (class color))
:background "grey75")
(((type x) (class mono))
:background "grey"))
diff --git a/src/android.c b/src/android.c
index dd841cf383a..db12e244275 100644
--- a/src/android.c
+++ b/src/android.c
@@ -24,6 +24,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <limits.h>
#include <signal.h>
#include <semaphore.h>
+#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -46,6 +47,7 @@ bool android_init_gui;
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
+#include <android/bitmap.h>
#include <android/log.h>
#include <linux/ashmem.h>
@@ -86,6 +88,7 @@ struct android_emacs_pixmap
{
jclass class;
jmethodID constructor;
+ jmethodID constructor_mutable;
};
struct android_graphics_point
@@ -94,6 +97,12 @@ struct android_graphics_point
jmethodID constructor;
};
+struct android_emacs_drawable
+{
+ jclass class;
+ jmethodID get_bitmap;
+};
+
/* The asset manager being used. */
static AAssetManager *asset_manager;
@@ -106,6 +115,12 @@ char *android_site_load_path;
/* The path used to store native libraries. */
char *android_lib_dir;
+/* The path used to store game files. */
+char *android_game_path;
+
+/* The display's pixel densities. */
+double android_pixel_density_x, android_pixel_density_y;
+
/* The Android application data directory. */
static char *android_files_dir;
@@ -149,6 +164,9 @@ static struct android_emacs_pixmap pixmap_class;
/* Various methods associated with the Point class. */
static struct android_graphics_point point_class;
+/* Various methods associated with the EmacsDrawable class. */
+static struct android_emacs_drawable drawable_class;
+
/* Event handling functions. Events are stored on a (circular) queue
@@ -383,6 +401,10 @@ android_write_event (union android_event *event)
if (!container)
return;
+ /* If the event queue hasn't been initialized yet, return false. */
+ if (!event_queue.events.next)
+ return;
+
pthread_mutex_lock (&event_queue.mutex);
/* The event queue is full, wait for events to be read. */
@@ -451,6 +473,10 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds,
/* Unlock the event queue mutex. */
pthread_mutex_unlock (&event_queue.mutex);
+ /* This is to shut up process.c when pselect gets EINTR. */
+ if (nfds_return < 0)
+ errno = EINTR;
+
return nfds_return;
}
@@ -793,16 +819,20 @@ android_close (int fd)
{
if (fd < ANDROID_MAX_ASSET_FD
&& (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
- {
- __android_log_print (ANDROID_LOG_INFO, __func__,
- "closing android file descriptor %d",
- fd);
- android_table[fd].flags = 0;
- }
+ android_table[fd].flags = 0;
return close (fd);
}
+/* Return the current user's ``home'' directory, which is actually the
+ app data directory on Android. */
+
+const char *
+android_get_home_directory (void)
+{
+ return android_files_dir;
+}
+
/* JNI functions called by Java. */
@@ -814,6 +844,8 @@ JNIEXPORT void JNICALL
NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
jobject local_asset_manager,
jobject files_dir, jobject libs_dir,
+ jfloat pixel_density_x,
+ jfloat pixel_density_y,
jobject emacs_service_object)
{
int pipefd[2];
@@ -829,6 +861,9 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
return;
}
+ android_pixel_density_x = pixel_density_x;
+ android_pixel_density_y = pixel_density_y;
+
__android_log_print (ANDROID_LOG_INFO, __func__,
"Initializing "PACKAGE_STRING"...\nPlease report bugs to "
PACKAGE_BUGREPORT". Thanks.\n");
@@ -891,15 +926,23 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
if (!android_site_load_path)
emacs_abort ();
+ android_game_path = malloc (PATH_MAX + 1);
+
+ if (!android_game_path)
+ emacs_abort ();
+
snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
android_files_dir);
+ snprintf (android_game_path, PATH_MAX, "%s/scores", android_files_dir);
+
__android_log_print (ANDROID_LOG_INFO, __func__,
"Site-lisp directory: %s\n"
"Files directory: %s\n"
- "Native code directory: %s",
+ "Native code directory: %s\n"
+ "Game score path: %s\n",
android_site_load_path,
android_files_dir,
- android_lib_dir);
+ android_lib_dir, android_game_path);
/* Make a reference to the Emacs service. */
emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
@@ -997,6 +1040,7 @@ android_init_emacs_pixmap (void)
assert (pixmap_class.c_name);
FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+ FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
#undef FIND_METHOD
}
@@ -1031,6 +1075,36 @@ android_init_graphics_point (void)
#undef FIND_METHOD
}
+static void
+android_init_emacs_drawable (void)
+{
+ jclass old;
+
+ drawable_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDrawable");
+ eassert (drawable_class.class);
+
+ old = drawable_class.class;
+ drawable_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!drawable_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ drawable_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ drawable_class.class, \
+ name, signature); \
+ assert (drawable_class.c_name);
+
+ FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
+#undef FIND_METHOD
+}
+
extern JNIEXPORT void JNICALL
NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
{
@@ -1063,6 +1137,15 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
android_init_emacs_service ();
android_init_emacs_pixmap ();
android_init_graphics_point ();
+ android_init_emacs_drawable ();
+
+ /* Set HOME to the app data directory. */
+ setenv ("HOME", android_files_dir, 1);
+
+ /* Set the cwd to that directory as well. */
+ if (chdir (android_files_dir))
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "chdir: %s", strerror (errno));
/* Initialize the Android GUI. */
android_init_gui = true;
@@ -1099,7 +1182,8 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
jshort window, jlong time,
- jint state, jint keycode)
+ jint state, jint keycode,
+ jint unicode_char)
{
union android_event event;
@@ -1108,6 +1192,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
event.xkey.time = time;
event.xkey.state = state;
event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
android_write_event (&event);
}
@@ -1115,7 +1200,8 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
extern JNIEXPORT void JNICALL
NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
jshort window, jlong time,
- jint state, jint keycode)
+ jint state, jint keycode,
+ jint unicode_char)
{
union android_event event;
@@ -1124,6 +1210,46 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
event.xkey.time = time;
event.xkey.state = state;
event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
+
+ android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ union android_event event;
+
+ event.xkey.type = ANDROID_FOCUS_IN;
+ event.xkey.window = window;
+ event.xkey.time = time;
+
+ android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ union android_event event;
+
+ event.xkey.type = ANDROID_FOCUS_OUT;
+ event.xkey.window = window;
+ event.xkey.time = time;
+
+ android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
+ jshort window, jint action)
+{
+ union android_event event;
+
+ event.xaction.type = ANDROID_WINDOW_ACTION;
+ event.xaction.window = window;
+ event.xaction.action = action;
android_write_event (&event);
}
@@ -1142,13 +1268,6 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
#define MAX_HANDLE 65535
-enum android_handle_type
- {
- ANDROID_HANDLE_WINDOW,
- ANDROID_HANDLE_GCONTEXT,
- ANDROID_HANDLE_PIXMAP,
- };
-
struct android_handle_entry
{
/* The type. */
@@ -1245,7 +1364,7 @@ android_destroy_handle (android_handle handle)
android_handles[handle].handle = NULL;
}
-static jobject
+jobject
android_resolve_handle (android_handle handle,
enum android_handle_type type)
{
@@ -1625,14 +1744,15 @@ android_change_gc (struct android_gc *gc,
ANDROID_HANDLE_PIXMAP);
(*android_java_env)->SetObjectField (android_java_env,
gcontext,
- emacs_gc_stipple,
+ emacs_gc_clip_mask,
what);
/* Clearing GCClipMask also clears the clip rectangles. */
- (*android_java_env)->SetObjectField (android_java_env,
- gcontext,
- emacs_gc_clip_rects,
- NULL);
+ if (!what)
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_rects,
+ NULL);
}
if (mask & ANDROID_GC_STIPPLE)
@@ -2008,10 +2128,23 @@ android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
{
for (x = 0; x < width; ++x)
{
- if (data[y / 8] & (1 << (x % 8)))
- region[x] = foreground;
+ if (depth == 24)
+ {
+ /* The alpha channels must be set, or otherwise, the
+ pixmap will be created entirely transparent. */
+
+ if (data[y * (width + 7) / 8 + x / 8] & (1 << (x % 8)))
+ region[x] = foreground | 0xff000000;
+ else
+ region[x] = background | 0xff000000;
+ }
else
- region[x] = background;
+ {
+ if (data[y * (width + 7) / 8 + x / 8] & (1 << (x % 8)))
+ region[x] = foreground;
+ else
+ region[x] = background;
+ }
}
(*android_java_env)->SetIntArrayRegion (android_java_env,
@@ -2236,36 +2369,21 @@ android_create_pixmap (unsigned int width, unsigned int height,
{
android_handle prev_max_handle;
jobject object;
- jintArray colors;
android_pixmap pixmap;
- /* Create the color array holding the data. */
- colors = (*android_java_env)->NewIntArray (android_java_env,
- width * height);
-
- if (!colors)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
-
/* First, allocate the pixmap handle. */
prev_max_handle = max_handle;
pixmap = android_alloc_id ();
if (!pixmap)
- {
- ANDROID_DELETE_LOCAL_REF ((jobject) colors);
- error ("Out of pixmap handles!");
- }
+ error ("Out of pixmap handles!");
object = (*android_java_env)->NewObject (android_java_env,
pixmap_class.class,
- pixmap_class.constructor,
- (jshort) pixmap, colors,
+ pixmap_class.constructor_mutable,
+ (jshort) pixmap,
(jint) width, (jint) height,
(jint) depth);
- ANDROID_DELETE_LOCAL_REF ((jobject) colors);
if (!object)
{
@@ -2313,6 +2431,387 @@ android_clear_area (android_window handle, int x, int y,
(jint) width, (jint) height);
}
+android_pixmap
+android_create_bitmap_from_data (char *bits, unsigned int width,
+ unsigned int height)
+{
+ return android_create_pixmap_from_bitmap_data (bits, 1, 0,
+ width, height, 1);
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ struct android_image *image;
+
+ image = xmalloc (sizeof *image);
+
+ /* Fill in the fields required by image.c. N.B. that
+ android_destroy_image ostensibly will free data, but image.c
+ mostly sets and frees data itself. */
+ image->width = width;
+ image->height = height;
+ image->data = data;
+ image->depth = depth;
+ image->format = format;
+
+ /* Now fill in the image dimensions. There are only two depths
+ supported by this function. */
+
+ if (depth == 1)
+ {
+ image->bytes_per_line = (width + 7) / 8;
+ image->bits_per_pixel = 1;
+ }
+ else if (depth == 24)
+ {
+ image->bytes_per_line = width * 4;
+ image->bits_per_pixel = 32;
+ }
+ else
+ emacs_abort ();
+
+ return image;
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ /* If XIMG->data is NULL, then it has already been freed by
+ image.c. */
+
+ if (ximg->data)
+ xfree (ximg->data);
+ xfree (ximg);
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ char *byte, *word;
+ unsigned int r, g, b;
+
+ /* Ignore out-of-bounds accesses. */
+
+ if (x >= ximg->width || y >= ximg->height || x < 0 || y < 0)
+ return;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+
+ if (pixel)
+ *byte |= (1 << x % 8);
+ else
+ *byte &= ~(1 << x % 8);
+ break;
+
+ case 24:
+ /* Unaligned accesses are problematic on Android devices. */
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+
+ /* Swizzle the pixel into ABGR format. Android uses Skia's
+ ``native color type'', which is ABGR. This is despite the
+ format being named ``ARGB'', and more confusingly
+ `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h. */
+ r = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ b = pixel & 0x000000ff;
+ pixel = (r >> 16) | g | (b << 16) | 0xff000000;
+
+ memcpy (word, &pixel, sizeof pixel);
+ break;
+ }
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ char *byte, *word;
+ unsigned int pixel, r, g, b;
+
+ if (x >= ximg->width || y >= ximg->height
+ || x < 0 || y < 0)
+ return 0;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+ return (*byte & (1 << x % 8)) ? 1 : 0;
+
+ case 24:
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+ memcpy (&pixel, word, sizeof pixel);
+
+ /* Convert the pixel back to RGB. */
+ b = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ r = pixel & 0x000000ff;
+ pixel = ((r << 16) | g | (b >> 16)) & ~0xff000000;
+
+ return pixel;
+ }
+
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable handle,
+ enum android_image_format format)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ size_t byte_size;
+ void *data;
+ struct android_image *image;
+ unsigned char *data1, *data2;
+ int i, x;
+
+ /* N.B. that supporting windows requires some more work to make
+ EmacsDrawable.getBitmap thread safe. */
+ drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ if (!bitmap)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Compute how big the image data will be. Fail if it would be too
+ big. */
+
+ if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8)
+ {
+ if (INT_MULTIPLY_WRAPV ((size_t) bitmap_info.stride,
+ (size_t) bitmap_info.height,
+ &byte_size))
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+ }
+ else
+ /* This A8 image will be packed into A1 later on. */
+ byte_size = (bitmap_info.width + 7) / 8;
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Copy the data into a new struct android_image. */
+ image = xmalloc (sizeof *image);
+ image->width = bitmap_info.width;
+ image->height = bitmap_info.height;
+ image->data = malloc (byte_size);
+
+ if (!image->data)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ xfree (image);
+ memory_full (byte_size);
+ }
+
+ /* Use the format of the bitmap to determine the image depth. */
+ switch (bitmap_info.format)
+ {
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
+ image->depth = 24;
+ image->bits_per_pixel = 32;
+ break;
+
+ /* A8 images are used by Emacs to represent bitmaps. They have
+ to be packed manually. */
+ case ANDROID_BITMAP_FORMAT_A_8:
+ image->depth = 1;
+ image->bits_per_pixel = 1;
+ break;
+
+ /* Other formats are currently not supported. */
+ default:
+ emacs_abort ();
+ }
+
+ image->format = format;
+
+ if (image->depth == 24)
+ {
+ image->bytes_per_line = bitmap_info.stride;
+
+ /* Copy the bitmap data over. */
+ memcpy (image->data, data, byte_size);
+ }
+ else
+ {
+ /* Pack the A8 image data into bits manually. */
+ image->bytes_per_line = (image->width + 7) / 8;
+
+ data1 = (unsigned char *) image->data;
+ data2 = data;
+
+ for (i = 0; i < image->height; ++i)
+ {
+ for (x = 0; x < image->width; ++x)
+ /* Some bits in data1 might be initialized at this point,
+ but they will all be set properly later. */
+ data1[x / 8] = (data2[x]
+ ? (data1[x / 8] | (1 << (x % 8)))
+ : (data1[x / 8] & ~(1 << (x % 8))));
+
+ data1 += image->bytes_per_line;
+ data2 += bitmap_info.stride;
+ }
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return image;
+}
+
+void
+android_put_image (android_pixmap handle, struct android_image *image)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ void *data;
+ unsigned char *data_1, *data_2;
+ int i, x;
+
+ drawable = android_resolve_handle (handle, ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ if (!bitmap)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ if (bitmap_info.width != image->width
+ || bitmap_info.height != image->height)
+ /* This is not yet supported. */
+ emacs_abort ();
+
+ /* Make sure the bitmap formats are compatible with each other. */
+
+ if ((image->depth == 24
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
+ || (image->depth == 1
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8))
+ emacs_abort ();
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ data_1 = data;
+ data_2 = (unsigned char *) image->data;
+
+ /* Copy the bitmap data over scanline-by-scanline. */
+ for (i = 0; i < image->height; ++i)
+ {
+ if (image->depth != 1)
+ memcpy (data_1, data_2,
+ image->width * (image->bits_per_pixel / 8));
+ else
+ {
+ /* Android internally uses a 1 byte-per-pixel format for
+ ALPHA_8 images. Expand the image from the 1
+ bit-per-pixel X format correctly. */
+
+ for (x = 0; x < image->width; ++x)
+ data_1[x] = (data_2[x / 8] & (1 << x % 8)) ? 0xff : 0;
+ }
+
+ data_1 += bitmap_info.stride;
+ data_2 += image->bytes_per_line;
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+}
+
+
+
+#undef faccessat
+
+/* Replace the system faccessat with one which understands AT_EACCESS.
+ Android's faccessat simply fails upon using AT_EACCESS, so repalce
+ it with zero here. This isn't caught during configuration. */
+
+int
+faccessat (int dirfd, const char *pathname, int mode, int flags)
+{
+ static int (*real_faccessat) (int, const char *, int, int);
+
+ if (!real_faccessat)
+ real_faccessat = dlsym (RTLD_NEXT, "faccessat");
+
+ return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
+}
+
#else /* ANDROID_STUBIFY */
/* X emulation functions for Android. */
@@ -2332,4 +2831,44 @@ android_free_gc (struct android_gc *gc)
emacs_abort ();
}
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ emacs_abort ();
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ emacs_abort ();
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ emacs_abort ();
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable drawable,
+ enum android_image_format format)
+{
+ emacs_abort ();
+}
+
+void
+android_put_image (android_pixmap pixmap,
+ struct android_image *image)
+{
+ emacs_abort ();
+}
+
#endif
diff --git a/src/android.h b/src/android.h
index 6bdcd38ed68..4d702fe2079 100644
--- a/src/android.h
+++ b/src/android.h
@@ -28,6 +28,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <jni.h>
#include <pwd.h>
#include <sys/stat.h>
+
+#include "androidgui.h"
#endif
/* This must be used in every symbol declaration to export it to the
@@ -49,6 +51,19 @@ extern int android_fstat (int, struct stat *);
extern int android_fstatat (int, const char *restrict,
struct stat *restrict, int);
extern int android_close (int);
+extern const char *android_get_home_directory (void);
+
+extern double android_pixel_density_x, android_pixel_density_y;
+
+enum android_handle_type
+ {
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_GCONTEXT,
+ ANDROID_HANDLE_PIXMAP,
+ };
+
+extern jobject android_resolve_handle (android_handle,
+ enum android_handle_type);
#endif
diff --git a/src/androidfns.c b/src/androidfns.c
index e9e1ae3d52e..a78b35fc8ff 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -531,9 +531,10 @@ android_default_font_parameter (struct frame *f, Lisp_Object parms)
if (! FONTP (font) && ! STRINGP (font))
{
const char *names[] = {
- /* This will find the normal font. */
- "DroidSansMono",
+ /* This will find the normal font. The default font size on
+ Android is 8. */
"monospace",
+ "DroidSansMono",
NULL
};
int i;
diff --git a/src/androidfont.c b/src/androidfont.c
index e312e55c54a..b2c83201b06 100644
--- a/src/androidfont.c
+++ b/src/androidfont.c
@@ -49,6 +49,7 @@ struct android_emacs_font_driver
jmethodID has_char;
jmethodID text_extents;
jmethodID encode_char;
+ jmethodID draw;
/* Static methods. */
jmethodID create_font_driver;
@@ -67,6 +68,7 @@ struct android_emacs_font_spec
jfieldID size;
jfieldID spacing;
jfieldID avgwidth;
+ jfieldID dpi;
};
struct android_emacs_font_metrics
@@ -113,6 +115,9 @@ struct androidfont_info
/* The Java-side font. */
jobject object;
+
+ /* Cached glyph metrics arranged in a two dimensional array. */
+ struct font_metrics **metrics;
};
struct androidfont_entity
@@ -197,9 +202,11 @@ android_init_font_driver (void)
FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
"Spec;C)I");
FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
- "$FontObject;[I[Lorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+ "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
"$FontObject;C)I");
+ FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
+ "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
font_driver_class.create_font_driver
= (*android_java_env)->GetStaticMethodID (android_java_env,
@@ -252,6 +259,7 @@ android_init_font_spec (void)
FIND_FIELD (size, "size", "Ljava/lang/Integer;");
FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
+ FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
#undef FIND_FIELD
}
@@ -449,6 +457,9 @@ androidfont_from_lisp (Lisp_Object font)
DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
: -1));
+ DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
+ ? XFIXNUM (AREF (font, FONT_DPI_INDEX))
+ : -1));
#undef DO_CARDINAL_FIELD
@@ -507,6 +518,8 @@ androidfont_from_java (jobject spec, Lisp_Object entity)
DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
+ DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
+
#undef DO_CARDINAL_FIELD
}
@@ -613,49 +626,98 @@ static int
androidfont_draw (struct glyph_string *s, int from, int to,
int x, int y, bool with_background)
{
- return 0;
+ struct androidfont_info *info;
+ jarray chars;
+ int rc;
+ jobject gcontext, drawable;
+
+ verify (sizeof (unsigned int) == sizeof (jint));
+ info = (struct androidfont_info *) s->font;
+
+ gcontext = android_resolve_handle (s->gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+ drawable = android_resolve_handle (FRAME_ANDROID_WINDOW (s->f),
+ ANDROID_HANDLE_WINDOW);
+ chars = (*android_java_env)->NewIntArray (android_java_env,
+ to - from);
+
+ if (!chars)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ (*android_java_env)->SetIntArrayRegion (android_java_env, chars,
+ 0, to - from,
+ (jint *) s->char2b + from);
+
+ info = (struct androidfont_info *) s->font;
+ prepare_face_for_display (s->f, s->face);
+
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.draw,
+ info->object,
+ gcontext, drawable,
+ chars, (jint) x, (jint) y,
+ (jint) s->width,
+ (jboolean) with_background);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (chars);
+
+ return rc;
}
static Lisp_Object
-androidfont_open_font (struct frame *f, Lisp_Object font_entity, int x)
+androidfont_open_font (struct frame *f, Lisp_Object font_entity,
+ int pixel_size)
{
struct androidfont_info *font_info;
struct androidfont_entity *entity;
struct font *font;
- Lisp_Object font_object, tem;
+ Lisp_Object font_object;
jobject old;
jint value;
- if (x <= 0)
+ if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+ pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+ else if (pixel_size == 0)
{
- /* Get pixel size from frame instead. */
- tem = get_frame_param (f, Qfontsize);
- x = NILP (tem) ? 0 : XFIXNAT (tem);
+ /* This bit was copied from xfont.c. The values might need
+ adjustment. */
+
+ if (FRAME_FONT (f))
+ pixel_size = FRAME_FONT (f)->pixel_size;
+ else
+ pixel_size = 12;
}
__android_log_print (ANDROID_LOG_DEBUG, __func__,
"opening font entity %"pI"x:%d",
- (EMACS_INT) font_entity, x);
+ (EMACS_INT) font_entity, pixel_size);
entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
block_input ();
font_object = font_make_object (VECSIZE (struct androidfont_info),
- font_entity, x);
+ font_entity, pixel_size);
ASET (font_object, FONT_TYPE_INDEX, Qandroid);
font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
font = &font_info->font;
font->driver = &androidfont_driver;
- /* Clear font_info->object early in case GC happens later on! */
+ /* Clear font_info->object and font_info->metrics early in case GC
+ happens later on! */
font_info->object = NULL;
+ font_info->metrics = NULL;
unblock_input ();
font_info->object
= (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.open_font,
- entity->object, (jint) x);
+ entity->object,
+ (jint) pixel_size);
if (!font_info->object)
{
(*android_java_env)->ExceptionClear (android_java_env);
@@ -710,9 +772,19 @@ static void
androidfont_close_font (struct font *font)
{
struct androidfont_info *info;
+ int i;
info = (struct androidfont_info *) font;
+ /* Free the font metrics cache if it exists. */
+
+ if (info->metrics)
+ {
+ for (i = 0; i < 256; ++i)
+ xfree (info->metrics[i]);
+ xfree (info->metrics);
+ }
+
/* If info->object is NULL, then FONT was unsuccessfully created,
and there is no global reference that has to be deleted. */
@@ -763,17 +835,64 @@ androidfont_encode_char (struct font *font, int c)
}
static void
+androidfont_cache_text_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ int i;
+
+ /* Glyphs larger than 65535 can't be cached. */
+ if (glyph >= 256 * 256)
+ return;
+
+ if (!info->metrics)
+ info->metrics = xzalloc (256 * sizeof *info->metrics);
+
+ if (!info->metrics[glyph / 256])
+ {
+ info->metrics[glyph / 256]
+ = xnmalloc (256, sizeof **info->metrics);
+
+ /* Now, all the metrics in that array as invalid by setting
+ lbearing to SHRT_MAX. */
+ for (i = 0; i < 256; ++i)
+ info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
+ }
+
+ /* Finally, cache the glyph. */
+ info->metrics[glyph / 256][glyph % 256] = *metrics;
+}
+
+static bool
+androidfont_check_cached_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ if (info->metrics && info->metrics[glyph / 256]
+ && info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
+ {
+ *metrics = info->metrics[glyph / 256][glyph % 256];
+ return true;
+ }
+
+ return false;
+}
+
+static void
androidfont_text_extents (struct font *font, const unsigned int *code,
int nglyphs, struct font_metrics *metrics)
{
struct androidfont_info *info;
- jarray codepoint_array, metrics_array;
+ jarray codepoint_array;
jobject metrics_object;
- int i;
short value;
info = (struct androidfont_info *) font;
+ if (nglyphs == 1
+ && androidfont_check_cached_extents (info, *code, metrics))
+ return;
+
/* Allocate the arrays of code points and font metrics. */
codepoint_array
= (*android_java_env)->NewIntArray (android_java_env,
@@ -784,92 +903,103 @@ androidfont_text_extents (struct font *font, const unsigned int *code,
memory_full (0);
}
- metrics_array
- = (*android_java_env)->NewObjectArray (android_java_env,
- nglyphs,
- font_metrics_class.class,
- NULL);
- if (!metrics_array)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- ANDROID_DELETE_LOCAL_REF (metrics_array);
- memory_full (0);
- }
-
- if (sizeof (unsigned int) == sizeof (jint))
- /* Always true on every Android device. */
- (*android_java_env)->SetIntArrayRegion (android_java_env,
- codepoint_array,
- 0, nglyphs,
- (jint *) code);
- else
- emacs_abort ();
-
- for (i = 0; i < nglyphs; ++i)
- {
- metrics_object
- = (*android_java_env)->AllocObject (android_java_env,
- font_metrics_class.class);
+ verify (sizeof (unsigned int) == sizeof (jint));
- if (!metrics_object)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- ANDROID_DELETE_LOCAL_REF (metrics_array);
- ANDROID_DELETE_LOCAL_REF (codepoint_array);
- memory_full (0);
- }
+ /* Always true on every Android device. */
+ (*android_java_env)->SetIntArrayRegion (android_java_env,
+ codepoint_array,
+ 0, nglyphs,
+ (jint *) code);
- (*android_java_env)->SetObjectArrayElement (android_java_env,
- metrics_array, i,
- metrics_object);
- ANDROID_DELETE_LOCAL_REF (metrics_object);
- }
+ metrics_object
+ = (*android_java_env)->AllocObject (android_java_env,
+ font_metrics_class.class);
(*android_java_env)->CallVoidMethod (android_java_env,
font_driver,
font_driver_class.text_extents,
info->object, codepoint_array,
- metrics_array);
+ metrics_object);
if ((*android_java_env)->ExceptionCheck (android_java_env))
{
(*android_java_env)->ExceptionClear (android_java_env);
- ANDROID_DELETE_LOCAL_REF (metrics_array);
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
memory_full (0);
}
- for (i = 0; i < nglyphs; ++i)
- {
- metrics_object
- = (*android_java_env)->GetObjectArrayElement (android_java_env,
- metrics_array, i);
#define DO_CARDINAL_FIELD(field) \
value \
= (*android_java_env)->GetShortField (android_java_env, \
metrics_object, \
font_metrics_class.field); \
- metrics[i].field = value;
+ metrics->field = value;
- DO_CARDINAL_FIELD (lbearing);
- DO_CARDINAL_FIELD (rbearing);
- DO_CARDINAL_FIELD (width);
- DO_CARDINAL_FIELD (ascent);
- DO_CARDINAL_FIELD (descent);
+ DO_CARDINAL_FIELD (lbearing);
+ DO_CARDINAL_FIELD (rbearing);
+ DO_CARDINAL_FIELD (width);
+ DO_CARDINAL_FIELD (ascent);
+ DO_CARDINAL_FIELD (descent);
#undef DO_CARDINAL_FIELD
- ANDROID_DELETE_LOCAL_REF (metrics_object);
- }
-
- ANDROID_DELETE_LOCAL_REF (metrics_array);
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
+
+ /* Emacs spends a lot of time in androidfont_text_extents, which
+ makes calling JNI too slow. Cache the metrics for this single
+ glyph. */
+
+ if (nglyphs == 1)
+ androidfont_cache_text_extents (info, *code, metrics);
}
static Lisp_Object
androidfont_list_family (struct frame *f)
{
- return Qnil;
+ Lisp_Object families;
+ jarray family_array;
+ jobject string;
+ jsize i, length;
+ const char *family;
+
+ family_array
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.list_families);
+ if (!family_array)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ family_array);
+ families = Qnil;
+
+ for (i = 0; i < length; ++i)
+ {
+ string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ family_array, i);
+ family = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string, NULL);
+
+ if (!family)
+ {
+ ANDROID_DELETE_LOCAL_REF (string);
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ }
+
+ families = Fcons (build_string_from_utf8 (string), families);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ family);
+ ANDROID_DELETE_LOCAL_REF (string);
+ }
+
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ return Fnreverse (families);
}
struct font_driver androidfont_driver =
diff --git a/src/androidgui.h b/src/androidgui.h
index 43ccc86e5c7..b0ea820cfdf 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -201,6 +201,9 @@ enum android_event_type
ANDROID_KEY_PRESS,
ANDROID_KEY_RELEASE,
ANDROID_CONFIGURE_NOTIFY,
+ ANDROID_FOCUS_IN,
+ ANDROID_FOCUS_OUT,
+ ANDROID_WINDOW_ACTION,
};
struct android_any_event
@@ -209,6 +212,13 @@ struct android_any_event
android_window window;
};
+enum android_modifier_mask
+ {
+ ANDROID_SHIFT_MASK = 193,
+ ANDROID_CONTROL_MASK = 4096,
+ ANDROID_ALT_MASK = 2,
+ };
+
struct android_key_event
{
enum android_event_type type;
@@ -216,8 +226,18 @@ struct android_key_event
android_time time;
unsigned int state;
unsigned int keycode;
+ unsigned int unicode_char;
};
+/* These hard coded values are Android modifier keycodes derived
+ through experimentation. */
+
+#define ANDROID_IS_MODIFIER_KEY(key) \
+ ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \
+ || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \
+ || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \
+ || (key) == 63)
+
struct android_configure_event
{
enum android_event_type type;
@@ -227,12 +247,35 @@ struct android_configure_event
int width, height;
};
+struct android_focus_event
+{
+ enum android_event_type type;
+ android_window window;
+ android_time time;
+};
+
+struct android_window_action_event
+{
+ enum android_event_type type;
+
+ /* The window handle. This can be ANDROID_NONE. */
+ android_window window;
+
+ /* Numerical identifier for this action. If 0 and WINDOW is set,
+ then it means the frame associated with that window has been
+ destroyed. Otherwise, it means Emacs should create a new
+ frame. */
+ unsigned int action;
+};
+
union android_event
{
enum android_event_type type;
struct android_any_event xany;
struct android_key_event xkey;
struct android_configure_event xconfigure;
+ struct android_focus_event xfocus;
+ struct android_window_action_event xaction;
};
extern int android_pending (void);
@@ -303,9 +346,47 @@ extern android_pixmap android_create_pixmap (unsigned int, unsigned int,
extern void android_set_ts_origin (struct android_gc *, int, int);
extern void android_clear_area (android_window, int, int, unsigned int,
unsigned int);
+extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
+ unsigned int);
#endif
+
+
+/* Image support. Keep the API as similar to XImage as possible. To
+ avoid leaving a huge mess of "#ifndef ANDROID_STUBIFY" in image.c,
+ stubs should be defined for all functions. */
+
+enum android_image_format
+ {
+ ANDROID_Z_PIXMAP,
+ };
+
+struct android_image
+{
+ int width, height;
+ enum android_image_format format;
+ char *data;
+ int depth;
+ int bytes_per_line;
+ int bits_per_pixel;
+};
+
+extern struct android_image *android_create_image (unsigned int,
+ enum android_image_format,
+ char *, unsigned int,
+ unsigned int);
+extern void android_destroy_image (struct android_image *);
+
+extern void android_put_pixel (struct android_image *, int, int,
+ unsigned long);
+extern unsigned long android_get_pixel (struct android_image *, int, int);
+extern struct android_image *android_get_image (android_drawable,
+ enum android_image_format);
+extern void android_put_image (android_pixmap, struct android_image *);
+
+
+
/* X emulation stuff also needed while building stubs. */
extern struct android_gc *android_create_gc (enum android_gc_value_mask,
diff --git a/src/androidterm.c b/src/androidterm.c
index bb3c2a29737..0279da4f58d 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -39,6 +39,8 @@ struct android_display_info *x_display_list;
#ifndef ANDROID_STUBIFY
+#include <android/log.h>
+
enum
{
ANDROID_EVENT_NORMAL,
@@ -141,6 +143,124 @@ android_flush_dirty_back_buffer_on (struct frame *f)
show_back_buffer (f);
}
+/* Convert between the modifier bits Android uses and the modifier
+ bits Emacs uses. */
+
+static int
+android_android_to_emacs_modifiers (struct android_display_info *dpyinfo,
+ int state)
+{
+ return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0
+ | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0
+ | (state & ANDROID_ALT_MASK) ? meta_modifier : 0);
+}
+
+static int
+android_emacs_to_android_modifiers (struct android_display_info *dpyinfo,
+ intmax_t state)
+{
+ return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0
+ | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0
+ | (state & meta_modifier) ? ANDROID_ALT_MASK : 0);
+}
+
+static void android_frame_rehighlight (struct android_display_info *);
+
+static void
+android_lower_frame (struct frame *f)
+{
+ /* TODO. */
+}
+
+static void
+android_raise_frame (struct frame *f)
+{
+ /* TODO. */
+}
+
+static void
+android_new_focus_frame (struct android_display_info *dpyinfo,
+ struct frame *frame)
+{
+ struct frame *old_focus;
+
+ old_focus = dpyinfo->focus_frame;
+
+ if (frame != dpyinfo->focus_frame)
+ {
+ /* Set this before calling other routines, so that they see
+ the correct value of x_focus_frame. */
+ dpyinfo->focus_frame = frame;
+
+ if (old_focus && old_focus->auto_lower)
+ android_lower_frame (old_focus);
+
+ if (dpyinfo->focus_frame && dpyinfo->focus_frame->auto_raise)
+ dpyinfo->pending_autoraise_frame = dpyinfo->focus_frame;
+ else
+ dpyinfo->pending_autoraise_frame = NULL;
+ }
+
+ android_frame_rehighlight (dpyinfo);
+}
+
+static void
+android_focus_changed (int type, int state,
+ struct android_display_info *dpyinfo,
+ struct frame *frame, struct input_event *bufp)
+{
+ if (type == ANDROID_FOCUS_IN)
+ {
+ if (dpyinfo->x_focus_event_frame != frame)
+ {
+ android_new_focus_frame (dpyinfo, frame);
+ dpyinfo->x_focus_event_frame = frame;
+ bufp->kind = FOCUS_IN_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ frame->output_data.android->focus_state |= state;
+ }
+ else if (type == ANDROID_FOCUS_OUT)
+ {
+ frame->output_data.android->focus_state &= ~state;
+
+ if (dpyinfo->x_focus_event_frame == frame)
+ {
+ dpyinfo->x_focus_event_frame = 0;
+ android_new_focus_frame (dpyinfo, 0);
+
+ bufp->kind = FOCUS_OUT_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ if (frame->pointer_invisible)
+ android_toggle_invisible_pointer (frame, false);
+ }
+}
+
+static void
+android_detect_focus_change (struct android_display_info *dpyinfo,
+ struct frame *frame,
+ union android_event *event,
+ struct input_event *bufp)
+{
+ if (!frame)
+ return;
+
+ switch (event->type)
+ {
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_focus_changed (event->type, FOCUS_EXPLICIT,
+ dpyinfo, frame, bufp);
+ break;
+
+ default:
+ break;
+ }
+}
+
static int
handle_one_android_event (struct android_display_info *dpyinfo,
union android_event *event, int *finish,
@@ -149,11 +269,20 @@ handle_one_android_event (struct android_display_info *dpyinfo,
union android_event configureEvent;
struct frame *f, *any, *mouse_frame;
Mouse_HLInfo *hlinfo;
+ union buffered_input_event inev;
+ int modifiers, count;
+
+ /* It is okay for this to not resemble handle_one_xevent so much.
+ Differences in event handling code are much less nasty than
+ stuble differences in the graphics code. */
+ count = 0;
hlinfo = &dpyinfo->mouse_highlight;
*finish = ANDROID_EVENT_NORMAL;
any = android_window_to_frame (dpyinfo, event->xany.window);
+ EVENT_INIT (inev.ie);
+
switch (event->type)
{
case ANDROID_CONFIGURE_NOTIFY:
@@ -192,12 +321,15 @@ handle_one_android_event (struct android_display_info *dpyinfo,
case ANDROID_KEY_PRESS:
+ /* Set f to any. There are no ``outer windows'' on Android. */
+ f = any;
+
/* If mouse-highlight is an integer, input clears out
mouse highlighting. */
if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
&& (any == 0
- || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
- || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)))
+ || !EQ (any->tool_bar_window, hlinfo->mouse_face_window)
+ || !EQ (any->tab_bar_window, hlinfo->mouse_face_window)))
{
mouse_frame = hlinfo->mouse_face_mouse_frame;
@@ -208,13 +340,90 @@ handle_one_android_event (struct android_display_info *dpyinfo,
android_flush_dirty_back_buffer_on (mouse_frame);
}
+ event->xkey.state
+ |= android_emacs_to_android_modifiers (dpyinfo,
+ extra_keyboard_modifiers);
+ modifiers = event->xkey.state;
+
+ /* Common for all keysym input events. */
+ XSETFRAME (inev.ie.frame_or_window, any);
+ inev.ie.modifiers
+ = android_android_to_emacs_modifiers (dpyinfo, modifiers);
+ inev.ie.timestamp = event->xkey.time;
+
+ /* First deal with keysyms which have defined translations to
+ characters. */
+
+ if (event->xkey.unicode_char >= 32
+ && event->xkey.unicode_char < 128)
+ {
+ inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = event->xkey.unicode_char;
+ }
+ else if (event->xkey.unicode_char < 32)
+ {
+ /* If the key is a modifier key, just return. */
+ if (ANDROID_IS_MODIFIER_KEY (event->xkey.keycode))
+ goto done_keysym;
+
+ /* Next, deal with special ``characters'' by giving the
+ keycode to keyboard.c. */
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = event->xkey.keycode;
+ }
+ else
+ {
+ /* Finally, deal with Unicode characters. */
+ inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+ inev.ie.code = event->xkey.unicode_char;
+ }
+
+ goto done_keysym;
+
+ done_keysym:
+ goto OTHER;
+
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_detect_focus_change (dpyinfo, any, event, &inev.ie);
+ goto OTHER;
+
+ case ANDROID_WINDOW_ACTION:
+
+ f = any;
+
+ if (event->xaction.action == 0)
+ {
+ /* Action 0 either means to destroy a frame or to create a
+ new frame, depending on whether or not
+ event->xaction.window exists. */
+
+ if (event->xaction.window)
+ {
+ if (!f)
+ goto OTHER;
+
+ inev.ie.kind = DELETE_WINDOW_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ }
+ else
+ /* A new frame must be created. */;
+ }
+
+ goto OTHER;
+
default:
goto OTHER;
}
OTHER:
+ if (inev.ie.kind != NO_EVENT)
+ {
+ kbd_buffer_store_buffered_event (&inev, hold_quit);
+ count++;
+ }
- return 0;
+ return count;
}
static int
@@ -378,15 +587,52 @@ android_focus_frame (struct frame *f, bool noactivate)
}
static void
-android_frame_rehighlight (struct frame *f)
+android_frame_rehighlight (struct android_display_info *dpyinfo)
{
- /* TODO */
+ struct frame *old_highlight;
+
+ old_highlight = dpyinfo->highlight_frame;
+
+ if (dpyinfo->focus_frame)
+ {
+ dpyinfo->highlight_frame
+ = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->focus_frame)))
+ ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->focus_frame))
+ : dpyinfo->focus_frame);
+ if (!FRAME_LIVE_P (dpyinfo->highlight_frame))
+ {
+ fset_focus_frame (dpyinfo->focus_frame, Qnil);
+ dpyinfo->highlight_frame = dpyinfo->focus_frame;
+ }
+ }
+ else
+ dpyinfo->highlight_frame = 0;
+
+ 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
+ }
+}
+
+static void
+android_frame_rehighlight_hook (struct frame *f)
+{
+ android_frame_rehighlight (FRAME_DISPLAY_INFO (f));
}
static void
android_frame_raise_lower (struct frame *f, bool raise_flag)
{
- /* TODO */
+ if (raise_flag)
+ android_raise_frame (f);
+ else
+ android_lower_frame (f);
}
void
@@ -401,6 +647,10 @@ android_make_frame_visible (struct frame *f)
void
android_make_frame_invisible (struct frame *f)
{
+ /* Don't keep the highlight on an invisible frame. */
+ if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
+ FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+
android_unmap_window (FRAME_ANDROID_WINDOW (f));
SET_FRAME_VISIBLE (f, false);
@@ -584,6 +834,8 @@ android_free_frame_resources (struct frame *f)
if (f == dpyinfo->focus_frame)
dpyinfo->focus_frame = 0;
+ if (f == dpyinfo->x_focus_event_frame)
+ dpyinfo->x_focus_event_frame = 0;
if (f == dpyinfo->highlight_frame)
dpyinfo->highlight_frame = 0;
if (f == hlinfo->mouse_face_mouse_frame)
@@ -783,7 +1035,7 @@ android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
if (p->overlay_p)
{
clipmask = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
- 1, 0, 0);
+ 1, 0, 1);
gcv.clip_mask = clipmask;
gcv.clip_x_origin = p->x;
@@ -1606,158 +1858,47 @@ android_draw_image_foreground (struct glyph_string *s)
if (s->img->pixmap)
{
- if (s->img->mask)
- {
- unsigned long mask = (ANDROID_GC_CLIP_MASK
- | ANDROID_GC_CLIP_X_ORIGIN
- | ANDROID_GC_CLIP_Y_ORIGIN
- | ANDROID_GC_FUNCTION);
- struct android_gc_values xgcv;
- struct android_rectangle clip_rect, image_rect, r;
-
- xgcv.clip_mask = s->img->mask;
- xgcv.clip_x_origin = x - s->slice.x;
- xgcv.clip_y_origin = y - s->slice.y;
- xgcv.function = ANDROID_GC_COPY;
- android_change_gc (s->gc, mask, &xgcv);
-
- get_glyph_string_clip_rect (s, &clip_rect);
- image_rect.x = x;
- image_rect.y = y;
- image_rect.width = s->slice.width;
- image_rect.height = s->slice.height;
-
- if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
- android_copy_area (s->img->pixmap, FRAME_ANDROID_WINDOW (s->f),
- s->gc, s->slice.x + r.x - x,
- s->slice.y + r.y - y,
- r.x, r.y, r.width, r.height);
- }
- else
+ unsigned long mask = (ANDROID_GC_CLIP_MASK
+ | ANDROID_GC_CLIP_X_ORIGIN
+ | ANDROID_GC_CLIP_Y_ORIGIN
+ | ANDROID_GC_FUNCTION);
+ struct android_gc_values xgcv;
+ struct android_rectangle clip_rect, image_rect, r;
+
+ xgcv.clip_mask = s->img->mask;
+ xgcv.clip_x_origin = x - s->slice.x;
+ xgcv.clip_y_origin = y - s->slice.y;
+ xgcv.function = ANDROID_GC_COPY;
+ android_change_gc (s->gc, mask, &xgcv);
+
+ get_glyph_string_clip_rect (s, &clip_rect);
+ image_rect.x = x;
+ image_rect.y = y;
+ image_rect.width = s->slice.width;
+ image_rect.height = s->slice.height;
+
+ if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
+ android_copy_area (s->img->pixmap,
+ FRAME_ANDROID_WINDOW (s->f),
+ s->gc, s->slice.x + r.x - x,
+ s->slice.y + r.y - y,
+ r.width, r.height, r.x, r.y);
+
+ /* When the image has a mask, we can expect that at least part
+ of a mouse highlight or a block cursor will be visible. If
+ the image doesn't have a mask, make a block cursor visible by
+ drawing a rectangle around the image. I believe it's looking
+ better if we do nothing here for mouse-face. */
+ if (s->hl == DRAW_CURSOR && !s->img->mask)
{
- unsigned long mask = (ANDROID_GC_CLIP_MASK
- | ANDROID_GC_CLIP_X_ORIGIN
- | ANDROID_GC_CLIP_Y_ORIGIN
- | ANDROID_GC_FUNCTION);
- struct android_gc_values xgcv;
- struct android_rectangle clip_rect, image_rect, r;
-
- xgcv.clip_mask = s->img->mask;
- xgcv.clip_x_origin = x - s->slice.x;
- xgcv.clip_y_origin = y - s->slice.y;
- xgcv.function = ANDROID_GC_COPY;
- android_change_gc (s->gc, mask, &xgcv);
-
- get_glyph_string_clip_rect (s, &clip_rect);
- image_rect.x = x;
- image_rect.y = y;
- image_rect.width = s->slice.width;
- image_rect.height = s->slice.height;
-
- if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
- android_copy_area (s->img->pixmap,
- FRAME_ANDROID_WINDOW (s->f),
- s->gc, s->slice.x + r.x - x,
- s->slice.y + r.y - y,
- r.x, r.y, r.width, r.height);
-
- /* When the image has a mask, we can expect that at
- least part of a mouse highlight or a block cursor will
- be visible. If the image doesn't have a mask, make
- a block cursor visible by drawing a rectangle around
- the image. I believe it's looking better if we do
- nothing here for mouse-face. */
- if (s->hl == DRAW_CURSOR)
- {
- int relief = eabs (s->img->relief);
- android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
- x - relief, y - relief,
- s->slice.width + relief*2 - 1,
- s->slice.height + relief*2 - 1);
- }
+ int relief = eabs (s->img->relief);
+ android_draw_rectangle (FRAME_ANDROID_WINDOW (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,
- s->slice.width - 1, s->slice.height - 1);
-}
-
-/* Draw the foreground of image glyph string S to PIXMAP. */
-
-static void
-android_draw_image_foreground_1 (struct glyph_string *s,
- android_pixmap pixmap)
-{
- int x = 0;
- int y = s->ybase - s->y - image_ascent (s->img, s->face, &s->slice);
-
- /* If first glyph of S has a left box line, start drawing it to the
- right of that line. */
- if (s->face->box != FACE_NO_BOX
- && s->first_glyph->left_box_line_p
- && s->slice.x == 0)
- x += max (s->face->box_vertical_line_width, 0);
-
- /* If there is a margin around the image, adjust x- and y-position
- by that margin. */
- if (s->slice.x == 0)
- x += s->img->hmargin;
- if (s->slice.y == 0)
- y += s->img->vmargin;
- if (s->img->pixmap)
- {
- if (s->img->mask)
- {
- unsigned long mask = (ANDROID_GC_CLIP_MASK
- | ANDROID_GC_CLIP_X_ORIGIN
- | ANDROID_GC_CLIP_Y_ORIGIN
- | ANDROID_GC_FUNCTION);
- struct android_gc_values xgcv;
- struct android_rectangle clip_rect, image_rect, r;
-
- xgcv.clip_mask = s->img->mask;
- xgcv.clip_x_origin = x - s->slice.x;
- xgcv.clip_y_origin = y - s->slice.y;
- xgcv.function = ANDROID_GC_COPY;
- android_change_gc (s->gc, mask, &xgcv);
-
- get_glyph_string_clip_rect (s, &clip_rect);
- image_rect.x = x;
- image_rect.y = y;
- image_rect.width = s->slice.width;
- image_rect.height = s->slice.height;
-
- if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
- android_copy_area (s->img->pixmap, pixmap, s->gc,
- s->slice.x + r.x - x,
- s->slice.y + r.y - y,
- r.x, r.y, r.width, r.height);
-
- android_set_clip_mask (s->gc, ANDROID_NONE);
- }
- else
- {
- android_copy_area (s->img->pixmap, pixmap, s->gc,
- s->slice.x, s->slice.y,
- s->slice.width, s->slice.height, x, y);
-
- /* When the image has a mask, we can expect that at
- least part of a mouse highlight or a block cursor will
- be visible. If the image doesn't have a mask, make
- a block cursor visible by drawing a rectangle around
- the image. I believe it's looking better if we do
- nothing here for mouse-face. */
- if (s->hl == DRAW_CURSOR)
- {
- int r = eabs (s->img->relief);
- android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
- s->gc, x - r, y - r,
- s->slice.width + r*2 - 1,
- s->slice.height + r*2 - 1);
- }
- }
+ android_set_clip_mask (s->gc, ANDROID_NONE);
}
else
/* Draw a rectangle if image could not be loaded. */
@@ -1771,7 +1912,6 @@ android_draw_image_glyph_string (struct glyph_string *s)
int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
int height;
- android_pixmap pixmap = ANDROID_NONE;
height = s->height;
if (s->slice.y == 0)
@@ -1793,79 +1933,27 @@ android_draw_image_glyph_string (struct glyph_string *s)
if (s->stippled_p)
s->row->stipple_p = true;
- if (s->img->mask)
- {
- /* Create a pixmap as large as the glyph string. Fill it
- with the background color. Copy the image to it, using
- its mask. Copy the temporary pixmap to the display. */
- int depth = FRAME_DISPLAY_INFO (s->f)->n_planes;
-
- /* Create a pixmap as large as the glyph string. */
- pixmap = android_create_pixmap (s->background_width,
- s->height, depth);
-
- /* Don't clip in the following because we're working on the
- pixmap. */
- android_set_clip_mask (s->gc, ANDROID_NONE);
-
- /* Fill the pixmap with the background color/stipple. */
- if (s->stippled_p)
- {
- /* Fill background with a stipple pattern. */
- android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
- android_set_ts_origin (s->gc, - s->x, - s->y);
- android_fill_rectangle (pixmap, s->gc,
- 0, 0, s->background_width, s->height);
- android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
- android_set_ts_origin (s->gc, 0, 0);
- }
- else
- {
- struct android_gc_values xgcv;
+ int x = s->x;
+ int y = s->y;
+ int width = s->background_width;
- android_get_gc_values (s->gc, (ANDROID_GC_FOREGROUND
- | ANDROID_GC_BACKGROUND),
- &xgcv);
- android_set_foreground (s->gc, xgcv.background);
- android_fill_rectangle (pixmap, s->gc,
- 0, 0, s->background_width, s->height);
- android_set_background (s->gc, xgcv.foreground);
- }
- }
- else
+ if (s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
{
- int x = s->x;
- int y = s->y;
- int width = s->background_width;
+ x += box_line_hwidth;
+ width -= box_line_hwidth;
+ }
- if (s->first_glyph->left_box_line_p
- && s->slice.x == 0)
- {
- x += box_line_hwidth;
- width -= box_line_hwidth;
- }
+ if (s->slice.y == 0)
+ y += box_line_vwidth;
- if (s->slice.y == 0)
- y += box_line_vwidth;
-
- android_draw_glyph_string_bg_rect (s, x, y, width, height);
- }
+ android_draw_glyph_string_bg_rect (s, x, y, width, height);
s->background_filled_p = true;
}
/* Draw the foreground. */
- if (pixmap != ANDROID_NONE)
- {
- android_draw_image_foreground_1 (s, pixmap);
- android_set_glyph_string_clipping (s);
- android_copy_area (pixmap, FRAME_ANDROID_WINDOW (s->f), s->gc,
- 0, 0, s->background_width, s->height, s->x,
- s->y);
- android_free_pixmap (pixmap);
- }
- else
- android_draw_image_foreground (s);
+ android_draw_image_foreground (s);
/* If we must draw a relief around the image, do it. */
if (s->img->relief
@@ -3047,7 +3135,7 @@ android_create_terminal (struct android_display_info *dpyinfo)
terminal->mouse_position_hook = android_mouse_position;
terminal->get_focus_frame = android_get_focus_frame;
terminal->focus_frame_hook = android_focus_frame;
- terminal->frame_rehighlight_hook = android_frame_rehighlight;
+ terminal->frame_rehighlight_hook = android_frame_rehighlight_hook;
terminal->frame_raise_lower_hook = android_frame_raise_lower;
terminal->frame_visible_invisible_hook
= android_make_frame_visible_invisible;
@@ -3117,9 +3205,12 @@ android_term_init (void)
dpyinfo->color_map = color_map;
- /* TODO! */
- dpyinfo->resx = 96.0;
- dpyinfo->resy = 96.0;
+#ifndef ANDROID_STUBIFY
+
+ dpyinfo->resx = android_pixel_density_x;
+ dpyinfo->resy = android_pixel_density_y;
+
+#endif
/* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */
dpyinfo->smallest_font_height = 1;
@@ -3130,6 +3221,9 @@ android_term_init (void)
/* The display "connection" is now set up, and it must never go
away. */
terminal->reference_count = 30000;
+
+ /* Set the baud rate to the same value it gets set to on X. */
+ baud_rate = 19200;
}
diff --git a/src/androidterm.h b/src/androidterm.h
index 3a0c9f60555..c834ffb70e5 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -28,8 +28,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
struct android_bitmap_record
{
- /* The image backing the bitmap. */
- Emacs_Pixmap img;
+ /* The image backing the bitmap and its mask. */
+ android_pixmap pixmap, mask;
/* The file from which it comes. */
char *file;
@@ -37,8 +37,11 @@ struct android_bitmap_record
/* The number of references to it. */
int refcount;
- /* The height and width. */
- int height, width;
+ /* The height and width and the depth. */
+ int height, width, depth;
+
+ /* Whether or not there is a mask. */
+ bool have_mask;
};
struct android_display_info
@@ -95,6 +98,9 @@ struct android_display_info
/* The frame currently with the input focus. */
struct frame *focus_frame;
+ /* The last frame mentioned in a focus event. */
+ struct frame *x_focus_event_frame;
+
/* The frame which currently has the visual highlight, and should
get keyboard input. It points to the focus frame's selected
window's frame, but can differ. */
@@ -206,8 +212,24 @@ struct android_output
/* The background for which the above relief GCs were set up.
They are changed only when a different background is involved. */
unsigned long relief_background;
+
+ /* Focus state. Only present for consistency with X; it is actually
+ a boolean. */
+ int focus_state;
};
+enum
+ {
+ /* Values for focus_state, used as bit mask. EXPLICIT means we
+ received a FocusIn for the frame and know it has the focus.
+ IMPLICIT means we received an EnterNotify and the frame may
+ have the focus if no window manager is running. FocusOut and
+ LeaveNotify clears EXPLICIT/IMPLICIT. */
+ FOCUS_NONE = 0,
+ FOCUS_IMPLICIT = 1,
+ FOCUS_EXPLICIT = 2
+ };
+
/* Return the Android output data for frame F. */
#define FRAME_ANDROID_OUTPUT(f) ((f)->output_data.android)
#define FRAME_OUTPUT_DATA(f) ((f)->output_data.android)
diff --git a/src/dispextern.h b/src/dispextern.h
index e521dffc37c..c9b3495934b 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -160,8 +160,8 @@ typedef Emacs_Pixmap Emacs_Pix_Context;
#ifdef HAVE_ANDROID
#include "androidgui.h"
typedef struct android_display_info Display_Info;
-typedef Emacs_Pixmap Emacs_Pix_Container;
-typedef Emacs_Pixmap Emacs_Pix_Context;
+typedef struct android_image *Emacs_Pix_Container;
+typedef struct android_image *Emacs_Pix_Context;
#endif
#ifdef HAVE_WINDOW_SYSTEM
@@ -3119,6 +3119,13 @@ struct image
int original_width, original_height;
# endif
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+ /* Android images of the image, corresponding to the above Pixmaps.
+ Non-NULL means it and its Pixmap counterpart may be out of sync
+ and the latter is outdated. NULL means the X image has been
+ synchronized to Pixmap. */
+ struct android_image *ximg, *mask_img;
+#endif /* HAVE_ANDROID */
#ifdef HAVE_NTGUI
XFORM xform;
#endif
diff --git a/src/emacs.c b/src/emacs.c
index 9ef58ca412e..d3c53dd8051 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1440,9 +1440,8 @@ main (int argc, char **argv)
bool only_version = false;
sort_args (argc, argv);
old_argc = argc, argc = 0;
- while (argv[argc]
- /* Don't allow going past argv. */
- && argc < old_argc) argc++;
+ /* Don't allow going past argv. */
+ while (argc < old_argc && argv[argc]) argc++;
skip_args = 0;
if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
diff --git a/src/fileio.c b/src/fileio.c
index 1d22c51ebaa..0f205283d7e 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -898,6 +898,10 @@ user_homedir (char const *name)
p[length] = 0;
struct passwd *pw = getpwnam (p);
SAFE_FREE ();
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (pw && !pw->pw_dir && pw->pw_uid == getuid ())
+ return (char *) android_get_home_directory ();
+#endif
if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
return NULL;
return pw->pw_dir;
@@ -1888,6 +1892,11 @@ get_homedir (void)
pw = getpwuid (getuid ());
if (pw)
home = pw->pw_dir;
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!home && pw && pw->pw_uid == getuid ())
+ return android_get_home_directory ();
+#endif
if (!home)
return "";
}
diff --git a/src/font.h b/src/font.h
index c1ab26b87cb..1db94a9f3f8 100644
--- a/src/font.h
+++ b/src/font.h
@@ -547,8 +547,14 @@ CHECK_FONT_GET_OBJECT (Lisp_Object x)
return XFONT_OBJECT (x);
}
+#ifndef HAVE_ANDROID
/* Number of pt per inch (from the TeXbook). */
#define PT_PER_INCH 72.27
+#else
+/* Android uses this value instead to compensate for different device
+ dimensions. */
+#define PT_PER_INCH 160.00
+#endif
/* Return a pixel size (integer) corresponding to POINT size (double)
on resolution DPI. */
diff --git a/src/fringe.c b/src/fringe.c
index 5d7c8dca998..f10bc424239 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -1422,25 +1422,30 @@ If BITMAP overrides a standard fringe bitmap, the original bitmap is restored.
On X, we bit-swap the built-in bitmaps and reduce bitmap
from short to char array if width is <= 8 bits.
+ The Android port tries to follow X as closely as possible, so do
+ that there too.
+
On MAC with big-endian CPU, we need to byte-swap each short.
On W32 and MAC (little endian), there's no need to do this.
*/
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
-static const unsigned char swap_nibble[16] = {
- 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
- 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
- 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
- 0x3, 0xb, 0x7, 0xf}; /* 0011 1011 0111 1111 */
-#endif /* HAVE_X_WINDOWS */
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_ANDROID)
+static const unsigned char swap_nibble[16] =
+ {
+ 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
+ 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
+ 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
+ 0x3, 0xb, 0x7, 0xf, /* 0011 1011 0111 1111 */
+ };
+#endif
static void
init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
{
if (once_p || fb->dynamic)
{
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
unsigned short *bits = fb->bits;
int j;
@@ -1488,7 +1493,7 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
}
}
#endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
unsigned short *bits = fb->bits;
diff --git a/src/image.c b/src/image.c
index 986dd7aada4..08d092e2c6e 100644
--- a/src/image.c
+++ b/src/image.c
@@ -177,11 +177,14 @@ typedef struct haiku_bitmap_record Bitmap_Record;
#ifdef HAVE_ANDROID
#include "androidterm.h"
+
typedef struct android_bitmap_record Bitmap_Record;
-/* TODO: implement images on Android. */
-#define GET_PIXEL(ximg, x, y) 0
-#define PUT_PIXEL(ximg, x, y, pixel) ((void) (pixel))
+typedef struct android_image XImage;
+typedef android_pixmap Pixmap;
+
+#define GET_PIXEL(ximg, x, y) android_get_pixel (ximg, x, y)
+#define PUT_PIXEL(ximg, x, y, pixel) android_put_pixel (ximg, x, y, pixel)
#define NO_PIXMAP 0
#define PIX_MASK_RETAIN 0
@@ -507,6 +510,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return -1;
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_pixmap bitmap;
+
+ bitmap = android_create_bitmap_from_data (bits, width, height);
+
+ if (!bitmap)
+ return -1;
+#else
+ ((void) dpyinfo);
+ emacs_abort ();
+#endif
+
#ifdef HAVE_NTGUI
Lisp_Object frame UNINIT; /* The value is not used. */
Emacs_Pixmap bitmap;
@@ -619,14 +634,14 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
dpyinfo->bitmaps[id - 1].width = width;
dpyinfo->bitmaps[id - 1].refcount = 1;
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS && defined HAVE_ANDROID
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
dpyinfo->bitmaps[id - 1].have_mask = false;
dpyinfo->bitmaps[id - 1].depth = 1;
#ifdef USE_CAIRO
dpyinfo->bitmaps[id - 1].stipple = NULL;
#endif /* USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#ifdef HAVE_NTGUI
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
@@ -637,7 +652,7 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return id;
}
-#if defined HAVE_HAIKU || defined HAVE_NS
+#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_ANDROID
static char *slurp_file (int, ptrdiff_t *);
static Lisp_Object image_find_image_fd (Lisp_Object, int *);
static bool xbm_read_bitmap_data (struct frame *, char *, char *,
@@ -861,8 +876,62 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
/* This function should never be called when building stubs. */
emacs_abort ();
#else
- /* you lose, not yet implemented TODO */
- return 0;
+ ptrdiff_t id, size;
+ int fd, width, height, rc;
+ char *contents, *data;
+ Lisp_Object found;
+ android_pixmap bitmap;
+
+ /* Look for an existing bitmap with the same name. */
+ for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+ {
+ if (dpyinfo->bitmaps[id].refcount
+ && dpyinfo->bitmaps[id].file
+ && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+ {
+ ++dpyinfo->bitmaps[id].refcount;
+ return id + 1;
+ }
+ }
+
+ /* Search bitmap-file-path for the file, if appropriate. */
+ if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+ make_fixnum (R_OK), false, false)
+ < 0)
+ return -1;
+
+ if (!STRINGP (image_find_image_fd (file, &fd))
+ && !STRINGP (image_find_image_fd (found, &fd)))
+ return -1;
+
+ contents = slurp_file (fd, &size);
+
+ if (!contents)
+ return -1;
+
+ rc = xbm_read_bitmap_data (f, contents, contents + size,
+ &width, &height, &data, 0);
+
+ if (!rc)
+ {
+ xfree (contents);
+ return -1;
+ }
+
+ xfree (contents);
+ bitmap = android_create_bitmap_from_data (data, width, height);
+ xfree (data);
+
+ id = image_allocate_bitmap_record (f);
+ dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+ dpyinfo->bitmaps[id - 1].have_mask = false;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].height = height;
+ dpyinfo->bitmaps[id - 1].width = width;
+
+ return id;
#endif
#endif
}
@@ -882,6 +951,13 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
#endif /* USE_CAIRO */
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_free_pixmap (bm->pixmap);
+
+ if (bm->have_mask)
+ android_free_pixmap (bm->pixmap);
+#endif
+
#ifdef HAVE_NTGUI
DeleteObject (bm->pixmap);
#endif /* HAVE_NTGUI */
@@ -969,7 +1045,7 @@ static void image_unget_x_image (struct image *, bool, Emacs_Pix_Container);
image_unget_x_image (img, mask_p, ximg)
#endif
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
#ifndef USE_CAIRO
static void image_sync_to_pixmaps (struct frame *, struct image *);
@@ -983,6 +1059,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, int, int, int,
XImage **, Pixmap *);
static void x_destroy_x_image (XImage *);
+#if defined HAVE_X_WINDOWS
+
/* Create a mask of a bitmap. Note is this not a perfect mask.
It's nicer with some borders in this context */
@@ -1079,7 +1157,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
x_destroy_x_image (mask_img);
}
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
/***********************************************************************
Image types
@@ -1113,7 +1193,7 @@ struct image_type
#if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \
defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \
defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \
- defined HAVE_WEBP
+ defined HAVE_WEBP || defined HAVE_ANDROID
# ifdef WINDOWSNT
# define IMAGE_TYPE_INIT(f) f
# else
@@ -1615,7 +1695,7 @@ prepare_image_for_display (struct frame *f, struct image *img)
}
unblock_input ();
}
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!img->load_failed_p)
{
block_input ();
@@ -1822,7 +1902,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
/* NOTE (HAVE_NS): background color is NOT an indexed color! */
img->background_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->ximg)
{
image_destroy_x_image (img->ximg);
@@ -1840,7 +1920,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
img->mask = NO_PIXMAP;
img->background_transparent_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->mask_img)
{
image_destroy_x_image (img->mask_img);
@@ -2212,11 +2292,11 @@ image_size_in_bytes (struct image *img)
if (msk)
size += msk->height * msk->bytes_per_line;
-#elif defined HAVE_X_WINDOWS
- /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask,
- to avoid having to query the server. */
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+ /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp
+ for mask, to avoid having to query the server. */
if (img->pixmap != NO_PIXMAP)
- size += img->width * img->height * 3;
+ size += img->width * img->height * 4;
if (img->mask != NO_PIXMAP)
size += img->width * img->height / 8;
@@ -3195,9 +3275,12 @@ mark_image_cache (struct image_cache *c)
/***********************************************************************
X / NS / W32 support code
+ Most of this code is shared with Android to make
+ it easier to maintain.
***********************************************************************/
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+
static bool
x_check_image_size (XImage *ximg, int width, int height)
{
@@ -3213,7 +3296,11 @@ x_check_image_size (XImage *ximg, int width, int height)
int bitmap_pad, depth, bytes_per_line;
if (ximg)
{
+#ifndef HAVE_ANDROID
bitmap_pad = ximg->bitmap_pad;
+#else
+ bitmap_pad = (ximg->depth == 1 ? 8 : 32);
+#endif
depth = ximg->depth;
bytes_per_line = ximg->bytes_per_line;
}
@@ -3231,16 +3318,23 @@ static bool
x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
XImage **ximg, Pixmap *pixmap)
{
+#ifndef HAVE_ANDROID
Display *display = FRAME_X_DISPLAY (f);
Drawable drawable = FRAME_X_DRAWABLE (f);
+#endif
eassert (input_blocked_p ());
if (depth <= 0)
depth = FRAME_DISPLAY_INFO (f)->n_planes;
+#ifndef HAVE_ANDROID
*ximg = XCreateImage (display, FRAME_X_VISUAL (f),
depth, ZPixmap, 0, NULL, width, height,
depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+#else
+ *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width,
+ height);
+#endif
if (*ximg == NULL)
{
image_error ("Unable to allocate X image");
@@ -3260,7 +3354,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
(*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
/* Allocate a pixmap of the same size. */
+#ifndef HAVE_ANDROID
*pixmap = XCreatePixmap (display, drawable, width, height, depth);
+#else
+#ifndef ANDROID_STUBIFY
+ *pixmap = android_create_pixmap (width, height, depth);
+#else
+ emacs_abort ();
+#endif
+#endif
if (*pixmap == NO_PIXMAP)
{
x_destroy_x_image (*ximg);
@@ -3281,7 +3383,11 @@ x_destroy_x_image (XImage *ximg)
ximg->data = NULL;
}
+#ifndef HAVE_ANDROID
XDestroyImage (ximg);
+#else
+ android_destroy_image (ximg);
+#endif
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -3348,7 +3454,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth)
static bool
image_check_image_size (Emacs_Pix_Container ximg, int width, int height)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
return x_check_image_size (ximg, width, height);
#else
/* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases.
@@ -3372,16 +3478,6 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
Emacs_Pix_Container *pimg,
Emacs_Pixmap *pixmap, Picture *picture)
{
-#ifdef HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
- /* This function should never be called when building stubs. */
- emacs_abort ();
-#else
- /* you lose, not yet implemented TODO */
- return false;
-#endif
-#endif
-
#ifdef USE_CAIRO
eassert (input_blocked_p ());
@@ -3396,7 +3492,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
*pimg = *pixmap;
return 1;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
return 0;
# ifdef HAVE_XRENDER
@@ -3542,7 +3638,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
static void
image_destroy_x_image (Emacs_Pix_Container pimg)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
x_destroy_x_image (pimg);
#else
eassert (input_blocked_p ());
@@ -3581,7 +3677,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0,
pimg->width, pimg->height);
XFreeGC (FRAME_X_DISPLAY (f), gc);
-#endif /* HAVE_X_WINDOWS */
+#elif defined HAVE_ANDROID
+ android_put_image (pixmap, pimg);
+#endif
#ifdef HAVE_NS
eassert (pimg == pixmap);
@@ -3618,7 +3716,7 @@ static void
image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
bool mask_p)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (!mask_p)
{
eassert (img->ximg == NULL);
@@ -3636,7 +3734,7 @@ image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
#endif
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
/* Put the X images recorded in IMG on frame F into pixmaps, then free
the X images and their buffers. */
@@ -3690,19 +3788,9 @@ image_unget_x_image_or_dc (struct image *img, bool mask_p,
static Emacs_Pix_Container
image_get_x_image (struct frame *f, struct image *img, bool mask_p)
{
-#ifdef HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
- /* This function should never be called when building stubs. */
- emacs_abort ();
-#else
- /* you lose, not yet implemented TODO */
- return 0;
-#endif
-#endif
-
#if defined USE_CAIRO || defined (HAVE_HAIKU)
return !mask_p ? img->pixmap : img->mask;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
@@ -3712,9 +3800,15 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p)
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->original_width, img->original_height, ~0, ZPixmap);
#endif
+#ifndef HAVE_ANDROID
else
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+ else
+ return android_get_image (!mask_p ? img->pixmap : img->mask,
+ ANDROID_Z_PIXMAP);
+#endif
#elif defined (HAVE_NS)
Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask;
@@ -3727,13 +3821,18 @@ static void
image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
{
#ifdef USE_CAIRO
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
eassert (ximg == ximg_in_img);
+#ifdef HAVE_ANDROID
+ else
+ android_destroy_image (ximg);
+#else
else
XDestroyImage (ximg);
+#endif
#elif defined (HAVE_NS)
ns_release_object (ximg);
#endif
@@ -4256,6 +4355,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
img->picture = x_create_xrender_picture (f, img->pixmap, 0);
# endif
+#elif defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+ img->pixmap
+ = android_create_pixmap_from_bitmap_data (data, img->width, img->height,
+ fg, bg,
+ FRAME_DISPLAY_INFO (f)->n_planes);
+#else
+ emacs_abort ();
+#endif
#elif defined HAVE_NTGUI
img->pixmap
= w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@@ -4686,7 +4794,8 @@ xbm_load (struct frame *f, struct image *img)
XPM images
***********************************************************************/
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
static bool xpm_image_p (Lisp_Object object);
static bool xpm_load (struct frame *f, struct image *img);
@@ -4716,7 +4825,8 @@ static bool xpm_load (struct frame *f, struct image *img);
#endif /* not HAVE_NTGUI */
#endif /* HAVE_XPM */
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS \
+ || defined HAVE_HAIKU || defined HAVE_ANDROID
/* Indices of image specification fields in xpm_format, below. */
@@ -4736,7 +4846,8 @@ enum xpm_keyword_index
XPM_LAST
};
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Vector of image_keyword structures describing the format
of valid XPM image specifications. */
@@ -4978,7 +5089,8 @@ init_xpm_functions (void)
#endif /* WINDOWSNT */
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Value is true if COLOR_SYMBOLS is a valid color symbols list
for XPM images. Such a list must consist of conses whose car and
cdr are strings. */
@@ -5014,9 +5126,9 @@ xpm_image_p (Lisp_Object object)
&& (! fmt[XPM_COLOR_SYMBOLS].count
|| xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
}
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
#if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
ptrdiff_t
@@ -5389,10 +5501,12 @@ xpm_load (struct frame *f, struct image *img)
#if (defined USE_CAIRO && defined HAVE_XPM) \
|| (defined HAVE_NS && !defined HAVE_XPM) \
|| (defined HAVE_HAIKU && !defined HAVE_XPM) \
- || (defined HAVE_PGTK && !defined HAVE_XPM)
+ || (defined HAVE_PGTK && !defined HAVE_XPM) \
+ || (defined HAVE_ANDROID && !defined HAVE_XPM)
-/* XPM support functions for NS and Haiku where libxpm is not available, and for
- Cairo. Only XPM version 3 (without any extensions) is supported. */
+/* XPM support functions for NS, Haiku and Android where libxpm is not
+ available, and for Cairo. Only XPM version 3 (without any
+ extensions) is supported. */
static void xpm_put_color_table_v (Lisp_Object, const char *,
int, Lisp_Object);
@@ -6492,11 +6606,16 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
#elif HAVE_HAIKU
be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
#elif HAVE_ANDROID
-#ifdef ANDROID_STUBIFY
- /* This function should never be called when building stubs. */
- emacs_abort ();
+#ifndef ANDROID_STUBIFY
+ struct android_gc *gc;
+
+ gc = android_create_gc (0, NULL);
+ android_set_foreground (gc, color);
+ android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1);
+ android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y);
+ android_free_gc (gc);
#else
- /* you lose, not yet implemented TODO */
+ emacs_abort ();
#endif
#endif
}
@@ -6552,7 +6671,7 @@ image_disable_image (struct frame *f, struct image *img)
#define MaskForeground(f) PIX_MASK_DRAW
#endif /* USE_CAIRO || HAVE_HAIKU */
-#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
+#if !defined USE_CAIRO && !defined HAVE_HAIKU
image_sync_to_pixmaps (f, img);
#endif /* !USE_CAIRO && !HAVE_HAIKU */
image_pixmap_draw_cross (f, img->pixmap, 0, 0, img->width, img->height,
@@ -12079,7 +12198,8 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
IMAGE_TYPE_INIT (init_jpeg_functions) },
#endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
{ SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
IMAGE_TYPE_INIT (init_xpm_functions) },
#endif
@@ -12138,7 +12258,7 @@ syms_of_image (void)
DEFVAR_LISP ("image-types", Vimage_types,
doc: /* List of potentially supported image types.
Each element of the list is a symbol for an image type, like `jpeg' or `png'.
-To check whether it is really supported, use `image-type-available-p'. */);
+ check whether it is really supported, use `image-type-available-p'. */);
Vimage_types = Qnil;
DEFVAR_LISP ("max-image-size", Vmax_image_size,
@@ -12241,7 +12361,8 @@ non-numeric, there is no explicit limit on the size of images. */);
add_image_type (Qxbm);
#if defined (HAVE_XPM) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+ || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
DEFSYM (Qxpm, "xpm");
add_image_type (Qxpm);
#endif
diff --git a/src/keyboard.c b/src/keyboard.c
index 7bf89ac7d4b..8eec833a9ca 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4909,7 +4909,58 @@ static const char *const lispy_accent_keys[] =
"dead-horn",
};
-#ifdef HAVE_NTGUI
+#ifdef HAVE_ANDROID
+#define FUNCTION_KEY_OFFSET 0
+
+const char *const lispy_function_keys[] =
+ {
+ /* All elements in this array default to 0, except for the few
+ function keys that Emacs recognizes. */
+ [111] = "escape",
+ [121] = "break",
+ [122] = "home",
+ [123] = "end",
+ [124] = "insert",
+ [131] = "f1",
+ [132] = "f2",
+ [133] = "f3",
+ [134] = "f4",
+ [135] = "f5",
+ [136] = "f6",
+ [137] = "f7",
+ [138] = "f8",
+ [139] = "f9",
+ [140] = "f10",
+ [141] = "f11",
+ [142] = "f12",
+ [160] = "kp-ret",
+ [19] = "up",
+ [20] = "down",
+ [213] = "muhenkan",
+ [214] = "henkan",
+ [215] = "hiragana-katakana",
+ [218] = "kana",
+ [21] = "left",
+ [22] = "right",
+ [259] = "help",
+ [268] = "kp-up-left",
+ [269] = "kp-down-left",
+ [270] = "kp-up-right",
+ [271] = "kp-down-right",
+ [277] = "cut",
+ [278] = "copy",
+ [279] = "paste",
+ [28] = "clear",
+ [4] = "back",
+ [61] = "tab",
+ [66] = "return",
+ [67] = "backspace",
+ [82] = "menu",
+ [92] = "page-up",
+ [93] = "page-down",
+ };
+
+#elif defined HAVE_NTGUI
#define FUNCTION_KEY_OFFSET 0x0
const char *const lispy_function_keys[] =
diff --git a/src/lread.c b/src/lread.c
index eb029f01623..15273ad34fa 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -2073,7 +2073,7 @@ static void
build_load_history (Lisp_Object filename, bool entire)
{
Lisp_Object tail, prev, newelt;
- Lisp_Object tem, tem2;
+ Lisp_Object tem, tem2, association;
bool foundit = 0;
tail = Vload_history;
@@ -2088,7 +2088,7 @@ build_load_history (Lisp_Object filename, bool entire)
{
foundit = 1;
- /* If we're loading the entire file, remove old data. */
+ /* If we're loading the entire file, remove old data. */
if (entire)
{
if (NILP (prev))
@@ -2096,8 +2096,8 @@ build_load_history (Lisp_Object filename, bool entire)
else
Fsetcdr (prev, XCDR (tail));
}
-
- /* Otherwise, cons on new symbols that are not already members. */
+ /* Otherwise, cons on new symbols that are not already
+ members. */
else
{
tem2 = Vcurrent_load_list;
@@ -2122,8 +2122,16 @@ build_load_history (Lisp_Object filename, bool entire)
front of load-history, the most-recently-loaded position. Also
do this if we didn't find an existing member for the file. */
if (entire || !foundit)
- Vload_history = Fcons (Fnreverse (Vcurrent_load_list),
- Vload_history);
+ {
+ association = Fnreverse (Vcurrent_load_list);
+
+ if (!NILP (association) && STRINGP (XCAR (association)))
+ /* readevalloop can be called with SOURCENAME set to some
+ nonsense value, meaning the car of ASSOCIATION will be nil
+ (or worse something else), leading to an invalid
+ Vload_history. Ignore such invalid entries. */
+ Vload_history = Fcons (association, Vload_history);
+ }
}
static void