diff options
Diffstat (limited to 'java')
27 files changed, 3553 insertions, 0 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml new file mode 100644 index 00000000000..75aa5bdf409 --- /dev/null +++ b/java/AndroidManifest.xml | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | package="org.gnu.emacs" android:installLocation="auto"> | ||
| 3 | |||
| 4 | <!-- Paste in every permission in existence so Emacs can do | ||
| 5 | anything. --> | ||
| 6 | |||
| 7 | <uses-permission android:name="android.permission.READ_CONTACTS" /> | ||
| 8 | <uses-permission android:name="android.permission.WRITE_CONTACTS" /> | ||
| 9 | <uses-permission android:name="android.permission.VIBRATE" /> | ||
| 10 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | ||
| 11 | <uses-permission android:name="android.permission.INTERNET" /> | ||
| 12 | <uses-permission android:name="android.permission.SET_WALLPAPER" /> | ||
| 13 | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||
| 14 | <uses-permission android:name="android.permission.SEND_SMS" /> | ||
| 15 | <uses-permission android:name="android.permission.RECEIVE_SMS" /> | ||
| 16 | <uses-permission android:name="android.permission.RECEIVE_MMS"/> | ||
| 17 | <uses-permission android:name="android.permission.WRITE_SMS"/> | ||
| 18 | <uses-permission android:name="android.permission.READ_SMS"/> | ||
| 19 | <uses-permission android:name="android.permission.NFC" /> | ||
| 20 | <uses-permission android:name="android.permission.TRANSMIT_IR" /> | ||
| 21 | <uses-permission android:name="android.permission.READ_PHONE_STATE"/> | ||
| 22 | <uses-permission android:name="android.permission.WAKE_LOCK"/> | ||
| 23 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> | ||
| 24 | <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> | ||
| 25 | <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/> | ||
| 26 | <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> | ||
| 27 | <uses-permission android:name="android.permission.RECORD_AUDIO" /> | ||
| 28 | <uses-permission android:name="android.permission.CAMERA" /> | ||
| 29 | |||
| 30 | <uses-sdk android:minSdkVersion="7" | ||
| 31 | android:targetSdkVersion="28"/> | ||
| 32 | |||
| 33 | <application android:name="org.gnu.emacs.EmacsApplication" | ||
| 34 | android:label="GNU Emacs" | ||
| 35 | android:hardwareAccelerated="true" | ||
| 36 | android:supportsRtl="true" | ||
| 37 | android:theme="@android:style/Theme" | ||
| 38 | android:debuggable="true" | ||
| 39 | android:extractNativeLibs="true"> | ||
| 40 | <activity android:name="org.gnu.emacs.EmacsActivity"> | ||
| 41 | <intent-filter> | ||
| 42 | <action android:name="android.intent.action.MAIN" /> | ||
| 43 | <category android:name="android.intent.category.DEFAULT" /> | ||
| 44 | <category android:name="android.intent.category.LAUNCHER" /> | ||
| 45 | </intent-filter> | ||
| 46 | </activity> | ||
| 47 | |||
| 48 | <service android:name="org.gnu.emacs.EmacsService" | ||
| 49 | android:directBootAware="false" | ||
| 50 | android:enabled="true" | ||
| 51 | android:exported="false" | ||
| 52 | android:label="GNU Emacs service"/> | ||
| 53 | </application> | ||
| 54 | </manifest> | ||
diff --git a/java/Makefile.in b/java/Makefile.in new file mode 100644 index 00000000000..e9fcc625cb2 --- /dev/null +++ b/java/Makefile.in | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | ### @configure_input@ | ||
| 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 | ||
| 10 | # (at 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 | top_builddir = @top_builddir@ | ||
| 21 | |||
| 22 | -include ${top_builddir}/src/verbose.mk | ||
| 23 | |||
| 24 | SHELL = @SHELL@ | ||
| 25 | JAVAC = @JAVAC@ | ||
| 26 | AAPT = @AAPT@ | ||
| 27 | DX = @DX@ | ||
| 28 | ZIPALIGN = @ZIPALIGN@ | ||
| 29 | JARSIGNER = @JARSIGNER@ | ||
| 30 | ANDROID_JAR = @ANDROID_JAR@ | ||
| 31 | ANDROID_ABI = @ANDROID_ABI@ | ||
| 32 | |||
| 33 | WARN_JAVAFLAGS = -Xlint:deprecation | ||
| 34 | JAVAFLAGS = -classpath "$(ANDROID_JAR):." -target 1.7 -source 1.7 \ | ||
| 35 | $(WARN_JAVAFLAGS) | ||
| 36 | |||
| 37 | SIGN_EMACS = -keystore emacs.keystore -storepass emacs1 | ||
| 38 | |||
| 39 | JAVA_FILES = $(shell find . -type f -name *.java) | ||
| 40 | CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class) | ||
| 41 | |||
| 42 | # How this stuff works. | ||
| 43 | |||
| 44 | # emacs.apk depends on emacs.apk-in, which is simply a ZIP archive | ||
| 45 | # containing the following files: | ||
| 46 | # lib/$(ANDROID_ABI)/libemacs.so | ||
| 47 | # lib/$(ANDROID_ABI)/libandroid-emacs.so | ||
| 48 | # lib/$(ANDROID_ABI)/libctags.so | ||
| 49 | # lib/$(ANDROID_ABI)/libhexl.so | ||
| 50 | # lib/$(ANDROID_ABI)/libmovemail.so | ||
| 51 | # lib/$(ANDROID_ABI)/librcs2log.so | ||
| 52 | # lib/$(ANDROID_ABI)/libebrowse.so | ||
| 53 | # assets/info/ | ||
| 54 | # assets/etc/ | ||
| 55 | # assets/lisp/ | ||
| 56 | |||
| 57 | .PHONY: emacs.apk-in all | ||
| 58 | all: emacs.apk | ||
| 59 | |||
| 60 | # Binaries to cross-compile. | ||
| 61 | CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags \ | ||
| 62 | ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \ | ||
| 63 | ../xcompile/lib-src/ebrowse | ||
| 64 | |||
| 65 | # Libraries to cross-compile. | ||
| 66 | CROSS_LIBS = ../xcompile/src/libemacs.so | ||
| 67 | |||
| 68 | .PHONY: $(CROSS_BINS) $(CROSS_LIBS) | ||
| 69 | |||
| 70 | ../xcompile/src/android-emacs ../xcompile/src/libemacs.so: | ||
| 71 | make -C ../xcompile src/$(notdir $@) | ||
| 72 | |||
| 73 | ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \ | ||
| 74 | ../xcompile/lib-src/ctags ../xcompile/lib-src/ebrowse &: | ||
| 75 | make -C ../xcompile lib-src/$(notdir $@) | ||
| 76 | |||
| 77 | emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml | ||
| 78 | # Make the working directory for this stuff | ||
| 79 | rm -rf install_temp | ||
| 80 | mkdir -p install_temp/lib/$(ANDROID_ABI) | ||
| 81 | mkdir -p install_temp/assets/etc | ||
| 82 | mkdir -p install_temp/assets/lisp | ||
| 83 | mkdir -p install_temp/assets/info | ||
| 84 | # Install architecture independents to assets/etc and assets/lisp | ||
| 85 | cp -r $(top_builddir)/lisp install_temp/assets | ||
| 86 | cp -r $(top_builddir)/etc install_temp/assets | ||
| 87 | # Remove undesirable files from those directories. | ||
| 88 | for subdir in `find install_temp -type d -print`; do \ | ||
| 89 | chmod a+rx $${subdir} ; \ | ||
| 90 | rm -rf $${subdir}/.gitignore ; \ | ||
| 91 | rm -rf $${subdir}/.DS_Store ; \ | ||
| 92 | rm -rf $${subdir}/#* ; \ | ||
| 93 | rm -rf $${subdir}/.#* ; \ | ||
| 94 | rm -rf $${subdir}/*~ ; \ | ||
| 95 | rm -rf $${subdir}/*.orig ; \ | ||
| 96 | rm -rf $${subdir}/ChangeLog* ; \ | ||
| 97 | rm -rf $${subdir}/[mM]akefile*[.-]in ; \ | ||
| 98 | rm -rf $${subdir}/Makefile; \ | ||
| 99 | done | ||
| 100 | # Install architecture dependents to lib/$(ANDROID_ABI). This | ||
| 101 | # perculiar naming scheme is required to make Android preserve these | ||
| 102 | # binaries upon installation. | ||
| 103 | for file in $(CROSS_BINS); do \ | ||
| 104 | if [ -x $$file ]; then \ | ||
| 105 | filename=`basename $$file`; \ | ||
| 106 | cp -f $$file install_temp/lib/$(ANDROID_ABI)/lib$${filename}.so; \ | ||
| 107 | fi \ | ||
| 108 | done | ||
| 109 | for file in $(CROSS_LIBS); do \ | ||
| 110 | if [ -x $$file ]; then \ | ||
| 111 | cp -f $$file install_temp/lib/$(ANDROID_ABI); \ | ||
| 112 | fi \ | ||
| 113 | done | ||
| 114 | # Package everything. | ||
| 115 | $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml | ||
| 116 | pushd install_temp; $(AAPT) add ../$@ `find lib -type f`; popd | ||
| 117 | pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd | ||
| 118 | rm -rf install_temp | ||
| 119 | |||
| 120 | .SUFFIXES: .java .class | ||
| 121 | .java.class &: | ||
| 122 | $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $< | ||
| 123 | |||
| 124 | # N.B. that find must be called all over again in case javac generated | ||
| 125 | # nested classes. | ||
| 126 | |||
| 127 | classes.dex: $(CLASS_FILES) | ||
| 128 | $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \ | ||
| 129 | $(subst $$,\$$,$(shell find . -type f -name *.class)) | ||
| 130 | |||
| 131 | # When emacs.keystore expires, regenerate it with: | ||
| 132 | # | ||
| 133 | # keytool -genkey -v -keystore emacs.keystore -alias "Emacs keystore" \ | ||
| 134 | # -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 100000 | ||
| 135 | |||
| 136 | .PHONY: clean maintainer-clean | ||
| 137 | |||
| 138 | emacs.apk: classes.dex emacs.apk-in emacs.keystore | ||
| 139 | cp -f emacs.apk-in $@.unaligned | ||
| 140 | $(AAPT) add $@.unaligned classes.dex | ||
| 141 | $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore" | ||
| 142 | $(ZIPALIGN) -f 4 $@.unaligned $@ | ||
| 143 | rm -f $@.unaligned | ||
| 144 | |||
| 145 | clean: | ||
| 146 | rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class | ||
| 147 | rm -rf install-temp | ||
| 148 | find . -name '*.class' -delete | ||
| 149 | |||
| 150 | maintainer-clean: clean | ||
diff --git a/java/README b/java/README new file mode 100644 index 00000000000..50c2332ce95 --- /dev/null +++ b/java/README | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | This directory holds the Java sources of the port of GNU Emacs to | ||
| 2 | Android-like systems. | ||
| 3 | |||
| 4 | Please keep the Java code indented with tabs and formatted according | ||
| 5 | to the rules for C code in the GNU coding standards. Always use | ||
| 6 | C-style comments. | ||
diff --git a/java/debug.sh b/java/debug.sh new file mode 100755 index 00000000000..dd710dc31af --- /dev/null +++ b/java/debug.sh | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | ### Run Emacs under GDB or JDB on Android. | ||
| 3 | |||
| 4 | ## Copyright (C) 2023 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | ## This file is part of GNU Emacs. | ||
| 7 | |||
| 8 | ## GNU Emacs is free software: you can redistribute it and/or modify | ||
| 9 | ## it under the terms of the GNU General Public License as published by | ||
| 10 | ## the Free Software Foundation, either version 3 of the License, or | ||
| 11 | ## (at your option) any later version. | ||
| 12 | |||
| 13 | ## GNU Emacs is distributed in the hope that it will be useful, | ||
| 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | ## GNU General Public License for more details. | ||
| 17 | |||
| 18 | ## You should have received a copy of the GNU General Public License | ||
| 19 | ## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 20 | |||
| 21 | set -m | ||
| 22 | oldpwd=`pwd` | ||
| 23 | cd `dirname $0` | ||
| 24 | |||
| 25 | devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -` | ||
| 26 | device= | ||
| 27 | progname=$0 | ||
| 28 | package=org.gnu.emacs | ||
| 29 | activity=org.gnu.emacs.EmacsActivity | ||
| 30 | gdb_port=5039 | ||
| 31 | jdb_port=64013 | ||
| 32 | jdb=no | ||
| 33 | |||
| 34 | while [ $# -gt 0 ]; do | ||
| 35 | case "$1" in | ||
| 36 | ## This option specifies the serial number of a device to use. | ||
| 37 | "--device" ) | ||
| 38 | device="$2" | ||
| 39 | if [ -z device ]; then | ||
| 40 | echo "You must specify an argument to --device" | ||
| 41 | exit 1 | ||
| 42 | fi | ||
| 43 | ;; | ||
| 44 | "--help" ) | ||
| 45 | echo "Usage: $progname [options] -- [gdb options]" | ||
| 46 | echo "" | ||
| 47 | echo " --device DEVICE run Emacs on the specified device" | ||
| 48 | echo " --port PORT run the GDB server on a specific port" | ||
| 49 | echo " --jdb-port PORT run the JDB server on a specific port" | ||
| 50 | echo " --jdb run JDB instead of GDB" | ||
| 51 | echo " --help print this message" | ||
| 52 | echo "" | ||
| 53 | echo "Available devices:" | ||
| 54 | for device in $devices; do | ||
| 55 | echo " " $device | ||
| 56 | done | ||
| 57 | echo "" | ||
| 58 | exit 0 | ||
| 59 | ;; | ||
| 60 | "--jdb" ) | ||
| 61 | jdb=yes | ||
| 62 | ;; | ||
| 63 | "--port" ) | ||
| 64 | gdb_port=$1 | ||
| 65 | ;; | ||
| 66 | "--" ) | ||
| 67 | shift | ||
| 68 | gdbargs=$@ | ||
| 69 | break; | ||
| 70 | ;; | ||
| 71 | * ) | ||
| 72 | echo "$progname: Unrecognized argument $1" | ||
| 73 | exit 1 | ||
| 74 | ;; | ||
| 75 | esac | ||
| 76 | shift | ||
| 77 | done | ||
| 78 | |||
| 79 | if [ -z $devices ]; then | ||
| 80 | echo "No devices are available." | ||
| 81 | exit 1 | ||
| 82 | fi | ||
| 83 | |||
| 84 | if [ -z $device ]; then | ||
| 85 | device=$devices | ||
| 86 | fi | ||
| 87 | |||
| 88 | if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then | ||
| 89 | echo "Multiple devices are available. Please pick one using" | ||
| 90 | echo "--device and try again." | ||
| 91 | fi | ||
| 92 | |||
| 93 | echo "Looking for $package on device $device" | ||
| 94 | |||
| 95 | # Find the application data directory | ||
| 96 | app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'` | ||
| 97 | |||
| 98 | if [ -z $app_data_dir ]; then | ||
| 99 | echo "The data directory for the package $package was not found." | ||
| 100 | echo "Is it installed?" | ||
| 101 | fi | ||
| 102 | |||
| 103 | echo "Found application data directory at $app_data_dir..." | ||
| 104 | |||
| 105 | # Find which PIDs are associated with org.gnu.emacs | ||
| 106 | package_uid=`adb -s $device shell run-as $package id -u` | ||
| 107 | |||
| 108 | if [ -z $package_uid ]; then | ||
| 109 | echo "Failed to obtain UID of packages named $package" | ||
| 110 | exit 1 | ||
| 111 | fi | ||
| 112 | |||
| 113 | # First, run ps -u $package_uid -o PID,CMD to fetch the list of | ||
| 114 | # process IDs. | ||
| 115 | package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD` | ||
| 116 | |||
| 117 | # Next, remove lines matching "ps" itself. | ||
| 118 | package_pids=`awk -- '{ | ||
| 119 | if (!match ($0, /(PID|ps)/)) | ||
| 120 | print $1 | ||
| 121 | }' <<< $package_pids` | ||
| 122 | |||
| 123 | # Finally, kill each existing process. | ||
| 124 | for pid in $package_pids; do | ||
| 125 | echo "Killing existing process $pid..." | ||
| 126 | adb -s $device shell run-as $package kill -9 $pid &> /dev/null | ||
| 127 | done | ||
| 128 | |||
| 129 | # Now run the main activity. This must be done as the adb user and | ||
| 130 | # not as the package user. | ||
| 131 | echo "Starting activity $activity and attaching debugger" | ||
| 132 | |||
| 133 | # Exit if the activity could not be started. | ||
| 134 | adb -s $device shell am start -D "$package/$activity" | ||
| 135 | if [ ! $? ]; then | ||
| 136 | exit 1; | ||
| 137 | fi | ||
| 138 | |||
| 139 | # Now look for processes matching the package again. | ||
| 140 | package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o PID,CMD` | ||
| 141 | |||
| 142 | # Next, remove lines matching "ps" itself. | ||
| 143 | package_pids=`awk -- '{ | ||
| 144 | if (!match ($0, /(PID|ps)/)) | ||
| 145 | print $1 | ||
| 146 | }' <<< $package_pids` | ||
| 147 | |||
| 148 | pid=$package_pids | ||
| 149 | num_pids=`wc -w <<< "$package_pids"` | ||
| 150 | |||
| 151 | if [ $num_pids -gt 1 ]; then | ||
| 152 | echo "More than one process was started:" | ||
| 153 | echo "" | ||
| 154 | adb -s $device shell run-as $package ps -u $package_uid | awk -- '{ | ||
| 155 | if (!match ($0, /ps/)) | ||
| 156 | print $0 | ||
| 157 | }' | ||
| 158 | echo "" | ||
| 159 | printf "Which one do you want to attach to? " | ||
| 160 | read pid | ||
| 161 | elif [ -z $package_pids ]; then | ||
| 162 | echo "No processes were found to attach to." | ||
| 163 | exit 1 | ||
| 164 | fi | ||
| 165 | |||
| 166 | # Start JDB to make the wait dialog disappear. | ||
| 167 | echo "Attaching JDB to unblock the application." | ||
| 168 | adb -s $device forward --remove-all | ||
| 169 | adb -s $device forward "tcp:$jdb_port" "jdwp:$pid" | ||
| 170 | |||
| 171 | if [ ! $? ]; then | ||
| 172 | echo "Failed to forward jdwp:$pid to $jdb_port!" | ||
| 173 | echo "Perhaps you need to specify a different port with --port?" | ||
| 174 | exit 1; | ||
| 175 | fi | ||
| 176 | |||
| 177 | jdb_command="jdb -connect \ | ||
| 178 | com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port" | ||
| 179 | |||
| 180 | if [ $jdb = "yes" ]; then | ||
| 181 | # Just start JDB and then exit | ||
| 182 | $jdb_command | ||
| 183 | exit 1 | ||
| 184 | fi | ||
| 185 | |||
| 186 | exec 4<> /tmp/file-descriptor-stamp | ||
| 187 | |||
| 188 | # Now run JDB with IO redirected to file descriptor 4 in a subprocess. | ||
| 189 | $jdb_command <&4 >&4 & | ||
| 190 | |||
| 191 | character= | ||
| 192 | # Next, wait until the prompt is found. | ||
| 193 | while read -n1 -u 4 character; do | ||
| 194 | if [ "$character" = ">" ]; then | ||
| 195 | echo "JDB attached successfully" | ||
| 196 | break; | ||
| 197 | fi | ||
| 198 | done | ||
| 199 | |||
| 200 | # Now start gdbserver on the device asynchronously. | ||
| 201 | |||
| 202 | echo "Attaching gdbserver to $pid on $device..." | ||
| 203 | exec 5<> /tmp/file-descriptor-stamp | ||
| 204 | adb -s $device shell run-as $package /system/bin/gdbserver --once \ | ||
| 205 | "+debug.$package_uid.socket" --attach $pid >&5 & | ||
| 206 | |||
| 207 | # Wait until gdbserver successfully runs. | ||
| 208 | line= | ||
| 209 | while read -u 5 line; do | ||
| 210 | case "$line" in | ||
| 211 | *Attached* ) | ||
| 212 | break; | ||
| 213 | ;; | ||
| 214 | *error* | *Error* | failed ) | ||
| 215 | echo $line | ||
| 216 | exit 1 | ||
| 217 | ;; | ||
| 218 | * ) | ||
| 219 | ;; | ||
| 220 | esac | ||
| 221 | done | ||
| 222 | |||
| 223 | # Send EOF to JDB to make it go away. This will also cause Android to | ||
| 224 | # allow Emacs to continue executing. | ||
| 225 | echo "Making JDB go away..." | ||
| 226 | echo "exit" >&4 | ||
| 227 | read -u 4 line | ||
| 228 | echo "JDB has gone away with $line" | ||
| 229 | |||
| 230 | # Forward the gdb server port here. | ||
| 231 | adb -s $device forward "tcp:$gdb_port" \ | ||
| 232 | "localfilesystem:$app_data_dir/debug.$package_uid.socket" | ||
| 233 | if [ ! $? ]; then | ||
| 234 | echo "Failed to forward $app_data_dir/debug.$package_uid.socket" | ||
| 235 | echo "to $gdb_port! Perhaps you need to specify a different port" | ||
| 236 | echo "with --port?" | ||
| 237 | exit 1; | ||
| 238 | fi | ||
| 239 | |||
| 240 | # Finally, start gdb with any extra arguments needed. | ||
| 241 | cd "$oldpwd" | ||
| 242 | gdb --eval-command "" --eval-command "target remote localhost:$gdb_port" $gdbargs | ||
diff --git a/java/emacs.keystore b/java/emacs.keystore new file mode 100644 index 00000000000..76d80b6db65 --- /dev/null +++ b/java/emacs.keystore | |||
| Binary files differ | |||
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java new file mode 100644 index 00000000000..cacd5f13e60 --- /dev/null +++ b/java/org/gnu/emacs/EmacsActivity.java | |||
| @@ -0,0 +1,146 @@ | |||
| 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.lang.IllegalStateException; | ||
| 23 | import java.util.List; | ||
| 24 | import java.util.ArrayList; | ||
| 25 | |||
| 26 | import android.app.Activity; | ||
| 27 | import android.content.Context; | ||
| 28 | import android.content.Intent; | ||
| 29 | import android.os.Bundle; | ||
| 30 | import android.util.Log; | ||
| 31 | import android.widget.FrameLayout; | ||
| 32 | import android.widget.FrameLayout.LayoutParams; | ||
| 33 | |||
| 34 | public class EmacsActivity extends Activity | ||
| 35 | { | ||
| 36 | public static final String TAG = "EmacsActivity"; | ||
| 37 | |||
| 38 | /* List of all activities that do not have an associated | ||
| 39 | EmacsWindow. */ | ||
| 40 | public static List<EmacsActivity> availableActivities; | ||
| 41 | |||
| 42 | /* The currently attached EmacsWindow, or null if none. */ | ||
| 43 | private EmacsWindow window; | ||
| 44 | |||
| 45 | /* The frame layout associated with the activity. */ | ||
| 46 | private FrameLayout layout; | ||
| 47 | |||
| 48 | static | ||
| 49 | { | ||
| 50 | /* Set up the list of available activities. */ | ||
| 51 | availableActivities = new ArrayList<EmacsActivity> (); | ||
| 52 | }; | ||
| 53 | |||
| 54 | public void | ||
| 55 | attachChild (EmacsWindow child) | ||
| 56 | { | ||
| 57 | if (window != null) | ||
| 58 | throw new IllegalStateException ("trying to attach window when one" | ||
| 59 | + " already exists"); | ||
| 60 | |||
| 61 | /* Record and attach the view. */ | ||
| 62 | window = child; | ||
| 63 | layout.addView (window.view); | ||
| 64 | |||
| 65 | /* Remove the objects from the lists of what is available. */ | ||
| 66 | EmacsService.availableChildren.remove (child); | ||
| 67 | availableActivities.remove (this); | ||
| 68 | |||
| 69 | /* Now set child->activity. */ | ||
| 70 | child.setActivity (this); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* Make this activity available for future windows to attach | ||
| 74 | again. */ | ||
| 75 | |||
| 76 | public void | ||
| 77 | makeAvailable () | ||
| 78 | { | ||
| 79 | window = null; | ||
| 80 | |||
| 81 | for (EmacsWindow iterWindow | ||
| 82 | : EmacsService.availableChildren) | ||
| 83 | { | ||
| 84 | synchronized (iterWindow) | ||
| 85 | { | ||
| 86 | if (!iterWindow.isDestroyed ()) | ||
| 87 | attachChild (iterWindow); | ||
| 88 | |||
| 89 | return; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | availableActivities.add (this); | ||
| 94 | } | ||
| 95 | |||
| 96 | @Override | ||
| 97 | public void | ||
| 98 | onCreate (Bundle savedInstanceState) | ||
| 99 | { | ||
| 100 | FrameLayout.LayoutParams params; | ||
| 101 | |||
| 102 | params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, | ||
| 103 | LayoutParams.MATCH_PARENT); | ||
| 104 | |||
| 105 | /* Make the frame layout. */ | ||
| 106 | layout = new FrameLayout (this); | ||
| 107 | layout.setLayoutParams (params); | ||
| 108 | |||
| 109 | /* Set it as the content view. */ | ||
| 110 | setContentView (layout); | ||
| 111 | |||
| 112 | /* Make the activity available before starting the | ||
| 113 | service. */ | ||
| 114 | makeAvailable (); | ||
| 115 | |||
| 116 | if (EmacsService.SERVICE == null) | ||
| 117 | /* Start the Emacs service now. */ | ||
| 118 | startService (new Intent (this, EmacsService.class)); | ||
| 119 | |||
| 120 | super.onCreate (savedInstanceState); | ||
| 121 | } | ||
| 122 | |||
| 123 | @Override | ||
| 124 | public void | ||
| 125 | onStop () | ||
| 126 | { | ||
| 127 | /* The activity is no longer visible. If there is a window | ||
| 128 | attached, detach it. */ | ||
| 129 | |||
| 130 | if (window != null) | ||
| 131 | { | ||
| 132 | layout.removeView (window.view); | ||
| 133 | |||
| 134 | /* Notice that the window is already available too. But do | ||
| 135 | not call noticeAvailableChild; that might assign it to some | ||
| 136 | other activity, which behaves badly. */ | ||
| 137 | EmacsService.availableChildren.add (window); | ||
| 138 | window = null; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Finally, remove this activity from the list of available | ||
| 142 | activities. */ | ||
| 143 | availableActivities.remove (this); | ||
| 144 | super.onStop (); | ||
| 145 | } | ||
| 146 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java new file mode 100644 index 00000000000..125da05cfd4 --- /dev/null +++ b/java/org/gnu/emacs/EmacsApplication.java | |||
| @@ -0,0 +1,27 @@ | |||
| 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 android.app.Application; | ||
| 23 | |||
| 24 | public class EmacsApplication extends Application | ||
| 25 | { | ||
| 26 | /* This class currently does nothing. */ | ||
| 27 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java new file mode 100644 index 00000000000..f34d1ecde01 --- /dev/null +++ b/java/org/gnu/emacs/EmacsCopyArea.java | |||
| @@ -0,0 +1,131 @@ | |||
| 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 android.graphics.Bitmap; | ||
| 23 | import android.graphics.Canvas; | ||
| 24 | import android.graphics.Paint; | ||
| 25 | import android.graphics.PorterDuff.Mode; | ||
| 26 | import android.graphics.PorterDuffXfermode; | ||
| 27 | import android.graphics.Rect; | ||
| 28 | import android.graphics.Xfermode; | ||
| 29 | |||
| 30 | public class EmacsCopyArea implements EmacsPaintReq | ||
| 31 | { | ||
| 32 | private int src_x, src_y, dest_x, dest_y, width, height; | ||
| 33 | private EmacsDrawable destination, source; | ||
| 34 | private EmacsGC immutableGC; | ||
| 35 | private static Xfermode xorAlu, srcInAlu; | ||
| 36 | |||
| 37 | static | ||
| 38 | { | ||
| 39 | xorAlu = new PorterDuffXfermode (Mode.XOR); | ||
| 40 | srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); | ||
| 41 | }; | ||
| 42 | |||
| 43 | public | ||
| 44 | EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source, | ||
| 45 | int src_x, int src_y, int width, int height, | ||
| 46 | int dest_x, int dest_y, EmacsGC immutableGC) | ||
| 47 | { | ||
| 48 | this.destination = destination; | ||
| 49 | this.source = source; | ||
| 50 | this.src_x = src_x; | ||
| 51 | this.src_y = src_y; | ||
| 52 | this.width = width; | ||
| 53 | this.height = height; | ||
| 54 | this.dest_x = dest_x; | ||
| 55 | this.dest_y = dest_y; | ||
| 56 | this.immutableGC = immutableGC; | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public Rect | ||
| 61 | getRect () | ||
| 62 | { | ||
| 63 | return new Rect (dest_x, dest_y, dest_x + width, | ||
| 64 | dest_y + height); | ||
| 65 | } | ||
| 66 | |||
| 67 | @Override | ||
| 68 | public EmacsDrawable | ||
| 69 | getDrawable () | ||
| 70 | { | ||
| 71 | return destination; | ||
| 72 | } | ||
| 73 | |||
| 74 | @Override | ||
| 75 | public EmacsGC | ||
| 76 | getGC () | ||
| 77 | { | ||
| 78 | return immutableGC; | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public void | ||
| 83 | paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC) | ||
| 84 | { | ||
| 85 | int alu; | ||
| 86 | Bitmap bitmap; | ||
| 87 | Paint maskPaint; | ||
| 88 | Canvas maskCanvas; | ||
| 89 | Bitmap maskBitmap; | ||
| 90 | Rect rect, srcRect; | ||
| 91 | |||
| 92 | /* TODO implement stippling. */ | ||
| 93 | if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) | ||
| 94 | return; | ||
| 95 | |||
| 96 | alu = immutableGC.function; | ||
| 97 | rect = getRect (); | ||
| 98 | bitmap = source.getBitmap (); | ||
| 99 | |||
| 100 | if (alu == EmacsGC.GC_COPY) | ||
| 101 | paint.setXfermode (null); | ||
| 102 | else | ||
| 103 | paint.setXfermode (xorAlu); | ||
| 104 | |||
| 105 | if (immutableGC.clip_mask == null) | ||
| 106 | canvas.drawBitmap (bitmap, new Rect (src_x, src_y, | ||
| 107 | src_x + width, | ||
| 108 | src_y + height), | ||
| 109 | rect, paint); | ||
| 110 | else | ||
| 111 | { | ||
| 112 | maskPaint = new Paint (); | ||
| 113 | srcRect = new Rect (0, 0, rect.width (), | ||
| 114 | rect.height ()); | ||
| 115 | maskBitmap | ||
| 116 | = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, | ||
| 117 | true); | ||
| 118 | |||
| 119 | if (maskBitmap == null) | ||
| 120 | return; | ||
| 121 | |||
| 122 | maskPaint.setXfermode (srcInAlu); | ||
| 123 | maskCanvas = new Canvas (maskBitmap); | ||
| 124 | maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y, | ||
| 125 | src_x + width, | ||
| 126 | src_y + height), | ||
| 127 | srcRect, maskPaint); | ||
| 128 | canvas.drawBitmap (maskBitmap, srcRect, rect, paint); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java new file mode 100644 index 00000000000..6389031bbfa --- /dev/null +++ b/java/org/gnu/emacs/EmacsDrawLine.java | |||
| @@ -0,0 +1,137 @@ | |||
| 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.lang.Math; | ||
| 23 | |||
| 24 | import android.graphics.Bitmap; | ||
| 25 | import android.graphics.Canvas; | ||
| 26 | import android.graphics.Paint; | ||
| 27 | import android.graphics.PorterDuff.Mode; | ||
| 28 | import android.graphics.PorterDuffXfermode; | ||
| 29 | import android.graphics.Rect; | ||
| 30 | import android.graphics.Xfermode; | ||
| 31 | |||
| 32 | public class EmacsDrawLine implements EmacsPaintReq | ||
| 33 | { | ||
| 34 | private int x, y, x2, y2; | ||
| 35 | private EmacsDrawable drawable; | ||
| 36 | private EmacsGC immutableGC; | ||
| 37 | private static Xfermode xorAlu, srcInAlu; | ||
| 38 | |||
| 39 | static | ||
| 40 | { | ||
| 41 | xorAlu = new PorterDuffXfermode (Mode.XOR); | ||
| 42 | srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); | ||
| 43 | }; | ||
| 44 | |||
| 45 | public | ||
| 46 | EmacsDrawLine (EmacsDrawable drawable, int x, int y, | ||
| 47 | int x2, int y2, EmacsGC immutableGC) | ||
| 48 | { | ||
| 49 | this.drawable = drawable; | ||
| 50 | this.x = x; | ||
| 51 | this.y = y; | ||
| 52 | this.x2 = x2; | ||
| 53 | this.y2 = y2; | ||
| 54 | this.immutableGC = immutableGC; | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public Rect | ||
| 59 | getRect () | ||
| 60 | { | ||
| 61 | return new Rect (Math.min (x, x2 + 1), | ||
| 62 | Math.min (y, y2 + 1), | ||
| 63 | Math.max (x2 + 1, x), | ||
| 64 | Math.max (y2 + 1, y)); | ||
| 65 | } | ||
| 66 | |||
| 67 | @Override | ||
| 68 | public EmacsDrawable | ||
| 69 | getDrawable () | ||
| 70 | { | ||
| 71 | return drawable; | ||
| 72 | } | ||
| 73 | |||
| 74 | @Override | ||
| 75 | public EmacsGC | ||
| 76 | getGC () | ||
| 77 | { | ||
| 78 | return immutableGC; | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public void | ||
| 83 | paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC) | ||
| 84 | { | ||
| 85 | int alu; | ||
| 86 | Paint maskPaint; | ||
| 87 | Canvas maskCanvas; | ||
| 88 | Bitmap maskBitmap; | ||
| 89 | Rect rect, srcRect; | ||
| 90 | int width, height; | ||
| 91 | |||
| 92 | /* TODO implement stippling. */ | ||
| 93 | if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) | ||
| 94 | return; | ||
| 95 | |||
| 96 | alu = immutableGC.function; | ||
| 97 | rect = getRect (); | ||
| 98 | width = rect.width (); | ||
| 99 | height = rect.height (); | ||
| 100 | |||
| 101 | paint.setStyle (Paint.Style.STROKE); | ||
| 102 | |||
| 103 | if (alu == EmacsGC.GC_COPY) | ||
| 104 | paint.setXfermode (null); | ||
| 105 | else | ||
| 106 | paint.setXfermode (xorAlu); | ||
| 107 | |||
| 108 | if (immutableGC.clip_mask == null) | ||
| 109 | { | ||
| 110 | paint.setColor (immutableGC.foreground | 0xff000000); | ||
| 111 | canvas.drawLine ((float) x, (float) y, | ||
| 112 | (float) x2, (float) y2, | ||
| 113 | paint); | ||
| 114 | } | ||
| 115 | else | ||
| 116 | { | ||
| 117 | maskPaint = new Paint (); | ||
| 118 | maskBitmap | ||
| 119 | = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, | ||
| 120 | true); | ||
| 121 | |||
| 122 | if (maskBitmap == null) | ||
| 123 | return; | ||
| 124 | |||
| 125 | maskPaint.setXfermode (srcInAlu); | ||
| 126 | maskPaint.setColor (immutableGC.foreground | 0xff000000); | ||
| 127 | maskCanvas = new Canvas (maskBitmap); | ||
| 128 | srcRect = new Rect (0, 0, maskBitmap.getWidth (), | ||
| 129 | maskBitmap.getHeight ()); | ||
| 130 | maskCanvas.drawLine (0.0f, 0.0f, (float) Math.abs (x - x2), | ||
| 131 | (float) Math.abs (y - y2), maskPaint); | ||
| 132 | canvas.drawBitmap (maskBitmap, srcRect, rect, paint); | ||
| 133 | } | ||
| 134 | |||
| 135 | paint.setXfermode (null); | ||
| 136 | } | ||
| 137 | } | ||
diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java b/java/org/gnu/emacs/EmacsDrawPoint.java new file mode 100644 index 00000000000..772757ff424 --- /dev/null +++ b/java/org/gnu/emacs/EmacsDrawPoint.java | |||
| @@ -0,0 +1,30 @@ | |||
| 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 | public class EmacsDrawPoint extends EmacsDrawRectangle | ||
| 23 | { | ||
| 24 | public | ||
| 25 | EmacsDrawPoint (EmacsDrawable drawable, int x, int y, | ||
| 26 | EmacsGC immutableGC) | ||
| 27 | { | ||
| 28 | super (drawable, x, y, 1, 1, immutableGC); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java new file mode 100644 index 00000000000..462bf7c85b5 --- /dev/null +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java | |||
| @@ -0,0 +1,127 @@ | |||
| 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 android.graphics.Bitmap; | ||
| 23 | import android.graphics.Canvas; | ||
| 24 | import android.graphics.Paint; | ||
| 25 | import android.graphics.PorterDuff.Mode; | ||
| 26 | import android.graphics.PorterDuffXfermode; | ||
| 27 | import android.graphics.Rect; | ||
| 28 | import android.graphics.Xfermode; | ||
| 29 | |||
| 30 | public class EmacsDrawRectangle implements EmacsPaintReq | ||
| 31 | { | ||
| 32 | private int x, y, width, height; | ||
| 33 | private EmacsDrawable drawable; | ||
| 34 | private EmacsGC immutableGC; | ||
| 35 | private static Xfermode xorAlu, srcInAlu; | ||
| 36 | |||
| 37 | static | ||
| 38 | { | ||
| 39 | xorAlu = new PorterDuffXfermode (Mode.XOR); | ||
| 40 | srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); | ||
| 41 | }; | ||
| 42 | |||
| 43 | public | ||
| 44 | EmacsDrawRectangle (EmacsDrawable drawable, int x, int y, | ||
| 45 | int width, int height, | ||
| 46 | EmacsGC immutableGC) | ||
| 47 | { | ||
| 48 | this.drawable = drawable; | ||
| 49 | this.x = x; | ||
| 50 | this.y = y; | ||
| 51 | this.width = width; | ||
| 52 | this.height = height; | ||
| 53 | this.immutableGC = immutableGC; | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | public Rect | ||
| 58 | getRect () | ||
| 59 | { | ||
| 60 | return new Rect (x, y, x + width, y + height); | ||
| 61 | } | ||
| 62 | |||
| 63 | @Override | ||
| 64 | public EmacsDrawable | ||
| 65 | getDrawable () | ||
| 66 | { | ||
| 67 | return drawable; | ||
| 68 | } | ||
| 69 | |||
| 70 | @Override | ||
| 71 | public EmacsGC | ||
| 72 | getGC () | ||
| 73 | { | ||
| 74 | return immutableGC; | ||
| 75 | } | ||
| 76 | |||
| 77 | @Override | ||
| 78 | public void | ||
| 79 | paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC) | ||
| 80 | { | ||
| 81 | int alu; | ||
| 82 | Paint maskPaint; | ||
| 83 | Canvas maskCanvas; | ||
| 84 | Bitmap maskBitmap; | ||
| 85 | Rect rect, srcRect; | ||
| 86 | |||
| 87 | /* TODO implement stippling. */ | ||
| 88 | if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) | ||
| 89 | return; | ||
| 90 | |||
| 91 | alu = immutableGC.function; | ||
| 92 | rect = getRect (); | ||
| 93 | |||
| 94 | paint.setStyle (Paint.Style.STROKE); | ||
| 95 | |||
| 96 | if (alu == EmacsGC.GC_COPY) | ||
| 97 | paint.setXfermode (null); | ||
| 98 | else | ||
| 99 | paint.setXfermode (xorAlu); | ||
| 100 | |||
| 101 | if (immutableGC.clip_mask == null) | ||
| 102 | { | ||
| 103 | paint.setColor (immutableGC.foreground | 0xff000000); | ||
| 104 | canvas.drawRect (rect, paint); | ||
| 105 | } | ||
| 106 | else | ||
| 107 | { | ||
| 108 | maskPaint = new Paint (); | ||
| 109 | maskBitmap | ||
| 110 | = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, | ||
| 111 | true); | ||
| 112 | |||
| 113 | if (maskBitmap == null) | ||
| 114 | return; | ||
| 115 | |||
| 116 | maskPaint.setXfermode (srcInAlu); | ||
| 117 | maskPaint.setColor (immutableGC.foreground | 0xff000000); | ||
| 118 | maskCanvas = new Canvas (maskBitmap); | ||
| 119 | srcRect = new Rect (0, 0, maskBitmap.getWidth (), | ||
| 120 | maskBitmap.getHeight ()); | ||
| 121 | maskCanvas.drawRect (srcRect, maskPaint); | ||
| 122 | canvas.drawBitmap (maskBitmap, srcRect, rect, paint); | ||
| 123 | } | ||
| 124 | |||
| 125 | paint.setXfermode (null); | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/java/org/gnu/emacs/EmacsDrawable.java b/java/org/gnu/emacs/EmacsDrawable.java new file mode 100644 index 00000000000..19062137213 --- /dev/null +++ b/java/org/gnu/emacs/EmacsDrawable.java | |||
| @@ -0,0 +1,33 @@ | |||
| 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 android.graphics.Rect; | ||
| 23 | import android.graphics.Bitmap; | ||
| 24 | import android.graphics.Canvas; | ||
| 25 | |||
| 26 | public interface EmacsDrawable | ||
| 27 | { | ||
| 28 | public Canvas lockCanvas (); | ||
| 29 | public void unlockCanvas (); | ||
| 30 | public void damageRect (Rect damageRect); | ||
| 31 | public Bitmap getBitmap (); | ||
| 32 | public boolean isDestroyed (); | ||
| 33 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java new file mode 100644 index 00000000000..3198c7f07c4 --- /dev/null +++ b/java/org/gnu/emacs/EmacsFillPolygon.java | |||
| @@ -0,0 +1,150 @@ | |||
| 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.lang.Math; | ||
| 23 | |||
| 24 | import android.graphics.Bitmap; | ||
| 25 | import android.graphics.Canvas; | ||
| 26 | import android.graphics.Paint; | ||
| 27 | import android.graphics.Path; | ||
| 28 | import android.graphics.Point; | ||
| 29 | import android.graphics.PorterDuff.Mode; | ||
| 30 | import android.graphics.PorterDuffXfermode; | ||
| 31 | import android.graphics.Rect; | ||
| 32 | import android.graphics.RectF; | ||
| 33 | import android.graphics.Xfermode; | ||
| 34 | |||
| 35 | public class EmacsFillPolygon implements EmacsPaintReq | ||
| 36 | { | ||
| 37 | private EmacsDrawable drawable; | ||
| 38 | private EmacsGC immutableGC; | ||
| 39 | private Path path; | ||
| 40 | |||
| 41 | private static Xfermode xorAlu, srcInAlu; | ||
| 42 | |||
| 43 | static | ||
| 44 | { | ||
| 45 | xorAlu = new PorterDuffXfermode (Mode.XOR); | ||
| 46 | srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); | ||
| 47 | }; | ||
| 48 | |||
| 49 | public | ||
| 50 | EmacsFillPolygon (EmacsDrawable drawable, Point points[], | ||
| 51 | EmacsGC immutableGC) | ||
| 52 | { | ||
| 53 | int i; | ||
| 54 | |||
| 55 | this.drawable = drawable; | ||
| 56 | this.immutableGC = immutableGC; | ||
| 57 | |||
| 58 | /* Build the path from the given array of points. */ | ||
| 59 | path = new Path (); | ||
| 60 | |||
| 61 | if (points.length >= 1) | ||
| 62 | { | ||
| 63 | path.moveTo (points[0].x, points[0].y); | ||
| 64 | |||
| 65 | for (i = 1; i < points.length; ++i) | ||
| 66 | path.lineTo (points[i].x, points[i].y); | ||
| 67 | |||
| 68 | path.close (); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | @Override | ||
| 73 | public Rect | ||
| 74 | getRect () | ||
| 75 | { | ||
| 76 | RectF rect; | ||
| 77 | |||
| 78 | rect = new RectF (0, 0, 0, 0); | ||
| 79 | path.computeBounds (rect, true); | ||
| 80 | |||
| 81 | return new Rect ((int) Math.floor (rect.left), | ||
| 82 | (int) Math.floor (rect.top), | ||
| 83 | (int) Math.ceil (rect.right), | ||
| 84 | (int) Math.ceil (rect.bottom)); | ||
| 85 | } | ||
| 86 | |||
| 87 | @Override | ||
| 88 | public EmacsDrawable | ||
| 89 | getDrawable () | ||
| 90 | { | ||
| 91 | return drawable; | ||
| 92 | } | ||
| 93 | |||
| 94 | @Override | ||
| 95 | public EmacsGC | ||
| 96 | getGC () | ||
| 97 | { | ||
| 98 | return immutableGC; | ||
| 99 | } | ||
| 100 | |||
| 101 | @Override | ||
| 102 | public void | ||
| 103 | paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC) | ||
| 104 | { | ||
| 105 | int alu; | ||
| 106 | Paint maskPaint; | ||
| 107 | Canvas maskCanvas; | ||
| 108 | Bitmap maskBitmap; | ||
| 109 | Rect rect; | ||
| 110 | |||
| 111 | /* TODO implement stippling. */ | ||
| 112 | if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) | ||
| 113 | return; | ||
| 114 | |||
| 115 | alu = immutableGC.function; | ||
| 116 | rect = getRect (); | ||
| 117 | |||
| 118 | if (alu == EmacsGC.GC_COPY) | ||
| 119 | paint.setXfermode (null); | ||
| 120 | else | ||
| 121 | paint.setXfermode (xorAlu); | ||
| 122 | |||
| 123 | paint.setStyle (Paint.Style.FILL); | ||
| 124 | |||
| 125 | if (immutableGC.clip_mask == null) | ||
| 126 | { | ||
| 127 | paint.setColor (immutableGC.foreground | 0xff000000); | ||
| 128 | canvas.drawPath (path, paint); | ||
| 129 | } | ||
| 130 | else | ||
| 131 | { | ||
| 132 | maskPaint = new Paint (); | ||
| 133 | maskBitmap = immutableGC.clip_mask.bitmap; | ||
| 134 | maskBitmap = maskBitmap.copy (Bitmap.Config.ARGB_8888, | ||
| 135 | true); | ||
| 136 | |||
| 137 | if (maskBitmap == null) | ||
| 138 | return; | ||
| 139 | |||
| 140 | maskPaint.setXfermode (srcInAlu); | ||
| 141 | maskPaint.setColor (immutableGC.foreground | 0xff000000); | ||
| 142 | maskCanvas = new Canvas (maskBitmap); | ||
| 143 | path.offset (-rect.left, -rect.top, null); | ||
| 144 | maskCanvas.drawPath (path, maskPaint); | ||
| 145 | canvas.drawBitmap (maskBitmap, new Rect (0, 0, rect.width (), | ||
| 146 | rect.height ()), | ||
| 147 | rect, paint); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java new file mode 100644 index 00000000000..7246c13de7f --- /dev/null +++ b/java/org/gnu/emacs/EmacsFillRectangle.java | |||
| @@ -0,0 +1,129 @@ | |||
| 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 android.graphics.Bitmap; | ||
| 23 | import android.graphics.Canvas; | ||
| 24 | import android.graphics.Paint; | ||
| 25 | import android.graphics.PorterDuff.Mode; | ||
| 26 | import android.graphics.PorterDuffXfermode; | ||
| 27 | import android.graphics.Rect; | ||
| 28 | import android.graphics.Xfermode; | ||
| 29 | |||
| 30 | import android.util.Log; | ||
| 31 | |||
| 32 | public class EmacsFillRectangle implements EmacsPaintReq | ||
| 33 | { | ||
| 34 | private int x, y, width, height; | ||
| 35 | private EmacsDrawable drawable; | ||
| 36 | private EmacsGC immutableGC; | ||
| 37 | private static Xfermode xorAlu, srcInAlu; | ||
| 38 | |||
| 39 | static | ||
| 40 | { | ||
| 41 | xorAlu = new PorterDuffXfermode (Mode.XOR); | ||
| 42 | srcInAlu = new PorterDuffXfermode (Mode.SRC_IN); | ||
| 43 | }; | ||
| 44 | |||
| 45 | public | ||
| 46 | EmacsFillRectangle (EmacsDrawable drawable, int x, int y, | ||
| 47 | int width, int height, | ||
| 48 | EmacsGC immutableGC) | ||
| 49 | { | ||
| 50 | this.drawable = drawable; | ||
| 51 | this.x = x; | ||
| 52 | this.y = y; | ||
| 53 | this.width = width; | ||
| 54 | this.height = height; | ||
| 55 | this.immutableGC = immutableGC; | ||
| 56 | } | ||
| 57 | |||
| 58 | @Override | ||
| 59 | public Rect | ||
| 60 | getRect () | ||
| 61 | { | ||
| 62 | return new Rect (x, y, x + width, y + height); | ||
| 63 | } | ||
| 64 | |||
| 65 | @Override | ||
| 66 | public EmacsDrawable | ||
| 67 | getDrawable () | ||
| 68 | { | ||
| 69 | return drawable; | ||
| 70 | } | ||
| 71 | |||
| 72 | @Override | ||
| 73 | public EmacsGC | ||
| 74 | getGC () | ||
| 75 | { | ||
| 76 | return immutableGC; | ||
| 77 | } | ||
| 78 | |||
| 79 | @Override | ||
| 80 | public void | ||
| 81 | paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC) | ||
| 82 | { | ||
| 83 | int alu; | ||
| 84 | Paint maskPaint; | ||
| 85 | Canvas maskCanvas; | ||
| 86 | Bitmap maskBitmap; | ||
| 87 | Rect rect, srcRect; | ||
| 88 | |||
| 89 | /* TODO implement stippling. */ | ||
| 90 | if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) | ||
| 91 | return; | ||
| 92 | |||
| 93 | alu = immutableGC.function; | ||
| 94 | rect = getRect (); | ||
| 95 | |||
| 96 | paint.setStyle (Paint.Style.FILL); | ||
| 97 | |||
| 98 | if (alu == EmacsGC.GC_COPY) | ||
| 99 | paint.setXfermode (null); | ||
| 100 | else | ||
| 101 | paint.setXfermode (xorAlu); | ||
| 102 | |||
| 103 | if (immutableGC.clip_mask == null) | ||
| 104 | { | ||
| 105 | paint.setColor (immutableGC.foreground | 0xff000000); | ||
| 106 | canvas.drawRect (rect, paint); | ||
| 107 | } | ||
| 108 | else | ||
| 109 | { | ||
| 110 | maskPaint = new Paint (); | ||
| 111 | maskBitmap | ||
| 112 | = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888, | ||
| 113 | true); | ||
| 114 | |||
| 115 | if (maskBitmap == null) | ||
| 116 | return; | ||
| 117 | |||
| 118 | maskPaint.setXfermode (srcInAlu); | ||
| 119 | maskPaint.setColor (immutableGC.foreground | 0xff000000); | ||
| 120 | maskCanvas = new Canvas (maskBitmap); | ||
| 121 | srcRect = new Rect (0, 0, maskBitmap.getWidth (), | ||
| 122 | maskBitmap.getHeight ()); | ||
| 123 | maskCanvas.drawRect (srcRect, maskPaint); | ||
| 124 | canvas.drawBitmap (maskBitmap, srcRect, rect, paint); | ||
| 125 | } | ||
| 126 | |||
| 127 | paint.setXfermode (null); | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java new file mode 100644 index 00000000000..f419e71059d --- /dev/null +++ b/java/org/gnu/emacs/EmacsFontDriver.java | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | /* Font backend 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 | |||
| 24 | public abstract class EmacsFontDriver | ||
| 25 | { | ||
| 26 | /* Font weights. */ | ||
| 27 | public static final int THIN = 0; | ||
| 28 | public static final int ULTRA_LIGHT = 40; | ||
| 29 | public static final int LIGHT = 50; | ||
| 30 | public static final int SEMI_LIGHT = 55; | ||
| 31 | public static final int REGULAR = 80; | ||
| 32 | public static final int MEDIUM = 100; | ||
| 33 | public static final int SEMI_BOLD = 180; | ||
| 34 | public static final int BOLD = 200; | ||
| 35 | public static final int EXTRA_BOLD = 205; | ||
| 36 | public static final int BLACK = 210; | ||
| 37 | public static final int ULTRA_HEAVY = 250; | ||
| 38 | |||
| 39 | /* Font slants. */ | ||
| 40 | public static final int REVERSE_OBLIQUE = 0; | ||
| 41 | public static final int REVERSE_ITALIC = 10; | ||
| 42 | public static final int NORMAL = 100; | ||
| 43 | public static final int ITALIC = 200; | ||
| 44 | public static final int OBLIQUE = 210; | ||
| 45 | |||
| 46 | /* Font widths. */ | ||
| 47 | public static final int ULTRA_CONDENSED = 50; | ||
| 48 | public static final int EXTRA_CONDENSED = 63; | ||
| 49 | public static final int CONDENSED = 75; | ||
| 50 | public static final int SEMI_CONDENSED = 87; | ||
| 51 | public static final int UNSPECIFIED = 100; | ||
| 52 | public static final int SEMI_EXPANDED = 113; | ||
| 53 | public static final int EXPANDED = 125; | ||
| 54 | public static final int EXTRA_EXPANDED = 150; | ||
| 55 | public static final int ULTRA_EXPANDED = 200; | ||
| 56 | |||
| 57 | /* Font spacings. */ | ||
| 58 | public static final int PROPORTIONAL = 0; | ||
| 59 | public static final int DUAL = 90; | ||
| 60 | public static final int MONO = 100; | ||
| 61 | public static final int CHARCELL = 110; | ||
| 62 | |||
| 63 | public class FontSpec | ||
| 64 | { | ||
| 65 | /* The fields below mean the same as they do in enum | ||
| 66 | font_property_index in font.h. */ | ||
| 67 | |||
| 68 | public String foundry; | ||
| 69 | public String family; | ||
| 70 | public String adstyle; | ||
| 71 | public String registry; | ||
| 72 | public Integer width; | ||
| 73 | public Integer weight; | ||
| 74 | public Integer slant; | ||
| 75 | public Integer size; | ||
| 76 | public Integer spacing; | ||
| 77 | public Integer avgwidth; | ||
| 78 | |||
| 79 | @Override | ||
| 80 | public String | ||
| 81 | toString () | ||
| 82 | { | ||
| 83 | return ("foundry: " + foundry | ||
| 84 | + " family: " + family | ||
| 85 | + " adstyle: " + adstyle | ||
| 86 | + " registry: " + registry | ||
| 87 | + " width: " + width | ||
| 88 | + " weight: " + weight | ||
| 89 | + " slant: " + slant | ||
| 90 | + " spacing: " + spacing | ||
| 91 | + " avgwidth: " + avgwidth); | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | |||
| 95 | public class FontMetrics | ||
| 96 | { | ||
| 97 | public short lbearing; | ||
| 98 | public short rbearing; | ||
| 99 | public short width; | ||
| 100 | public short ascent; | ||
| 101 | public short descent; | ||
| 102 | } | ||
| 103 | |||
| 104 | public class FontEntity extends FontSpec | ||
| 105 | { | ||
| 106 | /* No extra fields here. */ | ||
| 107 | }; | ||
| 108 | |||
| 109 | public abstract class FontObject extends FontSpec | ||
| 110 | { | ||
| 111 | public int minWidth; | ||
| 112 | public int maxWidth; | ||
| 113 | public int pixelSize; | ||
| 114 | public int height; | ||
| 115 | public int spaceWidth; | ||
| 116 | public int averageWidth; | ||
| 117 | public int ascent; | ||
| 118 | public int descent; | ||
| 119 | public int underlineThickness; | ||
| 120 | public int underlinePosition; | ||
| 121 | public int baselineOffset; | ||
| 122 | public int relativeCompose; | ||
| 123 | public int defaultAscent; | ||
| 124 | public int encodingCharset; | ||
| 125 | public int repertoryCharset; | ||
| 126 | |||
| 127 | public | ||
| 128 | FontObject () | ||
| 129 | { | ||
| 130 | encodingCharset = -1; | ||
| 131 | repertoryCharset = -1; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | /* These mean the same as they do in struct font_driver. */ | ||
| 136 | public abstract FontEntity[] list (FontSpec fontSpec); | ||
| 137 | public abstract FontEntity match (FontSpec fontSpec); | ||
| 138 | public abstract String[] listFamilies (); | ||
| 139 | public abstract FontObject openFont (FontEntity fontEntity, int pixelSize); | ||
| 140 | public abstract int hasChar (FontSpec font, char charCode); | ||
| 141 | public abstract void textExtents (FontObject font, int code[], | ||
| 142 | FontMetrics fontMetrics[]); | ||
| 143 | public abstract int encodeChar (FontObject fontObject, char charCode); | ||
| 144 | |||
| 145 | public static EmacsFontDriver | ||
| 146 | createFontDriver () | ||
| 147 | { | ||
| 148 | return new EmacsSdk7FontDriver (); | ||
| 149 | } | ||
| 150 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java new file mode 100644 index 00000000000..0becb04519d --- /dev/null +++ b/java/org/gnu/emacs/EmacsGC.java | |||
| @@ -0,0 +1,116 @@ | |||
| 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 android.graphics.Rect; | ||
| 23 | |||
| 24 | /* X like graphics context structures. Keep the enums in synch with | ||
| 25 | androidgui.h! */ | ||
| 26 | |||
| 27 | public class EmacsGC extends EmacsHandleObject | ||
| 28 | { | ||
| 29 | public static final int GC_COPY = 0; | ||
| 30 | public static final int GC_XOR = 1; | ||
| 31 | |||
| 32 | public static final int GC_FILL_SOLID = 0; | ||
| 33 | public static final int GC_FILL_OPAQUE_STIPPLED = 1; | ||
| 34 | |||
| 35 | public int function, fill_style; | ||
| 36 | public int foreground, background; | ||
| 37 | public int clip_x_origin, clip_y_origin; | ||
| 38 | public int ts_origin_x, ts_origin_y; | ||
| 39 | public Rect clip_rects[]; | ||
| 40 | public EmacsPixmap clip_mask, stipple; | ||
| 41 | private boolean dirty; | ||
| 42 | private EmacsGC immutableGC; | ||
| 43 | |||
| 44 | /* The following fields are only set on immutable GCs. */ | ||
| 45 | |||
| 46 | public | ||
| 47 | EmacsGC (short handle) | ||
| 48 | { | ||
| 49 | /* For historical reasons the C code has an extra layer of | ||
| 50 | indirection above this GC handle. struct android_gc is the GC | ||
| 51 | used by Emacs code, while android_gcontext is the type of the | ||
| 52 | handle. */ | ||
| 53 | super (handle); | ||
| 54 | |||
| 55 | fill_style = GC_FILL_SOLID; | ||
| 56 | function = GC_COPY; | ||
| 57 | foreground = 0; | ||
| 58 | background = 0xffffffff; | ||
| 59 | } | ||
| 60 | |||
| 61 | public | ||
| 62 | EmacsGC (EmacsGC source) | ||
| 63 | { | ||
| 64 | super ((short) 0); | ||
| 65 | |||
| 66 | int i; | ||
| 67 | |||
| 68 | function = source.function; | ||
| 69 | fill_style = source.fill_style; | ||
| 70 | foreground = source.foreground; | ||
| 71 | background = source.background; | ||
| 72 | clip_x_origin = source.clip_x_origin; | ||
| 73 | clip_y_origin = source.clip_y_origin; | ||
| 74 | clip_rects = source.clip_rects; | ||
| 75 | clip_mask = source.clip_mask; | ||
| 76 | stipple = source.stipple; | ||
| 77 | ts_origin_x = source.ts_origin_x; | ||
| 78 | ts_origin_y = source.ts_origin_y; | ||
| 79 | |||
| 80 | /* Offset all the clip rects by ts_origin_x and ts_origin_y. */ | ||
| 81 | |||
| 82 | if ((ts_origin_x != 0 || ts_origin_y != 0) | ||
| 83 | && clip_rects != null) | ||
| 84 | { | ||
| 85 | clip_rects = new Rect[clip_rects.length]; | ||
| 86 | |||
| 87 | for (i = 0; i < clip_rects.length; ++i) | ||
| 88 | { | ||
| 89 | clip_rects[i] = new Rect (source.clip_rects[i]); | ||
| 90 | clip_rects[i].offset (ts_origin_x, | ||
| 91 | ts_origin_y); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Mark this GC as dirty. This means immutableGC will return a new | ||
| 97 | copy of this GC the next time it is called. */ | ||
| 98 | |||
| 99 | public void | ||
| 100 | markDirty () | ||
| 101 | { | ||
| 102 | dirty = true; | ||
| 103 | } | ||
| 104 | |||
| 105 | public EmacsGC | ||
| 106 | immutableGC () | ||
| 107 | { | ||
| 108 | if (immutableGC == null || dirty) | ||
| 109 | { | ||
| 110 | immutableGC = new EmacsGC (this); | ||
| 111 | dirty = false; | ||
| 112 | } | ||
| 113 | |||
| 114 | return immutableGC; | ||
| 115 | }; | ||
| 116 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsHandleObject.java b/java/org/gnu/emacs/EmacsHandleObject.java new file mode 100644 index 00000000000..a57a3bbdfa9 --- /dev/null +++ b/java/org/gnu/emacs/EmacsHandleObject.java | |||
| @@ -0,0 +1,62 @@ | |||
| 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 | import java.lang.Object; | ||
| 25 | import java.lang.IllegalStateException; | ||
| 26 | |||
| 27 | /* This defines something that is a so-called ``handle''. Handles | ||
| 28 | must be created by C code, and will remain existing until | ||
| 29 | destroyHandle is called. C code then refers to the handle by a | ||
| 30 | number which maps into the Java object representing the handle. | ||
| 31 | |||
| 32 | All handle operations must be done from the Emacs thread. */ | ||
| 33 | |||
| 34 | public abstract class EmacsHandleObject | ||
| 35 | { | ||
| 36 | /* Whether or not this handle has been destroyed. */ | ||
| 37 | volatile boolean destroyed; | ||
| 38 | |||
| 39 | /* The handle associated with this object. */ | ||
| 40 | public short handle; | ||
| 41 | |||
| 42 | public | ||
| 43 | EmacsHandleObject (short handle) | ||
| 44 | { | ||
| 45 | this.handle = handle; | ||
| 46 | } | ||
| 47 | |||
| 48 | public void | ||
| 49 | destroyHandle () throws IllegalStateException | ||
| 50 | { | ||
| 51 | synchronized (this) | ||
| 52 | { | ||
| 53 | destroyed = true; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | public boolean | ||
| 58 | isDestroyed () | ||
| 59 | { | ||
| 60 | return destroyed; | ||
| 61 | } | ||
| 62 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java new file mode 100644 index 00000000000..6550e6fa2a1 --- /dev/null +++ b/java/org/gnu/emacs/EmacsNative.java | |||
| @@ -0,0 +1,72 @@ | |||
| 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.lang.System; | ||
| 23 | |||
| 24 | import android.content.res.AssetManager; | ||
| 25 | |||
| 26 | public class EmacsNative | ||
| 27 | { | ||
| 28 | /* Set certain parameters before initializing Emacs. This proves | ||
| 29 | that libemacs.so is being loaded from Java code. | ||
| 30 | |||
| 31 | assetManager must be the asset manager associated with the | ||
| 32 | context that is loading Emacs. It is saved and remains for the | ||
| 33 | remainder the lifetime of the Emacs process. | ||
| 34 | |||
| 35 | filesDir must be the package's data storage location for the | ||
| 36 | current Android user. | ||
| 37 | |||
| 38 | libDir must be the package's data storage location for native | ||
| 39 | libraries. It is used as PATH. | ||
| 40 | |||
| 41 | emacsService must be the emacsService singleton. */ | ||
| 42 | public static native void setEmacsParams (AssetManager assetManager, | ||
| 43 | String filesDir, | ||
| 44 | String libDir, | ||
| 45 | EmacsService emacsService); | ||
| 46 | |||
| 47 | /* Initialize Emacs with the argument array ARGV. Each argument | ||
| 48 | must contain a NULL terminated string, or else the behavior is | ||
| 49 | undefined. */ | ||
| 50 | public static native void initEmacs (String argv[]); | ||
| 51 | |||
| 52 | /* Abort and generate a native core dump. */ | ||
| 53 | public static native void emacsAbort (); | ||
| 54 | |||
| 55 | /* Send an ANDROID_CONFIGURE_NOTIFY event. */ | ||
| 56 | public static native void sendConfigureNotify (short window, long time, | ||
| 57 | int x, int y, int width, | ||
| 58 | int height); | ||
| 59 | |||
| 60 | /* Send an ANDROID_KEY_PRESS event. */ | ||
| 61 | public static native void sendKeyPress (short window, long time, int state, | ||
| 62 | int keyCode); | ||
| 63 | |||
| 64 | /* Send an ANDROID_KEY_RELEASE event. */ | ||
| 65 | public static native void sendKeyRelease (short window, long time, int state, | ||
| 66 | int keyRelease); | ||
| 67 | |||
| 68 | static | ||
| 69 | { | ||
| 70 | System.loadLibrary ("emacs"); | ||
| 71 | }; | ||
| 72 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsPaintQueue.java b/java/org/gnu/emacs/EmacsPaintQueue.java new file mode 100644 index 00000000000..5af5868d3b9 --- /dev/null +++ b/java/org/gnu/emacs/EmacsPaintQueue.java | |||
| @@ -0,0 +1,138 @@ | |||
| 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.LinkedList; | ||
| 23 | import java.util.List; | ||
| 24 | |||
| 25 | import android.graphics.Canvas; | ||
| 26 | import android.graphics.Paint; | ||
| 27 | import android.graphics.Rect; | ||
| 28 | |||
| 29 | public class EmacsPaintQueue | ||
| 30 | { | ||
| 31 | /* Queue of paint operations. This is modified from the Emacs | ||
| 32 | thread, and entire paint queues are periodically flushed to the | ||
| 33 | application thread where it is executed. */ | ||
| 34 | private List<EmacsPaintReq> paintOperations; | ||
| 35 | |||
| 36 | /* Number of operations in this queue. */ | ||
| 37 | public int numRequests; | ||
| 38 | |||
| 39 | public | ||
| 40 | EmacsPaintQueue () | ||
| 41 | { | ||
| 42 | paintOperations = new LinkedList<EmacsPaintReq> (); | ||
| 43 | } | ||
| 44 | |||
| 45 | public void | ||
| 46 | run () | ||
| 47 | { | ||
| 48 | EmacsDrawable drawable, last; | ||
| 49 | Canvas canvas; | ||
| 50 | EmacsGC gc, lastGC; | ||
| 51 | int i; | ||
| 52 | Paint paint; | ||
| 53 | Rect rect, offsetRect, copyRect; | ||
| 54 | |||
| 55 | canvas = null; | ||
| 56 | last = null; | ||
| 57 | gc = null; | ||
| 58 | paint = new Paint (); | ||
| 59 | |||
| 60 | for (EmacsPaintReq req : paintOperations) | ||
| 61 | { | ||
| 62 | drawable = req.getDrawable (); | ||
| 63 | |||
| 64 | synchronized (req) | ||
| 65 | { | ||
| 66 | /* Ignore graphics requests for drawables that have been | ||
| 67 | destroyed. */ | ||
| 68 | if (drawable.isDestroyed ()) | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | |||
| 72 | canvas = drawable.lockCanvas (); | ||
| 73 | |||
| 74 | if (canvas == null) | ||
| 75 | /* No canvas is currently available. */ | ||
| 76 | continue; | ||
| 77 | |||
| 78 | lastGC = gc; | ||
| 79 | gc = req.getGC (); | ||
| 80 | rect = req.getRect (); | ||
| 81 | |||
| 82 | if (gc.clip_rects == null) | ||
| 83 | { | ||
| 84 | /* No clipping is applied. Just draw and continue. */ | ||
| 85 | canvas.save (); | ||
| 86 | req.paintTo (canvas, paint, gc); | ||
| 87 | canvas.restore (); | ||
| 88 | drawable.damageRect (rect); | ||
| 89 | continue; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (gc.clip_rects != null && gc.clip_rects.length > 0) | ||
| 93 | { | ||
| 94 | canvas.save (); | ||
| 95 | |||
| 96 | if (gc.clip_rects.length == 1) | ||
| 97 | { | ||
| 98 | /* There is only a single clip rect, which is simple | ||
| 99 | enough. */ | ||
| 100 | canvas.clipRect (gc.clip_rects[0]); | ||
| 101 | req.paintTo (canvas, paint, gc); | ||
| 102 | } | ||
| 103 | else | ||
| 104 | { | ||
| 105 | /* There are multiple clip rects. Android doesn't let | ||
| 106 | programs use RegionOp.UNION on the clip rectangle, | ||
| 107 | so Emacs must iterate over each intersection and | ||
| 108 | paint it manually. This seems inefficient but | ||
| 109 | thankfully Emacs never seems to use more than one | ||
| 110 | clip rect. */ | ||
| 111 | |||
| 112 | for (i = 0; i < gc.clip_rects.length; ++i) | ||
| 113 | { | ||
| 114 | copyRect = new Rect (gc.clip_rects[i]); | ||
| 115 | |||
| 116 | if (copyRect.intersect (rect)) | ||
| 117 | { | ||
| 118 | canvas.save (); | ||
| 119 | canvas.clipRect (copyRect); | ||
| 120 | req.paintTo (canvas, paint, gc); | ||
| 121 | canvas.restore (); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | drawable.damageRect (rect); | ||
| 127 | canvas.restore (); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | public void | ||
| 133 | appendPaintOperation (EmacsPaintReq req) | ||
| 134 | { | ||
| 135 | paintOperations.add (req); | ||
| 136 | numRequests++; | ||
| 137 | } | ||
| 138 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsPaintReq.java b/java/org/gnu/emacs/EmacsPaintReq.java new file mode 100644 index 00000000000..5b14b005093 --- /dev/null +++ b/java/org/gnu/emacs/EmacsPaintReq.java | |||
| @@ -0,0 +1,33 @@ | |||
| 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 android.graphics.Canvas; | ||
| 23 | import android.graphics.Paint; | ||
| 24 | import android.graphics.Rect; | ||
| 25 | |||
| 26 | public interface EmacsPaintReq | ||
| 27 | { | ||
| 28 | public EmacsDrawable getDrawable (); | ||
| 29 | public EmacsGC getGC (); | ||
| 30 | public void paintTo (Canvas canvas, Paint paint, | ||
| 31 | EmacsGC immutableGC); | ||
| 32 | public Rect getRect (); | ||
| 33 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java new file mode 100644 index 00000000000..ccd5f1e0043 --- /dev/null +++ b/java/org/gnu/emacs/EmacsPixmap.java | |||
| @@ -0,0 +1,102 @@ | |||
| 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.lang.IllegalArgumentException; | ||
| 23 | |||
| 24 | import android.graphics.Bitmap; | ||
| 25 | import android.graphics.Canvas; | ||
| 26 | import android.graphics.Rect; | ||
| 27 | |||
| 28 | /* Drawable backed by bitmap. */ | ||
| 29 | |||
| 30 | public class EmacsPixmap extends EmacsHandleObject | ||
| 31 | implements EmacsDrawable | ||
| 32 | { | ||
| 33 | /* The depth of the bitmap. This is not actually used, just defined | ||
| 34 | in order to be consistent with X. */ | ||
| 35 | public int depth, width, height; | ||
| 36 | |||
| 37 | /* The bitmap itself. */ | ||
| 38 | public Bitmap bitmap; | ||
| 39 | |||
| 40 | /* The canvas used to draw to BITMAP. */ | ||
| 41 | public Canvas canvas; | ||
| 42 | |||
| 43 | public | ||
| 44 | EmacsPixmap (short handle, int colors[], int width, | ||
| 45 | int height, int depth) | ||
| 46 | { | ||
| 47 | super (handle); | ||
| 48 | |||
| 49 | if (depth != 1 && depth != 24) | ||
| 50 | throw new IllegalArgumentException ("Invalid depth specified" | ||
| 51 | + " for pixmap: " + depth); | ||
| 52 | |||
| 53 | switch (depth) | ||
| 54 | { | ||
| 55 | case 1: | ||
| 56 | bitmap = Bitmap.createBitmap (colors, width, height, | ||
| 57 | Bitmap.Config.ALPHA_8); | ||
| 58 | break; | ||
| 59 | |||
| 60 | case 24: | ||
| 61 | bitmap = Bitmap.createBitmap (colors, width, height, | ||
| 62 | Bitmap.Config.ARGB_8888); | ||
| 63 | bitmap.setHasAlpha (false); | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | |||
| 67 | this.width = width; | ||
| 68 | this.height = height; | ||
| 69 | this.depth = depth; | ||
| 70 | } | ||
| 71 | |||
| 72 | @Override | ||
| 73 | public Canvas | ||
| 74 | lockCanvas () | ||
| 75 | { | ||
| 76 | if (canvas == null) | ||
| 77 | canvas = new Canvas (bitmap); | ||
| 78 | |||
| 79 | return canvas; | ||
| 80 | } | ||
| 81 | |||
| 82 | @Override | ||
| 83 | public void | ||
| 84 | unlockCanvas () | ||
| 85 | { | ||
| 86 | |||
| 87 | } | ||
| 88 | |||
| 89 | @Override | ||
| 90 | public void | ||
| 91 | damageRect (Rect damageRect) | ||
| 92 | { | ||
| 93 | |||
| 94 | } | ||
| 95 | |||
| 96 | @Override | ||
| 97 | public Bitmap | ||
| 98 | getBitmap () | ||
| 99 | { | ||
| 100 | return bitmap; | ||
| 101 | } | ||
| 102 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java new file mode 100644 index 00000000000..5a8cdbfc75b --- /dev/null +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java | |||
| @@ -0,0 +1,460 @@ | |||
| 1 | /* Font backend 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.io.File; | ||
| 23 | import java.io.IOException; | ||
| 24 | |||
| 25 | import java.util.LinkedList; | ||
| 26 | import java.util.List; | ||
| 27 | |||
| 28 | import android.graphics.Paint; | ||
| 29 | import android.graphics.Rect; | ||
| 30 | import android.graphics.Typeface; | ||
| 31 | |||
| 32 | import android.util.Log; | ||
| 33 | |||
| 34 | public class EmacsSdk7FontDriver extends EmacsFontDriver | ||
| 35 | { | ||
| 36 | private static final String TOFU_STRING = "\uDB3F\uDFFD"; | ||
| 37 | private static final String EM_STRING = "m"; | ||
| 38 | private static final String TAG = "EmacsSdk7FontDriver"; | ||
| 39 | |||
| 40 | private class Sdk7Typeface | ||
| 41 | { | ||
| 42 | /* The typeface and paint. */ | ||
| 43 | public Typeface typeface; | ||
| 44 | public Paint typefacePaint; | ||
| 45 | public String familyName; | ||
| 46 | public int slant, width, weight, spacing; | ||
| 47 | |||
| 48 | public | ||
| 49 | Sdk7Typeface (String fileName, Typeface typeface) | ||
| 50 | { | ||
| 51 | String style, testString; | ||
| 52 | int index, measured, i; | ||
| 53 | float[] widths; | ||
| 54 | |||
| 55 | slant = NORMAL; | ||
| 56 | weight = REGULAR; | ||
| 57 | width = UNSPECIFIED; | ||
| 58 | spacing = PROPORTIONAL; | ||
| 59 | |||
| 60 | typefacePaint = new Paint (); | ||
| 61 | typefacePaint.setTypeface (typeface); | ||
| 62 | |||
| 63 | /* For the calls to measureText below. */ | ||
| 64 | typefacePaint.setTextSize (10.0f); | ||
| 65 | |||
| 66 | /* Parse the file name into some useful data. First, strip off | ||
| 67 | the extension. */ | ||
| 68 | fileName = fileName.split ("\\.", 2)[0]; | ||
| 69 | |||
| 70 | /* Next, split the file name by dashes. Everything before the | ||
| 71 | last dash is part of the family name. */ | ||
| 72 | index = fileName.lastIndexOf ("-"); | ||
| 73 | |||
| 74 | if (index > 0) | ||
| 75 | { | ||
| 76 | style = fileName.substring (index + 1, fileName.length ()); | ||
| 77 | familyName = fileName.substring (0, index); | ||
| 78 | |||
| 79 | /* Look for something describing the weight. */ | ||
| 80 | if (style.contains ("Thin")) | ||
| 81 | weight = THIN; | ||
| 82 | else if (style.contains ("UltraLight")) | ||
| 83 | weight = ULTRA_LIGHT; | ||
| 84 | else if (style.contains ("SemiLight")) | ||
| 85 | weight = SEMI_LIGHT; | ||
| 86 | else if (style.contains ("Light")) | ||
| 87 | weight = LIGHT; | ||
| 88 | else if (style.contains ("Medium")) | ||
| 89 | weight = MEDIUM; | ||
| 90 | else if (style.contains ("SemiBold")) | ||
| 91 | weight = SEMI_BOLD; | ||
| 92 | else if (style.contains ("ExtraBold")) | ||
| 93 | weight = EXTRA_BOLD; | ||
| 94 | else if (style.contains ("Bold")) | ||
| 95 | weight = BOLD; | ||
| 96 | else if (style.contains ("Black")) | ||
| 97 | weight = BLACK; | ||
| 98 | else if (style.contains ("UltraHeavy")) | ||
| 99 | weight = ULTRA_HEAVY; | ||
| 100 | |||
| 101 | /* And the slant. */ | ||
| 102 | if (style.contains ("ReverseOblique")) | ||
| 103 | slant = OBLIQUE; | ||
| 104 | else if (style.contains ("ReverseItalic")) | ||
| 105 | slant = REVERSE_ITALIC; | ||
| 106 | else if (style.contains ("Italic")) | ||
| 107 | slant = ITALIC; | ||
| 108 | else if (style.contains ("Oblique")) | ||
| 109 | slant = OBLIQUE; | ||
| 110 | |||
| 111 | /* Finally, the width. */ | ||
| 112 | if (style.contains ("UltraCondensed")) | ||
| 113 | width = ULTRA_CONDENSED; | ||
| 114 | else if (style.contains ("ExtraCondensed")) | ||
| 115 | width = EXTRA_CONDENSED; | ||
| 116 | else if (style.contains ("SemiCondensed")) | ||
| 117 | width = SEMI_CONDENSED; | ||
| 118 | else if (style.contains ("Condensed")) | ||
| 119 | width = CONDENSED; | ||
| 120 | else if (style.contains ("SemiExpanded")) | ||
| 121 | width = SEMI_EXPANDED; | ||
| 122 | else if (style.contains ("ExtraExpanded")) | ||
| 123 | width = EXTRA_EXPANDED; | ||
| 124 | else if (style.contains ("UltraExpanded")) | ||
| 125 | width = ULTRA_EXPANDED; | ||
| 126 | else if (style.contains ("Expanded")) | ||
| 127 | width = EXPANDED; | ||
| 128 | |||
| 129 | /* Guess the spacing information. */ | ||
| 130 | testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
| 131 | widths = new float[testString.length ()]; | ||
| 132 | |||
| 133 | measured = typefacePaint.getTextWidths (testString, | ||
| 134 | 0, testString.length (), | ||
| 135 | widths); | ||
| 136 | spacing = MONO; | ||
| 137 | for (i = 0; i < measured; ++i) | ||
| 138 | { | ||
| 139 | if (i != 0 && widths[i - 1] != widths[i]) | ||
| 140 | /* This isn't a monospace font. */ | ||
| 141 | spacing = PROPORTIONAL; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | else | ||
| 145 | familyName = fileName; | ||
| 146 | |||
| 147 | Log.d (TAG, "Initialized new typeface " + familyName); | ||
| 148 | } | ||
| 149 | |||
| 150 | @Override | ||
| 151 | public String | ||
| 152 | toString () | ||
| 153 | { | ||
| 154 | return ("Sdk7Typeface (" | ||
| 155 | + String.valueOf (familyName) + ", " | ||
| 156 | + String.valueOf (slant) + ", " | ||
| 157 | + String.valueOf (width) + ", " | ||
| 158 | + String.valueOf (weight) + ", " | ||
| 159 | + String.valueOf (spacing) + ")"); | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | |||
| 163 | private class Sdk7FontEntity extends FontEntity | ||
| 164 | { | ||
| 165 | /* The typeface. */ | ||
| 166 | public Sdk7Typeface typeface; | ||
| 167 | |||
| 168 | public | ||
| 169 | Sdk7FontEntity (Sdk7Typeface typeface) | ||
| 170 | { | ||
| 171 | float width; | ||
| 172 | |||
| 173 | foundry = "Google"; | ||
| 174 | family = typeface.familyName; | ||
| 175 | adstyle = null; | ||
| 176 | weight = typeface.weight; | ||
| 177 | slant = typeface.slant; | ||
| 178 | spacing = typeface.spacing; | ||
| 179 | width = typeface.width; | ||
| 180 | |||
| 181 | this.typeface = typeface; | ||
| 182 | } | ||
| 183 | }; | ||
| 184 | |||
| 185 | private class Sdk7FontObject extends FontObject | ||
| 186 | { | ||
| 187 | /* The typeface. */ | ||
| 188 | public Sdk7Typeface typeface; | ||
| 189 | |||
| 190 | /* The text size. */ | ||
| 191 | public int pixelSize; | ||
| 192 | |||
| 193 | public | ||
| 194 | Sdk7FontObject (Sdk7Typeface typeface, int pixelSize) | ||
| 195 | { | ||
| 196 | float totalWidth; | ||
| 197 | String testWidth, testString; | ||
| 198 | |||
| 199 | this.typeface = typeface; | ||
| 200 | this.pixelSize = pixelSize; | ||
| 201 | |||
| 202 | family = typeface.familyName; | ||
| 203 | adstyle = null; | ||
| 204 | weight = typeface.weight; | ||
| 205 | slant = typeface.slant; | ||
| 206 | spacing = typeface.spacing; | ||
| 207 | width = typeface.width; | ||
| 208 | |||
| 209 | /* Compute the ascent and descent. */ | ||
| 210 | typeface.typefacePaint.setTextSize (pixelSize); | ||
| 211 | ascent | ||
| 212 | = Math.round (-typeface.typefacePaint.ascent ()); | ||
| 213 | descent | ||
| 214 | = Math.round (typeface.typefacePaint.descent ()); | ||
| 215 | |||
| 216 | /* Compute the average width. */ | ||
| 217 | testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
| 218 | totalWidth = typeface.typefacePaint.measureText (testString); | ||
| 219 | |||
| 220 | if (totalWidth > 0) | ||
| 221 | avgwidth = Math.round (totalWidth | ||
| 222 | / testString.length ()); | ||
| 223 | |||
| 224 | /* Android doesn't expose the font average width and height | ||
| 225 | information, so this will have to do. */ | ||
| 226 | minWidth = maxWidth = avgwidth; | ||
| 227 | |||
| 228 | /* This is different from avgwidth in the font spec! */ | ||
| 229 | averageWidth = avgwidth; | ||
| 230 | |||
| 231 | /* Set the space width. */ | ||
| 232 | totalWidth = typeface.typefacePaint.measureText (" "); | ||
| 233 | spaceWidth = Math.round (totalWidth); | ||
| 234 | |||
| 235 | /* Set the height and default ascent. */ | ||
| 236 | height = ascent + descent; | ||
| 237 | defaultAscent = ascent; | ||
| 238 | } | ||
| 239 | }; | ||
| 240 | |||
| 241 | private String[] fontFamilyList; | ||
| 242 | private Sdk7Typeface[] typefaceList; | ||
| 243 | private Sdk7Typeface fallbackTypeface; | ||
| 244 | |||
| 245 | public | ||
| 246 | EmacsSdk7FontDriver () | ||
| 247 | { | ||
| 248 | int i; | ||
| 249 | File systemFontsDirectory, fontFile; | ||
| 250 | Typeface typeface; | ||
| 251 | |||
| 252 | systemFontsDirectory = new File ("/system/fonts"); | ||
| 253 | |||
| 254 | fontFamilyList = systemFontsDirectory.list (); | ||
| 255 | typefaceList = new Sdk7Typeface[fontFamilyList.length]; | ||
| 256 | |||
| 257 | /* It would be nice to avoid opening each and every font upon | ||
| 258 | startup. But that doesn't seem to be possible on | ||
| 259 | Android. */ | ||
| 260 | |||
| 261 | for (i = 0; i < fontFamilyList.length; ++i) | ||
| 262 | { | ||
| 263 | fontFile = new File (systemFontsDirectory, | ||
| 264 | fontFamilyList[i]); | ||
| 265 | typeface = Typeface.createFromFile (fontFile); | ||
| 266 | typefaceList[i] = new Sdk7Typeface (fontFile.getName (), | ||
| 267 | typeface); | ||
| 268 | } | ||
| 269 | |||
| 270 | fallbackTypeface = new Sdk7Typeface ("monospace", | ||
| 271 | Typeface.MONOSPACE); | ||
| 272 | } | ||
| 273 | |||
| 274 | private boolean | ||
| 275 | checkMatch (Sdk7Typeface typeface, FontSpec fontSpec) | ||
| 276 | { | ||
| 277 | if (fontSpec.family != null | ||
| 278 | && !fontSpec.family.equals (typeface.familyName)) | ||
| 279 | return false; | ||
| 280 | |||
| 281 | |||
| 282 | if (fontSpec.adstyle != null | ||
| 283 | && !fontSpec.adstyle.isEmpty ()) | ||
| 284 | /* return false; */; | ||
| 285 | |||
| 286 | if (fontSpec.slant != null | ||
| 287 | && !fontSpec.weight.equals (typeface.weight)) | ||
| 288 | return false; | ||
| 289 | |||
| 290 | if (fontSpec.spacing != null | ||
| 291 | && !fontSpec.spacing.equals (typeface.spacing)) | ||
| 292 | return false; | ||
| 293 | |||
| 294 | if (fontSpec.weight != null | ||
| 295 | && !fontSpec.weight.equals (typeface.weight)) | ||
| 296 | return false; | ||
| 297 | |||
| 298 | if (fontSpec.width != null | ||
| 299 | && !fontSpec.width.equals (typeface.width)) | ||
| 300 | return false; | ||
| 301 | |||
| 302 | return true; | ||
| 303 | } | ||
| 304 | |||
| 305 | @Override | ||
| 306 | public FontEntity[] | ||
| 307 | list (FontSpec fontSpec) | ||
| 308 | { | ||
| 309 | LinkedList<FontEntity> list; | ||
| 310 | int i; | ||
| 311 | |||
| 312 | list = new LinkedList<FontEntity> (); | ||
| 313 | |||
| 314 | Log.d (TAG, ("Looking for fonts matching font spec: " | ||
| 315 | + fontSpec.toString ())); | ||
| 316 | |||
| 317 | for (i = 0; i < typefaceList.length; ++i) | ||
| 318 | { | ||
| 319 | if (checkMatch (typefaceList[i], fontSpec)) | ||
| 320 | list.add (new Sdk7FontEntity (typefaceList[i])); | ||
| 321 | } | ||
| 322 | |||
| 323 | Log.d (TAG, "Found font entities: " + list.toString ()); | ||
| 324 | |||
| 325 | return (FontEntity[]) list.toArray (new FontEntity[0]); | ||
| 326 | } | ||
| 327 | |||
| 328 | @Override | ||
| 329 | public FontEntity | ||
| 330 | match (FontSpec fontSpec) | ||
| 331 | { | ||
| 332 | FontEntity[] entities; | ||
| 333 | int i; | ||
| 334 | |||
| 335 | entities = this.list (fontSpec); | ||
| 336 | |||
| 337 | if (entities.length == 0) | ||
| 338 | return new Sdk7FontEntity (fallbackTypeface); | ||
| 339 | |||
| 340 | return entities[0]; | ||
| 341 | } | ||
| 342 | |||
| 343 | @Override | ||
| 344 | public String[] | ||
| 345 | listFamilies () | ||
| 346 | { | ||
| 347 | return fontFamilyList; | ||
| 348 | } | ||
| 349 | |||
| 350 | @Override | ||
| 351 | public FontObject | ||
| 352 | openFont (FontEntity fontEntity, int pixelSize) | ||
| 353 | { | ||
| 354 | return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface, | ||
| 355 | pixelSize); | ||
| 356 | } | ||
| 357 | |||
| 358 | @Override | ||
| 359 | public int | ||
| 360 | hasChar (FontSpec font, char charCode) | ||
| 361 | { | ||
| 362 | float missingGlyphWidth, emGlyphWidth, width; | ||
| 363 | Rect rect1, rect2; | ||
| 364 | Paint paint; | ||
| 365 | Sdk7FontObject fontObject; | ||
| 366 | |||
| 367 | if (font instanceof Sdk7FontObject) | ||
| 368 | { | ||
| 369 | fontObject = (Sdk7FontObject) font; | ||
| 370 | paint = fontObject.typeface.typefacePaint; | ||
| 371 | } | ||
| 372 | else | ||
| 373 | paint = ((Sdk7FontEntity) font).typeface.typefacePaint; | ||
| 374 | |||
| 375 | paint.setTextSize (10); | ||
| 376 | |||
| 377 | if (Character.isWhitespace (charCode)) | ||
| 378 | return 1; | ||
| 379 | |||
| 380 | missingGlyphWidth = paint.measureText (TOFU_STRING); | ||
| 381 | emGlyphWidth = paint.measureText (EM_STRING); | ||
| 382 | width = paint.measureText ("" + charCode); | ||
| 383 | |||
| 384 | if (width == 0f) | ||
| 385 | return 0; | ||
| 386 | |||
| 387 | if (width != missingGlyphWidth) | ||
| 388 | return 1; | ||
| 389 | |||
| 390 | rect1 = new Rect (); | ||
| 391 | rect2 = new Rect (); | ||
| 392 | |||
| 393 | paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (), | ||
| 394 | rect1); | ||
| 395 | paint.getTextBounds ("" + charCode, 0, 1, rect2); | ||
| 396 | return rect1.equals (rect2) ? 1 : 0; | ||
| 397 | } | ||
| 398 | |||
| 399 | private void | ||
| 400 | textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics, | ||
| 401 | Paint paint, Rect bounds) | ||
| 402 | { | ||
| 403 | char[] text; | ||
| 404 | float[] width; | ||
| 405 | |||
| 406 | text = new char[1]; | ||
| 407 | text[0] = (char) code; | ||
| 408 | |||
| 409 | paint.getTextBounds (text, 0, 1, bounds); | ||
| 410 | width = new float[1]; | ||
| 411 | paint.getTextWidths (text, 0, 1, width); | ||
| 412 | |||
| 413 | /* bounds is the bounding box of the glyph corresponding to CODE. | ||
| 414 | Translate these into XCharStruct values. | ||
| 415 | |||
| 416 | The origin is at 0, 0, and lbearing is the distance counting | ||
| 417 | rightwards from the origin to the left most pixel in the glyph | ||
| 418 | raster. rbearing is the distance between the origin and the | ||
| 419 | rightmost pixel in the glyph raster. ascent is the distance | ||
| 420 | counting upwards between the the topmost pixel in the glyph | ||
| 421 | raster. descent is the distance (once again counting | ||
| 422 | downwards) between the origin and the bottommost pixel in the | ||
| 423 | glyph raster. | ||
| 424 | |||
| 425 | width is the distance between the origin and the origin of any | ||
| 426 | character to the right. */ | ||
| 427 | |||
| 428 | metrics.lbearing = (short) bounds.left; | ||
| 429 | metrics.rbearing = (short) bounds.right; | ||
| 430 | metrics.ascent = (short) -bounds.top; | ||
| 431 | metrics.descent = (short) bounds.bottom; | ||
| 432 | metrics.width = (short) Math.round (width[0]); | ||
| 433 | } | ||
| 434 | |||
| 435 | @Override | ||
| 436 | public void | ||
| 437 | textExtents (FontObject font, int code[], FontMetrics fontMetrics[]) | ||
| 438 | { | ||
| 439 | int i; | ||
| 440 | Paint paintCache; | ||
| 441 | Rect boundsCache; | ||
| 442 | Sdk7FontObject fontObject; | ||
| 443 | |||
| 444 | fontObject = (Sdk7FontObject) font; | ||
| 445 | paintCache = fontObject.typeface.typefacePaint; | ||
| 446 | paintCache.setTextSize (fontObject.pixelSize); | ||
| 447 | boundsCache = new Rect (); | ||
| 448 | |||
| 449 | for (i = 0; i < code.length; ++i) | ||
| 450 | textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i], | ||
| 451 | paintCache, boundsCache); | ||
| 452 | } | ||
| 453 | |||
| 454 | @Override | ||
| 455 | public int | ||
| 456 | encodeChar (FontObject fontObject, char charCode) | ||
| 457 | { | ||
| 458 | return charCode; | ||
| 459 | } | ||
| 460 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java new file mode 100644 index 00000000000..311226e6f7e --- /dev/null +++ b/java/org/gnu/emacs/EmacsService.java | |||
| @@ -0,0 +1,384 @@ | |||
| 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.lang.Runnable; | ||
| 23 | import java.io.IOException; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.ArrayList; | ||
| 26 | |||
| 27 | import android.graphics.Canvas; | ||
| 28 | import android.graphics.Bitmap; | ||
| 29 | import android.graphics.Point; | ||
| 30 | |||
| 31 | import android.annotation.TargetApi; | ||
| 32 | import android.app.Service; | ||
| 33 | import android.content.Context; | ||
| 34 | import android.content.Intent; | ||
| 35 | import android.content.res.AssetManager; | ||
| 36 | import android.os.Build; | ||
| 37 | import android.os.Looper; | ||
| 38 | import android.os.IBinder; | ||
| 39 | import android.os.Handler; | ||
| 40 | import android.util.Log; | ||
| 41 | |||
| 42 | class Holder<T> | ||
| 43 | { | ||
| 44 | T thing; | ||
| 45 | }; | ||
| 46 | |||
| 47 | /* EmacsService is the service that starts the thread running Emacs | ||
| 48 | and handles requests by that Emacs instance. */ | ||
| 49 | |||
| 50 | public class EmacsService extends Service | ||
| 51 | { | ||
| 52 | public static final String TAG = "EmacsService"; | ||
| 53 | public static final int MAX_PENDING_REQUESTS = 256; | ||
| 54 | public static volatile EmacsService SERVICE; | ||
| 55 | |||
| 56 | private EmacsThread thread; | ||
| 57 | private Handler handler; | ||
| 58 | private EmacsPaintQueue paintQueue; | ||
| 59 | |||
| 60 | /* List of all EmacsWindows that are available to attach to an | ||
| 61 | activity. */ | ||
| 62 | public static List<EmacsWindow> availableChildren; | ||
| 63 | |||
| 64 | static | ||
| 65 | { | ||
| 66 | availableChildren = new ArrayList<EmacsWindow> (); | ||
| 67 | }; | ||
| 68 | |||
| 69 | @Override | ||
| 70 | public int | ||
| 71 | onStartCommand (Intent intent, int flags, int startId) | ||
| 72 | { | ||
| 73 | return START_NOT_STICKY; | ||
| 74 | } | ||
| 75 | |||
| 76 | @Override | ||
| 77 | public IBinder | ||
| 78 | onBind (Intent intent) | ||
| 79 | { | ||
| 80 | return null; | ||
| 81 | } | ||
| 82 | |||
| 83 | @TargetApi (Build.VERSION_CODES.GINGERBREAD) | ||
| 84 | String | ||
| 85 | getLibraryDirectory () | ||
| 86 | { | ||
| 87 | int apiLevel; | ||
| 88 | Context context; | ||
| 89 | |||
| 90 | context = getApplicationContext (); | ||
| 91 | apiLevel = android.os.Build.VERSION.SDK_INT; | ||
| 92 | |||
| 93 | if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) | ||
| 94 | return context.getApplicationInfo().nativeLibraryDir; | ||
| 95 | else if (apiLevel >= Build.VERSION_CODES.DONUT) | ||
| 96 | return context.getApplicationInfo().dataDir + "/lib"; | ||
| 97 | |||
| 98 | return "/data/data/" + context.getPackageName() + "/lib"; | ||
| 99 | } | ||
| 100 | |||
| 101 | @Override | ||
| 102 | public void | ||
| 103 | onCreate () | ||
| 104 | { | ||
| 105 | AssetManager manager; | ||
| 106 | Context app_context; | ||
| 107 | String filesDir, libDir; | ||
| 108 | |||
| 109 | SERVICE = this; | ||
| 110 | handler = new Handler (Looper.getMainLooper ()); | ||
| 111 | manager = getAssets (); | ||
| 112 | app_context = getApplicationContext (); | ||
| 113 | |||
| 114 | try | ||
| 115 | { | ||
| 116 | /* Configure Emacs with the asset manager and other necessary | ||
| 117 | parameters. */ | ||
| 118 | filesDir = app_context.getFilesDir ().getCanonicalPath (); | ||
| 119 | libDir = getLibraryDirectory (); | ||
| 120 | |||
| 121 | Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir | ||
| 122 | + " and libDir = " + libDir); | ||
| 123 | |||
| 124 | EmacsNative.setEmacsParams (manager, filesDir, libDir, | ||
| 125 | this); | ||
| 126 | |||
| 127 | /* Start the thread that runs Emacs. */ | ||
| 128 | thread = new EmacsThread (this); | ||
| 129 | thread.start (); | ||
| 130 | } | ||
| 131 | catch (IOException exception) | ||
| 132 | { | ||
| 133 | EmacsNative.emacsAbort (); | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | /* Functions from here on must only be called from the Emacs | ||
| 141 | thread. */ | ||
| 142 | |||
| 143 | void | ||
| 144 | runOnUiThread (Runnable runnable) | ||
| 145 | { | ||
| 146 | handler.post (runnable); | ||
| 147 | } | ||
| 148 | |||
| 149 | EmacsView | ||
| 150 | getEmacsView (final EmacsWindow window) | ||
| 151 | { | ||
| 152 | Runnable runnable; | ||
| 153 | final Holder<EmacsView> view; | ||
| 154 | |||
| 155 | view = new Holder<EmacsView> (); | ||
| 156 | |||
| 157 | runnable = new Runnable () { | ||
| 158 | public void | ||
| 159 | run () | ||
| 160 | { | ||
| 161 | synchronized (this) | ||
| 162 | { | ||
| 163 | view.thing = new EmacsView (window); | ||
| 164 | notify (); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 169 | synchronized (runnable) | ||
| 170 | { | ||
| 171 | runOnUiThread (runnable); | ||
| 172 | |||
| 173 | try | ||
| 174 | { | ||
| 175 | runnable.wait (); | ||
| 176 | } | ||
| 177 | catch (InterruptedException e) | ||
| 178 | { | ||
| 179 | EmacsNative.emacsAbort (); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | return view.thing; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* Notice that a child of the root window named WINDOW is now | ||
| 187 | available for attachment to a specific activity. */ | ||
| 188 | |||
| 189 | public void | ||
| 190 | noticeAvailableChild (final EmacsWindow window) | ||
| 191 | { | ||
| 192 | Log.d (TAG, "A new child is available: " + window); | ||
| 193 | |||
| 194 | handler.post (new Runnable () { | ||
| 195 | public void | ||
| 196 | run () | ||
| 197 | { | ||
| 198 | for (EmacsActivity activity | ||
| 199 | : EmacsActivity.availableActivities) | ||
| 200 | { | ||
| 201 | /* TODO: check if the activity matches. */ | ||
| 202 | activity.attachChild (window); | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | |||
| 206 | /* Nope, wait for an activity to become available. */ | ||
| 207 | availableChildren.add (window); | ||
| 208 | } | ||
| 209 | }); | ||
| 210 | } | ||
| 211 | |||
| 212 | /* Notice that a child of the root window named WINDOW has been | ||
| 213 | destroyed. */ | ||
| 214 | |||
| 215 | public void | ||
| 216 | noticeChildDestroyed (final EmacsWindow child) | ||
| 217 | { | ||
| 218 | handler.post (new Runnable () { | ||
| 219 | @Override | ||
| 220 | public void | ||
| 221 | run () | ||
| 222 | { | ||
| 223 | availableChildren.remove (child); | ||
| 224 | } | ||
| 225 | }); | ||
| 226 | } | ||
| 227 | |||
| 228 | /* X drawing operations. These are quite primitive operations. The | ||
| 229 | drawing queue is kept on the Emacs thread, but is periodically | ||
| 230 | flushed to the application thread, upon buffers swaps and once it | ||
| 231 | gets too big. */ | ||
| 232 | |||
| 233 | |||
| 234 | |||
| 235 | private void | ||
| 236 | ensurePaintQueue () | ||
| 237 | { | ||
| 238 | if (paintQueue == null) | ||
| 239 | paintQueue = new EmacsPaintQueue (); | ||
| 240 | } | ||
| 241 | |||
| 242 | public void | ||
| 243 | flushPaintQueue () | ||
| 244 | { | ||
| 245 | final EmacsPaintQueue queue; | ||
| 246 | |||
| 247 | if (paintQueue == null) | ||
| 248 | return; | ||
| 249 | |||
| 250 | if (paintQueue.numRequests < 1) | ||
| 251 | /* No requests to flush. */ | ||
| 252 | return; | ||
| 253 | |||
| 254 | queue = paintQueue; | ||
| 255 | |||
| 256 | handler.post (new Runnable () { | ||
| 257 | @Override | ||
| 258 | public void | ||
| 259 | run () | ||
| 260 | { | ||
| 261 | queue.run (); | ||
| 262 | } | ||
| 263 | }); | ||
| 264 | |||
| 265 | /* Clear the paint queue. */ | ||
| 266 | paintQueue = null; | ||
| 267 | } | ||
| 268 | |||
| 269 | private void | ||
| 270 | checkFlush () | ||
| 271 | { | ||
| 272 | if (paintQueue != null | ||
| 273 | && paintQueue.numRequests > MAX_PENDING_REQUESTS) | ||
| 274 | flushPaintQueue (); | ||
| 275 | } | ||
| 276 | |||
| 277 | public void | ||
| 278 | fillRectangle (EmacsDrawable drawable, EmacsGC gc, | ||
| 279 | int x, int y, int width, int height) | ||
| 280 | { | ||
| 281 | EmacsPaintReq req; | ||
| 282 | |||
| 283 | ensurePaintQueue (); | ||
| 284 | |||
| 285 | req = new EmacsFillRectangle (drawable, x, y, | ||
| 286 | width, height, | ||
| 287 | gc.immutableGC ()); | ||
| 288 | paintQueue.appendPaintOperation (req); | ||
| 289 | checkFlush (); | ||
| 290 | } | ||
| 291 | |||
| 292 | public void | ||
| 293 | fillPolygon (EmacsDrawable drawable, EmacsGC gc, | ||
| 294 | Point points[]) | ||
| 295 | { | ||
| 296 | EmacsPaintReq req; | ||
| 297 | |||
| 298 | ensurePaintQueue (); | ||
| 299 | |||
| 300 | req = new EmacsFillPolygon (drawable, points, | ||
| 301 | gc.immutableGC ()); | ||
| 302 | paintQueue.appendPaintOperation (req); | ||
| 303 | checkFlush (); | ||
| 304 | } | ||
| 305 | |||
| 306 | public void | ||
| 307 | drawRectangle (EmacsDrawable drawable, EmacsGC gc, | ||
| 308 | int x, int y, int width, int height) | ||
| 309 | { | ||
| 310 | EmacsPaintReq req; | ||
| 311 | |||
| 312 | ensurePaintQueue (); | ||
| 313 | |||
| 314 | if (gc.clip_rects != null && gc.clip_rects.length >= 1) | ||
| 315 | android.util.Log.d ("drawRectangle", | ||
| 316 | gc.clip_rects[0].toString () | ||
| 317 | + " " + gc.toString ()); | ||
| 318 | |||
| 319 | req = new EmacsDrawRectangle (drawable, x, y, | ||
| 320 | width, height, | ||
| 321 | gc.immutableGC ()); | ||
| 322 | paintQueue.appendPaintOperation (req); | ||
| 323 | checkFlush (); | ||
| 324 | } | ||
| 325 | |||
| 326 | public void | ||
| 327 | drawLine (EmacsDrawable drawable, EmacsGC gc, | ||
| 328 | int x, int y, int x2, int y2) | ||
| 329 | { | ||
| 330 | EmacsPaintReq req; | ||
| 331 | |||
| 332 | ensurePaintQueue (); | ||
| 333 | |||
| 334 | req = new EmacsDrawLine (drawable, x, y, | ||
| 335 | x2, y2, | ||
| 336 | gc.immutableGC ()); | ||
| 337 | paintQueue.appendPaintOperation (req); | ||
| 338 | checkFlush (); | ||
| 339 | } | ||
| 340 | |||
| 341 | public void | ||
| 342 | drawPoint (EmacsDrawable drawable, EmacsGC gc, | ||
| 343 | int x, int y) | ||
| 344 | { | ||
| 345 | EmacsPaintReq req; | ||
| 346 | |||
| 347 | ensurePaintQueue (); | ||
| 348 | |||
| 349 | req = new EmacsDrawPoint (drawable, x, y, | ||
| 350 | gc.immutableGC ()); | ||
| 351 | paintQueue.appendPaintOperation (req); | ||
| 352 | checkFlush (); | ||
| 353 | } | ||
| 354 | |||
| 355 | public void | ||
| 356 | copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable, | ||
| 357 | EmacsGC gc, | ||
| 358 | int srcX, int srcY, int width, int height, int destX, | ||
| 359 | int destY) | ||
| 360 | { | ||
| 361 | EmacsPaintReq req; | ||
| 362 | |||
| 363 | ensurePaintQueue (); | ||
| 364 | |||
| 365 | req = new EmacsCopyArea (srcDrawable, dstDrawable, | ||
| 366 | srcX, srcY, width, height, destX, | ||
| 367 | destY, gc.immutableGC ()); | ||
| 368 | paintQueue.appendPaintOperation (req); | ||
| 369 | checkFlush (); | ||
| 370 | } | ||
| 371 | |||
| 372 | public void | ||
| 373 | clearWindow (EmacsWindow window) | ||
| 374 | { | ||
| 375 | window.clearWindow (); | ||
| 376 | } | ||
| 377 | |||
| 378 | public void | ||
| 379 | clearArea (EmacsWindow window, int x, int y, int width, | ||
| 380 | int height) | ||
| 381 | { | ||
| 382 | window.clearArea (x, y, width, height); | ||
| 383 | } | ||
| 384 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java new file mode 100644 index 00000000000..194f6ad37a3 --- /dev/null +++ b/java/org/gnu/emacs/EmacsSurfaceView.java | |||
| @@ -0,0 +1,83 @@ | |||
| 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 android.view.SurfaceView; | ||
| 23 | import android.view.SurfaceHolder; | ||
| 24 | |||
| 25 | import android.graphics.Canvas; | ||
| 26 | import android.graphics.Rect; | ||
| 27 | |||
| 28 | public class EmacsSurfaceView extends SurfaceView | ||
| 29 | { | ||
| 30 | private boolean created; | ||
| 31 | |||
| 32 | public | ||
| 33 | EmacsSurfaceView (final EmacsView view) | ||
| 34 | { | ||
| 35 | super (view.getContext ()); | ||
| 36 | |||
| 37 | getHolder ().addCallback (new SurfaceHolder.Callback () { | ||
| 38 | @Override | ||
| 39 | public void | ||
| 40 | surfaceChanged (SurfaceHolder holder, int format, | ||
| 41 | int width, int height) | ||
| 42 | { | ||
| 43 | |||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public void | ||
| 48 | surfaceCreated (SurfaceHolder holder) | ||
| 49 | { | ||
| 50 | created = true; | ||
| 51 | |||
| 52 | /* Force a buffer swap now to get the contents of the Emacs | ||
| 53 | view on screen. */ | ||
| 54 | view.swapBuffers (); | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public void | ||
| 59 | surfaceDestroyed (SurfaceHolder holder) | ||
| 60 | { | ||
| 61 | created = false; | ||
| 62 | } | ||
| 63 | }); | ||
| 64 | } | ||
| 65 | |||
| 66 | public boolean | ||
| 67 | isCreated () | ||
| 68 | { | ||
| 69 | return created; | ||
| 70 | } | ||
| 71 | |||
| 72 | public Canvas | ||
| 73 | lockCanvas (Rect damage) | ||
| 74 | { | ||
| 75 | return getHolder ().lockCanvas (damage); | ||
| 76 | } | ||
| 77 | |||
| 78 | public void | ||
| 79 | unlockCanvasAndPost (Canvas canvas) | ||
| 80 | { | ||
| 81 | getHolder ().unlockCanvasAndPost (canvas); | ||
| 82 | } | ||
| 83 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java new file mode 100644 index 00000000000..e9281a13391 --- /dev/null +++ b/java/org/gnu/emacs/EmacsThread.java | |||
| @@ -0,0 +1,44 @@ | |||
| 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.lang.Thread; | ||
| 23 | |||
| 24 | public class EmacsThread extends Thread | ||
| 25 | { | ||
| 26 | EmacsService context; | ||
| 27 | |||
| 28 | public | ||
| 29 | EmacsThread (EmacsService service) | ||
| 30 | { | ||
| 31 | context = service; | ||
| 32 | } | ||
| 33 | |||
| 34 | public void | ||
| 35 | run () | ||
| 36 | { | ||
| 37 | String args[]; | ||
| 38 | |||
| 39 | args = new String[] { "android-emacs", }; | ||
| 40 | |||
| 41 | /* Run the native code now. */ | ||
| 42 | EmacsNative.initEmacs (args); | ||
| 43 | } | ||
| 44 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java new file mode 100644 index 00000000000..237946d6366 --- /dev/null +++ b/java/org/gnu/emacs/EmacsView.java | |||
| @@ -0,0 +1,211 @@ | |||
| 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 android.view.View; | ||
| 23 | import android.view.KeyEvent; | ||
| 24 | import android.view.ViewGroup; | ||
| 25 | import android.graphics.Bitmap; | ||
| 26 | import android.graphics.Canvas; | ||
| 27 | import android.graphics.Rect; | ||
| 28 | import android.graphics.Region; | ||
| 29 | import android.graphics.Paint; | ||
| 30 | import android.util.Log; | ||
| 31 | |||
| 32 | import android.os.Build; | ||
| 33 | |||
| 34 | /* This is an Android view which has a back and front buffer. When | ||
| 35 | swapBuffers is called, the back buffer is swapped to the front | ||
| 36 | buffer, and any damage is invalidated. frontBitmap and backBitmap | ||
| 37 | are modified and used both from the UI and the Emacs thread. As a | ||
| 38 | result, there is a lock held during all drawing operations. | ||
| 39 | |||
| 40 | It is also a ViewGroup, as it also lays out children. */ | ||
| 41 | |||
| 42 | public class EmacsView extends ViewGroup | ||
| 43 | { | ||
| 44 | public static final String TAG = "EmacsView"; | ||
| 45 | |||
| 46 | /* The associated EmacsWindow. */ | ||
| 47 | public EmacsWindow window; | ||
| 48 | |||
| 49 | /* The buffer bitmap. */ | ||
| 50 | public Bitmap bitmap; | ||
| 51 | |||
| 52 | /* The associated canvases. */ | ||
| 53 | public Canvas canvas; | ||
| 54 | |||
| 55 | /* The damage region. */ | ||
| 56 | public Region damageRegion; | ||
| 57 | |||
| 58 | /* The paint. */ | ||
| 59 | public Paint paint; | ||
| 60 | |||
| 61 | /* The associated surface view. */ | ||
| 62 | private EmacsSurfaceView surfaceView; | ||
| 63 | |||
| 64 | public | ||
| 65 | EmacsView (EmacsWindow window) | ||
| 66 | { | ||
| 67 | super (EmacsService.SERVICE); | ||
| 68 | |||
| 69 | this.window = window; | ||
| 70 | this.damageRegion = new Region (); | ||
| 71 | this.paint = new Paint (); | ||
| 72 | |||
| 73 | /* Create the surface view. */ | ||
| 74 | this.surfaceView = new EmacsSurfaceView (this); | ||
| 75 | |||
| 76 | setFocusable (FOCUSABLE); | ||
| 77 | addView (this.surfaceView); | ||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | protected void | ||
| 82 | onMeasure (int widthMeasureSpec, int heightMeasureSpec) | ||
| 83 | { | ||
| 84 | Rect measurements; | ||
| 85 | int width, height; | ||
| 86 | |||
| 87 | /* Return the width and height of the window regardless of what | ||
| 88 | the parent says. */ | ||
| 89 | measurements = window.getGeometry (); | ||
| 90 | |||
| 91 | width = measurements.width (); | ||
| 92 | height = measurements.height (); | ||
| 93 | |||
| 94 | /* Now apply any extra requirements in widthMeasureSpec and | ||
| 95 | heightMeasureSpec. */ | ||
| 96 | |||
| 97 | if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY) | ||
| 98 | width = MeasureSpec.getSize (widthMeasureSpec); | ||
| 99 | else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST | ||
| 100 | && width > MeasureSpec.getSize (widthMeasureSpec)) | ||
| 101 | width = MeasureSpec.getSize (widthMeasureSpec); | ||
| 102 | |||
| 103 | if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY) | ||
| 104 | height = MeasureSpec.getSize (heightMeasureSpec); | ||
| 105 | else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST | ||
| 106 | && height > MeasureSpec.getSize (heightMeasureSpec)) | ||
| 107 | height = MeasureSpec.getSize (heightMeasureSpec); | ||
| 108 | |||
| 109 | super.setMeasuredDimension (width, height); | ||
| 110 | } | ||
| 111 | |||
| 112 | @Override | ||
| 113 | protected void | ||
| 114 | onLayout (boolean changed, int left, int top, int right, | ||
| 115 | int bottom) | ||
| 116 | { | ||
| 117 | int count, i; | ||
| 118 | View child; | ||
| 119 | Rect windowRect; | ||
| 120 | |||
| 121 | if (changed) | ||
| 122 | { | ||
| 123 | window.viewLayout (left, top, right, bottom); | ||
| 124 | |||
| 125 | /* Recreate the front and back buffer bitmaps. */ | ||
| 126 | bitmap | ||
| 127 | = Bitmap.createBitmap (right - left, bottom - top, | ||
| 128 | Bitmap.Config.ARGB_8888); | ||
| 129 | |||
| 130 | /* And canvases. */ | ||
| 131 | canvas = new Canvas (bitmap); | ||
| 132 | } | ||
| 133 | |||
| 134 | count = getChildCount (); | ||
| 135 | |||
| 136 | for (i = 0; i < count; ++i) | ||
| 137 | { | ||
| 138 | child = getChildAt (i); | ||
| 139 | |||
| 140 | if (child == surfaceView) | ||
| 141 | /* The child is the surface view, so give it the entire | ||
| 142 | view. */ | ||
| 143 | child.layout (left, top, right, bottom); | ||
| 144 | else if (child.getVisibility () != GONE) | ||
| 145 | { | ||
| 146 | if (!(child instanceof EmacsView)) | ||
| 147 | continue; | ||
| 148 | |||
| 149 | /* What to do: lay out the view precisely according to its | ||
| 150 | window rect. */ | ||
| 151 | windowRect = ((EmacsView) child).window.getGeometry (); | ||
| 152 | child.layout (windowRect.left, windowRect.top, | ||
| 153 | windowRect.right, windowRect.bottom); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | public void | ||
| 159 | damageRect (Rect damageRect) | ||
| 160 | { | ||
| 161 | damageRegion.union (damageRect); | ||
| 162 | } | ||
| 163 | |||
| 164 | public void | ||
| 165 | swapBuffers () | ||
| 166 | { | ||
| 167 | Bitmap back; | ||
| 168 | Canvas canvas; | ||
| 169 | Rect damageRect; | ||
| 170 | |||
| 171 | if (damageRegion.isEmpty ()) | ||
| 172 | return; | ||
| 173 | |||
| 174 | if (!surfaceView.isCreated ()) | ||
| 175 | return; | ||
| 176 | |||
| 177 | if (bitmap == null) | ||
| 178 | return; | ||
| 179 | |||
| 180 | /* Lock the canvas with the specified damage. */ | ||
| 181 | damageRect = damageRegion.getBounds (); | ||
| 182 | canvas = surfaceView.lockCanvas (damageRect); | ||
| 183 | |||
| 184 | /* Return if locking the canvas failed. */ | ||
| 185 | if (canvas == null) | ||
| 186 | return; | ||
| 187 | |||
| 188 | /* Copy from the back buffer to the canvas. */ | ||
| 189 | canvas.drawBitmap (bitmap, damageRect, damageRect, paint); | ||
| 190 | |||
| 191 | /* Unlock the canvas and clear the damage. */ | ||
| 192 | surfaceView.unlockCanvasAndPost (canvas); | ||
| 193 | damageRegion.setEmpty (); | ||
| 194 | } | ||
| 195 | |||
| 196 | @Override | ||
| 197 | public boolean | ||
| 198 | onKeyDown (int keyCode, KeyEvent event) | ||
| 199 | { | ||
| 200 | window.onKeyDown (keyCode, event); | ||
| 201 | return true; | ||
| 202 | } | ||
| 203 | |||
| 204 | @Override | ||
| 205 | public boolean | ||
| 206 | onKeyUp (int keyCode, KeyEvent event) | ||
| 207 | { | ||
| 208 | window.onKeyUp (keyCode, event); | ||
| 209 | return true; | ||
| 210 | } | ||
| 211 | }; | ||
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java new file mode 100644 index 00000000000..28db04a261d --- /dev/null +++ b/java/org/gnu/emacs/EmacsWindow.java | |||
| @@ -0,0 +1,336 @@ | |||
| 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.lang.IllegalStateException; | ||
| 23 | import java.util.ArrayList; | ||
| 24 | import java.util.List; | ||
| 25 | |||
| 26 | import android.graphics.Rect; | ||
| 27 | import android.graphics.Canvas; | ||
| 28 | import android.graphics.Bitmap; | ||
| 29 | import android.graphics.Point; | ||
| 30 | |||
| 31 | import android.view.View; | ||
| 32 | import android.view.ViewGroup; | ||
| 33 | import android.view.KeyEvent; | ||
| 34 | |||
| 35 | /* This defines a window, which is a handle. Windows represent a | ||
| 36 | rectangular subset of the screen with their own contents. | ||
| 37 | |||
| 38 | Windows either have a parent window, in which case their views are | ||
| 39 | attached to the parent's view, or are "floating", in which case | ||
| 40 | their views are attached to the parent activity (if any), else | ||
| 41 | nothing. | ||
| 42 | |||
| 43 | Views are also drawables, meaning they can accept drawing | ||
| 44 | requests. */ | ||
| 45 | |||
| 46 | public class EmacsWindow extends EmacsHandleObject | ||
| 47 | implements EmacsDrawable | ||
| 48 | { | ||
| 49 | /* The view associated with the window. */ | ||
| 50 | public EmacsView view; | ||
| 51 | |||
| 52 | /* The geometry of the window. */ | ||
| 53 | private Rect rect; | ||
| 54 | |||
| 55 | /* The parent window, or null if it is the root window. */ | ||
| 56 | private EmacsWindow parent; | ||
| 57 | |||
| 58 | /* List of all children in stacking order. This must be kept | ||
| 59 | consistent! */ | ||
| 60 | private ArrayList<EmacsWindow> children; | ||
| 61 | |||
| 62 | /* The EmacsActivity currently attached, if it exists. */ | ||
| 63 | private EmacsActivity attached; | ||
| 64 | |||
| 65 | /* The window background scratch GC. foreground is always the | ||
| 66 | window background. */ | ||
| 67 | private EmacsGC scratchGC; | ||
| 68 | |||
| 69 | public | ||
| 70 | EmacsWindow (short handle, final EmacsWindow parent, int x, int y, | ||
| 71 | int width, int height) | ||
| 72 | { | ||
| 73 | super (handle); | ||
| 74 | |||
| 75 | rect = new Rect (x, y, x + width, y + height); | ||
| 76 | |||
| 77 | /* Create the view from the context's UI thread. */ | ||
| 78 | view = EmacsService.SERVICE.getEmacsView (this); | ||
| 79 | this.parent = parent; | ||
| 80 | children = new ArrayList<EmacsWindow> (); | ||
| 81 | |||
| 82 | /* The window is unmapped by default. */ | ||
| 83 | view.setVisibility (View.GONE); | ||
| 84 | |||
| 85 | /* If parent is the root window, notice that there are new | ||
| 86 | children available for interested activites to pick up. */ | ||
| 87 | if (parent == null) | ||
| 88 | EmacsService.SERVICE.noticeAvailableChild (this); | ||
| 89 | else | ||
| 90 | { | ||
| 91 | /* Otherwise, directly add this window as a child of that | ||
| 92 | window's view. */ | ||
| 93 | synchronized (parent) | ||
| 94 | { | ||
| 95 | parent.children.add (this); | ||
| 96 | parent.view.post (new Runnable () { | ||
| 97 | @Override | ||
| 98 | public void | ||
| 99 | run () | ||
| 100 | { | ||
| 101 | parent.view.addView (view); | ||
| 102 | } | ||
| 103 | }); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | scratchGC = new EmacsGC ((short) 0); | ||
| 108 | } | ||
| 109 | |||
| 110 | public void | ||
| 111 | changeWindowBackground (int pixel) | ||
| 112 | { | ||
| 113 | /* scratchGC is used as the argument to a FillRectangles req. */ | ||
| 114 | scratchGC.foreground = pixel; | ||
| 115 | scratchGC.markDirty (); | ||
| 116 | } | ||
| 117 | |||
| 118 | public Rect | ||
| 119 | getGeometry () | ||
| 120 | { | ||
| 121 | synchronized (this) | ||
| 122 | { | ||
| 123 | /* Huh, this is it. */ | ||
| 124 | return rect; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | @Override | ||
| 129 | public void | ||
| 130 | destroyHandle () throws IllegalStateException | ||
| 131 | { | ||
| 132 | synchronized (this) | ||
| 133 | { | ||
| 134 | if (!children.isEmpty ()) | ||
| 135 | throw new IllegalStateException ("Trying to destroy window with " | ||
| 136 | + "children!"); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Notice that the child has been destroyed. */ | ||
| 140 | EmacsService.SERVICE.noticeChildDestroyed (this); | ||
| 141 | |||
| 142 | /* Remove the view from its parent and make it invisible. */ | ||
| 143 | view.post (new Runnable () { | ||
| 144 | public void | ||
| 145 | run () | ||
| 146 | { | ||
| 147 | view.setVisibility (View.GONE); | ||
| 148 | |||
| 149 | if (view.getParent () != null) | ||
| 150 | ((ViewGroup) view.getParent ()).removeView (view); | ||
| 151 | |||
| 152 | if (attached != null) | ||
| 153 | attached.makeAvailable (); | ||
| 154 | } | ||
| 155 | }); | ||
| 156 | |||
| 157 | super.destroyHandle (); | ||
| 158 | } | ||
| 159 | |||
| 160 | public void | ||
| 161 | setActivity (EmacsActivity activity) | ||
| 162 | { | ||
| 163 | synchronized (this) | ||
| 164 | { | ||
| 165 | activity = activity; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | public void | ||
| 170 | viewLayout (int left, int top, int right, int bottom) | ||
| 171 | { | ||
| 172 | synchronized (this) | ||
| 173 | { | ||
| 174 | rect.left = left; | ||
| 175 | rect.top = top; | ||
| 176 | rect.right = right; | ||
| 177 | rect.bottom = bottom; | ||
| 178 | |||
| 179 | EmacsNative.sendConfigureNotify (this.handle, | ||
| 180 | System.currentTimeMillis (), | ||
| 181 | left, top, rect.width (), | ||
| 182 | rect.height ()); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | public void | ||
| 187 | requestViewLayout () | ||
| 188 | { | ||
| 189 | view.post (new Runnable () { | ||
| 190 | @Override | ||
| 191 | public void | ||
| 192 | run () | ||
| 193 | { | ||
| 194 | view.requestLayout (); | ||
| 195 | } | ||
| 196 | }); | ||
| 197 | } | ||
| 198 | |||
| 199 | public void | ||
| 200 | resizeWindow (int width, int height) | ||
| 201 | { | ||
| 202 | synchronized (this) | ||
| 203 | { | ||
| 204 | rect.right = rect.left + width; | ||
| 205 | rect.bottom = rect.top + height; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | public void | ||
| 210 | moveWindow (int x, int y) | ||
| 211 | { | ||
| 212 | int width, height; | ||
| 213 | |||
| 214 | synchronized (this) | ||
| 215 | { | ||
| 216 | width = rect.width (); | ||
| 217 | height = rect.height (); | ||
| 218 | |||
| 219 | rect.left = x; | ||
| 220 | rect.top = y; | ||
| 221 | rect.right = x + width; | ||
| 222 | rect.bottom = y + height; | ||
| 223 | |||
| 224 | requestViewLayout (); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | public void | ||
| 229 | mapWindow () | ||
| 230 | { | ||
| 231 | view.post (new Runnable () { | ||
| 232 | @Override | ||
| 233 | public void | ||
| 234 | run () | ||
| 235 | { | ||
| 236 | view.setVisibility (View.VISIBLE); | ||
| 237 | } | ||
| 238 | }); | ||
| 239 | } | ||
| 240 | |||
| 241 | public void | ||
| 242 | unmapWindow () | ||
| 243 | { | ||
| 244 | view.post (new Runnable () { | ||
| 245 | @Override | ||
| 246 | public void | ||
| 247 | run () | ||
| 248 | { | ||
| 249 | view.setVisibility (View.GONE); | ||
| 250 | } | ||
| 251 | }); | ||
| 252 | } | ||
| 253 | |||
| 254 | @Override | ||
| 255 | public Canvas | ||
| 256 | lockCanvas () | ||
| 257 | { | ||
| 258 | if (view.canvas != null) | ||
| 259 | return view.canvas; | ||
| 260 | |||
| 261 | return null; | ||
| 262 | } | ||
| 263 | |||
| 264 | @Override | ||
| 265 | public void | ||
| 266 | unlockCanvas () | ||
| 267 | { | ||
| 268 | |||
| 269 | } | ||
| 270 | |||
| 271 | @Override | ||
| 272 | public void | ||
| 273 | damageRect (Rect damageRect) | ||
| 274 | { | ||
| 275 | view.damageRect (damageRect); | ||
| 276 | } | ||
| 277 | |||
| 278 | public void | ||
| 279 | swapBuffers () | ||
| 280 | { | ||
| 281 | /* Before calling swapBuffers, make sure to flush the paint | ||
| 282 | queue. */ | ||
| 283 | EmacsService.SERVICE.flushPaintQueue (); | ||
| 284 | view.post (new Runnable () { | ||
| 285 | @Override | ||
| 286 | public void | ||
| 287 | run () | ||
| 288 | { | ||
| 289 | view.swapBuffers (); | ||
| 290 | } | ||
| 291 | }); | ||
| 292 | } | ||
| 293 | |||
| 294 | public void | ||
| 295 | clearWindow () | ||
| 296 | { | ||
| 297 | synchronized (this) | ||
| 298 | { | ||
| 299 | EmacsService.SERVICE.fillRectangle (this, scratchGC, | ||
| 300 | 0, 0, rect.width (), | ||
| 301 | rect.height ()); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | public void | ||
| 306 | clearArea (int x, int y, int width, int height) | ||
| 307 | { | ||
| 308 | EmacsService.SERVICE.fillRectangle (this, scratchGC, | ||
| 309 | x, y, width, height); | ||
| 310 | } | ||
| 311 | |||
| 312 | @Override | ||
| 313 | public Bitmap | ||
| 314 | getBitmap () | ||
| 315 | { | ||
| 316 | return view.bitmap; | ||
| 317 | } | ||
| 318 | |||
| 319 | public void | ||
| 320 | onKeyDown (int keyCode, KeyEvent event) | ||
| 321 | { | ||
| 322 | EmacsNative.sendKeyPress (this.handle, | ||
| 323 | event.getEventTime (), | ||
| 324 | event.getModifiers (), | ||
| 325 | keyCode); | ||
| 326 | } | ||
| 327 | |||
| 328 | public void | ||
| 329 | onKeyUp (int keyCode, KeyEvent event) | ||
| 330 | { | ||
| 331 | EmacsNative.sendKeyRelease (this.handle, | ||
| 332 | event.getEventTime (), | ||
| 333 | event.getModifiers (), | ||
| 334 | keyCode); | ||
| 335 | } | ||
| 336 | }; | ||