1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-26 07:33:47 +00:00

Implement frame restacking under Android

* 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.
This commit is contained in:
Po Lu 2023-10-10 13:11:14 +08:00
parent 238292d657
commit 336c367411
7 changed files with 264 additions and 33 deletions

View 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

View File

@ -505,15 +505,18 @@ invocation of app_process (through android-emacs) can
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;
}

View File

@ -581,12 +581,12 @@ else if (child.getVisibility () != GONE)
/* 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 @@ else if (child.getVisibility () != GONE)
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)

View File

@ -22,6 +22,7 @@
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 @@ private static class Coordinate
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 @@ private static class Coordinate
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 @@ private static class Coordinate
destroyHandle () throws IllegalStateException
{
if (parent != null)
parent.children.remove (this);
{
synchronized (parent.children)
{
parent.children.remove (this);
}
}
EmacsActivity.invalidateFocus ();
@ -1163,10 +1175,20 @@ private static class Coordinate
/* 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 @@ else if (EmacsWindow.this.isMapped)
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 @@ else if (EmacsWindow.this.isMapped)
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 @@ else if (EmacsWindow.this.isMapped)
});
}
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 ()
{

View 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,

View 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",

View 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 *);