Update Android port
* configure.ac (ANDROID_MIN_SDK): New variable. (DX): Remove and replace with D8. (XCONFIGURE): Check for the minimum version of Android the cross compiler compiles for. Generate java/AndroidManifest.xml from java/AndroidManifest.xml.in. Allow using Zlib on Android. * java/AndroidManifest.xml.in: New file. Use the minimum SDK detected by configure. * java/Makefile.in (top_srcdir, version): New variables. (DX, D8): Replace with D8. (ANDROID_MIN_SDK, APK_NAME): New variables. (.PHONY): (.PRECIOUS): (classes.dex): (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'. * java/debug.sh: New option --attach-existing. Attach to an existing Emacs instance when specified. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New field `isPaused'. (invalidateFocus1): Fix infinite recursion. (detachWindow): Deiconify window. (attachWindow): Iconify the window if the activity is paused. (onCreate): Use the ``no title bar'' theme. (onPause, onResume): New functions. * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown) (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New functions. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface): (list): Remove logging for code that is mostly going to be unused. * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree) (getScreenWidth, getScreenHeight, detectMouse): New functions. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra debug logging. Avoid deadlock in surfaceCreated. * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard to make the SurfaceView respect Z order. It didn't work. (handleDirtyBitmap): Copy over the contents from the old bitmap. (explicitlyDirtyBitmap): New function. (onLayout): Don't dirty bitmap if unnecessary. (damageRect, swapBuffers): Don't synchronize so hard. (onTouchEvent): Call window.onTouchEvent instead. (moveChildToBack, raise, lower): New functions. * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New subclass. (pointerMap, isMapped, isIconified, dontFocusOnMap) (dontAcceptFocus): New fields. (EmacsWindow): Don't immediately register unmapped window. (viewLayout): Send configure event outside the lock. (requestViewLayout): Explicitly dirty the bitmap. (mapWindow): Register the window now. Respect dontFocusOnMap. (unmapWindow): Unregister the window now. (figureChange, onTouchEvent): New functions. (onSomeKindOfMotionEvent): Handle scroll wheel events. (reparentTo, makeInputFocus, raise, lower, getWindowGeometry) (noticeIconified, noticeDeiconified, setDontAcceptFocus) (setDontFocusOnMap, getDontFocusOnMap): New functions. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (registerWindow, detachWindow): Synchronize. (noticeIconified, noticeDeiconified): New functions. (copyWindows): New function. * lisp/frame.el (frame-geometry, frame-edges) (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position) (frame-list-z-order, frame-restack, display-mouse-p) (display-monitor-attributes-list): Implement on Android. * lisp/mwheel.el (mouse-wheel-down-event): (mouse-wheel-up-event): (mouse-wheel-left-event): (mouse-wheel-right-event): Define on Android. * src/android.c (struct android_emacs_service): New methods `ringBell', `queryTree', `getScreenWidth', `getScreenHeight', and `detectMouse'. (struct android_event_queue, android_init_events) (android_next_event, android_write_event): Remove write limit. (android_file_access_p): Handle directories correcty. (android_close): Fix coding style. (android_fclose): New function. (android_init_emacs_service): Initialize new methods. (android_reparent_window): Implement function. (android_bell, android_set_input_focus, android_raise_window) (android_lower_window, android_query_tree, android_get_geometry) (android_get_screen_width, android_get_screen_height) (android_get_mm_width, android_get_mm_height, android_detect_mouse) (android_set_dont_focus_on_map, android_set_dont_accept_focus): New functions. (struct android_dir): New structure. (android_opendir, android_readdir, android_closedir): New functions. (emacs_abort): Implement here on Android and poke debuggerd into generating a tombstone. * src/android.h: Update prototypes. * src/androidfns.c (android_set_parent_frame): New function. (android_default_font_parameter): Use sane font size by default. (Fx_display_pixel_width, Fx_display_pixel_height) (Fx_display_mm_width, Fx_display_mm_height) (Fx_display_monitor_attributes_list): Rename to start with `android-'. Implement. Fiddle with documentation to introduce Android specific nuances. (Fandroid_display_monitor_attributes_list): New function. (Fx_frame_geometry, frame_geometry): New function. (Fandroid_frame_geometry): Implement correctly. (Fx_frame_list_z_order): Rename to start with `android-'. (android_frame_list_z_order, Fandroid_frame_list_z_order): Implement. (Fx_frame_restack): Rename to start with `android-'. (Fandroid_frame_restack): ``Implement''. (Fx_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_mouse_absolute_pixel_position): ``Implement''. (Fx_set_mouse_absolute_pixel_position): Rename to start with `android-'. (Fandroid_set_mouse_absolute_pixel_position): ``Implement''. (Fandroid_detect_mouse): New function. (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when clearing area. (android_set_no_focus_on_map, android_set_no_accept_focus): New functions. (android_frame_parm_handlers): Register new frame parameter handlers. (syms_of_androidfns): Update appropriately. * src/androidfont.c (androidfont_draw): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. * src/androidgui.h (enum android_event_type): New events. (struct android_touch_event, struct android_wheel_event) (struct android_iconify_event): New structures. (union android_event): Add new events. * src/androidterm.c (android_clear_frame): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. (android_flash, android_ring_bell): Implement bell ringing. (android_toggle_invisible_pointer): Don't TODO function that can't be implemented. (show_back_buffer, android_flush_dirty_back_buffer_on): Check if a buffer flip is required before doing the flip. (android_lower_frame, android_raise_frame): Implement functions. (android_update_tools, android_find_tool): New functions. (handle_one_android_event): Handle new iconification, wheel and touch events. (android_read_socket): Implement pending-autoraise-frames. (android_frame_up_to_date): Implement bell ringing. (android_buffer_flipping_unblocked_hook): Check if a buffer flip is required before doing the flip. (android_focus_frame, android_frame_highlight) (android_frame_unhighlight): New function. (android_frame_rehighlight): Implement functions. (android_iconify_frame): Always display error. (android_set_alpha): Update commentary. (android_free_frame_resources): Free frame touch points. (android_scroll_run, android_flip_and_flush) (android_clear_rectangle, android_draw_fringe_bitmap) (android_draw_glyph_string_background, android_fill_triangle) (android_clear_point, android_draw_relief_rect) (android_draw_box_rect, android_draw_glyph_string_bg_rect) (android_draw_image_foreground, android_draw_stretch_glyph_string) (android_draw_underwave, android_draw_glyph_string_foreground) (android_draw_composite_glyph_string_foreground) (android_draw_glyphless_glyph_string_foreground) (android_draw_glyph_string, android_clear_frame_area) (android_clear_under_internal_border, android_draw_hollow_cursor) (android_draw_bar_cursor, android_draw_vertical_window_border) (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW for drawing operations. * src/androidterm.h (struct android_touch_point): New structure. (struct android_output): New fields. (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro. * src/dired.c (emacs_readdir, open_directory) (directory_files_internal_unwind, read_dirent) (directory_files_internal, file_name_completion): Add indirection over readdir and opendir. Use android variants on Android. * src/dispnew.c (Fopen_termscript): * src/fileio.c (fclose_unwind): Use emacs_fclose. (Faccess_file): Call android_file_access_p. (file_accessible_directory_p): Append right suffix to Android assets directory. (do_auto_save_unwind): Use emacs_fclose. * src/keyboard.c (lispy_function_keys): Use right function key for page up and page down. (Fopen_dribble_file): Use emacs_fclose. * src/lisp.h: New prototype emacs_fclose. * src/lread.c (close_infile_unwind): Use emacs_fclose. * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation. (sfnt_prepare_raster): Compute raster width and height consistently with outline building. (sfnt_build_outline_edges): Use the same offsets used to set offy and offx. (main): Adjust debug code. * src/sfntfont-android.c (sfntfont_android_saturate32): Delete function. (sfntfont_android_blend, sfntfont_android_blendrgb): Remove unnecessary debug code. (sfntfont_android_composite_bitmap): Prevent out of bounds write. (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE. (init_sfntfont_android): Initialize Monospace Serif font to something sensible. * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics before summing up pcm. (sfntfont_draw): Use s->font instead of s->face->font. * src/sysdep.c (emacs_fclose): Wrap around android_fclose on android. * src/term.c (Fsuspend_tty): (delete_tty): Use emacs_fclose. * src/verbose.mk.in (AM_V_DX): Replace with D8 version.
This commit is contained in:
@@ -48,6 +48,9 @@ public class EmacsActivity extends Activity
|
||||
/* The currently focused window. */
|
||||
public static EmacsWindow focusedWindow;
|
||||
|
||||
/* Whether or not this activity is paused. */
|
||||
private boolean isPaused;
|
||||
|
||||
static
|
||||
{
|
||||
focusedActivities = new ArrayList<EmacsActivity> ();
|
||||
@@ -60,7 +63,7 @@ public class EmacsActivity extends Activity
|
||||
focusedWindow = window;
|
||||
|
||||
for (EmacsWindow child : window.children)
|
||||
invalidateFocus1 (window);
|
||||
invalidateFocus1 (child);
|
||||
}
|
||||
|
||||
public static void
|
||||
@@ -103,6 +106,9 @@ public class EmacsActivity extends Activity
|
||||
/* Clear the window's pointer to this activity and remove the
|
||||
window's view. */
|
||||
window.setConsumer (null);
|
||||
|
||||
/* The window can't be iconified any longer. */
|
||||
window.noticeDeiconified ();
|
||||
layout.removeView (window.view);
|
||||
window = null;
|
||||
|
||||
@@ -114,6 +120,8 @@ public class EmacsActivity extends Activity
|
||||
public void
|
||||
attachWindow (EmacsWindow child)
|
||||
{
|
||||
Log.d (TAG, "attachWindow: " + child);
|
||||
|
||||
if (window != null)
|
||||
throw new IllegalStateException ("trying to attach window when one"
|
||||
+ " already exists");
|
||||
@@ -124,6 +132,10 @@ public class EmacsActivity extends Activity
|
||||
layout.addView (window.view);
|
||||
child.setConsumer (this);
|
||||
|
||||
/* If the activity is iconified, send that to the window. */
|
||||
if (isPaused)
|
||||
window.noticeIconified ();
|
||||
|
||||
/* Invalidate the focus. */
|
||||
invalidateFocus ();
|
||||
}
|
||||
@@ -148,6 +160,9 @@ public class EmacsActivity extends Activity
|
||||
{
|
||||
FrameLayout.LayoutParams params;
|
||||
|
||||
/* Set the theme to one without a title bar. */
|
||||
setTheme (android.R.style.Theme_NoTitleBar);
|
||||
|
||||
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
|
||||
@@ -192,4 +207,24 @@ public class EmacsActivity extends Activity
|
||||
|
||||
invalidateFocus ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onPause ()
|
||||
{
|
||||
isPaused = true;
|
||||
|
||||
EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
|
||||
super.onResume ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onResume ()
|
||||
{
|
||||
isPaused = false;
|
||||
|
||||
EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
|
||||
super.onResume ();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,11 +96,34 @@ public class EmacsNative
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_BUTTON_RELEASE event. */
|
||||
/* Send an ANDROID_BUTTON_RELEASE event. */
|
||||
public static native void sendButtonRelease (short window, int x, int y,
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_TOUCH_DOWN event. */
|
||||
public static native void sendTouchDown (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_UP event. */
|
||||
public static native void sendTouchUp (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_MOVE event. */
|
||||
public static native void sendTouchMove (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_WHEEL event. */
|
||||
public static native void sendWheel (short window, int x, int y,
|
||||
long time, int state,
|
||||
float xDelta, float yDelta);
|
||||
|
||||
/* Send an ANDROID_ICONIFIED event. */
|
||||
public static native void sendIconified (short window);
|
||||
|
||||
/* Send an ANDROID_DEICONIFIED event. */
|
||||
public static native void sendDeiconified (short window);
|
||||
|
||||
static
|
||||
{
|
||||
System.loadLibrary ("emacs");
|
||||
|
||||
@@ -149,8 +149,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
|
||||
}
|
||||
else
|
||||
familyName = fileName;
|
||||
|
||||
Log.d (TAG, "Initialized new typeface " + familyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -321,17 +319,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
|
||||
|
||||
list = new LinkedList<FontEntity> ();
|
||||
|
||||
Log.d (TAG, ("Looking for fonts matching font spec: "
|
||||
+ fontSpec.toString ()));
|
||||
|
||||
for (i = 0; i < typefaceList.length; ++i)
|
||||
{
|
||||
if (checkMatch (typefaceList[i], fontSpec))
|
||||
list.add (new Sdk7FontEntity (typefaceList[i]));
|
||||
}
|
||||
|
||||
Log.d (TAG, "Found font entities: " + list.toString ());
|
||||
|
||||
return (FontEntity[]) list.toArray (new FontEntity[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,20 +28,27 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.os.IBinder;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibratorManager;
|
||||
import android.os.VibrationEffect;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import android.hardware.input.InputManager;
|
||||
|
||||
class Holder<T>
|
||||
{
|
||||
T thing;
|
||||
@@ -250,4 +257,113 @@ public class EmacsService extends Service
|
||||
{
|
||||
window.clearArea (x, y, width, height);
|
||||
}
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
public void
|
||||
ringBell ()
|
||||
{
|
||||
Vibrator vibrator;
|
||||
VibrationEffect effect;
|
||||
VibratorManager vibratorManager;
|
||||
Object tem;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
{
|
||||
tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
|
||||
vibratorManager = (VibratorManager) tem;
|
||||
vibrator = vibratorManager.getDefaultVibrator ();
|
||||
}
|
||||
else
|
||||
vibrator
|
||||
= (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
effect
|
||||
= VibrationEffect.createOneShot (50,
|
||||
VibrationEffect.DEFAULT_AMPLITUDE);
|
||||
vibrator.vibrate (effect);
|
||||
}
|
||||
else
|
||||
vibrator.vibrate (50);
|
||||
}
|
||||
|
||||
public short[]
|
||||
queryTree (EmacsWindow window)
|
||||
{
|
||||
short[] array;
|
||||
List<EmacsWindow> windowList;
|
||||
int i;
|
||||
|
||||
if (window == null)
|
||||
/* Just return all the windows without a parent. */
|
||||
windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
|
||||
else
|
||||
windowList = window.children;
|
||||
|
||||
array = new short[windowList.size () + 1];
|
||||
i = 1;
|
||||
|
||||
array[0] = window.parent != null ? 0 : window.parent.handle;
|
||||
|
||||
for (EmacsWindow treeWindow : windowList)
|
||||
array[i++] = treeWindow.handle;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public int
|
||||
getScreenWidth (boolean mmWise)
|
||||
{
|
||||
DisplayMetrics metrics;
|
||||
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
|
||||
if (!mmWise)
|
||||
return metrics.widthPixels;
|
||||
else
|
||||
return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
|
||||
}
|
||||
|
||||
public int
|
||||
getScreenHeight (boolean mmWise)
|
||||
{
|
||||
DisplayMetrics metrics;
|
||||
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
|
||||
if (!mmWise)
|
||||
return metrics.heightPixels;
|
||||
else
|
||||
return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
|
||||
}
|
||||
|
||||
public boolean
|
||||
detectMouse ()
|
||||
{
|
||||
InputManager manager;
|
||||
InputDevice device;
|
||||
int[] ids;
|
||||
int i;
|
||||
|
||||
if (Build.VERSION.SDK_INT
|
||||
< Build.VERSION_CODES.JELLY_BEAN)
|
||||
return false;
|
||||
|
||||
manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
|
||||
ids = manager.getInputDeviceIds ();
|
||||
|
||||
for (i = 0; i < ids.length; ++i)
|
||||
{
|
||||
device = manager.getInputDevice (ids[i]);
|
||||
|
||||
if (device == null)
|
||||
continue;
|
||||
|
||||
if (device.supportsSource (InputDevice.SOURCE_MOUSE))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,8 +27,12 @@ import android.os.Build;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class EmacsSurfaceView extends SurfaceView
|
||||
{
|
||||
private static final String TAG = "EmacsSurfaceView";
|
||||
|
||||
public Object surfaceChangeLock;
|
||||
private boolean created;
|
||||
|
||||
@@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView
|
||||
surfaceChanged (SurfaceHolder holder, int format,
|
||||
int width, int height)
|
||||
{
|
||||
Log.d (TAG, "surfaceChanged: " + view);
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceCreated: " + view);
|
||||
created = true;
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
/* Drop the lock when doing this, or a deadlock can
|
||||
result. */
|
||||
view.swapBuffers ();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView
|
||||
{
|
||||
synchronized (surfaceChangeLock)
|
||||
{
|
||||
Log.d (TAG, "surfaceDestroyed: " + view);
|
||||
created = false;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView
|
||||
return holder.lockCanvas (damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void
|
||||
onLayout (boolean changed, int left, int top, int right,
|
||||
int bottom)
|
||||
{
|
||||
Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
|
||||
+ " " + bottom + " -- " + changed + " visibility "
|
||||
+ getVisibility ()));
|
||||
}
|
||||
|
||||
/* This method is only used during debugging when it seems damage
|
||||
isn't working correctly. */
|
||||
|
||||
|
||||
@@ -87,12 +87,27 @@ public class EmacsView extends ViewGroup
|
||||
|
||||
/* Create the surface view. */
|
||||
this.surfaceView = new EmacsSurfaceView (this);
|
||||
this.surfaceView.setZOrderMediaOverlay (true);
|
||||
addView (this.surfaceView);
|
||||
|
||||
/* Not sure exactly what this does but it makes things magically
|
||||
work. Why is something as simple as XRaiseWindow so involved
|
||||
on Android? */
|
||||
setChildrenDrawingOrderEnabled (true);
|
||||
|
||||
/* Get rid of the foreground and background tint. */
|
||||
setBackgroundTintList (null);
|
||||
setForegroundTintList (null);
|
||||
}
|
||||
|
||||
private void
|
||||
handleDirtyBitmap ()
|
||||
{
|
||||
Bitmap oldBitmap;
|
||||
|
||||
/* Save the old bitmap. */
|
||||
oldBitmap = bitmap;
|
||||
|
||||
/* Recreate the front and back buffer bitmaps. */
|
||||
bitmap
|
||||
= Bitmap.createBitmap (bitmapDirty.width (),
|
||||
@@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup
|
||||
/* And canvases. */
|
||||
canvas = new Canvas (bitmap);
|
||||
|
||||
/* If Emacs is drawing to the bitmap right now from the
|
||||
main thread, the image contents are lost until the next
|
||||
ConfigureNotify and complete garbage. Sorry! */
|
||||
/* Copy over the contents of the old bitmap. */
|
||||
if (oldBitmap != null)
|
||||
canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
|
||||
|
||||
bitmapDirty = null;
|
||||
}
|
||||
|
||||
public synchronized void
|
||||
explicitlyDirtyBitmap (Rect rect)
|
||||
{
|
||||
if (bitmapDirty == null
|
||||
&& (bitmap == null
|
||||
|| rect.width () != bitmap.getWidth ()
|
||||
|| rect.height () != bitmap.getHeight ()))
|
||||
bitmapDirty = rect;
|
||||
}
|
||||
|
||||
public synchronized Bitmap
|
||||
getBitmap ()
|
||||
{
|
||||
@@ -168,25 +194,31 @@ public class EmacsView extends ViewGroup
|
||||
View child;
|
||||
Rect windowRect;
|
||||
|
||||
count = getChildCount ();
|
||||
|
||||
if (changed || mustReportLayout)
|
||||
{
|
||||
mustReportLayout = false;
|
||||
window.viewLayout (left, top, right, bottom);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
if (changed
|
||||
/* Check that a change has really happened. */
|
||||
&& (bitmapDirty == null
|
||||
|| bitmapDirty.width () != right - left
|
||||
|| bitmapDirty.height () != bottom - top))
|
||||
bitmapDirty = new Rect (left, top, right, bottom);
|
||||
|
||||
count = getChildCount ();
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
child = getChildAt (i);
|
||||
|
||||
Log.d (TAG, "onLayout: " + child);
|
||||
|
||||
if (child == surfaceView)
|
||||
/* The child is the surface view, so give it the entire
|
||||
view. */
|
||||
child.layout (left, top, right, bottom);
|
||||
child.layout (0, 0, right - left, bottom - top);
|
||||
else if (child.getVisibility () != GONE)
|
||||
{
|
||||
if (!(child instanceof EmacsView))
|
||||
@@ -201,59 +233,68 @@ public class EmacsView extends ViewGroup
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void
|
||||
public void
|
||||
damageRect (Rect damageRect)
|
||||
{
|
||||
damageRegion.union (damageRect);
|
||||
synchronized (damageRegion)
|
||||
{
|
||||
damageRegion.union (damageRect);
|
||||
}
|
||||
}
|
||||
|
||||
/* This method is called from both the UI thread and the Emacs
|
||||
thread. */
|
||||
|
||||
public synchronized void
|
||||
public void
|
||||
swapBuffers (boolean force)
|
||||
{
|
||||
Canvas canvas;
|
||||
Rect damageRect;
|
||||
Bitmap bitmap;
|
||||
|
||||
if (damageRegion.isEmpty ())
|
||||
return;
|
||||
/* Code must always take damageRegion, and then surfaceChangeLock,
|
||||
never the other way around! */
|
||||
|
||||
bitmap = getBitmap ();
|
||||
|
||||
/* Emacs must take the following lock to ensure the access to the
|
||||
canvas occurs with the surface created. Otherwise, Android
|
||||
will throttle calls to lockCanvas. */
|
||||
|
||||
synchronized (surfaceView.surfaceChangeLock)
|
||||
synchronized (damageRegion)
|
||||
{
|
||||
damageRect = damageRegion.getBounds ();
|
||||
|
||||
if (!surfaceView.isCreated ())
|
||||
if (damageRegion.isEmpty ())
|
||||
return;
|
||||
|
||||
if (bitmap == null)
|
||||
return;
|
||||
bitmap = getBitmap ();
|
||||
|
||||
/* Lock the canvas with the specified damage. */
|
||||
canvas = surfaceView.lockCanvas (damageRect);
|
||||
/* Emacs must take the following lock to ensure the access to the
|
||||
canvas occurs with the surface created. Otherwise, Android
|
||||
will throttle calls to lockCanvas. */
|
||||
|
||||
/* Return if locking the canvas failed. */
|
||||
if (canvas == null)
|
||||
return;
|
||||
synchronized (surfaceView.surfaceChangeLock)
|
||||
{
|
||||
damageRect = damageRegion.getBounds ();
|
||||
|
||||
/* Copy from the back buffer to the canvas. If damageRect was
|
||||
made empty, then draw the entire back buffer. */
|
||||
if (!surfaceView.isCreated ())
|
||||
return;
|
||||
|
||||
if (damageRect.isEmpty ())
|
||||
canvas.drawBitmap (bitmap, 0f, 0f, paint);
|
||||
else
|
||||
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
|
||||
if (bitmap == null)
|
||||
return;
|
||||
|
||||
/* Unlock the canvas and clear the damage. */
|
||||
surfaceView.unlockCanvasAndPost (canvas);
|
||||
damageRegion.setEmpty ();
|
||||
/* Lock the canvas with the specified damage. */
|
||||
canvas = surfaceView.lockCanvas (damageRect);
|
||||
|
||||
/* Return if locking the canvas failed. */
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
/* Copy from the back buffer to the canvas. If damageRect was
|
||||
made empty, then draw the entire back buffer. */
|
||||
|
||||
if (damageRect.isEmpty ())
|
||||
canvas.drawBitmap (bitmap, 0f, 0f, paint);
|
||||
else
|
||||
canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
|
||||
|
||||
/* Unlock the canvas and clear the damage. */
|
||||
surfaceView.unlockCanvasAndPost (canvas);
|
||||
damageRegion.setEmpty ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,6 +349,78 @@ public class EmacsView extends ViewGroup
|
||||
public boolean
|
||||
onTouchEvent (MotionEvent motion)
|
||||
{
|
||||
return window.onSomeKindOfMotionEvent (motion);
|
||||
return window.onTouchEvent (motion);
|
||||
}
|
||||
|
||||
private void
|
||||
moveChildToBack (View child)
|
||||
{
|
||||
int index;
|
||||
|
||||
index = indexOfChild (child);
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
detachViewFromParent (index);
|
||||
|
||||
/* The view at 0 is the surface view. */
|
||||
attachViewToParent (child, 1,
|
||||
child.getLayoutParams());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* The following two functions must not be called if the view has no
|
||||
parent, or is parented to an activity. */
|
||||
|
||||
public void
|
||||
raise ()
|
||||
{
|
||||
EmacsView parent;
|
||||
|
||||
parent = (EmacsView) getParent ();
|
||||
|
||||
Log.d (TAG, "raise: parent " + parent);
|
||||
|
||||
if (parent.indexOfChild (this)
|
||||
== parent.getChildCount () - 1)
|
||||
return;
|
||||
|
||||
parent.bringChildToFront (this);
|
||||
|
||||
/* Yes, all of this is really necessary! */
|
||||
parent.requestLayout ();
|
||||
parent.invalidate ();
|
||||
requestLayout ();
|
||||
invalidate ();
|
||||
|
||||
/* The surface view must be destroyed and recreated. */
|
||||
removeView (surfaceView);
|
||||
addView (surfaceView, 0);
|
||||
}
|
||||
|
||||
public void
|
||||
lower ()
|
||||
{
|
||||
EmacsView parent;
|
||||
|
||||
parent = (EmacsView) getParent ();
|
||||
|
||||
Log.d (TAG, "lower: parent " + parent);
|
||||
|
||||
if (parent.indexOfChild (this) == 1)
|
||||
return;
|
||||
|
||||
parent.moveChildToBack (this);
|
||||
|
||||
/* Yes, all of this is really necessary! */
|
||||
parent.requestLayout ();
|
||||
parent.invalidate ();
|
||||
requestLayout ();
|
||||
invalidate ();
|
||||
|
||||
/* The surface view must be removed and attached again. */
|
||||
removeView (surfaceView);
|
||||
addView (surfaceView, 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ package org.gnu.emacs;
|
||||
import java.lang.IllegalStateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Canvas;
|
||||
@@ -50,9 +51,29 @@ import android.os.Build;
|
||||
Views are also drawables, meaning they can accept drawing
|
||||
requests. */
|
||||
|
||||
/* Help wanted. What does not work includes `EmacsView.raise',
|
||||
`EmacsView.lower', reparenting a window onto another window.
|
||||
|
||||
All three are likely undocumented restrictions within
|
||||
EmacsSurface. */
|
||||
|
||||
public class EmacsWindow extends EmacsHandleObject
|
||||
implements EmacsDrawable
|
||||
{
|
||||
private static final String TAG = "EmacsWindow";
|
||||
|
||||
private class Coordinate
|
||||
{
|
||||
/* Integral coordinate. */
|
||||
int x, y;
|
||||
|
||||
Coordinate (int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
};
|
||||
|
||||
/* The view associated with the window. */
|
||||
public EmacsView view;
|
||||
|
||||
@@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
private Rect rect;
|
||||
|
||||
/* The parent window, or null if it is the root window. */
|
||||
private EmacsWindow parent;
|
||||
public EmacsWindow parent;
|
||||
|
||||
/* List of all children in stacking order. This must be kept
|
||||
consistent! */
|
||||
public ArrayList<EmacsWindow> children;
|
||||
|
||||
/* Map between pointer identifiers and last known position. Used to
|
||||
compute which pointer changed upon a touch event. */
|
||||
private HashMap<Integer, Coordinate> pointerMap;
|
||||
|
||||
/* The window consumer currently attached, if it exists. */
|
||||
private EmacsWindowAttachmentManager.WindowConsumer attached;
|
||||
|
||||
@@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
last button press or release event. */
|
||||
private int lastButtonState, lastModifiers;
|
||||
|
||||
/* Whether or not the window is mapped, and whether or not it is
|
||||
deiconified. */
|
||||
private boolean isMapped, isIconified;
|
||||
|
||||
/* Whether or not to ask for focus upon being mapped, and whether or
|
||||
not the window should be focusable. */
|
||||
private boolean dontFocusOnMap, dontAcceptFocus;
|
||||
|
||||
public
|
||||
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
|
||||
int width, int height)
|
||||
@@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
super (handle);
|
||||
|
||||
rect = new Rect (x, y, x + width, y + height);
|
||||
pointerMap = new HashMap<Integer, Coordinate> ();
|
||||
|
||||
/* Create the view from the context's UI thread. The window is
|
||||
unmapped, so the view is GONE. */
|
||||
@@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
if (parent != null)
|
||||
{
|
||||
parent.children.add (this);
|
||||
parent.view.post (new Runnable () {
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
@@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
|
||||
/* If parent is the root window, notice that there are new
|
||||
children available for interested activites to pick
|
||||
up. */
|
||||
|
||||
manager.registerWindow (EmacsWindow.this);
|
||||
}
|
||||
});
|
||||
|
||||
scratchGC = new EmacsGC ((short) 0);
|
||||
}
|
||||
@@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
+ "children!");
|
||||
|
||||
/* Remove the view from its parent and make it invisible. */
|
||||
view.post (new Runnable () {
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
@@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
|
||||
parent = (View) view.getParent ();
|
||||
|
||||
if (parent != null && attached == null)
|
||||
if (parent != null)
|
||||
((ViewGroup) parent).removeView (view);
|
||||
|
||||
manager.detachWindow (EmacsWindow.this);
|
||||
@@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
public void
|
||||
viewLayout (int left, int top, int right, int bottom)
|
||||
{
|
||||
int rectWidth, rectHeight;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
rect.left = left;
|
||||
rect.top = top;
|
||||
rect.right = right;
|
||||
rect.bottom = bottom;
|
||||
|
||||
EmacsNative.sendConfigureNotify (this.handle,
|
||||
System.currentTimeMillis (),
|
||||
left, top, rect.width (),
|
||||
rect.height ());
|
||||
}
|
||||
|
||||
rectWidth = right - left;
|
||||
rectHeight = bottom - top;
|
||||
|
||||
EmacsNative.sendConfigureNotify (this.handle,
|
||||
System.currentTimeMillis (),
|
||||
left, top, rectWidth,
|
||||
rectHeight);
|
||||
}
|
||||
|
||||
public void
|
||||
requestViewLayout ()
|
||||
{
|
||||
view.post (new Runnable () {
|
||||
/* This is necessary because otherwise subsequent drawing on the
|
||||
Emacs thread may be lost. */
|
||||
view.explicitlyDirtyBitmap (rect);
|
||||
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
@@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
public void
|
||||
mapWindow ()
|
||||
{
|
||||
view.post (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
if (isMapped)
|
||||
return;
|
||||
|
||||
view.setVisibility (View.VISIBLE);
|
||||
/* Eventually this should check no-focus-on-map. */
|
||||
view.requestFocus ();
|
||||
}
|
||||
});
|
||||
isMapped = true;
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
|
||||
/* Make the view visible, first of all. */
|
||||
view.setVisibility (View.VISIBLE);
|
||||
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
|
||||
/* If parent is the root window, notice that there are new
|
||||
children available for interested activites to pick
|
||||
up. */
|
||||
manager.registerWindow (EmacsWindow.this);
|
||||
|
||||
if (!getDontFocusOnMap ())
|
||||
/* Eventually this should check no-focus-on-map. */
|
||||
view.requestFocus ();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Do the same thing as above, but don't register this
|
||||
window. */
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.setVisibility (View.VISIBLE);
|
||||
|
||||
if (!getDontFocusOnMap ())
|
||||
/* Eventually this should check no-focus-on-map. */
|
||||
view.requestFocus ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
unmapWindow ()
|
||||
{
|
||||
if (!isMapped)
|
||||
return;
|
||||
|
||||
isMapped = false;
|
||||
|
||||
view.post (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
|
||||
view.setVisibility (View.GONE);
|
||||
|
||||
/* Now that the window is unmapped, unregister it as
|
||||
well. */
|
||||
manager.detachWindow (EmacsWindow.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* Return the ID of the pointer which changed in EVENT. Value is -1
|
||||
if it could not be determined, else the pointer that changed, or
|
||||
-2 if -1 would have been returned, but there is also a pointer
|
||||
that is a mouse. */
|
||||
|
||||
private int
|
||||
figureChange (MotionEvent event)
|
||||
{
|
||||
int pointerID, i, truncatedX, truncatedY, pointerIndex;
|
||||
Coordinate coordinate;
|
||||
boolean mouseFlag;
|
||||
|
||||
/* pointerID is always initialized but the Java compiler is too
|
||||
dumb to know that. */
|
||||
pointerID = -1;
|
||||
mouseFlag = false;
|
||||
|
||||
switch (event.getActionMasked ())
|
||||
{
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
/* Primary pointer pressed with index 0. */
|
||||
|
||||
/* Detect mice. If this is a mouse event, give it to
|
||||
onSomeKindOfMotionEvent. */
|
||||
if ((Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
&& event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
|
||||
return -2;
|
||||
|
||||
pointerID = event.getPointerId (0);
|
||||
pointerMap.put (pointerID,
|
||||
new Coordinate ((int) event.getX (0),
|
||||
(int) event.getY (0)));
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
/* Primary pointer released with index 0. */
|
||||
pointerID = event.getPointerId (0);
|
||||
pointerMap.remove (pointerID);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
/* New pointer. Find the pointer ID from the index and place
|
||||
it in the map. */
|
||||
pointerIndex = event.getActionIndex ();
|
||||
pointerID = event.getPointerId (pointerIndex);
|
||||
pointerMap.put (pointerID,
|
||||
new Coordinate ((int) event.getX (pointerID),
|
||||
(int) event.getY (pointerID)));
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
/* Pointer removed. Remove it from the map. */
|
||||
pointerIndex = event.getActionIndex ();
|
||||
pointerID = event.getPointerId (pointerIndex);
|
||||
pointerMap.remove (pointerID);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Loop through each pointer in the event. */
|
||||
for (i = 0; i < event.getPointerCount (); ++i)
|
||||
{
|
||||
pointerID = event.getPointerId (i);
|
||||
|
||||
/* Look up that pointer in the map. */
|
||||
coordinate = pointerMap.get (pointerID);
|
||||
|
||||
if (coordinate != null)
|
||||
{
|
||||
/* See if coordinates have changed. */
|
||||
truncatedX = (int) event.getX (i);
|
||||
truncatedY = (int) event.getY (i);
|
||||
|
||||
if (truncatedX != coordinate.x
|
||||
|| truncatedY != coordinate.y)
|
||||
{
|
||||
/* The pointer changed. Update the coordinate and
|
||||
break out of the loop. */
|
||||
coordinate.x = truncatedX;
|
||||
coordinate.y = truncatedY;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if this is a mouse. If so, set the mouseFlag. */
|
||||
if ((Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
&& event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
|
||||
mouseFlag = true;
|
||||
}
|
||||
|
||||
/* Set the pointer ID to -1 if the loop failed to find any
|
||||
changed pointer. If a mouse pointer was found, set it to
|
||||
-2. */
|
||||
if (i == event.getPointerCount ())
|
||||
pointerID = (mouseFlag ? -2 : -1);
|
||||
}
|
||||
|
||||
/* Return the pointer ID. */
|
||||
return pointerID;
|
||||
}
|
||||
|
||||
public boolean
|
||||
onTouchEvent (MotionEvent event)
|
||||
{
|
||||
int pointerID, index;
|
||||
|
||||
/* Extract the ``touch ID'' (or in Android, the ``pointer
|
||||
ID''.) */
|
||||
pointerID = figureChange (event);
|
||||
|
||||
if (pointerID < 0)
|
||||
{
|
||||
/* If this is a mouse event, give it to
|
||||
onSomeKindOfMotionEvent. */
|
||||
if (pointerID == -2)
|
||||
return onSomeKindOfMotionEvent (event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find the pointer index corresponding to the event. */
|
||||
index = event.findPointerIndex (pointerID);
|
||||
|
||||
switch (event.getActionMasked ())
|
||||
{
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
/* Touch down event. */
|
||||
EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
|
||||
(int) event.getY (index),
|
||||
event.getEventTime (), pointerID);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
/* Touch up event. */
|
||||
EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
|
||||
(int) event.getY (index),
|
||||
event.getEventTime (), pointerID);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
/* Pointer motion event. */
|
||||
EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
|
||||
(int) event.getY (index),
|
||||
event.getEventTime (), pointerID);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean
|
||||
onSomeKindOfMotionEvent (MotionEvent event)
|
||||
{
|
||||
@@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_UP:
|
||||
/* Emacs must return true even though touch events are not yet
|
||||
handled, because the value of this function is used by the
|
||||
system to decide whether or not Emacs gets ACTION_MOVE
|
||||
/* Emacs must return true even though touch events are not
|
||||
handled here, because the value of this function is used by
|
||||
the system to decide whether or not Emacs gets ACTION_MOVE
|
||||
events. */
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
/* Send a scroll event with the specified deltas. */
|
||||
EmacsNative.sendWheel (this.handle, (int) event.getX (),
|
||||
(int) event.getY (),
|
||||
event.getEventTime (),
|
||||
lastModifiers,
|
||||
event.getAxisValue (MotionEvent.AXIS_HSCROLL),
|
||||
event.getAxisValue (MotionEvent.AXIS_VSCROLL));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void
|
||||
reparentTo (final EmacsWindow otherWindow, int x, int y)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
/* Reparent this window to the other window. */
|
||||
|
||||
if (parent != null)
|
||||
parent.children.remove (this);
|
||||
|
||||
if (otherWindow != null)
|
||||
otherWindow.children.add (this);
|
||||
|
||||
parent = otherWindow;
|
||||
|
||||
/* Move this window to the new location. */
|
||||
synchronized (this)
|
||||
{
|
||||
width = rect.width ();
|
||||
height = rect.height ();
|
||||
rect.left = x;
|
||||
rect.top = y;
|
||||
rect.right = x + width;
|
||||
rect.bottom = y + height;
|
||||
}
|
||||
|
||||
/* Now do the work necessary on the UI thread to reparent the
|
||||
window. */
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
View parent;
|
||||
|
||||
/* First, detach this window if necessary. */
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
manager.detachWindow (EmacsWindow.this);
|
||||
|
||||
/* Also unparent this view. */
|
||||
parent = (View) view.getParent ();
|
||||
|
||||
if (parent != null)
|
||||
((ViewGroup) parent).removeView (view);
|
||||
|
||||
/* Next, either add this window as a child of the new
|
||||
parent's view, or make it available again. */
|
||||
if (otherWindow != null)
|
||||
otherWindow.view.addView (view);
|
||||
else if (EmacsWindow.this.isMapped)
|
||||
manager.registerWindow (EmacsWindow.this);
|
||||
|
||||
/* Request relayout. */
|
||||
view.requestLayout ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void
|
||||
makeInputFocus (long time)
|
||||
{
|
||||
/* TIME is currently ignored. Request the input focus now. */
|
||||
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.requestFocus ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void
|
||||
raise ()
|
||||
{
|
||||
/* This does nothing here. */
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
/* Remove and add this view again. */
|
||||
parent.children.remove (this);
|
||||
parent.children.add (this);
|
||||
|
||||
/* Request a relayout. */
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.raise ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void
|
||||
lower ()
|
||||
{
|
||||
/* This does nothing here. */
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
/* Remove and add this view again. */
|
||||
parent.children.remove (this);
|
||||
parent.children.add (this);
|
||||
|
||||
/* Request a relayout. */
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.lower ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int[]
|
||||
getWindowGeometry ()
|
||||
{
|
||||
int[] array;
|
||||
Rect rect;
|
||||
|
||||
array = new int[4];
|
||||
rect = getGeometry ();
|
||||
|
||||
array[0] = rect.left;
|
||||
array[1] = rect.top;
|
||||
array[2] = rect.width ();
|
||||
array[3] = rect.height ();
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public void
|
||||
noticeIconified ()
|
||||
{
|
||||
isIconified = true;
|
||||
EmacsNative.sendIconified (this.handle);
|
||||
}
|
||||
|
||||
public void
|
||||
noticeDeiconified ()
|
||||
{
|
||||
isIconified = false;
|
||||
EmacsNative.sendDeiconified (this.handle);
|
||||
}
|
||||
|
||||
public synchronized void
|
||||
setDontAcceptFocus (final boolean dontAcceptFocus)
|
||||
{
|
||||
this.dontAcceptFocus = dontAcceptFocus;
|
||||
|
||||
/* Update the view's focus state. */
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
view.setFocusable (!dontAcceptFocus);
|
||||
view.setFocusableInTouchMode (!dontAcceptFocus);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized void
|
||||
setDontFocusOnMap (final boolean dontFocusOnMap)
|
||||
{
|
||||
this.dontFocusOnMap = dontFocusOnMap;
|
||||
}
|
||||
|
||||
public synchronized boolean
|
||||
getDontFocusOnMap ()
|
||||
{
|
||||
return dontFocusOnMap;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -68,7 +69,7 @@ public class EmacsWindowAttachmentManager
|
||||
};
|
||||
|
||||
private List<WindowConsumer> consumers;
|
||||
private List<EmacsWindow> windows;
|
||||
public List<EmacsWindow> windows;
|
||||
|
||||
public
|
||||
EmacsWindowAttachmentManager ()
|
||||
@@ -98,12 +99,19 @@ public class EmacsWindowAttachmentManager
|
||||
EmacsNative.sendWindowAction ((short) 0, 0);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
registerWindow (EmacsWindow window)
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
Log.d (TAG, "registerWindow " + window);
|
||||
Log.d (TAG, "registerWindow (maybe): " + window);
|
||||
|
||||
if (windows.contains (window))
|
||||
/* The window is already registered. */
|
||||
return;
|
||||
|
||||
Log.d (TAG, "registerWindow: " + window);
|
||||
|
||||
windows.add (window);
|
||||
|
||||
for (WindowConsumer consumer : consumers)
|
||||
@@ -146,7 +154,7 @@ public class EmacsWindowAttachmentManager
|
||||
consumers.remove (consumer);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
detachWindow (EmacsWindow window)
|
||||
{
|
||||
WindowConsumer consumer;
|
||||
@@ -162,5 +170,43 @@ public class EmacsWindowAttachmentManager
|
||||
consumers.remove (consumer);
|
||||
consumer.destroy ();
|
||||
}
|
||||
|
||||
windows.remove (window);
|
||||
}
|
||||
|
||||
public void
|
||||
noticeIconified (WindowConsumer consumer)
|
||||
{
|
||||
EmacsWindow window;
|
||||
|
||||
Log.d (TAG, "noticeIconified " + consumer);
|
||||
|
||||
/* If a window is attached, send the appropriate iconification
|
||||
events. */
|
||||
window = consumer.getAttachedWindow ();
|
||||
|
||||
if (window != null)
|
||||
window.noticeIconified ();
|
||||
}
|
||||
|
||||
public void
|
||||
noticeDeiconified (WindowConsumer consumer)
|
||||
{
|
||||
EmacsWindow window;
|
||||
|
||||
Log.d (TAG, "noticeDeiconified " + consumer);
|
||||
|
||||
/* If a window is attached, send the appropriate iconification
|
||||
events. */
|
||||
window = consumer.getAttachedWindow ();
|
||||
|
||||
if (window != null)
|
||||
window.noticeDeiconified ();
|
||||
}
|
||||
|
||||
public synchronized List<EmacsWindow>
|
||||
copyWindows ()
|
||||
{
|
||||
return new ArrayList<EmacsWindow> (windows);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user