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