aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-01-02 21:38:19 +0800
committerPo Lu2023-01-02 21:38:19 +0800
commita32963e11f9f8e5d22b0d754d34a867f3b178ed2 (patch)
tree898f12248beb2feb63ed4295f9de40c89053ae19 /java
parentfd074f3133a348dd1d3b7ee569f0fc046223efb9 (diff)
downloademacs-a32963e11f9f8e5d22b0d754d34a867f3b178ed2.tar.gz
emacs-a32963e11f9f8e5d22b0d754d34a867f3b178ed2.zip
Update Android port
* Makefile.in (java): Depend on info. (MAKEFILE_NAME): (config.status): Remove unneeded changes. * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a C++ compiler on Android. * java/AndroidManifest.xml: <EmacsActivity>: Set launchMode appropriately. <EmacsMultitaskActivity>: New activity. * java/Makefile.in (CROSS_BINS): Add EmacsClient. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity) (onCreate): Use the window attachment manager. * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea) (paintTo): Implement clip masks correctly. * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo): Fix damage tracking rectangles. * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString): New function. (FontMetrics, EmacsFontDriver): Fix signature of textExtents. * java/org/gnu/emacs/EmacsMultitaskActivity.java (EmacsMultitaskActivity): New file. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New functions sendFocusIn, sendFocusOut, sendWindowAction. * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping handling. * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add constructor for mutable pixmaps. * java/org/gnu/emacs/EmacsSdk23FontDriver.java (EmacsSdk23FontDriver): New file. * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject) (checkMatch, hasChar, encodeChar): Implement text display and fix font metrics semantics. * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove availableChildren. (getLibraryDirectory, onCreate): Pass pixel density to Emacs. (clearArea): Fix arguments. Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged) (surfaceCreated): Flip buffers on surface attachment. * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): New argument FORCE. Always swap if it is true. (onKeyMultiple, onFocusChanged): New functions. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle) (run): Switch to using the window attachment manager. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): New file. * lisp/cus-edit.el (custom-button, custom-button-mouse) (custom-button-pressed): * lisp/faces.el (tool-bar): Define faces correctly on Android. * src/android.c (struct android_emacs_pixmap): Add mutable constructor. (struct android_emacs_drawable): New structure. (android_write_event): Check if event queue hasn't yet been initialized. (android_select): Set errno to EINTR if pselect fails. (android_close): Remove unused debugging code. (android_get_home_directory): New function. (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel density and compute game path. (android_init_emacs_drawable): New function. (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument `unicode_char'. Pass it in events. (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise. (Java_org_gnu_emacs_EmacsNative_sendFocusIn) (Java_org_gnu_emacs_EmacsNative_sendFocusOut) (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New functions. (android_resolve_handle): Export function. (android_change_gc): Clear clip rects under the right circumstances. Set right clip mask field. (android_create_pixmap_from_bitmap_data): Use correct alpha channels. (android_create_pixmap): Create mutable pixmap and avoid redundant color array allocation. (android_create_bitmap_from_data, android_create_image) (android_destroy_image, android_put_pixel, android_get_pixel) (android_get_image, android_put_image, faccessat): New functions. * src/android.h: Update prototypes. * src/androidfns.c (android_default_font_parameter): Prefer monospace to Droid Sans Mono. * src/androidfont.c (struct android_emacs_font_driver): New method `draw'. (struct android_emacs_font_spec): New field `dpi'. (struct androidfont_info): Add font metrics cache. (android_init_font_driver, android_init_font_spec): Adjust accordingly. (androidfont_from_lisp, androidfont_from_java): Handle new fields. (androidfont_draw): Implement function. (androidfont_open_font): Set pixel size correctly. (androidfont_close_font): Free metrics cache. (androidfont_cache_text_extents) (androidfont_check_cached_extents): New functions. (androidfont_text_extents): Cache glyph metrics somewhere for future use. (androidfont_list_family): Implement function. * src/androidgui.h (enum android_event_type): New focus and window action events. (enum android_modifier_mask): New masks. (struct android_key_event): New field `unicode_char'. (ANDROID_IS_MODIFIER_KEY): Newmacro. (struct android_focus_event, struct android_window_action_event): New structs. (union android_event): Add new fields. (enum android_image_format, struct android_image): New enums and structs. * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers, android_lower_frame) (android_raise_frame, android_new_focus_frame) (android_focus_changed, android_detect_focus_change): New functions. (handle_one_android_event): Implement focus and key event handling. (android_frame_rehighlight): New function. (android_frame_raise_lower): Implement accordingly. (android_make_frame_invisible): Clear highlight_frame if required. (android_free_frame_resources): Clear x_focus_event_frame if required. (android_draw_fringe_bitmap, android_draw_image_foreground) (android_draw_image_foreground_1) (android_draw_image_glyph_string): Remove unnecessary code. (android_create_terminal, android_term_init): Set the baud rate to something sensible. * src/androidterm.h (struct android_bitmap_record): Make structure the same as on X. (struct android_display_info): New focus tracking fields. (struct android_output): Likewise. * src/dispextern.h (struct image): Add ximg and mask_img on Android. * src/emacs.c (android_emacs_init): Fix argc sorting iteration. * src/fileio.c (user_homedir): (get_homedir): Implement correctly on Android. * src/font.h (PT_PER_INCH): Define correctly on Android. * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe bitmaps correctly on Android. * src/image.c (GET_PIXEL, image_create_bitmap_from_data) (image_create_bitmap_from_file, free_bitmap_record) (image_unget_x_image_or_dc, struct image_type) (prepare_image_for_display, image_clear_image_1) (image_size_in_bytes, x_check_image_size) (x_create_x_image_and_pixmap, x_destroy_x_image) (image_check_image_size, image_create_x_image_and_pixmap_1) (image_destroy_x_image, gui_put_x_image, image_put_x_image) (image_get_x_image, image_unget_x_image) (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross) (MaskForeground, image_types, syms_of_image): Implement all of the above on Android in terms of an API very similar to X. * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys): Define on Android to something sensible. * src/lread.c (build_load_history): Fix problem.
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml9
-rw-r--r--java/Makefile.in3
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java153
-rw-r--r--java/org/gnu/emacs/EmacsCopyArea.java135
-rw-r--r--java/org/gnu/emacs/EmacsDrawRectangle.java8
-rw-r--r--java/org/gnu/emacs/EmacsFontDriver.java26
-rw-r--r--java/org/gnu/emacs/EmacsMultitaskActivity.java25
-rw-r--r--java/org/gnu/emacs/EmacsNative.java18
-rw-r--r--java/org/gnu/emacs/EmacsPaintQueue.java24
-rw-r--r--java/org/gnu/emacs/EmacsPixmap.java29
-rw-r--r--java/org/gnu/emacs/EmacsSdk23FontDriver.java43
-rw-r--r--java/org/gnu/emacs/EmacsSdk7FontDriver.java188
-rw-r--r--java/org/gnu/emacs/EmacsService.java83
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java37
-rw-r--r--java/org/gnu/emacs/EmacsView.java45
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java143
-rw-r--r--java/org/gnu/emacs/EmacsWindowAttachmentManager.java166
17 files changed, 904 insertions, 231 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 75aa5bdf409..06417017ae3 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -31,13 +31,15 @@
31 android:targetSdkVersion="28"/> 31 android:targetSdkVersion="28"/>
32 32
33 <application android:name="org.gnu.emacs.EmacsApplication" 33 <application android:name="org.gnu.emacs.EmacsApplication"
34 android:label="GNU Emacs" 34 android:label="Emacs"
35 android:hardwareAccelerated="true" 35 android:hardwareAccelerated="true"
36 android:supportsRtl="true" 36 android:supportsRtl="true"
37 android:theme="@android:style/Theme" 37 android:theme="@android:style/Theme"
38 android:debuggable="true" 38 android:debuggable="true"
39 android:extractNativeLibs="true"> 39 android:extractNativeLibs="true">
40 <activity android:name="org.gnu.emacs.EmacsActivity"> 40 <activity android:name="org.gnu.emacs.EmacsActivity"
41 android:launchMode="singleTop"
42 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
41 <intent-filter> 43 <intent-filter>
42 <action android:name="android.intent.action.MAIN" /> 44 <action android:name="android.intent.action.MAIN" />
43 <category android:name="android.intent.category.DEFAULT" /> 45 <category android:name="android.intent.category.DEFAULT" />
@@ -45,6 +47,9 @@
45 </intent-filter> 47 </intent-filter>
46 </activity> 48 </activity>
47 49
50 <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
51 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
52
48 <service android:name="org.gnu.emacs.EmacsService" 53 <service android:name="org.gnu.emacs.EmacsService"
49 android:directBootAware="false" 54 android:directBootAware="false"
50 android:enabled="true" 55 android:enabled="true"
diff --git a/java/Makefile.in b/java/Makefile.in
index e9fcc625cb2..31bd22b5a2e 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -60,7 +60,7 @@ all: emacs.apk
60# Binaries to cross-compile. 60# Binaries to cross-compile.
61CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \ 61CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \
62 ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \ 62 ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
63 ../xcompile/lib-src/ebrowse 63 ../xcompile/lib-src/ebrowse ../xcompile/lib-src/emacsclient
64 64
65# Libraries to cross-compile. 65# Libraries to cross-compile.
66CROSS_LIBS = ../xcompile/src/libemacs.so 66CROSS_LIBS = ../xcompile/src/libemacs.so
@@ -84,6 +84,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
84# Install architecture independents to assets/etc and assets/lisp 84# Install architecture independents to assets/etc and assets/lisp
85 cp -r $(top_builddir)/lisp install_temp/assets 85 cp -r $(top_builddir)/lisp install_temp/assets
86 cp -r $(top_builddir)/etc install_temp/assets 86 cp -r $(top_builddir)/etc install_temp/assets
87 cp -r $(top_builddir)/info install_temp/assets
87# Remove undesirable files from those directories. 88# Remove undesirable files from those directories.
88 for subdir in `find install_temp -type d -print`; do \ 89 for subdir in `find install_temp -type d -print`; do \
89 chmod a+rx $${subdir} ; \ 90 chmod a+rx $${subdir} ; \
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
index cacd5f13e60..67f411a38c3 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -32,65 +32,114 @@ import android.widget.FrameLayout;
32import android.widget.FrameLayout.LayoutParams; 32import android.widget.FrameLayout.LayoutParams;
33 33
34public class EmacsActivity extends Activity 34public class EmacsActivity extends Activity
35 implements EmacsWindowAttachmentManager.WindowConsumer
35{ 36{
36 public static final String TAG = "EmacsActivity"; 37 public static final String TAG = "EmacsActivity";
37 38
38 /* List of all activities that do not have an associated
39 EmacsWindow. */
40 public static List<EmacsActivity> availableActivities;
41
42 /* The currently attached EmacsWindow, or null if none. */ 39 /* The currently attached EmacsWindow, or null if none. */
43 private EmacsWindow window; 40 private EmacsWindow window;
44 41
45 /* The frame layout associated with the activity. */ 42 /* The frame layout associated with the activity. */
46 private FrameLayout layout; 43 private FrameLayout layout;
47 44
45 /* List of activities with focus. */
46 private static List<EmacsActivity> focusedActivities;
47
48 /* The currently focused window. */
49 public static EmacsWindow focusedWindow;
50
48 static 51 static
49 { 52 {
50 /* Set up the list of available activities. */ 53 focusedActivities = new ArrayList<EmacsActivity> ();
51 availableActivities = new ArrayList<EmacsActivity> ();
52 }; 54 };
53 55
56 public static void
57 invalidateFocus1 (EmacsWindow window)
58 {
59 if (window.view.isFocused ())
60 focusedWindow = window;
61
62 for (EmacsWindow child : window.children)
63 invalidateFocus1 (window);
64 }
65
66 public static void
67 invalidateFocus ()
68 {
69 EmacsWindow oldFocus;
70
71 /* Walk through each focused activity and assign the window focus
72 to the bottom-most focused window within. Record the old focus
73 as well. */
74 oldFocus = focusedWindow;
75 focusedWindow = null;
76
77 for (EmacsActivity activity : focusedActivities)
78 {
79 if (activity.window != null)
80 invalidateFocus1 (activity.window);
81 }
82
83 /* Send focus in- and out- events to the previous and current
84 focus. */
85
86 if (oldFocus != null)
87 EmacsNative.sendFocusOut (oldFocus.handle,
88 System.currentTimeMillis ());
89
90 if (focusedWindow != null)
91 EmacsNative.sendFocusIn (focusedWindow.handle,
92 System.currentTimeMillis ());
93 }
94
95 @Override
54 public void 96 public void
55 attachChild (EmacsWindow child) 97 detachWindow ()
98 {
99 if (window == null)
100 Log.w (TAG, "detachWindow called, but there is no window");
101 else
102 {
103 /* Clear the window's pointer to this activity and remove the
104 window's view. */
105 window.setConsumer (null);
106 layout.removeView (window.view);
107 window = null;
108
109 invalidateFocus ();
110 }
111 }
112
113 @Override
114 public void
115 attachWindow (EmacsWindow child)
56 { 116 {
57 if (window != null) 117 if (window != null)
58 throw new IllegalStateException ("trying to attach window when one" 118 throw new IllegalStateException ("trying to attach window when one"
59 + " already exists"); 119 + " already exists");
60 120
61 /* Record and attach the view. */ 121 /* Record and attach the view. */
122
62 window = child; 123 window = child;
63 layout.addView (window.view); 124 layout.addView (window.view);
125 child.setConsumer (this);
64 126
65 /* Remove the objects from the lists of what is available. */ 127 /* Invalidate the focus. */
66 EmacsService.availableChildren.remove (child); 128 invalidateFocus ();
67 availableActivities.remove (this);
68
69 /* Now set child->activity. */
70 child.setActivity (this);
71 } 129 }
72 130
73 /* Make this activity available for future windows to attach 131 @Override
74 again. */
75
76 public void 132 public void
77 makeAvailable () 133 destroy ()
78 { 134 {
79 window = null; 135 finish ();
80 136 }
81 for (EmacsWindow iterWindow
82 : EmacsService.availableChildren)
83 {
84 synchronized (iterWindow)
85 {
86 if (!iterWindow.isDestroyed ())
87 attachChild (iterWindow);
88
89 return;
90 }
91 }
92 137
93 availableActivities.add (this); 138 @Override
139 public EmacsWindow
140 getAttachedWindow ()
141 {
142 return window;
94 } 143 }
95 144
96 @Override 145 @Override
@@ -109,38 +158,38 @@ public class EmacsActivity extends Activity
109 /* Set it as the content view. */ 158 /* Set it as the content view. */
110 setContentView (layout); 159 setContentView (layout);
111 160
112 /* Make the activity available before starting the
113 service. */
114 makeAvailable ();
115
116 if (EmacsService.SERVICE == null) 161 if (EmacsService.SERVICE == null)
117 /* Start the Emacs service now. */ 162 /* Start the Emacs service now. */
118 startService (new Intent (this, EmacsService.class)); 163 startService (new Intent (this, EmacsService.class));
119 164
165 /* Add this activity to the list of available activities. */
166 EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
167
120 super.onCreate (savedInstanceState); 168 super.onCreate (savedInstanceState);
121 } 169 }
122 170
123 @Override 171 @Override
124 public void 172 public void
125 onStop () 173 onDestroy ()
126 { 174 {
127 /* The activity is no longer visible. If there is a window 175 /* The activity will die shortly hereafter. If there is a window
128 attached, detach it. */ 176 attached, close it now. */
129 177 Log.d (TAG, "onDestroy " + this);
130 if (window != null) 178 EmacsWindowAttachmentManager.MANAGER.removeWindowConsumer (this);
131 { 179 focusedActivities.remove (this);
132 layout.removeView (window.view); 180 invalidateFocus ();
181 super.onDestroy ();
182 }
133 183
134 /* Notice that the window is already available too. But do 184 @Override
135 not call noticeAvailableChild; that might assign it to some 185 public void
136 other activity, which behaves badly. */ 186 onWindowFocusChanged (boolean isFocused)
137 EmacsService.availableChildren.add (window); 187 {
138 window = null; 188 if (isFocused && !focusedActivities.contains (this))
139 } 189 focusedActivities.add (this);
190 else
191 focusedActivities.remove (this);
140 192
141 /* Finally, remove this activity from the list of available 193 invalidateFocus ();
142 activities. */
143 availableActivities.remove (this);
144 super.onStop ();
145 } 194 }
146}; 195};
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java
index f34d1ecde01..0dd5b2c1fb6 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -32,19 +32,22 @@ public class EmacsCopyArea implements EmacsPaintReq
32 private int src_x, src_y, dest_x, dest_y, width, height; 32 private int src_x, src_y, dest_x, dest_y, width, height;
33 private EmacsDrawable destination, source; 33 private EmacsDrawable destination, source;
34 private EmacsGC immutableGC; 34 private EmacsGC immutableGC;
35 private static Xfermode xorAlu, srcInAlu; 35 private static Xfermode xorAlu, srcInAlu, overAlu;
36 36
37 static 37 static
38 { 38 {
39 overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
39 xorAlu = new PorterDuffXfermode (Mode.XOR); 40 xorAlu = new PorterDuffXfermode (Mode.XOR);
40 srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); 41 srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
41 }; 42 };
42 43
43 public 44 public
44 EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source, 45 EmacsCopyArea (EmacsDrawable source, EmacsDrawable destination,
45 int src_x, int src_y, int width, int height, 46 int src_x, int src_y, int width, int height,
46 int dest_x, int dest_y, EmacsGC immutableGC) 47 int dest_x, int dest_y, EmacsGC immutableGC)
47 { 48 {
49 Bitmap bitmap;
50
48 this.destination = destination; 51 this.destination = destination;
49 this.source = source; 52 this.source = source;
50 this.src_x = src_x; 53 this.src_x = src_x;
@@ -71,6 +74,16 @@ public class EmacsCopyArea implements EmacsPaintReq
71 return destination; 74 return destination;
72 } 75 }
73 76
77 private void
78 insetRectBy (Rect rect, int left, int top, int right,
79 int bottom)
80 {
81 rect.left += left;
82 rect.top += top;
83 rect.right -= right;
84 rect.bottom -= bottom;
85 }
86
74 @Override 87 @Override
75 public EmacsGC 88 public EmacsGC
76 getGC () 89 getGC ()
@@ -86,16 +99,45 @@ public class EmacsCopyArea implements EmacsPaintReq
86 Bitmap bitmap; 99 Bitmap bitmap;
87 Paint maskPaint; 100 Paint maskPaint;
88 Canvas maskCanvas; 101 Canvas maskCanvas;
89 Bitmap maskBitmap; 102 Bitmap srcBitmap, maskBitmap, clipBitmap;
90 Rect rect, srcRect; 103 Rect rect, maskRect, srcRect, dstRect, maskDestRect;
104 boolean needFill;
91 105
92 /* TODO implement stippling. */ 106 /* TODO implement stippling. */
93 if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) 107 if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
94 return; 108 return;
95 109
96 alu = immutableGC.function; 110 alu = immutableGC.function;
111
112 /* A copy must be created or drawBitmap could end up overwriting
113 itself. */
114 srcBitmap = source.getBitmap ();
115
116 /* If srcBitmap is out of bounds, then adjust the source rectangle
117 to be within bounds. Note that tiling on windows with
118 backgrounds is unimplemented. */
119
120 if (src_x < 0)
121 {
122 width += src_x;
123 dest_x -= src_x;
124 src_x = 0;
125 }
126
127 if (src_y < 0)
128 {
129 height += src_y;
130 dest_y -= src_y;
131 src_y = 0;
132 }
133
134 if (src_x + width > srcBitmap.getWidth ())
135 width = srcBitmap.getWidth () - src_x;
136
137 if (src_y + height > srcBitmap.getHeight ())
138 height = srcBitmap.getHeight () - src_y;
139
97 rect = getRect (); 140 rect = getRect ();
98 bitmap = source.getBitmap ();
99 141
100 if (alu == EmacsGC.GC_COPY) 142 if (alu == EmacsGC.GC_COPY)
101 paint.setXfermode (null); 143 paint.setXfermode (null);
@@ -103,29 +145,76 @@ public class EmacsCopyArea implements EmacsPaintReq
103 paint.setXfermode (xorAlu); 145 paint.setXfermode (xorAlu);
104 146
105 if (immutableGC.clip_mask == null) 147 if (immutableGC.clip_mask == null)
106 canvas.drawBitmap (bitmap, new Rect (src_x, src_y, 148 {
107 src_x + width, 149 bitmap = Bitmap.createBitmap (srcBitmap,
108 src_y + height), 150 src_x, src_y, width,
109 rect, paint); 151 height);
152 canvas.drawBitmap (bitmap, null, rect, paint);
153 }
110 else 154 else
111 { 155 {
112 maskPaint = new Paint (); 156 /* Drawing with a clip mask involves calculating the
113 srcRect = new Rect (0, 0, rect.width (), 157 intersection of the clip mask with the dst rect, and
114 rect.height ()); 158 extrapolating the corresponding part of the src rect. */
115 maskBitmap 159 clipBitmap = immutableGC.clip_mask.bitmap;
116 = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, 160 dstRect = new Rect (dest_x, dest_y,
117 true); 161 dest_x + width,
118 162 dest_y + height);
119 if (maskBitmap == null) 163 maskRect = new Rect (immutableGC.clip_x_origin,
164 immutableGC.clip_y_origin,
165 (immutableGC.clip_x_origin
166 + clipBitmap.getWidth ()),
167 (immutableGC.clip_y_origin
168 + clipBitmap.getHeight ()));
169 clipBitmap = immutableGC.clip_mask.bitmap;
170
171 if (!maskRect.setIntersect (dstRect, maskRect))
172 /* There is no intersection between the clip mask and the
173 dest rect. */
120 return; 174 return;
121 175
122 maskPaint.setXfermode (srcInAlu); 176 /* Now figure out which part of the source corresponds to
177 maskRect and return it relative to srcBitmap. */
178 srcRect = new Rect (src_x, src_y, src_x + width,
179 src_y + height);
180 insetRectBy (srcRect, maskRect.left - dstRect.left,
181 maskRect.top - dstRect.top,
182 maskRect.right - dstRect.right,
183 maskRect.bottom - dstRect.bottom);
184
185 /* Finally, create a temporary bitmap that is the size of
186 maskRect. */
187
188 maskBitmap
189 = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
190 Bitmap.Config.ARGB_8888);
191
192 /* Draw the mask onto the maskBitmap. */
123 maskCanvas = new Canvas (maskBitmap); 193 maskCanvas = new Canvas (maskBitmap);
124 maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y, 194 maskRect.offset (-immutableGC.clip_x_origin,
125 src_x + width, 195 -immutableGC.clip_y_origin);
126 src_y + height), 196 maskCanvas.drawBitmap (immutableGC.clip_mask.bitmap,
127 srcRect, maskPaint); 197 maskRect, new Rect (0, 0,
128 canvas.drawBitmap (maskBitmap, srcRect, rect, paint); 198 maskRect.width (),
199 maskRect.height ()),
200 paint);
201 maskRect.offset (immutableGC.clip_x_origin,
202 immutableGC.clip_y_origin);
203
204 /* Set the transfer mode to SRC_IN to preserve only the parts
205 of the source that overlap with the mask. */
206 maskPaint = new Paint ();
207 maskPaint.setXfermode (srcInAlu);
208
209 /* Draw the source. */
210 maskDestRect = new Rect (0, 0, srcRect.width (),
211 srcRect.height ());
212 maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect,
213 maskPaint);
214
215 /* Finally, draw the mask bitmap to the destination. */
216 paint.setXfermode (overAlu);
217 canvas.drawBitmap (maskBitmap, null, maskRect, paint);
129 } 218 }
130 } 219 }
131} 220}
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java
index 462bf7c85b5..e3f28227146 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -57,7 +57,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
57 public Rect 57 public Rect
58 getRect () 58 getRect ()
59 { 59 {
60 return new Rect (x, y, x + width, y + height); 60 /* Canvas.drawRect actually behaves exactly like PolyRectangle wrt
61 to where the lines are placed, so extend the width and height
62 by 1 in the damage rectangle. */
63 return new Rect (x, y, x + width + 1, y + height + 1);
61 } 64 }
62 65
63 @Override 66 @Override
@@ -89,9 +92,10 @@ public class EmacsDrawRectangle implements EmacsPaintReq
89 return; 92 return;
90 93
91 alu = immutableGC.function; 94 alu = immutableGC.function;
92 rect = getRect (); 95 rect = new Rect (x, y, x + width, y + height);
93 96
94 paint.setStyle (Paint.Style.STROKE); 97 paint.setStyle (Paint.Style.STROKE);
98 paint.setStrokeWidth (1);
95 99
96 if (alu == EmacsGC.GC_COPY) 100 if (alu == EmacsGC.GC_COPY)
97 paint.setXfermode (null); 101 paint.setXfermode (null);
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java
index f419e71059d..9f40aa04c44 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -21,6 +21,8 @@ package org.gnu.emacs;
21 21
22import java.util.List; 22import java.util.List;
23 23
24import android.os.Build;
25
24public abstract class EmacsFontDriver 26public abstract class EmacsFontDriver
25{ 27{
26 /* Font weights. */ 28 /* Font weights. */
@@ -75,6 +77,7 @@ public abstract class EmacsFontDriver
75 public Integer size; 77 public Integer size;
76 public Integer spacing; 78 public Integer spacing;
77 public Integer avgwidth; 79 public Integer avgwidth;
80 public Integer dpi;
78 81
79 @Override 82 @Override
80 public String 83 public String
@@ -88,7 +91,8 @@ public abstract class EmacsFontDriver
88 + " weight: " + weight 91 + " weight: " + weight
89 + " slant: " + slant 92 + " slant: " + slant
90 + " spacing: " + spacing 93 + " spacing: " + spacing
91 + " avgwidth: " + avgwidth); 94 + " avgwidth: " + avgwidth
95 + " dpi: " + dpi);
92 } 96 }
93 }; 97 };
94 98
@@ -99,6 +103,17 @@ public abstract class EmacsFontDriver
99 public short width; 103 public short width;
100 public short ascent; 104 public short ascent;
101 public short descent; 105 public short descent;
106
107 @Override
108 public String
109 toString ()
110 {
111 return ("lbearing " + lbearing
112 + " rbearing " + rbearing
113 + " width " + width
114 + " ascent " + ascent
115 + " descent " + descent);
116 }
102 } 117 }
103 118
104 public class FontEntity extends FontSpec 119 public class FontEntity extends FontSpec
@@ -139,12 +154,19 @@ public abstract class EmacsFontDriver
139 public abstract FontObject openFont (FontEntity fontEntity, int pixelSize); 154 public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
140 public abstract int hasChar (FontSpec font, char charCode); 155 public abstract int hasChar (FontSpec font, char charCode);
141 public abstract void textExtents (FontObject font, int code[], 156 public abstract void textExtents (FontObject font, int code[],
142 FontMetrics fontMetrics[]); 157 FontMetrics fontMetrics);
143 public abstract int encodeChar (FontObject fontObject, char charCode); 158 public abstract int encodeChar (FontObject fontObject, char charCode);
159 public abstract int draw (FontObject fontObject, EmacsGC gc,
160 EmacsDrawable drawable, int[] chars,
161 int x, int y, int backgroundWidth,
162 boolean withBackground);
144 163
145 public static EmacsFontDriver 164 public static EmacsFontDriver
146 createFontDriver () 165 createFontDriver ()
147 { 166 {
167 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
168 return new EmacsSdk23FontDriver ();
169
148 return new EmacsSdk7FontDriver (); 170 return new EmacsSdk7FontDriver ();
149 } 171 }
150}; 172};
diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java
new file mode 100644
index 00000000000..dbdc99a3559
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java
@@ -0,0 +1,25 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22public class EmacsMultitaskActivity extends EmacsActivity
23{
24
25}
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 6550e6fa2a1..c80339031a8 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -38,10 +38,15 @@ public class EmacsNative
38 libDir must be the package's data storage location for native 38 libDir must be the package's data storage location for native
39 libraries. It is used as PATH. 39 libraries. It is used as PATH.
40 40
41 pixelDensityX and pixelDensityY are the DPI values that will be
42 used by Emacs.
43
41 emacsService must be the emacsService singleton. */ 44 emacsService must be the emacsService singleton. */
42 public static native void setEmacsParams (AssetManager assetManager, 45 public static native void setEmacsParams (AssetManager assetManager,
43 String filesDir, 46 String filesDir,
44 String libDir, 47 String libDir,
48 float pixelDensityX,
49 float pixelDensityY,
45 EmacsService emacsService); 50 EmacsService emacsService);
46 51
47 /* Initialize Emacs with the argument array ARGV. Each argument 52 /* Initialize Emacs with the argument array ARGV. Each argument
@@ -59,11 +64,20 @@ public class EmacsNative
59 64
60 /* Send an ANDROID_KEY_PRESS event. */ 65 /* Send an ANDROID_KEY_PRESS event. */
61 public static native void sendKeyPress (short window, long time, int state, 66 public static native void sendKeyPress (short window, long time, int state,
62 int keyCode); 67 int keyCode, int unicodeChar);
63 68
64 /* Send an ANDROID_KEY_RELEASE event. */ 69 /* Send an ANDROID_KEY_RELEASE event. */
65 public static native void sendKeyRelease (short window, long time, int state, 70 public static native void sendKeyRelease (short window, long time, int state,
66 int keyRelease); 71 int keyCode, int unicodeChar);
72
73 /* Send an ANDROID_FOCUS_IN event. */
74 public static native void sendFocusIn (short window, long time);
75
76 /* Send an ANDROID_FOCUS_OUT event. */
77 public static native void sendFocusOut (short window, long time);
78
79 /* Send an ANDROID_WINDOW_ACTION event. */
80 public static native void sendWindowAction (short window, int action);
67 81
68 static 82 static
69 { 83 {
diff --git a/java/org/gnu/emacs/EmacsPaintQueue.java b/java/org/gnu/emacs/EmacsPaintQueue.java
index 5af5868d3b9..f4840dbf5ae 100644
--- a/java/org/gnu/emacs/EmacsPaintQueue.java
+++ b/java/org/gnu/emacs/EmacsPaintQueue.java
@@ -47,7 +47,7 @@ public class EmacsPaintQueue
47 { 47 {
48 EmacsDrawable drawable, last; 48 EmacsDrawable drawable, last;
49 Canvas canvas; 49 Canvas canvas;
50 EmacsGC gc, lastGC; 50 EmacsGC gc;
51 int i; 51 int i;
52 Paint paint; 52 Paint paint;
53 Rect rect, offsetRect, copyRect; 53 Rect rect, offsetRect, copyRect;
@@ -60,45 +60,34 @@ public class EmacsPaintQueue
60 for (EmacsPaintReq req : paintOperations) 60 for (EmacsPaintReq req : paintOperations)
61 { 61 {
62 drawable = req.getDrawable (); 62 drawable = req.getDrawable ();
63
64 synchronized (req)
65 {
66 /* Ignore graphics requests for drawables that have been
67 destroyed. */
68 if (drawable.isDestroyed ())
69 continue;
70 }
71
72 canvas = drawable.lockCanvas (); 63 canvas = drawable.lockCanvas ();
73 64
74 if (canvas == null) 65 if (canvas == null)
75 /* No canvas is currently available. */ 66 /* No canvas is currently available. */
76 continue; 67 continue;
77 68
78 lastGC = gc;
79 gc = req.getGC (); 69 gc = req.getGC ();
80 rect = req.getRect (); 70 rect = req.getRect ();
81 71
72 drawable.damageRect (rect);
73
82 if (gc.clip_rects == null) 74 if (gc.clip_rects == null)
83 { 75 {
84 /* No clipping is applied. Just draw and continue. */ 76 /* No clipping is applied. Just draw and continue. */
85 canvas.save ();
86 req.paintTo (canvas, paint, gc); 77 req.paintTo (canvas, paint, gc);
87 canvas.restore ();
88 drawable.damageRect (rect);
89 continue; 78 continue;
90 } 79 }
91 80
92 if (gc.clip_rects != null && gc.clip_rects.length > 0) 81 if (gc.clip_rects != null && gc.clip_rects.length > 0)
93 { 82 {
94 canvas.save ();
95
96 if (gc.clip_rects.length == 1) 83 if (gc.clip_rects.length == 1)
97 { 84 {
98 /* There is only a single clip rect, which is simple 85 /* There is only a single clip rect, which is simple
99 enough. */ 86 enough. */
87 canvas.save ();
100 canvas.clipRect (gc.clip_rects[0]); 88 canvas.clipRect (gc.clip_rects[0]);
101 req.paintTo (canvas, paint, gc); 89 req.paintTo (canvas, paint, gc);
90 canvas.restore ();
102 } 91 }
103 else 92 else
104 { 93 {
@@ -122,9 +111,6 @@ public class EmacsPaintQueue
122 } 111 }
123 } 112 }
124 } 113 }
125
126 drawable.damageRect (rect);
127 canvas.restore ();
128 } 114 }
129 } 115 }
130 } 116 }
diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java
index ccd5f1e0043..897902c5b57 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -69,6 +69,35 @@ public class EmacsPixmap extends EmacsHandleObject
69 this.depth = depth; 69 this.depth = depth;
70 } 70 }
71 71
72 public
73 EmacsPixmap (short handle, int width, int height, int depth)
74 {
75 super (handle);
76
77 if (depth != 1 && depth != 24)
78 throw new IllegalArgumentException ("Invalid depth specified"
79 + " for pixmap: " + depth);
80
81 switch (depth)
82 {
83 case 1:
84 bitmap = Bitmap.createBitmap (width, height,
85 Bitmap.Config.ALPHA_8,
86 false);
87 break;
88
89 case 24:
90 bitmap = Bitmap.createBitmap (width, height,
91 Bitmap.Config.ARGB_8888,
92 false);
93 break;
94 }
95
96 this.width = width;
97 this.height = height;
98 this.depth = depth;
99 }
100
72 @Override 101 @Override
73 public Canvas 102 public Canvas
74 lockCanvas () 103 lockCanvas ()
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
new file mode 100644
index 00000000000..34e2b1803a2
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -0,0 +1,43 @@
1/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import android.graphics.Paint;
23
24public class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
25{
26 @Override
27 public int
28 hasChar (FontSpec font, char charCode)
29 {
30 Sdk7FontObject fontObject;
31 Paint paint;
32
33 if (font instanceof Sdk7FontObject)
34 {
35 fontObject = (Sdk7FontObject) font;
36 paint = fontObject.typeface.typefacePaint;
37 }
38 else
39 paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
40
41 return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
42 }
43};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 5a8cdbfc75b..437f38e62db 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -28,16 +28,19 @@ import java.util.List;
28import android.graphics.Paint; 28import android.graphics.Paint;
29import android.graphics.Rect; 29import android.graphics.Rect;
30import android.graphics.Typeface; 30import android.graphics.Typeface;
31import android.graphics.Canvas;
31 32
32import android.util.Log; 33import android.util.Log;
33 34
35import android.os.Build;
36
34public class EmacsSdk7FontDriver extends EmacsFontDriver 37public class EmacsSdk7FontDriver extends EmacsFontDriver
35{ 38{
36 private static final String TOFU_STRING = "\uDB3F\uDFFD"; 39 private static final String TOFU_STRING = "\uDB3F\uDFFD";
37 private static final String EM_STRING = "m"; 40 private static final String EM_STRING = "m";
38 private static final String TAG = "EmacsSdk7FontDriver"; 41 private static final String TAG = "EmacsSdk7FontDriver";
39 42
40 private class Sdk7Typeface 43 protected class Sdk7Typeface
41 { 44 {
42 /* The typeface and paint. */ 45 /* The typeface and paint. */
43 public Typeface typeface; 46 public Typeface typeface;
@@ -57,7 +60,10 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
57 width = UNSPECIFIED; 60 width = UNSPECIFIED;
58 spacing = PROPORTIONAL; 61 spacing = PROPORTIONAL;
59 62
63 this.typeface = typeface;
64
60 typefacePaint = new Paint (); 65 typefacePaint = new Paint ();
66 typefacePaint.setAntiAlias (true);
61 typefacePaint.setTypeface (typeface); 67 typefacePaint.setTypeface (typeface);
62 68
63 /* For the calls to measureText below. */ 69 /* For the calls to measureText below. */
@@ -160,7 +166,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
160 } 166 }
161 }; 167 };
162 168
163 private class Sdk7FontEntity extends FontEntity 169 protected class Sdk7FontEntity extends FontEntity
164 { 170 {
165 /* The typeface. */ 171 /* The typeface. */
166 public Sdk7Typeface typeface; 172 public Sdk7Typeface typeface;
@@ -177,19 +183,17 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
177 slant = typeface.slant; 183 slant = typeface.slant;
178 spacing = typeface.spacing; 184 spacing = typeface.spacing;
179 width = typeface.width; 185 width = typeface.width;
186 dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
180 187
181 this.typeface = typeface; 188 this.typeface = typeface;
182 } 189 }
183 }; 190 };
184 191
185 private class Sdk7FontObject extends FontObject 192 protected class Sdk7FontObject extends FontObject
186 { 193 {
187 /* The typeface. */ 194 /* The typeface. */
188 public Sdk7Typeface typeface; 195 public Sdk7Typeface typeface;
189 196
190 /* The text size. */
191 public int pixelSize;
192
193 public 197 public
194 Sdk7FontObject (Sdk7Typeface typeface, int pixelSize) 198 Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
195 { 199 {
@@ -205,6 +209,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
205 slant = typeface.slant; 209 slant = typeface.slant;
206 spacing = typeface.spacing; 210 spacing = typeface.spacing;
207 width = typeface.width; 211 width = typeface.width;
212 dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
208 213
209 /* Compute the ascent and descent. */ 214 /* Compute the ascent and descent. */
210 typeface.typefacePaint.setTextSize (pixelSize); 215 typeface.typefacePaint.setTextSize (pixelSize);
@@ -238,6 +243,93 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
238 } 243 }
239 }; 244 };
240 245
246 private class Sdk7DrawString implements EmacsPaintReq
247 {
248 private boolean drawBackground;
249 private Sdk7FontObject fontObject;
250 private char[] chars;
251 private EmacsGC immutableGC;
252 private EmacsDrawable drawable;
253 private Rect rect;
254 private int originX, originY;
255
256 public
257 Sdk7DrawString (Sdk7FontObject fontObject, char[] chars,
258 EmacsGC immutableGC, EmacsDrawable drawable,
259 boolean drawBackground, Rect rect,
260 int originX, int originY)
261 {
262 this.fontObject = fontObject;
263 this.chars = chars;
264 this.immutableGC = immutableGC;
265 this.drawable = drawable;
266 this.drawBackground = drawBackground;
267 this.rect = rect;
268 this.originX = originX;
269 this.originY = originY;
270 }
271
272 @Override
273 public EmacsDrawable
274 getDrawable ()
275 {
276 return drawable;
277 }
278
279 @Override
280 public EmacsGC
281 getGC ()
282 {
283 return immutableGC;
284 }
285
286 @Override
287 public void
288 paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
289 {
290 int scratch;
291
292 paint.setStyle (Paint.Style.FILL);
293
294 if (drawBackground)
295 {
296 paint.setColor (immutableGC.background | 0xff000000);
297 canvas.drawRect (rect, paint);
298 }
299
300 paint.setTextSize (fontObject.pixelSize);
301 paint.setColor (immutableGC.foreground | 0xff000000);
302 paint.setTypeface (fontObject.typeface.typeface);
303 paint.setAntiAlias (true);
304
305 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
306 /* Disable hinting as that leads to displayed text not
307 matching the computed metrics. */
308 paint.setHinting (Paint.HINTING_OFF);
309
310 canvas.drawText (chars, 0, chars.length, originX, originY, paint);
311 paint.setAntiAlias (false);
312 }
313
314 @Override
315 public Rect
316 getRect ()
317 {
318 Rect rect;
319
320 rect = new Rect ();
321
322 fontObject.typeface.typefacePaint.setTextSize (fontObject.pixelSize);
323 fontObject.typeface.typefacePaint.getTextBounds (chars, 0, chars.length,
324 rect);
325
326 /* Add the background rect to the damage as well. */
327 rect.union (this.rect);
328
329 return rect;
330 }
331 };
332
241 private String[] fontFamilyList; 333 private String[] fontFamilyList;
242 private Sdk7Typeface[] typefaceList; 334 private Sdk7Typeface[] typefaceList;
243 private Sdk7Typeface fallbackTypeface; 335 private Sdk7Typeface fallbackTypeface;
@@ -252,7 +344,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
252 systemFontsDirectory = new File ("/system/fonts"); 344 systemFontsDirectory = new File ("/system/fonts");
253 345
254 fontFamilyList = systemFontsDirectory.list (); 346 fontFamilyList = systemFontsDirectory.list ();
255 typefaceList = new Sdk7Typeface[fontFamilyList.length]; 347 typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
256 348
257 /* It would be nice to avoid opening each and every font upon 349 /* It would be nice to avoid opening each and every font upon
258 startup. But that doesn't seem to be possible on 350 startup. But that doesn't seem to be possible on
@@ -267,8 +359,18 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
267 typeface); 359 typeface);
268 } 360 }
269 361
362 /* Initialize the default monospace and serif typefaces. */
270 fallbackTypeface = new Sdk7Typeface ("monospace", 363 fallbackTypeface = new Sdk7Typeface ("monospace",
271 Typeface.MONOSPACE); 364 Typeface.MONOSPACE);
365 typefaceList[fontFamilyList.length] = fallbackTypeface;
366
367 fallbackTypeface = new Sdk7Typeface ("Monospace",
368 Typeface.MONOSPACE);
369 typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
370
371 fallbackTypeface = new Sdk7Typeface ("Sans Serif",
372 Typeface.DEFAULT);
373 typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
272 } 374 }
273 375
274 private boolean 376 private boolean
@@ -278,11 +380,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
278 && !fontSpec.family.equals (typeface.familyName)) 380 && !fontSpec.family.equals (typeface.familyName))
279 return false; 381 return false;
280 382
281
282 if (fontSpec.adstyle != null
283 && !fontSpec.adstyle.isEmpty ())
284 /* return false; */;
285
286 if (fontSpec.slant != null 383 if (fontSpec.slant != null
287 && !fontSpec.weight.equals (typeface.weight)) 384 && !fontSpec.weight.equals (typeface.weight))
288 return false; 385 return false;
@@ -393,7 +490,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
393 paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (), 490 paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
394 rect1); 491 rect1);
395 paint.getTextBounds ("" + charCode, 0, 1, rect2); 492 paint.getTextBounds ("" + charCode, 0, 1, rect2);
396 return rect1.equals (rect2) ? 1 : 0; 493 return rect1.equals (rect2) ? 0 : 1;
397 } 494 }
398 495
399 private void 496 private void
@@ -434,21 +531,47 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
434 531
435 @Override 532 @Override
436 public void 533 public void
437 textExtents (FontObject font, int code[], FontMetrics fontMetrics[]) 534 textExtents (FontObject font, int code[], FontMetrics fontMetrics)
438 { 535 {
439 int i; 536 int i;
440 Paint paintCache; 537 Paint paintCache;
441 Rect boundsCache; 538 Rect boundsCache;
442 Sdk7FontObject fontObject; 539 Sdk7FontObject fontObject;
540 char[] text;
541 float width;
443 542
444 fontObject = (Sdk7FontObject) font; 543 fontObject = (Sdk7FontObject) font;
445 paintCache = fontObject.typeface.typefacePaint; 544 paintCache = fontObject.typeface.typefacePaint;
446 paintCache.setTextSize (fontObject.pixelSize); 545 paintCache.setTextSize (fontObject.pixelSize);
447 boundsCache = new Rect (); 546 boundsCache = new Rect ();
448 547
449 for (i = 0; i < code.length; ++i) 548 if (code.length == 0)
450 textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i], 549 {
550 fontMetrics.lbearing = 0;
551 fontMetrics.rbearing = 0;
552 fontMetrics.ascent = 0;
553 fontMetrics.descent = 0;
554 fontMetrics.width = 0;
555 }
556 else if (code.length == 1)
557 textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
451 paintCache, boundsCache); 558 paintCache, boundsCache);
559 else
560 {
561 text = new char[code.length];
562
563 for (i = 0; i < code.length; ++i)
564 text[i] = (char) code[i];
565
566 paintCache.getTextBounds (text, 0, 1, boundsCache);
567 width = paintCache.measureText (text, 0, code.length);
568
569 fontMetrics.lbearing = (short) boundsCache.left;
570 fontMetrics.rbearing = (short) boundsCache.right;
571 fontMetrics.ascent = (short) -boundsCache.top;
572 fontMetrics.descent = (short) boundsCache.bottom;
573 fontMetrics.width = (short) Math.round (width);
574 }
452 } 575 }
453 576
454 @Override 577 @Override
@@ -457,4 +580,37 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
457 { 580 {
458 return charCode; 581 return charCode;
459 } 582 }
583
584 @Override
585 public int
586 draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
587 int[] chars, int x, int y, int backgroundWidth,
588 boolean withBackground)
589 {
590 Rect backgroundRect;
591 Sdk7FontObject sdk7FontObject;
592 Sdk7DrawString op;
593 char[] charsArray;
594 int i;
595
596 sdk7FontObject = (Sdk7FontObject) fontObject;
597 charsArray = new char[chars.length];
598
599 for (i = 0; i < chars.length; ++i)
600 charsArray[i] = (char) chars[i];
601
602 backgroundRect = new Rect ();
603 backgroundRect.top = y - sdk7FontObject.ascent;
604 backgroundRect.left = x;
605 backgroundRect.right = x + backgroundWidth;
606 backgroundRect.bottom = y + sdk7FontObject.descent;
607
608 op = new Sdk7DrawString (sdk7FontObject, charsArray,
609 gc.immutableGC (), drawable,
610 withBackground,
611 backgroundRect, x, y);
612
613 EmacsService.SERVICE.appendPaintOperation (op);
614 return 1;
615 }
460}; 616};
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index 311226e6f7e..41a45b0bd85 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -28,6 +28,8 @@ import android.graphics.Canvas;
28import android.graphics.Bitmap; 28import android.graphics.Bitmap;
29import android.graphics.Point; 29import android.graphics.Point;
30 30
31import android.view.View;
32
31import android.annotation.TargetApi; 33import android.annotation.TargetApi;
32import android.app.Service; 34import android.app.Service;
33import android.content.Context; 35import android.content.Context;
@@ -37,7 +39,9 @@ import android.os.Build;
37import android.os.Looper; 39import android.os.Looper;
38import android.os.IBinder; 40import android.os.IBinder;
39import android.os.Handler; 41import android.os.Handler;
42
40import android.util.Log; 43import android.util.Log;
44import android.util.DisplayMetrics;
41 45
42class Holder<T> 46class Holder<T>
43{ 47{
@@ -57,14 +61,8 @@ public class EmacsService extends Service
57 private Handler handler; 61 private Handler handler;
58 private EmacsPaintQueue paintQueue; 62 private EmacsPaintQueue paintQueue;
59 63
60 /* List of all EmacsWindows that are available to attach to an 64 /* Display metrics used by font backends. */
61 activity. */ 65 public DisplayMetrics metrics;
62 public static List<EmacsWindow> availableChildren;
63
64 static
65 {
66 availableChildren = new ArrayList<EmacsWindow> ();
67 };
68 66
69 @Override 67 @Override
70 public int 68 public int
@@ -88,7 +86,7 @@ public class EmacsService extends Service
88 Context context; 86 Context context;
89 87
90 context = getApplicationContext (); 88 context = getApplicationContext ();
91 apiLevel = android.os.Build.VERSION.SDK_INT; 89 apiLevel = Build.VERSION.SDK_INT;
92 90
93 if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) 91 if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
94 return context.getApplicationInfo().nativeLibraryDir; 92 return context.getApplicationInfo().nativeLibraryDir;
@@ -105,11 +103,16 @@ public class EmacsService extends Service
105 AssetManager manager; 103 AssetManager manager;
106 Context app_context; 104 Context app_context;
107 String filesDir, libDir; 105 String filesDir, libDir;
106 double pixelDensityX;
107 double pixelDensityY;
108 108
109 SERVICE = this; 109 SERVICE = this;
110 handler = new Handler (Looper.getMainLooper ()); 110 handler = new Handler (Looper.getMainLooper ());
111 manager = getAssets (); 111 manager = getAssets ();
112 app_context = getApplicationContext (); 112 app_context = getApplicationContext ();
113 metrics = getResources ().getDisplayMetrics ();
114 pixelDensityX = metrics.xdpi;
115 pixelDensityY = metrics.ydpi;
113 116
114 try 117 try
115 { 118 {
@@ -122,6 +125,8 @@ public class EmacsService extends Service
122 + " and libDir = " + libDir); 125 + " and libDir = " + libDir);
123 126
124 EmacsNative.setEmacsParams (manager, filesDir, libDir, 127 EmacsNative.setEmacsParams (manager, filesDir, libDir,
128 (float) pixelDensityX,
129 (float) pixelDensityY,
125 this); 130 this);
126 131
127 /* Start the thread that runs Emacs. */ 132 /* Start the thread that runs Emacs. */
@@ -147,7 +152,8 @@ public class EmacsService extends Service
147 } 152 }
148 153
149 EmacsView 154 EmacsView
150 getEmacsView (final EmacsWindow window) 155 getEmacsView (final EmacsWindow window, final int visibility,
156 final boolean isFocusedByDefault)
151 { 157 {
152 Runnable runnable; 158 Runnable runnable;
153 final Holder<EmacsView> view; 159 final Holder<EmacsView> view;
@@ -161,6 +167,8 @@ public class EmacsService extends Service
161 synchronized (this) 167 synchronized (this)
162 { 168 {
163 view.thing = new EmacsView (window); 169 view.thing = new EmacsView (window);
170 view.thing.setVisibility (visibility);
171 view.thing.setFocusedByDefault (isFocusedByDefault);
164 notify (); 172 notify ();
165 } 173 }
166 } 174 }
@@ -183,48 +191,6 @@ public class EmacsService extends Service
183 return view.thing; 191 return view.thing;
184 } 192 }
185 193
186 /* Notice that a child of the root window named WINDOW is now
187 available for attachment to a specific activity. */
188
189 public void
190 noticeAvailableChild (final EmacsWindow window)
191 {
192 Log.d (TAG, "A new child is available: " + window);
193
194 handler.post (new Runnable () {
195 public void
196 run ()
197 {
198 for (EmacsActivity activity
199 : EmacsActivity.availableActivities)
200 {
201 /* TODO: check if the activity matches. */
202 activity.attachChild (window);
203 break;
204 }
205
206 /* Nope, wait for an activity to become available. */
207 availableChildren.add (window);
208 }
209 });
210 }
211
212 /* Notice that a child of the root window named WINDOW has been
213 destroyed. */
214
215 public void
216 noticeChildDestroyed (final EmacsWindow child)
217 {
218 handler.post (new Runnable () {
219 @Override
220 public void
221 run ()
222 {
223 availableChildren.remove (child);
224 }
225 });
226 }
227
228 /* X drawing operations. These are quite primitive operations. The 194 /* X drawing operations. These are quite primitive operations. The
229 drawing queue is kept on the Emacs thread, but is periodically 195 drawing queue is kept on the Emacs thread, but is periodically
230 flushed to the application thread, upon buffers swaps and once it 196 flushed to the application thread, upon buffers swaps and once it
@@ -311,11 +277,6 @@ public class EmacsService extends Service
311 277
312 ensurePaintQueue (); 278 ensurePaintQueue ();
313 279
314 if (gc.clip_rects != null && gc.clip_rects.length >= 1)
315 android.util.Log.d ("drawRectangle",
316 gc.clip_rects[0].toString ()
317 + " " + gc.toString ());
318
319 req = new EmacsDrawRectangle (drawable, x, y, 280 req = new EmacsDrawRectangle (drawable, x, y,
320 width, height, 281 width, height,
321 gc.immutableGC ()); 282 gc.immutableGC ());
@@ -381,4 +342,12 @@ public class EmacsService extends Service
381 { 342 {
382 window.clearArea (x, y, width, height); 343 window.clearArea (x, y, width, height);
383 } 344 }
345
346 public void
347 appendPaintOperation (EmacsPaintReq op)
348 {
349 ensurePaintQueue ();
350 paintQueue.appendPaintOperation (op);
351 checkFlush ();
352 }
384}; 353};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java
index 194f6ad37a3..b8b828e4820 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -22,6 +22,8 @@ package org.gnu.emacs;
22import android.view.SurfaceView; 22import android.view.SurfaceView;
23import android.view.SurfaceHolder; 23import android.view.SurfaceHolder;
24 24
25import android.os.Build;
26
25import android.graphics.Canvas; 27import android.graphics.Canvas;
26import android.graphics.Rect; 28import android.graphics.Rect;
27 29
@@ -40,7 +42,9 @@ public class EmacsSurfaceView extends SurfaceView
40 surfaceChanged (SurfaceHolder holder, int format, 42 surfaceChanged (SurfaceHolder holder, int format,
41 int width, int height) 43 int width, int height)
42 { 44 {
43 45 /* Force a buffer swap now to get the contents of the Emacs
46 view on screen. */
47 view.swapBuffers (true);
44 } 48 }
45 49
46 @Override 50 @Override
@@ -51,7 +55,7 @@ public class EmacsSurfaceView extends SurfaceView
51 55
52 /* Force a buffer swap now to get the contents of the Emacs 56 /* Force a buffer swap now to get the contents of the Emacs
53 view on screen. */ 57 view on screen. */
54 view.swapBuffers (); 58 view.swapBuffers (true);
55 } 59 }
56 60
57 @Override 61 @Override
@@ -72,12 +76,37 @@ public class EmacsSurfaceView extends SurfaceView
72 public Canvas 76 public Canvas
73 lockCanvas (Rect damage) 77 lockCanvas (Rect damage)
74 { 78 {
75 return getHolder ().lockCanvas (damage); 79 SurfaceHolder holder;
80
81 holder = getHolder ();
82
83 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
84 {
85 damage.setEmpty ();
86 return holder.lockHardwareCanvas ();
87 }
88
89 return holder.lockCanvas (damage);
90 }
91
92 /* This method is only used during debugging when it seems damage
93 isn't working correctly. */
94
95 public Canvas
96 lockCanvas ()
97 {
98 SurfaceHolder holder;
99
100 holder = getHolder ();
101 return holder.lockCanvas ();
76 } 102 }
77 103
78 public void 104 public void
79 unlockCanvasAndPost (Canvas canvas) 105 unlockCanvasAndPost (Canvas canvas)
80 { 106 {
81 getHolder ().unlockCanvasAndPost (canvas); 107 SurfaceHolder holder;
108
109 holder = getHolder ();
110 holder.unlockCanvasAndPost (canvas);
82 } 111 }
83}; 112};
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index 237946d6366..7b48eaf0aa6 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -19,17 +19,20 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19 19
20package org.gnu.emacs; 20package org.gnu.emacs;
21 21
22import android.content.res.ColorStateList;
23
22import android.view.View; 24import android.view.View;
23import android.view.KeyEvent; 25import android.view.KeyEvent;
24import android.view.ViewGroup; 26import android.view.ViewGroup;
27
25import android.graphics.Bitmap; 28import android.graphics.Bitmap;
26import android.graphics.Canvas; 29import android.graphics.Canvas;
27import android.graphics.Rect; 30import android.graphics.Rect;
28import android.graphics.Region; 31import android.graphics.Region;
29import android.graphics.Paint; 32import android.graphics.Paint;
30import android.util.Log;
31 33
32import android.os.Build; 34import android.os.Build;
35import android.util.Log;
33 36
34/* This is an Android view which has a back and front buffer. When 37/* This is an Android view which has a back and front buffer. When
35 swapBuffers is called, the back buffer is swapped to the front 38 swapBuffers is called, the back buffer is swapped to the front
@@ -70,10 +73,11 @@ public class EmacsView extends ViewGroup
70 this.damageRegion = new Region (); 73 this.damageRegion = new Region ();
71 this.paint = new Paint (); 74 this.paint = new Paint ();
72 75
76 setFocusable (true);
77 setFocusableInTouchMode (true);
78
73 /* Create the surface view. */ 79 /* Create the surface view. */
74 this.surfaceView = new EmacsSurfaceView (this); 80 this.surfaceView = new EmacsSurfaceView (this);
75
76 setFocusable (FOCUSABLE);
77 addView (this.surfaceView); 81 addView (this.surfaceView);
78 } 82 }
79 83
@@ -162,7 +166,7 @@ public class EmacsView extends ViewGroup
162 } 166 }
163 167
164 public void 168 public void
165 swapBuffers () 169 swapBuffers (boolean force)
166 { 170 {
167 Bitmap back; 171 Bitmap back;
168 Canvas canvas; 172 Canvas canvas;
@@ -185,14 +189,25 @@ public class EmacsView extends ViewGroup
185 if (canvas == null) 189 if (canvas == null)
186 return; 190 return;
187 191
188 /* Copy from the back buffer to the canvas. */ 192 /* Copy from the back buffer to the canvas. If damageRect was
189 canvas.drawBitmap (bitmap, damageRect, damageRect, paint); 193 made empty, then draw the entire back buffer. */
194
195 if (damageRect.isEmpty ())
196 canvas.drawBitmap (bitmap, 0f, 0f, paint);
197 else
198 canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
190 199
191 /* Unlock the canvas and clear the damage. */ 200 /* Unlock the canvas and clear the damage. */
192 surfaceView.unlockCanvasAndPost (canvas); 201 surfaceView.unlockCanvasAndPost (canvas);
193 damageRegion.setEmpty (); 202 damageRegion.setEmpty ();
194 } 203 }
195 204
205 public void
206 swapBuffers ()
207 {
208 swapBuffers (false);
209 }
210
196 @Override 211 @Override
197 public boolean 212 public boolean
198 onKeyDown (int keyCode, KeyEvent event) 213 onKeyDown (int keyCode, KeyEvent event)
@@ -203,9 +218,27 @@ public class EmacsView extends ViewGroup
203 218
204 @Override 219 @Override
205 public boolean 220 public boolean
221 onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
222 {
223 window.onKeyDown (keyCode, event);
224 return true;
225 }
226
227 @Override
228 public boolean
206 onKeyUp (int keyCode, KeyEvent event) 229 onKeyUp (int keyCode, KeyEvent event)
207 { 230 {
208 window.onKeyUp (keyCode, event); 231 window.onKeyUp (keyCode, event);
209 return true; 232 return true;
210 } 233 }
234
235 @Override
236 public void
237 onFocusChanged (boolean gainFocus, int direction,
238 Rect previouslyFocusedRect)
239 {
240 window.onFocusChanged (gainFocus);
241 super.onFocusChanged (gainFocus, direction,
242 previouslyFocusedRect);
243 }
211}; 244};
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index 28db04a261d..26e788a20a8 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -32,6 +32,8 @@ import android.view.View;
32import android.view.ViewGroup; 32import android.view.ViewGroup;
33import android.view.KeyEvent; 33import android.view.KeyEvent;
34 34
35import android.content.Intent;
36
35/* This defines a window, which is a handle. Windows represent a 37/* This defines a window, which is a handle. Windows represent a
36 rectangular subset of the screen with their own contents. 38 rectangular subset of the screen with their own contents.
37 39
@@ -57,10 +59,10 @@ public class EmacsWindow extends EmacsHandleObject
57 59
58 /* List of all children in stacking order. This must be kept 60 /* List of all children in stacking order. This must be kept
59 consistent! */ 61 consistent! */
60 private ArrayList<EmacsWindow> children; 62 public ArrayList<EmacsWindow> children;
61 63
62 /* The EmacsActivity currently attached, if it exists. */ 64 /* The window consumer currently attached, if it exists. */
63 private EmacsActivity attached; 65 private EmacsWindowAttachmentManager.WindowConsumer attached;
64 66
65 /* The window background scratch GC. foreground is always the 67 /* The window background scratch GC. foreground is always the
66 window background. */ 68 window background. */
@@ -74,35 +76,44 @@ public class EmacsWindow extends EmacsHandleObject
74 76
75 rect = new Rect (x, y, x + width, y + height); 77 rect = new Rect (x, y, x + width, y + height);
76 78
77 /* Create the view from the context's UI thread. */ 79 /* Create the view from the context's UI thread. The window is
78 view = EmacsService.SERVICE.getEmacsView (this); 80 unmapped, so the view is GONE. */
81 view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
82 parent == null);
79 this.parent = parent; 83 this.parent = parent;
80 children = new ArrayList<EmacsWindow> ();
81 84
82 /* The window is unmapped by default. */ 85 /* Create the list of children. */
83 view.setVisibility (View.GONE); 86 children = new ArrayList<EmacsWindow> ();
84 87
85 /* If parent is the root window, notice that there are new 88 if (parent != null)
86 children available for interested activites to pick up. */
87 if (parent == null)
88 EmacsService.SERVICE.noticeAvailableChild (this);
89 else
90 { 89 {
91 /* Otherwise, directly add this window as a child of that 90 parent.children.add (this);
92 window's view. */ 91 parent.view.post (new Runnable () {
93 synchronized (parent) 92 @Override
93 public void
94 run ()
95 {
96 parent.view.addView (view);
97 }
98 });
99 }
100 else
101 EmacsService.SERVICE.runOnUiThread (new Runnable () {
102 @Override
103 public void
104 run ()
94 { 105 {
95 parent.children.add (this); 106 EmacsWindowAttachmentManager manager;
96 parent.view.post (new Runnable () { 107
97 @Override 108 manager = EmacsWindowAttachmentManager.MANAGER;
98 public void 109
99 run () 110 /* If parent is the root window, notice that there are new
100 { 111 children available for interested activites to pick
101 parent.view.addView (view); 112 up. */
102 } 113
103 }); 114 manager.registerWindow (EmacsWindow.this);
104 } 115 }
105 } 116 });
106 117
107 scratchGC = new EmacsGC ((short) 0); 118 scratchGC = new EmacsGC ((short) 0);
108 } 119 }
@@ -129,28 +140,35 @@ public class EmacsWindow extends EmacsHandleObject
129 public void 140 public void
130 destroyHandle () throws IllegalStateException 141 destroyHandle () throws IllegalStateException
131 { 142 {
132 synchronized (this) 143 if (parent != null)
133 { 144 parent.children.remove (this);
134 if (!children.isEmpty ()) 145
135 throw new IllegalStateException ("Trying to destroy window with " 146 EmacsActivity.invalidateFocus ();
136 + "children!");
137 }
138 147
139 /* Notice that the child has been destroyed. */ 148 if (!children.isEmpty ())
140 EmacsService.SERVICE.noticeChildDestroyed (this); 149 throw new IllegalStateException ("Trying to destroy window with "
150 + "children!");
141 151
142 /* Remove the view from its parent and make it invisible. */ 152 /* Remove the view from its parent and make it invisible. */
143 view.post (new Runnable () { 153 view.post (new Runnable () {
144 public void 154 public void
145 run () 155 run ()
146 { 156 {
157 View parent;
158 EmacsWindowAttachmentManager manager;
159
160 if (EmacsActivity.focusedWindow == EmacsWindow.this)
161 EmacsActivity.focusedWindow = null;
162
163 manager = EmacsWindowAttachmentManager.MANAGER;
147 view.setVisibility (View.GONE); 164 view.setVisibility (View.GONE);
148 165
149 if (view.getParent () != null) 166 parent = (View) view.getParent ();
150 ((ViewGroup) view.getParent ()).removeView (view);
151 167
152 if (attached != null) 168 if (parent != null && attached == null)
153 attached.makeAvailable (); 169 ((ViewGroup) parent).removeView (view);
170
171 manager.detachWindow (EmacsWindow.this);
154 } 172 }
155 }); 173 });
156 174
@@ -158,12 +176,15 @@ public class EmacsWindow extends EmacsHandleObject
158 } 176 }
159 177
160 public void 178 public void
161 setActivity (EmacsActivity activity) 179 setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
162 { 180 {
163 synchronized (this) 181 attached = consumer;
164 { 182 }
165 activity = activity; 183
166 } 184 public EmacsWindowAttachmentManager.WindowConsumer
185 getAttachedConsumer ()
186 {
187 return attached;
167 } 188 }
168 189
169 public void 190 public void
@@ -233,7 +254,10 @@ public class EmacsWindow extends EmacsHandleObject
233 public void 254 public void
234 run () 255 run ()
235 { 256 {
257
236 view.setVisibility (View.VISIBLE); 258 view.setVisibility (View.VISIBLE);
259 /* Eventually this should check no-focus-on-map. */
260 view.requestFocus ();
237 } 261 }
238 }); 262 });
239 } 263 }
@@ -319,18 +343,47 @@ public class EmacsWindow extends EmacsHandleObject
319 public void 343 public void
320 onKeyDown (int keyCode, KeyEvent event) 344 onKeyDown (int keyCode, KeyEvent event)
321 { 345 {
346 int state;
347
348 state = event.getModifiers ();
349 state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
350
322 EmacsNative.sendKeyPress (this.handle, 351 EmacsNative.sendKeyPress (this.handle,
323 event.getEventTime (), 352 event.getEventTime (),
324 event.getModifiers (), 353 event.getModifiers (),
325 keyCode); 354 keyCode,
355 /* Ignore meta-state understood by Emacs
356 for now, or Ctrl+C will not be
357 recognized as an ASCII key press
358 event. */
359 event.getUnicodeChar (state));
326 } 360 }
327 361
328 public void 362 public void
329 onKeyUp (int keyCode, KeyEvent event) 363 onKeyUp (int keyCode, KeyEvent event)
330 { 364 {
365 int state;
366
367 state = event.getModifiers ();
368 state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
369
331 EmacsNative.sendKeyRelease (this.handle, 370 EmacsNative.sendKeyRelease (this.handle,
332 event.getEventTime (), 371 event.getEventTime (),
333 event.getModifiers (), 372 event.getModifiers (),
334 keyCode); 373 keyCode,
374 event.getUnicodeChar (state));
375 }
376
377 public void
378 onFocusChanged (boolean gainFocus)
379 {
380 EmacsActivity.invalidateFocus ();
381 }
382
383 public void
384 onActivityDetached ()
385 {
386 /* Destroy the associated frame when the activity is detached. */
387 EmacsNative.sendWindowAction (this.handle, 0);
335 } 388 }
336}; 389};
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
new file mode 100644
index 00000000000..34be2ab8789
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -0,0 +1,166 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import java.util.LinkedList;
23import java.util.List;
24
25import android.content.Intent;
26import android.util.Log;
27
28/* Code to paper over the differences in lifecycles between
29 "activities" and windows. There are four interfaces to an instance
30 of this class:
31
32 registerWindowConsumer (WindowConsumer)
33 registerWindow (EmacsWindow)
34 removeWindowConsumer (WindowConsumer)
35 removeWindow (EmacsWindow)
36
37 A WindowConsumer is expected to allow an EmacsWindow to be attached
38 to it, and be created or destroyed.
39
40 Every time a window is created, registerWindow checks the list of
41 window consumers. If a consumer exists and does not currently have
42 a window of its own attached, it gets the new window. Otherwise,
43 the window attachment manager starts a new consumer.
44
45 Every time a consumer is registered, registerWindowConsumer checks
46 the list of available windows. If a window exists and is not
47 currently attached to a consumer, then the consumer gets it.
48
49 Finally, every time a window is removed, the consumer is
50 destroyed. */
51
52public class EmacsWindowAttachmentManager
53{
54 public static EmacsWindowAttachmentManager MANAGER;
55 private final static String TAG = "EmacsWindowAttachmentManager";
56
57 static
58 {
59 MANAGER = new EmacsWindowAttachmentManager ();
60 };
61
62 public interface WindowConsumer
63 {
64 public void attachWindow (EmacsWindow window);
65 public EmacsWindow getAttachedWindow ();
66 public void detachWindow ();
67 public void destroy ();
68 };
69
70 private List<WindowConsumer> consumers;
71 private List<EmacsWindow> windows;
72
73 public
74 EmacsWindowAttachmentManager ()
75 {
76 consumers = new LinkedList<WindowConsumer> ();
77 windows = new LinkedList<EmacsWindow> ();
78 }
79
80 public void
81 registerWindowConsumer (WindowConsumer consumer)
82 {
83 Log.d (TAG, "registerWindowConsumer " + consumer);
84
85 consumers.add (consumer);
86
87 for (EmacsWindow window : windows)
88 {
89 if (window.getAttachedConsumer () == null)
90 {
91 Log.d (TAG, "registerWindowConsumer: attaching " + window);
92 consumer.attachWindow (window);
93 return;
94 }
95 }
96
97 Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0");
98 EmacsNative.sendWindowAction ((short) 0, 0);
99 }
100
101 public void
102 registerWindow (EmacsWindow window)
103 {
104 Intent intent;
105
106 Log.d (TAG, "registerWindow " + window);
107 windows.add (window);
108
109 for (WindowConsumer consumer : consumers)
110 {
111 if (consumer.getAttachedWindow () == null)
112 {
113 Log.d (TAG, "registerWindow: attaching " + consumer);
114 consumer.attachWindow (window);
115 return;
116 }
117 }
118
119 intent = new Intent (EmacsService.SERVICE,
120 EmacsMultitaskActivity.class);
121 intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
122 | Intent.FLAG_ACTIVITY_NEW_TASK
123 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
124 EmacsService.SERVICE.startActivity (intent);
125 Log.d (TAG, "registerWindow: startActivity");
126 }
127
128 public void
129 removeWindowConsumer (WindowConsumer consumer)
130 {
131 EmacsWindow window;
132
133 Log.d (TAG, "removeWindowConsumer " + consumer);
134
135 window = consumer.getAttachedWindow ();
136
137 if (window != null)
138 {
139 Log.d (TAG, "removeWindowConsumer: detaching " + window);
140
141 consumer.detachWindow ();
142 window.onActivityDetached ();
143 }
144
145 Log.d (TAG, "removeWindowConsumer: removing " + consumer);
146 consumers.remove (consumer);
147 }
148
149 public void
150 detachWindow (EmacsWindow window)
151 {
152 WindowConsumer consumer;
153
154 Log.d (TAG, "detachWindow " + window);
155
156 if (window.getAttachedConsumer () != null)
157 {
158 consumer = window.getAttachedConsumer ();
159
160 Log.d (TAG, "detachWindow: removing" + consumer);
161
162 consumers.remove (consumer);
163 consumer.destroy ();
164 }
165 }
166};