From 884ede7c959b1331e1ede0b1b80f01a06c048bf5 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 10 Apr 2025 15:21:15 +0800 Subject: [PATCH] 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) : Delete fields. : 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. --- java/org/gnu/emacs/EmacsNative.java | 4 ++ java/org/gnu/emacs/EmacsSdk7FontDriver.java | 10 +++- java/org/gnu/emacs/EmacsService.java | 66 ++++++++++++++++----- lisp/dynamic-setting.el | 3 +- src/android.c | 19 ++++++ src/androidgui.h | 26 ++++++++ src/androidterm.c | 21 +++++++ 7 files changed, 132 insertions(+), 17 deletions(-) diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 94df9ff39b4..49a21ce1c4d 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -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); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 2fc40551984..b426c3ba74e 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -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); diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index babf2626ba5..3630329839f 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -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 diff --git a/lisp/dynamic-setting.el b/lisp/dynamic-setting.el index 3681df0b8f5..c38f4656e37 100644 --- a/lisp/dynamic-setting.el +++ b/lisp/dynamic-setting.el @@ -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 diff --git a/src/android.c b/src/android.c index 0be3e10dea4..5c78dbd9171 100644 --- a/src/android.c +++ b/src/android.c @@ -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) diff --git a/src/androidgui.h b/src/androidgui.h index 55c139d403d..b09d5eaabce 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -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 diff --git a/src/androidterm.c b/src/androidterm.c index 884d8f8d718..96f595f3bdf 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -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