1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-30 17:10:51 -08:00
emacs/java/org/gnu/emacs/EmacsSdk7FontDriver.java
Po Lu a496509ced Update Android port
* .gitignore: Add new files.
* INSTALL.android: Explain how to build Emacs for ancient
versions of Android.
* admin/merge-gnulib (GNULIB_MODULES): Add getdelim.
* build-aux/config.guess (timestamp, version):
* build-aux/config.sub (timestamp, version): Autoupdate.
* configure.ac (BUILD_DETAILS, ANDROID_MIN_SDK):
(ANDROID_STUBIFY): Allow specifying CFLAGS via ANDROID_CFLAGS.
Add new configure tests for Android API version when not
explicitly specified.

* doc/emacs/android.texi (Android): Add reference to ``Other
Input Devices''.
(Android File System): Remove restrictions on directory-files on
the assets directory.
* doc/emacs/emacs.texi (Top): Add Other Input Devices to menu.
* doc/emacs/input.texi (Other Input Devices): New node.
* doc/lispref/commands.texi (Touchscreen Events): Document
changes to touchscreen input events.
* doc/lispref/frames.texi (Pop-Up Menus): Likewise.
* etc/NEWS: Announce changes.
* java/Makefile.in: Use lib-src/asset-directory-tool to generate
an `directory-tree' file placed in /assets.
* java/debug.sh: Large adjustments to support Android 2.2 and
later.

* java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
* java/org/gnu/emacs/EmacsCopyArea.java (perform):
* java/org/gnu/emacs/EmacsDialog.java (toAlertDialog):
* java/org/gnu/emacs/EmacsDrawLine.java (perform):
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
* java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
* java/org/gnu/emacs/EmacsFillPolygon.java (perform):
* java/org/gnu/emacs/EmacsFillRectangle.java (perform):
* java/org/gnu/emacs/EmacsGC.java (EmacsGC):
* java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
(destroyHandle):
* java/org/gnu/emacs/EmacsSdk7FontDriver.java (draw): Avoid
redundant canvas saves and restores.
* java/org/gnu/emacs/EmacsService.java (run):
* java/org/gnu/emacs/EmacsView.java (EmacsView):
(handleDirtyBitmap):
* java/org/gnu/emacs/EmacsWindow.java (changeWindowBackground)
(EmacsWindow): Make compatible with Android 2.2 and later.

* lib-src/Makefile.in (DONT_INSTALL): Add asset-directory-tool
on Android.:(asset-directory-tool{EXEEXT}): New target.
* lib-src/asset-directory-tool.c (struct directory_tree, xmalloc)
(main_1, main_2, main): New file.

* lib, m4: Merge from gnulib.  This will be reverted before
merging to master.

* lisp/button.el (button-map):
(push-button):
* lisp/frame.el (display-popup-menus-p): Improve touchscreen
support.
* lisp/subr.el (event-start):
(event-end): Handle touchscreen events.
* lisp/touch-screen.el (touch-screen-handle-timeout):
(touch-screen-handle-point-update):
(touch-screen-handle-point-up):
(touch-screen-track-tap):
(touch-screen-track-drag):
(touch-screen-drag-mode-line-1):
(touch-screen-drag-mode-line): New functions.
([mode-line touchscreen-begin]):
([bottom-divider touchscreen-begin]): Bind new events.

* lisp/wid-edit.el (widget-event-point):
(widget-keymap):
(widget-event-start):
(widget-button--check-and-call-button):
(widget-button-click): Improve touchscreen support.

* src/alloc.c (make_lisp_symbol): Avoid ICE on Android NDK GCC.
(mark_pinned_symbols): Likewise.

* src/android.c (struct android_emacs_window): New struct.
(window_class): New variable.
(android_run_select_thread): Add workaround for Android platform
bug.
(android_extract_long, android_scan_directory_tree): New
functions.
(android_file_access_p): Use those functions instead.
(android_init_emacs_window): New function.
(android_init_emacs_gc_class): Update signature of `markDirty'.
(android_change_gc, android_set_clip_rectangles): Tell the GC
whether or not clip rects were dirtied.
(android_swap_buffers): Do not look up method every time.
(struct android_dir): Adjust for new directory tree lookup.
(android_opendir, android_readdir, android_closedir): Likewise.
(android_four_corners_bilinear): Fix coding style.
(android_ftruncate): New function.
* src/android.h: Update prototypes.  Replace ftruncate with
android_ftruncate when necessary.

