diff --git a/etc/NEWS b/etc/NEWS index 73335113180..7d132356484 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3573,10 +3573,10 @@ automatically toggling between them. --- *** 'toolkit-theme-set-functions' called when the toolkit theme is set for Emacs. -When the theme is set on PGTK or MS-Windows builds, +When the theme is set on PGTK, Android, or MS-Windows systems, 'toolkit-theme-set-functions' is called. The result is stored in the variable 'toolkit-theme' as either symbol 'dark' or 'light', but may be -expanded to future toolkit-specific symbols in the future. +extended to encompass other toolkit-specific symbols in the future. * Changes in Emacs 31.1 on Non-Free Operating Systems diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 49a21ce1c4d..fac4b6f01df 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -61,6 +61,9 @@ public final class EmacsNative scaledDensity is the DPI value used to translate point sizes to pixel sizes when loading fonts. + uiMode holds the bits of the system's UI mode specification + defining the active system theme. + classPath must be the classpath of this app_process process, or NULL. @@ -74,6 +77,7 @@ public final class EmacsNative float pixelDensityX, float pixelDensityY, float scaledDensity, + int uiMode, String classPath, EmacsService emacsService, int apiLevel); @@ -197,8 +201,9 @@ public final class EmacsNative 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); + public static native void sendConfigurationChanged (int detail, float dpiX, + float dpiY, float dpiScaled, + int ui_mode); /* Return the file name associated with the specified file descriptor, or NULL if there is none. */ diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index 83ef04b1cf1..b4d055ce4c9 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -67,7 +67,7 @@ public final class EmacsNoninteractive cacheDir = context.getCacheDir ().getCanonicalPath (); EmacsNative.setEmacsParams (assets, filesDir, libDir, cacheDir, 0.0f, - 0.0f, 0.0f, null, null, + 0.0f, 0.0f, 0x0, null, null, Build.VERSION.SDK_INT); /* Now find the dump file that Emacs should use, if it has already diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 3630329839f..d141f6f4d0d 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -150,6 +150,10 @@ public final class EmacsService extends Service consulted for font scaling. */ private double dpiX, dpiY, dpiScaled; + /* The display's previously observed UI mode as it relates to the + system theme. */ + private int uiMode; + static { servicingQuery = new AtomicInteger (); @@ -240,6 +244,7 @@ public final class EmacsService extends Service float tempScaledDensity; Resources resources; DisplayMetrics metrics; + Configuration configuration; super.onCreate (); @@ -254,6 +259,8 @@ public final class EmacsService extends Service tempScaledDensity = ((getScaledDensity (metrics) / metrics.density) * pixelDensityX); + configuration = resources.getConfiguration (); + uiMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK; resolver = getContentResolver (); mainThread = Thread.currentThread (); @@ -311,7 +318,8 @@ public final class EmacsService extends Service EmacsNative.setEmacsParams (manager, filesDir, libDir, cacheDir, pixelDensityX, pixelDensityY, scaledDensity, - classPath, EmacsService.this, + uiMode, classPath, + EmacsService.this, Build.VERSION.SDK_INT); } }, extraStartupArguments); @@ -375,8 +383,14 @@ public final class EmacsService extends Service dpiX = pixelDensityX; dpiY = pixelDensityY; dpiScaled = scaledDensity; - EmacsNative.sendConfigurationChanged (pixelDensityX, pixelDensityY, - scaledDensity); + EmacsNative.sendConfigurationChanged (0, pixelDensityX, pixelDensityY, + scaledDensity, 0); + } + + if ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != uiMode) + { + uiMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK; + EmacsNative.sendConfigurationChanged (1, 0.0f, 0.0f, 0.0f, uiMode); } super.onConfigurationChanged (newConfig); diff --git a/src/android.c b/src/android.c index 6cc416063f6..5f2f2e35239 100644 --- a/src/android.c +++ b/src/android.c @@ -163,6 +163,9 @@ double android_pixel_density_x, android_pixel_density_y; font sizes. */ double android_scaled_pixel_density; +/* The display's current UI mode. */ +int android_ui_mode; + /* The Android application data directory. */ static char *android_files_dir; @@ -1476,6 +1479,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, jfloat pixel_density_x, jfloat pixel_density_y, jfloat scaled_density, + jint ui_mode, jobject class_path, jobject emacs_service_object, jint api_level) @@ -1501,6 +1505,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, android_pixel_density_x = pixel_density_x; android_pixel_density_y = pixel_density_y; android_scaled_pixel_density = scaled_density; + android_ui_mode = ui_mode; __android_log_print (ANDROID_LOG_INFO, __func__, "Initializing "PACKAGE_STRING"...\nPlease report bugs to " @@ -2821,8 +2826,9 @@ NATIVE_NAME (sendNotificationAction) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendConfigurationChanged) (JNIEnv *env, jobject object, - jfloat dpi_x, jfloat dpi_y, - jfloat dpi_scaled) + int detail, jfloat dpi_x, + jfloat dpi_y, jfloat dpi_scaled, + int ui_mode) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2831,9 +2837,24 @@ NATIVE_NAME (sendConfigurationChanged) (JNIEnv *env, jobject object, 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; + event.config.detail = detail; + + switch (detail) + { + case ANDROID_PIXEL_DENSITY_CHANGED: + event.config.u.pixel_density.dpi_x = dpi_x; + event.config.u.pixel_density.dpi_y = dpi_y; + event.config.u.pixel_density.dpi_scaled = dpi_scaled; + break; + + case ANDROID_UI_MODE_CHANGED: + event.config.u.ui_mode = ui_mode; + break; + + default: + emacs_abort (); + } + android_write_event (&event); return event_serial; } diff --git a/src/android.h b/src/android.h index 1a12c95c9a5..fa94386a05e 100644 --- a/src/android.h +++ b/src/android.h @@ -102,6 +102,7 @@ extern ssize_t android_readlinkat (int, const char *restrict, char *restrict, extern double android_pixel_density_x, android_pixel_density_y; extern double android_scaled_pixel_density; +extern int android_ui_mode; static_assert (sizeof (android_handle) == sizeof (jobject)); #define android_resolve_handle(handle) ((jobject) (handle)) diff --git a/src/androidgui.h b/src/androidgui.h index b09d5eaabce..9b46f7120d7 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -596,6 +596,17 @@ struct android_notification_event size_t length; }; +enum android_configuration_change_type + { + ANDROID_PIXEL_DENSITY_CHANGED, + ANDROID_UI_MODE_CHANGED, + }; + +#define UI_MODE_NIGHT_MASK 0x00000030 +#define UI_MODE_NIGHT_NO 0x00000010 +#define UI_MODE_NIGHT_YES 0x00000020 +#define UI_MODE_NIGHT_UNDEFINED 0x00000000 + struct android_configuration_changed_event { /* Type of the event. */ @@ -607,13 +618,23 @@ struct android_configuration_changed_event /* 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; + /* What type of change this event represents. */ + enum android_configuration_change_type detail; - /* The density to take into account when converting between point and - pixel dimensions. */ - double dpi_scaled; + union { + struct { + /* 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; + } pixel_density; + + /* A change in the reported user interface UI mode. */ + int ui_mode; + } u; }; union android_event diff --git a/src/androidterm.c b/src/androidterm.c index beab3406fdd..d9830e5cd08 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1825,19 +1825,32 @@ handle_one_android_event (struct android_display_info *dpyinfo, 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); + + if (event->config.detail == ANDROID_PIXEL_DENSITY_CHANGED) + { + /* Update the display configuration from the event. */ + dpyinfo->resx = event->config.u.pixel_density.dpi_x; + dpyinfo->resy = event->config.u.pixel_density.dpi_y; + dpyinfo->font_resolution + = event->config.u.pixel_density.dpi_scaled; +#if 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; + inev.ie.kind = CONFIG_CHANGED_EVENT; + inev.ie.frame_or_window = XCAR (dpyinfo->name_list_element); + inev.ie.arg = Qfont_render; + } + else if (event->config.detail == ANDROID_UI_MODE_CHANGED) + { + android_ui_mode = event->config.u.ui_mode; + inev.ie.kind = TOOLKIT_THEME_CHANGED_EVENT; + inev.ie.arg = (android_ui_mode == UI_MODE_NIGHT_YES + ? Qdark : Qlight); + } + goto OTHER; default: @@ -6753,6 +6766,8 @@ android_term_init (void) dpyinfo->resx = android_pixel_density_x; dpyinfo->resy = android_pixel_density_y; dpyinfo->font_resolution = android_scaled_pixel_density; + Vtoolkit_theme = (android_ui_mode == UI_MODE_NIGHT_YES + ? Qdark : Qlight); #endif /* !ANDROID_STUBIFY */ /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */ diff --git a/src/frame.c b/src/frame.c index 65900665dd5..5aad8f890dc 100644 --- a/src/frame.c +++ b/src/frame.c @@ -7619,16 +7619,15 @@ The default is \\+`inhibit' in NS builds and nil everywhere else. */); #endif /* !HAVE_EXT_TOOL_BAR || USE_GTK */ #endif /* HAVE_WINDOW_SYSTEM */ -#if (defined(HAVE_PGTK) && defined(HAVE_GSETTINGS)) || defined (WINDOWSNT) DEFVAR_LISP ("toolkit-theme", Vtoolkit_theme, doc: /* The current toolkit theme. Either the symbol `light' or the symbol `dark', reflecting the system's current theme preference. This variable is updated automatically when the system theme changes. -This variable is only available on PGTK and MS-Windows builds. */); +This variable is only set on PGTK, Android, and MS-Windows builds. */); Vtoolkit_theme = Qnil; DEFSYM (Qlight, "light"); DEFSYM (Qdark, "dark"); -#endif + DEFSYM (Qtoolkit_theme_set_functions, "toolkit-theme-set-functions"); } diff --git a/src/gtkutil.c b/src/gtkutil.c index 5e9e8254506..b30f120b863 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1476,15 +1476,13 @@ xg_update_dark_mode_for_all_displays (bool dark_mode_p) xg_set_gtk_theme_dark_mode (dark_mode_p, settings); } - Vtoolkit_theme = dark_mode_p ? Qdark : Qlight; - Lisp_Object hook = intern ("toolkit-theme-set-functions"); - if (!NILP (Fboundp (hook))) - { - Lisp_Object args[2]; - args[0] = hook; - args[1] = Vtoolkit_theme; - Frun_hook_with_args (2, args); - } + { + struct input_event inev; + EVENT_INIT (inev); + inev.kind = TOOLKIT_THEME_CHANGED_EVENT; + inev.arg = msg.msg.wParam ? Qdark : Qlight; + kbd_buffer_store_event (&inev); + } } /* Set initial dark mode for a new frame (called during frame diff --git a/src/keyboard.c b/src/keyboard.c index 6c01811c2d8..0b34edb8042 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4242,6 +4242,15 @@ kbd_buffer_get_event (KBOARD **kbp, } #endif /* HAVE_ANDROID */ + case TOOLKIT_THEME_CHANGED_EVENT: + kbd_fetch_ptr = next_kbd_event (event); + input_pending = readable_events (0); + + Vtoolkit_theme = event->ie.arg; + CALLN (Frun_hook_with_args, Qtoolkit_theme_set_functions, + event->ie.arg); + break; + #ifdef HAVE_EXT_MENU_BAR case MENU_BAR_ACTIVATE_EVENT: { diff --git a/src/termhooks.h b/src/termhooks.h index a77ca25e159..e688ed40c0f 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -341,6 +341,10 @@ enum event_kind which the monitors changed. */ , MONITORS_CHANGED_EVENT + /* In a TOOLKIT_THEME_CHANGED_EVENT, .arg is the value to which the + toolkit theme was altered. */ + , TOOLKIT_THEME_CHANGED_EVENT + #ifdef HAVE_HAIKU /* In a NOTIFICATION_CLICKED_EVENT, .arg is an integer identifying the notification that was clicked. */ diff --git a/src/w32term.c b/src/w32term.c index 56e1fca9333..9387cc3e7b7 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -6085,15 +6085,8 @@ w32_read_socket (struct terminal *terminal, case WM_EMACS_SET_TOOLKIT_THEME: { - Vtoolkit_theme = msg.msg.wParam ? Qdark : Qlight; - Lisp_Object hook = intern ("toolkit-theme-set-functions"); - if (!NILP (Fboundp (hook))) - { - Lisp_Object args[2]; - args[0] = hook; - args[1] = Vtoolkit_theme; - Frun_hook_with_args (2, args); - } + inev.kind = TOOLKIT_THEME_CHANGED_EVENT; + inev.arg = msg.msg.wParam ? Qdark : Qlight; } break;