]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement frame restacking under Android
authorPo Lu <luangruo@yahoo.com>
Tue, 10 Oct 2023 05:11:14 +0000 (13:11 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 10 Oct 2023 05:11:14 +0000 (13:11 +0800)
* java/org/gnu/emacs/EmacsActivity.java (invalidateFocus1):
Synchronize with window.children for iteration through it.

* java/org/gnu/emacs/EmacsService.java (queryTree): Synchronize
with windowList for iteration through it.

* java/org/gnu/emacs/EmacsView.java (moveChildToBack): Correct
formatting mistake.
(moveAbove, moveBelow): New functions.

* java/org/gnu/emacs/EmacsWindow.java (destroyHandle, reparentTo)
(raise, lower): Remedy synchronization blunders.
(reconfigure): New function.

* src/android.c (android_init_emacs_window): Link with
`reconfigure'.
(android_reconfigure_wm_window): New wrapper function.

* src/androidfns.c (android_frame_restack): New function.
(Fandroid_frame_restack): Properly implement this function and
expunge outdated comment.

* src/androidgui.h (enum android_stack_mode)
(enum android_window_changes): New enumerators.

java/org/gnu/emacs/EmacsActivity.java
java/org/gnu/emacs/EmacsService.java
java/org/gnu/emacs/EmacsView.java
java/org/gnu/emacs/EmacsWindow.java
src/android.c
src/androidfns.c
src/androidgui.h

index cecd9c21d99116cb98f731055d77dc8f6e225e6a..f9aa261e355a84f8c7396958af2a0d84f8e490d3 100644 (file)
@@ -89,8 +89,11 @@ public class EmacsActivity extends Activity
     if (window.view.isFocused ())
       focusedWindow = window;
 
-    for (EmacsWindow child : window.children)
-      invalidateFocus1 (child);
+    synchronized (window.children)
+      {
+       for (EmacsWindow child : window.children)
+         invalidateFocus1 (child);
+      }
   }
 
   public static void
index 28b725d0cd06360fe4a3b110c9fe25d318800ec1..6fa2ebb3fdbce097adfb14388d412aaf2b53a2ad 100644 (file)
@@ -505,15 +505,18 @@ public final class EmacsService extends Service
     else
       windowList = window.children;
 
-    array = new short[windowList.size () + 1];
-    i = 1;
+    synchronized (windowList)
+      {
+       array = new short[windowList.size () + 1];
+       i = 1;
 
-    array[0] = (window == null
-               ? 0 : (window.parent != null
-                      ? window.parent.handle : 0));
+       array[0] = (window == null
+                   ? 0 : (window.parent != null
+                          ? window.parent.handle : 0));
 
-    for (EmacsWindow treeWindow : windowList)
-      array[i++] = treeWindow.handle;
+       for (EmacsWindow treeWindow : windowList)
+         array[i++] = treeWindow.handle;
+      }
 
     return array;
   }
index d09dcc7e50d74c66d83457109df169a62fc763dc..877b1ce242962bb4b68c0af86f0819c22a5c8836 100644 (file)
@@ -581,12 +581,12 @@ public final class EmacsView extends ViewGroup
 
        /* The view at 0 is the surface view.  */
        attachViewToParent (child, 1,
-                           child.getLayoutParams());
+                           child.getLayoutParams ());
       }
   }
 
-  /* The following two functions must not be called if the view has no
-     parent, or is parented to an activity.  */
+  /* The following four functions must not be called if the view has
+     no parent, or is parented to an activity.  */
 
   public void
   raise ()
@@ -615,6 +615,40 @@ public final class EmacsView extends ViewGroup
     parent.moveChildToBack (this);
   }
 
+  public void
+  moveAbove (EmacsView view)
+  {
+    EmacsView parent;
+    int index;
+
+    parent = (EmacsView) getParent ();
+
+    if (parent != view.getParent ())
+      throw new IllegalStateException ("Moving view above non-sibling");
+
+    index = parent.indexOfChild (this);
+    parent.detachViewFromParent (index);
+    index = parent.indexOfChild (view);
+    parent.attachViewToParent (this, index + 1, getLayoutParams ());
+  }
+
+  public void
+  moveBelow (EmacsView view)
+  {
+    EmacsView parent;
+    int index;
+
+    parent = (EmacsView) getParent ();
+
+    if (parent != view.getParent ())
+      throw new IllegalStateException ("Moving view above non-sibling");
+
+    index = parent.indexOfChild (this);
+    parent.detachViewFromParent (index);
+    index = parent.indexOfChild (view);
+    parent.attachViewToParent (this, index, getLayoutParams ());
+  }
+
   @Override
   protected void
   onCreateContextMenu (ContextMenu menu)
index 1f28d5f4f5353892fc45743e0139e7bbb9e78ebb..8d444aa27f50d7ca36c17d81d4c412013b64a7af 100644 (file)
@@ -22,6 +22,7 @@ package org.gnu.emacs;
 import java.lang.IllegalStateException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -93,7 +94,9 @@ public final class EmacsWindow extends EmacsHandleObject
   public EmacsWindow parent;
 
   /* List of all children in stacking order.  This must be kept
-     consistent with their Z order!  */
+     consistent with their Z order!
+
+     Synchronize access to this list with itself.  */
   public ArrayList<EmacsWindow> children;
 
   /* Map between pointer identifiers and last known position.  Used to
@@ -165,7 +168,11 @@ public final class EmacsWindow extends EmacsHandleObject
 
     if (parent != null)
       {
-       parent.children.add (this);
+       synchronized (parent.children)
+         {
+           parent.children.add (this);
+         }
+
         EmacsService.SERVICE.runOnUiThread (new Runnable () {
            @Override
            public void
@@ -214,7 +221,12 @@ public final class EmacsWindow extends EmacsHandleObject
   destroyHandle () throws IllegalStateException
   {
     if (parent != null)
-      parent.children.remove (this);
+      {
+       synchronized (parent.children)
+         {
+           parent.children.remove (this);
+         }
+      }
 
     EmacsActivity.invalidateFocus ();
 
@@ -1163,10 +1175,20 @@ public final class EmacsWindow extends EmacsHandleObject
     /* Reparent this window to the other window.  */
 
     if (parent != null)
-      parent.children.remove (this);
+      {
+       synchronized (parent.children)
+         {
+           parent.children.remove (this);
+         }
+      }
 
     if (otherWindow != null)
-      otherWindow.children.add (this);
+      {
+       synchronized (otherWindow.children)
+         {
+           otherWindow.children.add (this);
+         }
+      }
 
     parent = otherWindow;
 
@@ -1239,9 +1261,12 @@ public final class EmacsWindow extends EmacsHandleObject
     if (parent == null)
       return;
 
-    /* Remove and add this view again.  */
-    parent.children.remove (this);
-    parent.children.add (this);
+    synchronized (parent.children)
+      {
+       /* Remove and add this view again.  */
+       parent.children.remove (this);
+       parent.children.add (this);
+      }
 
     /* Request a relayout.  */
     EmacsService.SERVICE.runOnUiThread (new Runnable () {
@@ -1261,9 +1286,12 @@ public final class EmacsWindow extends EmacsHandleObject
     if (parent == null)
       return;
 
-    /* Remove and add this view again.  */
-    parent.children.remove (this);
-    parent.children.add (this);
+    synchronized (parent.children)
+      {
+       /* Remove and add this view again.  */
+       parent.children.remove (this);
+       parent.children.add (this);
+      }
 
     /* Request a relayout.  */
     EmacsService.SERVICE.runOnUiThread (new Runnable () {
@@ -1276,6 +1304,86 @@ public final class EmacsWindow extends EmacsHandleObject
       });
   }
 
+  public synchronized void
+  reconfigure (final EmacsWindow window, final int stackMode)
+  {
+    ListIterator<EmacsWindow> iterator;
+    EmacsWindow object;
+
+    /* This does nothing here.  */
+    if (parent == null)
+      return;
+
+    /* If window is NULL, call lower or upper subject to
+       stackMode.  */
+
+    if (window == null)
+      {
+       if (stackMode == 1) /* ANDROID_BELOW */
+         lower ();
+       else
+         raise ();
+
+       return;
+      }
+
+    /* Otherwise, if window.parent is distinct from this, return.  */
+    if (window.parent != this.parent)
+      return;
+
+    /* Synchronize with the parent's child list.  Iterate over each
+       item until WINDOW is encountered, before moving this window to
+       the location prescribed by STACKMODE.  */
+
+    synchronized (parent.children)
+      {
+       /* Remove this window from parent.children, for it will be
+          reinserted before or after WINDOW.  */
+       parent.children.remove (this);
+
+       /* Create an iterator.  */
+       iterator = parent.children.listIterator ();
+
+       while (iterator.hasNext ())
+         {
+           object = iterator.next ();
+
+           if (object == window)
+             {
+               /* Now place this before or after the cursor of the
+                  iterator.  */
+
+               if (stackMode == 0) /* ANDROID_ABOVE */
+                 iterator.add (this);
+               else
+                 {
+                   iterator.previous ();
+                   iterator.add (this);
+                 }
+
+               /* Effect the same adjustment upon the view
+                  hiearchy.  */
+
+               EmacsService.SERVICE.runOnUiThread (new Runnable () {
+                   @Override
+                   public void
+                   run ()
+                   {
+                     if (stackMode == 0)
+                       view.moveAbove (window.view);
+                     else
+                       view.moveBelow (window.view);
+                   }
+                 });
+             }
+         }
+
+       /* parent.children does not list WINDOW, which should never
+          transpire.  */
+       EmacsNative.emacsAbort ();
+      }
+  }
+
   public synchronized int[]
   getWindowGeometry ()
   {
index b9236075a1e0d272543c64601bd68b4db574f601..d11826986693dec212a849fba6c0a27b08aab5ff 100644 (file)
@@ -104,6 +104,7 @@ struct android_emacs_window
   jmethodID make_input_focus;
   jmethodID raise;
   jmethodID lower;
+  jmethodID reconfigure;
   jmethodID get_window_geometry;
   jmethodID translate_coordinates;
   jmethodID set_dont_accept_focus;
@@ -1755,6 +1756,7 @@ android_init_emacs_window (void)
   FIND_METHOD (make_input_focus, "makeInputFocus", "(J)V");
   FIND_METHOD (raise, "raise", "()V");
   FIND_METHOD (lower, "lower", "()V");
+  FIND_METHOD (reconfigure, "reconfigure", "(Lorg/gnu/emacs/EmacsWindow;I)V");
   FIND_METHOD (get_window_geometry, "getWindowGeometry",
               "()[I");
   FIND_METHOD (translate_coordinates, "translateCoordinates",
@@ -4963,6 +4965,37 @@ android_lower_window (android_window handle)
   android_exception_check ();
 }
 
+void
+android_reconfigure_wm_window (android_window handle,
+                              enum android_wc_value_mask value_mask,
+                              struct android_window_changes *values)
+{
+  jobject sibling, window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  if (!(value_mask & ANDROID_CW_STACK_MODE))
+    return;
+
+  /* If value_mask & ANDROID_CW_SIBLING, place HANDLE above or below
+     values->sibling pursuant to values->stack_mode; else, reposition
+     it at the top or the bottom of its parent.  */
+
+  sibling = NULL;
+
+  if (value_mask & ANDROID_CW_SIBLING)
+    sibling = android_resolve_handle (values->sibling,
+                                     ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                window_class.reconfigure,
+                                                sibling,
+                                                (jint) values->stack_mode);
+  android_exception_check ();
+}
+
 int
 android_query_tree (android_window handle, android_window *root_return,
                    android_window *parent_return,
index 3ee9f7634aa71d0c97e6a90122bd51d53c6677c4..772a4f51e78ebbd3a8a7a54d8c5f1710d6f0388f 100644 (file)
@@ -1591,7 +1591,8 @@ and width values are in pixels.
 #endif
 }
 
-DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 2, 0,
+DEFUN ("android-frame-edges", Fandroid_frame_edges,
+       Sandroid_frame_edges, 0, 2, 0,
        doc: /* Return edge coordinates of FRAME.
 FRAME must be a live frame and defaults to the selected one.  The return
 value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
@@ -1693,6 +1694,28 @@ TERMINAL is a frame.  */)
 #endif
 }
 
+#ifndef ANDROID_STUBIFY
+
+static void
+android_frame_restack (struct frame *f1, struct frame *f2,
+                      bool above_flag)
+{
+  android_window window1;
+  struct android_window_changes wc;
+  unsigned long mask;
+
+  window1 = FRAME_ANDROID_WINDOW (f1);
+  wc.sibling = FRAME_ANDROID_WINDOW (f2);
+  wc.stack_mode = above_flag ? ANDROID_ABOVE : ANDROID_BELOW;
+  mask = ANDROID_CW_SIBLING | ANDROID_CW_STACK_MODE;
+
+  block_input ();
+  android_reconfigure_wm_window (window1, mask, &wc);
+  unblock_input ();
+}
+
+#endif /* !ANDROID_STUBIFY */
+
 DEFUN ("android-frame-restack", Fandroid_frame_restack,
        Sandroid_frame_restack, 2, 3, 0,
        doc: /* Restack FRAME1 below FRAME2.
@@ -1709,19 +1732,25 @@ that of FRAME2.  Hence the position of FRAME2 in its display's Z
 \(stacking) order relative to all other frames excluding FRAME1 remains
 unaltered.
 
-The Android system refuses to restack windows, so this does not
-work.  */)
-  (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
+Android does not facilitate restacking top-level windows managed by
+its own window manager; nor is it possible to restack frames that are
+children of different parents.  Consequently, this function only
+functions when FRAME1 and FRAME2 are both child frames subordinate to
+the same parent frame.  */)
+  (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above)
 {
 #ifdef ANDROID_STUBIFY
   error ("Android cross-compilation stub called!");
   return Qnil;
-#else
-  /* This is not supported on Android because of limitations in the
-     platform that prevent ViewGroups from restacking
-     SurfaceViews.  */
-  return Qnil;
-#endif
+#else /* !ANDROID_STUBIFY */
+  struct frame *f1 = decode_live_frame (frame1);
+  struct frame *f2 = decode_live_frame (frame2);
+
+  if (!(FRAME_ANDROID_WINDOW (f1) && FRAME_ANDROID_WINDOW (f2)))
+    error ("Cannot restack frames");
+  android_frame_restack (f1, f2, !NILP (above));
+  return Qt;
+#endif /* ANDROID_STUBIFY */
 }
 
 DEFUN ("android-mouse-absolute-pixel-position",
index 936706b092e61c2830709635c52e3ffb480c7900..b58c39a527666c9138b96b30afebf60d2c9b5d81 100644 (file)
@@ -564,6 +564,24 @@ enum android_ic_mode
     ANDROID_IC_MODE_TEXT   = 2,
   };
 
+enum android_stack_mode
+  {
+    ANDROID_ABOVE = 0,
+    ANDROID_BELOW = 1,
+  };
+
+enum android_wc_value_mask
+  {
+    ANDROID_CW_SIBLING   = 0,
+    ANDROID_CW_STACK_MODE = 1,
+  };
+
+struct android_window_changes
+{
+  android_window sibling;
+  enum android_stack_mode stack_mode;
+};
+
 extern int android_pending (void);
 extern void android_next_event (union android_event *);
 extern bool android_check_if_event (union android_event *,
@@ -643,6 +661,9 @@ extern void android_bell (void);
 extern void android_set_input_focus (android_window, unsigned long);
 extern void android_raise_window (android_window);
 extern void android_lower_window (android_window);
+extern void android_reconfigure_wm_window (android_window,
+                                          enum android_wc_value_mask,
+                                          struct android_window_changes *);
 extern int android_query_tree (android_window, android_window *,
                               android_window *, android_window **,
                               unsigned int *);