* src/androidterm.c (handle_one_android_event): Pacify GCC.  Fix
touch screen tool bar bug.
* src/emacs.c (using_utf8): Fix compilation error.
* src/fileio.c (Ffile_system_info): Return Qnil when fsusage.o
is not built.
* src/filelock.c (BOOT_TIME_FILE): Fix definition for Android.
* src/frame.c (Fx_parse_geometry): Fix uninitialized variable
uses.
* src/keyboard.c (lispy_function_keys): Fix `back'.
* src/menu.c (x_popup_menu_1): Handle touch screen events.
(Fx_popup_menu): Document changes.

* src/sfnt.c (main): Improve tests.

* src/sfntfont-android.c (sfntfont_android_put_glyphs): Fix
minor problem.
(init_sfntfont_android): Check for
HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL.
* src/sfntfont.c (struct sfnt_font_desc): New fields `adstyle'
and `languages'.
(sfnt_parse_style): Append tokens to adstyle.
(sfnt_parse_languages): New function.
(sfnt_enum_font_1): Parse supported languages and adstyle.
(sfntfont_list_1): Handle new fields.
(sfntfont_text_extents): Fix uninitialized variable use.
(syms_of_sfntfont, mark_sfntfont): Adjust accordingly.
2023-01-19 22:19:06 +08:00

541 lines
14 KiB
Java

/* 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 java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Canvas;
import android.util.Log;
import android.os.Build;
public class EmacsSdk7FontDriver extends EmacsFontDriver
{
private static final String TOFU_STRING = "\uDB3F\uDFFD";
private static final String EM_STRING = "m";
private static final String TAG = "EmacsSdk7FontDriver";
protected class Sdk7Typeface
{
/* The typeface and paint. */
public Typeface typeface;
public Paint typefacePaint;
public String familyName;
public int slant, width, weight, spacing;
public
Sdk7Typeface (String fileName, Typeface typeface)
{
String style, testString;
int index, measured, i;
float[] widths;
slant = NORMAL;
weight = REGULAR;
width = UNSPECIFIED;
spacing = PROPORTIONAL;
this.typeface = typeface;
typefacePaint = new Paint ();
typefacePaint.setAntiAlias (true);
typefacePaint.setTypeface (typeface);
/* For the calls to measureText below. */
typefacePaint.setTextSize (10.0f);
/* Parse the file name into some useful data. First, strip off
the extension. */
fileName = fileName.split ("\\.", 2)[0];
/* Next, split the file name by dashes. Everything before the
last dash is part of the family name. */
index = fileName.lastIndexOf ("-");
if (index > 0)
{
style = fileName.substring (index + 1, fileName.length ());
familyName = fileName.substring (0, index);
/* Look for something describing the weight. */
if (style.contains ("Thin"))
weight = THIN;
else if (style.contains ("UltraLight"))
weight = ULTRA_LIGHT;
else if (style.contains ("SemiLight"))
weight = SEMI_LIGHT;
else if (style.contains ("Light"))
weight = LIGHT;
else if (style.contains ("Medium"))
weight = MEDIUM;
else if (style.contains ("SemiBold"))
weight = SEMI_BOLD;
else if (style.contains ("ExtraBold"))
weight = EXTRA_BOLD;
else if (style.contains ("Bold"))
weight = BOLD;
else if (style.contains ("Black"))
weight = BLACK;
else if (style.contains ("UltraHeavy"))
weight = ULTRA_HEAVY;
/* And the slant. */
if (style.contains ("ReverseOblique"))
slant = OBLIQUE;
else if (style.contains ("ReverseItalic"))
slant = REVERSE_ITALIC;
else if (style.contains ("Italic"))
slant = ITALIC;
else if (style.contains ("Oblique"))
slant = OBLIQUE;
/* Finally, the width. */
if (style.contains ("UltraCondensed"))
width = ULTRA_CONDENSED;
else if (style.contains ("ExtraCondensed"))
width = EXTRA_CONDENSED;
else if (style.contains ("SemiCondensed"))
width = SEMI_CONDENSED;
else if (style.contains ("Condensed"))
width = CONDENSED;
else if (style.contains ("SemiExpanded"))
width = SEMI_EXPANDED;
else if (style.contains ("ExtraExpanded"))
width = EXTRA_EXPANDED;
else if (style.contains ("UltraExpanded"))
width = ULTRA_EXPANDED;
else if (style.contains ("Expanded"))
width = EXPANDED;
/* Guess the spacing information. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
widths = new float[testString.length ()];
measured = typefacePaint.getTextWidths (testString,
0, testString.length (),
widths);
spacing = MONO;
for (i = 0; i < measured; ++i)
{
if (i != 0 && widths[i - 1] != widths[i])
/* This isn't a monospace font. */
spacing = PROPORTIONAL;
}
}
else
familyName = fileName;
}
@Override
public String
toString ()
{
return ("Sdk7Typeface ("
+ String.valueOf (familyName) + ", "
+ String.valueOf (slant) + ", "
+ String.valueOf (width) + ", "
+ String.valueOf (weight) + ", "
+ String.valueOf (spacing) + ")");
}
};
protected class Sdk7FontEntity extends FontEntity
{
/* The typeface. */
public Sdk7Typeface typeface;
public
Sdk7FontEntity (Sdk7Typeface typeface)
{
float width;
foundry = "Google";
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
this.typeface = typeface;
}
};
protected class Sdk7FontObject extends FontObject
{
/* The typeface. */
public Sdk7Typeface typeface;
public
Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
{
float totalWidth;
String testWidth, testString;
this.typeface = typeface;
this.pixelSize = pixelSize;
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
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);
ascent
= Math.round (-typeface.typefacePaint.ascent ());
descent
= Math.round (typeface.typefacePaint.descent ());
/* Compute the average width. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
totalWidth = typeface.typefacePaint.measureText (testString);
if (totalWidth > 0)
avgwidth = Math.round (totalWidth
/ testString.length ());
/* Android doesn't expose the font average width and height
information, so this will have to do. */
minWidth = maxWidth = avgwidth;
/* This is different from avgwidth in the font spec! */
averageWidth = avgwidth;
/* Set the space width. */
totalWidth = typeface.typefacePaint.measureText (" ");
spaceWidth = Math.round (totalWidth);
/* Set the height and default ascent. */
height = ascent + descent;
defaultAscent = ascent;
}
};
private String[] fontFamilyList;
private Sdk7Typeface[] typefaceList;
private Sdk7Typeface fallbackTypeface;
public
EmacsSdk7FontDriver ()
{
int i;
File systemFontsDirectory, fontFile;
Typeface typeface;
systemFontsDirectory = new File ("/system/fonts");
fontFamilyList = systemFontsDirectory.list ();
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
Android. */
for (i = 0; i < fontFamilyList.length; ++i)
{
fontFile = new File (systemFontsDirectory,
fontFamilyList[i]);
typeface = Typeface.createFromFile (fontFile);
typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
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
checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
{
if (fontSpec.family != null
&& !fontSpec.family.equals (typeface.familyName))
return false;
if (fontSpec.slant != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.spacing != null
&& !fontSpec.spacing.equals (typeface.spacing))
return false;
if (fontSpec.weight != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.width != null
&& !fontSpec.width.equals (typeface.width))
return false;
return true;
}
@Override
public FontEntity[]
list (FontSpec fontSpec)
{
LinkedList<FontEntity> list;
int i;
list = new LinkedList<FontEntity> ();
for (i = 0; i < typefaceList.length; ++i)
{
if (checkMatch (typefaceList[i], fontSpec))
list.add (new Sdk7FontEntity (typefaceList[i]));
}
return (FontEntity[]) list.toArray (new FontEntity[0]);
}
@Override
public FontEntity
match (FontSpec fontSpec)
{
FontEntity[] entities;
int i;
entities = this.list (fontSpec);
if (entities.length == 0)
return new Sdk7FontEntity (fallbackTypeface);
return entities[0];
}
@Override
public String[]
listFamilies ()
{
return fontFamilyList;
}
@Override
public FontObject
openFont (FontEntity fontEntity, int pixelSize)
{
return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
pixelSize);
}
@Override
public int
hasChar (FontSpec font, char charCode)
{
float missingGlyphWidth, emGlyphWidth, width;
Rect rect1, rect2;
Paint paint;
Sdk7FontObject fontObject;
if (font instanceof Sdk7FontObject)
{
fontObject = (Sdk7FontObject) font;
paint = fontObject.typeface.typefacePaint;
}
else
paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
paint.setTextSize (10);
if (Character.isWhitespace (charCode))
return 1;
missingGlyphWidth = paint.measureText (TOFU_STRING);
emGlyphWidth = paint.measureText (EM_STRING);
width = paint.measureText ("" + charCode);
if (width == 0f)
return 0;
if (width != missingGlyphWidth)
return 1;
rect1 = new Rect ();
rect2 = new Rect ();
paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
rect1);
paint.getTextBounds ("" + charCode, 0, 1, rect2);
return rect1.equals (rect2) ? 0 : 1;
}
private void
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
Paint paint, Rect bounds)
{
char[] text;
text = new char[1];
text[0] = (char) code;
paint.getTextBounds (text, 0, 1, bounds);
/* bounds is the bounding box of the glyph corresponding to CODE.
Translate these into XCharStruct values.
The origin is at 0, 0, and lbearing is the distance counting
rightwards from the origin to the left most pixel in the glyph
raster. rbearing is the distance between the origin and the
rightmost pixel in the glyph raster. ascent is the distance
counting upwards between the the topmost pixel in the glyph
raster. descent is the distance (once again counting
downwards) between the origin and the bottommost pixel in the
glyph raster.
width is the distance between the origin and the origin of any
character to the right. */
metrics.lbearing = (short) bounds.left;
metrics.rbearing = (short) bounds.right;
metrics.ascent = (short) -bounds.top;
metrics.descent = (short) bounds.bottom;
metrics.width = (short) paint.measureText ("" + text[0]);
}
@Override
public void
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 ();
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, code.length,
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
public int
encodeChar (FontObject fontObject, char charCode)
{
return charCode;
}
@Override
public int
draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
int[] chars, int x, int y, int backgroundWidth,
boolean withBackground)
{
Rect backgroundRect, bounds;
Sdk7FontObject sdk7FontObject;
char[] charsArray;
int i;
Canvas canvas;
Paint paint;
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;
canvas = drawable.lockCanvas (gc);
if (canvas == null)
return 0;
paint = gc.gcPaint;
paint.setStyle (Paint.Style.FILL);
if (withBackground)
{
paint.setColor (gc.background | 0xff000000);
canvas.drawRect (backgroundRect, paint);
paint.setColor (gc.foreground | 0xff000000);
}
paint.setTextSize (sdk7FontObject.pixelSize);
paint.setTypeface (sdk7FontObject.typeface.typeface);
paint.setAntiAlias (true);
canvas.drawText (charsArray, 0, chars.length, x, y, paint);
bounds = new Rect ();
paint.getTextBounds (charsArray, 0, chars.length, bounds);
bounds.offset (x, y);
bounds.union (backgroundRect);
drawable.damageRect (bounds);
paint.setAntiAlias (false);
return 1;
}
};