diff options
| author | Po Lu | 2023-01-14 22:12:16 +0800 |
|---|---|---|
| committer | Po Lu | 2023-01-14 22:12:16 +0800 |
| commit | 2b87ab7b27163fbd7b6b64c5a44e26b0e692c00a (patch) | |
| tree | 3ab31df90bd435009d2d42b636ce3baf33bd2b28 | |
| parent | 28a9baccd4c8e997895d3adb3cfce4a11fa29896 (diff) | |
| download | emacs-2b87ab7b27163fbd7b6b64c5a44e26b0e692c00a.tar.gz emacs-2b87ab7b27163fbd7b6b64c5a44e26b0e692c00a.zip | |
Update Android port
* java/Makefile.in (clean): Fix distclean and bootstrap-clean rules.
* java/debug.sh (jdb_port):
(attach_existing):
(num_pids):
(line): Add new options to upload a gdbserver binary to the device.
* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
focusedActivities public.
* java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
New class.
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
bounds computation.
* java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width
explicitly.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(getLocationOnScreen, nameKeysym): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus
highlight.
(onCreateContextMenu, popupMenu, cancelPopupMenu): New
functions.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a
kind of ``override redirect'' window for tooltips.
* src/android.c (struct android_emacs_service): New method
`name_keysym'.
(android_run_select_thread, android_init_events):
(android_select): Release select thread on semaphores instead of
signals to avoid one nasty race on SIGUSR2 delivery.
(android_init_emacs_service): Initialize new method.
(android_create_window): Handle CW_OVERRIDE_REDIRECT.
(android_move_resize_window, android_map_raised)
(android_translate_coordinates, android_get_keysym_name)
(android_build_string, android_exception_check): New functions.
* src/android.h: Update prototypes.
* src/androidfns.c (android_set_parent_frame, Fx_create_frame)
(unwind_create_tip_frame, android_create_tip_frame)
(android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip)
(syms_of_androidfns): Implement tooltips and iconification
reporting.
* src/androidgui.h (enum android_window_value_mask): Add
CWOverrideRedirect.
(struct android_set_window_attributes): Add `override_redirect'.
(ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock.
* src/androidmenu.c (struct android_emacs_context_menu): New
struct.
(android_init_emacs_context_menu, android_unwind_local_frame)
(android_push_local_frame, android_menu_show, init_androidmenu):
New functions.
* src/androidterm.c (handle_one_android_event): Fix NULL pointer
dereference.
(android_fullscreen_hook): Handle fullscreen correctly.
(android_draw_box_rect): Fix top line.
(get_keysym_name): Implement function.
(android_create_terminal): Remove scroll bar stubs and add menu
hook.
* src/androidterm.h: Update prototypes.
* src/emacs.c (android_emacs_init): Initialize androidmenu.c.
* xcompile/Makefile.in: Fix clean rules.
| -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 | ||||
| -rw-r--r-- | src/android.c | 169 | ||||
| -rw-r--r-- | src/android.h | 6 | ||||
| -rw-r--r-- | src/androidfns.c | 677 | ||||
| -rw-r--r-- | src/androidgui.h | 14 | ||||
| -rw-r--r-- | src/androidmenu.c | 293 | ||||
| -rw-r--r-- | src/androidterm.c | 34 | ||||
| -rw-r--r-- | src/androidterm.h | 6 | ||||
| -rw-r--r-- | src/emacs.c | 4 | ||||
| -rw-r--r-- | xcompile/Makefile.in | 13 |
18 files changed, 1685 insertions, 112 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 | }; |
diff --git a/src/android.c b/src/android.c index fba43129ee3..e4022501f9d 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -87,6 +87,7 @@ struct android_emacs_service | |||
| 87 | jmethodID get_screen_width; | 87 | jmethodID get_screen_width; |
| 88 | jmethodID get_screen_height; | 88 | jmethodID get_screen_height; |
| 89 | jmethodID detect_mouse; | 89 | jmethodID detect_mouse; |
| 90 | jmethodID name_keysym; | ||
| 90 | }; | 91 | }; |
| 91 | 92 | ||
| 92 | struct android_emacs_pixmap | 93 | struct android_emacs_pixmap |
| @@ -229,14 +230,14 @@ static volatile bool android_pselect_completed; | |||
| 229 | /* The global event queue. */ | 230 | /* The global event queue. */ |
| 230 | static struct android_event_queue event_queue; | 231 | static struct android_event_queue event_queue; |
| 231 | 232 | ||
| 232 | /* Semaphore used to signal select completion. */ | 233 | /* Semaphores used to signal select completion and start. */ |
| 233 | static sem_t android_pselect_sem; | 234 | static sem_t android_pselect_sem, android_pselect_start_sem; |
| 234 | 235 | ||
| 235 | static void * | 236 | static void * |
| 236 | android_run_select_thread (void *data) | 237 | android_run_select_thread (void *data) |
| 237 | { | 238 | { |
| 238 | sigset_t signals; | 239 | sigset_t signals; |
| 239 | int sig, rc; | 240 | int rc; |
| 240 | 241 | ||
| 241 | sigfillset (&signals); | 242 | sigfillset (&signals); |
| 242 | 243 | ||
| @@ -245,23 +246,10 @@ android_run_select_thread (void *data) | |||
| 245 | "pthread_sigmask: %s", | 246 | "pthread_sigmask: %s", |
| 246 | strerror (errno)); | 247 | strerror (errno)); |
| 247 | 248 | ||
| 248 | sigemptyset (&signals); | ||
| 249 | sigaddset (&signals, SIGUSR1); | ||
| 250 | |||
| 251 | if (pthread_sigmask (SIG_UNBLOCK, &signals, NULL)) | ||
| 252 | __android_log_print (ANDROID_LOG_FATAL, __func__, | ||
| 253 | "pthread_sigmask: %s", | ||
| 254 | strerror (errno)); | ||
| 255 | |||
| 256 | sigemptyset (&signals); | ||
| 257 | sigaddset (&signals, SIGUSR2); | ||
| 258 | |||
| 259 | while (true) | 249 | while (true) |
| 260 | { | 250 | { |
| 261 | /* Keep waiting for SIGUSR2, ignoring EINTR in the meantime. */ | 251 | /* Wait for the thread to be released. */ |
| 262 | 252 | sem_wait (&android_pselect_start_sem); | |
| 263 | while (sigwait (&signals, &sig)) | ||
| 264 | /* Spin. */; | ||
| 265 | 253 | ||
| 266 | /* Get the select lock and call pselect. */ | 254 | /* Get the select lock and call pselect. */ |
| 267 | pthread_mutex_lock (&event_queue.select_mutex); | 255 | pthread_mutex_lock (&event_queue.select_mutex); |
| @@ -322,6 +310,7 @@ android_init_events (void) | |||
| 322 | strerror (errno)); | 310 | strerror (errno)); |
| 323 | 311 | ||
| 324 | sem_init (&android_pselect_sem, 0, 0); | 312 | sem_init (&android_pselect_sem, 0, 0); |
| 313 | sem_init (&android_pselect_start_sem, 0, 0); | ||
| 325 | 314 | ||
| 326 | event_queue.events.next = &event_queue.events; | 315 | event_queue.events.next = &event_queue.events; |
| 327 | event_queue.events.last = &event_queue.events; | 316 | event_queue.events.last = &event_queue.events; |
| @@ -444,7 +433,9 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, | |||
| 444 | android_pselect_sigset = sigset; | 433 | android_pselect_sigset = sigset; |
| 445 | pthread_mutex_unlock (&event_queue.select_mutex); | 434 | pthread_mutex_unlock (&event_queue.select_mutex); |
| 446 | 435 | ||
| 447 | pthread_kill (event_queue.select_thread, SIGUSR2); | 436 | /* Release the select thread. */ |
| 437 | sem_post (&android_pselect_start_sem); | ||
| 438 | |||
| 448 | pthread_cond_wait (&event_queue.read_var, &event_queue.mutex); | 439 | pthread_cond_wait (&event_queue.read_var, &event_queue.mutex); |
| 449 | 440 | ||
| 450 | /* Interrupt the select thread now, in case it's still in | 441 | /* Interrupt the select thread now, in case it's still in |
| @@ -1058,6 +1049,7 @@ android_init_emacs_service (void) | |||
| 1058 | FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I"); | 1049 | FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I"); |
| 1059 | FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I"); | 1050 | FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I"); |
| 1060 | FIND_METHOD (detect_mouse, "detectMouse", "()Z"); | 1051 | FIND_METHOD (detect_mouse, "detectMouse", "()Z"); |
| 1052 | FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;"); | ||
| 1061 | #undef FIND_METHOD | 1053 | #undef FIND_METHOD |
| 1062 | } | 1054 | } |
| 1063 | 1055 | ||
| @@ -1678,6 +1670,7 @@ android_create_window (android_window parent, int x, int y, | |||
| 1678 | jobject object, parent_object, old; | 1670 | jobject object, parent_object, old; |
| 1679 | android_window window; | 1671 | android_window window; |
| 1680 | android_handle prev_max_handle; | 1672 | android_handle prev_max_handle; |
| 1673 | bool override_redirect; | ||
| 1681 | 1674 | ||
| 1682 | parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW); | 1675 | parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW); |
| 1683 | 1676 | ||
| @@ -1695,7 +1688,8 @@ android_create_window (android_window parent, int x, int y, | |||
| 1695 | 1688 | ||
| 1696 | constructor | 1689 | constructor |
| 1697 | = (*android_java_env)->GetMethodID (android_java_env, class, "<init>", | 1690 | = (*android_java_env)->GetMethodID (android_java_env, class, "<init>", |
| 1698 | "(SLorg/gnu/emacs/EmacsWindow;IIII)V"); | 1691 | "(SLorg/gnu/emacs/EmacsWindow;" |
| 1692 | "IIIIZ)V"); | ||
| 1699 | assert (constructor != NULL); | 1693 | assert (constructor != NULL); |
| 1700 | 1694 | ||
| 1701 | old = class; | 1695 | old = class; |
| @@ -1707,10 +1701,17 @@ android_create_window (android_window parent, int x, int y, | |||
| 1707 | memory_full (0); | 1701 | memory_full (0); |
| 1708 | } | 1702 | } |
| 1709 | 1703 | ||
| 1704 | /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window | ||
| 1705 | creation time. */ | ||
| 1706 | override_redirect = ((value_mask | ||
| 1707 | & ANDROID_CW_OVERRIDE_REDIRECT) | ||
| 1708 | && attrs->override_redirect); | ||
| 1709 | |||
| 1710 | object = (*android_java_env)->NewObject (android_java_env, class, | 1710 | object = (*android_java_env)->NewObject (android_java_env, class, |
| 1711 | constructor, (jshort) window, | 1711 | constructor, (jshort) window, |
| 1712 | parent_object, (jint) x, (jint) y, | 1712 | parent_object, (jint) x, (jint) y, |
| 1713 | (jint) width, (jint) height); | 1713 | (jint) width, (jint) height, |
| 1714 | (jboolean) override_redirect); | ||
| 1714 | if (!object) | 1715 | if (!object) |
| 1715 | { | 1716 | { |
| 1716 | (*android_java_env)->ExceptionClear (android_java_env); | 1717 | (*android_java_env)->ExceptionClear (android_java_env); |
| @@ -3212,6 +3213,66 @@ android_get_geometry (android_window handle, | |||
| 3212 | ANDROID_DELETE_LOCAL_REF (window_geometry); | 3213 | ANDROID_DELETE_LOCAL_REF (window_geometry); |
| 3213 | } | 3214 | } |
| 3214 | 3215 | ||
| 3216 | void | ||
| 3217 | android_move_resize_window (android_window window, int x, int y, | ||
| 3218 | unsigned int width, unsigned int height) | ||
| 3219 | { | ||
| 3220 | android_move_window (window, x, y); | ||
| 3221 | android_resize_window (window, width, height); | ||
| 3222 | } | ||
| 3223 | |||
| 3224 | void | ||
| 3225 | android_map_raised (android_window window) | ||
| 3226 | { | ||
| 3227 | android_raise_window (window); | ||
| 3228 | android_map_window (window); | ||
| 3229 | } | ||
| 3230 | |||
| 3231 | void | ||
| 3232 | android_translate_coordinates (android_window src, int x, | ||
| 3233 | int y, int *root_x, int *root_y) | ||
| 3234 | { | ||
| 3235 | jobject window; | ||
| 3236 | jarray coordinates; | ||
| 3237 | jmethodID method; | ||
| 3238 | jint *ints; | ||
| 3239 | |||
| 3240 | window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW); | ||
| 3241 | method = android_lookup_method ("org/gnu/emacs/EmacsWindow", | ||
| 3242 | "translateCoordinates", | ||
| 3243 | "(II)[I"); | ||
| 3244 | coordinates | ||
| 3245 | = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 3246 | window, method, | ||
| 3247 | (jint) x, (jint) y); | ||
| 3248 | |||
| 3249 | if (!coordinates) | ||
| 3250 | { | ||
| 3251 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 3252 | memory_full (0); | ||
| 3253 | } | ||
| 3254 | |||
| 3255 | /* The array must contain two elements: X, Y translated to the root | ||
| 3256 | window. */ | ||
| 3257 | eassert ((*android_java_env)->GetArrayLength (android_java_env, | ||
| 3258 | coordinates) | ||
| 3259 | == 2); | ||
| 3260 | |||
| 3261 | /* Obtain the coordinates from the array. */ | ||
| 3262 | ints = (*android_java_env)->GetIntArrayElements (android_java_env, | ||
| 3263 | coordinates, NULL); | ||
| 3264 | *root_x = ints[0]; | ||
| 3265 | *root_y = ints[1]; | ||
| 3266 | |||
| 3267 | /* Release the coordinates. */ | ||
| 3268 | (*android_java_env)->ReleaseIntArrayElements (android_java_env, | ||
| 3269 | coordinates, ints, | ||
| 3270 | JNI_ABORT); | ||
| 3271 | |||
| 3272 | /* And free the local reference. */ | ||
| 3273 | ANDROID_DELETE_LOCAL_REF (coordinates); | ||
| 3274 | } | ||
| 3275 | |||
| 3215 | 3276 | ||
| 3216 | 3277 | ||
| 3217 | /* Low level drawing primitives. */ | 3278 | /* Low level drawing primitives. */ |
| @@ -3384,6 +3445,30 @@ android_set_dont_accept_focus (android_window handle, | |||
| 3384 | (jboolean) no_accept_focus); | 3445 | (jboolean) no_accept_focus); |
| 3385 | } | 3446 | } |
| 3386 | 3447 | ||
| 3448 | void | ||
| 3449 | android_get_keysym_name (int keysym, char *name_return, size_t size) | ||
| 3450 | { | ||
| 3451 | jobject string; | ||
| 3452 | const char *buffer; | ||
| 3453 | |||
| 3454 | string = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 3455 | emacs_service, | ||
| 3456 | service_class.name_keysym, | ||
| 3457 | (jint) keysym); | ||
| 3458 | android_exception_check (); | ||
| 3459 | |||
| 3460 | buffer = (*android_java_env)->GetStringUTFChars (android_java_env, | ||
| 3461 | (jstring) string, | ||
| 3462 | NULL); | ||
| 3463 | android_exception_check (); | ||
| 3464 | strncpy (name_return, buffer, size - 1); | ||
| 3465 | |||
| 3466 | (*android_java_env)->ReleaseStringUTFChars (android_java_env, | ||
| 3467 | (jstring) string, | ||
| 3468 | buffer); | ||
| 3469 | ANDROID_DELETE_LOCAL_REF (string); | ||
| 3470 | } | ||
| 3471 | |||
| 3387 | 3472 | ||
| 3388 | 3473 | ||
| 3389 | #undef faccessat | 3474 | #undef faccessat |
| @@ -3525,6 +3610,48 @@ emacs_abort (void) | |||
| 3525 | abort (); | 3610 | abort (); |
| 3526 | } | 3611 | } |
| 3527 | 3612 | ||
| 3613 | |||
| 3614 | |||
| 3615 | /* Given a Lisp string TEXT, return a local reference to an equivalent | ||
| 3616 | Java string. */ | ||
| 3617 | |||
| 3618 | jstring | ||
| 3619 | android_build_string (Lisp_Object text) | ||
| 3620 | { | ||
| 3621 | Lisp_Object encoded; | ||
| 3622 | jstring string; | ||
| 3623 | |||
| 3624 | encoded = ENCODE_UTF_8 (text); | ||
| 3625 | |||
| 3626 | /* Note that Java expects this string to be in ``modified UTF | ||
| 3627 | encoding'', which is actually UTF-8, except with NUL encoded as a | ||
| 3628 | two-byte sequence. The only consequence of passing an actual | ||
| 3629 | UTF-8 string is that NUL bytes cannot be represented, which is | ||
| 3630 | not really of consequence. */ | ||
| 3631 | string = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 3632 | SSDATA (encoded)); | ||
| 3633 | if (!string) | ||
| 3634 | { | ||
| 3635 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 3636 | memory_full (0); | ||
| 3637 | } | ||
| 3638 | |||
| 3639 | return string; | ||
| 3640 | } | ||
| 3641 | |||
| 3642 | /* Check for JNI exceptions and call memory_full in that | ||
| 3643 | situation. */ | ||
| 3644 | |||
| 3645 | void | ||
| 3646 | android_exception_check (void) | ||
| 3647 | { | ||
| 3648 | if ((*android_java_env)->ExceptionCheck (android_java_env)) | ||
| 3649 | { | ||
| 3650 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 3651 | memory_full (0); | ||
| 3652 | } | ||
| 3653 | } | ||
| 3654 | |||
| 3528 | #else /* ANDROID_STUBIFY */ | 3655 | #else /* ANDROID_STUBIFY */ |
| 3529 | 3656 | ||
| 3530 | /* X emulation functions for Android. */ | 3657 | /* X emulation functions for Android. */ |
diff --git a/src/android.h b/src/android.h index af1e42ec370..98f2494e9a3 100644 --- a/src/android.h +++ b/src/android.h | |||
| @@ -35,6 +35,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 35 | #include <android/bitmap.h> | 35 | #include <android/bitmap.h> |
| 36 | 36 | ||
| 37 | #include "androidgui.h" | 37 | #include "androidgui.h" |
| 38 | #include "lisp.h" | ||
| 38 | #endif | 39 | #endif |
| 39 | 40 | ||
| 40 | /* This must be used in every symbol declaration to export it to the | 41 | /* This must be used in every symbol declaration to export it to the |
| @@ -84,6 +85,11 @@ extern bool android_detect_mouse (void); | |||
| 84 | extern void android_set_dont_focus_on_map (android_window, bool); | 85 | extern void android_set_dont_focus_on_map (android_window, bool); |
| 85 | extern void android_set_dont_accept_focus (android_window, bool); | 86 | extern void android_set_dont_accept_focus (android_window, bool); |
| 86 | 87 | ||
| 88 | extern jstring android_build_string (Lisp_Object); | ||
| 89 | extern void android_exception_check (void); | ||
| 90 | |||
| 91 | extern void android_get_keysym_name (int, char *, size_t); | ||
| 92 | |||
| 87 | 93 | ||
| 88 | 94 | ||
| 89 | /* Directory listing emulation. */ | 95 | /* Directory listing emulation. */ |
diff --git a/src/androidfns.c b/src/androidfns.c index 459e407b901..ab136bc2722 100644 --- a/src/androidfns.c +++ b/src/androidfns.c | |||
| @@ -25,12 +25,36 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 25 | #include "androidterm.h" | 25 | #include "androidterm.h" |
| 26 | #include "blockinput.h" | 26 | #include "blockinput.h" |
| 27 | #include "keyboard.h" | 27 | #include "keyboard.h" |
| 28 | #include "buffer.h" | ||
| 28 | 29 | ||
| 29 | #ifndef ANDROID_STUBIFY | 30 | #ifndef ANDROID_STUBIFY |
| 30 | 31 | ||
| 31 | /* Some kind of reference count for the image cache. */ | 32 | /* Some kind of reference count for the image cache. */ |
| 32 | static ptrdiff_t image_cache_refcount; | 33 | static ptrdiff_t image_cache_refcount; |
| 33 | 34 | ||
| 35 | /* The frame of the currently visible tooltip, or nil if none. */ | ||
| 36 | static Lisp_Object tip_frame; | ||
| 37 | |||
| 38 | /* The window-system window corresponding to the frame of the | ||
| 39 | currently visible tooltip. */ | ||
| 40 | static android_window tip_window; | ||
| 41 | |||
| 42 | /* The X and Y deltas of the last call to `x-show-tip'. */ | ||
| 43 | static Lisp_Object tip_dx, tip_dy; | ||
| 44 | |||
| 45 | /* A timer that hides or deletes the currently visible tooltip when it | ||
| 46 | fires. */ | ||
| 47 | static Lisp_Object tip_timer; | ||
| 48 | |||
| 49 | /* STRING argument of last `x-show-tip' call. */ | ||
| 50 | static Lisp_Object tip_last_string; | ||
| 51 | |||
| 52 | /* Normalized FRAME argument of last `x-show-tip' call. */ | ||
| 53 | static Lisp_Object tip_last_frame; | ||
| 54 | |||
| 55 | /* PARMS argument of last `x-show-tip' call. */ | ||
| 56 | static Lisp_Object tip_last_parms; | ||
| 57 | |||
| 34 | #endif | 58 | #endif |
| 35 | 59 | ||
| 36 | static struct android_display_info * | 60 | static struct android_display_info * |
| @@ -180,6 +204,9 @@ android_set_parent_frame (struct frame *f, Lisp_Object new_value, | |||
| 180 | 204 | ||
| 181 | fset_parent_frame (f, new_value); | 205 | fset_parent_frame (f, new_value); |
| 182 | } | 206 | } |
| 207 | |||
| 208 | /* Update the fullscreen frame parameter as well. */ | ||
| 209 | FRAME_TERMINAL (f)->fullscreen_hook (f); | ||
| 183 | } | 210 | } |
| 184 | 211 | ||
| 185 | void | 212 | void |
| @@ -858,13 +885,13 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, | |||
| 858 | gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), | 885 | gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), |
| 859 | NULL, NULL, RES_TYPE_NUMBER); | 886 | NULL, NULL, RES_TYPE_NUMBER); |
| 860 | 887 | ||
| 861 | /* gui_default_parameter (f, parms, Qvertical_scroll_bars, */ | 888 | gui_default_parameter (f, parms, Qvertical_scroll_bars, |
| 862 | /* Qleft, */ | 889 | Qleft, |
| 863 | /* "verticalScrollBars", "ScrollBars", */ | 890 | "verticalScrollBars", "ScrollBars", |
| 864 | /* RES_TYPE_SYMBOL); */ | 891 | RES_TYPE_SYMBOL); |
| 865 | /* gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, */ | 892 | gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, |
| 866 | /* "horizontalScrollBars", "ScrollBars", */ | 893 | "horizontalScrollBars", "ScrollBars", |
| 867 | /* RES_TYPE_SYMBOL); TODO */ | 894 | RES_TYPE_SYMBOL); |
| 868 | 895 | ||
| 869 | /* Also do the stuff which must be set before the window exists. */ | 896 | /* Also do the stuff which must be set before the window exists. */ |
| 870 | gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), | 897 | gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), |
| @@ -893,7 +920,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, | |||
| 893 | android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background, | 920 | android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background, |
| 894 | "scrollBarBackground", | 921 | "scrollBarBackground", |
| 895 | "ScrollBarBackground", false); | 922 | "ScrollBarBackground", false); |
| 896 | #endif /* TODO */ | 923 | #endif |
| 897 | 924 | ||
| 898 | /* Init faces before gui_default_parameter is called for the | 925 | /* Init faces before gui_default_parameter is called for the |
| 899 | scroll-bar-width parameter because otherwise we end up in | 926 | scroll-bar-width parameter because otherwise we end up in |
| @@ -974,12 +1001,16 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, | |||
| 974 | "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); | 1001 | "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); |
| 975 | gui_default_parameter (f, parms, Qcursor_type, Qbox, | 1002 | gui_default_parameter (f, parms, Qcursor_type, Qbox, |
| 976 | "cursorType", "CursorType", RES_TYPE_SYMBOL); | 1003 | "cursorType", "CursorType", RES_TYPE_SYMBOL); |
| 1004 | /* Scroll bars are not supported on Android, as they are near | ||
| 1005 | useless. */ | ||
| 1006 | #if 0 | ||
| 977 | gui_default_parameter (f, parms, Qscroll_bar_width, Qnil, | 1007 | gui_default_parameter (f, parms, Qscroll_bar_width, Qnil, |
| 978 | "scrollBarWidth", "ScrollBarWidth", | 1008 | "scrollBarWidth", "ScrollBarWidth", |
| 979 | RES_TYPE_NUMBER); | 1009 | RES_TYPE_NUMBER); |
| 980 | gui_default_parameter (f, parms, Qscroll_bar_height, Qnil, | 1010 | gui_default_parameter (f, parms, Qscroll_bar_height, Qnil, |
| 981 | "scrollBarHeight", "ScrollBarHeight", | 1011 | "scrollBarHeight", "ScrollBarHeight", |
| 982 | RES_TYPE_NUMBER); | 1012 | RES_TYPE_NUMBER); |
| 1013 | #endif | ||
| 983 | gui_default_parameter (f, parms, Qalpha, Qnil, | 1014 | gui_default_parameter (f, parms, Qalpha, Qnil, |
| 984 | "alpha", "Alpha", RES_TYPE_NUMBER); | 1015 | "alpha", "Alpha", RES_TYPE_NUMBER); |
| 985 | gui_default_parameter (f, parms, Qalpha_background, Qnil, | 1016 | gui_default_parameter (f, parms, Qalpha_background, Qnil, |
| @@ -1009,8 +1040,9 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, | |||
| 1009 | 1040 | ||
| 1010 | /* Process fullscreen parameter here in the hope that normalizing a | 1041 | /* Process fullscreen parameter here in the hope that normalizing a |
| 1011 | fullheight/fullwidth frame will produce the size set by the last | 1042 | fullheight/fullwidth frame will produce the size set by the last |
| 1012 | adjust_frame_size call. */ | 1043 | adjust_frame_size call. Note that Android only supports the |
| 1013 | gui_default_parameter (f, parms, Qfullscreen, Qnil, | 1044 | `maximized' state. */ |
| 1045 | gui_default_parameter (f, parms, Qfullscreen, Qmaximized, | ||
| 1014 | "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); | 1046 | "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); |
| 1015 | 1047 | ||
| 1016 | /* When called from `x-create-frame-with-faces' visibility is | 1048 | /* When called from `x-create-frame-with-faces' visibility is |
| @@ -1661,6 +1693,391 @@ DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0, | |||
| 1661 | return result; | 1693 | return result; |
| 1662 | } | 1694 | } |
| 1663 | 1695 | ||
| 1696 | #ifndef ANDROID_STUBIFY | ||
| 1697 | |||
| 1698 | static void | ||
| 1699 | unwind_create_tip_frame (Lisp_Object frame) | ||
| 1700 | { | ||
| 1701 | Lisp_Object deleted; | ||
| 1702 | |||
| 1703 | deleted = unwind_create_frame (frame); | ||
| 1704 | if (EQ (deleted, Qt)) | ||
| 1705 | { | ||
| 1706 | tip_window = ANDROID_NONE; | ||
| 1707 | tip_frame = Qnil; | ||
| 1708 | } | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | static Lisp_Object | ||
| 1712 | android_create_tip_frame (struct android_display_info *dpyinfo, | ||
| 1713 | Lisp_Object parms) | ||
| 1714 | { | ||
| 1715 | struct frame *f; | ||
| 1716 | Lisp_Object frame; | ||
| 1717 | Lisp_Object name; | ||
| 1718 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 1719 | bool face_change_before = face_change; | ||
| 1720 | |||
| 1721 | if (!dpyinfo->terminal->name) | ||
| 1722 | error ("Terminal is not live, can't create new frames on it"); | ||
| 1723 | |||
| 1724 | parms = Fcopy_alist (parms); | ||
| 1725 | |||
| 1726 | /* Get the name of the frame to use for resource lookup. */ | ||
| 1727 | name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name", | ||
| 1728 | RES_TYPE_STRING); | ||
| 1729 | if (!STRINGP (name) | ||
| 1730 | && !BASE_EQ (name, Qunbound) | ||
| 1731 | && !NILP (name)) | ||
| 1732 | error ("Invalid frame name--not a string or nil"); | ||
| 1733 | |||
| 1734 | frame = Qnil; | ||
| 1735 | f = make_frame (false); | ||
| 1736 | f->wants_modeline = false; | ||
| 1737 | XSETFRAME (frame, f); | ||
| 1738 | record_unwind_protect (unwind_create_tip_frame, frame); | ||
| 1739 | |||
| 1740 | f->terminal = dpyinfo->terminal; | ||
| 1741 | |||
| 1742 | /* By setting the output method, we're essentially saying that | ||
| 1743 | the frame is live, as per FRAME_LIVE_P. If we get a signal | ||
| 1744 | from this point on, x_destroy_window might screw up reference | ||
| 1745 | counts etc. */ | ||
| 1746 | f->output_method = output_android; | ||
| 1747 | f->output_data.android = xzalloc (sizeof *f->output_data.android); | ||
| 1748 | FRAME_FONTSET (f) = -1; | ||
| 1749 | f->output_data.android->white_relief.pixel = -1; | ||
| 1750 | f->output_data.android->black_relief.pixel = -1; | ||
| 1751 | |||
| 1752 | f->tooltip = true; | ||
| 1753 | fset_icon_name (f, Qnil); | ||
| 1754 | FRAME_DISPLAY_INFO (f) = dpyinfo; | ||
| 1755 | f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; | ||
| 1756 | |||
| 1757 | /* These colors will be set anyway later, but it's important | ||
| 1758 | to get the color reference counts right, so initialize them! */ | ||
| 1759 | { | ||
| 1760 | Lisp_Object black; | ||
| 1761 | |||
| 1762 | /* Function android_decode_color can signal an error. Make sure | ||
| 1763 | to initialize color slots so that we won't try to free colors | ||
| 1764 | we haven't allocated. */ | ||
| 1765 | FRAME_FOREGROUND_PIXEL (f) = -1; | ||
| 1766 | FRAME_BACKGROUND_PIXEL (f) = -1; | ||
| 1767 | f->output_data.android->cursor_pixel = -1; | ||
| 1768 | f->output_data.android->cursor_foreground_pixel = -1; | ||
| 1769 | |||
| 1770 | black = build_string ("black"); | ||
| 1771 | FRAME_FOREGROUND_PIXEL (f) | ||
| 1772 | = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); | ||
| 1773 | FRAME_BACKGROUND_PIXEL (f) | ||
| 1774 | = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); | ||
| 1775 | f->output_data.android->cursor_pixel | ||
| 1776 | = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); | ||
| 1777 | f->output_data.android->cursor_foreground_pixel | ||
| 1778 | = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | /* Set the name; the functions to which we pass f expect the name to | ||
| 1782 | be set. */ | ||
| 1783 | if (BASE_EQ (name, Qunbound) || NILP (name)) | ||
| 1784 | f->explicit_name = false; | ||
| 1785 | else | ||
| 1786 | { | ||
| 1787 | fset_name (f, name); | ||
| 1788 | f->explicit_name = true; | ||
| 1789 | /* use the frame's title when getting resources for this frame. */ | ||
| 1790 | specbind (Qx_resource_name, name); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | register_font_driver (&androidfont_driver, f); | ||
| 1794 | register_font_driver (&android_sfntfont_driver, f); | ||
| 1795 | |||
| 1796 | image_cache_refcount | ||
| 1797 | = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0; | ||
| 1798 | #ifdef GLYPH_DEBUG | ||
| 1799 | dpyinfo_refcount = dpyinfo->reference_count; | ||
| 1800 | #endif /* GLYPH_DEBUG */ | ||
| 1801 | |||
| 1802 | gui_default_parameter (f, parms, Qfont_backend, Qnil, | ||
| 1803 | "fontBackend", "FontBackend", RES_TYPE_STRING); | ||
| 1804 | |||
| 1805 | /* Extract the window parameters from the supplied values that are | ||
| 1806 | needed to determine window geometry. */ | ||
| 1807 | android_default_font_parameter (f, parms); | ||
| 1808 | |||
| 1809 | gui_default_parameter (f, parms, Qborder_width, make_fixnum (0), | ||
| 1810 | "borderWidth", "BorderWidth", RES_TYPE_NUMBER); | ||
| 1811 | |||
| 1812 | /* This defaults to 1 in order to match xterm. We recognize either | ||
| 1813 | internalBorderWidth or internalBorder (which is what xterm calls | ||
| 1814 | it). */ | ||
| 1815 | if (NILP (Fassq (Qinternal_border_width, parms))) | ||
| 1816 | { | ||
| 1817 | Lisp_Object value; | ||
| 1818 | |||
| 1819 | value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width, | ||
| 1820 | "internalBorder", "internalBorder", | ||
| 1821 | RES_TYPE_NUMBER); | ||
| 1822 | if (! BASE_EQ (value, Qunbound)) | ||
| 1823 | parms = Fcons (Fcons (Qinternal_border_width, value), | ||
| 1824 | parms); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1), | ||
| 1828 | "internalBorderWidth", "internalBorderWidth", | ||
| 1829 | RES_TYPE_NUMBER); | ||
| 1830 | gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0), | ||
| 1831 | NULL, NULL, RES_TYPE_NUMBER); | ||
| 1832 | gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), | ||
| 1833 | NULL, NULL, RES_TYPE_NUMBER); | ||
| 1834 | |||
| 1835 | /* Also do the stuff which must be set before the window exists. */ | ||
| 1836 | gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), | ||
| 1837 | "foreground", "Foreground", RES_TYPE_STRING); | ||
| 1838 | gui_default_parameter (f, parms, Qbackground_color, build_string ("white"), | ||
| 1839 | "background", "Background", RES_TYPE_STRING); | ||
| 1840 | gui_default_parameter (f, parms, Qmouse_color, build_string ("black"), | ||
| 1841 | "pointerColor", "Foreground", RES_TYPE_STRING); | ||
| 1842 | gui_default_parameter (f, parms, Qcursor_color, build_string ("black"), | ||
| 1843 | "cursorColor", "Foreground", RES_TYPE_STRING); | ||
| 1844 | gui_default_parameter (f, parms, Qborder_color, build_string ("black"), | ||
| 1845 | "borderColor", "BorderColor", RES_TYPE_STRING); | ||
| 1846 | gui_default_parameter (f, parms, Qno_special_glyphs, Qnil, | ||
| 1847 | NULL, NULL, RES_TYPE_BOOLEAN); | ||
| 1848 | |||
| 1849 | { | ||
| 1850 | struct android_set_window_attributes attrs; | ||
| 1851 | unsigned long mask; | ||
| 1852 | |||
| 1853 | block_input (); | ||
| 1854 | mask = ANDROID_CW_OVERRIDE_REDIRECT; | ||
| 1855 | |||
| 1856 | attrs.override_redirect = true; | ||
| 1857 | tip_window | ||
| 1858 | = FRAME_ANDROID_WINDOW (f) | ||
| 1859 | = android_create_window (FRAME_DISPLAY_INFO (f)->root_window, | ||
| 1860 | /* x, y, width, height, value-mask, | ||
| 1861 | attrs. */ | ||
| 1862 | 0, 0, 1, 1, mask, &attrs); | ||
| 1863 | unblock_input (); | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | /* Init faces before gui_default_parameter is called for the | ||
| 1867 | scroll-bar-width parameter because otherwise we end up in | ||
| 1868 | init_iterator with a null face cache, which should not happen. */ | ||
| 1869 | init_frame_faces (f); | ||
| 1870 | |||
| 1871 | gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, | ||
| 1872 | "inhibitDoubleBuffering", "InhibitDoubleBuffering", | ||
| 1873 | RES_TYPE_BOOLEAN); | ||
| 1874 | |||
| 1875 | gui_figure_window_size (f, parms, false, false); | ||
| 1876 | |||
| 1877 | f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; | ||
| 1878 | |||
| 1879 | android_make_gc (f); | ||
| 1880 | |||
| 1881 | gui_default_parameter (f, parms, Qauto_raise, Qnil, | ||
| 1882 | "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); | ||
| 1883 | gui_default_parameter (f, parms, Qauto_lower, Qnil, | ||
| 1884 | "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); | ||
| 1885 | gui_default_parameter (f, parms, Qcursor_type, Qbox, | ||
| 1886 | "cursorType", "CursorType", RES_TYPE_SYMBOL); | ||
| 1887 | gui_default_parameter (f, parms, Qalpha, Qnil, | ||
| 1888 | "alpha", "Alpha", RES_TYPE_NUMBER); | ||
| 1889 | gui_default_parameter (f, parms, Qalpha_background, Qnil, | ||
| 1890 | "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); | ||
| 1891 | |||
| 1892 | /* Add `tooltip' frame parameter's default value. */ | ||
| 1893 | if (NILP (Fframe_parameter (frame, Qtooltip))) | ||
| 1894 | { | ||
| 1895 | AUTO_FRAME_ARG (arg, Qtooltip, Qt); | ||
| 1896 | Fmodify_frame_parameters (frame, arg); | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | /* FIXME - can this be done in a similar way to normal frames? | ||
| 1900 | https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */ | ||
| 1901 | |||
| 1902 | /* Set the `display-type' frame parameter before setting up faces. */ | ||
| 1903 | { | ||
| 1904 | Lisp_Object disptype; | ||
| 1905 | |||
| 1906 | disptype = Qcolor; | ||
| 1907 | |||
| 1908 | if (NILP (Fframe_parameter (frame, Qdisplay_type))) | ||
| 1909 | { | ||
| 1910 | AUTO_FRAME_ARG (arg, Qdisplay_type, disptype); | ||
| 1911 | Fmodify_frame_parameters (frame, arg); | ||
| 1912 | } | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | /* Set up faces after all frame parameters are known. This call | ||
| 1916 | also merges in face attributes specified for new frames. */ | ||
| 1917 | { | ||
| 1918 | Lisp_Object bg = Fframe_parameter (frame, Qbackground_color); | ||
| 1919 | |||
| 1920 | call2 (Qface_set_after_frame_default, frame, Qnil); | ||
| 1921 | |||
| 1922 | if (!EQ (bg, Fframe_parameter (frame, Qbackground_color))) | ||
| 1923 | { | ||
| 1924 | AUTO_FRAME_ARG (arg, Qbackground_color, bg); | ||
| 1925 | Fmodify_frame_parameters (frame, arg); | ||
| 1926 | } | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | f->no_split = true; | ||
| 1930 | |||
| 1931 | /* Now that the frame will be official, it counts as a reference to | ||
| 1932 | its display and terminal. */ | ||
| 1933 | f->terminal->reference_count++; | ||
| 1934 | |||
| 1935 | /* It is now ok to make the frame official even if we get an error | ||
| 1936 | below. And the frame needs to be on Vframe_list or making it | ||
| 1937 | visible won't work. */ | ||
| 1938 | Vframe_list = Fcons (frame, Vframe_list); | ||
| 1939 | f->can_set_window_size = true; | ||
| 1940 | adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), | ||
| 1941 | 0, true, Qtip_frame); | ||
| 1942 | |||
| 1943 | /* Setting attributes of faces of the tooltip frame from resources | ||
| 1944 | and similar will set face_change, which leads to the clearing of | ||
| 1945 | all current matrices. Since this isn't necessary here, avoid it | ||
| 1946 | by resetting face_change to the value it had before we created | ||
| 1947 | the tip frame. */ | ||
| 1948 | face_change = face_change_before; | ||
| 1949 | |||
| 1950 | /* Discard the unwind_protect. */ | ||
| 1951 | return unbind_to (count, frame); | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | static Lisp_Object | ||
| 1955 | android_hide_tip (bool delete) | ||
| 1956 | { | ||
| 1957 | if (!NILP (tip_timer)) | ||
| 1958 | { | ||
| 1959 | call1 (Qcancel_timer, tip_timer); | ||
| 1960 | tip_timer = Qnil; | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | if (NILP (tip_frame) | ||
| 1964 | || (!delete | ||
| 1965 | && !NILP (tip_frame) | ||
| 1966 | && FRAME_LIVE_P (XFRAME (tip_frame)) | ||
| 1967 | && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) | ||
| 1968 | return Qnil; | ||
| 1969 | else | ||
| 1970 | { | ||
| 1971 | Lisp_Object was_open = Qnil; | ||
| 1972 | |||
| 1973 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 1974 | specbind (Qinhibit_redisplay, Qt); | ||
| 1975 | specbind (Qinhibit_quit, Qt); | ||
| 1976 | |||
| 1977 | if (!NILP (tip_frame)) | ||
| 1978 | { | ||
| 1979 | struct frame *f = XFRAME (tip_frame); | ||
| 1980 | |||
| 1981 | if (FRAME_LIVE_P (f)) | ||
| 1982 | { | ||
| 1983 | if (delete) | ||
| 1984 | { | ||
| 1985 | delete_frame (tip_frame, Qnil); | ||
| 1986 | tip_frame = Qnil; | ||
| 1987 | } | ||
| 1988 | else | ||
| 1989 | android_make_frame_invisible (XFRAME (tip_frame)); | ||
| 1990 | |||
| 1991 | was_open = Qt; | ||
| 1992 | } | ||
| 1993 | else | ||
| 1994 | tip_frame = Qnil; | ||
| 1995 | } | ||
| 1996 | else | ||
| 1997 | tip_frame = Qnil; | ||
| 1998 | |||
| 1999 | return unbind_to (count, was_open); | ||
| 2000 | } | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | static void | ||
| 2004 | compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, | ||
| 2005 | Lisp_Object dy, int width, int height, int *root_x, | ||
| 2006 | int *root_y) | ||
| 2007 | { | ||
| 2008 | Lisp_Object left, top, right, bottom; | ||
| 2009 | int min_x, min_y, max_x, max_y = -1; | ||
| 2010 | android_window window; | ||
| 2011 | struct frame *mouse_frame; | ||
| 2012 | |||
| 2013 | /* Initialize these values in case there is no mouse frame. */ | ||
| 2014 | *root_x = 0; | ||
| 2015 | *root_y = 0; | ||
| 2016 | |||
| 2017 | /* User-specified position? */ | ||
| 2018 | left = CDR (Fassq (Qleft, parms)); | ||
| 2019 | top = CDR (Fassq (Qtop, parms)); | ||
| 2020 | right = CDR (Fassq (Qright, parms)); | ||
| 2021 | bottom = CDR (Fassq (Qbottom, parms)); | ||
| 2022 | |||
| 2023 | /* Move the tooltip window where the mouse pointer was last seen. | ||
| 2024 | Resize and show it. */ | ||
| 2025 | if ((!FIXNUMP (left) && !FIXNUMP (right)) | ||
| 2026 | || (!FIXNUMP (top) && !FIXNUMP (bottom))) | ||
| 2027 | { | ||
| 2028 | if (x_display_list->last_mouse_motion_frame) | ||
| 2029 | { | ||
| 2030 | *root_x = x_display_list->last_mouse_motion_x; | ||
| 2031 | *root_y = x_display_list->last_mouse_motion_y; | ||
| 2032 | mouse_frame = x_display_list->last_mouse_motion_frame; | ||
| 2033 | window = FRAME_ANDROID_WINDOW (mouse_frame); | ||
| 2034 | |||
| 2035 | /* Translate the coordinates to the screen. */ | ||
| 2036 | android_translate_coordinates (window, *root_x, *root_y, | ||
| 2037 | root_x, root_y); | ||
| 2038 | } | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | min_x = 0; | ||
| 2042 | min_y = 0; | ||
| 2043 | max_x = android_get_screen_width (); | ||
| 2044 | max_y = android_get_screen_height (); | ||
| 2045 | |||
| 2046 | if (FIXNUMP (top)) | ||
| 2047 | *root_y = XFIXNUM (top); | ||
| 2048 | else if (FIXNUMP (bottom)) | ||
| 2049 | *root_y = XFIXNUM (bottom) - height; | ||
| 2050 | else if (*root_y + XFIXNUM (dy) <= min_y) | ||
| 2051 | *root_y = min_y; /* Can happen for negative dy */ | ||
| 2052 | else if (*root_y + XFIXNUM (dy) + height <= max_y) | ||
| 2053 | /* It fits below the pointer */ | ||
| 2054 | *root_y += XFIXNUM (dy); | ||
| 2055 | else if (height + XFIXNUM (dy) + min_y <= *root_y) | ||
| 2056 | /* It fits above the pointer. */ | ||
| 2057 | *root_y -= height + XFIXNUM (dy); | ||
| 2058 | else | ||
| 2059 | /* Put it on the top. */ | ||
| 2060 | *root_y = min_y; | ||
| 2061 | |||
| 2062 | if (FIXNUMP (left)) | ||
| 2063 | *root_x = XFIXNUM (left); | ||
| 2064 | else if (FIXNUMP (right)) | ||
| 2065 | *root_x = XFIXNUM (right) - width; | ||
| 2066 | else if (*root_x + XFIXNUM (dx) <= min_x) | ||
| 2067 | *root_x = 0; /* Can happen for negative dx */ | ||
| 2068 | else if (*root_x + XFIXNUM (dx) + width <= max_x) | ||
| 2069 | /* It fits to the right of the pointer. */ | ||
| 2070 | *root_x += XFIXNUM (dx); | ||
| 2071 | else if (width + XFIXNUM (dx) + min_x <= *root_x) | ||
| 2072 | /* It fits to the left of the pointer. */ | ||
| 2073 | *root_x -= width + XFIXNUM (dx); | ||
| 2074 | else | ||
| 2075 | /* Put it left justified on the screen -- it ought to fit that way. */ | ||
| 2076 | *root_x = min_x; | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | #endif | ||
| 2080 | |||
| 1664 | DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, | 2081 | DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, |
| 1665 | doc: /* SKIP: real doc in xfns.c. */) | 2082 | doc: /* SKIP: real doc in xfns.c. */) |
| 1666 | (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, | 2083 | (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, |
| @@ -1670,8 +2087,214 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, | |||
| 1670 | error ("Android cross-compilation stub called!"); | 2087 | error ("Android cross-compilation stub called!"); |
| 1671 | return Qnil; | 2088 | return Qnil; |
| 1672 | #else | 2089 | #else |
| 1673 | /* TODO tooltips */ | 2090 | struct frame *f, *tip_f; |
| 1674 | return Qnil; | 2091 | struct window *w; |
| 2092 | int root_x, root_y; | ||
| 2093 | struct buffer *old_buffer; | ||
| 2094 | struct text_pos pos; | ||
| 2095 | int width, height; | ||
| 2096 | int old_windows_or_buffers_changed = windows_or_buffers_changed; | ||
| 2097 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 2098 | Lisp_Object window, size, tip_buf; | ||
| 2099 | bool displayed; | ||
| 2100 | #ifdef ENABLE_CHECKING | ||
| 2101 | struct glyph_row *row, *end; | ||
| 2102 | #endif | ||
| 2103 | AUTO_STRING (tip, " *tip*"); | ||
| 2104 | |||
| 2105 | specbind (Qinhibit_redisplay, Qt); | ||
| 2106 | |||
| 2107 | CHECK_STRING (string); | ||
| 2108 | if (SCHARS (string) == 0) | ||
| 2109 | string = make_unibyte_string (" ", 1); | ||
| 2110 | |||
| 2111 | if (NILP (frame)) | ||
| 2112 | frame = selected_frame; | ||
| 2113 | f = decode_window_system_frame (frame); | ||
| 2114 | |||
| 2115 | if (NILP (timeout)) | ||
| 2116 | timeout = Vx_show_tooltip_timeout; | ||
| 2117 | CHECK_FIXNAT (timeout); | ||
| 2118 | |||
| 2119 | if (NILP (dx)) | ||
| 2120 | dx = make_fixnum (5); | ||
| 2121 | else | ||
| 2122 | CHECK_FIXNUM (dx); | ||
| 2123 | |||
| 2124 | if (NILP (dy)) | ||
| 2125 | dy = make_fixnum (-10); | ||
| 2126 | else | ||
| 2127 | CHECK_FIXNUM (dy); | ||
| 2128 | |||
| 2129 | tip_dx = dx; | ||
| 2130 | tip_dy = dy; | ||
| 2131 | |||
| 2132 | if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) | ||
| 2133 | { | ||
| 2134 | if (FRAME_VISIBLE_P (XFRAME (tip_frame)) | ||
| 2135 | && !NILP (Fequal_including_properties (tip_last_string, | ||
| 2136 | string)) | ||
| 2137 | && !NILP (Fequal (tip_last_parms, parms))) | ||
| 2138 | { | ||
| 2139 | /* Only DX and DY have changed. */ | ||
| 2140 | tip_f = XFRAME (tip_frame); | ||
| 2141 | if (!NILP (tip_timer)) | ||
| 2142 | { | ||
| 2143 | call1 (Qcancel_timer, tip_timer); | ||
| 2144 | tip_timer = Qnil; | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | block_input (); | ||
| 2148 | compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f), | ||
| 2149 | FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y); | ||
| 2150 | android_move_window (FRAME_ANDROID_WINDOW (tip_f), | ||
| 2151 | root_x, root_y); | ||
| 2152 | unblock_input (); | ||
| 2153 | |||
| 2154 | goto start_timer; | ||
| 2155 | } | ||
| 2156 | else | ||
| 2157 | android_hide_tip (true); | ||
| 2158 | } | ||
| 2159 | else | ||
| 2160 | android_hide_tip (true); | ||
| 2161 | |||
| 2162 | tip_last_frame = frame; | ||
| 2163 | tip_last_string = string; | ||
| 2164 | tip_last_parms = parms; | ||
| 2165 | |||
| 2166 | if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) | ||
| 2167 | { | ||
| 2168 | /* Add default values to frame parameters. */ | ||
| 2169 | if (NILP (Fassq (Qname, parms))) | ||
| 2170 | parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); | ||
| 2171 | if (NILP (Fassq (Qinternal_border_width, parms))) | ||
| 2172 | parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), | ||
| 2173 | parms); | ||
| 2174 | if (NILP (Fassq (Qborder_width, parms))) | ||
| 2175 | parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms); | ||
| 2176 | if (NILP (Fassq (Qborder_color, parms))) | ||
| 2177 | parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), | ||
| 2178 | parms); | ||
| 2179 | if (NILP (Fassq (Qbackground_color, parms))) | ||
| 2180 | parms = Fcons (Fcons (Qbackground_color, | ||
| 2181 | build_string ("lightyellow")), | ||
| 2182 | parms); | ||
| 2183 | |||
| 2184 | /* Create a frame for the tooltip, and record it in the global | ||
| 2185 | variable tip_frame. */ | ||
| 2186 | if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f), | ||
| 2187 | parms))) | ||
| 2188 | /* Creating the tip frame failed. */ | ||
| 2189 | return unbind_to (count, Qnil); | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | tip_f = XFRAME (tip_frame); | ||
| 2193 | window = FRAME_ROOT_WINDOW (tip_f); | ||
| 2194 | tip_buf = Fget_buffer_create (tip, Qnil); | ||
| 2195 | /* We will mark the tip window a "pseudo-window" below, and such | ||
| 2196 | windows cannot have display margins. */ | ||
| 2197 | bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); | ||
| 2198 | bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); | ||
| 2199 | set_window_buffer (window, tip_buf, false, false); | ||
| 2200 | w = XWINDOW (window); | ||
| 2201 | w->pseudo_window_p = true; | ||
| 2202 | /* Try to avoid that `other-window' select us (Bug#47207). */ | ||
| 2203 | Fset_window_parameter (window, Qno_other_window, Qt); | ||
| 2204 | |||
| 2205 | /* Set up the frame's root window. Note: The following code does not | ||
| 2206 | try to size the window or its frame correctly. Its only purpose is | ||
| 2207 | to make the subsequent text size calculations work. The right | ||
| 2208 | sizes should get installed when the toolkit gets back to us. */ | ||
| 2209 | w->left_col = 0; | ||
| 2210 | w->top_line = 0; | ||
| 2211 | w->pixel_left = 0; | ||
| 2212 | w->pixel_top = 0; | ||
| 2213 | |||
| 2214 | if (CONSP (Vx_max_tooltip_size) | ||
| 2215 | && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX) | ||
| 2216 | && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) | ||
| 2217 | { | ||
| 2218 | w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size)); | ||
| 2219 | w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size)); | ||
| 2220 | } | ||
| 2221 | else | ||
| 2222 | { | ||
| 2223 | w->total_cols = 80; | ||
| 2224 | w->total_lines = 40; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f); | ||
| 2228 | w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f); | ||
| 2229 | FRAME_TOTAL_COLS (tip_f) = w->total_cols; | ||
| 2230 | adjust_frame_glyphs (tip_f); | ||
| 2231 | |||
| 2232 | /* Insert STRING into root window's buffer and fit the frame to the | ||
| 2233 | buffer. */ | ||
| 2234 | specpdl_ref count_1 = SPECPDL_INDEX (); | ||
| 2235 | old_buffer = current_buffer; | ||
| 2236 | set_buffer_internal_1 (XBUFFER (w->contents)); | ||
| 2237 | bset_truncate_lines (current_buffer, Qnil); | ||
| 2238 | specbind (Qinhibit_read_only, Qt); | ||
| 2239 | specbind (Qinhibit_modification_hooks, Qt); | ||
| 2240 | specbind (Qinhibit_point_motion_hooks, Qt); | ||
| 2241 | Ferase_buffer (); | ||
| 2242 | Finsert (1, &string); | ||
| 2243 | clear_glyph_matrix (w->desired_matrix); | ||
| 2244 | clear_glyph_matrix (w->current_matrix); | ||
| 2245 | SET_TEXT_POS (pos, BEGV, BEGV_BYTE); | ||
| 2246 | displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); | ||
| 2247 | |||
| 2248 | if (!displayed && NILP (Vx_max_tooltip_size)) | ||
| 2249 | { | ||
| 2250 | #ifdef ENABLE_CHECKING | ||
| 2251 | row = w->desired_matrix->rows; | ||
| 2252 | end = w->desired_matrix->rows + w->desired_matrix->nrows; | ||
| 2253 | |||
| 2254 | while (row < end) | ||
| 2255 | { | ||
| 2256 | if (!row->displays_text_p | ||
| 2257 | || row->ends_at_zv_p) | ||
| 2258 | break; | ||
| 2259 | ++row; | ||
| 2260 | } | ||
| 2261 | |||
| 2262 | eassert (row < end && row->ends_at_zv_p); | ||
| 2263 | #endif | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | /* Calculate size of tooltip window. */ | ||
| 2267 | size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, | ||
| 2268 | make_fixnum (w->pixel_height), Qnil, | ||
| 2269 | Qnil); | ||
| 2270 | /* Add the frame's internal border to calculated size. */ | ||
| 2271 | width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); | ||
| 2272 | height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); | ||
| 2273 | |||
| 2274 | /* Calculate position of tooltip frame. */ | ||
| 2275 | compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); | ||
| 2276 | |||
| 2277 | /* Show tooltip frame. */ | ||
| 2278 | block_input (); | ||
| 2279 | android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f), | ||
| 2280 | root_x, root_y, width, | ||
| 2281 | height); | ||
| 2282 | android_map_raised (FRAME_ANDROID_WINDOW (tip_f)); | ||
| 2283 | unblock_input (); | ||
| 2284 | |||
| 2285 | w->must_be_updated_p = true; | ||
| 2286 | update_single_window (w); | ||
| 2287 | flush_frame (tip_f); | ||
| 2288 | set_buffer_internal_1 (old_buffer); | ||
| 2289 | unbind_to (count_1, Qnil); | ||
| 2290 | windows_or_buffers_changed = old_windows_or_buffers_changed; | ||
| 2291 | |||
| 2292 | start_timer: | ||
| 2293 | /* Let the tip disappear after timeout seconds. */ | ||
| 2294 | tip_timer = call3 (Qrun_at_time, timeout, Qnil, | ||
| 2295 | Qx_hide_tip); | ||
| 2296 | |||
| 2297 | return unbind_to (count, Qnil); | ||
| 1675 | #endif | 2298 | #endif |
| 1676 | } | 2299 | } |
| 1677 | 2300 | ||
| @@ -1683,7 +2306,7 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, | |||
| 1683 | error ("Android cross-compilation stub called!"); | 2306 | error ("Android cross-compilation stub called!"); |
| 1684 | return Qnil; | 2307 | return Qnil; |
| 1685 | #else | 2308 | #else |
| 1686 | return Qnil; | 2309 | return android_hide_tip (true); |
| 1687 | #endif | 2310 | #endif |
| 1688 | } | 2311 | } |
| 1689 | 2312 | ||
| @@ -2112,6 +2735,17 @@ syms_of_androidfns (void) | |||
| 2112 | doc: /* SKIP: real doc in xfns.c. */); | 2735 | doc: /* SKIP: real doc in xfns.c. */); |
| 2113 | Vx_cursor_fore_pixel = Qnil; | 2736 | Vx_cursor_fore_pixel = Qnil; |
| 2114 | 2737 | ||
| 2738 | /* Used by Fx_show_tip. */ | ||
| 2739 | DEFSYM (Qrun_at_time, "run-at-time"); | ||
| 2740 | DEFSYM (Qx_hide_tip, "x-hide-tip"); | ||
| 2741 | DEFSYM (Qcancel_timer, "cancel-timer"); | ||
| 2742 | DEFSYM (Qassq_delete_all, "assq-delete-all"); | ||
| 2743 | DEFSYM (Qcolor, "color"); | ||
| 2744 | |||
| 2745 | DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size, | ||
| 2746 | doc: /* SKIP: real doc in xfns.c. */); | ||
| 2747 | Vx_max_tooltip_size = Qnil; | ||
| 2748 | |||
| 2115 | /* Functions defined. */ | 2749 | /* Functions defined. */ |
| 2116 | defsubr (&Sx_create_frame); | 2750 | defsubr (&Sx_create_frame); |
| 2117 | defsubr (&Sxw_color_defined_p); | 2751 | defsubr (&Sxw_color_defined_p); |
| @@ -2139,4 +2773,21 @@ syms_of_androidfns (void) | |||
| 2139 | defsubr (&Sx_show_tip); | 2773 | defsubr (&Sx_show_tip); |
| 2140 | defsubr (&Sx_hide_tip); | 2774 | defsubr (&Sx_hide_tip); |
| 2141 | defsubr (&Sandroid_detect_mouse); | 2775 | defsubr (&Sandroid_detect_mouse); |
| 2776 | |||
| 2777 | #ifndef ANDROID_STUBIFY | ||
| 2778 | tip_timer = Qnil; | ||
| 2779 | staticpro (&tip_timer); | ||
| 2780 | tip_frame = Qnil; | ||
| 2781 | staticpro (&tip_frame); | ||
| 2782 | tip_last_frame = Qnil; | ||
| 2783 | staticpro (&tip_last_frame); | ||
| 2784 | tip_last_string = Qnil; | ||
| 2785 | staticpro (&tip_last_string); | ||
| 2786 | tip_last_parms = Qnil; | ||
| 2787 | staticpro (&tip_last_parms); | ||
| 2788 | tip_dx = Qnil; | ||
| 2789 | staticpro (&tip_dx); | ||
| 2790 | tip_dy = Qnil; | ||
| 2791 | staticpro (&tip_dy); | ||
| 2792 | #endif | ||
| 2142 | } | 2793 | } |
diff --git a/src/androidgui.h b/src/androidgui.h index 422e72408c7..8450a1f637b 100644 --- a/src/androidgui.h +++ b/src/androidgui.h | |||
| @@ -80,13 +80,18 @@ enum android_fill_style | |||
| 80 | 80 | ||
| 81 | enum android_window_value_mask | 81 | enum android_window_value_mask |
| 82 | { | 82 | { |
| 83 | ANDROID_CW_BACK_PIXEL = (1 << 1), | 83 | ANDROID_CW_BACK_PIXEL = (1 << 1), |
| 84 | ANDROID_CW_OVERRIDE_REDIRECT = (1 << 2), | ||
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | struct android_set_window_attributes | 87 | struct android_set_window_attributes |
| 87 | { | 88 | { |
| 88 | /* The background pixel. */ | 89 | /* The background pixel. */ |
| 89 | unsigned long background_pixel; | 90 | unsigned long background_pixel; |
| 91 | |||
| 92 | /* Whether or not the window is override redirect. This cannot be | ||
| 93 | set after creation on Android. */ | ||
| 94 | bool override_redirect; | ||
| 90 | }; | 95 | }; |
| 91 | 96 | ||
| 92 | struct android_gc_values | 97 | struct android_gc_values |
| @@ -260,7 +265,7 @@ struct android_key_event | |||
| 260 | ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \ | 265 | ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \ |
| 261 | || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \ | 266 | || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \ |
| 262 | || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \ | 267 | || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \ |
| 263 | || (key) == 63) | 268 | || (key) == 63 || (key) == 115) |
| 264 | 269 | ||
| 265 | struct android_configure_event | 270 | struct android_configure_event |
| 266 | { | 271 | { |
| @@ -478,6 +483,11 @@ extern int android_query_tree (android_window, android_window *, | |||
| 478 | extern void android_get_geometry (android_window, android_window *, | 483 | extern void android_get_geometry (android_window, android_window *, |
| 479 | int *, int *, unsigned int *, | 484 | int *, int *, unsigned int *, |
| 480 | unsigned int *, unsigned int *); | 485 | unsigned int *, unsigned int *); |
| 486 | extern void android_move_resize_window (android_window, int, int, | ||
| 487 | unsigned int, unsigned int); | ||
| 488 | extern void android_map_raised (android_window); | ||
| 489 | extern void android_translate_coordinates (android_window, int, | ||
| 490 | int, int *, int *); | ||
| 481 | 491 | ||
| 482 | #endif | 492 | #endif |
| 483 | 493 | ||
diff --git a/src/androidmenu.c b/src/androidmenu.c index 6f6e4ca8de9..0f0c6f4ef1f 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c | |||
| @@ -21,6 +21,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 21 | 21 | ||
| 22 | #include "lisp.h" | 22 | #include "lisp.h" |
| 23 | #include "androidterm.h" | 23 | #include "androidterm.h" |
| 24 | #include "android.h" | ||
| 25 | #include "blockinput.h" | ||
| 26 | #include "keyboard.h" | ||
| 27 | #include "menu.h" | ||
| 24 | 28 | ||
| 25 | #ifndef ANDROID_STUBIFY | 29 | #ifndef ANDROID_STUBIFY |
| 26 | 30 | ||
| @@ -35,4 +39,293 @@ popup_activated (void) | |||
| 35 | return popup_activated_flag; | 39 | return popup_activated_flag; |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 42 | |||
| 43 | |||
| 44 | /* Toolkit menu implementation. */ | ||
| 45 | |||
| 46 | /* Structure describing the EmacsContextMenu class. */ | ||
| 47 | |||
| 48 | struct android_emacs_context_menu | ||
| 49 | { | ||
| 50 | jclass class; | ||
| 51 | jmethodID create_context_menu; | ||
| 52 | jmethodID add_item; | ||
| 53 | jmethodID add_submenu; | ||
| 54 | jmethodID add_pane; | ||
| 55 | jmethodID parent; | ||
| 56 | jmethodID display; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* Identifiers associated with the EmacsContextMenu class. */ | ||
| 60 | static struct android_emacs_context_menu menu_class; | ||
| 61 | |||
| 62 | static void | ||
| 63 | android_init_emacs_context_menu (void) | ||
| 64 | { | ||
| 65 | jclass old; | ||
| 66 | |||
| 67 | menu_class.class | ||
| 68 | = (*android_java_env)->FindClass (android_java_env, | ||
| 69 | "org/gnu/emacs/EmacsContextMenu"); | ||
| 70 | eassert (menu_class.class); | ||
| 71 | |||
| 72 | old = menu_class.class; | ||
| 73 | menu_class.class | ||
| 74 | = (jclass) (*android_java_env)->NewGlobalRef (android_java_env, | ||
| 75 | (jobject) old); | ||
| 76 | ANDROID_DELETE_LOCAL_REF (old); | ||
| 77 | |||
| 78 | if (!menu_class.class) | ||
| 79 | emacs_abort (); | ||
| 80 | |||
| 81 | #define FIND_METHOD(c_name, name, signature) \ | ||
| 82 | menu_class.c_name \ | ||
| 83 | = (*android_java_env)->GetMethodID (android_java_env, \ | ||
| 84 | menu_class.class, \ | ||
| 85 | name, signature); \ | ||
| 86 | eassert (menu_class.c_name); | ||
| 87 | |||
| 88 | #define FIND_METHOD_STATIC(c_name, name, signature) \ | ||
| 89 | menu_class.c_name \ | ||
| 90 | = (*android_java_env)->GetStaticMethodID (android_java_env, \ | ||
| 91 | menu_class.class, \ | ||
| 92 | name, signature); \ | ||
| 93 | eassert (menu_class.c_name); | ||
| 94 | |||
| 95 | FIND_METHOD_STATIC (create_context_menu, "createContextMenu", | ||
| 96 | "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); | ||
| 97 | |||
| 98 | FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;Z)V"); | ||
| 99 | FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" | ||
| 100 | "Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); | ||
| 101 | FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); | ||
| 102 | FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;"); | ||
| 103 | FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;II)Z"); | ||
| 104 | |||
| 105 | #undef FIND_METHOD | ||
| 106 | #undef FIND_METHOD_STATIC | ||
| 107 | } | ||
| 108 | |||
| 109 | static void | ||
| 110 | android_unwind_local_frame (void) | ||
| 111 | { | ||
| 112 | (*android_java_env)->PopLocalFrame (android_java_env, NULL); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Push a local reference frame to the JVM stack and record it on the | ||
| 116 | specpdl. Release local references created within that frame when | ||
| 117 | the specpdl is unwound past where it is after returning. */ | ||
| 118 | |||
| 119 | static void | ||
| 120 | android_push_local_frame (void) | ||
| 121 | { | ||
| 122 | int rc; | ||
| 123 | |||
| 124 | rc = (*android_java_env)->PushLocalFrame (android_java_env, 30); | ||
| 125 | |||
| 126 | /* This means the JVM ran out of memory. */ | ||
| 127 | if (rc < 1) | ||
| 128 | android_exception_check (); | ||
| 129 | |||
| 130 | record_unwind_protect_void (android_unwind_local_frame); | ||
| 131 | } | ||
| 132 | |||
| 133 | Lisp_Object | ||
| 134 | android_menu_show (struct frame *f, int x, int y, int menuflags, | ||
| 135 | Lisp_Object title, const char **error_name) | ||
| 136 | { | ||
| 137 | jobject context_menu, current_context_menu; | ||
| 138 | jobject title_string, temp; | ||
| 139 | size_t i; | ||
| 140 | Lisp_Object pane_name, prefix; | ||
| 141 | const char *pane_string; | ||
| 142 | specpdl_ref count, count1; | ||
| 143 | Lisp_Object item_name, enable, def; | ||
| 144 | jmethodID method; | ||
| 145 | jobject store; | ||
| 146 | bool rc; | ||
| 147 | jobject window; | ||
| 148 | |||
| 149 | count = SPECPDL_INDEX (); | ||
| 150 | |||
| 151 | block_input (); | ||
| 152 | |||
| 153 | /* Push the first local frame. */ | ||
| 154 | android_push_local_frame (); | ||
| 155 | |||
| 156 | /* Push the first local frame for the context menu. */ | ||
| 157 | title_string = (!NILP (title) | ||
| 158 | ? (jobject) android_build_string (title) | ||
| 159 | : NULL); | ||
| 160 | method = menu_class.create_context_menu; | ||
| 161 | current_context_menu = context_menu | ||
| 162 | = (*android_java_env)->CallStaticObjectMethod (android_java_env, | ||
| 163 | menu_class.class, | ||
| 164 | method, | ||
| 165 | title_string); | ||
| 166 | |||
| 167 | if (title_string) | ||
| 168 | ANDROID_DELETE_LOCAL_REF (title_string); | ||
| 169 | |||
| 170 | /* Push the second local frame for temporaries. */ | ||
| 171 | count1 = SPECPDL_INDEX (); | ||
| 172 | android_push_local_frame (); | ||
| 173 | |||
| 174 | /* Iterate over the menu. */ | ||
| 175 | i = 0; | ||
| 176 | |||
| 177 | while (i < menu_items_used) | ||
| 178 | { | ||
| 179 | if (NILP (AREF (menu_items, i))) | ||
| 180 | { | ||
| 181 | /* This is the start of a new submenu. However, it can be | ||
| 182 | ignored here. */ | ||
| 183 | i += 1; | ||
| 184 | } | ||
| 185 | else if (EQ (AREF (menu_items, i), Qlambda)) | ||
| 186 | { | ||
| 187 | /* This is the end of a submenu. Go back to the previous | ||
| 188 | context menu. */ | ||
| 189 | store = current_context_menu; | ||
| 190 | current_context_menu | ||
| 191 | = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 192 | current_context_menu, | ||
| 193 | menu_class.parent); | ||
| 194 | android_exception_check (); | ||
| 195 | |||
| 196 | if (store != context_menu) | ||
| 197 | ANDROID_DELETE_LOCAL_REF (store); | ||
| 198 | i += 1; | ||
| 199 | |||
| 200 | eassert (current_context_menu); | ||
| 201 | } | ||
| 202 | else if (EQ (AREF (menu_items, i), Qquote)) | ||
| 203 | i += 1; | ||
| 204 | else if (EQ (AREF (menu_items, i), Qt)) | ||
| 205 | { | ||
| 206 | /* This is a new pane. Switch back to the topmost context | ||
| 207 | menu. */ | ||
| 208 | if (current_context_menu != context_menu) | ||
| 209 | ANDROID_DELETE_LOCAL_REF (current_context_menu); | ||
| 210 | current_context_menu = context_menu; | ||
| 211 | |||
| 212 | /* Now figure out the title of this pane. */ | ||
| 213 | pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); | ||
| 214 | prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); | ||
| 215 | pane_string = (NILP (pane_name) | ||
| 216 | ? "" : SSDATA (pane_name)); | ||
| 217 | if ((menuflags & MENU_KEYMAPS) && !NILP (prefix)) | ||
| 218 | pane_string++; | ||
| 219 | |||
| 220 | /* Add the pane. */ | ||
| 221 | temp = (*android_java_env)->NewStringUTF (android_java_env, | ||
| 222 | pane_string); | ||
| 223 | android_exception_check (); | ||
| 224 | |||
| 225 | (*android_java_env)->CallVoidMethod (android_java_env, | ||
| 226 | current_context_menu, | ||
| 227 | menu_class.add_pane, | ||
| 228 | temp); | ||
| 229 | android_exception_check (); | ||
| 230 | ANDROID_DELETE_LOCAL_REF (temp); | ||
| 231 | |||
| 232 | i += MENU_ITEMS_PANE_LENGTH; | ||
| 233 | } | ||
| 234 | else | ||
| 235 | { | ||
| 236 | item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); | ||
| 237 | enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); | ||
| 238 | def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); | ||
| 239 | |||
| 240 | /* This is an actual menu item (or submenu). Add it to the | ||
| 241 | menu. */ | ||
| 242 | |||
| 243 | if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used && | ||
| 244 | NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH))) | ||
| 245 | { | ||
| 246 | /* This is a submenu. Add it. */ | ||
| 247 | title_string = (!NILP (item_name) | ||
| 248 | ? android_build_string (item_name) | ||
| 249 | : NULL); | ||
| 250 | store = current_context_menu; | ||
| 251 | current_context_menu | ||
| 252 | = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 253 | current_context_menu, | ||
| 254 | menu_class.add_submenu, | ||
| 255 | title_string); | ||
| 256 | android_exception_check (); | ||
| 257 | |||
| 258 | if (store != context_menu) | ||
| 259 | ANDROID_DELETE_LOCAL_REF (store); | ||
| 260 | |||
| 261 | if (title_string) | ||
| 262 | ANDROID_DELETE_LOCAL_REF (title_string); | ||
| 263 | } | ||
| 264 | else if (NILP (def) && menu_separator_name_p (SSDATA (item_name))) | ||
| 265 | /* Ignore this separator item. */ | ||
| 266 | ; | ||
| 267 | else | ||
| 268 | { | ||
| 269 | /* Add this menu item with the appropriate state. */ | ||
| 270 | |||
| 271 | title_string = (!NILP (item_name) | ||
| 272 | ? android_build_string (item_name) | ||
| 273 | : NULL); | ||
| 274 | (*android_java_env)->CallVoidMethod (android_java_env, | ||
| 275 | current_context_menu, | ||
| 276 | menu_class.add_item, | ||
| 277 | (jint) 1, | ||
| 278 | title_string, | ||
| 279 | (jboolean) !NILP (enable)); | ||
| 280 | android_exception_check (); | ||
| 281 | |||
| 282 | if (title_string) | ||
| 283 | ANDROID_DELETE_LOCAL_REF (title_string); | ||
| 284 | } | ||
| 285 | |||
| 286 | i += MENU_ITEMS_ITEM_LENGTH; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /* The menu has now been built. Pop the second local frame. */ | ||
| 291 | unbind_to (count1, Qnil); | ||
| 292 | |||
| 293 | /* Now, display the context menu. */ | ||
| 294 | window = android_resolve_handle (FRAME_ANDROID_WINDOW (f), | ||
| 295 | ANDROID_HANDLE_WINDOW); | ||
| 296 | rc = (*android_java_env)->CallBooleanMethod (android_java_env, | ||
| 297 | context_menu, | ||
| 298 | window, (jint) x, | ||
| 299 | (jint) y); | ||
| 300 | android_exception_check (); | ||
| 301 | |||
| 302 | if (!rc) | ||
| 303 | /* This means displaying the menu failed. */ | ||
| 304 | goto finish; | ||
| 305 | |||
| 306 | #if 0 | ||
| 307 | record_unwind_protect_ptr (android_dismiss_menu, &context_menu); | ||
| 308 | |||
| 309 | /* Otherwise, loop waiting for the menu event to arrive. */ | ||
| 310 | android_process_events_for_menu (&id); | ||
| 311 | |||
| 312 | if (!id) | ||
| 313 | /* This means no menu item was selected. */ | ||
| 314 | goto finish; | ||
| 315 | |||
| 316 | #endif | ||
| 317 | |||
| 318 | finish: | ||
| 319 | unblock_input (); | ||
| 320 | return unbind_to (count, Qnil); | ||
| 321 | } | ||
| 322 | |||
| 323 | #endif | ||
| 324 | |||
| 325 | void | ||
| 326 | init_androidmenu (void) | ||
| 327 | { | ||
| 328 | #ifndef ANDROID_STUBIFY | ||
| 329 | android_init_emacs_context_menu (); | ||
| 38 | #endif | 330 | #endif |
| 331 | } | ||
diff --git a/src/androidterm.c b/src/androidterm.c index a8325312498..002d39af707 100644 --- a/src/androidterm.c +++ b/src/androidterm.c | |||
| @@ -559,6 +559,9 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 559 | f = android_window_to_frame (dpyinfo, | 559 | f = android_window_to_frame (dpyinfo, |
| 560 | configureEvent.xconfigure.window); | 560 | configureEvent.xconfigure.window); |
| 561 | 561 | ||
| 562 | if (!f) | ||
| 563 | goto OTHER; | ||
| 564 | |||
| 562 | int width = configureEvent.xconfigure.width; | 565 | int width = configureEvent.xconfigure.width; |
| 563 | int height = configureEvent.xconfigure.height; | 566 | int height = configureEvent.xconfigure.height; |
| 564 | 567 | ||
| @@ -884,10 +887,6 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 884 | inev.ie.arg = tab_bar_arg; | 887 | inev.ie.arg = tab_bar_arg; |
| 885 | } | 888 | } |
| 886 | } | 889 | } |
| 887 | else | ||
| 888 | { | ||
| 889 | /* TODO: scroll bars */ | ||
| 890 | } | ||
| 891 | 890 | ||
| 892 | if (event->type == ANDROID_BUTTON_PRESS) | 891 | if (event->type == ANDROID_BUTTON_PRESS) |
| 893 | { | 892 | { |
| @@ -1451,7 +1450,12 @@ android_make_frame_visible_invisible (struct frame *f, bool visible) | |||
| 1451 | static void | 1450 | static void |
| 1452 | android_fullscreen_hook (struct frame *f) | 1451 | android_fullscreen_hook (struct frame *f) |
| 1453 | { | 1452 | { |
| 1454 | /* TODO */ | 1453 | /* Explicitly setting fullscreen is not supported on Android. */ |
| 1454 | |||
| 1455 | if (!FRAME_PARENT_FRAME (f)) | ||
| 1456 | store_frame_param (f, Qfullscreen, Qmaximized); | ||
| 1457 | else | ||
| 1458 | store_frame_param (f, Qfullscreen, Qnil); | ||
| 1455 | } | 1459 | } |
| 1456 | 1460 | ||
| 1457 | void | 1461 | void |
| @@ -2360,7 +2364,7 @@ android_draw_box_rect (struct glyph_string *s, | |||
| 2360 | 2364 | ||
| 2361 | /* Top. */ | 2365 | /* Top. */ |
| 2362 | android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x, | 2366 | android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x, |
| 2363 | left_x, right_x - left_x + 1, hwidth); | 2367 | top_y, right_x - left_x + 1, hwidth); |
| 2364 | 2368 | ||
| 2365 | /* Left. */ | 2369 | /* Left. */ |
| 2366 | if (left_p) | 2370 | if (left_p) |
| @@ -3958,7 +3962,14 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) | |||
| 3958 | char * | 3962 | char * |
| 3959 | get_keysym_name (int keysym) | 3963 | get_keysym_name (int keysym) |
| 3960 | { | 3964 | { |
| 3961 | return (char *) "UNKNOWN KEYSYM"; | 3965 | static char buffer[64]; |
| 3966 | |||
| 3967 | #ifndef ANDROID_STUBIFY | ||
| 3968 | android_get_keysym_name (keysym, buffer, 64); | ||
| 3969 | #else | ||
| 3970 | emacs_abort (); | ||
| 3971 | #endif | ||
| 3972 | return buffer; | ||
| 3962 | } | 3973 | } |
| 3963 | 3974 | ||
| 3964 | 3975 | ||
| @@ -4009,20 +4020,13 @@ android_create_terminal (struct android_display_info *dpyinfo) | |||
| 4009 | terminal->set_new_font_hook = android_new_font; | 4020 | terminal->set_new_font_hook = android_new_font; |
| 4010 | terminal->set_bitmap_icon_hook = android_bitmap_icon; | 4021 | terminal->set_bitmap_icon_hook = android_bitmap_icon; |
| 4011 | terminal->implicit_set_name_hook = android_implicitly_set_name; | 4022 | terminal->implicit_set_name_hook = android_implicitly_set_name; |
| 4012 | /* terminal->menu_show_hook = android_menu_show; XXX */ | 4023 | terminal->menu_show_hook = android_menu_show; |
| 4013 | terminal->change_tab_bar_height_hook = android_change_tab_bar_height; | 4024 | terminal->change_tab_bar_height_hook = android_change_tab_bar_height; |
| 4014 | terminal->change_tool_bar_height_hook = android_change_tool_bar_height; | 4025 | terminal->change_tool_bar_height_hook = android_change_tool_bar_height; |
| 4015 | /* terminal->set_vertical_scroll_bar_hook */ | ||
| 4016 | /* = android_set_vertical_scroll_bar; */ | ||
| 4017 | /* terminal->set_horizontal_scroll_bar_hook */ | ||
| 4018 | /* = android_set_horizontal_scroll_bar; */ | ||
| 4019 | terminal->set_scroll_bar_default_width_hook | 4026 | terminal->set_scroll_bar_default_width_hook |
| 4020 | = android_set_scroll_bar_default_width; | 4027 | = android_set_scroll_bar_default_width; |
| 4021 | terminal->set_scroll_bar_default_height_hook | 4028 | terminal->set_scroll_bar_default_height_hook |
| 4022 | = android_set_scroll_bar_default_height; | 4029 | = android_set_scroll_bar_default_height; |
| 4023 | /* terminal->condemn_scroll_bars_hook = android_condemn_scroll_bars; */ | ||
| 4024 | /* terminal->redeem_scroll_bars_hook = android_redeem_scroll_bars; */ | ||
| 4025 | /* terminal->judge_scroll_bars_hook = android_judge_scroll_bars; */ | ||
| 4026 | terminal->free_pixmap = android_free_pixmap_hook; | 4030 | terminal->free_pixmap = android_free_pixmap_hook; |
| 4027 | terminal->delete_frame_hook = android_delete_frame; | 4031 | terminal->delete_frame_hook = android_delete_frame; |
| 4028 | terminal->delete_terminal_hook = android_delete_terminal; | 4032 | terminal->delete_terminal_hook = android_delete_terminal; |
diff --git a/src/androidterm.h b/src/androidterm.h index ebde15c40a8..e83e32a5854 100644 --- a/src/androidterm.h +++ b/src/androidterm.h | |||
| @@ -402,6 +402,12 @@ extern void syms_of_androidfont (void); | |||
| 402 | 402 | ||
| 403 | extern void android_finalize_font_entity (struct font_entity *); | 403 | extern void android_finalize_font_entity (struct font_entity *); |
| 404 | 404 | ||
| 405 | /* Defined in androidmenu.c. */ | ||
| 406 | |||
| 407 | extern Lisp_Object android_menu_show (struct frame *, int, int, int, | ||
| 408 | Lisp_Object, const char **); | ||
| 409 | extern void init_androidmenu (void); | ||
| 410 | |||
| 405 | /* Defined in sfntfont-android.c. */ | 411 | /* Defined in sfntfont-android.c. */ |
| 406 | 412 | ||
| 407 | extern const struct font_driver android_sfntfont_driver; | 413 | extern const struct font_driver android_sfntfont_driver; |
diff --git a/src/emacs.c b/src/emacs.c index e3f6c7d66f7..8f5be53aad9 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -2499,6 +2499,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 2499 | init_window (); | 2499 | init_window (); |
| 2500 | init_font (); | 2500 | init_font (); |
| 2501 | 2501 | ||
| 2502 | #ifdef HAVE_ANDROID | ||
| 2503 | init_androidmenu (); | ||
| 2504 | #endif | ||
| 2505 | |||
| 2502 | #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY | 2506 | #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY |
| 2503 | init_androidfont (); | 2507 | init_androidfont (); |
| 2504 | init_sfntfont (); | 2508 | init_sfntfont (); |
diff --git a/xcompile/Makefile.in b/xcompile/Makefile.in index ed2b10b91a7..9f817bb4c53 100644 --- a/xcompile/Makefile.in +++ b/xcompile/Makefile.in | |||
| @@ -110,7 +110,7 @@ lib/libgnu.a: src/verbose.mk config.status $(LIB_DEPS) $(PRE_BUILD_DEPS) | |||
| 110 | +make -C lib libgnu.a | 110 | +make -C lib libgnu.a |
| 111 | 111 | ||
| 112 | src/Makefile src/config.h &: $(top_builddir)/src/config.h.android \ | 112 | src/Makefile src/config.h &: $(top_builddir)/src/config.h.android \ |
| 113 | $(top_builddir)/src/Makefile.android $(PRE_BUILD_DEPS) | 113 | $(top_builddir)/src/Makefile.android |
| 114 | mkdir -p src src/deps | 114 | mkdir -p src src/deps |
| 115 | # Copy config.h to src/ | 115 | # Copy config.h to src/ |
| 116 | cp -f -p $(top_builddir)/src/config.h.android src/config.h | 116 | cp -f -p $(top_builddir)/src/config.h.android src/config.h |
| @@ -155,13 +155,20 @@ $(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \ | |||
| 155 | # Finally, go into lib-src and make everything being built | 155 | # Finally, go into lib-src and make everything being built |
| 156 | +make -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin))) | 156 | +make -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin))) |
| 157 | 157 | ||
| 158 | .PHONY: clean maintainer-clean | 158 | .PHONY: clean maintainer-clean distclean |
| 159 | clean: | 159 | clean: |
| 160 | rm -rf $(CLEAN_SUBDIRS) *.bak sys | 160 | rm -rf $(CLEAN_SUBDIRS) *.bak sys |
| 161 | if [ -e lib/Makefile ]; then \ | 161 | if [ -e lib/Makefile ]; then \ |
| 162 | make -C lib clean; \ | 162 | make -C lib clean; \ |
| 163 | fi | 163 | fi |
| 164 | rm -rf lib/gnulib.mk lib/Makefile lib/config.h | 164 | rm -rf lib/config.h |
| 165 | |||
| 166 | distclean bootstrap-clean: clean | ||
| 167 | if [ -e lib/Makefile ]; then \ | ||
| 168 | make -C lib distclean; \ | ||
| 169 | fi | ||
| 170 | # Just in case. | ||
| 171 | rm -rf lib/Makefile lib/gnulib.mk | ||
| 165 | 172 | ||
| 166 | maintainer-clean: clean | 173 | maintainer-clean: clean |
| 167 | if [ -e lib/Makefile ]; then \ | 174 | if [ -e lib/Makefile ]; then \ |