From 7cfbcce496681c3591f4c97b8bff471ca3d93800 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 13 May 2024 14:40:15 +0800 Subject: [PATCH] Communicate frame titles to the window manager on Android * java/org/gnu/emacs/EmacsActivity.java (detachWindow) (attachWindow): Call updateWmName. (updateWmName): New function; transfer wm name from the window attached to the task's description. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) : New field. (setWmName): New function. * src/android.c (android_init_emacs_window): Link to new function. (android_set_wm_name): New function. * src/android.h (struct android_emacs_service): Delete unused entries. * src/androidfns.c (android_set_name_internal, android_set_name) (android_implicitly_set_name, android_explicitly_set_name) (android_set_title): Port from X. * src/androidterm.c (android_term_init): Compute default frame title. * src/androidterm.h (struct android_display_info) : New field. (cherry picked from commit 9443f8145e1db86664a4af318b3bd1448094040e) --- java/org/gnu/emacs/EmacsActivity.java | 33 ++++++++++++ java/org/gnu/emacs/EmacsWindow.java | 37 ++++++++++++- src/android.c | 23 ++++++++ src/android.h | 3 +- src/androidfns.c | 78 ++++++++++++++++++++++++++- src/androidterm.c | 20 +++++++ src/androidterm.h | 3 ++ 7 files changed, 192 insertions(+), 5 deletions(-) diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 118c3375ad5..7d02e4f4834 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.concurrent.TimeUnit; import android.app.Activity; +import android.app.ActivityManager.TaskDescription; import android.content.ContentResolver; import android.content.Context; @@ -166,6 +167,10 @@ public class EmacsActivity extends Activity layout.removeView (window.view); window = null; + /* Reset the WM name. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + updateWmName (); + invalidateFocus (0); } } @@ -205,6 +210,11 @@ public class EmacsActivity extends Activity invalidateFocus (1); } }); + + /* Synchronize the window's window manager name with this activity's + task in the recents list. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + updateWmName (); } @Override @@ -522,6 +532,29 @@ public class EmacsActivity extends Activity } } + /* Update the name of this activity's task description from the + current window, or reset the same if no window is attached. */ + + @SuppressWarnings ("deprecation") + public final void + updateWmName () + { + String wmName; + TaskDescription description; + + if (window == null || window.wmName == null) + wmName = "Emacs"; + else + wmName = window.wmName; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) + description = new TaskDescription (wmName); + else + description = (new TaskDescription.Builder () + .setLabel (wmName).build ()); + setTaskDescription (description); + } + @Override public final void onAttachedToWindow () diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index aa1f3e9d96d..d18212759db 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -169,6 +169,11 @@ public final class EmacsWindow extends EmacsHandleObject and whether this window has previously been attached to a task. */ public boolean preserve, previouslyAttached; + /* The window manager name of this window, which supplies the name of + activities in which it is displayed as a toplevel window, or + NULL. */ + public String wmName; + public EmacsWindow (final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -1562,6 +1567,36 @@ public final class EmacsWindow extends EmacsHandleObject return dontFocusOnMap; } + public void + setWmName (final String wmName) + { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return; + + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + EmacsActivity activity; + Object tem; + + EmacsWindow.this.wmName = wmName; + + /* If an activity is already attached, replace its task + description. */ + + tem = getAttachedConsumer (); + + if (tem != null && tem instanceof EmacsActivity) + { + activity = (EmacsActivity) tem; + activity.updateWmName (); + } + } + }); + } + public int[] translateCoordinates (int x, int y) { @@ -1631,7 +1666,7 @@ public final class EmacsWindow extends EmacsHandleObject fullscreen = isFullscreen; tem = getAttachedConsumer (); - if (tem != null) + if (tem != null && tem instanceof EmacsActivity) { activity = (EmacsActivity) tem; activity.syncFullscreenWith (EmacsWindow.this); diff --git a/src/android.c b/src/android.c index 72ef9e689ef..17b5d6d4115 100644 --- a/src/android.c +++ b/src/android.c @@ -115,6 +115,7 @@ struct android_emacs_window jmethodID recreate_activity; jmethodID clear_window; jmethodID clear_area; + jmethodID set_wm_name; }; struct android_emacs_cursor @@ -1842,6 +1843,7 @@ android_init_emacs_window (void) FIND_METHOD (recreate_activity, "recreateActivity", "()V"); FIND_METHOD (clear_window, "clearWindow", "()V"); FIND_METHOD (clear_area, "clearArea", "(IIII)V"); + FIND_METHOD (set_wm_name, "setWmName", "(Ljava/lang/String;)V"); #undef FIND_METHOD } @@ -5603,6 +5605,27 @@ android_set_dont_accept_focus (android_window handle, android_exception_check (); } +/* Set the WM name of HANDLE to STRING, a Java string. This name + provides the task description of activities that receive HANDLE. */ + +void +android_set_wm_name (android_window handle, jstring name) +{ + jmethodID method; + jobject window; + + window = android_resolve_handle (handle); + method = window_class.set_wm_name; + + if (android_get_current_api_level () < 21) + return; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, + window_class.class, method, + name); + android_exception_check (); +} + void android_get_keysym_name (int keysym, char *name_return, size_t size) { diff --git a/src/android.h b/src/android.h index a582a9b7dff..78482d64de4 100644 --- a/src/android.h +++ b/src/android.h @@ -118,6 +118,7 @@ extern bool android_detect_keyboard (void); extern void android_set_dont_focus_on_map (android_window, bool); extern void android_set_dont_accept_focus (android_window, bool); +extern void android_set_wm_name (android_window, jstring); extern int android_verify_jni_string (const char *); extern jstring android_build_string (Lisp_Object, ...); @@ -275,8 +276,6 @@ struct android_emacs_service jmethodID draw_rectangle; jmethodID draw_line; jmethodID draw_point; - jmethodID clear_window; - jmethodID clear_area; jmethodID ring_bell; jmethodID query_tree; jmethodID get_screen_width; diff --git a/src/androidfns.c b/src/androidfns.c index df425e5779e..1c2690394a6 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -211,18 +211,90 @@ android_set_parent_frame (struct frame *f, Lisp_Object new_value, FRAME_TERMINAL (f)->fullscreen_hook (f); } +/* Set the WM name to NAME for frame F. Also set the icon name. + If the frame already has an icon name, use that, otherwise set the + icon name to NAME. */ + +static void +android_set_name_internal (struct frame *f, Lisp_Object name) +{ + jstring java_name; + + if (FRAME_ANDROID_WINDOW (f)) + { + java_name = android_build_string (name, NULL); + android_set_wm_name (FRAME_ANDROID_WINDOW (f), java_name); + ANDROID_DELETE_LOCAL_REF (java_name); + } +} + +/* Change the name of frame F to NAME. If NAME is nil, set F's name to + x_id_name. + + If EXPLICIT is true, that indicates that lisp code is setting the + name; if NAME is a string, set F's name to NAME and set + F->explicit_name; if NAME is Qnil, then clear F->explicit_name. + + If EXPLICIT is false, that indicates that Emacs redisplay code is + suggesting a new name, which lisp code should override; if + F->explicit_name is set, ignore the new name; otherwise, set it. */ + +static void +android_set_name (struct frame *f, Lisp_Object name, bool explicit) +{ + /* Make sure that requests from lisp code override requests from + Emacs redisplay code. */ + if (explicit) + { + /* If we're switching from explicit to implicit, we had better + update the mode lines and thereby update the title. */ + if (f->explicit_name && NILP (name)) + update_mode_lines = 37; + + f->explicit_name = ! NILP (name); + } + else if (f->explicit_name) + return; + + /* If NAME is nil, set the name to the x_id_name. */ + if (NILP (name)) + { + /* Check for no change needed in this very common case + before we do any consing. */ + if (!strcmp (FRAME_DISPLAY_INFO (f)->x_id_name, + SSDATA (f->name))) + return; + name = build_string (FRAME_DISPLAY_INFO (f)->x_id_name); + } + else + CHECK_STRING (name); + + /* Don't change the name if it's already NAME. */ + if (! NILP (Fstring_equal (name, f->name))) + return; + + fset_name (f, name); + + /* For setting the frame title, the title parameter should override + the name parameter. */ + if (! NILP (f->title)) + name = f->title; + + android_set_name_internal (f, name); +} + void android_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - + android_set_name (f, arg, false); } void android_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - + android_set_name (f, arg, true); } /* Set the number of lines used for the tool bar of frame F to VALUE. @@ -2988,6 +3060,8 @@ android_set_title (struct frame *f, Lisp_Object name, name = f->name; else CHECK_STRING (name); + + android_set_name_internal (f, name); } static void diff --git a/src/androidterm.c b/src/androidterm.c index 67c20ec5245..94a115a66a6 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -6649,6 +6649,26 @@ android_term_init (void) terminal->name = xstrdup ("android"); + { + Lisp_Object system_name = Fsystem_name (); + static char const title[] = "GNU Emacs"; + if (STRINGP (system_name)) + { + static char const at[] = " at "; + ptrdiff_t nbytes = sizeof (title) + sizeof (at); + if (ckd_add (&nbytes, nbytes, SBYTES (system_name))) + memory_full (SIZE_MAX); + dpyinfo->x_id_name = xmalloc (nbytes); + sprintf (dpyinfo->x_id_name, "%s%s%s", title, at, + SDATA (system_name)); + } + else + { + dpyinfo->x_id_name = xmalloc (sizeof (title)); + strcpy (dpyinfo->x_id_name, title); + } + } + /* The display "connection" is now set up, and it must never go away. */ terminal->reference_count = 30000; diff --git a/src/androidterm.h b/src/androidterm.h index f4459c45dc9..c8f1ab655a9 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -90,6 +90,9 @@ struct android_display_info /* Minimum font height over all fonts in font_table. */ int smallest_font_height; + /* Default name for all frames on this display. */ + char *x_id_name; + /* The number of fonts opened for this display. */ int n_fonts; -- 2.39.5