* 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.
lib lib-src lisp nt: Makefile
$(MAKE) -C $@ all
-java: lisp
+java: lisp info
$(MAKE) -C $@ all
xcompile: 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
# 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
# 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,
;;
*) 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])
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
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
*) 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])
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
ANDROID_CFLAGS="-fPIC -fvisibility=hidden"
# Link with libraries required for Android support.
- ANDROID_LIBS="-landroid -llog"
+ ANDROID_LIBS="-landroid -llog -ljnigraphics"
fi
fi
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" />
</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"
# 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
# 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} ; \
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
/* 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 ();
}
};
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;
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 ()
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);
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);
}
}
}
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
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);
import java.util.List;
+import android.os.Build;
+
public abstract class EmacsFontDriver
{
/* Font weights. */
public Integer size;
public Integer spacing;
public Integer avgwidth;
+ public Integer dpi;
@Override
public String
+ " weight: " + weight
+ " slant: " + slant
+ " spacing: " + spacing
- + " avgwidth: " + avgwidth);
+ + " avgwidth: " + avgwidth
+ + " dpi: " + dpi);
}
};
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
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 ();
}
};
--- /dev/null
+/* 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
+{
+
+}
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
/* 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
{
{
EmacsDrawable drawable, last;
Canvas canvas;
- EmacsGC gc, lastGC;
+ EmacsGC gc;
int i;
Paint paint;
Rect rect, offsetRect, copyRect;
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
{
}
}
}
-
- drawable.damageRect (rect);
- canvas.restore ();
}
}
}
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 ()
--- /dev/null
+/* 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;
+ }
+};
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;
width = UNSPECIFIED;
spacing = PROPORTIONAL;
+ this.typeface = typeface;
+
typefacePaint = new Paint ();
+ typefacePaint.setAntiAlias (true);
typefacePaint.setTypeface (typeface);
/* For the calls to measureText below. */
}
};
- private class Sdk7FontEntity extends FontEntity
+ protected class Sdk7FontEntity extends FontEntity
{
/* The typeface. */
public Sdk7Typeface typeface;
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)
{
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);
}
};
+ 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;
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
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
&& !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;
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
@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
{
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;
+ }
};
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.view.View;
+
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.os.Looper;
import android.os.IBinder;
import android.os.Handler;
+
import android.util.Log;
+import android.util.DisplayMetrics;
class Holder<T>
{
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
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;
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
{
+ " and libDir = " + libDir);
EmacsNative.setEmacsParams (manager, filesDir, libDir,
+ (float) pixelDensityX,
+ (float) pixelDensityY,
this);
/* Start the thread that runs Emacs. */
}
EmacsView
- getEmacsView (final EmacsWindow window)
+ getEmacsView (final EmacsWindow window, final int visibility,
+ final boolean isFocusedByDefault)
{
Runnable runnable;
final Holder<EmacsView> view;
synchronized (this)
{
view.thing = new EmacsView (window);
+ view.thing.setVisibility (visibility);
+ view.thing.setFocusedByDefault (isFocusedByDefault);
notify ();
}
}
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
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 ());
{
window.clearArea (x, y, width, height);
}
+
+ public void
+ appendPaintOperation (EmacsPaintReq op)
+ {
+ ensurePaintQueue ();
+ paintQueue.appendPaintOperation (op);
+ checkFlush ();
+ }
};
import android.view.SurfaceView;
import android.view.SurfaceHolder;
+import android.os.Build;
+
import android.graphics.Canvas;
import android.graphics.Rect;
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
/* Force a buffer swap now to get the contents of the Emacs
view on screen. */
- view.swapBuffers ();
+ view.swapBuffers (true);
}
@Override
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);
}
};
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
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);
}
}
public void
- swapBuffers ()
+ swapBuffers (boolean force)
{
Bitmap back;
Canvas canvas;
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)
return true;
}
+ @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);
+ }
};
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.
/* 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. */
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);
}
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);
}
});
}
public void
- setActivity (EmacsActivity activity)
+ setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
{
- synchronized (this)
- {
- activity = activity;
- }
+ attached = consumer;
+ }
+
+ public EmacsWindowAttachmentManager.WindowConsumer
+ getAttachedConsumer ()
+ {
+ return attached;
}
public void
public void
run ()
{
+
view.setVisibility (View.VISIBLE);
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
}
});
}
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);
}
};
--- /dev/null
+/* 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 ();
+ }
+ }
+};
;;; 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."
: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
(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))
(((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"))
#include <limits.h>
#include <signal.h>
#include <semaphore.h>
+#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
+#include <android/bitmap.h>
#include <android/log.h>
#include <linux/ashmem.h>
{
jclass class;
jmethodID constructor;
+ jmethodID constructor_mutable;
};
struct android_graphics_point
jmethodID constructor;
};
+struct android_emacs_drawable
+{
+ jclass class;
+ jmethodID get_bitmap;
+};
+
/* The asset manager being used. */
static AAssetManager *asset_manager;
/* 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;
/* 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;
+
\f
/* Event handling functions. Events are stored on a (circular) queue
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. */
/* 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;
}
{
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;
+}
+
\f
/* JNI functions called by Java. */
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];
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");
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);
assert (pixmap_class.c_name);
FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+ FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
#undef FIND_METHOD
}
#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)
{
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;
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;
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 (sendKeyRelease) (JNIEnv *env, jobject object,
jshort window, jlong time,
- jint state, jint keycode)
+ jint state, jint keycode,
+ jint unicode_char)
{
union android_event event;
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);
}
#define MAX_HANDLE 65535
-enum android_handle_type
- {
- ANDROID_HANDLE_WINDOW,
- ANDROID_HANDLE_GCONTEXT,
- ANDROID_HANDLE_PIXMAP,
- };
-
struct android_handle_entry
{
/* The type. */
android_handles[handle].handle = NULL;
}
-static jobject
+jobject
android_resolve_handle (android_handle handle,
enum android_handle_type type)
{
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)
{
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,
{
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)
{
(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);
+}
+
+\f
+
+#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. */
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
#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
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
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;
jmethodID has_char;
jmethodID text_extents;
jmethodID encode_char;
+ jmethodID draw;
/* Static methods. */
jmethodID create_font_driver;
jfieldID size;
jfieldID spacing;
jfieldID avgwidth;
+ jfieldID dpi;
};
struct android_emacs_font_metrics
/* The Java-side font. */
jobject object;
+
+ /* Cached glyph metrics arranged in a two dimensional array. */
+ struct font_metrics **metrics;
};
struct androidfont_entity
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,
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
}
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
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
}
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);
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. */
info->object, (jchar) 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,
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 =
ANDROID_KEY_PRESS,
ANDROID_KEY_RELEASE,
ANDROID_CONFIGURE_NOTIFY,
+ ANDROID_FOCUS_IN,
+ ANDROID_FOCUS_OUT,
+ ANDROID_WINDOW_ACTION,
};
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;
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;
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);
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
+\f
+
+/* 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 *);
+
+\f
+
/* X emulation stuff also needed while building stubs. */
extern struct android_gc *android_create_gc (enum android_gc_value_mask,
#ifndef ANDROID_STUBIFY
+#include <android/log.h>
+
enum
{
ANDROID_EVENT_NORMAL,
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,
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:
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;
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
}
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
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);
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)
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;
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. */
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)
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
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;
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;
/* 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;
}
\f
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;
/* 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
/* 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. */
/* 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)
#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
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
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))
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;
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 "";
}
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. */
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;
}
}
#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;
#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
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;
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;
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 *,
/* 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
}
#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 */
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 *);
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 */
x_destroy_x_image (mask_img);
}
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
/***********************************************************************
Image types
#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
}
unblock_input ();
}
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!img->load_failed_p)
{
block_input ();
/* 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);
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);
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;
\f
/***********************************************************************
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)
{
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;
}
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");
(*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);
ximg->data = NULL;
}
+#ifndef HAVE_ANDROID
XDestroyImage (ximg);
+#else
+ android_destroy_image (ximg);
+#endif
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
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.
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 ());
*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
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 ());
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);
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);
#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. */
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)
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;
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
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);
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);
#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. */
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. */
#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. */
&& (! 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
#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);
#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
}
#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,
{ 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
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,
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
"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[] =
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;
{
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))
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;
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