Respond to display configuration updates on Android

* java/org/gnu/emacs/EmacsNative.java
(sendConfigurationChanged): Declare function.

* java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7FontEntity)
(Sdk7FontObject): Do not access `metrics' field deleted from
`EmacsService'.

* java/org/gnu/emacs/EmacsService.java (EmacsService)
<metrics, resources>: Delete fields.
<dpiX, dpiY, dpiScaled>: New fields.
(onCreate): Adjust accordingly.  Record current display metrics
for subsequent comparison.
(onConfigurationChanged): New function.

* lisp/dynamic-setting.el (font-setting-change-default-font):
Enable on systems where font-get-system-font is not defined if
invoked with SET-FONT nil.

* src/android.c (sendConfigurationChanged): New function.

* src/androidgui.h (ANDROID_CONFIGURATION_CHANGED): New enumerator.
(struct android_configuration_changed): New structure.
(union android_event): Add `config' member.

* src/androidterm.c (handle_one_android_event): Handle
ANDROID_CONFIGURATION_CHANGED events.
(syms_of_androidterm): Define Qfont_render, and
Qdynamic_setting.  Provide the latter.
This commit is contained in:
Po Lu 2025-04-10 15:21:15 +08:00
parent cb339ad8f4
commit 884ede7c95
7 changed files with 132 additions and 17 deletions

View file

@ -196,6 +196,10 @@ public static native long sendDndText (long window, int x, int y,
/* Send an ANDROID_NOTIFICATION_ACTION event. */
public static native void sendNotificationAction (String tag, String action);
/* Send an ANDROID_CONFIGURATION_CHANGED event. */
public static native void sendConfigurationChanged (float dpiX, float dpiY,
float dpiScaled);
/* Return the file name associated with the specified file
descriptor, or NULL if there is none. */
public static native byte[] getProcName (int fd);

View file

@ -29,6 +29,7 @@
import android.graphics.Typeface;
import android.graphics.Canvas;
import android.util.DisplayMetrics;
import android.util.Log;
@ -103,6 +104,8 @@ protected static final class Sdk7FontEntity extends FontEntity
public
Sdk7FontEntity (Sdk7Typeface typeface)
{
DisplayMetrics metrics;
foundry = "Google";
family = typeface.familyName;
adstyle = null;
@ -110,7 +113,8 @@ protected static final class Sdk7FontEntity extends FontEntity
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
metrics = EmacsService.SERVICE.getResources ().getDisplayMetrics ();
dpi = Math.round (metrics.scaledDensity * 160f);
this.typeface = typeface;
}
@ -127,6 +131,7 @@ protected final class Sdk7FontObject extends FontObject
{
float totalWidth;
String testWidth, testString;
DisplayMetrics metrics;
this.typeface = typeface;
this.pixelSize = pixelSize;
@ -137,7 +142,8 @@ protected final class Sdk7FontObject extends FontObject
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
metrics = EmacsService.SERVICE.getResources ().getDisplayMetrics ();
dpi = Math.round (metrics.scaledDensity * 160f);
/* Compute the ascent and descent. */
typeface.typefacePaint.setTextSize (pixelSize);

View file

@ -123,9 +123,6 @@ public final class EmacsService extends Service
public static final int IC_MODE_TEXT = 2;
public static final int IC_MODE_PASSWORD = 3;
/* Display metrics used by font backends. */
public DisplayMetrics metrics;
/* Flag that says whether or not to print verbose debugging
information when responding to an input method. */
public static final boolean DEBUG_IC = false;
@ -149,8 +146,9 @@ public final class EmacsService extends Service
thread. */
private Thread mainThread;
/* "Resources" object required by GContext bookkeeping. */
public static Resources resources;
/* The display's horizontal and vertical density and that which is
consulted for font scaling. */
private double dpiX, dpiY, dpiScaled;
static
{
@ -236,10 +234,12 @@ public final class EmacsService extends Service
final AssetManager manager;
Context app_context;
final String filesDir, libDir, cacheDir, classPath;
final double pixelDensityX;
final double pixelDensityY;
final double scaledDensity;
double tempScaledDensity;
final float pixelDensityX;
final float pixelDensityY;
final float scaledDensity;
float tempScaledDensity;
Resources resources;
DisplayMetrics metrics;
super.onCreate ();
@ -265,13 +265,18 @@ public final class EmacsService extends Service
corresponds to 1 pixel, not 72 or 96 as used elsewhere. This
difference is codified in PT_PER_INCH defined in font.h. */
if (tempScaledDensity < 160)
tempScaledDensity = 160;
if (tempScaledDensity < 160.0f)
tempScaledDensity = 160.0f;
/* scaledDensity is const as required to refer to it from within
the nested function below. */
scaledDensity = tempScaledDensity;
/* Save these fields for future reference. */
dpiX = pixelDensityX;
dpiY = pixelDensityY;
dpiScaled = scaledDensity;
/* Remove all tasks from previous Emacs sessions but the task
created by the system at startup. */
EmacsWindowManager.MANAGER.removeOldTasks (this);
@ -304,9 +309,8 @@ invocation of app_process (through android-emacs) can
run ()
{
EmacsNative.setEmacsParams (manager, filesDir, libDir,
cacheDir, (float) pixelDensityX,
(float) pixelDensityY,
(float) scaledDensity,
cacheDir, pixelDensityX,
pixelDensityY, scaledDensity,
classPath, EmacsService.this,
Build.VERSION.SDK_INT);
}
@ -344,6 +348,40 @@ invocation of app_process (through android-emacs) can
super.onLowMemory ();
}
@Override
public void
onConfigurationChanged (Configuration newConfig)
{
DisplayMetrics metrics;
float pixelDensityX, pixelDensityY, scaledDensity;
metrics = getResources ().getDisplayMetrics ();
/* The display configuration may have been altered. Retrieve the
revised display density and deliver an event if so. */
pixelDensityX = metrics.xdpi;
pixelDensityY = metrics.ydpi;
scaledDensity = ((getScaledDensity (metrics)
/ metrics.density) * pixelDensityX);
/* A density below 160 probably indicates a system bug. See
onCreate for more commentary. */
if (scaledDensity < 160.0f)
scaledDensity = 160.0f;
if (pixelDensityX != dpiX || pixelDensityY != dpiY
|| scaledDensity != dpiScaled)
{
dpiX = pixelDensityX;
dpiY = pixelDensityY;
dpiScaled = scaledDensity;
EmacsNative.sendConfigurationChanged (pixelDensityX, pixelDensityY,
scaledDensity);
}
super.onConfigurationChanged (newConfig);
}
/* Functions from here on must only be called from the Emacs

View file

@ -46,7 +46,8 @@ the current form for the frame (i.e. hinting or somesuch changed)."
(let ((new-font (and (fboundp 'font-get-system-font)
(font-get-system-font)))
(frame-list (frames-on-display-list display-or-frame)))
(when (and new-font (display-graphic-p display-or-frame))
(when (and (or (not set-font) new-font)
(display-graphic-p display-or-frame))
(clear-font-cache)
(if set-font
;; Set the font on all current and future frames, as though

View file

@ -2819,6 +2819,25 @@ NATIVE_NAME (sendNotificationAction) (JNIEnv *env, jobject object,
return event_serial;
}
JNIEXPORT jlong JNICALL
NATIVE_NAME (sendConfigurationChanged) (JNIEnv *env, jobject object,
jfloat dpi_x, jfloat dpi_y,
jfloat dpi_scaled)
{
JNI_STACK_ALIGNMENT_PROLOGUE;
union android_event event;
event.config.type = ANDROID_CONFIGURATION_CHANGED;
event.config.serial = ++event_serial;
event.config.window = ANDROID_NONE;
event.config.dpi_x = dpi_x;
event.config.dpi_y = dpi_y;
event.config.dpi_scaled = dpi_scaled;
android_write_event (&event);
return event_serial;
}
JNIEXPORT jboolean JNICALL
NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env,
jobject object)

View file

@ -288,6 +288,7 @@ enum android_event_type
ANDROID_DND_TEXT_EVENT,
ANDROID_NOTIFICATION_DELETED,
ANDROID_NOTIFICATION_ACTION,
ANDROID_CONFIGURATION_CHANGED,
};
struct android_any_event
@ -595,6 +596,26 @@ struct android_notification_event
size_t length;
};
struct android_configuration_changed_event
{
/* Type of the event. */
enum android_event_type type;
/* The event serial. */
unsigned long serial;
/* The window that gave rise to the event (None). */
android_window window;
/* The density of the display along the horizontal and vertical
axes. */
double dpi_x, dpi_y;
/* The density to take into account when converting between point and
pixel dimensions. */
double dpi_scaled;
};
union android_event
{
enum android_event_type type;
@ -635,6 +656,11 @@ union android_event
/* X provides no equivalent interface for displaying
notifications. */
struct android_notification_event notification;
/* The equivalent under X is provided through XSettings, which is a
byzantine protocol that extends client messages and is therefore
not worthwhile to emulate. */
struct android_configuration_changed_event config;
};
enum

View file

@ -1824,6 +1824,22 @@ handle_one_android_event (struct android_display_info *dpyinfo,
free (event->notification.action);
goto OTHER;
case ANDROID_CONFIGURATION_CHANGED:
/* Update the display configuration from the event. */
dpyinfo->resx = event->config.dpi_x;
dpyinfo->resy = event->config.dpi_y;
dpyinfo->font_resolution = event->config.dpi_scaled;
#ifdef notdef
__android_log_print (ANDROID_LOG_VERBOSE, __func__,
"New display configuration: "
"resx = %.2f resy = %.2f font_resolution = %.2f",
dpyinfo->resx, dpyinfo->resy, dpyinfo->font_resolution);
#endif /* notdef */
inev.ie.kind = CONFIG_CHANGED_EVENT;
inev.ie.frame_or_window = XCAR (dpyinfo->name_list_element);
inev.ie.arg = Qfont_render;
goto OTHER;
default:
goto OTHER;
}
@ -7014,6 +7030,11 @@ for instance, `early-init.el', or they will be of no effect. */);
/* Key symbols. */
DEFSYM (Qselect, "select");
DEFSYM (Qreturn, "return");
/* Display configuration updates. */
DEFSYM (Qfont_render, "font-render");
DEFSYM (Qdynamic_setting, "dynamic-setting");
Fprovide (Qdynamic_setting, Qnil);
}
void