diff options
Diffstat (limited to 'java')
| -rw-r--r-- | java/Makefile.in | 2 | ||||
| -rwxr-xr-x | java/debug.sh | 110 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsActivity.java | 2 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsContextMenu.java | 213 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsDrawRectangle.java | 2 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsGC.java | 1 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 43 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsView.java | 47 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsWindow.java | 161 |
9 files changed, 523 insertions, 58 deletions
diff --git a/java/Makefile.in b/java/Makefile.in index 05e61dede89..c539fb0f1fb 100644 --- a/java/Makefile.in +++ b/java/Makefile.in | |||
| @@ -168,4 +168,4 @@ clean: | |||
| 168 | rm -rf install-temp | 168 | rm -rf install-temp |
| 169 | find . -name '*.class' -delete | 169 | find . -name '*.class' -delete |
| 170 | 170 | ||
| 171 | maintainer-clean: clean | 171 | maintainer-clean distclean bootstrap-clean: clean |
diff --git a/java/debug.sh b/java/debug.sh index 3e3e3d9c281..aa80aeeebcd 100755 --- a/java/debug.sh +++ b/java/debug.sh | |||
| @@ -31,6 +31,7 @@ gdb_port=5039 | |||
| 31 | jdb_port=64013 | 31 | jdb_port=64013 |
| 32 | jdb=no | 32 | jdb=no |
| 33 | attach_existing=no | 33 | attach_existing=no |
| 34 | gdbserver= | ||
| 34 | 35 | ||
| 35 | while [ $# -gt 0 ]; do | 36 | while [ $# -gt 0 ]; do |
| 36 | case "$1" in | 37 | case "$1" in |
| @@ -41,6 +42,7 @@ while [ $# -gt 0 ]; do | |||
| 41 | echo "You must specify an argument to --device" | 42 | echo "You must specify an argument to --device" |
| 42 | exit 1 | 43 | exit 1 |
| 43 | fi | 44 | fi |
| 45 | shift | ||
| 44 | ;; | 46 | ;; |
| 45 | "--help" ) | 47 | "--help" ) |
| 46 | echo "Usage: $progname [options] -- [gdb options]" | 48 | echo "Usage: $progname [options] -- [gdb options]" |
| @@ -50,6 +52,7 @@ while [ $# -gt 0 ]; do | |||
| 50 | echo " --jdb-port PORT run the JDB server on a specific port" | 52 | echo " --jdb-port PORT run the JDB server on a specific port" |
| 51 | echo " --jdb run JDB instead of GDB" | 53 | echo " --jdb run JDB instead of GDB" |
| 52 | echo " --attach-existing attach to an existing process" | 54 | echo " --attach-existing attach to an existing process" |
| 55 | echo " --gdbserver BINARY upload and use the specified gdbserver binary" | ||
| 53 | echo " --help print this message" | 56 | echo " --help print this message" |
| 54 | echo "" | 57 | echo "" |
| 55 | echo "Available devices:" | 58 | echo "Available devices:" |
| @@ -62,9 +65,18 @@ while [ $# -gt 0 ]; do | |||
| 62 | "--jdb" ) | 65 | "--jdb" ) |
| 63 | jdb=yes | 66 | jdb=yes |
| 64 | ;; | 67 | ;; |
| 68 | "--gdbserver" ) | ||
| 69 | shift | ||
| 70 | gdbserver=$1 | ||
| 71 | ;; | ||
| 65 | "--port" ) | 72 | "--port" ) |
| 73 | shift | ||
| 66 | gdb_port=$1 | 74 | gdb_port=$1 |
| 67 | ;; | 75 | ;; |
| 76 | "--jdb-port" ) | ||
| 77 | shift | ||
| 78 | jdb_port=$1 | ||
| 79 | ;; | ||
| 68 | "--attach-existing" ) | 80 | "--attach-existing" ) |
| 69 | attach_existing=yes | 81 | attach_existing=yes |
| 70 | ;; | 82 | ;; |
| @@ -170,46 +182,71 @@ elif [ -z $package_pids ]; then | |||
| 170 | exit 1 | 182 | exit 1 |
| 171 | fi | 183 | fi |
| 172 | 184 | ||
| 173 | # Start JDB to make the wait dialog disappear. | 185 | # This isn't necessary when attaching gdb to an existing process. |
| 174 | echo "Attaching JDB to unblock the application." | 186 | if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then |
| 175 | adb -s $device forward --remove-all | 187 | # Start JDB to make the wait dialog disappear. |
| 176 | adb -s $device forward "tcp:$jdb_port" "jdwp:$pid" | 188 | echo "Attaching JDB to unblock the application." |
| 189 | adb -s $device forward --remove-all | ||
| 190 | adb -s $device forward "tcp:$jdb_port" "jdwp:$pid" | ||
| 177 | 191 | ||
| 178 | if [ ! $? ]; then | 192 | if [ ! $? ]; then |
| 179 | echo "Failed to forward jdwp:$pid to $jdb_port!" | 193 | echo "Failed to forward jdwp:$pid to $jdb_port!" |
| 180 | echo "Perhaps you need to specify a different port with --port?" | 194 | echo "Perhaps you need to specify a different port with --port?" |
| 181 | exit 1; | 195 | exit 1; |
| 182 | fi | 196 | fi |
| 183 | 197 | ||
| 184 | jdb_command="jdb -connect \ | 198 | jdb_command="jdb -connect \ |
| 185 | com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port" | 199 | com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port" |
| 186 | 200 | ||
| 187 | if [ $jdb = "yes" ]; then | 201 | if [ $jdb = "yes" ]; then |
| 188 | # Just start JDB and then exit | 202 | # Just start JDB and then exit |
| 189 | $jdb_command | 203 | $jdb_command |
| 190 | exit 1 | 204 | exit 1 |
| 191 | fi | 205 | fi |
| 192 | 206 | ||
| 193 | exec 4<> /tmp/file-descriptor-stamp | 207 | exec 4<> /tmp/file-descriptor-stamp |
| 194 | 208 | ||
| 195 | # Now run JDB with IO redirected to file descriptor 4 in a subprocess. | 209 | # Now run JDB with IO redirected to file descriptor 4 in a subprocess. |
| 196 | $jdb_command <&4 >&4 & | 210 | $jdb_command <&4 >&4 & |
| 197 | 211 | ||
| 198 | character= | 212 | character= |
| 199 | # Next, wait until the prompt is found. | 213 | # Next, wait until the prompt is found. |
| 200 | while read -n1 -u 4 character; do | 214 | while read -n1 -u 4 character; do |
| 201 | if [ "$character" = ">" ]; then | 215 | if [ "$character" = ">" ]; then |
| 202 | echo "JDB attached successfully" | 216 | echo "JDB attached successfully" |
| 203 | break; | 217 | break; |
| 204 | fi | 218 | fi |
| 205 | done | 219 | done |
| 220 | fi | ||
| 221 | |||
| 222 | # See if gdbserver has to be uploaded | ||
| 223 | if [ -z "$gdbserver" ]; then | ||
| 224 | gdbserver_bin=/system/bin/gdbserver | ||
| 225 | else | ||
| 226 | gdbserver_bin=/data/local/tmp/gdbserver | ||
| 227 | |||
| 228 | # Upload the specified gdbserver binary to the device. | ||
| 229 | adb -s $device push "$gdbserver" "$gdbserver_bin" | ||
| 230 | adb -s $device shell chmod +x "$gdbserver_bin" | ||
| 231 | fi | ||
| 206 | 232 | ||
| 207 | # Now start gdbserver on the device asynchronously. | 233 | # Now start gdbserver on the device asynchronously. |
| 208 | 234 | ||
| 209 | echo "Attaching gdbserver to $pid on $device..." | 235 | echo "Attaching gdbserver to $pid on $device..." |
| 210 | exec 5<> /tmp/file-descriptor-stamp | 236 | exec 5<> /tmp/file-descriptor-stamp |
| 211 | adb -s $device shell run-as $package /system/bin/gdbserver --once \ | 237 | |
| 212 | "+debug.$package_uid.socket" --attach $pid >&5 & | 238 | if [ -z "$gdbserver" ]; then |
| 239 | adb -s $device shell run-as $package $gdbserver_bin --once \ | ||
| 240 | "+debug.$package_uid.socket" --attach $pid >&5 & | ||
| 241 | gdb_socket="localfilesystem:$app_data_dir/debug.$package_uid.socket" | ||
| 242 | else | ||
| 243 | # Normally the program cannot access $gdbserver_bin when it is | ||
| 244 | # placed in /data/local/tmp. | ||
| 245 | adb -s $device shell $gdbserver_bin --once \ | ||
| 246 | "+/data/local/tmp/debug.$package_uid.socket" \ | ||
| 247 | --attach $pid >&5 & | ||
| 248 | gdb_socket="localfilesystem:/data/local/tmp/debug.$package_uid.socket" | ||
| 249 | fi | ||
| 213 | 250 | ||
| 214 | # Wait until gdbserver successfully runs. | 251 | # Wait until gdbserver successfully runs. |
| 215 | line= | 252 | line= |
| @@ -227,16 +264,17 @@ while read -u 5 line; do | |||
| 227 | esac | 264 | esac |
| 228 | done | 265 | done |
| 229 | 266 | ||
| 230 | # Send EOF to JDB to make it go away. This will also cause Android to | 267 | if [ "$attach_existing" != "yes" ]; then |
| 231 | # allow Emacs to continue executing. | 268 | # Send EOF to JDB to make it go away. This will also cause |
| 232 | echo "Making JDB go away..." | 269 | # Android to allow Emacs to continue executing. |
| 233 | echo "exit" >&4 | 270 | echo "Making JDB go away..." |
| 234 | read -u 4 line | 271 | echo "exit" >&4 |
| 235 | echo "JDB has gone away with $line" | 272 | read -u 4 line |
| 273 | echo "JDB has gone away with $line" | ||
| 274 | fi | ||
| 236 | 275 | ||
| 237 | # Forward the gdb server port here. | 276 | # Forward the gdb server port here. |
| 238 | adb -s $device forward "tcp:$gdb_port" \ | 277 | adb -s $device forward "tcp:$gdb_port" $gdb_socket |
| 239 | "localfilesystem:$app_data_dir/debug.$package_uid.socket" | ||
| 240 | if [ ! $? ]; then | 278 | if [ ! $? ]; then |
| 241 | echo "Failed to forward $app_data_dir/debug.$package_uid.socket" | 279 | echo "Failed to forward $app_data_dir/debug.$package_uid.socket" |
| 242 | echo "to $gdb_port! Perhaps you need to specify a different port" | 280 | echo "to $gdb_port! Perhaps you need to specify a different port" |
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 2b661024842..4cd286d1e89 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java | |||
| @@ -43,7 +43,7 @@ public class EmacsActivity extends Activity | |||
| 43 | private FrameLayout layout; | 43 | private FrameLayout layout; |
| 44 | 44 | ||
| 45 | /* List of activities with focus. */ | 45 | /* List of activities with focus. */ |
| 46 | private static List<EmacsActivity> focusedActivities; | 46 | public static List<EmacsActivity> focusedActivities; |
| 47 | 47 | ||
| 48 | /* The currently focused window. */ | 48 | /* The currently focused window. */ |
| 49 | public static EmacsWindow focusedWindow; | 49 | public static EmacsWindow focusedWindow; |
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java new file mode 100644 index 00000000000..8d7ae08b257 --- /dev/null +++ b/java/org/gnu/emacs/EmacsContextMenu.java | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | /* Communication module for Android terminals. -*- c-file-style: "GNU" -*- | ||
| 2 | |||
| 3 | Copyright (C) 2023 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 10 | your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 19 | |||
| 20 | package org.gnu.emacs; | ||
| 21 | |||
| 22 | import java.util.List; | ||
| 23 | import java.util.ArrayList; | ||
| 24 | |||
| 25 | import android.content.Context; | ||
| 26 | import android.content.Intent; | ||
| 27 | |||
| 28 | import android.os.Bundle; | ||
| 29 | |||
| 30 | import android.view.Menu; | ||
| 31 | import android.view.MenuItem; | ||
| 32 | import android.view.View; | ||
| 33 | |||
| 34 | import android.widget.PopupMenu; | ||
| 35 | |||
| 36 | /* Context menu implementation. This object is built from JNI and | ||
| 37 | describes a menu hiearchy. Then, `inflate' can turn it into an | ||
| 38 | Android menu, which can be turned into a popup (or other kind of) | ||
| 39 | menu. */ | ||
| 40 | |||
| 41 | public class EmacsContextMenu | ||
| 42 | { | ||
| 43 | private class Item | ||
| 44 | { | ||
| 45 | public int itemID; | ||
| 46 | public String itemName; | ||
| 47 | public EmacsContextMenu subMenu; | ||
| 48 | public boolean isEnabled; | ||
| 49 | }; | ||
| 50 | |||
| 51 | public List<Item> menuItems; | ||
| 52 | public String title; | ||
| 53 | private EmacsContextMenu parent; | ||
| 54 | |||
| 55 | /* Create a context menu with no items inside and the title TITLE, | ||
| 56 | which may be NULL. */ | ||
| 57 | |||
| 58 | public static EmacsContextMenu | ||
| 59 | createContextMenu (String title) | ||
| 60 | { | ||
| 61 | EmacsContextMenu menu; | ||
| 62 | |||
| 63 | menu = new EmacsContextMenu (); | ||
| 64 | menu.menuItems = new ArrayList<Item> (); | ||
| 65 | menu.title = title; | ||
| 66 | |||
| 67 | return menu; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Add a normal menu item to the context menu with the id ITEMID and | ||
| 71 | the name ITEMNAME. Enable it if ISENABLED, else keep it | ||
| 72 | disabled. */ | ||
| 73 | |||
| 74 | public void | ||
| 75 | addItem (int itemID, String itemName, boolean isEnabled) | ||
| 76 | { | ||
| 77 | Item item; | ||
| 78 | |||
| 79 | item = new Item (); | ||
| 80 | item.itemID = itemID; | ||
| 81 | item.itemName = itemName; | ||
| 82 | item.isEnabled = isEnabled; | ||
| 83 | |||
| 84 | menuItems.add (item); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Create a disabled menu item with the name ITEMNAME. */ | ||
| 88 | |||
| 89 | public void | ||
| 90 | addPane (String itemName) | ||
| 91 | { | ||
| 92 | Item item; | ||
| 93 | |||
| 94 | item = new Item (); | ||
| 95 | item.itemName = itemName; | ||
| 96 | |||
| 97 | menuItems.add (item); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Add a submenu to the context menu with the specified title and | ||
| 101 | item name. */ | ||
| 102 | |||
| 103 | public EmacsContextMenu | ||
| 104 | addSubmenu (String itemName, String title) | ||
| 105 | { | ||
| 106 | EmacsContextMenu submenu; | ||
| 107 | Item item; | ||
| 108 | |||
| 109 | item = new Item (); | ||
| 110 | item.itemID = 0; | ||
| 111 | item.itemName = itemName; | ||
| 112 | item.subMenu = createContextMenu (title); | ||
| 113 | item.subMenu.parent = this; | ||
| 114 | |||
| 115 | menuItems.add (item); | ||
| 116 | return item.subMenu; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* Add the contents of this menu to MENU. */ | ||
| 120 | |||
| 121 | private void | ||
| 122 | inflateMenuItems (Menu menu) | ||
| 123 | { | ||
| 124 | Intent intent; | ||
| 125 | MenuItem menuItem; | ||
| 126 | Menu submenu; | ||
| 127 | |||
| 128 | for (Item item : menuItems) | ||
| 129 | { | ||
| 130 | if (item.subMenu != null) | ||
| 131 | { | ||
| 132 | /* This is a submenu. Create the submenu and add the | ||
| 133 | contents of the menu to it. */ | ||
| 134 | submenu = menu.addSubMenu (item.itemName); | ||
| 135 | inflateMenuItems (submenu); | ||
| 136 | } | ||
| 137 | else | ||
| 138 | { | ||
| 139 | menuItem = menu.add (item.itemName); | ||
| 140 | |||
| 141 | /* If the item ID is zero, then disable the item. */ | ||
| 142 | if (item.itemID == 0 || !item.isEnabled) | ||
| 143 | menuItem.setEnabled (false); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /* Enter the items in this context menu to MENU. Create each menu | ||
| 149 | item with an Intent containing a Bundle, where the key | ||
| 150 | "emacs:menu_item_hi" maps to the high 16 bits of the | ||
| 151 | corresponding item ID, and the key "emacs:menu_item_low" maps to | ||
| 152 | the low 16 bits of the item ID. */ | ||
| 153 | |||
| 154 | public void | ||
| 155 | expandTo (Menu menu) | ||
| 156 | { | ||
| 157 | inflateMenuItems (menu); | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Return the parent or NULL. */ | ||
| 161 | |||
| 162 | public EmacsContextMenu | ||
| 163 | parent () | ||
| 164 | { | ||
| 165 | return parent; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Like display, but does the actual work and runs in the main | ||
| 169 | thread. */ | ||
| 170 | |||
| 171 | private boolean | ||
| 172 | display1 (EmacsWindow window, int xPosition, int yPosition) | ||
| 173 | { | ||
| 174 | return window.view.popupMenu (this, xPosition, yPosition); | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Display this context menu on WINDOW, at xPosition and | ||
| 178 | yPosition. */ | ||
| 179 | |||
| 180 | public boolean | ||
| 181 | display (final EmacsWindow window, final int xPosition, | ||
| 182 | final int yPosition) | ||
| 183 | { | ||
| 184 | Runnable runnable; | ||
| 185 | final Holder<Boolean> rc; | ||
| 186 | |||
| 187 | rc = new Holder<Boolean> (); | ||
| 188 | |||
| 189 | runnable = new Runnable () { | ||
| 190 | @Override | ||
| 191 | public void | ||
| 192 | run () | ||
| 193 | { | ||
| 194 | synchronized (this) | ||
| 195 | { | ||
| 196 | rc.thing = display1 (window, xPosition, yPosition); | ||
| 197 | notify (); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | }; | ||
| 201 | |||
| 202 | try | ||
| 203 | { | ||
| 204 | runnable.wait (); | ||
| 205 | } | ||
| 206 | catch (InterruptedException e) | ||
| 207 | { | ||
| 208 | EmacsNative.emacsAbort (); | ||
| 209 | } | ||
| 210 | |||
| 211 | return rc.thing; | ||
| 212 | } | ||
| 213 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index b42e9556e8c..84ff498847b 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java | |||
| @@ -59,7 +59,7 @@ public class EmacsDrawRectangle | |||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | paint = gc.gcPaint; | 61 | paint = gc.gcPaint; |
| 62 | rect = new Rect (x, y, x + width, y + height); | 62 | rect = new Rect (x + 1, y + 1, x + width, y + height); |
| 63 | 63 | ||
| 64 | paint.setStyle (Paint.Style.STROKE); | 64 | paint.setStyle (Paint.Style.STROKE); |
| 65 | 65 | ||
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java index caa5c91edd4..c579625f3f7 100644 --- a/java/org/gnu/emacs/EmacsGC.java +++ b/java/org/gnu/emacs/EmacsGC.java | |||
| @@ -93,6 +93,7 @@ public class EmacsGC extends EmacsHandleObject | |||
| 93 | else | 93 | else |
| 94 | real_clip_rects = clip_rects; | 94 | real_clip_rects = clip_rects; |
| 95 | 95 | ||
| 96 | gcPaint.setStrokeWidth (1f); | ||
| 96 | gcPaint.setColor (foreground | 0xff000000); | 97 | gcPaint.setColor (foreground | 0xff000000); |
| 97 | gcPaint.setXfermode (function == GC_XOR | 98 | gcPaint.setXfermode (function == GC_XOR |
| 98 | ? xorAlu : srcInAlu); | 99 | ? xorAlu : srcInAlu); |
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index c008300dd3a..f935b63fa0d 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java | |||
| @@ -29,6 +29,7 @@ import android.graphics.Point; | |||
| 29 | 29 | ||
| 30 | import android.view.View; | 30 | import android.view.View; |
| 31 | import android.view.InputDevice; | 31 | import android.view.InputDevice; |
| 32 | import android.view.KeyEvent; | ||
| 32 | 33 | ||
| 33 | import android.annotation.TargetApi; | 34 | import android.annotation.TargetApi; |
| 34 | import android.app.Service; | 35 | import android.app.Service; |
| @@ -150,13 +151,13 @@ public class EmacsService extends Service | |||
| 150 | /* Functions from here on must only be called from the Emacs | 151 | /* Functions from here on must only be called from the Emacs |
| 151 | thread. */ | 152 | thread. */ |
| 152 | 153 | ||
| 153 | void | 154 | public void |
| 154 | runOnUiThread (Runnable runnable) | 155 | runOnUiThread (Runnable runnable) |
| 155 | { | 156 | { |
| 156 | handler.post (runnable); | 157 | handler.post (runnable); |
| 157 | } | 158 | } |
| 158 | 159 | ||
| 159 | EmacsView | 160 | public EmacsView |
| 160 | getEmacsView (final EmacsWindow window, final int visibility, | 161 | getEmacsView (final EmacsWindow window, final int visibility, |
| 161 | final boolean isFocusedByDefault) | 162 | final boolean isFocusedByDefault) |
| 162 | { | 163 | { |
| @@ -197,6 +198,38 @@ public class EmacsService extends Service | |||
| 197 | } | 198 | } |
| 198 | 199 | ||
| 199 | public void | 200 | public void |
| 201 | getLocationOnScreen (final EmacsView view, final int[] coordinates) | ||
| 202 | { | ||
| 203 | Runnable runnable; | ||
| 204 | |||
| 205 | runnable = new Runnable () { | ||
| 206 | public void | ||
| 207 | run () | ||
| 208 | { | ||
| 209 | synchronized (this) | ||
| 210 | { | ||
| 211 | view.getLocationOnScreen (coordinates); | ||
| 212 | notify (); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | }; | ||
| 216 | |||
| 217 | synchronized (runnable) | ||
| 218 | { | ||
| 219 | runOnUiThread (runnable); | ||
| 220 | |||
| 221 | try | ||
| 222 | { | ||
| 223 | runnable.wait (); | ||
| 224 | } | ||
| 225 | catch (InterruptedException e) | ||
| 226 | { | ||
| 227 | EmacsNative.emacsAbort (); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | public void | ||
| 200 | fillRectangle (EmacsDrawable drawable, EmacsGC gc, | 233 | fillRectangle (EmacsDrawable drawable, EmacsGC gc, |
| 201 | int x, int y, int width, int height) | 234 | int x, int y, int width, int height) |
| 202 | { | 235 | { |
| @@ -368,4 +401,10 @@ public class EmacsService extends Service | |||
| 368 | 401 | ||
| 369 | return false; | 402 | return false; |
| 370 | } | 403 | } |
| 404 | |||
| 405 | public String | ||
| 406 | nameKeysym (int keysym) | ||
| 407 | { | ||
| 408 | return KeyEvent.keyCodeToString (keysym); | ||
| 409 | } | ||
| 371 | }; | 410 | }; |
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 41acabab97b..1391f630be0 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java | |||
| @@ -21,6 +21,7 @@ package org.gnu.emacs; | |||
| 21 | 21 | ||
| 22 | import android.content.res.ColorStateList; | 22 | import android.content.res.ColorStateList; |
| 23 | 23 | ||
| 24 | import android.view.ContextMenu; | ||
| 24 | import android.view.View; | 25 | import android.view.View; |
| 25 | import android.view.KeyEvent; | 26 | import android.view.KeyEvent; |
| 26 | import android.view.MotionEvent; | 27 | import android.view.MotionEvent; |
| @@ -73,6 +74,12 @@ public class EmacsView extends ViewGroup | |||
| 73 | next call to getBitmap. */ | 74 | next call to getBitmap. */ |
| 74 | private Rect bitmapDirty; | 75 | private Rect bitmapDirty; |
| 75 | 76 | ||
| 77 | /* Whether or not a popup is active. */ | ||
| 78 | private boolean popupActive; | ||
| 79 | |||
| 80 | /* The current context menu. */ | ||
| 81 | private EmacsContextMenu contextMenu; | ||
| 82 | |||
| 76 | public | 83 | public |
| 77 | EmacsView (EmacsWindow window) | 84 | EmacsView (EmacsWindow window) |
| 78 | { | 85 | { |
| @@ -98,6 +105,10 @@ public class EmacsView extends ViewGroup | |||
| 98 | /* Get rid of the foreground and background tint. */ | 105 | /* Get rid of the foreground and background tint. */ |
| 99 | setBackgroundTintList (null); | 106 | setBackgroundTintList (null); |
| 100 | setForegroundTintList (null); | 107 | setForegroundTintList (null); |
| 108 | |||
| 109 | /* Get rid of the default focus highlight. */ | ||
| 110 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) | ||
| 111 | setDefaultFocusHighlightEnabled (false); | ||
| 101 | } | 112 | } |
| 102 | 113 | ||
| 103 | private void | 114 | private void |
| @@ -423,4 +434,40 @@ public class EmacsView extends ViewGroup | |||
| 423 | removeView (surfaceView); | 434 | removeView (surfaceView); |
| 424 | addView (surfaceView, 0); | 435 | addView (surfaceView, 0); |
| 425 | } | 436 | } |
| 437 | |||
| 438 | @Override | ||
| 439 | protected void | ||
| 440 | onCreateContextMenu (ContextMenu menu) | ||
| 441 | { | ||
| 442 | if (contextMenu == null) | ||
| 443 | return; | ||
| 444 | |||
| 445 | contextMenu.expandTo (menu); | ||
| 446 | } | ||
| 447 | |||
| 448 | public boolean | ||
| 449 | popupMenu (EmacsContextMenu menu, int xPosition, | ||
| 450 | int yPosition) | ||
| 451 | { | ||
| 452 | if (popupActive) | ||
| 453 | return false; | ||
| 454 | |||
| 455 | contextMenu = menu; | ||
| 456 | |||
| 457 | /* On API 21 or later, use showContextMenu (float, float). */ | ||
| 458 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) | ||
| 459 | return showContextMenu ((float) xPosition, (float) yPosition); | ||
| 460 | else | ||
| 461 | return showContextMenu (); | ||
| 462 | } | ||
| 463 | |||
| 464 | public void | ||
| 465 | cancelPopupMenu () | ||
| 466 | { | ||
| 467 | if (!popupActive) | ||
| 468 | throw new IllegalStateException ("cancelPopupMenu called without" | ||
| 469 | + " popupActive set"); | ||
| 470 | |||
| 471 | contextMenu = null; | ||
| 472 | } | ||
| 426 | }; | 473 | }; |
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 1f8596dba50..6effa79d1a4 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java | |||
| @@ -24,16 +24,22 @@ import java.util.ArrayList; | |||
| 24 | import java.util.List; | 24 | import java.util.List; |
| 25 | import java.util.HashMap; | 25 | import java.util.HashMap; |
| 26 | 26 | ||
| 27 | import android.content.Context; | ||
| 28 | |||
| 27 | import android.graphics.Rect; | 29 | import android.graphics.Rect; |
| 28 | import android.graphics.Canvas; | 30 | import android.graphics.Canvas; |
| 29 | import android.graphics.Bitmap; | 31 | import android.graphics.Bitmap; |
| 30 | import android.graphics.Point; | 32 | import android.graphics.Point; |
| 33 | import android.graphics.PixelFormat; | ||
| 31 | 34 | ||
| 32 | import android.view.View; | 35 | import android.view.View; |
| 36 | import android.view.ViewManager; | ||
| 33 | import android.view.ViewGroup; | 37 | import android.view.ViewGroup; |
| 38 | import android.view.Gravity; | ||
| 34 | import android.view.KeyEvent; | 39 | import android.view.KeyEvent; |
| 35 | import android.view.MotionEvent; | 40 | import android.view.MotionEvent; |
| 36 | import android.view.InputDevice; | 41 | import android.view.InputDevice; |
| 42 | import android.view.WindowManager; | ||
| 37 | 43 | ||
| 38 | import android.content.Intent; | 44 | import android.content.Intent; |
| 39 | import android.util.Log; | 45 | import android.util.Log; |
| @@ -110,9 +116,17 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 110 | not the window should be focusable. */ | 116 | not the window should be focusable. */ |
| 111 | private boolean dontFocusOnMap, dontAcceptFocus; | 117 | private boolean dontFocusOnMap, dontAcceptFocus; |
| 112 | 118 | ||
| 119 | /* Whether or not the window is override-redirect. An | ||
| 120 | override-redirect window always has its own system window. */ | ||
| 121 | private boolean overrideRedirect; | ||
| 122 | |||
| 123 | /* The window manager that is the parent of this window. NULL if | ||
| 124 | there is no such window manager. */ | ||
| 125 | private WindowManager windowManager; | ||
| 126 | |||
| 113 | public | 127 | public |
| 114 | EmacsWindow (short handle, final EmacsWindow parent, int x, int y, | 128 | EmacsWindow (short handle, final EmacsWindow parent, int x, int y, |
| 115 | int width, int height) | 129 | int width, int height, boolean overrideRedirect) |
| 116 | { | 130 | { |
| 117 | super (handle); | 131 | super (handle); |
| 118 | 132 | ||
| @@ -124,6 +138,7 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 124 | view = EmacsService.SERVICE.getEmacsView (this, View.GONE, | 138 | view = EmacsService.SERVICE.getEmacsView (this, View.GONE, |
| 125 | parent == null); | 139 | parent == null); |
| 126 | this.parent = parent; | 140 | this.parent = parent; |
| 141 | this.overrideRedirect = overrideRedirect; | ||
| 127 | 142 | ||
| 128 | /* Create the list of children. */ | 143 | /* Create the list of children. */ |
| 129 | children = new ArrayList<EmacsWindow> (); | 144 | children = new ArrayList<EmacsWindow> (); |
| @@ -180,7 +195,7 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 180 | public void | 195 | public void |
| 181 | run () | 196 | run () |
| 182 | { | 197 | { |
| 183 | View parent; | 198 | ViewManager parent; |
| 184 | EmacsWindowAttachmentManager manager; | 199 | EmacsWindowAttachmentManager manager; |
| 185 | 200 | ||
| 186 | if (EmacsActivity.focusedWindow == EmacsWindow.this) | 201 | if (EmacsActivity.focusedWindow == EmacsWindow.this) |
| @@ -189,10 +204,15 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 189 | manager = EmacsWindowAttachmentManager.MANAGER; | 204 | manager = EmacsWindowAttachmentManager.MANAGER; |
| 190 | view.setVisibility (View.GONE); | 205 | view.setVisibility (View.GONE); |
| 191 | 206 | ||
| 192 | parent = (View) view.getParent (); | 207 | /* If the window manager is set, use that instead. */ |
| 208 | if (windowManager != null) | ||
| 209 | parent = windowManager; | ||
| 210 | else | ||
| 211 | parent = (ViewManager) view.getParent (); | ||
| 212 | windowManager = null; | ||
| 193 | 213 | ||
| 194 | if (parent != null) | 214 | if (parent != null) |
| 195 | ((ViewGroup) parent).removeView (view); | 215 | parent.removeView (view); |
| 196 | 216 | ||
| 197 | manager.detachWindow (EmacsWindow.this); | 217 | manager.detachWindow (EmacsWindow.this); |
| 198 | } | 218 | } |
| @@ -247,6 +267,10 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 247 | public void | 267 | public void |
| 248 | run () | 268 | run () |
| 249 | { | 269 | { |
| 270 | if (overrideRedirect) | ||
| 271 | /* Set the layout parameters again. */ | ||
| 272 | view.setLayoutParams (getWindowLayoutParams ()); | ||
| 273 | |||
| 250 | view.mustReportLayout = true; | 274 | view.mustReportLayout = true; |
| 251 | view.requestLayout (); | 275 | view.requestLayout (); |
| 252 | } | 276 | } |
| @@ -284,6 +308,39 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 284 | } | 308 | } |
| 285 | } | 309 | } |
| 286 | 310 | ||
| 311 | private WindowManager.LayoutParams | ||
| 312 | getWindowLayoutParams () | ||
| 313 | { | ||
| 314 | WindowManager.LayoutParams params; | ||
| 315 | int flags, type; | ||
| 316 | Rect rect; | ||
| 317 | |||
| 318 | flags = 0; | ||
| 319 | rect = getGeometry (); | ||
| 320 | flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; | ||
| 321 | flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; | ||
| 322 | type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; | ||
| 323 | |||
| 324 | params | ||
| 325 | = new WindowManager.LayoutParams (rect.width (), rect.height (), | ||
| 326 | rect.left, rect.top, | ||
| 327 | type, flags, | ||
| 328 | PixelFormat.RGBA_8888); | ||
| 329 | params.gravity = Gravity.TOP | Gravity.LEFT; | ||
| 330 | return params; | ||
| 331 | } | ||
| 332 | |||
| 333 | private Context | ||
| 334 | findSuitableActivityContext () | ||
| 335 | { | ||
| 336 | /* Find a recently focused activity. */ | ||
| 337 | if (!EmacsActivity.focusedActivities.isEmpty ()) | ||
| 338 | return EmacsActivity.focusedActivities.get (0); | ||
| 339 | |||
| 340 | /* Return the service context, which probably won't work. */ | ||
| 341 | return EmacsService.SERVICE; | ||
| 342 | } | ||
| 343 | |||
| 287 | public void | 344 | public void |
| 288 | mapWindow () | 345 | mapWindow () |
| 289 | { | 346 | { |
| @@ -300,20 +357,60 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 300 | run () | 357 | run () |
| 301 | { | 358 | { |
| 302 | EmacsWindowAttachmentManager manager; | 359 | EmacsWindowAttachmentManager manager; |
| 360 | WindowManager windowManager; | ||
| 361 | Context ctx; | ||
| 362 | Object tem; | ||
| 363 | WindowManager.LayoutParams params; | ||
| 303 | 364 | ||
| 304 | /* Make the view visible, first of all. */ | 365 | /* Make the view visible, first of all. */ |
| 305 | view.setVisibility (View.VISIBLE); | 366 | view.setVisibility (View.VISIBLE); |
| 306 | 367 | ||
| 307 | manager = EmacsWindowAttachmentManager.MANAGER; | 368 | if (!overrideRedirect) |
| 308 | 369 | { | |
| 309 | /* If parent is the root window, notice that there are new | 370 | manager = EmacsWindowAttachmentManager.MANAGER; |
| 310 | children available for interested activites to pick | 371 | |
| 311 | up. */ | 372 | /* If parent is the root window, notice that there are new |
| 312 | manager.registerWindow (EmacsWindow.this); | 373 | children available for interested activites to pick |
| 313 | 374 | up. */ | |
| 314 | if (!getDontFocusOnMap ()) | 375 | manager.registerWindow (EmacsWindow.this); |
| 315 | /* Eventually this should check no-focus-on-map. */ | 376 | |
| 316 | view.requestFocus (); | 377 | if (!getDontFocusOnMap ()) |
| 378 | /* Eventually this should check no-focus-on-map. */ | ||
| 379 | view.requestFocus (); | ||
| 380 | } | ||
| 381 | else | ||
| 382 | { | ||
| 383 | /* But if the window is an override-redirect window, | ||
| 384 | then: | ||
| 385 | |||
| 386 | - Find an activity that is currently active. | ||
| 387 | |||
| 388 | - Map the window as a panel on top of that | ||
| 389 | activity using the system window manager. */ | ||
| 390 | |||
| 391 | ctx = findSuitableActivityContext (); | ||
| 392 | tem = ctx.getSystemService (Context.WINDOW_SERVICE); | ||
| 393 | windowManager = (WindowManager) tem; | ||
| 394 | |||
| 395 | /* Calculate layout parameters. */ | ||
| 396 | params = getWindowLayoutParams (); | ||
| 397 | view.setLayoutParams (params); | ||
| 398 | |||
| 399 | /* Attach the view. */ | ||
| 400 | try | ||
| 401 | { | ||
| 402 | windowManager.addView (view, params); | ||
| 403 | |||
| 404 | /* Record the window manager being used in the | ||
| 405 | EmacsWindow object. */ | ||
| 406 | EmacsWindow.this.windowManager = windowManager; | ||
| 407 | } | ||
| 408 | catch (Exception e) | ||
| 409 | { | ||
| 410 | Log.w (TAG, | ||
| 411 | "failed to attach override-redirect window, " + e); | ||
| 412 | } | ||
| 413 | } | ||
| 317 | } | 414 | } |
| 318 | }); | 415 | }); |
| 319 | } | 416 | } |
| @@ -355,6 +452,11 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 355 | 452 | ||
| 356 | view.setVisibility (View.GONE); | 453 | view.setVisibility (View.GONE); |
| 357 | 454 | ||
| 455 | /* Detach the view from the window manager if possible. */ | ||
| 456 | if (windowManager != null) | ||
| 457 | windowManager.removeView (view); | ||
| 458 | windowManager = null; | ||
| 459 | |||
| 358 | /* Now that the window is unmapped, unregister it as | 460 | /* Now that the window is unmapped, unregister it as |
| 359 | well. */ | 461 | well. */ |
| 360 | manager.detachWindow (EmacsWindow.this); | 462 | manager.detachWindow (EmacsWindow.this); |
| @@ -756,17 +858,23 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 756 | run () | 858 | run () |
| 757 | { | 859 | { |
| 758 | EmacsWindowAttachmentManager manager; | 860 | EmacsWindowAttachmentManager manager; |
| 759 | View parent; | 861 | ViewManager parent; |
| 760 | 862 | ||
| 761 | /* First, detach this window if necessary. */ | 863 | /* First, detach this window if necessary. */ |
| 762 | manager = EmacsWindowAttachmentManager.MANAGER; | 864 | manager = EmacsWindowAttachmentManager.MANAGER; |
| 763 | manager.detachWindow (EmacsWindow.this); | 865 | manager.detachWindow (EmacsWindow.this); |
| 764 | 866 | ||
| 765 | /* Also unparent this view. */ | 867 | /* Also unparent this view. */ |
| 766 | parent = (View) view.getParent (); | 868 | |
| 869 | /* If the window manager is set, use that instead. */ | ||
| 870 | if (windowManager != null) | ||
| 871 | parent = windowManager; | ||
| 872 | else | ||
| 873 | parent = (ViewManager) view.getParent (); | ||
| 874 | windowManager = null; | ||
| 767 | 875 | ||
| 768 | if (parent != null) | 876 | if (parent != null) |
| 769 | ((ViewGroup) parent).removeView (view); | 877 | parent.removeView (view); |
| 770 | 878 | ||
| 771 | /* Next, either add this window as a child of the new | 879 | /* Next, either add this window as a child of the new |
| 772 | parent's view, or make it available again. */ | 880 | parent's view, or make it available again. */ |
| @@ -899,4 +1007,23 @@ public class EmacsWindow extends EmacsHandleObject | |||
| 899 | { | 1007 | { |
| 900 | return dontFocusOnMap; | 1008 | return dontFocusOnMap; |
| 901 | } | 1009 | } |
| 1010 | |||
| 1011 | public int[] | ||
| 1012 | translateCoordinates (int x, int y) | ||
| 1013 | { | ||
| 1014 | int[] array; | ||
| 1015 | |||
| 1016 | /* This is supposed to translate coordinates to the root | ||
| 1017 | window. */ | ||
| 1018 | array = new int[2]; | ||
| 1019 | EmacsService.SERVICE.getLocationOnScreen (view, array); | ||
| 1020 | |||
| 1021 | /* Now, the coordinates of the view should be in array. Offset X | ||
| 1022 | and Y by them. */ | ||
| 1023 | array[0] += x; | ||
| 1024 | array[1] += y; | ||
| 1025 | |||
| 1026 | /* Return the resulting coordinates. */ | ||
| 1027 | return array; | ||
| 1028 | } | ||
| 902 | }; | 1029 | }; |