forked from Github/emacs
Compare commits
471 commits
master
...
feature/an
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
889b61b999 | ||
|
|
c0a52c6cef | ||
|
|
11cb9cc598 | ||
|
|
c7ca46b0a7 | ||
|
|
3198b7dc56 | ||
|
|
96f9fe6514 | ||
|
|
0fbe79727b | ||
|
|
2ba6c5035c | ||
|
|
d5414f1797 | ||
|
|
daccdf7e6d | ||
|
|
ccef1ff072 | ||
|
|
339cdef28e | ||
|
|
19210f8b77 | ||
|
|
b0d6c67372 | ||
|
|
7b3c774bce | ||
|
|
35eae084bc | ||
|
|
d5b92bce5b | ||
|
|
258c98240d | ||
|
|
9283471cb4 | ||
|
|
f4a5e6d0e5 | ||
|
|
c47716f95b | ||
|
|
f4512cca0b | ||
|
|
5a58a6bc47 | ||
|
|
86b7ed619c | ||
|
|
f92bdbc677 | ||
|
|
8a90992799 | ||
|
|
b9de6e35b7 | ||
|
|
da6f0d9c6f | ||
|
|
843d8797db | ||
|
|
6c3369abe5 | ||
|
|
6a30a74cb2 | ||
|
|
ddc16de869 | ||
|
|
a8f9a4d2d9 | ||
|
|
5550816f59 | ||
|
|
9a7c645dd4 | ||
|
|
368f6f3942 | ||
|
|
4289ed6cff | ||
|
|
fee9efdf29 | ||
|
|
3d7c06869d | ||
|
|
a87272183b | ||
|
|
4ea40db823 | ||
|
|
ab10d1f663 | ||
|
|
7c504d91d1 | ||
|
|
83a9e4cee1 | ||
|
|
04ac6e6be8 | ||
|
|
136ae23575 | ||
|
|
a5e90e4eea | ||
|
|
5f389f4b63 | ||
|
|
9043bef65f | ||
|
|
85a9757b3c | ||
|
|
0a113a32c4 | ||
|
|
74fd40c030 | ||
|
|
ed2f8c660b | ||
|
|
a94e9f9644 | ||
|
|
5b31473189 | ||
|
|
55388c288a | ||
|
|
02c214a5e0 | ||
|
|
fe9e48a16a | ||
|
|
5919511881 | ||
|
|
618ba26ed1 | ||
|
|
e11e56a057 | ||
|
|
562b2fca7d | ||
|
|
91da696bbc | ||
|
|
933b5b51ab | ||
|
|
857e2bcb66 | ||
|
|
23e963b6f0 | ||
|
|
e1261fff85 | ||
|
|
3695ae4d63 | ||
|
|
6abed48e5c | ||
|
|
65987f8506 | ||
|
|
3b07a4b315 | ||
|
|
458c6e5c91 | ||
|
|
fb87f7a905 | ||
|
|
6bdbb4cbfc | ||
|
|
76a8347e43 | ||
|
|
4a7facac1b | ||
|
|
add40ccfb9 | ||
|
|
0e0ea976ca | ||
|
|
8bca62d552 | ||
|
|
1d84b4b286 | ||
|
|
aeac57fbbd | ||
|
|
ca53e2bfbc | ||
|
|
05f8fe3ae3 | ||
|
|
ab3ef576b4 | ||
|
|
956cb8ca65 | ||
|
|
8bf461bd36 | ||
|
|
8719ad534b | ||
|
|
89a30637b3 | ||
|
|
804b76ba8d | ||
|
|
219bfea874 | ||
|
|
2686a55aa1 | ||
|
|
fa6ac5ed1c | ||
|
|
2dc037e4a8 | ||
|
|
279efc6cc2 | ||
|
|
67da02444e | ||
|
|
13e1fb4c3f | ||
|
|
af6af86d08 | ||
|
|
f92413a796 | ||
|
|
bf28a372ab | ||
|
|
2826287b1a | ||
|
|
b3d100734b | ||
|
|
64206ee3af | ||
|
|
05f3f9c1c0 | ||
|
|
ee9c0a482c | ||
|
|
54d79f37ae | ||
|
|
dd4924ca90 | ||
|
|
18b34e9ca0 | ||
|
|
67a325243c | ||
|
|
a1c5461eda | ||
|
|
c0873f2382 | ||
|
|
d7457e1ce4 | ||
|
|
b78ef9bcd1 | ||
|
|
b26b4c537b | ||
|
|
a7902e9d80 | ||
|
|
6bd1cfa24f | ||
|
|
739558c369 | ||
|
|
f3dd887d18 | ||
|
|
84ffda4be5 | ||
|
|
90348dabbf | ||
|
|
6d28b596a9 | ||
|
|
634e3fcc20 | ||
|
|
773bdb15ab | ||
|
|
9d1285883c | ||
|
|
6e83b72706 | ||
|
|
e7025ed689 | ||
|
|
2d666daaa3 | ||
|
|
45b5c9b8b7 | ||
|
|
da660a1ffa | ||
|
|
b37bb4279c | ||
|
|
3702389a50 | ||
|
|
584eeb24eb | ||
|
|
34e9f7a0ee | ||
|
|
e38758aaf4 | ||
|
|
5bdbfba4fc | ||
|
|
ce66228ac5 | ||
|
|
d04731b588 | ||
|
|
9a4a7de914 | ||
|
|
4e9e72ea48 | ||
|
|
3504c7550d | ||
|
|
f57c64925b | ||
|
|
af7f0b6f9b | ||
|
|
4e05371fd1 | ||
|
|
8fbac937fb | ||
|
|
5b9d6738d1 | ||
|
|
c74bab6067 | ||
|
|
aec73dba8f | ||
|
|
a39ca9bf8e | ||
|
|
aa24854e7f | ||
|
|
d6bddca26c | ||
|
|
5964051fce | ||
|
|
e0417a4577 | ||
|
|
d31f557d4c | ||
|
|
e6186b6a2a | ||
|
|
d3bb4b6687 | ||
|
|
b776feb7f2 | ||
|
|
c3524b15aa | ||
|
|
a517c24697 | ||
|
|
08a3749794 | ||
|
|
90c22cb949 | ||
|
|
776f1ca3e3 | ||
|
|
82b4b9e869 | ||
|
|
3573db24ad | ||
|
|
a17380e80d | ||
|
|
b0abc50218 | ||
|
|
5c2fee0c25 | ||
|
|
7971b533a2 | ||
|
|
6c68d9bd3a | ||
|
|
b83324d1bd | ||
|
|
26640b7a4f | ||
|
|
a697ca5562 | ||
|
|
b931a92ac4 | ||
|
|
c4b77b82de | ||
|
|
78faad6330 | ||
|
|
4a328b8578 | ||
|
|
98c7825f5d | ||
|
|
f573ce3f66 | ||
|
|
248b345961 | ||
|
|
e9a879260d | ||
|
|
769a4e7ff5 | ||
|
|
1eb546309b | ||
|
|
98d43dbef5 | ||
|
|
ae5513ede5 | ||
|
|
a7c8ae7d67 | ||
|
|
417e0539cf | ||
|
|
488a75f2e2 | ||
|
|
4392423cb6 | ||
|
|
f34d9fab89 | ||
|
|
a7f0f9498f | ||
|
|
7e3c22536f | ||
|
|
e859a14bee | ||
|
|
745890de52 | ||
|
|
c99354042a | ||
|
|
682a6542cd | ||
|
|
dcc3c63c6e | ||
|
|
55634b5f79 | ||
|
|
009d4cfc88 | ||
|
|
ad678306c4 | ||
|
|
5e3bba2dbe | ||
|
|
1bf8fd61c5 | ||
|
|
c15f9aee2e | ||
|
|
f9e68ed00e | ||
|
|
bb55528c7b | ||
|
|
fdff5442a5 | ||
|
|
06cfa27e37 | ||
|
|
abe279cb17 | ||
|
|
84d27fe53b | ||
|
|
83c29bd40e | ||
|
|
327c2658ac | ||
|
|
44cf1ed7e5 | ||
|
|
a11ad7149b | ||
|
|
7c8cc9a633 | ||
|
|
c0a6f14f4a | ||
|
|
31883b8de1 | ||
|
|
97ca0a8551 | ||
|
|
0dbbdd20f4 | ||
|
|
1cae464859 | ||
|
|
26b3b8433d | ||
|
|
0760d5cc98 | ||
|
|
e0f4002161 | ||
|
|
7f288bf04a | ||
|
|
bf66725034 | ||
|
|
56d783cf9d | ||
|
|
f484f6b74f | ||
|
|
3be448f429 | ||
|
|
1f4818d3e3 | ||
|
|
b5bf4dc5b5 | ||
|
|
8f3f9c2550 | ||
|
|
df74188dba | ||
|
|
46c8e7617a | ||
|
|
2634765bc3 | ||
|
|
39a7e6b79f | ||
|
|
798003b04f | ||
|
|
138d500b60 | ||
|
|
0e995d06a8 | ||
|
|
0d363aded1 | ||
|
|
edfa0f8fa0 | ||
|
|
ece6d61301 | ||
|
|
468e8d7322 | ||
|
|
d8ea139e83 | ||
|
|
48b5a770f2 | ||
|
|
4354a3b699 | ||
|
|
bc9239eb51 | ||
|
|
bf93380c1c | ||
|
|
e18cbd0cc6 | ||
|
|
a6a586ffc1 | ||
|
|
42674a7144 | ||
|
|
960230d88d | ||
|
|
951bdd021f | ||
|
|
cb233cb88a | ||
|
|
44341959e8 | ||
|
|
09aa948ab4 | ||
|
|
424077fadf | ||
|
|
7fb3c0d039 | ||
|
|
5e416d02c5 | ||
|
|
daf9c62871 | ||
|
|
a0c3643abd | ||
|
|
920168c5d8 | ||
|
|
ad8e12b9f9 | ||
|
|
194b3f948c | ||
|
|
15bcb446be | ||
|
|
f8a2619d98 | ||
|
|
49d5aa3657 | ||
|
|
03c0cb8671 | ||
|
|
d96dac96e7 | ||
|
|
744f19c22f | ||
|
|
74a7d34361 | ||
|
|
28f6add67f | ||
|
|
39ddf1855b | ||
|
|
687f4fadde | ||
|
|
8fa86cc7cd | ||
|
|
80f26cc398 | ||
|
|
df29bb71fc | ||
|
|
8e4c5db193 | ||
|
|
d5cccfdc56 | ||
|
|
ea74f3c067 | ||
|
|
4ff3904e3c | ||
|
|
d3e71edc71 | ||
|
|
b91e8ada70 | ||
|
|
4aaa6fd24c | ||
|
|
4bcf251308 | ||
|
|
0b7d9bbc8a | ||
|
|
e3595debd0 | ||
|
|
351813556d | ||
|
|
c0a39006a2 | ||
|
|
8747568d69 | ||
|
|
1d84465236 | ||
|
|
2222c7c335 | ||
|
|
767da53fa3 | ||
|
|
f3b6cbb675 | ||
|
|
1e6f957c0d | ||
|
|
8356412d62 | ||
|
|
86f10534dc | ||
|
|
137bdaced6 | ||
|
|
a892c0487a | ||
|
|
77feba7456 | ||
|
|
8ca4162ecd | ||
|
|
7aa4ffddd8 | ||
|
|
d197d73491 | ||
|
|
fce2e2f264 | ||
|
|
57c19f477f | ||
|
|
fecd0a9fed | ||
|
|
ba87d2c28b | ||
|
|
34ba481c8e | ||
|
|
b91396bace | ||
|
|
1727777a46 | ||
|
|
e5232fc0e5 | ||
|
|
05791d09f6 | ||
|
|
585ee91b21 | ||
|
|
f319605207 | ||
|
|
18f723faa8 | ||
|
|
c09dca3fb0 | ||
|
|
47dbdb06dc | ||
|
|
0998ab3ade | ||
|
|
0aa19e993b | ||
|
|
efc46330aa | ||
|
|
c8f49c9276 | ||
|
|
c6809eb927 | ||
|
|
2bcfa0e68e | ||
|
|
4ab9fa7507 | ||
|
|
c045d5322c | ||
|
|
a61f9cb77c | ||
|
|
fa1b27930e | ||
|
|
3a0b3cd086 | ||
|
|
265435fdf8 | ||
|
|
ce440ae92c | ||
|
|
e88730a4b4 | ||
|
|
d70bb47aeb | ||
|
|
1f81186d67 | ||
|
|
2341d1d52a | ||
|
|
ecb48e8d63 | ||
|
|
60ed861b10 | ||
|
|
88afd96e36 | ||
|
|
759e6a24ab | ||
|
|
2dcce30290 | ||
|
|
cf24b61985 | ||
|
|
dd7066901f | ||
|
|
b875a2eaf3 | ||
|
|
a62fa69ec9 | ||
|
|
a158c1d5b9 | ||
|
|
5a7855e84a | ||
|
|
19eb27d477 | ||
|
|
0198b8cffd | ||
|
|
ab48881a2f | ||
|
|
c4c34f72a1 | ||
|
|
dc120c7ad6 | ||
|
|
2489126e68 | ||
|
|
9b50500b22 | ||
|
|
338f4be900 | ||
|
|
664140fc26 | ||
|
|
a1941cd7a7 | ||
|
|
60270d8ee3 | ||
|
|
283f34f09d | ||
|
|
03c929e528 | ||
|
|
209ae003b7 | ||
|
|
f0f45ab10d | ||
|
|
85dd157c38 | ||
|
|
c201043b4b | ||
|
|
6de4deb47e | ||
|
|
2c5e36e00b | ||
|
|
0bd4b7fdab | ||
|
|
7fb0df0ce2 | ||
|
|
ad3def330c | ||
|
|
1a70941c8e | ||
|
|
bd55cca4b8 | ||
|
|
45d3b0f6dc | ||
|
|
712c7450ce | ||
|
|
795f5a16b5 | ||
|
|
9b79f429ed | ||
|
|
e1c7b8ad61 | ||
|
|
85d225df87 | ||
|
|
fc82efc1fe | ||
|
|
7275e32d0b | ||
|
|
98c90135fe | ||
|
|
1b8beed960 | ||
|
|
0ee01457a8 | ||
|
|
87cdbbeb8a | ||
|
|
420533a8f9 | ||
|
|
bfce0ce57f | ||
|
|
9ffb5c0cf3 | ||
|
|
a2c3b9a787 | ||
|
|
aa32c9b78b | ||
|
|
718b1d73d7 | ||
|
|
0964328587 | ||
|
|
5a3ce490b9 | ||
|
|
7b43566a28 | ||
|
|
adb145f155 | ||
|
|
d8e42afeac | ||
|
|
eb72569dbe | ||
|
|
46e8ab23ea | ||
|
|
f69583941c | ||
|
|
d272df1817 | ||
|
|
ad90fe7db7 | ||
|
|
492aa74b13 | ||
|
|
f9e32ce157 | ||
|
|
22749d69e5 | ||
|
|
1ba1f277b6 | ||
|
|
198b8160cf | ||
|
|
5bd38905ac | ||
|
|
32910a0ef0 | ||
|
|
6f2a518f27 | ||
|
|
8732a5bd78 | ||
|
|
e3b50ec8ec | ||
|
|
65dddd7c99 | ||
|
|
f459c43e94 | ||
|
|
b0e7ae6d5b | ||
|
|
22f7ad1057 | ||
|
|
4255d7f051 | ||
|
|
8125e26a32 | ||
|
|
0b1ef9ea31 | ||
|
|
d3b29ccc89 | ||
|
|
888d351496 | ||
|
|
d63ee136d9 | ||
|
|
0900bfbcc5 | ||
|
|
6f9a2a8f29 | ||
|
|
9082b4e6ee | ||
|
|
54836c47c7 | ||
|
|
56e55a8008 | ||
|
|
3267a2d6d2 | ||
|
|
2b00f66926 | ||
|
|
8acab73908 | ||
|
|
47d731d214 | ||
|
|
4de6b18793 | ||
|
|
9d3aacedf0 | ||
|
|
de3d8ae71b | ||
|
|
62da1e5742 | ||
|
|
00fec0dd95 | ||
|
|
5f6971e5cb | ||
|
|
aaacf24ca2 | ||
|
|
a03eeb0109 | ||
|
|
761c763dd6 | ||
|
|
26f0dd5089 | ||
|
|
01ee2bd07b | ||
|
|
d44b60c2f0 | ||
|
|
e07b58dc35 | ||
|
|
f99f759931 | ||
|
|
a496509ced | ||
|
|
6253e7e742 | ||
|
|
1b8258a1f2 | ||
|
|
356249d9fa | ||
|
|
23845804a2 | ||
|
|
ad59d8986a | ||
|
|
da9ae10636 | ||
|
|
a336fd98a1 | ||
|
|
6e2bc91d92 | ||
|
|
c02a7b2ff4 | ||
|
|
422035143f | ||
|
|
498f207c86 | ||
|
|
9c6256df0d | ||
|
|
2b87ab7b27 | ||
|
|
28a9baccd4 | ||
|
|
a441ac5527 | ||
|
|
a02f52cc9e | ||
|
|
24910d3f37 | ||
|
|
7e35af1a10 | ||
|
|
0b61fd7700 | ||
|
|
bb3ceb6e04 | ||
|
|
f9732131cf | ||
|
|
2fa5583d96 | ||
|
|
494bedde32 | ||
|
|
4c09b9a5a6 | ||
|
|
8dbfb924b6 | ||
|
|
ee256d1e25 | ||
|
|
1584a58efd | ||
|
|
86fe893128 | ||
|
|
695e26079e | ||
|
|
e816e57039 | ||
|
|
dd8078ed11 | ||
|
|
6d823d1077 | ||
|
|
a32963e11f | ||
|
|
fd074f3133 | ||
|
|
cfbc8a5dbc |
315 changed files with 99628 additions and 936 deletions
|
|
@ -12,7 +12,7 @@
|
|||
(c-mode . ((c-file-style . "GNU")
|
||||
(c-noise-macro-names . ("INLINE" "NO_INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED"
|
||||
"UNINIT" "CALLBACK" "ALIGN_STACK" "ATTRIBUTE_MALLOC"
|
||||
"ATTRIBUTE_DEALLOC_FREE"))
|
||||
"ATTRIBUTE_DEALLOC_FREE" "ANDROID_EXPORT"))
|
||||
(electric-quote-comment . nil)
|
||||
(electric-quote-string . nil)
|
||||
(indent-tabs-mode . t)
|
||||
|
|
|
|||
45
.gitignore
vendored
45
.gitignore
vendored
|
|
@ -52,6 +52,23 @@ src/config.h
|
|||
src/epaths.h
|
||||
src/emacs-module.h
|
||||
|
||||
# Built by recursive call to `configure'.
|
||||
*.android
|
||||
!INSTALL.android
|
||||
!verbose.mk.android
|
||||
|
||||
# Built by `javac'.
|
||||
java/install_temp/*
|
||||
java/*.apk*
|
||||
java/*.dex
|
||||
java/org/gnu/emacs/*.class
|
||||
|
||||
# Built by `aapt'.
|
||||
java/org/gnu/emacs/R.java
|
||||
|
||||
# Built by `config.status'.
|
||||
java/AndroidManifest.xml
|
||||
|
||||
# C-level sources built by 'make'.
|
||||
lib/alloca.h
|
||||
lib/assert.h
|
||||
|
|
@ -70,8 +87,10 @@ lib/limits.h
|
|||
lib/malloc/*.gl.h
|
||||
lib/signal.h
|
||||
lib/std*.h
|
||||
lib/math.h
|
||||
!lib/std*.in.h
|
||||
!lib/stdio-impl.h
|
||||
!lib/_Noreturn.h
|
||||
lib/string.h
|
||||
lib/sys/
|
||||
lib/time.h
|
||||
|
|
@ -81,6 +100,18 @@ src/globals.h
|
|||
src/lisp.mk
|
||||
src/verbose.mk
|
||||
|
||||
# Stuff built during cross compilation
|
||||
cross/lib/*
|
||||
cross/src/*
|
||||
cross/lib-src/*
|
||||
cross/sys/*
|
||||
cross/config.status
|
||||
cross/*.bak
|
||||
|
||||
cross/ndk-build/Makefile
|
||||
cross/ndk-build/ndk-build.mk
|
||||
cross/ndk-build/*.o
|
||||
|
||||
# Lisp-level sources built by 'make'.
|
||||
*cus-load.el
|
||||
*loaddefs.el
|
||||
|
|
@ -186,6 +217,7 @@ ID
|
|||
# Executables.
|
||||
*.exe
|
||||
a.out
|
||||
lib-src/asset-directory-tool
|
||||
lib-src/be-resources
|
||||
lib-src/blessmail
|
||||
lib-src/ctags
|
||||
|
|
@ -208,6 +240,7 @@ nextstep/GNUstep/Emacs.base/Resources/Info-gnustep.plist
|
|||
src/bootstrap-emacs
|
||||
src/emacs
|
||||
src/emacs-[0-9]*
|
||||
src/sfnt
|
||||
src/Emacs
|
||||
src/temacs
|
||||
src/dmpstruct.h
|
||||
|
|
@ -338,3 +371,15 @@ lib-src/seccomp-filter-exec.pfc
|
|||
# GDB history
|
||||
.gdb_history
|
||||
_gdb_history
|
||||
|
||||
# Files ignored in exec/.
|
||||
exec/config.status
|
||||
exec/loader
|
||||
exec/test
|
||||
exec/exec1
|
||||
exec/deps/*
|
||||
exec/autom4te.cache
|
||||
exec/config.h
|
||||
exec/config-mips.m4
|
||||
exec/configure
|
||||
exec/*.s.s
|
||||
|
|
|
|||
6
INSTALL
6
INSTALL
|
|
@ -5,9 +5,9 @@ See the end of the file for license conditions.
|
|||
|
||||
|
||||
This file contains general information on building GNU Emacs. For
|
||||
more information specific to the MS-Windows, GNUstep/macOS, and MS-DOS
|
||||
ports, also read the files nt/INSTALL, nextstep/INSTALL, and
|
||||
msdos/INSTALL.
|
||||
more information specific to the MS-Windows, GNUstep/macOS, MS-DOS,
|
||||
and Android ports, also read the files nt/INSTALL, nextstep/INSTALL,
|
||||
msdos/INSTALL, and java/INSTALL.
|
||||
|
||||
For information about building from a Git checkout (rather than an
|
||||
Emacs release), read the INSTALL.REPO file first.
|
||||
|
|
|
|||
62
Makefile.in
62
Makefile.in
|
|
@ -106,15 +106,15 @@ top_builddir = @top_builddir@
|
|||
|
||||
FIND_DELETE = @FIND_DELETE@
|
||||
|
||||
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
|
||||
|
||||
USE_STARTUP_NOTIFICATION = @USE_STARTUP_NOTIFICATION@
|
||||
|
||||
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
|
||||
HAVE_BE_APP = @HAVE_BE_APP@
|
||||
|
||||
HAVE_PGTK = @HAVE_PGTK@
|
||||
HAVE_GSETTINGS = @HAVE_GSETTINGS@
|
||||
|
||||
ANDROID = @ANDROID@
|
||||
|
||||
# ==================== Where To Install Things ====================
|
||||
|
||||
# Location to install Emacs.app under GNUstep / macOS.
|
||||
|
|
@ -339,6 +339,10 @@ EMACS_PDMP = `./src/emacs${EXEEXT} --fingerprint`.pdmp
|
|||
# Subdirectories to make recursively.
|
||||
SUBDIR = $(NTDIR) lib lib-src src lisp
|
||||
|
||||
ifeq ($(ANDROID),yes)
|
||||
SUBDIR := $(SUBDIR) java
|
||||
endif
|
||||
|
||||
# The subdir makefiles created by config.status.
|
||||
SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
|
||||
SUBDIR_MAKEFILES = $(patsubst ${srcdir}/%,%,${SUBDIR_MAKEFILES_IN:.in=})
|
||||
|
|
@ -467,20 +471,20 @@ epaths-force:
|
|||
esac; \
|
||||
done
|
||||
@(gamedir='${gamedir}'; \
|
||||
sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
|
||||
-e 's;\(#.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
|
||||
-e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
|
||||
-e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
|
||||
-e 's;\(#.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
|
||||
-e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
|
||||
-e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
|
||||
-e 's;\(#.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
|
||||
-e 's;\(#.*PATH_INFO\).*$$;\1 "${infodir}";' \
|
||||
-e 's;\(#.*PATH_DATA\).*$$;\1 "${etcdir}";' \
|
||||
-e 's;\(#.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
|
||||
-e 's;\(#.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
|
||||
-e 's;\(#.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
|
||||
-e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
|
||||
sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
|
||||
-e 's;\(#define.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
|
||||
-e 's;\(#define.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
|
||||
-e 's;\(#define.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
|
||||
-e 's;\(#define.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
|
||||
-e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
|
||||
-e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
|
||||
-e 's;\(#define.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
|
||||
-e 's;\(#define.*PATH_INFO\).*$$;\1 "${infodir}";' \
|
||||
-e 's;\(#define.*PATH_DATA\).*$$;\1 "${etcdir}";' \
|
||||
-e 's;\(#define.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
|
||||
-e 's;\(#define.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
|
||||
-e 's;\(#define.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
|
||||
-e 's;\(#define.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
|
||||
${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
|
||||
|
||||
# The w32 build needs a slightly different editing, and it uses
|
||||
|
|
@ -532,6 +536,12 @@ lisp: src
|
|||
lib lib-src lisp nt: Makefile
|
||||
$(MAKE) -C $@ all
|
||||
|
||||
java: lisp info
|
||||
$(MAKE) -C $@ all
|
||||
|
||||
cross: src
|
||||
$(MAKE) -C $@ all
|
||||
|
||||
trampolines: src lisp
|
||||
ifeq ($(HAVE_NATIVE_COMP),yes)
|
||||
$(MAKE) -C lisp trampolines
|
||||
|
|
@ -569,10 +579,13 @@ $(MAKEFILE_NAME): config.status $(srcdir)/configure \
|
|||
# Don't erase these files if make is interrupted while refreshing them.
|
||||
.PRECIOUS: Makefile config.status
|
||||
|
||||
# Note that calling config.status --recheck is insufficient on Android
|
||||
# due to the recursive calls to configure.
|
||||
|
||||
config.status: ${srcdir}/configure
|
||||
if [ -x ./config.status ]; then \
|
||||
if [ -x ./config.status ]; then \
|
||||
$(CFG) ./config.status --recheck; \
|
||||
else \
|
||||
else \
|
||||
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
|
||||
fi
|
||||
|
||||
|
|
@ -987,6 +1000,12 @@ endef
|
|||
mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \
|
||||
doc/lispref doc/lispintro test
|
||||
|
||||
### Add the libexec directory to mostlyclean_dirs if its Makefile has
|
||||
### been created.
|
||||
ifneq ($(wildcard exec/Makefile),)
|
||||
mostlyclean_dirs := $(mostlyclean_dirs) exec
|
||||
endif
|
||||
|
||||
$(foreach dir,$(mostlyclean_dirs),$(eval $(call submake_template,$(dir),mostlyclean)))
|
||||
|
||||
mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
|
||||
|
|
@ -999,7 +1018,8 @@ mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
|
|||
### with them.
|
||||
###
|
||||
### Delete '.dvi' files here if they are not part of the distribution.
|
||||
clean_dirs = $(mostlyclean_dirs) nextstep admin/charsets admin/unidata
|
||||
clean_dirs = $(mostlyclean_dirs) java cross nextstep admin/charsets \
|
||||
admin/unidata
|
||||
|
||||
$(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
|
||||
|
||||
|
|
@ -1081,6 +1101,8 @@ extraclean: maintainer-clean
|
|||
-[ "${srcdir}" = "." ] || \
|
||||
find ${srcdir} '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
|
||||
-find . '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
|
||||
-rm -f ${srcdir}/exec/config-tmp-* ${srcdir}/exec/aclocal.m4 \
|
||||
${srcdir}/src/config.in ${srcdir}/exec/configure
|
||||
|
||||
# The src subdir knows how to do the right thing
|
||||
# even when the build directory and source dir are different.
|
||||
|
|
|
|||
5
README
5
README
|
|
@ -95,6 +95,11 @@ There are several subdirectories:
|
|||
'admin' holds files used by Emacs developers, and Unicode data files.
|
||||
'build-aux' holds auxiliary files used during the build.
|
||||
'm4' holds Autoconf macros used for generating the configure script.
|
||||
'java' holds the Java code for the Emacs port to Android.
|
||||
'cross' holds Makefiles and an additional copy of gnulib used to build
|
||||
Emacs for Android devices.
|
||||
'exec' holds the source code to several helper executables used to run
|
||||
user-installed programs on Android.
|
||||
|
||||
Building Emacs on non-Posix platforms requires tools that aren't part
|
||||
of the standard distribution of the OS. The platform-specific README
|
||||
|
|
|
|||
|
|
@ -37,15 +37,15 @@ GNULIB_MODULES='
|
|||
fchmodat fcntl fcntl-h fdopendir file-has-acl
|
||||
filemode filename filevercmp flexmember fpieee
|
||||
free-posix fstatat fsusage fsync futimens
|
||||
getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog
|
||||
getline getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog
|
||||
ieee754-h ignore-value intprops largefile libgmp lstat
|
||||
manywarnings memmem-simple mempcpy memrchr memset_explicit
|
||||
minmax mkostemp mktime
|
||||
nanosleep nproc nstrftime
|
||||
pathmax pipe2 pselect pthread_sigmask
|
||||
pathmax pipe2 printf-posix vasprintf-posix pselect pthread_sigmask
|
||||
qcopy-acl readlink readlinkat regex
|
||||
sig2str sigdescr_np socklen stat-time std-gnu11 stdbool stddef stdio
|
||||
stpcpy strnlen strtoimax symlink sys_stat sys_time
|
||||
stpcpy stpncpy strnlen strtoimax symlink sys_stat sys_time
|
||||
tempname time-h time_r time_rz timegm timer-time timespec-add timespec-sub
|
||||
update-copyright unlocked-io utimensat
|
||||
vla warnings
|
||||
|
|
@ -114,6 +114,11 @@ for module in $AVOIDED_MODULES; do
|
|||
avoided_flags="$avoided_flags --avoid=$module"
|
||||
done
|
||||
|
||||
# Clean the lib directory as well.
|
||||
if [ -e "$src"/lib/Makefile ]; then
|
||||
make -C "$src"/lib maintainer-clean
|
||||
fi
|
||||
|
||||
"$gnulib_srcdir"/gnulib-tool --dir="$src" $GNULIB_TOOL_FLAGS \
|
||||
$avoided_flags $GNULIB_MODULES &&
|
||||
rm -- "$src"lib/gl_openssl.h \
|
||||
|
|
|
|||
|
|
@ -256,6 +256,12 @@ Please report any problems with this script to bug-gnu-emacs@gnu.org .'
|
|||
## Let autoreconf figure out what, if anything, needs doing.
|
||||
## Use autoreconf's -f option in case autoreconf itself has changed.
|
||||
autoreconf -fi -I m4 || exit
|
||||
|
||||
echo "Running 'autoreconf -fi' in exec ..."
|
||||
|
||||
# Now, run autoreconf inside the exec directory to generate its
|
||||
# configure script.
|
||||
autoreconf -fi exec || exit
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
|||
112
build-aux/ndk-build-helper-1.mk
Normal file
112
build-aux/ndk-build-helper-1.mk
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# ndk-build-helper-1.mk -- Helper for ndk-build.m4.
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Print out information now defined. Important details include:
|
||||
# - list of source files to compile.
|
||||
# - module export include directories.
|
||||
# - module export CFLAGS.
|
||||
# - module export LDFLAGS.
|
||||
# - module name.
|
||||
|
||||
build_kind = shared
|
||||
NDK_SO_NAMES =
|
||||
NDK_A_NAMES =
|
||||
|
||||
# Record this module's dependencies. This information is used later
|
||||
# on to recurse over libraries.
|
||||
NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
|
||||
NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
|
||||
NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) :=
|
||||
|
||||
$(info Building $(build_kind))
|
||||
$(info $(LOCAL_MODULE))
|
||||
$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
|
||||
|
||||
ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
|
||||
NDK_SO_NAMES = $(LOCAL_MODULE)_emacs.so
|
||||
else
|
||||
NDK_SO_NAMES = lib$(LOCAL_MODULE)_emacs.so
|
||||
endif
|
||||
|
||||
define add-so-name-1
|
||||
# Now recurse over this module's dependencies.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
|
||||
endef
|
||||
|
||||
define add-so-name
|
||||
ifeq ($(findstring lib,$(1)),lib)
|
||||
NDK_SO_NAME = $(1)_emacs.so
|
||||
else
|
||||
NDK_SO_NAME = lib$(1)_emacs.so
|
||||
endif
|
||||
|
||||
ifeq ($$(findstring $$(NDK_SO_NAME),$$(NDK_SO_NAMES)),)
|
||||
NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
|
||||
|
||||
# Now recurse over this module's dependencies.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
|
||||
|
||||
# Recurse over static library dependencies of this shared library.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES) $$(NDK_$(1)_WHOLE_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
|
||||
endif
|
||||
|
||||
ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
|
||||
endif
|
||||
endef
|
||||
|
||||
# Figure out includes from dependencies as well.
|
||||
NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
|
||||
|
||||
define add-includes
|
||||
ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
|
||||
NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
|
||||
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call add-includes,$$(module))))
|
||||
|
||||
# Recurse over shared library dependencies of this static library.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
|
||||
|
||||
# Recurse over static or shared library dependencies of this static
|
||||
# library.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
|
||||
endif
|
||||
endef
|
||||
|
||||
# Resolve additional dependencies and their export includes based on
|
||||
# LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES. Static library
|
||||
# dependencies can be ignored while building a shared library, as they
|
||||
# will be linked in to the resulting shared object file later.
|
||||
|
||||
SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog android libandroid
|
||||
|
||||
$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
|
||||
$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call add-includes,$(module))))
|
||||
|
||||
ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
|
||||
endif
|
||||
|
||||
$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
|
||||
$(info $(LOCAL_EXPORT_CFLAGS))
|
||||
|
||||
$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix $(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) -L$(abspath $(NDK_BUILD_DIR)) $(foreach soname,$(NDK_SO_NAMES),-l:$(soname)))
|
||||
$(info $(NDK_SO_NAMES))
|
||||
$(info $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
|
||||
$(info End)
|
||||
105
build-aux/ndk-build-helper-2.mk
Normal file
105
build-aux/ndk-build-helper-2.mk
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# ndk-build-helper-2.mk -- Helper for ndk-build.m4.
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Say a static library is being built
|
||||
build_kind = static
|
||||
NDK_SO_NAMES =
|
||||
NDK_A_NAMES =
|
||||
|
||||
# Record this module's dependencies. This information is used later
|
||||
# on to recurse over libraries.
|
||||
NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
|
||||
NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
|
||||
NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) :=
|
||||
|
||||
$(info Building $(build_kind))
|
||||
$(info $(LOCAL_MODULE))
|
||||
$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
|
||||
|
||||
ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
|
||||
NDK_A_NAMES = $(LOCAL_MODULE).a
|
||||
else
|
||||
NDK_A_NAMES = lib$(LOCAL_MODULE).a
|
||||
endif
|
||||
|
||||
define add-a-name
|
||||
ifeq ($(findstring lib,$(1)),lib)
|
||||
NDK_A_NAME = $(1).a
|
||||
else
|
||||
NDK_A_NAME = lib$(1).a
|
||||
endif
|
||||
|
||||
ifeq ($$(findstring $$(NDK_A_NAME),$$(NDK_A_NAMES)),)
|
||||
NDK_A_NAMES := $$(NDK_A_NAMES) $$(NDK_A_NAME)
|
||||
|
||||
# Now recurse over this module's dependencies.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
|
||||
endif
|
||||
|
||||
ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
|
||||
endif
|
||||
endef
|
||||
|
||||
define add-so-name
|
||||
ifeq ($(findstring lib,$(1)),lib)
|
||||
NDK_SO_NAME = $(1)_emacs.so
|
||||
else
|
||||
NDK_SO_NAME = lib$(1)_emacs.so
|
||||
endif
|
||||
|
||||
ifeq ($$(NDK_SO_NAMES:$$(NDK_SO_NAME)=),$$(NDK_SO_NAMES))
|
||||
NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
|
||||
|
||||
# Now recurse over this module's dependencies.
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
|
||||
endif
|
||||
endef
|
||||
|
||||
# Figure out includes from dependencies as well.
|
||||
NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
|
||||
|
||||
define add-includes
|
||||
ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
|
||||
NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
|
||||
|
||||
$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call add-includes,$$(module))))
|
||||
endif
|
||||
endef
|
||||
|
||||
# Resolve additional dependencies based on LOCAL_STATIC_LIBRARIES and
|
||||
# LOCAL_SHARED_LIBRARIES.
|
||||
|
||||
SYSTEM_LIBRARIES = z libz libc c libdl dl libstdc++ stdc++ log liblog android libandroid
|
||||
|
||||
$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call add-a-name,$(module))))
|
||||
$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
|
||||
$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_LIBRARIES)),$(eval $(call add-includes,$(module))))
|
||||
|
||||
ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
|
||||
NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
|
||||
endif
|
||||
|
||||
$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
|
||||
$(info $(LOCAL_EXPORT_CFLAGS))
|
||||
$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix $(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) $(and $(NDK_SO_NAMES), -L$(abspath $(NDK_BUILD_DIR)) $(foreach soname,$(NDK_SO_NAMES),-l:$(soname))))
|
||||
$(info $(NDK_A_NAMES) $(NDK_SO_NAMES))
|
||||
$(info $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
|
||||
$(info End)
|
||||
28
build-aux/ndk-build-helper-3.mk
Normal file
28
build-aux/ndk-build-helper-3.mk
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# ndk-build-helper-3.mk -- Helper for ndk-build.m4.
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Say a static library is being built
|
||||
build_kind = executable
|
||||
|
||||
$(info Building $(build_kind))
|
||||
$(info $(LOCAL_MODULE))
|
||||
$(info $(addprefix $(ANDROID_MODULE_DIRECTORY),$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
|
||||
|
||||
$(info $(foreach dir,$(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES),-I$(dir)))
|
||||
$(info $(LOCAL_EXPORT_CFLAGS))
|
||||
$(info $(LOCAL_EXPORT_LDFLAGS))
|
||||
$(info End)
|
||||
39
build-aux/ndk-build-helper-4.mk
Normal file
39
build-aux/ndk-build-helper-4.mk
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
undefine LOCAL_MODULE
|
||||
undefine LOCAL_MODULE_FILENAME
|
||||
undefine LOCAL_SRC_FILES
|
||||
undefine LOCAL_CPP_EXTENSION
|
||||
undefine LOCAL_CPP_FEATURES
|
||||
undefine LOCAL_C_INCLUDES
|
||||
undefine LOCAL_CFLAGS
|
||||
undefine LOCAL_CPPFLAGS
|
||||
undefine LOCAL_STATIC_LIBRARIES
|
||||
undefine LOCAL_SHARED_LIBRARIES
|
||||
undefine LOCAL_WHOLE_STATIC_LIBRARIES
|
||||
undefine LOCAL_LDLIBS
|
||||
undefine LOCAL_LDFLAGS
|
||||
undefine LOCAL_ALLOW_UNDEFINED_SYMBOLS
|
||||
undefine LOCAL_ARM_MODE
|
||||
undefine LOCAL_ARM_NEON
|
||||
undefine LOCAL_DISABLE_FORMAT_STRING_CHECKS
|
||||
undefine LOCAL_EXPORT_CFLAGS
|
||||
undefine LOCAL_EXPORT_CPPFLAGS
|
||||
undefine LOCAL_EXPORT_C_INCLUDES
|
||||
undefine LOCAL_EXPORT_C_INCLUDE_DIRS
|
||||
undefine LOCAL_EXPORT_LDFLAGS
|
||||
undefine LOCAL_EXPORT_LDLIBS
|
||||
81
build-aux/ndk-build-helper.mk
Normal file
81
build-aux/ndk-build-helper.mk
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# ndk-build-helper.mk -- Helper for ndk-build.m4.
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# This Makefile sets up enough to parse an Android-style Android.mk
|
||||
# file and return useful information about its contents.
|
||||
|
||||
# See the text under ``NDK BUILD SYSTEM IMPLEMENTATION'' in
|
||||
# cross/ndk-build/README for more details.
|
||||
|
||||
# TARGET_ARCH_ABI is the ABI that is being built for.
|
||||
TARGET_ARCH_ABI := $(EMACS_ABI)
|
||||
|
||||
# TARGET_ARCH is the architecture that is being built for.
|
||||
TARGET_ARCH := $(NDK_BUILD_ARCH)
|
||||
|
||||
# NDK_LAST_MAKEFILE is the last Makefile that was included.
|
||||
NDK_LAST_MAKEFILE = $(lastword $(filter %Android.mk,$(MAKEFILE_LIST)))
|
||||
|
||||
# local-makefile is the current Makefile being loaded.
|
||||
local-makefile = $(NDK_LAST_MAKEFILE)
|
||||
|
||||
# Make NDK_BUILD_DIR absolute.
|
||||
NDK_BUILD_DIR := $(absname $(NDK_BUILD_DIR))
|
||||
|
||||
# Make EMACS_SRCDIR absolute. This must be absolute, or nested
|
||||
# Android.mk files will not be able to find CLEAR_VARS.
|
||||
EMACS_SRCDIR := $(absname $(EMACS_SRCDIR))
|
||||
|
||||
# my-dir is a function that returns the Android module directory. If
|
||||
# no Android.mk has been loaded, use ANDROID_MODULE_DIRECTORY.
|
||||
my-dir = $(or $(and $(local-makefile),$(dir $(local-makefile))),$(ANDROID_MODULE_DIRECTORY))
|
||||
|
||||
# Return all Android.mk files under the first arg.
|
||||
all-makefiles-under = $(wildcard $(1)/*/Android.mk)
|
||||
|
||||
# Return all Android.mk files in subdirectories of this Makefile's
|
||||
# location.
|
||||
all-subdir-makefiles = $(call all-makefiles-under,$(call my-dir))
|
||||
|
||||
# These functions are not implemented.
|
||||
parent-makefile =
|
||||
grand-parent-makefile =
|
||||
|
||||
NDK_IMPORTS :=
|
||||
|
||||
# Add the specified module (arg 1) to NDK_IMPORTS.
|
||||
import-module = $(eval NDK_IMPORTS += $(1))
|
||||
|
||||
# Print out module information every time BUILD_SHARED_LIBRARY is
|
||||
# called.
|
||||
|
||||
BUILD_SHARED_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-1.mk
|
||||
BUILD_STATIC_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-2.mk
|
||||
BUILD_EXECUTABLE=$(BUILD_AUXDIR)ndk-build-helper-3.mk
|
||||
CLEAR_VARS=$(BUILD_AUXDIR)ndk-build-helper-4.mk
|
||||
|
||||
# Now include Android.mk.
|
||||
|
||||
include $(ANDROID_MAKEFILE)
|
||||
|
||||
# Finally, print out the imports.
|
||||
$(info Start Imports)
|
||||
$(info $(NDK_IMPORTS))
|
||||
$(info End Imports)
|
||||
|
||||
# Dummy target.
|
||||
all:
|
||||
88
build-aux/ndk-module-extract.awk
Normal file
88
build-aux/ndk-module-extract.awk
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/^Building.+$/ {
|
||||
kind = $2
|
||||
}
|
||||
|
||||
/^Start Imports$/ {
|
||||
imports = 1
|
||||
}
|
||||
|
||||
// {
|
||||
if (imports && ++imports > 2)
|
||||
{
|
||||
if (!match ($0, /^End Imports$/))
|
||||
makefile_imports = makefile_imports " " $0
|
||||
}
|
||||
else if (!match ($0, /^End$/) && !match ($0, /^Building.+$/))
|
||||
{
|
||||
if (kind)
|
||||
{
|
||||
if (target_found)
|
||||
cxx_deps = $0
|
||||
else if (ldflags_found)
|
||||
{
|
||||
target = $0
|
||||
target_found = 1
|
||||
}
|
||||
else if (cflags_found)
|
||||
{
|
||||
ldflags = $0
|
||||
ldflags_found = 1
|
||||
}
|
||||
else if (includes_found)
|
||||
{
|
||||
cflags = $0
|
||||
cflags_found = 1
|
||||
}
|
||||
else if (src_found)
|
||||
{
|
||||
includes = $0
|
||||
includes_found = 1
|
||||
}
|
||||
else if (name_found)
|
||||
{
|
||||
src = $0
|
||||
src_found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = $0
|
||||
name_found = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/^End$/ {
|
||||
if (name == MODULE && (kind == "shared" || kind == "static"))
|
||||
{
|
||||
printf "module_name=%s\n", name
|
||||
printf "module_kind=%s\n", kind
|
||||
printf "module_src=\"%s\"\n", src
|
||||
printf "module_includes=\"%s\"\n", includes
|
||||
printf "module_cflags=\"%s\"\n", cflags
|
||||
printf "module_ldflags=\"%s\"\n", ldflags
|
||||
printf "module_target=\"%s\"\n", target
|
||||
printf "module_cxx_deps=\"%s\"\n", cxx_deps
|
||||
}
|
||||
|
||||
src = ""
|
||||
name = ""
|
||||
kind = ""
|
||||
includes = ""
|
||||
cflags = ""
|
||||
ldflags = ""
|
||||
name_found = ""
|
||||
src_found = ""
|
||||
includes_found = ""
|
||||
cflags_found = ""
|
||||
ldflags_found = ""
|
||||
target_found = ""
|
||||
}
|
||||
|
||||
/^End Imports$/ {
|
||||
imports = ""
|
||||
# Strip off leading whitespace.
|
||||
gsub (/^[ \t]+/, "", makefile_imports)
|
||||
printf "module_imports=\"%s\"\n", makefile_imports
|
||||
makefile_imports = ""
|
||||
}
|
||||
1486
configure.ac
1486
configure.ac
File diff suppressed because it is too large
Load diff
190
cross/Makefile.in
Normal file
190
cross/Makefile.in
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
top_srcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
builddir = @builddir@
|
||||
|
||||
-include $(top_builddir)/src/verbose.mk
|
||||
|
||||
# Cross-compiling Emacs for Android.
|
||||
|
||||
# The cross compiled binaries are built by having ``variant''
|
||||
# Makefiles generated at configure-time. First,
|
||||
# $(top_builddir)/src/Makefile.android,
|
||||
# $(top_builddir)/lib/Makefile.android,
|
||||
# $(top_builddir)/lib/gnulib.mk.android and
|
||||
# $(top_builddir)/lib-src/Makefile.android are copied to their usual
|
||||
# locations in this directory.
|
||||
|
||||
# N.B. that LIB_SRCDIR is actually relative to builddir, because that
|
||||
# is where the gnulib files get linked.
|
||||
|
||||
LIB_SRCDIR = $(realpath $(builddir)/lib)
|
||||
LIB_TOP_SRCDIR = $(realpath $(top_srcdir))
|
||||
|
||||
SRC_SRCDIR = $(realpath $(top_srcdir)/src)
|
||||
SRC_TOP_SRCDIR = $(realpath $(top_srcdir))
|
||||
|
||||
LIB_SRC_SRCDIR = $(realpath $(top_srcdir)/lib-src)
|
||||
LIB_SRC_TOP_SRCDIR = $(realpath $(top_src))
|
||||
|
||||
# This is a list of binaries to build and install in lib-src.
|
||||
|
||||
LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \
|
||||
lib-src/ebrowse lib-src/hexl lib-src/movemail
|
||||
|
||||
CLEAN_SUBDIRS=src lib-src lib
|
||||
|
||||
.PHONY: all
|
||||
all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES)
|
||||
|
||||
# This Makefile relies on builddir and top_builddir being relative
|
||||
# paths in *.android.
|
||||
|
||||
# This file is used to tell lib/gnulib.mk when
|
||||
# $(top_builddir)/config.status changes.
|
||||
config.status: $(top_builddir)/config.status
|
||||
$(AM_V_GEN) touch config.status
|
||||
|
||||
src/verbose.mk: $(srcdir)/verbose.mk.android
|
||||
$(AM_V_SILENT) cp -f $(srcdir)/verbose.mk.android \
|
||||
src/verbose.mk
|
||||
|
||||
# Gnulib, make-fingerprint and make-docfile must be built before
|
||||
# entering any of the rules below, or they will get the Android
|
||||
# versions of many headers.
|
||||
|
||||
.PHONY: $(top_builddir)/lib/libgnu.a
|
||||
$(top_builddir)/lib/libgnu.a:
|
||||
$(MAKE) -C $(top_builddir)/lib libgnu.a
|
||||
|
||||
.PHONY: $(top_builddir)/lib-src/make-fingerprint
|
||||
$(top_builddir)/lib-src/make-fingerprint: $(top_builddir)/lib/libgnu.a
|
||||
$(MAKE) -C $(top_builddir)/lib-src make-fingerprint
|
||||
|
||||
.PHONY: $(top_builddir)/lib-src/make-docfile
|
||||
$(top_builddir)/lib-src/make-docfile: $(top_builddir)/lib/libgnu.a
|
||||
$(MAKE) -C $(top_builddir)/lib-src make-docfile
|
||||
|
||||
PRE_BUILD_DEPS=$(top_builddir)/lib/libgnu.a \
|
||||
$(top_builddir)/lib-src/make-fingerprint \
|
||||
$(top_builddir)/lib-src/make-docfile
|
||||
|
||||
lib/config.h: $(top_builddir)/src/config.h.android
|
||||
$(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android \
|
||||
lib/config.h
|
||||
|
||||
lib-src/config.h: $(top_builddir)/src/config.h.android
|
||||
$(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android \
|
||||
lib-src/config.h
|
||||
|
||||
# Figure out where build-aux is.
|
||||
# Then, replace the build-aux directory with its actual location,
|
||||
# in case MKDIR_P points there.
|
||||
|
||||
relative_buildaux_dir := $(subst /,\/,$(top_srcdir)/build-aux)
|
||||
|
||||
lib/gnulib.mk: $(top_builddir)/lib/gnulib.mk.android
|
||||
$(AM_V_GEN) \
|
||||
sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
|
||||
-e 's/$(relative_buildaux_dir)/$(subst /,\/,../$(top_builddir))\/build-aux/g' \
|
||||
< $(top_builddir)/lib/gnulib.mk.android > $@
|
||||
|
||||
lib/Makefile: $(top_builddir)/lib/Makefile.android
|
||||
$(AM_V_GEN) \
|
||||
sed -e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
|
||||
-e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
|
||||
-e 's/^VPATH =.*$$/VPATH = $(subst /,\/,$(LIB_SRCDIR))/g' \
|
||||
< $(top_builddir)/lib/Makefile.android > $@
|
||||
|
||||
# What is needed to build gnulib.
|
||||
LIB_DEPS = lib/config.h lib/gnulib.mk lib/Makefile
|
||||
|
||||
.PHONY: lib/libgnu.a
|
||||
lib/libgnu.a: src/verbose.mk config.status $(LIB_DEPS) $(PRE_BUILD_DEPS)
|
||||
$(MAKE) -C lib libgnu.a
|
||||
|
||||
# Edit srcdir and top_srcdir to the right locations.
|
||||
# Edit references to ../admin/unidata to read ../../admin/unidata.
|
||||
# Next, edit libsrc to the location at top_srcdir! It is important
|
||||
# that src/Makefile uses the binaries there, instead of any
|
||||
# cross-compiled binaries at ./lib-src.
|
||||
# Edit out anything saying -I($(top_srcdir)/lib) into
|
||||
# -I$../(srcdir)/lib; that should be covered by -I$(lib)
|
||||
|
||||
src/Makefile: $(top_builddir)/src/Makefile.android
|
||||
$(AM_V_GEN) \
|
||||
sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' \
|
||||
-e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
|
||||
-e 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' \
|
||||
-e 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' \
|
||||
-e 's/^libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
|
||||
-e 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
|
||||
-e 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' \
|
||||
< $(top_builddir)/src/Makefile.android > $@
|
||||
|
||||
src/config.h: $(top_builddir)/src/config.h.android
|
||||
$(AM_V_GEN) cp -f -p $< $@
|
||||
|
||||
.PHONY: src/android-emacs src/libemacs.so
|
||||
|
||||
src/libemacs.so: src/Makefile src/config.h src/verbose.mk \
|
||||
lib/libgnu.a $(PRE_BUILD_DEPS)
|
||||
$(MAKE) -C src libemacs.so
|
||||
|
||||
src/android-emacs: src/Makefile src/config.h lib/libgnu.a \
|
||||
$(PRE_BUILD_DEPS)
|
||||
$(MAKE) -C src android-emacs
|
||||
|
||||
# Edit out SCRIPTS, it interferes with the build.
|
||||
# Make BASE_CFLAGS also include cross/lib as well as ../lib.
|
||||
|
||||
lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android
|
||||
$(AM_V_GEN) \
|
||||
sed -e 's/-I\$${srcdir}\/\.\.\/lib//g' \
|
||||
-e 's/^srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \
|
||||
-e 's/^top_srcdir=.*$$/top_srcdir = $(subst /,\/,$(LIB_SRC_TOP_SRCDIR))/g' \
|
||||
-e 's/^SCRIPTS=.*$$/SCRIPTS=/g' \
|
||||
-e 's/-I\.\.\/lib/-I..\/lib -I..\/$(subst /,\/,$(srcdir))\/lib/g' \
|
||||
< $(top_builddir)/lib-src/Makefile.android > $@
|
||||
|
||||
.PHONY: $(LIBSRC_BINARIES)
|
||||
$(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \
|
||||
lib-src/config.h lib-src/Makefile $(PRE_BUILD_DEPS)
|
||||
# Finally, go into lib-src and make everything being built
|
||||
$(MAKE) -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
|
||||
|
||||
.PHONY: clean maintainer-clean distclean
|
||||
clean:
|
||||
for dir in $(CLEAN_SUBDIRS); do \
|
||||
find $$dir -type f -delete; \
|
||||
done
|
||||
rm -rf lib/config.h lib-src/config.h
|
||||
# ndk-build won't have been generated in a non-Android build.
|
||||
-make -C ndk-build clean
|
||||
|
||||
maintainer-clean distclean bootstrap-clean: clean
|
||||
# Remove links created by configure.
|
||||
for dir in $(CLEAN_SUBDIRS); do \
|
||||
find $$dir -type l -delete; \
|
||||
done
|
||||
rm -rf lib/Makefile lib/gnulib.mk ndk-build/Makefile
|
||||
rm -rf ndk-build/ndk-build.mk Makefile
|
||||
5
cross/README
Normal file
5
cross/README
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
This directory holds Makefiles and other required assets to build an
|
||||
Emacs binary independently for another toolchain.
|
||||
|
||||
The directory ndk-build also contains an implementation of the Android
|
||||
`ndk-build' build system.
|
||||
20
cross/langinfo.h
Normal file
20
cross/langinfo.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* Replacement langinfo.h file for building GNU Emacs on Android.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#define nl_langinfo(ignore) "ASCII"
|
||||
144
cross/ndk-build/Makefile.in
Normal file
144
cross/ndk-build/Makefile.in
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
srcdir = @srcdir@
|
||||
|
||||
# This is a list of Android.mk files which provide targets.
|
||||
NDK_BUILD_ANDROID_MK = @NDK_BUILD_ANDROID_MK@
|
||||
NDK_BUILD_ARCH = @NDK_BUILD_ARCH@
|
||||
NDK_BUILD_ABI = @NDK_BUILD_ABI@
|
||||
NDK_BUILD_SDK = @NDK_BUILD_SDK@
|
||||
NDK_BUILD_CC = @NDK_BUILD_CC@
|
||||
NDK_BUILD_CXX = @NDK_BUILD_CXX@
|
||||
NDK_BUILD_AR = @NDK_BUILD_AR@
|
||||
NDK_BUILD_NASM = @NDK_BUILD_NASM@
|
||||
NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@
|
||||
|
||||
# This is a list of targets to build.
|
||||
NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
|
||||
|
||||
# This is set by the Android in tree build system and is used by some
|
||||
# libraries to look for the NDK. Its value is unimportant.
|
||||
NDK_ROOT = /tmp/
|
||||
|
||||
# Finally, here are rules common to Emacs.
|
||||
.PHONY: all
|
||||
all: $(NDK_BUILD_MODULES)
|
||||
|
||||
define uniqify
|
||||
$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
|
||||
endef
|
||||
|
||||
# Remove duplicate files.
|
||||
NDK_BUILD_ANDROID_MK := $(call uniqify,$(NDK_BUILD_ANDROID_MK))
|
||||
|
||||
# Remove duplicate modules as well. These can occur when a single
|
||||
# module imports a module and also declares it in
|
||||
# LOCAL_SHARED_LIBRARIES.
|
||||
NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
|
||||
|
||||
# Define CFLAGS for compiling C++ code; this involves removing all
|
||||
# -std=NNN options.
|
||||
NDK_BUILD_CFLAGS_CXX := $(filter-out -std=%,$(NDK_BUILD_CFLAGS))
|
||||
|
||||
define subr-1
|
||||
|
||||
# Define ndk-build functions. Many of these are identical to those in
|
||||
# build-aux/ndk-build-helper.mk.
|
||||
|
||||
# NDK_LAST_MAKEFILE is the last Makefile that was included.
|
||||
NDK_LAST_MAKEFILE = $$(lastword $$(filter %Android.mk,$$(MAKEFILE_LIST)))
|
||||
|
||||
# local-makefile is the current Makefile being loaded.
|
||||
local-makefile = $$(NDK_LAST_MAKEFILE)
|
||||
|
||||
# my-dir is a function that returns the Android module directory. If
|
||||
# no Android.mk has been loaded, use the directory of the Makefile
|
||||
# being included.
|
||||
my-dir = $$(or $$(and $$(local-makefile),$$(dir $$(local-makefile))),$(dir $(1)))
|
||||
|
||||
# Return all Android.mk files under the first arg.
|
||||
all-makefiles-under = $$(wildcard $$(1)/*/Android.mk)
|
||||
|
||||
# Return all Android.mk files in subdirectories of this Makefile's
|
||||
# location.
|
||||
all-subdir-makefiles = $$(call all-makefiles-under,$$(call my-dir))
|
||||
|
||||
# NDK-defined include variables.
|
||||
|
||||
CLEAR_VARS = $(srcdir)/ndk-clear-vars.mk
|
||||
BUILD_EXECUTABLE = $(srcdir)/ndk-build-executable.mk
|
||||
BUILD_SHARED_LIBRARY = $(srcdir)/ndk-build-shared-library.mk
|
||||
BUILD_STATIC_LIBRARY = $(srcdir)/ndk-build-static-library.mk
|
||||
PREBUILT_SHARED_LIBRARY = $(srcdir)/ndk-prebuilt-shared-library.mk
|
||||
PREBUILT_STATIC_LIBRARY = $(srcdir)/ndk-prebuilt-static-library.mk
|
||||
|
||||
# Target information variables.
|
||||
|
||||
TARGET_ARCH = $(NDK_BUILD_ARCH)
|
||||
TARGET_PLATFORM = android-$(NDK_BUILD_SDK)
|
||||
TARGET_ARCH_ABI = $(NDK_BUILD_ABI)
|
||||
TARGET_ABI = $(TARGET_PLATFORM)-$(TARGET_ABI)
|
||||
|
||||
# Module description variables. These are defined by Android.mk.
|
||||
LOCAL_PATH :=
|
||||
LOCAL_MODULE :=
|
||||
LOCAL_MODULE_FILENAME :=
|
||||
LOCAL_SRC_FILES :=
|
||||
LOCAL_CPP_EXTENSION :=
|
||||
LOCAL_CPP_FEATURES :=
|
||||
LOCAL_C_INCLUDES :=
|
||||
LOCAL_CFLAGS :=
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
LOCAL_SHARED_LIBRARIES :=
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES :=
|
||||
LOCAL_LDLIBS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
LOCAL_ALLOW_UNDEFINED_SYMBOLS :=
|
||||
LOCAL_ARM_MODE :=
|
||||
LOCAL_ARM_NEON :=
|
||||
LOCAL_DISABLE_FORMAT_STRING_CHECKS :=
|
||||
LOCAL_EXPORT_CFLAGS :=
|
||||
LOCAL_EXPORT_CPPFLAGS :=
|
||||
LOCAL_EXPORT_C_INCLUDES :=
|
||||
LOCAL_EXPORT_LDFLAGS :=
|
||||
LOCAL_EXPORT_LDLIBS :=
|
||||
LOCAL_ASM_RULE_DEFINED :=
|
||||
LOCAL_ASM_RULE :=
|
||||
|
||||
# Now load Android.mk.
|
||||
include $(1)
|
||||
|
||||
endef
|
||||
|
||||
# Now define rules for each Android.mk file.
|
||||
$(foreach android_mk,$(NDK_BUILD_ANDROID_MK),$(eval $(call subr-1,$(android_mk))))
|
||||
|
||||
.PHONY: clean mostlyclean
|
||||
clean mostlyclean:
|
||||
rm -rf *.o *.so *.a
|
||||
|
||||
.PHONY: extraclean dist-clean maintainer-clean
|
||||
extraclean dist-clean maintainer-clean:
|
||||
rm -rf Makefile
|
||||
353
cross/ndk-build/README
Normal file
353
cross/ndk-build/README
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
NDK BUILD SYSTEM IMPLEMENTATION
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
See the end of the file for license conditions.
|
||||
|
||||
Emacs implements ndk-build itself, because the version that comes with
|
||||
the Android NDK is not easy to use from another Makefile, and keeps
|
||||
accumulating incompatible changes.
|
||||
|
||||
The Emacs implementation of ndk-build consists of one m4 file:
|
||||
|
||||
m4/ndk-build.m4
|
||||
|
||||
four Makefiles in build-aux, run during configure:
|
||||
|
||||
build-aux/ndk-build-helper-1.mk
|
||||
build-aux/ndk-build-helper-2.mk
|
||||
build-aux/ndk-build-helper-3.mk
|
||||
build-aux/ndk-build-helper.mk
|
||||
|
||||
one awk script in build-awx, run during configure:
|
||||
|
||||
build-aux/ndk-module-extract.awk
|
||||
|
||||
seven Makefiles in cross/ndk-build,
|
||||
|
||||
cross/ndk-build/ndk-build-shared-library.mk
|
||||
cross/ndk-build/ndk-build-static-library.mk
|
||||
cross/ndk-build/ndk-build-executable.mk
|
||||
cross/ndk-build/ndk-clear-vars.mk
|
||||
cross/ndk-build/ndk-prebuilt-shared-library.mk
|
||||
cross/ndk-build/ndk-prebuilt-static-library.mk
|
||||
cross/ndk-build/ndk-resolve.mk
|
||||
|
||||
and finally, two more Makefiles in cross/ndk-build, generated by
|
||||
configure:
|
||||
|
||||
cross/ndk-build/Makefile (generated from cross/ndk-build/Makefile.in)
|
||||
cross/ndk-build/ndk-build.mk (generated from cross/ndk-build/ndk-build.mk.in)
|
||||
|
||||
m4/ndk-build.m4 is a collection of macros which are used by the
|
||||
configure script to set up the ndk-build system, look for modules, add
|
||||
the appropriate options to LIBS and CFLAGS, and generate the Makefiles
|
||||
necessary to build the rest of Emacs.
|
||||
|
||||
Immediately after determining the list of directories in which to look
|
||||
for ``Android.mk'' files, the version and type of Android system being
|
||||
built for, configure calls:
|
||||
|
||||
ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build])
|
||||
|
||||
This expands to a sequence of shell script that enumerates all of the
|
||||
Android.mk files specified in "$with_ndk_path", sets up some shell
|
||||
functions used by the rest of the ndk-build code run by the configure
|
||||
script, and teaches the ndk-build system that the Makefiles to be
|
||||
generated are found in the directory "cross/ndk-build/Makefile".
|
||||
|
||||
When configure is cross-compiling for Android, the macro
|
||||
EMACS_CHECK_MODULES will expand to the macro ndk_CHECK_MODULES,
|
||||
instead of pkg-config.m4's PKG_CHECK_MODULES. Thus, the following
|
||||
code:
|
||||
|
||||
EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
|
||||
|
||||
will actually expand to:
|
||||
|
||||
ndk_CHECK_MODULES([PNG], [libpng >= 1.0.0], [HAVE_PNG=yes],
|
||||
[HAVE_PNG=no])
|
||||
|
||||
which in turn expands to a sequence shell script that first invokes:
|
||||
|
||||
make -f build-aux/ndk-build-helper.mk
|
||||
|
||||
for each ``Android.mk'' file found by ndk_INIT, with the following
|
||||
variables given to Make:
|
||||
|
||||
EMACS_SRCDIR=. # the source directory (in which configure is running)
|
||||
BUILD_AUXDIR=$ndk_AUX_DIR # the build-aux directory
|
||||
EMACS_ABI=$ndk_ABI # this is the $android_abi given to ndk_INIT
|
||||
ANDROID_MAKEFILE="/opt/android/libpng/Android.mk"
|
||||
ANDROID_MODULE_DIRECTORY="/opt/android/libpng"
|
||||
NDK_BUILD_DIR="$ndk_DIR" # this is the directory given as to ndk_INIT
|
||||
|
||||
build-aux/ndk-build-helper.mk will then evaluate the contents
|
||||
$(ANDROID_MAKEFILE), the ``Android.mk'' file, for the first time. The
|
||||
purpose of this evaluation is to establish a list of packages (or
|
||||
modules) provided by the ``Android.mk'' file, and the corresponding
|
||||
Makefile targets and compiler and linker flags required to build and
|
||||
link to those tagets.
|
||||
|
||||
Before doing so, build-aux/ndk-build-helper.mk will define several
|
||||
variables and functions required by all ``Android.mk'' files. The
|
||||
most important of these are:
|
||||
|
||||
my-dir # the directory containing the Android.mk file.
|
||||
BUILD_SHARED_LIBRARY # build-aux/ndk-build-helper-1.mk
|
||||
BUILD_STATIC_LIBRARY # build-aux/ndk-build-helper-2.mk
|
||||
BUILD_EXECUTABLE # build-aux/ndk-build-helper-3.mk
|
||||
CLEAR_VARS # build-aux/ndk-build-helper-4.mk
|
||||
|
||||
Then, ``Android.mk'' will include $(CLEAN_VARS), possibly other
|
||||
``Android.mk'' files, (to clear variables previously set), set several
|
||||
variables describing each module to the ndk-build system, and include
|
||||
one of $(BUILD_SHARED_LIBRARY), $(BUILD_STATIC_LIBRARY) and
|
||||
$(BUILD_EXECUTABLE).
|
||||
|
||||
Each one of those three scripts will then read from the variables set
|
||||
by ``Android.mk'', resolve dependencies, and print out some text
|
||||
describing the module to Emacs. For example, the shared library
|
||||
module "libpng" results in the following text being printed:
|
||||
|
||||
Building shared
|
||||
libpng
|
||||
/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c
|
||||
-I/opt/android/libpng
|
||||
|
||||
-L/opt/emacs/cross/ndk-build -l:libpng_emacs.so
|
||||
libpng_emacs.so
|
||||
End
|
||||
|
||||
The output is arranged as follows:
|
||||
|
||||
- The first line consists of the word ``Building'', followed by
|
||||
either ``shared'', ``static'', or ``executable'', depending on
|
||||
what type of module being built.
|
||||
|
||||
- The second line consists of the name of the module currently being
|
||||
built.
|
||||
|
||||
- The third line consists of all of the source code files comprising
|
||||
the module.
|
||||
|
||||
- The fourth line consists of the text that has to be added to
|
||||
CFLAGS in order to find the includes associated with the module.
|
||||
|
||||
- The fifth line consists of the text that has to be added to LIBS
|
||||
in order to link with this module and all of its dependencies.
|
||||
|
||||
- The sixth line consists of the Make targets (more on this later)
|
||||
that will build the final shared object or library archive of this
|
||||
module, along with all of its dependencies.
|
||||
|
||||
- The seventh line is either empty, or the name of a dependency on
|
||||
the C++ standard library. This is used to determine whether or
|
||||
not Emacs will include the C++ standard library in the application
|
||||
package.
|
||||
|
||||
The output from Make is given to an awk script,
|
||||
build-aux/ndk-module-extract.awk. This is responsible for parsing the
|
||||
that output and filtering out modules other than what is being built:
|
||||
|
||||
awk -f build-aux/ndk-module-extract.awk MODULE=libpng
|
||||
|
||||
eventually generating this section of shell script:
|
||||
|
||||
module_name=libpng
|
||||
module_kind=shared
|
||||
module_src="/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c"
|
||||
module_includes="-I/opt/android/libpng"
|
||||
module_cflags=""
|
||||
module_ldflags=" -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so"
|
||||
module_target="libpng_emacs.so"
|
||||
module_cxx_deps=""
|
||||
module_imports=""
|
||||
|
||||
which is then evaluated by `configure'. Once the variable
|
||||
`module_name' is set, configure apends the remaining
|
||||
$(module_includes), $(module_cflags) and $(module_ldflags) to the
|
||||
module's CFLAGS and LIBS variables, and appends the list of Makefile
|
||||
targets specified to the variable NDK_BUILD_MODULES.
|
||||
|
||||
In some cases, an ``Android.mk'' file may chose to import a module
|
||||
defined in ``--with-ndk-path'', but not defined inside its own
|
||||
``Android.mk'' file. build-aux/ndk-build-helper.mk defines the
|
||||
`import-module' function to add the modules being imported to a
|
||||
variable, which is then printed out after ``ndk-build-helper.mk''
|
||||
completes. For example, libxml2 imports the ``libicucc'' module,
|
||||
which results in the following text being printed:
|
||||
|
||||
Building shared
|
||||
libxml2
|
||||
/home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c /home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c
|
||||
|
||||
|
||||
-L/home/oldosfan/emacs-dev/emacs-android/cross/ndk-build -l:libxml2_emacs.so -l:libicuuc_emacs.so
|
||||
libxml2_emacs.so libicuuc_emacs.so
|
||||
End
|
||||
Start Imports
|
||||
libicuuc
|
||||
End Imports
|
||||
|
||||
Upon encountering the ``Start Imports'' section,
|
||||
build-aux/ndk-module-extract.awk collects all imports until it
|
||||
encounters the line ``End Imports'', at which point it prints:
|
||||
|
||||
module_imports="libicuuc"
|
||||
|
||||
Then, if the list of imports is not empty, ndk_CHECK_MODULES
|
||||
additionally calls itself for each import before appending the
|
||||
module's own ``Android.mk'', ensuring that the module's imported
|
||||
dependencies are included by $ndk_DIR/Makefile before itself.
|
||||
|
||||
Finally, immediately before generating src/Makefile.android, configure
|
||||
expands:
|
||||
|
||||
ndk_CONFIG_FILES
|
||||
|
||||
to generate $ndk_DIR/Makefile and $ndk_DIR/ndk-build.mk.
|
||||
|
||||
Now, the $ndk_DIR directory is set up to build all modules upon which
|
||||
depends, and $ndk_DIR/ndk-build.mk includes a list of files required
|
||||
to link Emacs, along with the rules to chdir into $ndk_DIR in order to
|
||||
build them.
|
||||
|
||||
$ndk_DIR/ndk-build.mk is included by cross/src/Makefile
|
||||
(Makefile.android) and java/Makefile. It defines three different
|
||||
variables:
|
||||
|
||||
NDK_BUILD_MODULES the file names of all modules to be built.
|
||||
NDK_BUILD_STATIC absolute names of all library archives
|
||||
to be built.
|
||||
NDK_BUILD_SHARED absolute names of all shared libraries to
|
||||
be built.
|
||||
|
||||
and then proceeds to define rules to build each of the modules in
|
||||
$(NDK_BUILD_MODULES).
|
||||
|
||||
cross/src/Makefile arranges to have all dependencies of Emacs not
|
||||
already built built before linking ``libemacs.so'' with them.
|
||||
|
||||
java/Makefile additionally arranges to have all shared object
|
||||
dependencies built before the application package is built, which is
|
||||
normally redundant because they should have already been built before
|
||||
linking ``libemacs.so''.
|
||||
|
||||
Building the modules is performed through $ndk_DIR/Makefile, which
|
||||
contains the actual implementation of the ``ndk-build'' build system.
|
||||
First, it defines certain variables constant within the ``ndk-build''
|
||||
build system, such as the files included by ``Android.mk'' to build
|
||||
shared or static libraries, and CLEAR_VARS. The most important of
|
||||
these are:
|
||||
|
||||
CLEAR_VARS cross/ndk-build/ndk-clear-vars.mk
|
||||
BUILD_EXECUTABLE cross/ndk-build/ndk-build-executable.mk
|
||||
BUILD_SHARED_LIBRARY cross/ndk-build/ndk-build-shared-library.mk
|
||||
BUILD_STATIC_LIBRARY cross/ndk-build/ndk-build-static-library.mk
|
||||
PREBUILT_SHARED_LIBRARY cross/ndk-build/ndk-prebuilt-shared-library.mk
|
||||
PREBUILT_STATIC_LIBRARY cross/ndk-build/ndk-prebuilt-static-library.mk
|
||||
|
||||
Then, it loads each Emacs dependency's ``Android.mk'' file. For each
|
||||
module defined there, ``Android.mk'' includes $(CLEAR_VARS) to unset
|
||||
all variables specific to each module, and then includes
|
||||
$(BUILD_SHARED_LIBRARY) or $(BUILD_STATIC_LIBRARY) for each shared or
|
||||
static library module.
|
||||
|
||||
This results in cross/ndk-build/ndk-build-shared-library.mk or
|
||||
cross/ndk-build/ndk-build-static-library being included, just like the
|
||||
Makefiles in build-aux were inside the configure script.
|
||||
|
||||
Each one of those two scripts then defines rules to build all of the
|
||||
object files associated with the module, and then link or archive
|
||||
them. The name under which the module is linked is the same as the
|
||||
Make target found on the sixth line of output from
|
||||
build-aux/ndk-build-helper.mk.
|
||||
|
||||
In doing so, they both include the file ndk-resolve.mk.
|
||||
ndk-resolve.mk is expected to recursively add all of the exported
|
||||
CFLAGS and includes of any dependencies to the compiler and linker
|
||||
command lines for the module being built.
|
||||
|
||||
When building a shared library module, ndk-resolve.mk is also expected
|
||||
to define the variables NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) and
|
||||
NDK_WHOLE_A_NAMES_$(LOCAL_MODULE), containing all static library
|
||||
dependencies' archive files. They are to be linked in to the
|
||||
resulting shared object file.
|
||||
|
||||
This is done by including cross/ndk-build/ndk-resolve.mk each time a
|
||||
shared or static library module is going to be built. How is this
|
||||
done?
|
||||
|
||||
First, ndk-resolve.mk saves the LOCAL_PATH, LOCAL_STATIC_LIBRARIES,
|
||||
LOCAL_SHARED_LIBRARIES, LOCAL_EXPORT_CFLAGS and
|
||||
LOCAL_EXPORT_C_INCLUDES from the module.
|
||||
|
||||
Next, ndk-resolve loops through the dependencies the module has
|
||||
specified, appending its CFLAGS and includes to the command line for
|
||||
the current module.
|
||||
|
||||
Then, that process is repeated for each such dependency which has not
|
||||
already been resolved, until all dependencies have been resolved.
|
||||
|
||||
libpng is a very simple module, providing only a single shared object
|
||||
module. This module is named libpng_emacs.so and is eventually built
|
||||
and packaged into the library directory of the Emacs application
|
||||
package. Now, let us look at a more complex module, libwebp:
|
||||
|
||||
|
||||
|
||||
When built with libwebp, Emacs depends on a single library,
|
||||
libwebpdemux. This library is named ``libwebpdemux'' on Unix systems,
|
||||
and that is the name by which it is found with pkg-config.
|
||||
|
||||
However, the library's module is only named ``webpdemux'' on Android.
|
||||
When ndk_CHECK_MODULES begins to look for a module, it first tries to
|
||||
see if its name is found in the variable `ndk_package_map', which was
|
||||
set inside ndk_INIT. In this case, it finds the following word:
|
||||
|
||||
libwebpdemux:webpdemux
|
||||
|
||||
and immediately replaces ``libwebpdemux'' with ``webpdemux''.
|
||||
|
||||
Then, it locates the ``Android.mk'' file containing a static library
|
||||
module named webpdemux and gives the output from
|
||||
build-aux/ndk-build-helper.mk to the awk script, resulting in:
|
||||
|
||||
module_name=webpdemux
|
||||
module_kind=static
|
||||
module_src="/opt/android/webp/src/demux/anim_decode.c /opt/android/webp/src/demux/demux.c"
|
||||
module_includes="-I/opt/android/webp/src"
|
||||
module_cflags=""
|
||||
module_ldflags=" cross/ndk-build/libwebpdemux.a cross/ndk-build/libwebp.a cross/ndk-build/libwebpdecoder_static.a "
|
||||
module_target="libwebpdemux.a libwebp.a libwebpdecoder_static.a"
|
||||
|
||||
The attentive reader will notice that in addition to the
|
||||
``libwebpdemux.a'' archive associated with the ``webpdemux'' library,
|
||||
Emacs has been made to link with two additional libraries. This is
|
||||
because the ``webpdemux'' module specifies a dependency on the
|
||||
``webp'' module (defined in the same Android.mk).
|
||||
build-aux/ndk-build-helper.mk resolved that dependency, noticing that
|
||||
it in turn specified another dependency on ``webpdecoder_static'',
|
||||
which in turn was added to the linker command line and list of targets
|
||||
to build.
|
||||
|
||||
As a result, all three dependencies will be built and linked to Emacs,
|
||||
instead of just the single ``webpdemux'' dependency that was
|
||||
specified.
|
||||
|
||||
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
22
cross/ndk-build/ndk-build-executable.mk
Normal file
22
cross/ndk-build/ndk-build-executable.mk
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
# Building executables is not supported
|
||||
171
cross/ndk-build/ndk-build-shared-library.mk
Normal file
171
cross/ndk-build/ndk-build-shared-library.mk
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
|
||||
|
||||
# Objects for shared libraries are prefixed with `-shared-' in
|
||||
# addition to the name of the module, because a common practice in
|
||||
# Android.mk files written by Google is to define two modules with the
|
||||
# same name but of different types.
|
||||
objname = $(1)-shared-$(subst /,_,$(2).o)
|
||||
|
||||
# LOCAL_SRC_FILES sometimes contains absolute file names. Filter them
|
||||
# out with this function. If $(2), this is a file relative to the
|
||||
# build directory.
|
||||
maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard $(1)),$(1)),$(LOCAL_PATH)/$(1))
|
||||
|
||||
# Here are the default flags to link shared libraries with.
|
||||
NDK_SO_DEFAULT_LDFLAGS := -lc -lm
|
||||
|
||||
define single-object-target
|
||||
|
||||
ifeq (x$(suffix $(1)),x.c)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
|
||||
|
||||
else
|
||||
ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1))
|
||||
$(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
ifneq (x$(suffix $(1)),x.asm)
|
||||
ifeq (x$(suffix $(1)),x.cc)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
$$(error Unsupported suffix: $(suffix $(1)))
|
||||
endif
|
||||
else
|
||||
ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
|
||||
# Call this function to define a rule that will generate $(1) from
|
||||
# $(2), a ``.asm'' file. This is an Emacs extension.
|
||||
|
||||
$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename $(1))),$(LOCAL_PATH)/$(strip $(1)))
|
||||
|
||||
else
|
||||
ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
|
||||
$$(error Trying to build nasm file on non-Intel platform!)
|
||||
else
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
|
||||
$(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i $(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
|
||||
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename $(1)))
|
||||
|
||||
endef
|
||||
|
||||
define single-neon-target
|
||||
|
||||
# Define rules for the target.
|
||||
$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
|
||||
|
||||
endef
|
||||
|
||||
# Make sure to not add a prefix to local includes that already specify
|
||||
# $(LOCAL_PATH).
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) := $(addprefix -I,$(LOCAL_C_INCLUDES))
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) += -fPIC -iquote $(LOCAL_PATH) $(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
|
||||
NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) $(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
|
||||
NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
|
||||
|
||||
# Now look for features in LOCAL_CPP_FEATURES and enable them.
|
||||
|
||||
ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
|
||||
endif
|
||||
|
||||
ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
|
||||
endif
|
||||
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) :=
|
||||
|
||||
ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
|
||||
NDK_CFLAGS ::= -marm
|
||||
else
|
||||
ifeq ($(NDK_BUILD_ARCH),arm)
|
||||
NDK_CFLAGS ::= -mthumb
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
|
||||
LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)_emacs
|
||||
else
|
||||
LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)_emacs
|
||||
endif
|
||||
|
||||
# Since a shared library is being built, suffix the library with
|
||||
# _emacs. Otherwise, libraries already on the system will be found
|
||||
# first, with potentially nasty consequences.
|
||||
|
||||
LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).so
|
||||
|
||||
# Record this module's dependencies and exported includes and CFLAGS,
|
||||
# and then add that of its dependencies.
|
||||
|
||||
include $(srcdir)/ndk-resolve.mk
|
||||
|
||||
# Then define rules to build all objects.
|
||||
ALL_SOURCE_FILES := $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
|
||||
|
||||
# This defines all dependencies.
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) :=
|
||||
|
||||
# Now filter out code that is built with neon. Define rules to build
|
||||
# those separately.
|
||||
NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
|
||||
ALL_SOURCE_FILES := $(filter-out %.neon,$(ALL_SOURCE_FILES))
|
||||
|
||||
$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call single-object-target,$(source),)))
|
||||
$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call single-neon-target,$(source))))
|
||||
|
||||
# Now define the rule to build the shared library. Shared libraries
|
||||
# link with all of the archive files from the static libraries on
|
||||
# which they depend, and also any shared libraries they depend on.
|
||||
|
||||
define define-module-rule
|
||||
$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE)) $(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) $(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE))
|
||||
$(NDK_BUILD_CC) $(1) $(2) -o $$@ -shared $(NDK_LDFLAGS_$(LOCAL_MODULE)) $(NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)) $(NDK_SO_DEFAULT_LDFLAGS) $(foreach so,$(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE)),-L $(abspath $(CURDIR)) -l:$(so))
|
||||
endef
|
||||
|
||||
NDK_WHOLE_ARCHIVE_PREFIX = -Wl,--whole-archive
|
||||
NDK_WHOLE_ARCHIVE_SUFFIX = -Wl,--no-whole-archive
|
||||
|
||||
$(eval $(call define-module-rule,$(ALL_OBJECT_FILES$(LOCAL_MODULE)) $(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)),$(and $(strip $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE))),$(NDK_WHOLE_ARCHIVE_PREFIX) $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_ARCHIVE_SUFFIX))))
|
||||
142
cross/ndk-build/ndk-build-static-library.mk
Normal file
142
cross/ndk-build/ndk-build-static-library.mk
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
|
||||
objname = $(1)-static-$(subst /,_,$(2).o)
|
||||
maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard $(1)),$(1)),$(LOCAL_PATH)/$(1))
|
||||
|
||||
define single-object-target
|
||||
|
||||
ifeq (x$(suffix $(1)),x.c)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_BUILD_CFLAGS) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
|
||||
|
||||
else
|
||||
ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
ifneq (x$(suffix $(1)),x.asm)
|
||||
ifeq (x$(suffix $(1)),x.cc)
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
|
||||
|
||||
else
|
||||
$$(error Unsupported suffix: $(suffix $(1)))
|
||||
endif
|
||||
else
|
||||
ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
|
||||
# Call this function to define a rule that will generate $(1) from
|
||||
# $(2), a ``.asm'' file. This is an Emacs extension.
|
||||
|
||||
$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename $(1))),$(LOCAL_PATH)/$(strip $(1)))
|
||||
|
||||
else
|
||||
ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
|
||||
$$(error Trying to build nasm file on non-Intel platform!)
|
||||
else
|
||||
|
||||
$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
|
||||
$(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i $(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
|
||||
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename $(1)))
|
||||
endef
|
||||
|
||||
define single-neon-target
|
||||
|
||||
# Define rules for the target.
|
||||
$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
|
||||
|
||||
endef
|
||||
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) := $(addprefix -I,$(LOCAL_C_INCLUDES))
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) += -fPIC -iquote $(LOCAL_PATH) $(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
|
||||
NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) $(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
|
||||
NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) :=
|
||||
|
||||
# Now look for features in LOCAL_CPP_FEATURES and enable them.
|
||||
|
||||
ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
|
||||
endif
|
||||
|
||||
ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
|
||||
NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
|
||||
NDK_CFLAGS ::= -marm
|
||||
else
|
||||
ifeq ($(NDK_BUILD_ARCH),arm)
|
||||
NDK_CFLAGS ::= -mthumb
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
|
||||
LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)
|
||||
else
|
||||
LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)
|
||||
endif
|
||||
|
||||
LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).a
|
||||
|
||||
# Record this module's dependencies and exported includes and CFLAGS,
|
||||
# and then add that of its dependencies.
|
||||
|
||||
include $(srcdir)/ndk-resolve.mk
|
||||
|
||||
# Then define rules to build all objects.
|
||||
ALL_SOURCE_FILES := $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
|
||||
|
||||
# Now filter out code that is built with neon. Define rules to build
|
||||
# those separately.
|
||||
NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
|
||||
ALL_SOURCE_FILES := $(filter-out %.neon,$(ALL_SOURCE_FILES))
|
||||
|
||||
# This defines all dependencies.
|
||||
ALL_OBJECT_FILES$(LOCAL_MODULE) =
|
||||
|
||||
$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call single-object-target,$(source),)))
|
||||
$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call single-neon-target,$(source),)))
|
||||
|
||||
# Now define the rule to build the library.
|
||||
$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE))
|
||||
$(NDK_BUILD_AR) r $@ $^
|
||||
68
cross/ndk-build/ndk-build.mk.in
Normal file
68
cross/ndk-build/ndk-build.mk.in
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is included all over the place to get and build
|
||||
# prerequisites.
|
||||
|
||||
NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
|
||||
NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@
|
||||
NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
|
||||
NDK_BUILD_SHARED =
|
||||
NDK_BUILD_STATIC =
|
||||
|
||||
define uniqify
|
||||
$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
|
||||
endef
|
||||
|
||||
# Remove duplicate modules. These can occur when a single module
|
||||
# imports a module and also declares it in LOCAL_SHARED_LIBRARIES.
|
||||
NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
|
||||
|
||||
# Here are all of the files to build.
|
||||
NDK_BUILD_ALL_FILES := $(foreach file,$(NDK_BUILD_MODULES), \
|
||||
$(top_builddir)/cross/ndk-build/$(file))
|
||||
|
||||
# The C++ standard library must be extracted from the Android NDK
|
||||
# directories and included in the application package, if any module
|
||||
# requires the C++ standard library.
|
||||
|
||||
ifneq ($(NDK_BUILD_ANY_CXX_MODULE),)
|
||||
NDK_BUILD_SHARED += $(NDK_BUILD_CXX_SHARED)
|
||||
endif
|
||||
|
||||
define subr-1
|
||||
ifeq ($(suffix $(1)),.so)
|
||||
NDK_BUILD_SHARED += $(top_builddir)/cross/ndk-build/$(1)
|
||||
else
|
||||
ifeq ($(suffix $(1)),.a)
|
||||
NDK_BUILD_STATIC += $(top_builddir)/cross/ndk-build/$(1)
|
||||
endif
|
||||
endif
|
||||
endef
|
||||
|
||||
# Generate rules for each module.
|
||||
|
||||
$(foreach module,$(NDK_BUILD_MODULES),$(eval $(call subr-1,$(module))))
|
||||
|
||||
# Generate rules to build everything now.
|
||||
# Make sure to use the top_builddir currently defined.
|
||||
|
||||
NDK_TOP_BUILDDIR := $(top_builddir)
|
||||
$(NDK_BUILD_ALL_FILES) &:
|
||||
$(MAKE) -C $(NDK_TOP_BUILDDIR)/cross/ndk-build $(NDK_BUILD_MODULES)
|
||||
57
cross/ndk-build/ndk-clear-vars.mk
Normal file
57
cross/ndk-build/ndk-clear-vars.mk
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
LOCAL_MODULE :=
|
||||
LOCAL_MODULE_FILENAME :=
|
||||
LOCAL_SRC_FILES :=
|
||||
LOCAL_CPP_EXTENSION :=
|
||||
LOCAL_CPP_FEATURES :=
|
||||
LOCAL_C_INCLUDES :=
|
||||
LOCAL_CFLAGS :=
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
LOCAL_SHARED_LIBRARIES :=
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES :=
|
||||
LOCAL_LDLIBS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
LOCAL_ALLOW_UNDEFINED_SYMBOLS :=
|
||||
LOCAL_ARM_MODE :=
|
||||
LOCAL_ARM_NEON :=
|
||||
LOCAL_DISABLE_FORMAT_STRING_CHECKS :=
|
||||
LOCAL_EXPORT_CFLAGS :=
|
||||
LOCAL_EXPORT_CPPFLAGS :=
|
||||
LOCAL_EXPORT_C_INCLUDES :=
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS :=
|
||||
LOCAL_EXPORT_LDFLAGS :=
|
||||
LOCAL_EXPORT_LDLIBS :=
|
||||
|
||||
# AOSP extensions.
|
||||
LOCAL_SRC_FILES_$(NDK_BUILD_ARCH) :=
|
||||
LOCAL_ASFLAGS_$(NDK_BUILD_ARCH) :=
|
||||
LOCAL_CFLAGS_$(NDK_BUILD_ARCH) :=
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH) :=
|
||||
LOCAL_IS_HOST_MODULE :=
|
||||
|
||||
# Emacs extensions!
|
||||
LOCAL_ASM_RULE_DEFINED :=
|
||||
LOCAL_ASM_RULE :=
|
||||
LOCAL_C_ADDITIONAL_FLAGS :=
|
||||
24
cross/ndk-build/ndk-prebuilt-shared-library.mk
Normal file
24
cross/ndk-build/ndk-prebuilt-shared-library.mk
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
$(warn Prebuilt shared libraries are not supported)
|
||||
24
cross/ndk-build/ndk-prebuilt-static-library.mk
Normal file
24
cross/ndk-build/ndk-prebuilt-static-library.mk
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
$(warn Prebuilt static libraries are not supported)
|
||||
162
cross/ndk-build/ndk-resolve.mk
Normal file
162
cross/ndk-build/ndk-resolve.mk
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# Copyright 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ndk-build works by including a bunch of Makefiles which set
|
||||
# variables, and then having those Makefiles include another makefile
|
||||
# which actually builds targets.
|
||||
|
||||
# List of system libraries to ignore.
|
||||
NDK_SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog android libandroid
|
||||
|
||||
# Save information.
|
||||
NDK_LOCAL_PATH_$(LOCAL_MODULE) := $(LOCAL_PATH)
|
||||
NDK_LOCAL_STATIC_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
|
||||
NDK_LOCAL_WHOLE_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_WHOLE_STATIC_LIBRARIES)
|
||||
NDK_LOCAL_SHARED_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_SHARED_LIBRARIES)
|
||||
NDK_LOCAL_EXPORT_CFLAGS_$(LOCAL_MODULE) := $(LOCAL_EXPORT_CFLAGS)
|
||||
NDK_LOCAL_EXPORT_C_INCLUDES_$(LOCAL_MODULE) := $(LOCAL_EXPORT_C_INCLUDES) $(LOCAL_EXPORT_C_INCLUDE_DIRS)
|
||||
NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) :=
|
||||
NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) :=
|
||||
NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) :=
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) :=
|
||||
|
||||
# List of all dependencies resolved for this module thus far.
|
||||
# Used to avoid infinite recursion.
|
||||
# Separate the variable which lists modules for which CFLAGS
|
||||
# have been resolved from the variable which lists modules
|
||||
# for which library dependencies have been resolved, in order
|
||||
# to catch the case where a library dependency is skipped
|
||||
# despite its CFLAGS being added.
|
||||
NDK_RESOLVED_$(LOCAL_MODULE) :=
|
||||
NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) :=
|
||||
|
||||
define ndk-resolve
|
||||
|
||||
ifeq ($$(filter $(1)$(and $(3),whole),$$(NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE))),)
|
||||
# Always mark this module's cflags as having been resolved, even if
|
||||
# this is a whole library.
|
||||
NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) += $(1)
|
||||
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) += $(NDK_LOCAL_EXPORT_CFLAGS_$(1))
|
||||
NDK_CFLAGS_$(LOCAL_MODULE) += $(addprefix -I,$(NDK_LOCAL_EXPORT_C_INCLUDES_$(1)))
|
||||
endif
|
||||
|
||||
ifeq ($$(filter $(1)$(and $(3),whole),$$(NDK_RESOLVED_$(LOCAL_MODULE))),)
|
||||
# Now append local libraries, as long as this library isn't a shared
|
||||
# library itself.
|
||||
ifeq ($(4),)
|
||||
|
||||
# Mark this module's library dependencies as having been resolved.
|
||||
NDK_RESOLVED_$(LOCAL_MODULE) += $(1)
|
||||
|
||||
# If this is a whole library, then mark this as resolved too, and
|
||||
# remove the library from the normal static library list.
|
||||
ifneq ($(3),)
|
||||
NDK_RESOLVED_$(LOCAL_MODULE) += $(1)whole
|
||||
endif
|
||||
|
||||
# If the module happens to be zlib, then add -lz to the shared library
|
||||
# flags.
|
||||
ifeq ($(strip $(1)),libz)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(1)),z)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
|
||||
endif
|
||||
|
||||
# Likewise for libdl.
|
||||
ifeq ($(strip $(1)),libdl)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(1)),dl)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
|
||||
endif
|
||||
|
||||
# Likewise for libstdc++.
|
||||
ifeq ($(strip $(1)),libstdc++)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(1)),dl)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
|
||||
endif
|
||||
|
||||
# Likewise for liblog.
|
||||
ifeq ($(strip $(1)),liblog)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(1)),log)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
|
||||
endif
|
||||
|
||||
# Likewise for libandroid.
|
||||
ifeq ($(strip $(1)),libandroid)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(1)),android)
|
||||
NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
|
||||
endif
|
||||
|
||||
ifeq ($(findstring $(1),$(NDK_SYSTEM_LIBRARIES))$(2)$(3),)
|
||||
ifneq ($(findstring lib,$(1)),)
|
||||
NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += $(1)_emacs.so
|
||||
else
|
||||
NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += lib$(1)_emacs.so
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(2),)
|
||||
ifneq ($(findstring lib,$(1)),)
|
||||
NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += $(1).a
|
||||
else
|
||||
NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(3),)
|
||||
ifneq ($(findstring lib,$(1)),)
|
||||
NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += $(1).a
|
||||
else
|
||||
NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
|
||||
endif
|
||||
|
||||
# Remove this archive from the regular archive list, should it already
|
||||
# exists. Any given archive should only appear once, and if an
|
||||
# archive has been specified as whole it should always be whole.
|
||||
NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out lib$(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
|
||||
NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out $(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
|
||||
endif
|
||||
endif
|
||||
|
||||
$$(foreach module,$$(NDK_LOCAL_STATIC_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),1,,$(or $(4),$(if $(2)$(3),,1)))))
|
||||
$$(foreach module,$$(NDK_LOCAL_SHARED_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),,,$(or $(4),$(if $(2)$(3),,1)))))
|
||||
$$(foreach module,$$(NDK_LOCAL_WHOLE_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),,1,$(or $(4),$(if $(2)$(3),,1)))))
|
||||
endif
|
||||
|
||||
endef
|
||||
|
||||
# Add shared libraries to the shared object names when they appear as
|
||||
# a top level dependency. However, do not recursively add the names
|
||||
# of this module's shared library dependencies, if it is just a shared
|
||||
# library, since it will link to those shared libraries itself.
|
||||
$(foreach module,$(LOCAL_SHARED_LIBRARIES),$(eval $(call ndk-resolve,$(module),,,)))
|
||||
$(foreach module,$(LOCAL_STATIC_LIBRARIES),$(eval $(call ndk-resolve,$(module),1,,)))
|
||||
$(foreach module,$(LOCAL_WHOLE_STATIC_LIBRARIES), $(eval $(call ndk-resolve,$(module),,1,)))
|
||||
55
cross/verbose.mk.android
Normal file
55
cross/verbose.mk.android
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
### verbose.mk --- Makefile fragment for GNU Emacs during
|
||||
### cross-compilation.
|
||||
|
||||
## Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
## This file is part of GNU Emacs.
|
||||
|
||||
## GNU Emacs is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## GNU Emacs is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# 'make' verbosity.
|
||||
V = 0
|
||||
ifeq (${V},1)
|
||||
AM_V_AR =
|
||||
AM_V_at =
|
||||
AM_V_CC =
|
||||
AM_V_CXX =
|
||||
AM_V_CCLD =
|
||||
AM_V_CXXLD =
|
||||
AM_V_GEN =
|
||||
else
|
||||
|
||||
# Whether $(info ...) works. This is to work around a bug in GNU Make
|
||||
# 4.3 and earlier, which implements $(info MSG) via two system calls
|
||||
# { write (..., "MSG", 3); write (..., "\n", 1); }
|
||||
# which looks bad when make -j interleaves two of these at about the same time.
|
||||
#
|
||||
# Later versions of GNU Make have the 'notintermediate' feature,
|
||||
# so assume that $(info ...) works if this feature is present.
|
||||
#
|
||||
have_working_info = $(filter notintermediate,$(value .FEATURES))
|
||||
#
|
||||
# The workaround is to use the shell and 'echo' rather than $(info ...).
|
||||
# The workaround is done only for AM_V_ELC and AM_V_ELN,
|
||||
# since the bug is not annoying elsewhere.
|
||||
|
||||
AM_V_AR = @$(info $ AR $@)
|
||||
AM_V_at = @
|
||||
AM_V_CC = @$(info $ CC $@)
|
||||
AM_V_CXX = @$(info $ CXX $@)
|
||||
AM_V_CCLD = @$(info $ CCLD $@)
|
||||
AM_V_CXXLD = @$(info $ CXXLD $@)
|
||||
AM_V_GEN = @$(info $ GEN $@)
|
||||
AM_V_NO_PD = --no-print-directory
|
||||
endif
|
||||
|
|
@ -146,6 +146,8 @@ EMACSSOURCES= \
|
|||
${srcdir}/glossary.texi \
|
||||
${srcdir}/ack.texi \
|
||||
${srcdir}/kmacro.texi \
|
||||
${srcdir}/android.texi \
|
||||
${srcdir}/input.texi \
|
||||
$(EMACS_XTRA)
|
||||
|
||||
## Disable implicit rules.
|
||||
|
|
|
|||
647
doc/emacs/android.texi
Normal file
647
doc/emacs/android.texi
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
@c This is part of the Emacs manual.
|
||||
@c Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
@c See file emacs.texi for copying conditions.
|
||||
@node Android
|
||||
@appendix Emacs and Android
|
||||
@cindex Android
|
||||
|
||||
Android is a mobile operating system developed by the Open Handset
|
||||
Alliance. This section describes the peculiarities of using Emacs on
|
||||
an Android device running Android 2.2 or later.
|
||||
|
||||
Android devices commonly rely on user input through a touch screen
|
||||
or digitizer device and on-screen keyboard. For more information
|
||||
about using such devices with Emacs, @pxref{Other Input Devices}.
|
||||
|
||||
@menu
|
||||
* What is Android?:: Preamble.
|
||||
* Android Startup:: Starting up Emacs on Android.
|
||||
* Android Environment:: Running Emacs under Android.
|
||||
* Android File System:: The Android file system.
|
||||
* Android Windowing:: The Android window system.
|
||||
* Android Fonts:: Font selection under Android.
|
||||
* Android Troubleshooting:: Dealing with problems.
|
||||
@end menu
|
||||
|
||||
@node What is Android?
|
||||
@section Android history
|
||||
|
||||
Android is an operating system for mobile devices developed by the
|
||||
Open Handset Alliance, a group of companies interested in developing
|
||||
handsets that can run a common set of software. It is supposedly free
|
||||
software.
|
||||
|
||||
Like the X Consortium of times past, the Open Handset Alliance
|
||||
believes that ``openness'' (namely, the regular release of the Android
|
||||
source code) is simply a tool to increase the popularity of the
|
||||
Android platform. Computer companies normally produce proprietary
|
||||
software. The companies in the Open Handset Alliance are no different
|
||||
-- most versions of Android installed on devices are proprietary, by
|
||||
virtue of containing proprietary components, that often cannot even be
|
||||
replaced by the user.
|
||||
|
||||
Android is not designed to respect users' freedom. Almost all
|
||||
versions of Android (including some which are supposedly free
|
||||
software) include support for Digital Restrictions Management,
|
||||
technology that is designed to limit users' ability to copy media to
|
||||
and from their own devices. Most Android devices also come with
|
||||
proprietary Google applications which are required to run the system,
|
||||
and many other Android applications.
|
||||
|
||||
Thus, it must be necessary to consider Android proprietary software
|
||||
from a practical standpoint. That is an injustice. If you use
|
||||
Android, we urge you to switch to a free operating system, if only for
|
||||
your freedom's sake.
|
||||
|
||||
We support GNU Emacs on proprietary operating systems because we
|
||||
hope this taste of freedom will inspire users to escape from them.
|
||||
|
||||
@node Android Startup
|
||||
@section Starting up Emacs on Android
|
||||
|
||||
Emacs is not installed on Android devices from source code, or by a
|
||||
package manager. Instead, Emacs is compiled for Android on a
|
||||
different operating system, with the resulting binaries packaged into
|
||||
an archive, that is then transferred to the device and unpacked.
|
||||
|
||||
After being unpacked, Emacs instructs the system to display an
|
||||
application icon on the desktop. Emacs then starts up once the
|
||||
application icon is clicked.
|
||||
|
||||
@cindex ``adb logcat''
|
||||
|
||||
During startup, Emacs will display messages in the system log
|
||||
buffer; reading that buffer requires the Android Debug Bridge
|
||||
(@command{adb}) utility to be installed on another computer; it cannot
|
||||
be read on the computer running Android itself.
|
||||
|
||||
After enabling the ``USB Debugging'' feature on the Android system,
|
||||
and connecting it via USB to another system with the @command{adb}
|
||||
utility installed, the log can be viewed by running the following
|
||||
command on that other system:
|
||||
|
||||
@example
|
||||
$ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
|
||||
@end example
|
||||
|
||||
Assuming that the @command{adb} utility is installed on a GNU/Linux
|
||||
or Unix system, follow the steps below to connect to your device.
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
Enable ``developer options'' on your device, by going to the ``About''
|
||||
page in the system settings application and clicking on the ``build
|
||||
version'' or ``kernel version'' items five to seven times.
|
||||
|
||||
@item
|
||||
Open the ``developer options'' settings page, which should be under
|
||||
the ``system'' page in the settings application.
|
||||
|
||||
@item
|
||||
Turn on the switch ``USB debugging''.
|
||||
|
||||
@item
|
||||
Connect one end of a USB cable to your device, and the other end to
|
||||
your computer's USB port.
|
||||
|
||||
@item
|
||||
Run the command @command{adb shell} on your computer. This will fail
|
||||
or hang because you have not yet granted your computer permission to
|
||||
access the connected device.
|
||||
|
||||
@item
|
||||
Confirm the pop-up displayed on your device asking whether or not it
|
||||
should allow access from your computer.
|
||||
@end enumerate
|
||||
|
||||
Depending on the versions of Android and @command{adb} installed,
|
||||
there may be other ways to establish a connection. See the official
|
||||
documentation at
|
||||
@url{https://developer.android.com/studio/command-line/adb} for more
|
||||
details.
|
||||
|
||||
@cindex emacsclient wrapper, android
|
||||
Since there is no other way to start the @command{emacsclient}
|
||||
program (@pxref{Emacs Server}) from another Android program, Emacs
|
||||
provides a wrapper around the @command{emacsclient} program, which is
|
||||
registered with the system as an application that can open all text
|
||||
files.
|
||||
|
||||
When that wrapper is selected as the program with which to open a
|
||||
file, it invokes @command{emacsclient} with the options
|
||||
@command{--reuse-frame}, @command{--timeout=10}, @command{--no-wait},
|
||||
and the name of the file being opened. Then, upon success, the focus
|
||||
is transferred to any open Emacs frame.
|
||||
|
||||
However, if Emacs is not running at the time the wrapper is opened,
|
||||
it starts Emacs and gives it the file to open as an argument. Note
|
||||
that if that Emacs in turn does not start the Emacs server, subsequent
|
||||
attempts to open the file with the wrapper will fail.
|
||||
|
||||
@cindex /content directory, android
|
||||
Some files are given to Emacs as ``content identifiers'', which the
|
||||
system provides access to outside the normal filesystem APIs. Emacs
|
||||
internally supports a temporary @file{/content} directory which is
|
||||
used to access those files. Do not make any assumptions about the
|
||||
contents of this directory, or try to open files in it yourself.
|
||||
|
||||
This feature is not provided on Android 4.3 and earlier, in which
|
||||
case the file is copied to a temporary directory instead.
|
||||
|
||||
@node Android File System
|
||||
@section What files Emacs can access under Android
|
||||
@cindex /assets directory, android
|
||||
|
||||
Emacs exposes a special directory on Android systems: the name of
|
||||
the directory is @file{/assets}, and it contains the @file{etc},
|
||||
@file{lisp} and @file{info} directories which are normally installed
|
||||
in @file{/usr/share/emacs} directory on GNU and Unix systems. On
|
||||
Android systems, the Lisp emulation of @command{ls} (@pxref{ls in
|
||||
Lisp}) is also enabled by default, as the @command{ls} binary which
|
||||
comes with the system varies by manufacturer and usually does not
|
||||
support all of the features required by Emacs. One copy of
|
||||
@command{ls} shipped with some Android devices is even known to lack
|
||||
support for the @code{-l} flag.
|
||||
|
||||
@cindex limitations of the /assets directory
|
||||
|
||||
This directory exists because Android does not extract the contents
|
||||
of application packages on to the file system while unpacking them,
|
||||
but instead requires programs like Emacs to access its contents using
|
||||
a special ``asset manager'' interface. Here are the peculiarities
|
||||
that result from such an implementation:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Subprocesses (such as @command{ls}) can not run from the
|
||||
@file{/assets} directory; if you try to run a subprocess with
|
||||
@code{current-directory} set to @file{/assets} or a subdirectory
|
||||
thereof, it will run from the home directory instead.
|
||||
|
||||
@item
|
||||
There are no @file{.} and @file{..} directories inside the
|
||||
@file{/assets} directory.
|
||||
|
||||
@item
|
||||
Files in the @file{/assets} directory are always read only, and have
|
||||
to be completely read in to memory each time they are opened.
|
||||
@end itemize
|
||||
|
||||
Aside from the @file{/assets} directory, Android programs normally
|
||||
have access to three other directories. They are:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The @dfn{app data} directory. This also serves as the home directory
|
||||
for Emacs, and is always accessible read-write.
|
||||
|
||||
@item
|
||||
The @dfn{app library} directory. This is automatically appended to
|
||||
@code{exec-path} upon startup.
|
||||
|
||||
@item
|
||||
The @dfn{external storage} directory. This is accessible to Emacs
|
||||
when the user grants the ``Files and Media'' permission to Emacs via
|
||||
system settings.
|
||||
@end itemize
|
||||
|
||||
The external storage directory is found at @file{/sdcard}. The
|
||||
other directories are not found at any fixed location, although the
|
||||
app data directory is typically symlinked to
|
||||
@file{/data/data/org.gnu.emacs}.
|
||||
|
||||
@cindex temp~unlinked.NNNN files, Android
|
||||
On Android devices running very old (2.6.29) versions of the Linux
|
||||
kernel, Emacs needs to create files named starting with
|
||||
@file{temp~unlinked} in the the temporary file directory in order to
|
||||
read from asset files. Do not create files with such names yourself,
|
||||
or they may be overwritten or removed.
|
||||
|
||||
@cindex file system limitations, Android 11
|
||||
On Android 11 and later, the Android system restricts applications
|
||||
from accessing files in the @file{/sdcard} directory using
|
||||
file-related system calls such as @code{open} and @code{readdir}.
|
||||
|
||||
This restriction is known as ``Scoped Storage'', and supposedly
|
||||
makes the system more secure. Unfortunately, it also means that Emacs
|
||||
cannot access files in those directories, despite holding the
|
||||
necessary permissions. Thankfully, the Open Handset Alliance's
|
||||
version of Android allows this restriction to be disabled on a
|
||||
per-program basis; the corresponding option in the system settings
|
||||
panel is:
|
||||
|
||||
@example
|
||||
System -> Apps -> Special App Access -> All files access -> Emacs
|
||||
@end example
|
||||
|
||||
After you disable or enable this setting as appropriate and grant
|
||||
Emacs the ``Files and Media'' permission, it will be able to access
|
||||
files under @file{/sdcard} as usual.
|
||||
|
||||
These settings are not present on many proprietary versions of
|
||||
Android.
|
||||
|
||||
@node Android Environment
|
||||
@section Running Emacs under Android
|
||||
|
||||
From the perspective of users, Android is mostly a single user
|
||||
operating system; however, from the perspective of applications and
|
||||
Emacs, the system has an overwhelming number of users.
|
||||
|
||||
Each application runs in its own user, with his own home directory,
|
||||
which is the app data directory (@pxref{Android File System}.)
|
||||
|
||||
Each application is also prohibited from accessing system
|
||||
directories, and the app data directories of other applications.
|
||||
|
||||
Emacs comes with several binaries. While being executable files,
|
||||
they are packaged as libraries in the library directory, because
|
||||
otherwise the system will not unpack them while Emacs is being
|
||||
installed. This means, instead of specifying @code{ctags} or
|
||||
@code{emacsclient} in a subprocess, Lisp code must specify
|
||||
@code{libctags.so} or @code{libemacsclient.so} on the command line
|
||||
instead when starting either of those programs in a subprocess.
|
||||
|
||||
The @file{/assets} directory containing Emacs start-up files is
|
||||
supposed to be inaccessible to processes not directly created by
|
||||
@code{zygote}, the system service responsible for starting
|
||||
applications. Since required Lisp is found in the @file{/assets}
|
||||
directory, it would thus follow that it is not possible for Emacs to
|
||||
start itself as a subprocess. A special binary named
|
||||
@command{libandroid-emacs.so} is provided with Emacs, and does its
|
||||
best to start Emacs, for the purpose of running Lisp in batch mode.
|
||||
However, the approach it takes was devised by reading Android source
|
||||
code, and is not sanctioned by the Android compatibility definition
|
||||
documents, so your mileage may vary.
|
||||
|
||||
@cindex call-process, Android
|
||||
@vindex android-use-exec-loader
|
||||
Android 10 and later also prohibit Emacs itself from running
|
||||
executables inside the app data directory, obstensibly for security
|
||||
readers. On these systems, Emacs normally applies a workaround;
|
||||
however, this workaround requires running all sub-processes through
|
||||
another subprocess which implements an executable loader and applies
|
||||
process tracing to all its children, which may prove to be problematic
|
||||
for various different reasons. In that case, the workaround can be
|
||||
disabled by changing the variable @code{android-use-exec-loader} to
|
||||
@code{nil}.
|
||||
|
||||
When this workaround is in effect, process IDs retrieved through the
|
||||
@code{process-id} function will be that of the executable loader
|
||||
process; its child will belong to the same process group as the
|
||||
loader. As a result, @code{interrupt-process}, and other related
|
||||
functions will work correctly, but using the process ID returned by
|
||||
@code{process-id} for other purposes will not.
|
||||
|
||||
One side effect of the mechanism by which process tracing is carried
|
||||
out is that job control facilities will not be able to stop
|
||||
subprocesses, and the @code{SIGSTOP} signal will appear to have no
|
||||
effect.
|
||||
|
||||
In addition, Android 12 also terminates subprocesses which are
|
||||
consuming CPU while Emacs itself is in the background. The system
|
||||
determines which processes are consuming too much CPU in intervals of
|
||||
five minutes, and terminates the process that has consumed the most
|
||||
CPU time.
|
||||
|
||||
Android 12.1 and Android 13 provide an option to disable this
|
||||
behavior; to use it, enable ``USB debugging'' (@pxref{Android
|
||||
Startup}) connect the Android system to another computer, and run:
|
||||
|
||||
@example
|
||||
$ adb shell "settings put global settings_enable_monitor_phantom_procs false"
|
||||
@end example
|
||||
|
||||
@section Running Emacs in the background
|
||||
@cindex emacs killed, android
|
||||
@cindex emacs in the background, android
|
||||
|
||||
Application processes are treated as disposable entities by the
|
||||
system. When all Emacs frames move to the background, Emacs is liable
|
||||
to be killed by the system at any time, for the purpose of saving
|
||||
system resources.
|
||||
|
||||
On Android 7.1 and earlier, Emacs tells the system to treat it as a
|
||||
``background service''. The system will try to avoid killing Emacs
|
||||
unless the device is under memory stress.
|
||||
|
||||
Android 8.0 removed the ability for background services to receive
|
||||
such special treatment. However, Emacs applies a workaround: the
|
||||
system considers applications that create a permanent notification to
|
||||
be performing active work, and will avoid killing such applications.
|
||||
Thus, on those systems, Emacs displays a permanent notification for as
|
||||
long as it is running. Once the notification is displayed, it can be
|
||||
safely hidden through the system settings without resulting in Emacs
|
||||
being killed.
|
||||
|
||||
However, it is not guaranteed that the system will not kill Emacs,
|
||||
even if the notification is being displayed. While the Open Handset
|
||||
Alliance's sample implementation of Android behaves correctly, many
|
||||
manufacturers place additional restrictions on program execution in
|
||||
the background in their proprietary versions of Android. There is a
|
||||
list of such troublesome manufacturers and sometimes workarounds, at
|
||||
@url{https://dontkillmyapp.com/}.
|
||||
|
||||
@section Android permissions
|
||||
@cindex external storage, android
|
||||
|
||||
Android also defines a permissions system that determines what
|
||||
system services Emacs is allowed to access. Programs must specify
|
||||
what permissions they want; what then happens depends on the version
|
||||
of Android being used:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
On Android 5.1 and earlier, Emacs automatically receives the following
|
||||
permissions it has requested upon being installed:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
@code{android.permission.READ_CONTACTS}
|
||||
@item
|
||||
@code{android.permission.WRITE_CONTACTS}
|
||||
@item
|
||||
@code{android.permission.VIBRATE}
|
||||
@item
|
||||
@code{android.permission.ACCESS_COARSE_LOCATION}
|
||||
@item
|
||||
@code{android.permission.ACCESS_NETWORK_STATE}
|
||||
@item
|
||||
@code{android.permission.INTERNET}
|
||||
@item
|
||||
@code{android.permission.SET_WALLPAPER}
|
||||
@item
|
||||
@code{android.permission.WRITE_EXTERNAL_STORAGE}
|
||||
@item
|
||||
@code{android.permission.SEND_SMS}
|
||||
@item
|
||||
@code{android.permission.RECEIVE_SMS}
|
||||
@item
|
||||
@code{android.permission.RECEIVE_MMS}
|
||||
@item
|
||||
@code{android.permission.WRITE_SMS}
|
||||
@item
|
||||
@code{android.permission.READ_SMS}
|
||||
@item
|
||||
@code{android.permission.NFC}
|
||||
@item
|
||||
@code{android.permission.TRANSMIT_IR}
|
||||
@item
|
||||
@code{android.permission.READ_PHONE_STATE}
|
||||
@item
|
||||
@code{android.permission.WAKE_LOCK}
|
||||
@item
|
||||
@code{android.permission.FOREGROUND_SEVICE}
|
||||
@item
|
||||
@code{android.permission.REQUEST_INSTALL_PACKAGES}
|
||||
@item
|
||||
@code{android.permission.REQUEST_DELETE_PACKAGES}
|
||||
@item
|
||||
@code{android.permission.SYSTEM_ALERT_WINDOW}
|
||||
@item
|
||||
@code{android.permission.RECORD_AUDIO}
|
||||
@item
|
||||
@code{android.permission.CAMERA}
|
||||
@end itemize
|
||||
|
||||
While most of these permissions are left unused by Emacs itself, they
|
||||
are declared by Emacs as they could be useful for other programs; for
|
||||
example, the permission to access contacts may be useful for EUDC.
|
||||
|
||||
@item
|
||||
On Android 6.0 and later, Emacs only receives the following
|
||||
permissions upon installation:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
@code{android.permission.VIBRATE}
|
||||
@item
|
||||
@code{android.permission.ACCESS_NETWORK_STATE}
|
||||
@item
|
||||
@code{android.permission.INTERNET}
|
||||
@item
|
||||
@code{android.permission.SET_WALLPAPER}
|
||||
@item
|
||||
@code{android.permission.NFC}
|
||||
@item
|
||||
@code{android.permission.TRANSMIT_IR}
|
||||
@item
|
||||
@code{android.permission.WAKE_LOCK}
|
||||
@item
|
||||
@code{android.permission.POST_NOTIFICATIONS}
|
||||
@end itemize
|
||||
|
||||
Other permissions must be granted by the user through the system
|
||||
settings application. Consult the manufacturer of your device for
|
||||
more details, as how to do this varies by device.
|
||||
@end itemize
|
||||
|
||||
@node Android Windowing
|
||||
@section The Android window system
|
||||
|
||||
Android has an unusual window system; there, all windows are
|
||||
maximized or full-screen, and only one window can be displayed at a
|
||||
time. On larger devices, the system allows up to four windows to be
|
||||
tiled on the screen at any time.
|
||||
|
||||
Windows on Android do not continue to exist indefinitely after they
|
||||
are created. Instead, the system may choose to terminate windows that
|
||||
are not on screen in order to save memory, with the assumption that
|
||||
the program will save its contents to disk and restore them later,
|
||||
when the user asks for it to be opened again. As this is obviously
|
||||
not possible with Emacs, Emacs separates the resources associated with
|
||||
a frame from its system window.
|
||||
|
||||
Each system window created (including the initial window created
|
||||
during Emacs startup) is appended to a list of windows that do not
|
||||
have associated frames. When a frame is created, Emacs looks up any
|
||||
window within that list, and displays the contents of the frame
|
||||
within; if there is no window at all, then one is created. Likewise,
|
||||
when a new window is created by the system, Emacs places the contents
|
||||
of any frame that is not already displayed within a window inside.
|
||||
When a frame is closed, the corresponding system window is also
|
||||
closed. Upon startup, the system creates a window itself (within
|
||||
which Emacs displays the first window system frame shortly
|
||||
thereafter.) Emacs differentiates between that window and windows
|
||||
created on behalf of other frames to determine what to do when the
|
||||
system window associated with a frame is closed:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
When the system closes the window created during application startup
|
||||
in order to save memory, Emacs retains the frame for when that window
|
||||
is created later.
|
||||
|
||||
@item
|
||||
When the user closes the window created during application startup,
|
||||
and the window was not previously closed by the system in order to
|
||||
save resources, Emacs deletes any frame displayed within that window.
|
||||
|
||||
@item
|
||||
When the user or the system closes any window created by Emacs on
|
||||
behalf of a specific frame, Emacs deletes the frame displayed within
|
||||
that window.
|
||||
@end itemize
|
||||
|
||||
@cindex windowing limitations, android
|
||||
@cindex frame parameters, android
|
||||
Emacs only supports a limited subset of GUI features on Android; the
|
||||
limitations are as follows:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Scroll bars are not supported, as they are close to useless on Android
|
||||
devices.
|
||||
|
||||
@item
|
||||
The @code{alpha}, @code{alpha-background}, @code{z-group},
|
||||
@code{override-redirect}, @code{mouse-color}, @code{title},
|
||||
@code{wait-for-wm}, @code{sticky}, @code{undecorated} and
|
||||
@code{tool-bar-position} frame parameters (@pxref{Frame Parameters,,,
|
||||
elisp, the Emacs Lisp Reference Manual}) are unsupported.
|
||||
|
||||
@item
|
||||
On Android 4.0 and earlier, the @code{fullscreen} frame parameter is
|
||||
always @code{maximized} for top-level frames; on later versions of
|
||||
Android, it can also be @code{fullscreen}.
|
||||
@end itemize
|
||||
|
||||
@cindex selections, android
|
||||
@cindex android clipboard
|
||||
Emacs does not implement all selection related features supported
|
||||
under the X Window System on Android. For example, only the
|
||||
@code{CLIPBOARD} and @code{PRIMARY} selections (@pxref{Cut and Paste})
|
||||
are supported, and Emacs is only able to set selections to plain text.
|
||||
|
||||
In addition, the Android system itself places certain restrictions
|
||||
on what selection data Emacs can access:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
On Android 2.3 and earlier, the function @code{gui-selection-owner-p}
|
||||
always returns @code{nil} for the clipboard selection.
|
||||
|
||||
@item
|
||||
Between Android 3.0 and Android 9.0, Emacs is able to access the
|
||||
clipboard whenever it wants, and @code{gui-selection-owner-p} always
|
||||
returns accurate results.
|
||||
|
||||
@item
|
||||
Under Android 10.0 and later, Emacs can only access clipboard data
|
||||
when one of its frames has the input focus, and
|
||||
@code{gui-selection-owner-p} always returns @code{nil} for the
|
||||
clipboard selection.
|
||||
@end itemize
|
||||
|
||||
Since the Android system itself has no concept of a primary
|
||||
selection, Emacs provides an emulation instead. This means there is
|
||||
no way to transfer the contents of the primary selection to another
|
||||
application via cut-and-paste.
|
||||
|
||||
@vindex android-pass-multimedia-buttons-to-system
|
||||
@cindex volume/multimedia buttons, Android
|
||||
The volume keys are normally reserved by Emacs and used to provide
|
||||
the ability to quit Emacs without a physical keyboard
|
||||
(@pxref{On-Screen Keyboards}.) However, if you want them to adjust
|
||||
the volume instead, you can set the variable
|
||||
@code{android-pass-multimedia-buttons-to-system} to a non-@code{nil}
|
||||
value; note that you will no longer be able to quit Emacs using the
|
||||
volume buttons in that case.
|
||||
|
||||
@cindex dialog boxes, android
|
||||
Emacs is unable to display dialog boxes (@pxref{Dialog Boxes}) while
|
||||
it does not have the input focus on Android 6.0 or later. If this is
|
||||
important to you, this ability can be restored by granting Emacs
|
||||
permission to display over other programs. Normally, this can be done
|
||||
from the:
|
||||
|
||||
@example
|
||||
System -> Apps -> Emacs -> More -> Display over other apps
|
||||
@end example
|
||||
|
||||
menu in the system settings, but this procedure may vary by device.
|
||||
|
||||
@node Android Fonts
|
||||
@section Font backends and selection under Android
|
||||
@cindex fonts, android
|
||||
|
||||
Emacs supports two font backends under Android: they are respectively
|
||||
named @code{sfnt-android} and @code{android}.
|
||||
|
||||
Upon startup, Emacs enumerates all the TrueType format fonts in the
|
||||
directories @file{/system/fonts} and @file{/product/fonts}, and the
|
||||
@file{fonts} directory (@dfn{user fonts directory}) inside the Emacs
|
||||
home directory. Emacs assumes there will always be a font named
|
||||
``Droid Sans Mono'', and then defaults to using this font. These
|
||||
fonts are then displayed by the @code{sfnt-android} font driver.
|
||||
|
||||
When running on Android, Emacs currently lacks support for OpenType
|
||||
fonts. This means that only a subset of the fonts installed on the
|
||||
system are currently available to Emacs. If you are interested in
|
||||
lifting this limitation, please contact @email{emacs-devel@@gnu.org}.
|
||||
|
||||
If the @code{sfnt-android} font driver fails to find any fonts at
|
||||
all, Emacs falls back to the @code{android} font driver. This is a
|
||||
very lousy font driver, because of limitations and inaccuracies in the
|
||||
font metrics provided by the Android platform. In that case, Emacs
|
||||
uses the ``Monospace'' typeface configured on your system; this should
|
||||
always be Droid Sans Mono.
|
||||
|
||||
@cindex TrueType GX fonts, android
|
||||
@cindex distortable fonts, android
|
||||
|
||||
Like on X systems, Emacs supports distortable fonts under Android.
|
||||
These fonts (also termed ``TrueType GX fonts'', ``variable fonts'',
|
||||
and ``multiple master fonts'') provide multiple different styles
|
||||
(``Bold'', ``Italic'', etc) using a single font file.
|
||||
|
||||
When a user-installed distortable font is found, each font that a
|
||||
previously discovered font provided will no longer be used. In
|
||||
addition, any previously specified distortable fonts with the same
|
||||
family name are also removed. When a conventional font is found, any
|
||||
previous conventional font with the same style and family will be
|
||||
removed; distortable fonts with the same family will no longer be used
|
||||
to provide that style.
|
||||
|
||||
@node Android Troubleshooting
|
||||
@section What to do when something goes wrong on Android
|
||||
@cindex troubleshooting, android
|
||||
|
||||
@cindex emacs -Q, android
|
||||
Since Android has no command line, there is normally no way to
|
||||
specify command-line arguments when starting Emacs. This is very
|
||||
nasty when you make a mistake in your Emacs initialization files that
|
||||
prevents Emacs from starting up at all, as the system normally
|
||||
prevents other programs from accessing Emacs's home directory.
|
||||
|
||||
However, Emacs can be started with the equivalent of the
|
||||
@code{--quick} option (@pxref{Initial Options}) through a special
|
||||
preferences screen, which can be accessed through the Emacs ``app
|
||||
info'' page in the system settings application.
|
||||
|
||||
Consult the manufacturer of your device for more details, as how to
|
||||
do this varies by device.
|
||||
|
||||
@cindex dumping, android
|
||||
The first time any given copy of Emacs starts on a device, it spends
|
||||
a while loading the preloaded Lisp files which normally come with
|
||||
Emacs. This produces a ``dump file'' (@pxref{Initial Options}) in the
|
||||
files directory, containing an identifier unique to this copy of
|
||||
Emacs.
|
||||
|
||||
The next time that same copy of Emacs starts up, it simply loads the
|
||||
data contained in that dump file, greatly improving start up time.
|
||||
|
||||
If by some unforeseen circumstance the dump file is corrupted, Emacs
|
||||
can crash. If that happens, the dump file stored in the Emacs files
|
||||
directory can be erased through the same preferences screen.
|
||||
|
||||
@cindex accessing Emacs directories, Android
|
||||
Emacs supports an alternative method of rescuing broken Emacs
|
||||
installations on Android 4.4 and later: Emacs exports a ``documents
|
||||
provider'' which accesses the contents of Emacs's home directory, that
|
||||
can then be accessed by any file manager program.
|
||||
|
||||
If you can find out how to open that documents provider in the file
|
||||
manager that comes with your device, you can rename, delete, or edit
|
||||
your initialization or dump files from there instead.
|
||||
|
|
@ -223,7 +223,9 @@ Appendices
|
|||
* Antinews:: Information about Emacs version 28.
|
||||
* Mac OS / GNUstep:: Using Emacs under macOS and GNUstep.
|
||||
* Haiku:: Using Emacs on Haiku.
|
||||
* Android:: Using Emacs on Android.
|
||||
* Microsoft Windows:: Using Emacs on Microsoft Windows and MS-DOS.
|
||||
* Other Input Devices:: Using Emacs with other input devices.
|
||||
* Manifesto:: What's GNU? Gnu's Not Unix!
|
||||
|
||||
* Glossary:: Terms used in this manual.
|
||||
|
|
@ -1260,6 +1262,21 @@ Emacs and Haiku
|
|||
* Haiku Basics:: Basic Emacs usage and installation under Haiku.
|
||||
* Haiku Fonts:: The various options for displaying fonts on Haiku.
|
||||
|
||||
Emacs and Android
|
||||
|
||||
* What is Android?:: Preamble.
|
||||
* Android Startup:: Starting up Emacs on Android.
|
||||
* Android Environment:: Running Emacs under Android.
|
||||
* Android File System:: The Android file system.
|
||||
* Android Windowing:: The Android window system.
|
||||
* Android Fonts:: Font selection under Android.
|
||||
* Android Troubleshooting:: Dealing with problems.
|
||||
|
||||
Emacs and unconventional input devices
|
||||
|
||||
* Touchscreens:: Using Emacs on touchscreens.
|
||||
* On-Screen Keyboards:: Using Emacs with virtual keyboards.
|
||||
|
||||
Emacs and Microsoft Windows/MS-DOS
|
||||
|
||||
* Windows Startup:: How to start Emacs on Windows.
|
||||
|
|
@ -1630,8 +1647,10 @@ Lisp programming.
|
|||
@include anti.texi
|
||||
@include macos.texi
|
||||
@include haiku.texi
|
||||
@include android.texi
|
||||
@c Includes msdos-xtra.
|
||||
@include msdos.texi
|
||||
@include input.texi
|
||||
@include gnu.texi
|
||||
@include glossary.texi
|
||||
@ifnottex
|
||||
|
|
|
|||
146
doc/emacs/input.texi
Normal file
146
doc/emacs/input.texi
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
@c This is part of the Emacs manual.
|
||||
@c Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
@c See file emacs.texi for copying conditions.
|
||||
@node Other Input Devices
|
||||
@appendix Emacs and unconventional input devices
|
||||
@cindex other input devices
|
||||
|
||||
Emacs was originally developed with the assumption that users will
|
||||
be sitting in front of a desktop computer, with a keyboard and perhaps
|
||||
a suitable pointing device such as a mouse.
|
||||
|
||||
However, recent developments in the X Window System, and in other
|
||||
operating systems such as Android, mean that this assumption no longer
|
||||
holds true. As a result, Emacs now has support for other kinds of
|
||||
input devices, which is detailed here.
|
||||
|
||||
@menu
|
||||
* Touchscreens:: Using Emacs on touchscreens.
|
||||
* On-Screen Keyboards:: Using Emacs with virtual keyboards.
|
||||
@end menu
|
||||
|
||||
@node Touchscreens
|
||||
@section Using Emacs on touchscreens
|
||||
@cindex touchscreens
|
||||
|
||||
Touchscreen input works by having the user press tools onto the
|
||||
screen, which can be his own fingers, or a pointing device such as a
|
||||
stylus, in order to manipulate the contents there in.
|
||||
|
||||
When running under the X Window System or Android, Emacs
|
||||
automatically detects and maps the following touchscreen gestures to
|
||||
common actions:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
@cindex tapping, touchscreens
|
||||
``Tapping'', meaning to briefly place and lift a tool from the
|
||||
display, will result in Emacs selecting the window that was tapped,
|
||||
and executing any command bound to @code{mouse-1} at that location in
|
||||
the window. If the tap happened on top of a link (@pxref{Mouse
|
||||
References}), then Emacs will follow the link instead.
|
||||
|
||||
@item
|
||||
@cindex scrolling, touchscreens
|
||||
``Scrolling'', meaning to place a tool on the display and move it up
|
||||
or down, will result in Emacs scrolling the window contents in the
|
||||
direction where the tool moves.
|
||||
|
||||
If the tool is moved left or right, Emacs additionally scrolls the
|
||||
window horizontally to follow (@pxref{Horizontal Scrolling}.)
|
||||
|
||||
@item
|
||||
@cindex dragging, touchscreens
|
||||
``Dragging'', meaning to place a tool on the display and leave it
|
||||
there for a while before moving the tool around, will make Emacs set
|
||||
the point to where the tool was and begin selecting text under the
|
||||
tool as it moves around, much like what would happen if @code{mouse-1}
|
||||
were to be held down. @xref{Mouse Commands}.
|
||||
@end itemize
|
||||
|
||||
@vindex touch-screen-delay
|
||||
By default, Emacs considers a tool as having been left on the
|
||||
display for a while after 0.7 seconds, but this can be changed by
|
||||
customizing the variable @code{touch-screen-delay}.
|
||||
|
||||
@node On-Screen Keyboards
|
||||
@section Using Emacs with virtual keyboards
|
||||
@cindex virtual keyboards
|
||||
@cindex on-screen keyboards
|
||||
|
||||
When there is no physical keyboard attached to a system, the
|
||||
windowing system typically provides an on-screen keyboard, more often
|
||||
known as a ``virtual keyboard'', containing rows of clickable buttons
|
||||
that send keyboard input to the application, much like a real keyboard
|
||||
would. This virtual keyboard is hidden by default, as it uses up
|
||||
valuable on-screen real estate, and must be opened once the program
|
||||
being used is ready to accept keyboard input.
|
||||
|
||||
Under the X Window System, the client that provides the on-screen
|
||||
keyboard typically detects when the application is ready to accept
|
||||
keyboard input through a set of complex heuristics, and automatically
|
||||
displays the keyboard when necessary.
|
||||
|
||||
On other systems such as Android, Emacs must tell the system when it
|
||||
is ready to accept keyboard input. Typically, this is done in
|
||||
response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or
|
||||
once to the minibuffer becomes in use (@pxref{Minibuffer}.)
|
||||
|
||||
@vindex touch-screen-set-point-commands
|
||||
When a ``tap'' gesture results in a command being executed, Emacs
|
||||
checks to see whether or not the command is supposed to set the point
|
||||
by looking for it in the list @code{touch-screen-set-point-commands}.
|
||||
If it is, then Emacs looks up whether or not the text under the point
|
||||
is read-only; if not, it activates the on-screen keyboard, assuming
|
||||
that the user is about to enter text in to the current buffer.
|
||||
|
||||
@vindex touch-screen-display-keyboard
|
||||
The user option @code{touch-screen-display-keyboard} forces Emacs to
|
||||
always display the on screen keyboard; it may also be bound buffer
|
||||
locally, meaning to always display the keyboard when the buffer is
|
||||
selected.
|
||||
|
||||
Emacs also provides a set of functions to show or hide the on-screen
|
||||
keyboard. For more details, @pxref{On-Screen Keyboards,,, elisp, The
|
||||
Emacs Lisp Reference Manual}.
|
||||
|
||||
@cindex quitting, without a keyboard
|
||||
Since it may not be possible for Emacs to display the on screen
|
||||
keyboard when it is executing a command, Emacs implements a feature on
|
||||
devices with only an on-screen keyboard, by which two rapid clicks of
|
||||
a hardware button that is always present on the device results in
|
||||
Emacs quitting. @xref{Quitting}.
|
||||
|
||||
@vindex x-quit-keysym
|
||||
The exact button is used to do this varies by system: on X, it is
|
||||
defined in the variable @code{x-quit-keysym}, and on Android, it is
|
||||
always the volume down button.
|
||||
|
||||
@cindex text conversion, keyboards
|
||||
Most input methods designed to work with on-screen keyboards perform
|
||||
buffer edits differently from desktop input methods.
|
||||
|
||||
On a conventional desktop windowing system, an input method will
|
||||
simply display the contents of any on going character compositions on
|
||||
screen, and send the appropriate key events to Emacs after completion.
|
||||
|
||||
However, on screen keyboard input methods directly perform edits to
|
||||
the selected window of each frame; this is known as ``text
|
||||
conversion'', or ``string conversion'' under the X Window System.
|
||||
Emacs enables these input methods whenever the buffer local value of
|
||||
@code{text-conversion-style} is non-@code{nil}, normally inside
|
||||
derivatives of @code{text-mode} and @code{prog-mode}.
|
||||
|
||||
Text conversion is performed asynchronously whenever Emacs receives
|
||||
a request to perform the conversion from the input method, and Emacs
|
||||
is not currently reading a key sequence for which one prefix key has
|
||||
already been read (@pxref{Keys}.) After the conversion completes, a
|
||||
@code{text-conversion} event is sent. @xref{Misc Events,,, elisp, the
|
||||
Emacs Reference Manual}.
|
||||
|
||||
@vindex text-conversion-face
|
||||
If the input method needs to work on a region of the buffer, then
|
||||
the region becomes known as the ``composing region'' (or
|
||||
``preconversion region''.) The variable @code{text-conversion-face}
|
||||
describes whether or not to display the composing region in a specific
|
||||
face.
|
||||
|
|
@ -2011,11 +2011,18 @@ the position of the finger when the event occurred.
|
|||
This event is sent when @var{point} is created by the user pressing a
|
||||
finger against the touchscreen.
|
||||
|
||||
These events also have imaginary prefixes keys added by
|
||||
@code{read-key-sequence} when they originate on top of a special part
|
||||
of a frame or window. @xref{Key Sequence Input}. The reason the
|
||||
other touch screen events do not undergo this treatment is that they
|
||||
are rarely useful without being used in tandem from their
|
||||
corresponding @code{touchscreen-begin} events.
|
||||
|
||||
@cindex @code{touchscreen-update} event
|
||||
@item (touchscreen-update @var{points})
|
||||
This event is sent when a point on the touchscreen has changed
|
||||
position. @var{points} is a list of touch points containing the
|
||||
up-to-date positions of each touch point currently on the touchscreen.
|
||||
up-to-date positions of each touch point currently on the touchscxcompile/reen.
|
||||
|
||||
@cindex @code{touchscreen-end} event
|
||||
@item (touchscreen-end @var{point})
|
||||
|
|
@ -2024,6 +2031,45 @@ display, because another program took the grab, or because the user
|
|||
raised the finger from the touchscreen.
|
||||
@end table
|
||||
|
||||
If a touchpoint is pressed against the menu bar, then Emacs will not
|
||||
generate any corresponding @code{touchscreen-begin} or
|
||||
@code{touchscreen-end} events; instead, the menu bar may be displayed
|
||||
when @code{touchscreen-end} should have been delivered.
|
||||
|
||||
@cindex handling touch screen events
|
||||
@cindex tap and drag, touch screen gestures
|
||||
Emacs provides two functions to handle touch screen events. They are
|
||||
intended to be used by a command bound to @code{touchscreen-begin} to
|
||||
handle common gestures.
|
||||
|
||||
@defun touch-screen-track-tap event &optional update data
|
||||
This function is used to track a single ``tap'' gesture originating
|
||||
from the @code{touchscreen-begin} event @var{event}, often used to
|
||||
set the point or to activate a button. It waits for a
|
||||
@code{touchscreen-end} event with the same touch identifier to arrive,
|
||||
at which point it returns @code{t}, signifying the end of the gesture.
|
||||
|
||||
If a @code{touchscreen-update} event arrives in the mean time and
|
||||
contains at least one touchpoint with the same identifier as in
|
||||
@var{event}, the function @var{update} is called with two arguments,
|
||||
the list of touchpoints in that @code{touchscreen-update} event, and
|
||||
@var{data}.
|
||||
|
||||
If any other event arrives in the mean time, @code{nil} is returned.
|
||||
The caller should not perform any action in that case.
|
||||
@end defun
|
||||
|
||||
@defun touch-screen-track-drag event update &optional data
|
||||
This function is used to track a single ``drag'' gesture originating
|
||||
from the @code{touchscreen-begin} event @code{event}.
|
||||
|
||||
It behaves like @code{touch-screen-track-tap}, except that it returns
|
||||
@code{no-drag} and refrains from calling @var{update} if the
|
||||
touchpoint in @code{event} did not move far enough (by default, 5
|
||||
pixels from its position in @code{event}) to qualify as an actual
|
||||
drag.
|
||||
@end defun
|
||||
|
||||
@node Focus Events
|
||||
@subsection Focus Events
|
||||
@cindex focus event
|
||||
|
|
@ -2160,6 +2206,69 @@ the buffer in which the xwidget will be displayed, using
|
|||
A few other event types represent occurrences within the system.
|
||||
|
||||
@table @code
|
||||
@cindex @code{text-conversion} event
|
||||
@item text-conversion
|
||||
This kind of event is sent @strong{after} a system-wide input method
|
||||
performs an edit to one or more buffers.
|
||||
|
||||
@vindex text-conversion-edits
|
||||
Once the event is sent, the input method may already have made
|
||||
changes to multiple buffers inside many different frames. To
|
||||
determine which buffers have been changed, and what edits have
|
||||
been made to them, use the variable
|
||||
@code{text-conversion-edits}, which is set prior to each
|
||||
@code{text-conversion} event being sent; it is a list of the
|
||||
form:
|
||||
|
||||
@example
|
||||
@w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}}
|
||||
@end example
|
||||
|
||||
Where @var{ephemeral} is the buffer which was modified, @var{beg} and
|
||||
@var{end} are markers set to the positions of the edit at the time it
|
||||
was completed, and @var{ephemeral} is either a string, containing any
|
||||
text which was inserted, or any text before point which was deleted,
|
||||
@code{t}, meaning that the edit is a temporary edit made by the input
|
||||
method, and @code{nil}, meaning that some text was deleted after
|
||||
point.
|
||||
|
||||
@vindex text-conversion-style
|
||||
Whether or not this event is sent depends on the value of the
|
||||
buffer-local variable @code{text-conversion-style}, which determines
|
||||
how an input method that wishes to make edits to buffer contents will
|
||||
behave.
|
||||
|
||||
This variable can have one of three values:
|
||||
|
||||
@table @code
|
||||
@item nil
|
||||
This means that the input method will be disabled entirely, and key
|
||||
events will be sent instead of text conversion events.
|
||||
|
||||
@item action
|
||||
This means that the input method will be enabled, but @key{RET} will
|
||||
be sent wherever the input method wanted to insert a new line.
|
||||
|
||||
@item t
|
||||
This, or any other value, means that the input method will be enabled
|
||||
and make edits terminated by @code{text-conversion} events.
|
||||
@end table
|
||||
|
||||
@findex disable-text-conversion
|
||||
Changes to the value of this variable will only take effect upon
|
||||
the next redisplay after the buffer becomes the selected buffer
|
||||
of a frame. If you need to disable text conversion in a way
|
||||
that takes immediate effect, call the function
|
||||
@code{set-text-conversion-style} instead. This can potentially
|
||||
lock up the input method for a significant amount of time, so do
|
||||
not do this lightly!
|
||||
|
||||
@vindex disable-inhibit-text-conversion
|
||||
In addition, text conversion is automatically disabled after a prefix
|
||||
key is read by the command loop, or through @code{read-key-sequence}.
|
||||
This can be disabled by setting or binding the variable
|
||||
@code{disable-inhibit-text-conversion} to a non-@code{nil} value.
|
||||
|
||||
@cindex @code{delete-frame} event
|
||||
@item (delete-frame (@var{frame}))
|
||||
This kind of event indicates that the user gave the window manager
|
||||
|
|
@ -3036,19 +3145,21 @@ with any other events.
|
|||
@cindex @code{right-divider}, prefix key
|
||||
@cindex @code{bottom-divider}, prefix key
|
||||
@cindex mouse events, in special parts of window or frame
|
||||
When mouse events occur in special parts of a window or frame, such as a mode
|
||||
line or a scroll bar, the event type shows nothing special---it is the
|
||||
same symbol that would normally represent that combination of mouse
|
||||
button and modifier keys. The information about the window part is kept
|
||||
elsewhere in the event---in the coordinates. But
|
||||
@code{read-key-sequence} translates this information into imaginary
|
||||
prefix keys, all of which are symbols: @code{tab-line}, @code{header-line},
|
||||
@code{horizontal-scroll-bar}, @code{menu-bar}, @code{tab-bar}, @code{mode-line},
|
||||
@cindex touch screen events, in special parts of window or frame
|
||||
When mouse or @code{touch-screen-begin} events occur in special parts
|
||||
of a window or frame, such as a mode line or a scroll bar, the event
|
||||
type shows nothing special---it is the same symbol that would normally
|
||||
represent that combination of mouse button and modifier keys. The
|
||||
information about the window part is kept elsewhere in the event---in
|
||||
the coordinates. But @code{read-key-sequence} translates this
|
||||
information into imaginary prefix keys, all of which are symbols:
|
||||
@code{tab-line}, @code{header-line}, @code{horizontal-scroll-bar},
|
||||
@code{menu-bar}, @code{tab-bar}, @code{mode-line},
|
||||
@code{vertical-line}, @code{vertical-scroll-bar}, @code{left-margin},
|
||||
@code{right-margin}, @code{left-fringe}, @code{right-fringe},
|
||||
@code{right-divider}, and @code{bottom-divider}. You can define meanings for
|
||||
mouse clicks in special window parts by defining key sequences using these
|
||||
imaginary prefix keys.
|
||||
@code{right-divider}, and @code{bottom-divider}. You can define
|
||||
meanings for mouse clicks in special window parts by defining key
|
||||
sequences using these imaginary prefix keys.
|
||||
|
||||
For example, if you call @code{read-key-sequence} and then click the
|
||||
mouse on the window's mode line, you get two events, like this:
|
||||
|
|
|
|||
|
|
@ -2933,8 +2933,9 @@ apply to. Here are the possible values of @var{characteristic}:
|
|||
The kind of window system the terminal uses---either @code{graphic}
|
||||
(any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
|
||||
console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
|
||||
Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable
|
||||
display). @xref{Window Systems, window-system}.
|
||||
Haiku), @code{pgtk} (for pure GTK), @code{android} (for Android), or
|
||||
@code{tty} (a non-graphics-capable display). @xref{Window Systems,
|
||||
window-system}.
|
||||
|
||||
@item class
|
||||
What kinds of colors the terminal supports---either @code{color},
|
||||
|
|
@ -8857,6 +8858,8 @@ Emacs is displaying the frame using MS-DOS direct screen writes.
|
|||
Emacs is displaying the frame using the Application Kit on Haiku.
|
||||
@item pgtk
|
||||
Emacs is displaying the frame using pure GTK facilities.
|
||||
@item android
|
||||
Emacs is displaying the frame on Android.
|
||||
@item nil
|
||||
Emacs is displaying the frame on a character-based terminal.
|
||||
@end table
|
||||
|
|
|
|||
|
|
@ -1139,6 +1139,7 @@ Frames
|
|||
* Dialog Boxes:: Displaying a box to ask yes or no.
|
||||
* Pointer Shape:: Specifying the shape of the mouse pointer.
|
||||
* Window System Selections::Transferring text to and from other X clients.
|
||||
* Accessing Selections:: The multiple different kinds of selections.
|
||||
* Yanking Media:: Yanking things that aren't plain text.
|
||||
* Drag and Drop:: Internals of Drag-and-Drop implementation.
|
||||
* Color Names:: Getting the definitions of color names.
|
||||
|
|
|
|||
|
|
@ -104,9 +104,11 @@ window of another Emacs frame. @xref{Child Frames}.
|
|||
* Mouse Tracking:: Getting events that say when the mouse moves.
|
||||
* Mouse Position:: Asking where the mouse is, or moving it.
|
||||
* Pop-Up Menus:: Displaying a menu for the user to select from.
|
||||
* On-Screen Keyboards:: Displaying the virtual keyboard.
|
||||
* Dialog Boxes:: Displaying a box to ask yes or no.
|
||||
* Pointer Shape:: Specifying the shape of the mouse pointer.
|
||||
* Window System Selections:: Transferring text to and from other X clients.
|
||||
* Accessing Selections:: The multiple different kinds of selections.
|
||||
* Yanking Media:: Yanking things that aren't plain text.
|
||||
* Drag and Drop:: Internals of Drag-and-Drop implementation.
|
||||
* Color Names:: Getting the definitions of color names.
|
||||
|
|
@ -695,7 +697,7 @@ The position of the top left corner of the native frame specifies the
|
|||
indicate that position for the various builds:
|
||||
|
||||
@itemize @w{}
|
||||
@item (1) non-toolkit, Haiku, and terminal frames
|
||||
@item (1) non-toolkit, Android, Haiku, and terminal frames
|
||||
|
||||
@item (2) Lucid, Motif, and MS-Windows frames
|
||||
|
||||
|
|
@ -2394,6 +2396,7 @@ engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with
|
|||
HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs
|
||||
Manual}). The @code{harfbuzz} driver is similarly recommended. On
|
||||
Haiku, there can be several font drivers (@pxref{Haiku Fonts,,, emacs,
|
||||
The GNU Emacs Manual}), as on Android (@pxref{Android Fonts,,, emacs,
|
||||
The GNU Emacs Manual}).
|
||||
|
||||
On other systems, there is only one available font backend, so it does
|
||||
|
|
@ -3739,9 +3742,9 @@ This function displays a pop-up menu and returns an indication of
|
|||
what selection the user makes.
|
||||
|
||||
The argument @var{position} specifies where on the screen to put the
|
||||
top left corner of the menu. It can be either a mouse button event
|
||||
(which says to put the menu where the user actuated the button) or a
|
||||
list of this form:
|
||||
top left corner of the menu. It can be either a mouse button or
|
||||
@code{touchscreen-begin} event (which says to put the menu where the
|
||||
user actuated the button) or a list of this form:
|
||||
|
||||
@example
|
||||
((@var{xoffset} @var{yoffset}) @var{window})
|
||||
|
|
@ -3826,6 +3829,30 @@ keymap. It won't be called if @code{x-popup-menu} returns for some
|
|||
other reason without displaying a pop-up menu.
|
||||
@end defvar
|
||||
|
||||
@node On-Screen Keyboards
|
||||
@section On-Screen Keyboards
|
||||
|
||||
An on-screen keyboard is a special kind of pop up provided by the
|
||||
system, with rows of clickable buttons that act as a real keyboard.
|
||||
|
||||
On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs
|
||||
Manual}), Emacs is supposed to display and hide the on screen keyboard
|
||||
depending on whether or not the user is about to type something.
|
||||
|
||||
@defun frame-toggle-on-screen-keyboard frame hide
|
||||
This function displays or hides the on-screen keyboard on behalf of
|
||||
the frame @var{frame}. If @var{hide} is non-@code{nil}, then the
|
||||
on-screen keyboard is hidden; otherwise, it is displayed.
|
||||
|
||||
It returns whether or not the on screen keyboard @strong{may} have
|
||||
been displayed, which should be used to determine whether or not to
|
||||
hide the on-screen keyboard later.
|
||||
|
||||
This has no effect if the system automatically detects when to display
|
||||
the on-screen keyboard, or when it does not provide any on-screen
|
||||
keyboard.
|
||||
@end defun
|
||||
|
||||
@node Dialog Boxes
|
||||
@section Dialog Boxes
|
||||
@cindex dialog boxes
|
||||
|
|
@ -4033,6 +4060,542 @@ For backward compatibility, there are obsolete aliases
|
|||
names of @code{gui-get-selection} and @code{gui-set-selection} before
|
||||
Emacs 25.1.
|
||||
|
||||
@node Accessing Selections
|
||||
@section Accessing Selections
|
||||
|
||||
@code{gui-get-selection} is able to retrieve multiple different
|
||||
kinds of selection data from any number of selections. However, the
|
||||
data types and selections that Emacs understands is not precisely
|
||||
specified and differs depending on the window system on which Emacs is
|
||||
running.
|
||||
|
||||
At the same time, @code{gui-set-selection} hides a great deal of
|
||||
complexity behind its back, at least on some systems: its @var{data}
|
||||
argument need not be a string, but is actually given verbatim to
|
||||
system specific code.
|
||||
|
||||
Emacs's implementation of selections is most complete on the X
|
||||
Window System. This is both an artifact of history (X was the first
|
||||
window system supported by Emacs) and one of technical reasons:
|
||||
instead of using selections only to transfer text and multimedia
|
||||
content between clients, X uses selections as a general inter-client
|
||||
communication system, leading to a great proliferation of selection
|
||||
data types.
|
||||
|
||||
Even more confusingly, X also supports another inter-client
|
||||
communication mechanism: the Inter-Client Exchange. However, ICE is
|
||||
only used by Emacs to communicate with session managers, and is a
|
||||
separate topic.
|
||||
|
||||
@menu
|
||||
* X Selections:: Selection data types (and more) on X.
|
||||
* Other Selections:: How they work on other window systems.
|
||||
@end menu
|
||||
|
||||
@node X Selections
|
||||
@subsection X Selections
|
||||
|
||||
X refrains from defining fixed data types for selection data, or a
|
||||
fixed number of selections. Selections are instead identified by X
|
||||
``atoms'', which are unique 29-bit identifiers issued by the X server
|
||||
for a corresponding name. In Emacs, you can simply write a symbol
|
||||
with the name of the atom, and Emacs will transparently request these
|
||||
identifiers where necessary.
|
||||
|
||||
When a program ``sets'' a selection under X, it actually makes
|
||||
itself the ``owner'' of the selection---the X server will then deliver
|
||||
selection requests to the program, which is obliged to respond to the
|
||||
requesting client with the selection data.
|
||||
|
||||
Similarly, a program does not ``get'' selection data from the X
|
||||
server. Instead, its selection requests are sent to the client with
|
||||
the window which last took ownership over the selection, which then
|
||||
replies with the requested data.
|
||||
|
||||
Each selection request contains three parameters:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The window which requested the selection; this is used to identify the
|
||||
@c Not a typo: X spells ``requestor'' with an o.
|
||||
requesting program, otherwise known as the @dfn{requestor}.
|
||||
|
||||
@item
|
||||
An atom identifying the ``target'' to which the owner should convert
|
||||
the selection. It is easiest to think of the conversion target as the
|
||||
kind of data that the requestor wants: in selection requests made by
|
||||
Emacs, the target is determined by the @dfn{type} argument to
|
||||
@code{gui-get-selection}.
|
||||
|
||||
@item
|
||||
A 32-bit timestamp containing the X server time at which the requestor
|
||||
last obtained input.
|
||||
@end itemize
|
||||
|
||||
The selection owner responds by tranferring to the requestor a
|
||||
series of bytes, 16 bit words, or 32 bit words, along with another
|
||||
atom identifying the type of those words. After requesting a
|
||||
selection, Emacs then applies its own interpretation of the data
|
||||
format and data type to convert the data transferred by the selection
|
||||
owner to a Lisp representation, which @code{gui-get-selection}
|
||||
returns.
|
||||
|
||||
By default, Emacs converts selection data consisting of any series
|
||||
of bytes to a unibyte string containing those bytes, selection data
|
||||
consisting of a single 16-bit or 32-bit word as an unsigned number,
|
||||
and selection data consisting of multiple such words as a vector of
|
||||
unsigned numbers. However, Emacs applies special treatment for
|
||||
several selection data types:
|
||||
|
||||
@table @code
|
||||
@item INTEGER
|
||||
16-bit or 32-bit words of this type are treated as signed integers,
|
||||
instead of unsigned ones. If there are multiple words in the
|
||||
selection data, a vector is returned; otherwise, the integer is
|
||||
returned by itself.
|
||||
|
||||
@item ATOM
|
||||
32-bit words of this type are treated as X atoms, and returned (either
|
||||
alone or as vectors) as Lisp symbols containing the names they
|
||||
identify. Invalid atoms are returned as @code{nil}.
|
||||
|
||||
@item COMPOUND_TEXT
|
||||
@item UTF8_STRING
|
||||
@item STRING
|
||||
Unibyte strings returned for these data types will have a single
|
||||
@code{foreign-selection} text property set to a symbol with the type
|
||||
of the selection data.
|
||||
@end table
|
||||
|
||||
Each selection owner must return at least two selection targets:
|
||||
@code{TARGETS}, which returns a number of atoms describing the
|
||||
selection targets that the owner supports, and @code{MULTIPLE}, used
|
||||
for internal purposes by X clients. A selection owner may support any
|
||||
number of other targets, some of which may be standardized by the X
|
||||
Consortium's
|
||||
@url{http://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html,
|
||||
Inter-Client Communication Conventions Manual}, while others, such as
|
||||
@code{UTF8_STRING}, were supposed to be standardized by the XFree86
|
||||
Project, which unfortunately did not happen.
|
||||
|
||||
Requests for a given selection target may, by convention, return
|
||||
data in a specific type, or it may return data in one of several
|
||||
types, whichever is most convenient for the selection owner; the
|
||||
latter type of selection target is dubbed a @dfn{polymorphic target}.
|
||||
A selection target may also return no data at all: by convention, the
|
||||
selection owner performs some action a side effect upon responding to
|
||||
a selection request with that target, and as such these targets are
|
||||
referred to as @dfn{side-effect targets}.
|
||||
|
||||
Here are some selection targets which behave in a reasonably
|
||||
standard manner when used with the @code{CLIPBOARD}, @code{PRIMARY},
|
||||
or @code{SECONDARY} selections.
|
||||
|
||||
@table @code
|
||||
@item ADOBE_PORTABLE_DOCUMENT_FORMAT
|
||||
This target returns data in Adobe System's ``Portable Document
|
||||
Format'' format, as a string.
|
||||
|
||||
@item APPLE_PICT
|
||||
This target returns data in the ``PICT'' image format used on
|
||||
Macintosh computers, as a string.
|
||||
|
||||
@item BACKGROUND
|
||||
@item BITMAP
|
||||
@item COLORMAP
|
||||
@item FOREGROUND
|
||||
Together, these four targets return integer data necessary to make use
|
||||
of a bitmap image stored on the X server: the pixel value of the
|
||||
bitmap's background color, the X identifier of the bitmap, the
|
||||
colormap inside which the background and foreground are allocated, and
|
||||
the pixel value of the bitmap's foreground color.
|
||||
|
||||
@item CHARACTER_POSITION
|
||||
This target returns two unsigned 32-bit integers of type @code{SPAN}
|
||||
describing the start and end positions of the selection data in the
|
||||
text field containing it, in bytes.
|
||||
|
||||
@item COMPOUND_TEXT
|
||||
This target returns a string of type @code{COMPOUND_TEXT} in the X
|
||||
Consortium's multi-byte text encoding system.
|
||||
|
||||
@item DELETE
|
||||
This target returns nothing, but as a side-effect deletes the
|
||||
selection contents from any text field containing them.
|
||||
|
||||
@item DRAWABLE
|
||||
@item PIXMAP
|
||||
This target returns a list of unsigned 32-bit integers, each of which
|
||||
corresponds to an X server drawable or pixmap.
|
||||
|
||||
@item ENCAPSULATED_POSTSCRIPT
|
||||
@item _ADOBE_EPS
|
||||
This target returns a string containing encapsulated Postscript code.
|
||||
|
||||
@item FILE_NAME
|
||||
This target returns a string containing one or more file names,
|
||||
separated by NULL characters.
|
||||
|
||||
@item HOST_NAME
|
||||
This target returns a string containing the fully-qualified domain
|
||||
name of the machine on which the selection owner is running.
|
||||
|
||||
@item USER
|
||||
This target returns a string containing the user name of the machine
|
||||
on which the selection owner is running.
|
||||
|
||||
@item LENGTH
|
||||
This target returns an unsigned 32-bit or 16-bit integer containing
|
||||
the length of the selection data.
|
||||
|
||||
@item LINE_NUMBER
|
||||
This target returns two unsigned 32-bit integers of type @code{SPAN}
|
||||
describing the line numbers corresponding to the start and end
|
||||
positions of the selection data in the text field containing it.
|
||||
|
||||
@item MODULE
|
||||
This target returns the name of any function containing the selection
|
||||
data. It is mainly used by text editors.
|
||||
|
||||
@item STRING
|
||||
This target returns the selection data as a string of type
|
||||
@code{STRING}, encoded in ISO Latin-1 format, with Unix newline
|
||||
characters.
|
||||
|
||||
@item C_STRING
|
||||
This target returns the selection data as a ``C string''. This has
|
||||
been interpreted as meaning the raw selection data in whatever
|
||||
encoding used by the owner, either terminated with a NULL byte or not
|
||||
at all, or an ASCII string which may or may not be terminated.
|
||||
|
||||
@item UTF8_STRING
|
||||
This returns the selection data as a string of type
|
||||
@code{UTF8_STRING}, encoded in UTF-8, with unspecified EOL format.
|
||||
|
||||
@item TIMESTAMP
|
||||
This target returns the X server time at which the selection owner
|
||||
took ownership over the selection as a 16-bit or 32-bit word of type
|
||||
@code{CARDINAL}.
|
||||
|
||||
@item TEXT
|
||||
This polymorphic target returns selection data as a string, either
|
||||
@code{COMPOUND_TEXT}, @code{STRING}, @code{C_STRING}, or
|
||||
@code{UTF8_STRING}, whichever data type is convenient for the
|
||||
selection owner.
|
||||
@end table
|
||||
|
||||
When a request for the targets @code{STRING}, @code{COMPOUND_TEXT},
|
||||
or @code{UTF8_STRING} is made using the function
|
||||
@code{gui-get-selection}, and neither @code{selection-coding-system}
|
||||
nor @code{next-selection-coding-system} are set, the returned strings
|
||||
are additionally decoded using the appropriate coding system for those
|
||||
data types: @code{iso-8859-1}, @code{compound-text-with-extensions}
|
||||
and @code{utf-8} respectively.
|
||||
|
||||
In addition to the targets specified above (and the many targets
|
||||
used by various programs for their own purposes), several popular
|
||||
programs and toolkits have decided to define selection data types of
|
||||
their own, without consulting the appropriate X standards bodies.
|
||||
These targets are usually named after MIME types, such as
|
||||
@code{text/html} or @code{image/jpeg}, and have been known to contain:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Unterminated, newline terminated, or NULL character terminated file
|
||||
names of an image or text file.
|
||||
|
||||
@item
|
||||
Image or text data in the appropriate format.
|
||||
|
||||
@item
|
||||
@code{file://} URIs (or possibly newline or NUL terminated lists of
|
||||
URIs) leading to files in the appropriate format.
|
||||
@end itemize
|
||||
|
||||
These selection targets were first used by Netscape, but are now
|
||||
found in all kinds of programs, especially those based on recent
|
||||
versions of the GTK+ or Qt toolkits.
|
||||
|
||||
Emacs is also capable of acting as a selection owner. When
|
||||
@code{gui-set-selection} is called, the selection data specified is
|
||||
not transferred to the X server; instead, Emacs records it internally
|
||||
and obtains ownership of the selection.
|
||||
|
||||
@defvar selection-converter-alist
|
||||
Alist of selection targets to ``selection converter'' functions.
|
||||
When a selection request is received, Emacs looks up the selection
|
||||
converter associated with the requested selection target.
|
||||
|
||||
The selection converter is called with three arguments: the symbol
|
||||
corresponding to the atom identifying the selection being requested,
|
||||
the selection target that is being requested, and the value set with
|
||||
@code{gui-set-selection}. The value which it returns is either a cons
|
||||
of a symbol specifying the data type and a number, symbol, or a vector
|
||||
of numbers or symbols, or its cdr by itself.
|
||||
|
||||
If the value is the special symbol @code{NULL}, the data type is set
|
||||
to @code{NULL}, and no data is returned to the requestor.
|
||||
|
||||
If the value is a string, it must be a unibyte string; should no
|
||||
data type be explicitly specified, the data is transferred to the
|
||||
requestor with the type @code{STRING}.
|
||||
|
||||
If the value is a symbol, its ``atom'' is retrieved, and it is
|
||||
transferred to the requestor as a 32-bit value---if no data type was
|
||||
specified, its type is @code{ATOM}.
|
||||
|
||||
If the value is a number between @code{-32769} and @code{32768}, it
|
||||
is transferred to the requestor as a 16 bit value---if no data type
|
||||
was specified, its type is @code{INTEGER}.
|
||||
|
||||
If the value is any other number, it is returned as a 32 bit value.
|
||||
Even if the number returned is unsigned, the requestor will treat
|
||||
words of type @code{INTEGER} as signed. To return an unsigned value,
|
||||
specify the type @code{CARDINAL} instead.
|
||||
|
||||
If the value is a vector of symbols or numbers, it is returned as a
|
||||
list of multiple atoms or numbers. The data type returned by default
|
||||
is determined by that of its first element.
|
||||
@end defvar
|
||||
|
||||
By default, Emacs is configured with selection converters for the
|
||||
following selection targets:
|
||||
|
||||
@table @code
|
||||
@item TEXT
|
||||
This selection converter returns selection data as:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
A string of type @code{C_STRING}, if the selection contents contain no
|
||||
multibyte characters, or contains 8-bit characters with all 8 bits
|
||||
set.
|
||||
|
||||
@item
|
||||
A string of type @code{STRING}, if the selection contents can be
|
||||
represented as ISO-Latin-1 text.
|
||||
|
||||
@item
|
||||
A string of type @code{COMPOUND_TEXT}, if the selection contents can
|
||||
be encoded in the X Consortium's Compound Text Encoding, and
|
||||
@code{selection-coding-system} or @code{next-selection-coding-system}
|
||||
is set to a coding system whose @code{:mime-charset} property is
|
||||
@code{x-ctext}.
|
||||
|
||||
@item
|
||||
A string of type @code{UTF8_STRING} otherwise.
|
||||
@end itemize
|
||||
|
||||
@item COMPOUND_TEXT
|
||||
This selection converter returns selection data as a string of type
|
||||
@code{COMPOUND_TEXT}.
|
||||
|
||||
@item STRING
|
||||
This selection converter returns selection data as a string of type
|
||||
@code{STRING}, encoded in ISO-Latin-1 format.
|
||||
|
||||
@item UTF8_STRING
|
||||
This selection converter returns selection data in UTF-8 format.
|
||||
|
||||
@item text/plain
|
||||
@item text/plain;charset=utf-8
|
||||
@item text/uri-list
|
||||
@item text/x-xdnd-username
|
||||
@item XmTRANSFER_SUCCESS
|
||||
@item XmTRANSFER_FAILURE
|
||||
@item FILE
|
||||
@item _DT_NETFILE
|
||||
These selection converters are used for internal purposes during
|
||||
drag-and-drop operations and are not available for selections other
|
||||
than @code{XdndSelection}.
|
||||
|
||||
@item TARGETS
|
||||
This selection converter returns a list of atoms, one for each
|
||||
selection target understood by Emacs.
|
||||
|
||||
@item MULTIPLE
|
||||
This selection converter is implemented in C code and is used to
|
||||
implement efficient transfer of selection requests which specify
|
||||
multiple selection targets at the same time.
|
||||
|
||||
@item LENGTH
|
||||
This selection converter returns the length of the selection data, in
|
||||
bytes.
|
||||
|
||||
@item DELETE
|
||||
This selection converter is used for internal purposes during
|
||||
drag-and-drop operations.
|
||||
|
||||
@item FILE_NAME
|
||||
This selection converter returns the file name of the buffer
|
||||
containing the selection data.
|
||||
|
||||
@item CHARACTER_POSITION
|
||||
This selection converter returns the character positions of each end
|
||||
of the selection in the buffer containing the selection data.
|
||||
|
||||
@item LINE_NUMBER
|
||||
@item COLUMN_NUMBER
|
||||
This selection converter returns the line and column numbers of each
|
||||
end of the selection in the buffer containing the selection data.
|
||||
|
||||
@item OWNER_OS
|
||||
This selection converter returns the name of the operating system on
|
||||
which Emacs is running.
|
||||
|
||||
@item HOST_NAME
|
||||
This selection converter returns the fully-qualified domain name of
|
||||
the machine on which Emacs is running.
|
||||
|
||||
@item USER
|
||||
This selection converter returns the username of the user account
|
||||
under which Emacs is running.
|
||||
|
||||
@item CLASS
|
||||
@item NAME
|
||||
These selection converters return the resource class and name used by
|
||||
Emacs.
|
||||
|
||||
@item INTEGER
|
||||
This selection converter returns an integer value verbatim.
|
||||
|
||||
@item SAVE_TARGETS
|
||||
@item _EMACS_INTERNAL
|
||||
These selection converters are used for internal purposes.
|
||||
@end table
|
||||
|
||||
With the exception of @code{INTEGER}, all selection converters
|
||||
expect the value given to @code{gui-set-selection} to be one of the
|
||||
following:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
A string.
|
||||
|
||||
@item
|
||||
A list of the form @w{@code{(@var{beg} @var{end} @var{buf})}}, where
|
||||
@var{beg} and @var{end} are two markers or overlays describing the
|
||||
bounds of the selection data in the buffer @var{buf}.
|
||||
@end itemize
|
||||
|
||||
@node Other Selections
|
||||
@subsection Other Selections
|
||||
|
||||
Window systems such as MS-Windows, Nextstep, Haiku and Android do
|
||||
not provide selections corresponding to the X semantics. Each window
|
||||
system provides its own ad-hoc emulation of selections, none of which
|
||||
make use of the ``selection converter'' mechanism described above. In
|
||||
addition, only the @code{PRIMARY}, @code{CLIPBOARD}, and
|
||||
@code{SECONDARY} selections are typically supported, alongside the
|
||||
@code{XdndSelection} used for drag-and-drop operations.
|
||||
|
||||
GTK itself exposes emulations of X selections to applications, but
|
||||
those emulations are of varying completeness. While Emacs built with
|
||||
PGTK will use the same selection interface as Emacs built with X, many
|
||||
selection targets will not be useful.
|
||||
|
||||
On MS-Windows, @code{gui-get-selection} accepts a single target,
|
||||
@code{STRING}. The value returned is the selection data decoded
|
||||
using @code{selection-coding-system}.
|
||||
|
||||
@code{gui-set-selection} also only accepts strings, encodes them
|
||||
in the selection coding system, and saves them to the clipboard.
|
||||
|
||||
On Nextstep, Emacs only supports saving strings to selections.
|
||||
However, requests for the following targets are accepted:
|
||||
|
||||
@c FIXME: how is the text coding system determined, and do image/* or
|
||||
@c application/* return image data or file names?
|
||||
@itemize @bullet
|
||||
@item text/plain
|
||||
@item image/png
|
||||
@item text/html
|
||||
@item application/pdf
|
||||
@item application/rtf
|
||||
@item application/rtfd
|
||||
@item STRING
|
||||
@item text/plain
|
||||
@item image/tiff
|
||||
@end itemize
|
||||
|
||||
On Haiku, Emacs supports the same selection values as on X. In
|
||||
addition, Emacs fully implements the primary and secondary selections.
|
||||
However, instead of taking ownership over the selection data, Emacs
|
||||
transfers the selection data to the window server when
|
||||
@code{gui-set-selection} is called. The Haiku window server expects
|
||||
selection data to be provided in the form of a ``message'', containing
|
||||
associations between data types and selection data.
|
||||
|
||||
@defvar haiku-normal-selection-encoders
|
||||
List of functions which act as selection encoders. When
|
||||
@code{gui-set-selection} is called, each function in this list is
|
||||
successively called with its @var{selection} and @var{value}
|
||||
arguments. If the function returns non-@code{nil}, it should return a
|
||||
list of the form @w{@code{(@var{key} @var{type} @var{value})}}, where
|
||||
@var{key} is the name of the data type being transferred, @var{type}
|
||||
is either a number identifying a data type (in which case @var{value}
|
||||
should be a unibyte string that is directly transferred to the window
|
||||
server), or a symbol identifying both a data type and how @var{value}
|
||||
should be interpreted.
|
||||
@end defvar
|
||||
|
||||
Here are the meaningful values of @var{type}, and what they will
|
||||
cause Emacs to interpret @var{value} as:
|
||||
|
||||
@table @code
|
||||
@item string
|
||||
A unibyte string. The string is NULL-terminated after being placed in
|
||||
the message.
|
||||
|
||||
@item ref
|
||||
A file name. The file is looked up and file system information
|
||||
identifying the file is placed in the message.
|
||||
|
||||
@item short
|
||||
A 16-bit integer value.
|
||||
|
||||
@item long
|
||||
A 32-bit integer value.
|
||||
|
||||
@item llong
|
||||
A 64-bit integer value.
|
||||
|
||||
@item byte
|
||||
@item char
|
||||
An unsigned byte between 0 and 255.
|
||||
|
||||
@item size_t
|
||||
A number between 0 and 1 minus two to the power of the word size of
|
||||
the computer Emacs is running on.
|
||||
|
||||
@item ssize_t
|
||||
A number which fits in the C type @code{ssize_t}.
|
||||
|
||||
@item point
|
||||
A cons of two floats, specifying a coordinate on-screen.
|
||||
|
||||
@item float
|
||||
@item double
|
||||
A single or double-precision floating point number in an unspecified
|
||||
format.
|
||||
|
||||
@item (haiku-numeric-enum MIME)
|
||||
A unibyte string containing data in a certain MIME type.
|
||||
@end table
|
||||
|
||||
Under Haiku, @code{gui-get-selection} accepts either the targets
|
||||
@code{TARGETS} and @code{TIMESTAMP}, where the former returns a vector
|
||||
containing supported data types (much like on X), and the latter
|
||||
returns the number of times the selection has been set, the targets
|
||||
@code{STRING} and @code{UTF8_STRING}, which return text in ISO-Latin-1
|
||||
and UTF-8 format, or a MIME type, in which the data is returned
|
||||
undecoded as a unibyte string.
|
||||
|
||||
Under Android, @code{gui-get-selection} is restricted to returning
|
||||
UTF-8 string data of the type @code{STRING}, or image and application
|
||||
data associated with a MIME type. @code{gui-set-selection} will only
|
||||
set string data, as on MS-Windows.
|
||||
|
||||
@node Yanking Media
|
||||
@section Yanking Media
|
||||
|
||||
|
|
|
|||
|
|
@ -972,6 +972,9 @@ Hewlett-Packard HPUX operating system.
|
|||
@item nacl
|
||||
Google Native Client (@acronym{NaCl}) sandboxing system.
|
||||
|
||||
@item android
|
||||
The Open Handset Alliance's Android operating system.
|
||||
|
||||
@item ms-dos
|
||||
Microsoft's DOS@. Emacs compiled with DJGPP for MS-DOS binds
|
||||
@code{system-type} to @code{ms-dos} even when you run it on MS-Windows.
|
||||
|
|
|
|||
|
|
@ -185,6 +185,24 @@ respective remote host. In case of a local @code{default-directory},
|
|||
the function returns just the value of the variable @code{exec-path}.
|
||||
@end defun
|
||||
|
||||
@cindex programs distributed with Emacs, starting
|
||||
@vindex ctags-program-name
|
||||
@vindex etags-program-name
|
||||
@vindex hexl-program-name
|
||||
@vindex emacsclient-program-name
|
||||
@vindex movemail-program-name
|
||||
@vindex ebrowse-program-manem
|
||||
When starting a program that is part of the Emacs distribution,
|
||||
you must take into account that the program may have been renamed in
|
||||
order to comply with executable naming restrictions present on the
|
||||
system.
|
||||
|
||||
Instead of starting @command{ctags}, for example, you should specify
|
||||
the value of @code{ctags-program-name} instead. Likewise, instead of
|
||||
starting @command{movemail}, you must start
|
||||
@code{movemail-program-name}, and the same goes for @command{etags},
|
||||
@command{hexl}, @command{emacsclient}, and @command{ebrowse}.
|
||||
|
||||
@node Shell Arguments
|
||||
@section Shell Arguments
|
||||
@cindex arguments for shell commands
|
||||
|
|
|
|||
33
etc/DEBUG
33
etc/DEBUG
|
|
@ -1099,6 +1099,39 @@ Please refer to the LLDB reference on the web for more information
|
|||
about LLDB. If you already know GDB, you will also find a mapping
|
||||
from GDB commands to corresponding LLDB commands there.
|
||||
|
||||
** Debugging Emacs on Android.
|
||||
|
||||
Attaching GDB to Emacs running inside the Android application setup
|
||||
requires a special script found in the java/ directory, and a suitable
|
||||
GDB server binary to be present on the Android device, which is
|
||||
present on the free versions of Android. Connecting to the device
|
||||
also requires the `adb' (Android Debug Bridge) utility, and telling
|
||||
the Android system to resume the Emacs process after startup requires
|
||||
the Java debugger (jdb).
|
||||
|
||||
If all three of those tools are present, simply run (from the Emacs
|
||||
source directory):
|
||||
|
||||
../java/debug.sh -- [any extra arguments you wish to pass to gdb]
|
||||
|
||||
After which, upon waiting a while, the GDB prompt will show up.
|
||||
|
||||
If Emacs crashes and "JNI ERROR" shows up in the Android system log,
|
||||
then placing a breakpoint on:
|
||||
|
||||
break art::JavaVMExt::JniAbort
|
||||
|
||||
will let you find the source of the crash.
|
||||
|
||||
If there is no `gdbserver' binary present on the device, then you can
|
||||
specify one to upload, like so:
|
||||
|
||||
../java/debug.sh --gdbserver /path/to/gdbserver
|
||||
|
||||
In addition, when Emacs runs as a 64-bit process on a system
|
||||
supporting both 64 and 32-bit binaries, you must specify the path to a
|
||||
64-bit gdbserver binary.
|
||||
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
|
|
|
|||
12
etc/MACHINES
12
etc/MACHINES
|
|
@ -131,6 +131,18 @@ the list at the end of this file.
|
|||
The earliest release of Haiku that will successfully compile Emacs
|
||||
is R1/Beta2. For windowing support, R1/Beta3 or later is required.
|
||||
|
||||
** Android
|
||||
|
||||
Emacs is known to run on all Android versions from 2.2 onwards, on
|
||||
Linux kernel 2.26.29 or later.
|
||||
|
||||
Android 2.2 has only been tested on ARM. mips64 has not been
|
||||
tested, but builds. With these exceptions, Emacs is known to run on
|
||||
all supported versions of Android on all supported machines: arm,
|
||||
armv7, arm64, x86, x86_64, and mips.
|
||||
|
||||
See the file INSTALL.android for detailed installation instructions.
|
||||
|
||||
|
||||
* Obsolete platforms
|
||||
|
||||
|
|
|
|||
37
etc/NEWS
37
etc/NEWS
|
|
@ -24,6 +24,12 @@ applies, and please also update docstrings as needed.
|
|||
|
||||
* Installation Changes in Emacs 30.1
|
||||
|
||||
** Emacs has been ported to the Android operating system.
|
||||
This requires Emacs to be compiled on another computer. The Android
|
||||
NDK, SDK, and a suitable Java compiler must also be installed.
|
||||
|
||||
See the file 'java/INSTALL' for more details.
|
||||
|
||||
|
||||
* Startup Changes in Emacs 30.1
|
||||
|
||||
|
|
@ -46,6 +52,12 @@ compositing manager, Emacs will now redisplay such a frame even though
|
|||
'frame-visible-p' returns nil or 'icon' for it. This can happen, for
|
||||
example, as part of preview for iconified frames.
|
||||
|
||||
---
|
||||
** New user option 'menu-bar-close-window'.
|
||||
When non-nil, selecting Close from the File menu or clicking Close in
|
||||
the tool bar will result in the current window being closed, if
|
||||
possible.
|
||||
|
||||
+++
|
||||
** 'write-region-inhibit-fsync' now defaults to t in interactive mode,
|
||||
as it has in batch mode since Emacs 24.
|
||||
|
|
@ -79,6 +91,12 @@ plus, minus, check-mark, start, etc.
|
|||
|
||||
* Editing Changes in Emacs 30.1
|
||||
|
||||
+++
|
||||
** Emacs now has better support for touchscreen events.
|
||||
Many touch screen gestures are now implemented, as is support for
|
||||
tapping buttons and opening menus.
|
||||
|
||||
|
||||
---
|
||||
** On X, Emacs now supports input methods which perform "string conversion".
|
||||
This means an input method can now ask Emacs to delete text
|
||||
|
|
@ -380,6 +398,25 @@ hooks named after the feature name, like 'esh-mode-unload-hook'.
|
|||
|
||||
* Lisp Changes in Emacs 30.1
|
||||
|
||||
+++
|
||||
** New variables describing the names of built in programs.
|
||||
The new variables 'ctags-program-name', 'ebrowse-program-name',
|
||||
'etags-program-name', 'hexl-program-name', 'emacsclient-program-name'
|
||||
and 'movemail-program-name' should be used instead of "ctags",
|
||||
"ebrowse", "etags", "hexl", and "emacsclient", when starting one of
|
||||
these built in programs in a subprocess.
|
||||
|
||||
+++
|
||||
** 'x-popup-menu' now understands touch screen events.
|
||||
When a 'touchscreen-begin' or 'touchscreen-end' event is passed as the
|
||||
POSITION argument, it will behave as if that event was a mouse event.
|
||||
|
||||
+++
|
||||
** New functions for handling touch screen events.
|
||||
The new functions 'touch-screen-track-tap' and
|
||||
'touch-screen-track-drag' handle tracking common touch screen gestures
|
||||
from within a command.
|
||||
|
||||
** New variable 'inhibit-auto-fill' to temporarily prevent auto-fill.
|
||||
|
||||
** Functions and variables to transpose sexps
|
||||
|
|
|
|||
50
etc/PROBLEMS
50
etc/PROBLEMS
|
|
@ -3301,6 +3301,56 @@ Compose key to stop working.
|
|||
On X Windows, users should not use Emacs configured with PGTK, since
|
||||
this and many other problems do not exist on the regular X builds.
|
||||
|
||||
* Runtime problems specific to Android
|
||||
|
||||
** Text displayed in the default monospace font looks horrible.
|
||||
|
||||
Droid Sans Mono (the default Monospace font which comes with Android)
|
||||
comes with instruction code designed for Microsoft's proprietary
|
||||
TrueType font scaler. When this code is executed by Emacs to instruct
|
||||
a glyph containing more than one component, it tries to address
|
||||
"reference points" which are set to the values of two extra "phantom
|
||||
points" in the glyph, that are a proprietary extension of the MS font
|
||||
scaler.
|
||||
|
||||
Emacs does not support these extensions, and as a result characters
|
||||
such as
|
||||
|
||||
ĥ
|
||||
|
||||
display incorrectly, with the right most edge of the `h' component
|
||||
stretched very far out to the right, on some low density displays.
|
||||
|
||||
The solution is to replace the MS-specific hinting code in Droid Sans
|
||||
Mono with automatically generated code from the FreeType project's
|
||||
"ttfautohint" program. First, extract
|
||||
'/system/fonts/DroidSansMono.ttf' from your device:
|
||||
|
||||
$ adb pull /system/fonts/DroidSansMono.ttf
|
||||
/system/fonts/DroidSansMono.ttf: 1 file pulled, 0 skipped.
|
||||
23.1 MB/s (90208 bytes in 0.004s)
|
||||
|
||||
install the "ttfautohint" program:
|
||||
|
||||
http://freetype.org/ttfautohint/
|
||||
|
||||
generate a font file with new hinting instructions:
|
||||
|
||||
$ ttfautohint DroidSansMono.ttf > DroidSansMono.ttf.rpl
|
||||
|
||||
and upload them to your device, either back to /system/fonts (which is
|
||||
allowed by free versions of Android, such as Replicant):
|
||||
|
||||
$ adb root
|
||||
$ adb remount
|
||||
$ adb push DroidSansMono.ttf.rpl /system/fonts/DroidSansMono.ttf
|
||||
|
||||
or to the user fonts directory described in the "Android Fonts" node
|
||||
of the Emacs manual. You may want to perform this procedure even if
|
||||
you are not seeing problems with character display, as the
|
||||
automatically generated instructions result in superior display
|
||||
results that are easier to read.
|
||||
|
||||
* Build-time problems
|
||||
|
||||
** Configuration
|
||||
|
|
|
|||
140
exec/Makefile.in
Normal file
140
exec/Makefile.in
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Configure build directory information.
|
||||
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
builddir = @builddir@
|
||||
|
||||
# Set up compilation tools.
|
||||
|
||||
CC = @CC@
|
||||
AS = @AS@
|
||||
LD = @LD@
|
||||
M4 = @M4@
|
||||
CPP = @CPP@
|
||||
ASFLAGS = @ASFLAGS@
|
||||
ARFLAGS = @ARFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LOADERFLAGS = @LOADERFLAGS@
|
||||
FIND_DELETE = @FIND_DELETE@
|
||||
|
||||
# Set up object files.
|
||||
|
||||
LOADER = @exec_loader@
|
||||
OBJS = @OBJS@
|
||||
LOADOBJS = $(patsubst %.s,%.o,$(LOADER))
|
||||
|
||||
# Set up automatic dependency tracking.
|
||||
|
||||
AUTO_DEPEND = @AUTO_DEPEND@
|
||||
DEPDIR = deps
|
||||
ifeq ($(AUTO_DEPEND),yes)
|
||||
DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
|
||||
-include $(OBJS:%.o=$(DEPDIR)/%.d)
|
||||
-include $(DEPDIR)/test.d
|
||||
-include $(DEPDIR)/exec1.d
|
||||
else
|
||||
DEPFLAGS =
|
||||
include $(srcdir)/deps.mk
|
||||
endif
|
||||
|
||||
# Set up the appropriate targets.
|
||||
|
||||
all: libexec.a loader
|
||||
|
||||
# Set up automatic Makefile regeneration.
|
||||
|
||||
$(srcdir)/configure: $(srcdir)/configure.ac
|
||||
cd $(srcdir) && autoreconf
|
||||
|
||||
config.status: $(srcdir)/configure
|
||||
if [ -x config.status ]; then \
|
||||
./config.status --recheck; \
|
||||
else \
|
||||
$(srcdir)/configure; \
|
||||
fi
|
||||
|
||||
Makefile: config.status Makefile.in
|
||||
MAKE="$(MAKE)" ./config.status
|
||||
|
||||
# Set up rules to build targets.
|
||||
|
||||
.SUFFIXES: .c .s
|
||||
.c.o:
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I. -I$(srcdir) $< -o $@
|
||||
.s.o:
|
||||
$(M4) $< > $(notdir $<).s
|
||||
$(AS) $(ASFLAGS) $(notdir $<).s -o $@
|
||||
|
||||
# Set up dependencies for config-mips.m4.
|
||||
|
||||
config-mips.m4: config-mips.m4.in
|
||||
cd $(srcdir) && ./config.status $@
|
||||
$(LOADOBJS): config-mips.m4
|
||||
|
||||
# Set up rules to build libexec.a.
|
||||
|
||||
libexec.a: $(OBJS)
|
||||
$(AR) cru $(ARFLAGS) $@ $^
|
||||
|
||||
# And loader.
|
||||
|
||||
loader: $(LOADOBJS)
|
||||
$(LD) -o $@ $(LOADERFLAGS) $(LOADOBJS)
|
||||
|
||||
# And test.
|
||||
|
||||
test: test.o libexec.a
|
||||
$(CC) $(LDFLAGS) $< libexec.a -o $@
|
||||
|
||||
# And exec1.
|
||||
|
||||
exec1: exec1.o libexec.a
|
||||
$(CC) $(LDFLAGS) $< libexec.a -o $@
|
||||
|
||||
# Set up targets for cleaning.
|
||||
|
||||
.PHONY: clean distclean maintainer-clean extraclean bootstrap-clean
|
||||
clean:
|
||||
rm -f *.o *.a loader test *.s.s
|
||||
ifeq ($(AUTO_DEPEND),yes)
|
||||
rm -rf deps/*.d
|
||||
endif
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile config.status config.h config-mips.m4
|
||||
|
||||
maintainer-clean: distclean
|
||||
|
||||
### This doesn't actually appear in the coding standards, but Karl
|
||||
### says GCC supports it, and that's where the configuration part of
|
||||
### the coding standards seem to come from. It's like distclean, but
|
||||
### it deletes backup and autosave files too.
|
||||
|
||||
extraclean: maintainer-clean
|
||||
-rm -f config-tmp-* $(srcdir)/aclocal.m4 $(srcdir)/configure \
|
||||
$(srcdir)/src/config.in
|
||||
-[ "$(srcdir)" = "." ] || \
|
||||
find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
|
||||
-find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
|
||||
bootstrap-clean: extraclean
|
||||
3
exec/README
Normal file
3
exec/README
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
This directory holds the source code to a library used to replace the
|
||||
`execve' and `execveat' system calls, used by the Android port of
|
||||
Emacs to start executables without intervention from the system.
|
||||
42
exec/config-mips.m4.in
Normal file
42
exec/config-mips.m4.in
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
dnl Assembler templates for MIPS computers.
|
||||
dnl
|
||||
dnl Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
dnl
|
||||
dnl This file is part of GNU Emacs.
|
||||
dnl
|
||||
dnl GNU Emacs is free software: you can redistribute it and/or modify
|
||||
dnl it under the terms of the GNU General Public License as published by
|
||||
dnl the Free Software Foundation, either version 3 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl GNU Emacs is distributed in the hope that it will be useful,
|
||||
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
dnl GNU General Public License for more details.
|
||||
dnl
|
||||
dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
define(`SYSCALL_open', `ifelse(`@MIPS_N32@',`yes',`6002',`4005')')
|
||||
define(`SYSCALL_close', `ifelse(`@MIPS_N32@',`yes',`6003',`4006')')
|
||||
define(`SYSCALL_mmap', `ifelse(`@MIPS_N32@',`yes',`6009',`4090')')
|
||||
define(`SYSCALL_nanosleep', `ifelse(`@MIPS_N32@',`yes',`6034',`4166')')
|
||||
define(`SYSCALL_exit', `ifelse(`@MIPS_N32@',`yes',`6058',`4001')')
|
||||
define(`SYSCALL_prctl', `ifelse(`@MIPS_N32@',`yes',`6153',`4192')')
|
||||
|
||||
define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move $a4, $1
|
||||
move $a5, $2
|
||||
move $a6, $3
|
||||
move $a7, $4',` addi $sp, -32
|
||||
sw $1, 16($sp)
|
||||
sw $2, 20($sp)
|
||||
sw $3, 24($sp)
|
||||
sw $4, 28($sp)')')
|
||||
|
||||
define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')')
|
||||
|
||||
dnl For mips64. Some assemblers don't want to assemble `daddi'.
|
||||
define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $2
|
||||
dadd $1, $1, $at',` daddi $1, $2')')
|
||||
define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $3
|
||||
dadd $1, $2, $at',` daddi $1, $2, $3')')
|
||||
1768
exec/config.guess
vendored
Executable file
1768
exec/config.guess
vendored
Executable file
File diff suppressed because it is too large
Load diff
358
exec/config.h.in
Normal file
358
exec/config.h.in
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Define to number of reserved bytes past the stack frame. */
|
||||
#undef ABI_RED_ZONE
|
||||
|
||||
/* Define if building universal (internal helper macro) */
|
||||
#undef AC_APPLE_UNIVERSAL_BUILD
|
||||
|
||||
/* Define to number of the `clone3' system call. */
|
||||
#undef CLONE3_SYSCALL
|
||||
|
||||
/* Define to number of the `clone' system call. */
|
||||
#undef CLONE_SYSCALL
|
||||
|
||||
/* Virtual address for loading PIC executables */
|
||||
#undef EXECUTABLE_BASE
|
||||
|
||||
/* Define to 1 if the system utilizes 64-bit ELF. */
|
||||
#undef EXEC_64
|
||||
|
||||
/* Define to number of the `exec' system call. */
|
||||
#undef EXEC_SYSCALL
|
||||
|
||||
/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't.
|
||||
*/
|
||||
#undef HAVE_DECL_STPCPY
|
||||
|
||||
/* Define to 1 if you have the declaration of `stpncpy', and to 0 if you
|
||||
don't. */
|
||||
#undef HAVE_DECL_STPNCPY
|
||||
|
||||
/* Define to 1 if you have the `getpagesize' function. */
|
||||
#undef HAVE_GETPAGESIZE
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <minix/config.h> header file. */
|
||||
#undef HAVE_MINIX_CONFIG_H
|
||||
|
||||
/* Define to 1 if process_vm_readv is available. */
|
||||
#undef HAVE_PROCESS_VM
|
||||
|
||||
/* Define to 1 if `si_syscall' is a member of `siginfo_t'. */
|
||||
#undef HAVE_SIGINFO_T_SI_SYSCALL
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `stpcpy' function. */
|
||||
#undef HAVE_STPCPY
|
||||
|
||||
/* Define to 1 if you have the `stpncpy' function. */
|
||||
#undef HAVE_STPNCPY
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#undef HAVE_SYS_PARAM_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <sys/uio.h> header file. */
|
||||
#undef HAVE_SYS_UIO_H
|
||||
|
||||
/* Define to 1 if the system has the type `uintptr_t'. */
|
||||
#undef HAVE_UINTPTR_T
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if you have the <wchar.h> header file. */
|
||||
#undef HAVE_WCHAR_H
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Virtual address for loading PIC interpreters */
|
||||
#undef INTERPRETER_BASE
|
||||
|
||||
/* Define to 1 if MIPS NABI calling convention is being used. */
|
||||
#undef MIPS_NABI
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to number of the `readlinkat' system call. */
|
||||
#undef READLINKAT_SYSCALL
|
||||
|
||||
/* Define to number of the `readlink' system call. */
|
||||
#undef READLINK_SYSCALL
|
||||
|
||||
/* Define to 1 if the library is used within a signal handler. */
|
||||
#undef REENTRANT
|
||||
|
||||
/* Define to 1 if the stack grows downwards. */
|
||||
#undef STACK_GROWS_DOWNWARDS
|
||||
|
||||
/* Define to register holding the stack pointer. */
|
||||
#undef STACK_POINTER
|
||||
|
||||
/* Define to 1 if all of the C90 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to register holding arg1 to system calls. */
|
||||
#undef SYSCALL_ARG1_REG
|
||||
|
||||
/* Define to register holding arg2 to system calls. */
|
||||
#undef SYSCALL_ARG2_REG
|
||||
|
||||
/* Define to register holding arg3 to system calls. */
|
||||
#undef SYSCALL_ARG3_REG
|
||||
|
||||
/* Define to register holding arg0 to system calls. */
|
||||
#undef SYSCALL_ARG_REG
|
||||
|
||||
/* Define to header holding system call numbers. */
|
||||
#undef SYSCALL_HEADER
|
||||
|
||||
/* Define to register holding the system call number. */
|
||||
#undef SYSCALL_NUM_REG
|
||||
|
||||
/* Define to register holding value of system calls. */
|
||||
#undef SYSCALL_RET_REG
|
||||
|
||||
/* Define to header holding USER_REGS_STRUCT. */
|
||||
#undef USER_HEADER
|
||||
|
||||
/* Define to structure holding user registers. */
|
||||
#undef USER_REGS_STRUCT
|
||||
|
||||
/* Define to word type used by tracees. */
|
||||
#undef USER_WORD
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# undef _ALL_SOURCE
|
||||
#endif
|
||||
/* Enable general extensions on macOS. */
|
||||
#ifndef _DARWIN_C_SOURCE
|
||||
# undef _DARWIN_C_SOURCE
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# undef __EXTENSIONS__
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
/* Enable X/Open compliant socket functions that do not require linking
|
||||
with -lxnet on HP-UX 11.11. */
|
||||
#ifndef _HPUX_ALT_XOPEN_SOCKET_API
|
||||
# undef _HPUX_ALT_XOPEN_SOCKET_API
|
||||
#endif
|
||||
/* Identify the host operating system as Minix.
|
||||
This macro does not affect the system headers' behavior.
|
||||
A future release of Autoconf may stop defining this macro. */
|
||||
#ifndef _MINIX
|
||||
# undef _MINIX
|
||||
#endif
|
||||
/* Enable general extensions on NetBSD.
|
||||
Enable NetBSD compatibility extensions on Minix. */
|
||||
#ifndef _NETBSD_SOURCE
|
||||
# undef _NETBSD_SOURCE
|
||||
#endif
|
||||
/* Enable OpenBSD compatibility extensions on NetBSD.
|
||||
Oddly enough, this does nothing on OpenBSD. */
|
||||
#ifndef _OPENBSD_SOURCE
|
||||
# undef _OPENBSD_SOURCE
|
||||
#endif
|
||||
/* Define to 1 if needed for POSIX-compatible behavior. */
|
||||
#ifndef _POSIX_SOURCE
|
||||
# undef _POSIX_SOURCE
|
||||
#endif
|
||||
/* Define to 2 if needed for POSIX-compatible behavior. */
|
||||
#ifndef _POSIX_1_SOURCE
|
||||
# undef _POSIX_1_SOURCE
|
||||
#endif
|
||||
/* Enable POSIX-compatible threading on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# undef _POSIX_PTHREAD_SEMANTICS
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
|
||||
#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
|
||||
# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
|
||||
#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
|
||||
# undef __STDC_WANT_IEC_60559_BFP_EXT__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
|
||||
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
|
||||
# undef __STDC_WANT_IEC_60559_DFP_EXT__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
|
||||
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
|
||||
# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
|
||||
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
|
||||
# undef __STDC_WANT_IEC_60559_TYPES_EXT__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
|
||||
#ifndef __STDC_WANT_LIB_EXT2__
|
||||
# undef __STDC_WANT_LIB_EXT2__
|
||||
#endif
|
||||
/* Enable extensions specified by ISO/IEC 24747:2009. */
|
||||
#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
|
||||
# undef __STDC_WANT_MATH_SPEC_FUNCS__
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# undef _TANDEM_SOURCE
|
||||
#endif
|
||||
/* Enable X/Open extensions. Define to 500 only if necessary
|
||||
to make mbstate_t available. */
|
||||
#ifndef _XOPEN_SOURCE
|
||||
# undef _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
# undef WORDS_BIGENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
#undef _UINT32_T
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
#undef _UINT64_T
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
#undef _UINT8_T
|
||||
|
||||
/* Define as a signed integer type capable of holding a process identifier. */
|
||||
#undef pid_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef ssize_t
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 16 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#undef uint16_t
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#undef uint32_t
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 64 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#undef uint64_t
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
#undef uint8_t
|
||||
|
||||
/* Define to the type of an unsigned integer type wide enough to hold a
|
||||
pointer, if such a type exists, and if the system does not define it. */
|
||||
#undef uintptr_t
|
||||
|
||||
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#else
|
||||
# ifndef HAVE__BOOL
|
||||
# ifdef __cplusplus
|
||||
typedef bool _Bool;
|
||||
# else
|
||||
# define _Bool signed char
|
||||
# endif
|
||||
# endif
|
||||
# define bool _Bool
|
||||
# define false 0
|
||||
# define true 1
|
||||
# define __bool_true_false_are_defined 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif /* HAVE_SYS_PARAM_H */
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif /* MAX */
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif /* MIN */
|
||||
|
||||
1890
exec/config.sub
vendored
Executable file
1890
exec/config.sub
vendored
Executable file
File diff suppressed because it is too large
Load diff
537
exec/configure.ac
Normal file
537
exec/configure.ac
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
dnl Autoconf script for GNU Emacs's exec library.
|
||||
dnl To rebuild the 'configure' script from this, execute the command
|
||||
dnl autoconf
|
||||
dnl in the directory containing this script.
|
||||
dnl If you changed any AC_DEFINES, also run autoheader.
|
||||
dnl
|
||||
dnl Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
dnl
|
||||
dnl This file is part of GNU Emacs.
|
||||
dnl
|
||||
dnl GNU Emacs is free software: you can redistribute it and/or modify
|
||||
dnl it under the terms of the GNU General Public License as published by
|
||||
dnl the Free Software Foundation, either version 3 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl GNU Emacs is distributed in the hope that it will be useful,
|
||||
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
dnl GNU General Public License for more details.
|
||||
dnl
|
||||
dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
AC_PREREQ([2.65])
|
||||
AC_INIT([libexec], [30.0.50], [bug-gnu-emacs@gnu.org], [],
|
||||
[https://www.gnu.org/software/emacs/])
|
||||
|
||||
AH_TOP([/* Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */])
|
||||
|
||||
AC_ARG_WITH([reentrancy],
|
||||
[AS_HELP_STRING([--with-reentrancy],
|
||||
[Generate library which can be used within a signal handler.])],
|
||||
[AC_DEFINE([REENTRANT], [1])])
|
||||
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_UINTPTR_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_SSIZE_T
|
||||
AC_TYPE_PID_T
|
||||
|
||||
AC_HEADER_STDBOOL
|
||||
AC_CHECK_FUNCS([getpagesize stpcpy stpncpy])
|
||||
AC_CHECK_DECLS([stpcpy, stpncpy])
|
||||
AC_CHECK_FUNC([process_vm_readv],
|
||||
[AC_CHECK_FUNC([process_vm_writev],
|
||||
[AC_CHECK_DECL([process_vm_readv],
|
||||
[AC_DEFINE([HAVE_PROCESS_VM], [1],
|
||||
[Define to 1 if process_vm_readv is available.])],
|
||||
[], [[
|
||||
#include <sys/uio.h>
|
||||
]])])])
|
||||
AC_CHECK_HEADERS([sys/param.h sys/uio.h])
|
||||
AC_CHECK_MEMBERS([siginfo_t.si_syscall], [], [],
|
||||
[[
|
||||
#include <signal.h>
|
||||
]])
|
||||
|
||||
AH_BOTTOM([
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#else
|
||||
# ifndef HAVE__BOOL
|
||||
# ifdef __cplusplus
|
||||
typedef bool _Bool;
|
||||
# else
|
||||
# define _Bool signed char
|
||||
# endif
|
||||
# endif
|
||||
# define bool _Bool
|
||||
# define false 0
|
||||
# define true 1
|
||||
# define __bool_true_false_are_defined 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif /* HAVE_SYS_PARAM_H */
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif /* MAX */
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif /* MIN */
|
||||
])
|
||||
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
AH_TEMPLATE([SYSCALL_HEADER], [Define to header holding system call numbers.])
|
||||
AH_TEMPLATE([USER_HEADER], [Define to header holding USER_REGS_STRUCT.])
|
||||
AH_TEMPLATE([USER_REGS_STRUCT], [Define to structure holding user registers.])
|
||||
AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call number.])
|
||||
AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system calls.])
|
||||
AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system calls.])
|
||||
AH_TEMPLATE([SYSCALL_ARG2_REG], [Define to register holding arg2 to system calls.])
|
||||
AH_TEMPLATE([SYSCALL_ARG3_REG], [Define to register holding arg3 to system calls.])
|
||||
AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls.])
|
||||
AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.])
|
||||
AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.])
|
||||
AH_TEMPLATE([USER_WORD], [Define to word type used by tracees.])
|
||||
AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.])
|
||||
AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows downwards.])
|
||||
AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack frame.])
|
||||
AH_TEMPLATE([EXECUTABLE_BASE], [Virtual address for loading PIC executables])
|
||||
AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters])
|
||||
AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.])
|
||||
AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.])
|
||||
AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.])
|
||||
AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.])
|
||||
AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
# Check whether or not sys/user exists. If it doesn't, try
|
||||
# asm/user.h, and croak if that doesn't exist either.
|
||||
AS_CASE([$host], [*mips*], [], [*],
|
||||
[AC_CHECK_HEADER([sys/user.h], [user_h="<sys/user.h>"],
|
||||
[AC_CHECK_HEADER([asm/user.h], [user_h="<asm/user.h>"],
|
||||
[AC_MSG_ERROR([Can not find working user.h])])])])
|
||||
|
||||
# Look for required tools.
|
||||
|
||||
AC_ARG_VAR([M4], [`m4' preprocessor command.])
|
||||
AC_ARG_VAR([AS], [`as' assembler command.])
|
||||
AC_ARG_VAR([LD], [`ld' linker command.])
|
||||
|
||||
# Check for a working m4.
|
||||
AC_CHECK_PROGS([M4], [gm4 m4],
|
||||
[AC_MSG_ERROR([Cannot find m4])])
|
||||
|
||||
# Check for a working assembler.
|
||||
AC_CHECK_TOOL([AS], [as],
|
||||
[AC_MSG_ERROR([Cannot find a working assembler])])
|
||||
|
||||
# And ar.
|
||||
AC_CHECK_TOOL([AR], [ar],
|
||||
[AC_MSG_ERROR([Cannot find a working ar])])
|
||||
|
||||
# And ld.
|
||||
AC_CHECK_TOOL([LD], [ld],
|
||||
[AC_MSG_ERROR([Cannot find a working linker])])
|
||||
|
||||
# Now check if ld is a C compiler.
|
||||
LDPREFIX=
|
||||
AC_CACHE_CHECK([whether ld is a C compiler],
|
||||
[exec_cv_ld_is_cc],
|
||||
[cat <<_ACEOF > conftest.c
|
||||
AC_LANG_PROGRAM(,)
|
||||
_ACEOF
|
||||
exec_cv_ld_is_cc=yes
|
||||
$LD -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
|
||||
|| exec_cv_ld_is_cc=no
|
||||
rm -f conftest.c conftest.$OBJEXT])
|
||||
|
||||
# And if as is a C compiler.
|
||||
AC_CACHE_CHECK([whether as is a C compiler],
|
||||
[exec_cv_as_is_cc],
|
||||
[cat <<_ACEOF > conftest.c
|
||||
AC_LANG_PROGRAM(,)
|
||||
_ACEOF
|
||||
exec_cv_as_is_cc=yes
|
||||
$AS -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
|
||||
|| exec_cv_as_is_cc=no
|
||||
rm -f conftest.c conftest.$OBJEXT])
|
||||
|
||||
# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and
|
||||
# `-static'. Also, set LDPREFIX to -Wl,
|
||||
AS_IF([test "x$exec_cv_ld_is_cc" = "xyes"],
|
||||
[LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static"
|
||||
LDPREFIX=-Wl,])
|
||||
|
||||
# If as is a C compiler, add `-c' to ASFLAGS.
|
||||
AS_IF([test "x$exec_cv_as_is_cc" = "xyes"],
|
||||
[ASFLAGS="$ASFLAGS -c"])
|
||||
|
||||
AC_DEFUN([exec_CHECK_LINUX_CLONE3],
|
||||
[
|
||||
AC_CHECK_DECL([__NR_clone3],
|
||||
[AC_DEFINE([CLONE3_SYSCALL], [__NR_clone3])],
|
||||
[], [[
|
||||
#include <asm/unistd.h>
|
||||
]])
|
||||
])
|
||||
|
||||
AC_DEFUN([exec_CHECK_MIPS_NABI],
|
||||
[
|
||||
AC_CACHE_CHECK([whether MIPS NABI calling convention is used],
|
||||
[exec_cv_mips_nabi],
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <sgidefs.h>
|
||||
]], [[
|
||||
#ifndef __mips64__
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
OABI in use.
|
||||
#endif /* _MIPS_SIM == _ABIO32 */
|
||||
#endif /* !__mips64__ */
|
||||
]])], [exec_cv_mips_nabi=yes],
|
||||
[exec_cv_mips_nabi=no])])
|
||||
|
||||
dnl mips64 systems use N64 calling convention, a variant of nabi
|
||||
dnl calling convention.
|
||||
AS_IF([test "x$exec_cv_mips_nabi" != "xno"],
|
||||
[AC_DEFINE([MIPS_NABI], [1],
|
||||
[Define to 1 if MIPS NABI calling convention is being used.])],
|
||||
[OBJS="$OBJS mipsfpu.o"])
|
||||
])
|
||||
|
||||
# Determine the system type and define appropriate macros.
|
||||
exec_loader=
|
||||
is_mips=
|
||||
OBJS="exec.o trace.o"
|
||||
DADDI_BROKEN=no
|
||||
|
||||
AS_CASE([$host], [x86_64-*linux*],
|
||||
[AC_CHECK_MEMBER([struct user_regs_struct.rdi],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [orig_rax])
|
||||
AC_DEFINE([SYSCALL_RET_REG], [rax])
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [rdi])
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [rsi])
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [rdx])
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [r10])
|
||||
AC_DEFINE([STACK_POINTER], [rsp])
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXEC_64], [1])
|
||||
AC_DEFINE([ABI_RED_ZONE], [128])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x600000000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
# Make sure the loader doesn't conflict with other position
|
||||
# dependent code.
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000"
|
||||
exec_loader=loader-x86_64.s],
|
||||
[AC_MSG_ERROR([Missing `rdi' in user_regs_struct])],
|
||||
[[
|
||||
#include $user_h
|
||||
]])], [i[[34567]]86-*linux*],
|
||||
[AC_CHECK_MEMBER([struct user_regs_struct.edi],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [orig_eax])
|
||||
AC_DEFINE([SYSCALL_RET_REG], [eax])
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [ebx])
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [ecx])
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [edx])
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [esi])
|
||||
AC_DEFINE([STACK_POINTER], [esp])
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
# Make sure the loader doesn't conflict with other position
|
||||
# dependent code.
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000"
|
||||
exec_loader=loader-x86.s],
|
||||
[AC_MSG_ERROR([Missing `edi' in user_regs_struct])],
|
||||
[[
|
||||
#include $user_h
|
||||
]])], [aarch64-*linux*],
|
||||
[AC_CHECK_MEMBER([struct user_regs_struct.sp],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]])
|
||||
AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]])
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [[regs[2]]])
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [[regs[3]]])
|
||||
AC_DEFINE([STACK_POINTER], [sp])
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXEC_64], [1])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
# Note that aarch64 has no `readlink'.
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
# Make sure the loader doesn't conflict with other position
|
||||
# dependent code. ARM places rather significant restrictions on
|
||||
# virtual addresses for a 64 bit architecture.
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000"
|
||||
exec_loader=loader-aarch64.s],
|
||||
[AC_MSG_ERROR([Missing `sp' in user_regs_struct])],
|
||||
[[
|
||||
#include $user_h
|
||||
]])], [arm*linux*eabi* | armv7*linux*],
|
||||
[AC_CHECK_MEMBER([struct user_regs.uregs],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
|
||||
AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
|
||||
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
|
||||
exec_loader=loader-armeabi.s],
|
||||
[AC_CHECK_MEMBER([struct pt_regs.uregs],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE_UNQUOTED([USER_HEADER], [<asm/ptrace.h>])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct pt_regs])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
|
||||
AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
|
||||
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
|
||||
exec_loader=loader-armeabi.s],
|
||||
[AC_MSG_ERROR([Missing `uregs' in user_regs_struct or pt_regs])],
|
||||
[[
|
||||
#include <asm/ptrace.h>
|
||||
]])],
|
||||
[[
|
||||
#include $user_h
|
||||
]])], [mipsel*linux*],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
|
||||
AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
|
||||
AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI],
|
||||
[AC_MSG_ERROR([_MIPS_SIM could not be determined]),
|
||||
[[
|
||||
#include <sgidefs.h>
|
||||
]]])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
|
||||
is_mips=yes
|
||||
exec_loader=loader-mipsel.s], [mips64el*linux*],
|
||||
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
|
||||
AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
|
||||
AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
|
||||
AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
|
||||
AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
|
||||
AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
|
||||
AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
|
||||
AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
|
||||
AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
|
||||
AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
|
||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||
AC_DEFINE([EXEC_64], [1])
|
||||
AC_DEFINE([EXECUTABLE_BASE], [0x400000])
|
||||
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
|
||||
AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
|
||||
AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
|
||||
AC_CACHE_CHECK([whether as understands `daddi'],
|
||||
[exec_cv_as_daddi],
|
||||
[exec_cv_as_daddi=no
|
||||
cat <<_ACEOF >conftest.s
|
||||
.section text
|
||||
.global __start
|
||||
__start:
|
||||
li $t0, 0
|
||||
li $t1, 0
|
||||
daddi $t0, $t1, 1
|
||||
daddi $t0, $t1, -1
|
||||
daddi $t0, -1
|
||||
daddi $t0, 1
|
||||
|
||||
_ACEOF
|
||||
$AS $ASFLAGS conftest.s -o conftest.$OBJEXT \
|
||||
>&AS_MESSAGE_LOG_FD 2>&1 \
|
||||
&& exec_cv_as_daddi=yes
|
||||
rm -f conftest.s conftest.$OBJEXT])
|
||||
AS_IF([test "x$exec_cv_as_daddi" != "xyes"],
|
||||
[DADDI_BROKEN=yes])
|
||||
exec_CHECK_LINUX_CLONE3
|
||||
exec_CHECK_MIPS_NABI
|
||||
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000"
|
||||
is_mips=yes
|
||||
exec_loader=loader-mips64el.s], [*],
|
||||
[AC_MSG_ERROR([Please port libexec to $host])])
|
||||
|
||||
AC_SUBST([DADDI_BROKEN])
|
||||
|
||||
MIPS_N32=$exec_cv_mips_nabi
|
||||
|
||||
AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.])
|
||||
AC_ARG_VAR([ARFLAGS], [Flags for the archiver.])
|
||||
AC_ARG_VAR([ASFLAGS], [Flags for the assembler.])
|
||||
|
||||
# Make the assembler optimize for code size. Don't do this on MIPS,
|
||||
# as the assembler code manages branch delays manually.
|
||||
|
||||
AC_CACHE_CHECK([whether as understands -O],
|
||||
[exec_cv_as_O],
|
||||
[exec_cv_as_O=no
|
||||
cat <<_ACEOF >conftest.s
|
||||
.section text
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
_ACEOF
|
||||
$AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \
|
||||
>&AS_MESSAGE_LOG_FD 2>&1 \
|
||||
&& exec_cv_as_O=yes
|
||||
rm -f conftest.s conftest.$OBJEXT])
|
||||
|
||||
AS_IF([test "$exec_cv_as_O" = "yes" \
|
||||
&& test "$is_mips" != "yes"],
|
||||
[ASFLAGS="$ASFLAGS -O"])
|
||||
|
||||
# Make the assembler generate debug information.
|
||||
|
||||
AC_CACHE_CHECK([whether as understands -g],
|
||||
[exec_cv_as_g],
|
||||
[exec_cv_as_g=no
|
||||
cat <<_ACEOF >conftest.s
|
||||
.section text
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
_ACEOF
|
||||
$AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \
|
||||
>&AS_MESSAGE_LOG_FD 2>&1 \
|
||||
&& exec_cv_as_g=yes
|
||||
rm -f conftest.s conftest.$OBJEXT])
|
||||
AS_IF([test "$exec_cv_as_g" = "yes"], [ASFLAGS="$ASFLAGS -g"])
|
||||
|
||||
# Check for the ability to automatically generate dependencies for C
|
||||
# source files.
|
||||
AUTO_DEPEND=no
|
||||
AS_IF([test "x$GCC" = xyes],
|
||||
[AC_CACHE_CHECK([whether gcc understands -MMD -MF],
|
||||
[exec_cv_autodepend],
|
||||
[SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -MMD -MF deps.d -MP"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
|
||||
[exec_cv_autodepend=yes],
|
||||
[exec_cv_autodepend=no])
|
||||
CFLAGS="$SAVE_CFLAGS"
|
||||
test -f deps.d || emacs_cv_autodepend=no
|
||||
rm -rf deps.d])
|
||||
AS_IF([test "x$exec_cv_autodepend" = xyes],
|
||||
[AUTO_DEPEND=yes
|
||||
AS_MKDIR_P([deps])])])
|
||||
|
||||
# Now check for some other stuff.
|
||||
|
||||
AC_CACHE_CHECK([for 'find' args to delete a file],
|
||||
[exec_cv_find_delete],
|
||||
[AS_IF([touch conftest.tmp && find conftest.tmp -delete 2>/dev/null &&
|
||||
test ! -f conftest.tmp], [exec_cv_find_delete="-delete"],
|
||||
[exec_cv_find_delete="-exec rm -f {} ';'"])])
|
||||
FIND_DELETE=$exec_cv_find_delete
|
||||
AC_SUBST([FIND_DELETE])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_FILES([Makefile config-mips.m4])
|
||||
|
||||
AC_SUBST([AUTO_DEPEND])
|
||||
AC_SUBST([LOADERFLAGS])
|
||||
AC_SUBST([ARFLAGS])
|
||||
AC_SUBST([ASFLAGS])
|
||||
AC_SUBST([exec_loader])
|
||||
AC_SUBST([MIPS_N32])
|
||||
AC_SUBST([OBJS])
|
||||
|
||||
AC_OUTPUT
|
||||
21
exec/deps.mk
Normal file
21
exec/deps.mk
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
### deps.mk
|
||||
|
||||
## Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
## This file is part of GNU Emacs.
|
||||
|
||||
## GNU Emacs is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## GNU Emacs is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
exec.o: exec.h config.h
|
||||
trace.o: exec.h config.h
|
||||
1202
exec/exec.c
Normal file
1202
exec/exec.c
Normal file
File diff suppressed because it is too large
Load diff
201
exec/exec.h
Normal file
201
exec/exec.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
|
||||
#ifndef _EXEC_H_
|
||||
#define _EXEC_H_
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif /* HAVE_STDINT_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include USER_HEADER
|
||||
|
||||
/* Define a replacement for `uint64_t' if it's not present in the C
|
||||
library. */
|
||||
|
||||
#ifndef UINT64_MAX
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t word1;
|
||||
uint32_t word2;
|
||||
} xint64_t;
|
||||
|
||||
#else /* UINT64_MAX */
|
||||
typedef uint64_t xint64_t;
|
||||
#endif /* !UINT64_MAX */
|
||||
|
||||
|
||||
|
||||
/* 32-bit ELF headers. */
|
||||
|
||||
struct elf_header_32
|
||||
{
|
||||
unsigned char e_ident[16];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry;
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct program_header_32
|
||||
{
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
struct dt_entry_32
|
||||
{
|
||||
uint32_t d_tag;
|
||||
uint32_t d_val;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct elf_header_64
|
||||
{
|
||||
unsigned char e_ident[16];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
xint64_t e_entry;
|
||||
xint64_t e_phoff;
|
||||
xint64_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct program_header_64
|
||||
{
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
xint64_t p_offset;
|
||||
xint64_t p_vaddr;
|
||||
xint64_t p_paddr;
|
||||
xint64_t p_filesz;
|
||||
xint64_t p_memsz;
|
||||
xint64_t p_align;
|
||||
};
|
||||
|
||||
struct dt_entry_64
|
||||
{
|
||||
xint64_t d_tag;
|
||||
xint64_t d_val;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Define some types to the correct values. */
|
||||
|
||||
#ifdef EXEC_64
|
||||
typedef struct elf_header_64 elf_header;
|
||||
typedef struct program_header_64 program_header;
|
||||
typedef struct dt_entry_64 dt_entry;
|
||||
#else /* !EXEC_64 */
|
||||
typedef struct elf_header_32 elf_header;
|
||||
typedef struct program_header_32 program_header;
|
||||
typedef struct dt_entry_32 dt_entry;
|
||||
#endif /* EXEC_64 */
|
||||
|
||||
|
||||
|
||||
/* Defined in trace.c. */
|
||||
|
||||
/* Structure describing a process being traced. */
|
||||
|
||||
struct exec_tracee
|
||||
{
|
||||
/* The next process being traced. */
|
||||
struct exec_tracee *next;
|
||||
|
||||
/* The thread ID of this process. */
|
||||
pid_t pid;
|
||||
|
||||
/* Whether or not the tracee is currently waiting for a system call
|
||||
to complete. */
|
||||
bool waiting_for_syscall : 1;
|
||||
|
||||
/* Whether or not the tracee has been created but is not yet
|
||||
processed by `handle_clone'. */
|
||||
bool new_child : 1;
|
||||
|
||||
#ifndef REENTRANT
|
||||
/* Name of the executable being run. */
|
||||
char *exec_file;
|
||||
#endif /* !REENTRANT */
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef __aarch64__
|
||||
|
||||
extern int aarch64_get_regs (pid_t, USER_REGS_STRUCT *);
|
||||
extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool);
|
||||
|
||||
#endif /* __aarch64__ */
|
||||
|
||||
|
||||
|
||||
extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
|
||||
USER_REGS_STRUCT *, USER_WORD);
|
||||
extern int user_copy (struct exec_tracee *, const unsigned char *,
|
||||
USER_WORD, USER_WORD);
|
||||
extern void exec_init (const char *);
|
||||
|
||||
|
||||
|
||||
extern int tracing_execve (const char *, char *const *,
|
||||
char *const *);
|
||||
extern int after_fork (pid_t);
|
||||
extern pid_t exec_waitpid (pid_t, int *, int);
|
||||
|
||||
|
||||
|
||||
/* Defined in exec.c. */
|
||||
|
||||
extern char *exec_0 (char *, struct exec_tracee *,
|
||||
size_t *, USER_REGS_STRUCT *);
|
||||
|
||||
|
||||
|
||||
#endif /* _EXEC_H_ */
|
||||
94
exec/exec1.c
Normal file
94
exec/exec1.c
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "exec.h"
|
||||
|
||||
/* exec1 is a program which takes another program and its arguments,
|
||||
forks, and executes that program, all while tracing it and its
|
||||
children to use the program execution mechanism defined in exec.c.
|
||||
|
||||
This is necessary to bypass security restrictions which prohibit
|
||||
Emacs from loading executables from certain directories, by, in
|
||||
effect, replacing the executable loader in the Linux kernel. */
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
pid_t pid, pid1;
|
||||
extern char **environ;
|
||||
int wstatus;
|
||||
|
||||
pid1 = getpid ();
|
||||
pid = fork ();
|
||||
|
||||
if (!pid)
|
||||
{
|
||||
/* Set the process group used to the parent. */
|
||||
if (setpgid (0, pid1))
|
||||
perror ("setpgid");
|
||||
|
||||
tracing_execve (argv[2], argv + 2, environ);
|
||||
|
||||
/* An error occured. Exit with failure. */
|
||||
exit (127);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Provide the file name of the loader. */
|
||||
exec_init (argv[1]);
|
||||
|
||||
if (after_fork (pid))
|
||||
exit (127);
|
||||
|
||||
/* Start waiting for the process to exit. */
|
||||
|
||||
while (true)
|
||||
{
|
||||
pid1 = exec_waitpid (-1, &wstatus, 0);
|
||||
|
||||
/* If the child process exits normally, exit with its status
|
||||
code. If not, raise the signal that caused it to
|
||||
exit. */
|
||||
|
||||
if (pid == pid1)
|
||||
{
|
||||
if (WIFEXITED (wstatus))
|
||||
exit (WEXITSTATUS (wstatus));
|
||||
else /* if WIFSIGNALED (wstatus) */
|
||||
{
|
||||
raise (WTERMSIG (wstatus));
|
||||
|
||||
/* Just in case the signal raised doesn't cause an
|
||||
exit. */
|
||||
exit (127);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, continue looping. */
|
||||
}
|
||||
}
|
||||
}
|
||||
541
exec/install-sh
Executable file
541
exec/install-sh
Executable file
|
|
@ -0,0 +1,541 @@
|
|||
#!/usr/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2020-11-14.01; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
# Create dirs (including intermediate dirs) using mode 755.
|
||||
# This is like GNU 'install' as of coreutils 8.32 (2020).
|
||||
mkdir_umask=22
|
||||
|
||||
backupsuffix=
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-p pass -p to $cpprog.
|
||||
-s $stripprog installed files.
|
||||
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
|
||||
By default, rm is invoked with -f; when overridden with RMPROG,
|
||||
it's up to you to specify -f if you want it.
|
||||
|
||||
If -S is not specified, no backups are attempted.
|
||||
|
||||
Email bug reports to bug-automake@gnu.org.
|
||||
Automake home page: https://www.gnu.org/software/automake/
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-p) cpprog="$cpprog -p";;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-S) backupsuffix="$2"
|
||||
shift;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
# Don't chown directories that already exist.
|
||||
if test $dstdir_status = 0; then
|
||||
chowncmd=""
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dstbase=`basename "$src"`
|
||||
case $dst in
|
||||
*/) dst=$dst$dstbase;;
|
||||
*) dst=$dst/$dstbase;;
|
||||
esac
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
case $dstdir in
|
||||
*/) dstdirslash=$dstdir;;
|
||||
*) dstdirslash=$dstdir/;;
|
||||
esac
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
# The $RANDOM variable is not portable (e.g., dash). Use it
|
||||
# here however when possible just to lower collision chance.
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
|
||||
trap '
|
||||
ret=$?
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
|
||||
exit $ret
|
||||
' 0
|
||||
|
||||
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||
# directory is successfully created first before we actually test
|
||||
# 'mkdir -p'.
|
||||
if (umask $mkdir_umask &&
|
||||
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
test_tmpdir="$tmpdir/a"
|
||||
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=${dstdirslash}_inst.$$_
|
||||
rmtmp=${dstdirslash}_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask &&
|
||||
{ test -z "$stripcmd" || {
|
||||
# Create $dsttmp read-write so that cp doesn't create it read-only,
|
||||
# which would cause strip to fail.
|
||||
if test -z "$doit"; then
|
||||
: >"$dsttmp" # No need to fork-exec 'touch'.
|
||||
else
|
||||
$doit touch "$dsttmp"
|
||||
fi
|
||||
}
|
||||
} &&
|
||||
$doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# If $backupsuffix is set, and the file being installed
|
||||
# already exists, attempt a backup. Don't worry if it fails,
|
||||
# e.g., if mv doesn't support -f.
|
||||
if test -n "$backupsuffix" && test -f "$dst"; then
|
||||
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
187
exec/loader-aarch64.s
Normal file
187
exec/loader-aarch64.s
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of GNU Emacs.
|
||||
//
|
||||
// GNU Emacs is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
//
|
||||
// GNU Emacs is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Notice that aarch64 requires that sp be aligned to 16 bytes while
|
||||
// accessing memory from sp, so x20 is used to chase down the load
|
||||
// area.
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
_start:
|
||||
//mov x8, 101 // SYS_nanosleep
|
||||
//adr x0, timespec // req
|
||||
//mov x1, #0 // rem
|
||||
//svc #0 // syscall
|
||||
mov x20, sp // x20 = sp
|
||||
ldr x10, [x20] // x10 = original SP
|
||||
add x20, x20, #16 // x20 = start of load area
|
||||
mov x28, #-1 // x28 = secondary fd
|
||||
.next_action:
|
||||
ldr x11, [x20] // action number
|
||||
and x12, x11, #-17 // actual action number
|
||||
cbz x12, .open_file // open file?
|
||||
cmp x12, #3 // jump?
|
||||
beq .rest_of_exec
|
||||
cmp x12, #4 // anonymous mmap?
|
||||
beq .do_mmap_anon
|
||||
.do_mmap:
|
||||
ldr x0, [x20, 8] // vm_address
|
||||
ldr x1, [x20, 32] // length
|
||||
ldr x2, [x20, 24] // protection
|
||||
ldr x3, [x20, 40] // flags
|
||||
tst x11, #16 // primary fd?
|
||||
mov x4, x29 // primary fd
|
||||
beq .do_mmap_1
|
||||
mov x4, x28 // secondary fd
|
||||
.do_mmap_1:
|
||||
mov x8, #222 // SYS_mmap
|
||||
ldr x5, [x20, 16] // file_offset
|
||||
svc #0 // syscall
|
||||
ldr x9, [x20, 8] // length
|
||||
cmp x0, x9 // mmap result
|
||||
bne .perror // print error
|
||||
ldr x3, [x20, 48] // clear
|
||||
add x1, x1, x0 // x1 = vm_address + end
|
||||
sub x3, x1, x3 // x3 = x1 - clear
|
||||
mov x0, #0 // x0 = 0
|
||||
.fill64:
|
||||
sub x2, x1, x3 // x2 = x1 - x3
|
||||
cmp x2, #63 // x2 >= 64?
|
||||
ble .fillb // start filling bytes
|
||||
stp x0, x0, [x3] // x3[0] = 0, x3[1] = 0
|
||||
stp x0, x0, [x3, 16] // x3[2] = 0, x3[3] = 0
|
||||
stp x0, x0, [x3, 32] // x3[4] = 0, x3[5] = 0
|
||||
stp x0, x0, [x3, 48] // x3[6] = 0, x3[7] = 0
|
||||
add x3, x3, #64 // x3 += 8
|
||||
b .fill64
|
||||
.fillb:
|
||||
cmp x1, x3 // x1 == x3?
|
||||
beq .continue // done
|
||||
strb w0, [x3], #1 // ((char *) x3)++ = 0
|
||||
b .fillb
|
||||
.continue:
|
||||
add x20, x20, #56 // next action
|
||||
b .next_action
|
||||
.do_mmap_anon:
|
||||
ldr x0, [x20, 8] // vm_address
|
||||
ldr x1, [x20, 32] // length
|
||||
ldr x2, [x20, 24] // protection
|
||||
ldr x3, [x20, 40] // flags
|
||||
mov x4, #-1 // fd
|
||||
b .do_mmap_1
|
||||
.open_file:
|
||||
mov x8, #56 // SYS_openat
|
||||
mov x0, #-100 // AT_FDCWD
|
||||
add x1, x20, #8 // file name
|
||||
mov x2, #0 // O_RDONLY
|
||||
mov x3, #0 // mode
|
||||
svc #0 // syscall
|
||||
cmp x0, #-1 // rc < 0?
|
||||
ble .perror
|
||||
mov x19, x1 // x19 == x1
|
||||
.nextc:
|
||||
ldrb w2, [x1], #1 // b = *x1++
|
||||
cmp w2, #47 // dir separator?
|
||||
bne .nextc1 // not dir separator
|
||||
mov x19, x1 // x19 = char past separator
|
||||
.nextc1:
|
||||
cbnz w2, .nextc // b?
|
||||
add x1, x1, #7 // round up x1
|
||||
and x20, x1, #-8 // mask for round, set x20
|
||||
tst x11, #16 // primary fd?
|
||||
bne .secondary // secondary fd
|
||||
mov x29, x0 // primary fd
|
||||
mov x8, #167 // SYS_prctl
|
||||
mov x0, #15 // PR_SET_NAME
|
||||
mov x1, x19 // basename
|
||||
mov x2, #0 // arg2
|
||||
mov x3, #0 // arg3
|
||||
mov x4, #0 // arg4
|
||||
mov x5, #0 // arg5
|
||||
svc #0 // syscall
|
||||
b .next_action // next action
|
||||
.secondary:
|
||||
mov x28, x0 // secondary fd
|
||||
b .next_action // next action.
|
||||
.perror:
|
||||
mov x8, #93 // SYS_exit
|
||||
mvn x0, x0 // x1 = ~x0
|
||||
add x0, x0, 1 // x1 += 1
|
||||
svc #0 // exit
|
||||
.rest_of_exec:
|
||||
mov x7, x20 // x7 = x20
|
||||
mov x20, x10 // x20 = x10
|
||||
ldr x9, [x20] // argc
|
||||
add x9, x9, #2 // x9 += 2
|
||||
lsl x9, x9, #3 // argc * 8
|
||||
add x20, x20, x9 // now past argv
|
||||
.skipenv:
|
||||
ldr x9, [x20], #8 // x9 = *envp++
|
||||
cbnz x9, .skipenv // x9?
|
||||
.one_auxv:
|
||||
ldr x9, [x20], #16 // x9 = *sp, sp += 2
|
||||
cbz x9, .cleanup // !x9?
|
||||
cmp x9, #3 // is AT_PHDR?
|
||||
beq .replace_phdr // replace
|
||||
cmp x9, #4 // is AT_PHENT?
|
||||
beq .replace_phent // replace
|
||||
cmp x9, #5 // is AT_PHNUM?
|
||||
beq .replace_phnum // replace
|
||||
cmp x9, #9 // is AT_ENTRY?
|
||||
beq .replace_entry // replace
|
||||
cmp x9, #7 // is AT_BASE?
|
||||
beq .replace_base // replace
|
||||
b .one_auxv // next auxv
|
||||
.replace_phdr:
|
||||
ldr x9, [x7, 40] // at_phdr
|
||||
str x9, [x20, -8] // store value
|
||||
b .one_auxv
|
||||
.replace_phent:
|
||||
ldr x9, [x7, 24] // at_phent
|
||||
str x9, [x20, -8] // store value
|
||||
b .one_auxv
|
||||
.replace_phnum:
|
||||
ldr x9, [x7, 32] // at_phnum
|
||||
str x9, [x20, -8] // store value
|
||||
b .one_auxv
|
||||
.replace_entry:
|
||||
ldr x9, [x7, 16] // at_entry
|
||||
str x9, [x20, -8] // store value
|
||||
b .one_auxv
|
||||
.replace_base:
|
||||
ldr x9, [x7, 48] // at_base
|
||||
str x9, [x20, -8] // store value
|
||||
b .one_auxv
|
||||
.cleanup:
|
||||
cmp x28, #-1 // is secondary fd set?
|
||||
bne .cleanup1 // not set
|
||||
mov x8, #57 // SYS_close
|
||||
mov x0, x28 // secondary fd
|
||||
svc #0 // syscall
|
||||
.cleanup1:
|
||||
mov x8, #57 // SYS_close
|
||||
mov x0, x29 // primary fd
|
||||
svc #0 // syscall
|
||||
.enter:
|
||||
mov sp, x10 // restore original SP
|
||||
mov x0, #0 // clear rtld_fini
|
||||
ldr x1, [x7, 8] // branch to code
|
||||
br x1
|
||||
|
||||
timespec:
|
||||
.quad 10
|
||||
.quad 10
|
||||
204
exec/loader-armeabi.s
Normal file
204
exec/loader-armeabi.s
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
@ Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
@
|
||||
@ This file is part of GNU Emacs.
|
||||
@
|
||||
@ GNU Emacs is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published
|
||||
@ by the Free Software Foundation, either version 3 of the License,
|
||||
@ or (at your option) any later version.
|
||||
@
|
||||
@ GNU Emacs is distributed in the hope that it will be useful, but
|
||||
@ WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
@ General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with GNU Emacs. If not, see <https:@www.gnu.org/licenses/>.
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
_start:
|
||||
@mov r7, #162 @ SYS_nanosleep
|
||||
@adr r0, timespec @ req
|
||||
@mov r1, #0 @ rem
|
||||
@swi #0 @ syscall
|
||||
mov r8, sp @ r8 = sp
|
||||
ldr r9, [r8], #8 @ r9 = original sp, r8 += 8
|
||||
mov r14, #-1 @ r14 = secondary fd
|
||||
.next_action:
|
||||
ldr r11, [r8] @ r11 = action number
|
||||
and r12, r11, #-17 @ actual action number
|
||||
cmp r12, #0 @ open file?
|
||||
beq .open_file @ open file.
|
||||
cmp r12, #3 @ jump?
|
||||
beq .rest_of_exec @ jump to code.
|
||||
cmp r12, #4 @ anonymous mmap?
|
||||
beq .do_mmap_anon @ anonymous mmap.
|
||||
.do_mmap:
|
||||
add r6, r8, #4 @ r6 = r8 + 4
|
||||
ldm r6!, {r0, r5} @ vm_address, file_offset
|
||||
ldm r6!, {r1, r2} @ protection, length
|
||||
mov r3, r1 @ swap
|
||||
lsr r5, #12 @ divide file offset by page size
|
||||
mov r1, r2 @ swap
|
||||
mov r2, r3 @ swap
|
||||
ldm r6!, {r3, r12} @ flags, clear
|
||||
tst r11, #16 @ primary fd?
|
||||
mov r4, r10 @ primary fd
|
||||
beq .do_mmap_1
|
||||
mov r4, r14 @ secondary fd
|
||||
.do_mmap_1:
|
||||
mov r7, #192 @ SYS_mmap2
|
||||
swi #0 @ syscall
|
||||
ldr r2, [r8, #4] @ vm_address
|
||||
cmp r2, r0 @ rc == vm_address?
|
||||
bne .perror
|
||||
add r0, r1, r2 @ r0 = length + vm_address
|
||||
sub r3, r0, r12 @ r3 = r0 - clear
|
||||
mov r1, #0 @ r1 = 0
|
||||
.align:
|
||||
cmp r0, r3 @ r0 == r3?
|
||||
beq .continue @ continue
|
||||
tst r3, #3 @ r3 & 3?
|
||||
bne .fill32 @ fill aligned
|
||||
strb r1, [r3], #1 @ fill byte
|
||||
b .align @ align again
|
||||
.fill32:
|
||||
sub r2, r0, r3 @ r2 = r0 - r3
|
||||
cmp r2, #31 @ r2 >= 32?
|
||||
ble .fillb @ start filling bytes
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
str r1, [r3], #4 @ *r3++ = 0
|
||||
b .fill32
|
||||
.fillb:
|
||||
cmp r0, r3 @ r0 == r3
|
||||
beq .continue @ done
|
||||
strb r1, [r3], #1 @ ((char *) r3)++ = 0
|
||||
b .fillb
|
||||
.continue:
|
||||
add r8, r8, #28 @ next action
|
||||
b .next_action
|
||||
.do_mmap_anon:
|
||||
add r6, r8, #4 @ r6 = r8 + 4
|
||||
ldm r6!, {r0, r5} @ vm_address, file_offset
|
||||
ldm r6!, {r1, r2} @ protection, length
|
||||
mov r3, r1 @ swap
|
||||
lsr r5, #12 @ divide file offset by page size
|
||||
mov r1, r2 @ swap
|
||||
mov r2, r3 @ swap
|
||||
ldm r6!, {r3, r12} @ flags, clear
|
||||
mov r4, #-1 @ fd
|
||||
b .do_mmap_1
|
||||
.open_file:
|
||||
mov r7, #5 @ SYS_open
|
||||
add r0, r8, #4 @ file name
|
||||
mov r1, #0 @ O_RDONLY
|
||||
mov r2, #0 @ mode
|
||||
swi #0 @ syscall
|
||||
cmp r0, #-1 @ r0 <= -1?
|
||||
ble .perror
|
||||
add r8, r8, #4 @ r8 = start of string
|
||||
mov r1, r8 @ r1 = r8
|
||||
.nextc:
|
||||
ldrb r2, [r8], #1 @ b = *r0++
|
||||
cmp r2, #47 @ dir separator?
|
||||
bne .nextc1 @ not dir separator
|
||||
mov r1, r8 @ r1 = char past separator
|
||||
.nextc1:
|
||||
cmp r2, #0 @ b?
|
||||
bne .nextc @ next character
|
||||
add r8, r8, #3 @ round up r8
|
||||
and r8, r8, #-4 @ mask for round, set r8
|
||||
tst r11, #16 @ primary fd?
|
||||
bne .secondary @ secondary fd
|
||||
mov r10, r0 @ primary fd
|
||||
mov r7, #172 @ SYS_prctl
|
||||
mov r0, #15 @ PR_SET_NAME, r1 = name
|
||||
mov r2, #0 @ arg2
|
||||
mov r3, #0 @ arg3
|
||||
mov r4, #0 @ arg4
|
||||
mov r5, #0 @ arg5
|
||||
swi #0 @ syscall
|
||||
b .next_action @ next action
|
||||
.secondary:
|
||||
mov r14, r0 @ secondary fd
|
||||
b .next_action @ next action
|
||||
.perror:
|
||||
mov r7, #1 @ SYS_exit
|
||||
mvn r0, r0 @ r0 = ~r0
|
||||
add r0, r0, #1 @ r0 += 1
|
||||
swi #0
|
||||
.rest_of_exec:
|
||||
mov r7, r9 @ r7 = original SP
|
||||
ldr r6, [r7] @ argc
|
||||
add r6, r6, #2 @ argc + 2
|
||||
lsl r6, r6, #2 @ argc *= 4
|
||||
add r7, r7, r6 @ now past argv
|
||||
.skipenv:
|
||||
ldr r6, [r7], #4 @ r6 = *r7++
|
||||
cmp r6, #0 @ r6?
|
||||
bne .skipenv @ r6?
|
||||
.one_auxv:
|
||||
ldr r6, [r7], #8 @ r6 = *r7, r7 += 2
|
||||
cmp r6, #0 @ !r6?
|
||||
beq .cleanup @ r6?
|
||||
cmp r6, #3 @ is AT_PHDR?
|
||||
beq .replace_phdr @ replace
|
||||
cmp r6, #4 @ is AT_PHENT?
|
||||
beq .replace_phent @ replace
|
||||
cmp r6, #5 @ is AT_PHNUM?
|
||||
beq .replace_phnum @ replace
|
||||
cmp r6, #9 @ is AT_ENTRY?
|
||||
beq .replace_entry @ replace
|
||||
cmp r6, #7 @ is AT_BASE?
|
||||
beq .replace_base @ replace
|
||||
b .one_auxv @ next auxv
|
||||
.replace_phdr:
|
||||
ldr r6, [r8, #20] @ at_phdr
|
||||
str r6, [r7, #-4] @ store value
|
||||
b .one_auxv
|
||||
.replace_phent:
|
||||
ldr r6, [r8, #12] @ at_phent
|
||||
str r6, [r7, #-4] @ store value
|
||||
b .one_auxv
|
||||
.replace_phnum:
|
||||
ldr r6, [r8, #16] @ at_phnum
|
||||
str r6, [r7, #-4] @ store value
|
||||
b .one_auxv
|
||||
.replace_entry:
|
||||
ldr r6, [r8, #8] @ at_entry
|
||||
str r6, [r7, #-4] @ store value
|
||||
b .one_auxv
|
||||
.replace_base:
|
||||
ldr r6, [r8, #24] @ at_base
|
||||
str r6, [r7, #-4] @ store value
|
||||
b .one_auxv
|
||||
.cleanup:
|
||||
cmp r14, #-1 @ secondary fd set?
|
||||
bne .cleanup1 @ not set
|
||||
mov r7, #6 @ SYS_close
|
||||
mov r0, r14 @ secondary fd
|
||||
swi #0 @ syscall
|
||||
.cleanup1:
|
||||
mov r7, #6 @ SYS_close
|
||||
mov r0, r10 @ primary fd
|
||||
swi #0 @ syscall
|
||||
.enter:
|
||||
mov sp, r9 @ restore original SP
|
||||
mov r0, #0 @ clear rtld_fini
|
||||
ldr r1, [r8, #4] @ branch to code
|
||||
bx r1
|
||||
|
||||
timespec:
|
||||
.long 10
|
||||
.long 10
|
||||
|
||||
@ Local Variables:
|
||||
@ asm-comment-char: 64
|
||||
@ End:
|
||||
234
exec/loader-mips64el.s
Normal file
234
exec/loader-mips64el.s
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Emacs.
|
||||
#
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License,
|
||||
# or (at your option) any later version.
|
||||
#
|
||||
# GNU Emacs is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
include(`config-mips.m4')
|
||||
|
||||
.set noreorder # delay slots managed by hand
|
||||
.set noat # no assembler macros
|
||||
.section .text
|
||||
.global __start
|
||||
__start:
|
||||
dnl li $v0, 5034 # SYS_nanosleep
|
||||
dnl dla $a0, .timespec # rqtp
|
||||
dnl li $a1, 0 # rmtp
|
||||
dnl syscall # syscall
|
||||
ld $s2, ($sp) # original stack pointer
|
||||
DADDI3( $s0, $sp, 16) # start of load area
|
||||
DADDI2( $sp, -16) # primary fd, secondary fd
|
||||
li $t0, -1 # secondary fd
|
||||
sd $t0, 8($sp) # initialize secondary fd
|
||||
.next_action:
|
||||
ld $s1, ($s0) # action number
|
||||
andi $t0, $s1, 15 # t0 = action number & 15
|
||||
beqz $t0, .open_file # open file?
|
||||
nop # delay slot
|
||||
DADDI2( $t0, -3) # t0 -= 3
|
||||
beqz $t0, .rest_of_exec # jump to code
|
||||
nop # delay slot
|
||||
li $t1, 1
|
||||
beq $t0, $t1, .do_mmap_anon # anonymous mmap?
|
||||
nop # delay slot
|
||||
.do_mmap:
|
||||
ld $t0, 8($s0) # vm address
|
||||
ld $t1, 16($s0) # file_offset
|
||||
ld $t2, 24($s0) # protection
|
||||
ld $t3, 32($s0) # length
|
||||
ld $v0, 40($s0) # flags
|
||||
ld $v1, ($sp) # primary fd
|
||||
andi $s3, $s1, 16 # s1 & 16?
|
||||
beqz $s3, .do_mmap_1 # secondary fd?
|
||||
nop # delay slot
|
||||
ld $v1, 8($sp) # secondary fd
|
||||
.do_mmap_1:
|
||||
move $a0, $t0 # syscall arg
|
||||
move $a1, $t3 # syscall arg
|
||||
move $a2, $t2 # syscall arg
|
||||
move $a3, $v0 # syscall arg
|
||||
move $a4, $v1 # syscall arg
|
||||
move $a5, $t1 # syscall arg
|
||||
li $v0, 5009 # SYS_mmap
|
||||
syscall # syscall
|
||||
bne $a3, $zero, .perror # perror?
|
||||
nop # delay slot
|
||||
ld $t1, 48($s0) # clear
|
||||
dadd $t0, $a0, $a1 # t0 = end of mapping
|
||||
dsub $t1, $t0, $t1 # t1 = t0 - clear
|
||||
.align:
|
||||
beq $t0, $t1, .continue # already finished
|
||||
nop # delay slot
|
||||
andi $t2, $t1, 7 # t1 & 7?
|
||||
bnez $t2, .filld # start filling longs
|
||||
nop # delay slot
|
||||
.filld:
|
||||
dsub $t2, $t0, $t1 # t2 = t0 - t1
|
||||
sltiu $t2, $t2, 64 # t2 < 64?
|
||||
bne $t2, $zero, .fillb # fill bytes
|
||||
nop # delay slot
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
sd $zero, ($t1) # zero doubleword
|
||||
DADDI2( $t1, 8) # next doubleword
|
||||
j .filld # fill either doubleword or byte
|
||||
nop # delay slot
|
||||
.fillb:
|
||||
beq $t0, $t1, .continue # already finished?
|
||||
nop # delay slot
|
||||
sb $zero, ($t1) # clear byte
|
||||
DADDI2( $t1, 1) # t1++
|
||||
.continue:
|
||||
DADDI2( $s0, 56) # s0 = next action
|
||||
j .next_action # next action
|
||||
nop # delay slot
|
||||
.do_mmap_anon:
|
||||
ld $t0, 8($s0) # vm address
|
||||
ld $t1, 16($s0) # file_offset
|
||||
ld $t2, 24($s0) # protection
|
||||
ld $t3, 32($s0) # length
|
||||
ld $v0, 40($s0) # flags
|
||||
li $v1, -1 # fd
|
||||
j .do_mmap_1 # do mmap
|
||||
nop # branch delay slot
|
||||
.open_file:
|
||||
li $v0, 5002 # SYS_open
|
||||
DADDI3( $a0, $s0, 8) # start of name
|
||||
move $a1, $zero # flags = O_RDONLY
|
||||
move $a2, $zero # mode = 0
|
||||
syscall # syscall
|
||||
bne $a3, $zero, .perror # perror
|
||||
nop # delay slot
|
||||
DADDI2( $s0, 8) # start of string
|
||||
move $t3, $s0 # t3 = s0
|
||||
.nextc:
|
||||
lb $t0, ($s0) # load byte
|
||||
DADDI2( $s0, 1) # s0++
|
||||
li $t1, 47 # directory separator `/'
|
||||
bne $t0, $t1, .nextc1 # is separator char?
|
||||
nop # delay slot
|
||||
move $t3, $s0 # t3 = char past separator
|
||||
.nextc1:
|
||||
bnez $t0, .nextc # next character?
|
||||
nop # delay slot
|
||||
DADDI2( $s0, 7) # adjust for round
|
||||
li $t2, -8 # t2 = -8
|
||||
and $s0, $s0, $t2 # mask for round
|
||||
andi $t0, $s1, 16 # t1 = s1 & 16
|
||||
move $t1, $sp # address of primary fd
|
||||
beqz $t0, .primary # primary fd?
|
||||
nop # delay slot
|
||||
DADDI2( $t1, 8) # address of secondary fd
|
||||
sd $v0, ($t1) # store fd
|
||||
j .next_action # next action
|
||||
nop # delay slot
|
||||
.primary:
|
||||
sd $v0, ($t1) # store fd
|
||||
li $v0, 5153 # SYS_prctl
|
||||
li $a0, 15 # PR_SET_NAME
|
||||
move $a1, $t3 # char past separator
|
||||
move $a2, $zero # a2
|
||||
move $a3, $zero # a3
|
||||
move $a4, $zero # a4
|
||||
move $a5, $zero # a5
|
||||
syscall # syscall
|
||||
j .next_action # next action
|
||||
nop # delay slot
|
||||
.perror:
|
||||
move $a0, $v0 # errno
|
||||
li $v0, 5058 # SYS_exit
|
||||
syscall # syscall
|
||||
.rest_of_exec:
|
||||
move $s1, $s2 # original SP
|
||||
ld $t0, ($s1) # argc
|
||||
dsll $t0, $t0, 3 # argc *= 3
|
||||
DADDI2( $t0, 16) # argc += 16
|
||||
dadd $s1, $s1, $t0 # s1 = start of envp
|
||||
.skipenv:
|
||||
ld $t0, ($s1) # t0 = *s1
|
||||
DADDI2( $s1, 8) # s1++
|
||||
bne $t0, $zero, .skipenv # skip again
|
||||
nop # delay slot
|
||||
dla $t3, .auxvtab # address of auxv table
|
||||
.one_auxv:
|
||||
ld $t0, ($s1) # t0 = auxv type
|
||||
li $t1, 10 # t1 = 10
|
||||
beqz $t0, .finish # is AT_IGNORE?
|
||||
nop # delay slot
|
||||
sltu $t1, $t0, $t1 # t1 = t0 < num offsets
|
||||
beqz $t1, .next # next auxv
|
||||
nop # delay slot
|
||||
dsll $t1, $t0, 2 # t1 = t0 * 4
|
||||
dadd $t1, $t3, $t1 # t1 = .auxvtab + t1
|
||||
lw $t2, ($t1) # t2 = *t1
|
||||
beqz $t2, .next # skip auxv
|
||||
nop # delay slot
|
||||
dadd $t2, $s0, $t2 # t2 = s0 + t2
|
||||
ld $t2, ($t2) # t2 = *t2
|
||||
sd $t2, 8($s1) # set auxv value
|
||||
.next:
|
||||
DADDI2( $s1, 16) # next auxv
|
||||
j .one_auxv # next auxv
|
||||
nop # delay slot
|
||||
.finish:
|
||||
ld $t0, 8($sp) # secondary fd
|
||||
li $t1, -1 # t1 = -1
|
||||
ld $s1, ($sp) # s1 = primary fd
|
||||
li $v0, 5003 # SYS_close
|
||||
beq $t0, $t2, .finish1 # secondary fd set?
|
||||
nop # delay slot
|
||||
move $a0, $t0 # secondary fd
|
||||
syscall # syscall
|
||||
li $v0, 5003 # SYS_close
|
||||
.finish1:
|
||||
move $a0, $s1 # primary fd
|
||||
syscall # syscall
|
||||
.jump:
|
||||
move $v0, $zero # rtld_fini
|
||||
ld $t0, 8($s0) # entry
|
||||
move $sp, $s2 # restore stack pointer, delay slot
|
||||
jr $t0 # enter
|
||||
nop # delay slot
|
||||
|
||||
.auxvtab:
|
||||
.long 0 # 0
|
||||
.long 0 # 1
|
||||
.long 0 # 2
|
||||
.long 40 # 3 AT_PHDR
|
||||
.long 24 # 4 AT_PHENT
|
||||
.long 32 # 5 AT_PHNUM
|
||||
.long 0 # 6
|
||||
.long 48 # 7 AT_BASE
|
||||
.long 0 # 8
|
||||
.long 16 # 9 AT_ENTRY
|
||||
|
||||
.timespec:
|
||||
.quad 10
|
||||
.quad 10
|
||||
|
||||
# Local Variables:
|
||||
# asm-comment-char: 35
|
||||
# End:
|
||||
236
exec/loader-mipsel.s
Normal file
236
exec/loader-mipsel.s
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Emacs.
|
||||
#
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License,
|
||||
# or (at your option) any later version.
|
||||
#
|
||||
# GNU Emacs is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
include(`config-mips.m4')
|
||||
|
||||
# Make sure not to use t4 through t7, in order to maintain portability
|
||||
# with N32 ABI systems.
|
||||
|
||||
.set noreorder # delay slots managed by hand
|
||||
.section .text
|
||||
.global __start
|
||||
__start:
|
||||
dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep
|
||||
dnl la $a0, .timespec # rqtp
|
||||
dnl li $a1, 0 # rmtp
|
||||
dnl syscall # syscall
|
||||
lw $s6, ($sp) # original stack pointer
|
||||
addi $s0, $sp, 8 # start of load area
|
||||
addi $sp, -8 # primary fd, secondary fd
|
||||
li $t0, -1 # secondary fd
|
||||
sw $t0, 4($sp) # initialize secondary fd
|
||||
.next_action:
|
||||
lw $s2, ($s0) # action number
|
||||
nop # delay slot
|
||||
andi $t0, $s2, 15 # t0 = s2 & 15
|
||||
beqz $t0, .open_file # open file?
|
||||
li $t1, 3 # t1 = 3, delay slot
|
||||
beq $t0, $t1, .rest_of_exec # jump to code
|
||||
li $t1, 4 # t1 = 4, delay slot
|
||||
beq $t0, $t1, .do_mmap_anon # anonymous mmap
|
||||
.do_mmap:
|
||||
lw $a0, 4($s0) # vm_address, delay slot
|
||||
lw $v1, 8($s0) # file_offset
|
||||
lw $a2, 12($s0) # protection
|
||||
lw $a1, 16($s0) # length
|
||||
lw $a3, 20($s0) # flags
|
||||
lw $v0, ($sp) # primary fd
|
||||
andi $t1, $s2, 16 # t1 = s2 & 16
|
||||
beqz $t1, .do_mmap_1 # secondary fd?
|
||||
nop # delay slot
|
||||
lw $v0, 4($sp) # secondary fd
|
||||
nop # delay slot
|
||||
.do_mmap_1:
|
||||
SYSCALL(`$v0',`$v1',`$zero',`$zero') # syscall args
|
||||
li $v0, SYSCALL_mmap # SYS_mmap
|
||||
syscall # syscall
|
||||
bne $a3, $zero, .perror # perror
|
||||
RESTORE() # delay slot, restore sp
|
||||
lw $s5, 24($s0) # clear
|
||||
add $t0, $a0, $a1 # t0 = length + vm_address, delay slot
|
||||
sub $t1, $t0, $s5 # t1 = t0 - clear
|
||||
.align:
|
||||
beq $t0, $t1, .continue # already finished?
|
||||
nop # delay slot
|
||||
andi $t2, $t1, 3 # t1 & 3?
|
||||
bnez $t2, .fillw # start filling longs
|
||||
nop # delay slot
|
||||
sb $zero, ($t1) # clear byte
|
||||
addi $t1, $t1, 1 # t1++
|
||||
j .align # continue
|
||||
nop # delay slot
|
||||
.fillw:
|
||||
sub $t2, $t0, $t1 # t2 = t0 - t1
|
||||
sltiu $t2, $t2, 32 # r2 < 32?
|
||||
bne $t2, $zero, .fillb # fill bytes
|
||||
nop # delay slot
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
sw $zero, ($t1) # zero word
|
||||
addi $t1, $t1, 4 # next word
|
||||
j .fillw # fill either word or byte
|
||||
nop # delay slot
|
||||
.fillb:
|
||||
beq $t0, $t1, .continue # already finished?
|
||||
nop # delay slot
|
||||
sb $zero, ($t1) # clear byte
|
||||
addi $t1, $t1, 1 # t1++
|
||||
.continue:
|
||||
addi $s0, $s0, 28 # s0 = next action
|
||||
j .next_action # next action
|
||||
nop # delay slot
|
||||
.do_mmap_anon:
|
||||
lw $v1, 8($s0) # file_offset
|
||||
lw $a2, 12($s0) # protection
|
||||
lw $a1, 16($s0) # length
|
||||
lw $a3, 20($s0) # flags
|
||||
li $t4, -1 # fd
|
||||
j .do_mmap_1 # do mmap
|
||||
nop # delay slot
|
||||
.open_file:
|
||||
li $v0, SYSCALL_open # SYS_open
|
||||
addi $a0, $s0, 4 # start of name
|
||||
move $a1, $zero # flags = O_RDONLY
|
||||
move $a2, $zero # mode = 0
|
||||
syscall # syscall
|
||||
bne $a3, $zero, .perror # perror
|
||||
addi $s0, $s0, 4 # start of string, delay slot
|
||||
move $t3, $s0 # t3 = char past separator
|
||||
.nextc:
|
||||
lb $t0, ($s0) # load byte
|
||||
addi $s0, $s0, 1 # s0++
|
||||
li $t1, 47 # directory separator `/'
|
||||
bne $t0, $t1, .nextc1 # is separator char?
|
||||
nop # delay slot
|
||||
move $t3, $s0 # t3 = char past separator
|
||||
.nextc1:
|
||||
bnez $t0, .nextc # next character?
|
||||
nop # delay slot
|
||||
addi $s0, $s0, 3 # adjust for round
|
||||
li $t2, -4 # t2 = -4
|
||||
and $s0, $s0, $t2 # mask for round
|
||||
andi $t0, $s2, 16 # t1 = s2 & 16
|
||||
beqz $t0, .primary # primary fd?
|
||||
move $t0, $sp # address of primary fd, delay slot
|
||||
addi $t0, $t0, 4 # address of secondary fd
|
||||
j .next_action # next action
|
||||
.primary:
|
||||
sw $v0, ($t0) # store fd, delay slot
|
||||
li $v0, SYSCALL_prctl # SYS_prctl
|
||||
li $a0, 15 # PR_SET_NAME
|
||||
move $a1, $t3 # name
|
||||
move $a2, $zero # arg1
|
||||
move $a3, $zero # arg2
|
||||
SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args
|
||||
syscall # syscall
|
||||
RESTORE() # restore sp
|
||||
j .next_action # next action
|
||||
nop # delay slot
|
||||
.perror:
|
||||
move $a0, $v0 # errno
|
||||
li $v0, SYSCALL_exit # SYS_exit
|
||||
syscall # syscall
|
||||
.rest_of_exec:
|
||||
move $s1, $s6 # s1 = original SP
|
||||
lw $t0, ($s1) # argc
|
||||
nop # delay slot
|
||||
sll $t0, $t0, 2 # argc *= 4
|
||||
addi $t0, $t0, 8 # argc += 8
|
||||
add $s1, $s1, $t0 # s1 = start of envp
|
||||
.skipenv:
|
||||
lw $t0, ($s1) # t0 = *s1
|
||||
addi $s1, $s1, 4 # s1++
|
||||
bne $t0, $zero, .skipenv # skip again
|
||||
nop # delay slot
|
||||
la $s2, .auxvtab # address of auxv table
|
||||
.one_auxv:
|
||||
lw $t0, ($s1) # t0 = auxv type
|
||||
li $t1, 10 # t1 = 10, delay slot
|
||||
beqz $t0, .finish # is AT_IGNORE?
|
||||
sltu $t1, $t0, $t1 # t1 = t0 < num offsets, delay slot
|
||||
beq $t1, $zero, .next # next auxv
|
||||
sll $t1, $t0, 2 # t1 = t0 * 4, delay slot
|
||||
add $t1, $s2, $t1 # t1 = .auxvtab + t1
|
||||
lw $t2, ($t1) # t2 = *t1
|
||||
nop # delay slot
|
||||
beqz $t2, .next # skip auxv
|
||||
add $t2, $s0, $t2 # t2 = s0 + t2
|
||||
lw $t2, ($t2) # t2 = *t2
|
||||
nop # delay slot
|
||||
sw $t2, 4($s1) # set auxv value
|
||||
.next:
|
||||
addi $s1, $s1, 8 # next auxv
|
||||
j .one_auxv # next auxv
|
||||
nop # delay slot
|
||||
.finish:
|
||||
lw $t0, 4($sp) # secondary fd
|
||||
lw $s1, ($sp) # primary fd, delay slot, preserved
|
||||
li $t2, -1 # immediate -1
|
||||
beq $t0, $t2, .finish1 # secondary fd set?
|
||||
li $v0, SYSCALL_close # SYS_close, delay slot
|
||||
move $a0, $t0 # fd
|
||||
syscall # syscall
|
||||
li $v0, SYSCALL_close # SYS_close
|
||||
.finish1:
|
||||
move $a0, $s1 # primary fd
|
||||
syscall # syscall
|
||||
li $v0, SYSCALL_prctl # SYS_prctl
|
||||
li $a0, 45 # PR_SET_FP_MODE
|
||||
lw $a1, 28($s0) # fpu_mode
|
||||
move $a2, $zero # arg3
|
||||
move $a3, $zero # arg4
|
||||
SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args
|
||||
syscall # syscall
|
||||
RESTORE() # restore sp
|
||||
.jump:
|
||||
move $v0, $zero # rtld_fini
|
||||
lw $t0, 4($s0) # entry
|
||||
move $sp, $s6 # restore stack pointer, delay slot
|
||||
jr $t0 # enter
|
||||
nop # delay slot
|
||||
|
||||
.auxvtab:
|
||||
.long 0 # 0
|
||||
.long 0 # 1
|
||||
.long 0 # 2
|
||||
.long 20 # 3 AT_PHDR
|
||||
.long 12 # 4 AT_PHENT
|
||||
.long 16 # 5 AT_PHNUM
|
||||
.long 0 # 6
|
||||
.long 24 # 7 AT_BASE
|
||||
.long 0 # 8
|
||||
.long 8 # 9 AT_ENTRY
|
||||
|
||||
.timespec:
|
||||
.long 10
|
||||
.long 10
|
||||
|
||||
# Local Variables:
|
||||
# asm-comment-char: 35
|
||||
# End:
|
||||
203
exec/loader-x86.s
Normal file
203
exec/loader-x86.s
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
define(`CC', `
|
||||
dnl')
|
||||
|
||||
CC Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
CC
|
||||
CC This file is part of GNU Emacs.
|
||||
CC
|
||||
CC GNU Emacs is free software: you can redistribute it and/or modify
|
||||
CC it under the terms of the GNU General Public License as published
|
||||
CC by the Free Software Foundation, either version 3 of the License,
|
||||
CC or (at your option) any later version.
|
||||
CC
|
||||
CC GNU Emacs is distributed in the hope that it will be useful, but
|
||||
CC WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
CC General Public License for more details.
|
||||
CC
|
||||
CC You should have received a copy of the GNU General Public License
|
||||
CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
_start:
|
||||
dnl movl $162, %eax CC SYS_nanosleep
|
||||
dnl leal timespec, %ebx
|
||||
dnl xorl %ecx, %ecx
|
||||
dnl int $0x80
|
||||
leal 8(%esp), %ebp CC ebp = start of load area
|
||||
subl $8, %esp CC (%esp) = primary fd, 4(%esp) = secondary fd
|
||||
movl $-1, 4(%esp)
|
||||
.next_action:
|
||||
movl (%ebp), %edx CC edx = action number
|
||||
andl $-17, %edx
|
||||
cmpl $0, %edx CC open file?
|
||||
je .open_file
|
||||
cmpl $3, %edx CC jump?
|
||||
je .rest_of_exec
|
||||
cmpl $4, %edx CC anonymous mmap?
|
||||
je .do_mmap_anon
|
||||
.do_mmap:
|
||||
subl $24, %esp
|
||||
movl $90, %eax CC SYS_old_mmap
|
||||
movl %esp, %ebx
|
||||
movl 4(%ebp), %ecx CC address
|
||||
movl %ecx, (%esp)
|
||||
movl 16(%ebp), %ecx CC length
|
||||
movl %ecx, 4(%esp)
|
||||
movl 12(%ebp), %ecx CC protection
|
||||
movl %ecx, 8(%esp)
|
||||
movl 20(%ebp), %ecx CC flags
|
||||
movl %ecx, 12(%esp)
|
||||
testl $16, (%ebp) CC primary?
|
||||
movl 28(%esp), %ecx
|
||||
cmovzl 24(%esp), %ecx
|
||||
movl %ecx, 16(%esp) CC fd
|
||||
movl 8(%ebp), %ecx CC offset
|
||||
movl %ecx, 20(%esp)
|
||||
.do_mmap_1:
|
||||
int $0x80
|
||||
addl $24, %esp CC restore esp
|
||||
cmpl $-1, %eax CC mmap failed?
|
||||
je .perror
|
||||
movl 24(%ebp), %ecx CC clear
|
||||
testl %ecx, %ecx
|
||||
jz .continue
|
||||
movl 4(%ebp), %esi CC start of mapping
|
||||
addl 16(%ebp), %esi CC end of mapping
|
||||
subl %ecx, %esi CC start of clear area
|
||||
.again:
|
||||
testl %ecx, %ecx
|
||||
jz .continue
|
||||
subl $1, %ecx
|
||||
movb $0, (%esi, %ecx, 1)
|
||||
jmp .again
|
||||
.continue:
|
||||
leal 28(%ebp), %ebp
|
||||
jmp .next_action
|
||||
.do_mmap_anon:
|
||||
subl $24, %esp
|
||||
movl $90, %eax CC SYS_old_mmap
|
||||
movl %esp, %ebx
|
||||
movl 4(%ebp), %ecx CC address
|
||||
movl %ecx, (%esp)
|
||||
movl 16(%ebp), %ecx CC length
|
||||
movl %ecx, 4(%esp)
|
||||
movl 12(%ebp), %ecx CC protection
|
||||
movl %ecx, 8(%esp)
|
||||
movl 20(%ebp), %ecx CC flags
|
||||
movl %ecx, 12(%esp)
|
||||
movl $-1, 16(%esp) CC fd
|
||||
movl 8(%ebp), %ecx CC offset
|
||||
movl %ecx, 20(%esp)
|
||||
jmp .do_mmap_1
|
||||
.open_file:
|
||||
movl $5, %eax CC SYS_open
|
||||
leal 4(%ebp), %ebx CC ebx = %esp + 8
|
||||
pushl %ebx
|
||||
xorl %ecx, %ecx CC flags = O_RDONLY
|
||||
xorl %edx, %edx CC mode = 0
|
||||
int $0x80
|
||||
cmpl $-1, %eax CC open failed?
|
||||
jle .perror
|
||||
movl %ebp, %esi CC (esi) = original action number
|
||||
popl %ebp CC ebp = start of string
|
||||
movl %ebp, %ecx CC char past separator
|
||||
decl %ebp
|
||||
.nextc:
|
||||
incl %ebp
|
||||
movb (%ebp), %dl CC dl = *ebp
|
||||
cmpb $47, %dl CC dl == '\?'?
|
||||
jne .nextc1
|
||||
leal 1(%ebp), %ecx CC ecx = char past separator
|
||||
.nextc1:
|
||||
cmpb $0, %dl CC dl == 0?
|
||||
jne .nextc
|
||||
addl $4, %ebp CC adjust past ebp prior to rounding
|
||||
andl $-4, %ebp CC round ebp up to the next long
|
||||
testl $16, (%esi) CC original action number & 16?
|
||||
jz .primary
|
||||
movl %eax, 4(%esp) CC secondary fd = eax
|
||||
jmp .next_action
|
||||
.primary:
|
||||
pushl %ebp
|
||||
xorl %esi, %esi CC arg3
|
||||
movl %eax, 4(%esp) CC primary fd = eax
|
||||
xorl %edx, %edx CC arg2
|
||||
movl $15, %ebx CC PR_SET_NAME, arg1 = ecx
|
||||
xorl %edi, %edi CC arg4
|
||||
movl $172, %eax CC SYS_prctl
|
||||
xorl %ebp, %ebp CC arg5
|
||||
int $0x80 CC syscall
|
||||
popl %ebp
|
||||
jmp .next_action
|
||||
.perror:
|
||||
movl %eax, %ebx
|
||||
negl %ebx
|
||||
movl $1, %eax
|
||||
int $0x80
|
||||
.rest_of_exec:
|
||||
movl 8(%esp), %ecx CC ecx = original stack pointer
|
||||
movl (%ecx), %esi CC esi = argc
|
||||
leal 8(%ecx, %esi, 4), %ecx CC ecx = start of environ
|
||||
.skip_environ:
|
||||
movl (%ecx), %esi CC envp[N]
|
||||
addl $4, %ecx
|
||||
testl %esi, %esi CC envp[n] ?
|
||||
jnz .skip_environ CC otherwise, esi is now at the start of auxv
|
||||
.one_auxv:
|
||||
movl (%ecx), %esi CC auxv type
|
||||
leal 8(%ecx), %ecx CC skip to next auxv
|
||||
testl %esi, %esi CC is 0?
|
||||
jz .cleanup
|
||||
cmpl $3, %esi CC is AT_PHDR
|
||||
je .replace_phdr
|
||||
cmpl $4, %esi CC is AT_PHENT?
|
||||
je .replace_phent
|
||||
cmpl $5, %esi CC is AT_PHNUM?
|
||||
je .replace_phnum
|
||||
cmpl $9, %esi CC is AT_ENTRY?
|
||||
je .replace_entry
|
||||
cmpl $7, %esi CC is AT_BASE
|
||||
je .replace_base
|
||||
jmp .one_auxv
|
||||
.replace_phdr:
|
||||
movl 20(%ebp), %esi
|
||||
movl %esi, -4(%ecx)
|
||||
jmp .one_auxv
|
||||
.replace_phent:
|
||||
movl 12(%ebp), %esi
|
||||
movl %esi, -4(%ecx)
|
||||
jmp .one_auxv
|
||||
.replace_phnum:
|
||||
movl 16(%ebp), %esi
|
||||
movl %esi, -4(%ecx)
|
||||
jmp .one_auxv
|
||||
.replace_entry:
|
||||
movl 8(%ebp), %esi
|
||||
movl %esi, -4(%ecx)
|
||||
jmp .one_auxv
|
||||
.replace_base:
|
||||
movl 24(%ebp), %esi
|
||||
movl %esi, -4(%ecx)
|
||||
jmp .one_auxv
|
||||
.cleanup:
|
||||
movl $6, %eax CC SYS_close
|
||||
cmpl $-1, 4(%esp) CC see if interpreter fd is set
|
||||
je .cleanup_1
|
||||
movl 4(%esp), %ebx
|
||||
int $0x80
|
||||
movl $6, %eax CC SYS_close
|
||||
.cleanup_1:
|
||||
movl (%esp), %ebx
|
||||
int $0x80
|
||||
.enter:
|
||||
pushl $0
|
||||
popfl CC restore floating point state
|
||||
movl 8(%esp), %esp CC restore initial stack pointer
|
||||
xorl %edx, %edx CC clear rtld_fini
|
||||
jmpl *4(%ebp) CC entry
|
||||
|
||||
timespec:
|
||||
.long 10
|
||||
.long 10
|
||||
195
exec/loader-x86_64.s
Normal file
195
exec/loader-x86_64.s
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
define(`CC', `
|
||||
dnl')
|
||||
|
||||
CC Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
CC
|
||||
CC This file is part of GNU Emacs.
|
||||
CC
|
||||
CC GNU Emacs is free software: you can redistribute it and/or modify
|
||||
CC it under the terms of the GNU General Public License as published
|
||||
CC by the Free Software Foundation, either version 3 of the License,
|
||||
CC or (at your option) any later version.
|
||||
CC
|
||||
CC GNU Emacs is distributed in the hope that it will be useful, but
|
||||
CC WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
CC General Public License for more details.
|
||||
CC
|
||||
CC You should have received a copy of the GNU General Public License
|
||||
CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
_start:
|
||||
dnl movq $35, %rax CC SYS_nanosleep
|
||||
dnl leaq timespec(%rip), %rdi
|
||||
dnl xorq %rsi, %rsi
|
||||
dnl syscall
|
||||
popq %r13 CC original SP
|
||||
popq %r15 CC size of load area.
|
||||
movq $-1, %r12 CC r12 is the interpreter fd
|
||||
.next_action:
|
||||
movq (%rsp), %r14 CC action number
|
||||
movq %r14, %r15 CC original action number
|
||||
andq $-17, %r14
|
||||
cmpq $0, %r14 CC open file?
|
||||
je .open_file
|
||||
cmpq $3, %r14 CC jump?
|
||||
je .rest_of_exec
|
||||
cmpq $4, %r14 CC anonymous mmap?
|
||||
je .do_mmap_anon
|
||||
.do_mmap:
|
||||
movq $9, %rax CC SYS_mmap
|
||||
movq 8(%rsp), %rdi CC address
|
||||
movq 16(%rsp), %r9 CC offset
|
||||
movq 24(%rsp), %rdx CC protection
|
||||
movq 32(%rsp), %rsi CC length
|
||||
movq 40(%rsp), %r10 CC flags
|
||||
CC set r8 to the primary fd unless r15 & 16
|
||||
testq $16, %r15
|
||||
movq %r12, %r8
|
||||
cmovzq %rbx, %r8
|
||||
.do_mmap_1:
|
||||
syscall
|
||||
cmpq $-1, %rax CC mmap failed
|
||||
je .perror
|
||||
movq 48(%rsp), %r9 CC clear
|
||||
testq %r9, %r9
|
||||
jz .continue
|
||||
movq 8(%rsp), %r10 CC start of mapping
|
||||
addq 32(%rsp), %r10 CC end of mapping
|
||||
subq %r9, %r10 CC start of clear area
|
||||
.again:
|
||||
testq %r9, %r9
|
||||
jz .continue
|
||||
subq $1, %r9
|
||||
movb $0, (%r10, %r9, 1)
|
||||
jmp .again
|
||||
.continue:
|
||||
leaq 56(%rsp), %rsp
|
||||
jmp .next_action
|
||||
.do_mmap_anon:
|
||||
movq $9, %rax CC SYS_mmap
|
||||
movq 8(%rsp), %rdi CC address
|
||||
movq 16(%rsp), %r9 CC offset
|
||||
movq 24(%rsp), %rdx CC protection
|
||||
movq 32(%rsp), %rsi CC length
|
||||
movq 40(%rsp), %r10 CC flags
|
||||
movq $-1, %r8 CC -1
|
||||
jmp .do_mmap_1
|
||||
.open_file:
|
||||
movq $2, %rax CC SYS_open
|
||||
leaq 8(%rsp), %rdi CC rdi = %rsp + 8
|
||||
xorq %rsi, %rsi CC flags = O_RDONLY
|
||||
xorq %rdx, %rdx CC mode = 0
|
||||
syscall
|
||||
cmpq $-1, %rax CC open failed
|
||||
jle .perror
|
||||
movq %rdi, %rsp CC rsp = start of string
|
||||
subq $1, %rsp
|
||||
movq %rsp, %r14 CC r14 = start of string
|
||||
.nextc:
|
||||
addq $1, %rsp
|
||||
movb (%rsp), %dil CC rdi = *rsp
|
||||
cmpb $47, %dil CC *rsp == '/'?
|
||||
jne .nextc1
|
||||
movq %rsp, %r14 CC r14 = rsp
|
||||
addq $1, %r14 CC r14 = char past separator
|
||||
.nextc1:
|
||||
cmpb $0, %dil CC *rsp == 0?
|
||||
jne .nextc
|
||||
addq $8, %rsp CC adjust past rsp prior to rounding
|
||||
andq $-8, %rsp CC round rsp up to the next quad
|
||||
testq $16, %r15 CC r15 & 16?
|
||||
jz .primary
|
||||
movq %rax, %r12 CC otherwise, move fd to r12
|
||||
jmp .next_action
|
||||
.primary:
|
||||
movq %rax, %rbx CC if not, move fd to rbx
|
||||
movq $157, %rax CC SYS_prctl
|
||||
movq $15, %rdi CC PR_SET_NAME
|
||||
movq %r14, %rsi CC arg1
|
||||
xorq %rdx, %rdx CC arg2
|
||||
xorq %r10, %r10 CC arg3
|
||||
xorq %r8, %r8 CC arg4
|
||||
xorq %r9, %r9 CC arg5
|
||||
syscall
|
||||
jmp .next_action
|
||||
.perror:
|
||||
movq %rax, %r12 CC error code
|
||||
negq %r12
|
||||
movq $1, %rax CC SYS_write
|
||||
movq $1, %rdi CC stdout
|
||||
leaq error(%rip), %rsi CC buffer
|
||||
movq $23, %rdx CC count
|
||||
syscall
|
||||
movq $60, %rax CC SYS_exit
|
||||
movq %r12, %rdi CC code
|
||||
syscall
|
||||
.rest_of_exec: CC rsp now points to six quads:
|
||||
movq %rsp, %r8 CC now, they are r8
|
||||
movq %r13, %rsp CC restore SP
|
||||
popq %r10 CC argc
|
||||
leaq 8(%rsp,%r10,8), %rsp CC now at start of environ
|
||||
.skip_environ:
|
||||
popq %r10 CC envp[N]
|
||||
testq %r10, %r10 CC envp[n]?
|
||||
jnz .skip_environ CC otherwise, rsp is now at the start of auxv
|
||||
.one_auxv:
|
||||
popq %rcx CC auxv type
|
||||
addq $8, %rsp CC skip value
|
||||
testq %rcx, %rcx CC is 0?
|
||||
jz .cleanup
|
||||
cmpq $3, %rcx CC is AT_PHDR?
|
||||
je .replace_phdr
|
||||
cmpq $4, %rcx CC is AT_PHENT?
|
||||
je .replace_phent
|
||||
cmpq $5, %rcx CC is AT_PHNUM?
|
||||
je .replace_phnum
|
||||
cmpq $9, %rcx CC is AT_ENTRY?
|
||||
je .replace_entry
|
||||
cmpq $7, %rcx CC is AT_BASE?
|
||||
je .replace_base
|
||||
jmp .one_auxv
|
||||
.replace_phdr:
|
||||
movq 40(%r8), %r9
|
||||
movq %r9, -8(%rsp) CC set at_phdr
|
||||
jmp .one_auxv
|
||||
.replace_phent:
|
||||
movq 24(%r8), %r9
|
||||
movq %r9, -8(%rsp) CC set at_phent
|
||||
jmp .one_auxv
|
||||
.replace_phnum:
|
||||
movq 32(%r8), %r9
|
||||
movq %r9, -8(%rsp) CC set at_phnum
|
||||
jmp .one_auxv
|
||||
.replace_entry:
|
||||
movq 16(%r8), %r9
|
||||
movq %r9, -8(%rsp) CC set at_entry
|
||||
jmp .one_auxv
|
||||
.replace_base:
|
||||
movq 48(%r8), %r9
|
||||
movq %r9, -8(%rsp) CC set at_base
|
||||
jmp .one_auxv
|
||||
.cleanup:
|
||||
movq $3, %rax CC SYS_close
|
||||
cmpq $-1, %r12 CC see if interpreter fd is set
|
||||
je .cleanup_1
|
||||
movq %r12, %rdi
|
||||
syscall
|
||||
movq $3, %rax CC SYS_close
|
||||
.cleanup_1:
|
||||
movq %rbx, %rdi
|
||||
syscall
|
||||
.enter:
|
||||
pushq $0
|
||||
popfq CC clear FP state
|
||||
movq %r13, %rsp CC restore SP
|
||||
xorq %rdx, %rdx CC clear rtld_fini
|
||||
jmpq *8(%r8) CC entry
|
||||
|
||||
error:
|
||||
.ascii "_start: internal error."
|
||||
timespec:
|
||||
.quad 10
|
||||
.quad 10
|
||||
43
exec/mipsel-user.h
Normal file
43
exec/mipsel-user.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
|
||||
#ifndef _MIPSEL_USER_H_
|
||||
#define _MIPSEL_USER_H_
|
||||
|
||||
#include <sys/user.h>
|
||||
|
||||
#ifndef ELF_NGREG
|
||||
#define ELF_NGREG 45
|
||||
#endif /* ELF_NGREG */
|
||||
|
||||
|
||||
|
||||
/* This file defines a structure containing user mode general purpose
|
||||
registers on 32-bit mipsel systems. */
|
||||
|
||||
struct mipsel_regs
|
||||
{
|
||||
/* General purpose registers. */
|
||||
uint64_t gregs[ELF_NGREG];
|
||||
};
|
||||
|
||||
#endif /* _MIPSEL_USER_H_ */
|
||||
|
||||
289
exec/mipsfpu.c
Normal file
289
exec/mipsfpu.c
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "mipsfpu.h"
|
||||
|
||||
|
||||
|
||||
/* OABI MIPS systems support several different modes of execution.
|
||||
Each mode differs in the size and utilization of the hardware
|
||||
floating-point registers.
|
||||
|
||||
Linux normally sets the floating point mode to one appropriate for
|
||||
execution, taking into account the floating point modes of the
|
||||
interpreter and executable binaries. However, this logic is
|
||||
forsaken when the `execve' system call is overwritten.
|
||||
|
||||
Thus, the correct floating point mode must be determined and set
|
||||
within the loader binary. */
|
||||
|
||||
|
||||
|
||||
/* Various constants used throughout this code. */
|
||||
|
||||
#define MIPS_ABI_FP_ANY 0 /* FP ABI doesn't matter */
|
||||
#define MIPS_ABI_FP_DOUBLE 1 /* -mdouble-float */
|
||||
#define MIPS_ABI_FP_SINGLE 2 /* -msingle-float */
|
||||
#define MIPS_ABI_FP_SOFT 3 /* -msoft-float */
|
||||
#define MIPS_ABI_FP_OLD_64 4 /* -mips32r2 -mfp64 */
|
||||
#define MIPS_ABI_FP_XX 5 /* -mfpxx */
|
||||
#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */
|
||||
#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */
|
||||
|
||||
#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */
|
||||
#define EF_MIPS_PIC 2 /* Contains PIC code. */
|
||||
#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */
|
||||
#define EF_MIPS_XGOT 8
|
||||
#define EF_MIPS_64BIT_WHIRL 16
|
||||
#define EF_MIPS_ABI2 32
|
||||
#define EF_MIPS_ABI_ON32 64
|
||||
#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */
|
||||
#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */
|
||||
#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */
|
||||
|
||||
|
||||
|
||||
/* Structure describing the requirements of a single floating-point
|
||||
ABI. */
|
||||
|
||||
struct mode_description
|
||||
{
|
||||
/* Whether or not the ABI only executes single precision
|
||||
instructions, and can operate in both 32-bit or 64-bit floating
|
||||
point mode. */
|
||||
bool single;
|
||||
|
||||
/* Whether or not the ABI performs floating point operations in
|
||||
software, using integer registers. */
|
||||
bool soft;
|
||||
|
||||
/* Whether or not the ABI requires the use of 64-bit floating point
|
||||
registers. */
|
||||
bool fr1;
|
||||
|
||||
/* Whether or not the ABI requires the use of 64-bit floating point
|
||||
registers on NABI systems, and 32-bit ones on OABI systems. */
|
||||
bool frdefault;
|
||||
|
||||
/* Whether or not this ABI requires single precision floating point
|
||||
emulation. */
|
||||
bool fre;
|
||||
};
|
||||
|
||||
static struct mode_description fpu_reqs[] =
|
||||
{
|
||||
[MIPS_ABI_FP_ANY] = { true, true, true, true, true, },
|
||||
[MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true, },
|
||||
[MIPS_ABI_FP_SINGLE] = { true, false, false, false, false, },
|
||||
[MIPS_ABI_FP_SOFT] = { false, true, false, false, false, },
|
||||
[MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
|
||||
[MIPS_ABI_FP_XX] = { false, false, true, true, true, },
|
||||
[MIPS_ABI_FP_64] = { false, false, true, false, false, },
|
||||
[MIPS_ABI_FP_64A] = { false, false, true, false, true, },
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Return whether or not the given floating-point ABI is valid. */
|
||||
|
||||
static bool
|
||||
valid_abi_p (int abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
case MIPS_ABI_FP_ANY:
|
||||
case MIPS_ABI_FP_DOUBLE:
|
||||
case MIPS_ABI_FP_SINGLE:
|
||||
case MIPS_ABI_FP_SOFT:
|
||||
case MIPS_ABI_FP_OLD_64:
|
||||
case MIPS_ABI_FP_XX:
|
||||
case MIPS_ABI_FP_64:
|
||||
case MIPS_ABI_FP_64A:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the floating point mode appropriate for the specified
|
||||
floating point ABI. */
|
||||
|
||||
static int
|
||||
fp_mode_for_abi (int abi)
|
||||
{
|
||||
struct mode_description *desc;
|
||||
|
||||
desc = &fpu_reqs[abi];
|
||||
|
||||
if (desc->fre)
|
||||
return FP_FRE;
|
||||
else if (desc->fr1)
|
||||
return FP_FR1;
|
||||
|
||||
return FP_FR0;
|
||||
}
|
||||
|
||||
/* Determine whether or not the CPU is capable of operating in FR0
|
||||
floating point mode. */
|
||||
|
||||
bool
|
||||
cpu_supports_fr0_p (void)
|
||||
{
|
||||
#if defined __mips_isa_rev && __mips_isa_rev >= 6
|
||||
return true;
|
||||
#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
|
||||
return false;
|
||||
#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
|
||||
}
|
||||
|
||||
/* Determine the FPU mode for the executable whose ELF header is
|
||||
HEADER. If INTERPRETER is non-NULL, also take an interpreter whose
|
||||
header is INTERPRETER into account.
|
||||
|
||||
ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
|
||||
header, and ABIFLAGS1 should be that of INTERPRETER, if set. Both
|
||||
fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
|
||||
that case, use HEADER->e_flags to determine the ABI instead.
|
||||
|
||||
Return the FPU mode in *MODE. Value is 0 upon success, 1
|
||||
otherwise, with errno set. */
|
||||
|
||||
int
|
||||
determine_fpu_mode (elf_header *header, elf_header *interpreter,
|
||||
int *mode, struct mips_elf_abi_flags *abiflags,
|
||||
struct mips_elf_abi_flags *abiflags1)
|
||||
{
|
||||
int exec_abi, interpreter_abi;
|
||||
struct mode_description *exec_desc, *interpreter_desc, common;
|
||||
|
||||
/* Figure out the executable's floating point ABI. First, consult
|
||||
header->e_flags, and use the old 64-bit floating point ABI if it
|
||||
is specified. */
|
||||
|
||||
exec_abi = MIPS_ABI_FP_ANY;
|
||||
|
||||
/* First, check HEADER->e_flags. */
|
||||
|
||||
if (header->e_flags & EF_MIPS_FP64)
|
||||
exec_abi = MIPS_ABI_FP_OLD_64;
|
||||
|
||||
/* Next, use ABIFLAGS if it exists. */
|
||||
|
||||
if (abiflags && valid_abi_p (abiflags->fp_abi))
|
||||
exec_abi = abiflags->fp_abi;
|
||||
else if (abiflags)
|
||||
{
|
||||
errno = ENOEXEC;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Now determine that of the interpreter. */
|
||||
|
||||
interpreter_abi = MIPS_ABI_FP_ANY;
|
||||
|
||||
if (interpreter)
|
||||
{
|
||||
if (interpreter->e_flags & EF_MIPS_FP64)
|
||||
interpreter_abi = MIPS_ABI_FP_OLD_64;
|
||||
|
||||
if (abiflags1 && valid_abi_p (abiflags->fp_abi))
|
||||
interpreter_abi = abiflags->fp_abi;
|
||||
else if (abiflags1)
|
||||
{
|
||||
errno = ELIBBAD;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no interpreter flag is set, just return that of the
|
||||
executable. */
|
||||
|
||||
if (!interpreter)
|
||||
{
|
||||
*mode = fp_mode_for_abi (exec_abi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, compare both ABIs and try to find one which will run
|
||||
both kinds of code.
|
||||
|
||||
First, see if there's an easy way out: both ABIs are identical,
|
||||
or one ABI is MIPS_ABI_FP_ANY. */
|
||||
|
||||
if (exec_abi == interpreter_abi)
|
||||
{
|
||||
*mode = fp_mode_for_abi (exec_abi);
|
||||
return 0;
|
||||
}
|
||||
else if (exec_abi == MIPS_ABI_FP_ANY)
|
||||
{
|
||||
*mode = fp_mode_for_abi (interpreter_abi);
|
||||
return 0;
|
||||
}
|
||||
else if (interpreter_abi == MIPS_ABI_FP_ANY)
|
||||
{
|
||||
*mode = fp_mode_for_abi (exec_abi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If that doesn't work, compare various characteristics of both
|
||||
ABIs and select an appropriate floating point mode. */
|
||||
|
||||
exec_desc = &fpu_reqs[exec_abi];
|
||||
interpreter_desc = &fpu_reqs[interpreter_abi];
|
||||
|
||||
/* Merge both sets of requirements. */
|
||||
common.single = exec_desc->single && interpreter_desc->single;
|
||||
common.soft = exec_desc->soft && interpreter_desc->soft;
|
||||
common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
|
||||
common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
|
||||
common.fre = exec_desc->fre && interpreter_desc->fre;
|
||||
|
||||
/* Default to a mode capable of running code expecting 32-bit
|
||||
registers. */
|
||||
|
||||
if (!(header->e_flags & EF_MIPS_ABI2))
|
||||
*mode = FP_FR0;
|
||||
else
|
||||
/* But in this case, use FR1. */
|
||||
*mode = FP_FR1;
|
||||
|
||||
if (common.fre && !common.frdefault && !common.fr1)
|
||||
/* Floating point emulation mode is required. */
|
||||
*mode = FP_FRE;
|
||||
else if ((common.fr1 && common.frdefault)
|
||||
|| (common.single && !common.frdefault)
|
||||
|| common.fr1)
|
||||
/* 64-bit mode is required. */
|
||||
*mode = FP_FR1;
|
||||
else if (!common.fre && !common.frdefault
|
||||
&& !common.fr1 && !common.single
|
||||
&& !common.soft)
|
||||
{
|
||||
/* The floating point modes specified are incompatible. */
|
||||
errno = ELIBBAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
exec/mipsfpu.h
Normal file
82
exec/mipsfpu.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
|
||||
#ifndef _MIPSFPU_H_
|
||||
#define _MIPSFPU_H_
|
||||
|
||||
#include "exec.h"
|
||||
|
||||
struct mips_elf_abi_flags
|
||||
{
|
||||
/* Version of flags structure. */
|
||||
uint16_t version;
|
||||
|
||||
/* The level of the ISA: 1-5, 32, 64. */
|
||||
uint8_t isa_level;
|
||||
|
||||
/* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
|
||||
uint8_t isa_rev;
|
||||
|
||||
/* The size of general purpose registers. */
|
||||
uint8_t gpr_size;
|
||||
|
||||
/* The size of co-processor 1 registers. */
|
||||
uint8_t cpr1_size;
|
||||
|
||||
/* The size of co-processor 2 registers. */
|
||||
uint8_t cpr2_size;
|
||||
|
||||
/* The floating-point ABI. */
|
||||
uint8_t fp_abi;
|
||||
|
||||
/* Mask of processor-specific extensions. */
|
||||
uint32_t isa_ext;
|
||||
|
||||
/* Mask of ASEs used. */
|
||||
uint32_t ases;
|
||||
|
||||
/* Mask of general flags. */
|
||||
uint32_t flags1;
|
||||
|
||||
/* Mask of general flags. */
|
||||
uint32_t flags2;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Floating point modes. */
|
||||
|
||||
#define FP_FR0 0
|
||||
#define FP_FR1 1
|
||||
#define FP_FRE 3
|
||||
|
||||
|
||||
|
||||
/* Defined in mipsfpu.c. */
|
||||
|
||||
extern bool cpu_supports_fr0_p (void);
|
||||
extern int determine_fpu_mode (elf_header *, elf_header *,
|
||||
int *, struct mips_elf_abi_flags *,
|
||||
struct mips_elf_abi_flags *);
|
||||
|
||||
|
||||
|
||||
#endif /* _MIPSFPU_H_ */
|
||||
105
exec/test.c
Normal file
105
exec/test.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* Program execution for Emacs.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "exec.h"
|
||||
|
||||
|
||||
|
||||
static void
|
||||
print_usage (void)
|
||||
{
|
||||
fprintf (stderr, "test loader-name program [args...]\n"
|
||||
"Run the given program using the specified loader.\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern char **environ;
|
||||
|
||||
/* This program uses libexec to wrap the execution of a child
|
||||
process. */
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
pid_t pid, child;
|
||||
int sig;
|
||||
sigset_t sigset;
|
||||
|
||||
/* Check that there are a sufficient number of arguments. */
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
print_usage ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
exec_init (argv[1]);
|
||||
|
||||
/* Block SIGCHLD to avoid reentrant modification of the child
|
||||
process list. */
|
||||
|
||||
sigemptyset (&sigset);
|
||||
sigaddset (&sigset, SIGCHLD);
|
||||
sigprocmask (SIG_BLOCK, &sigset, NULL);
|
||||
|
||||
if (!(pid = fork ()))
|
||||
{
|
||||
tracing_execve (argv[2], argv + 2, environ);
|
||||
fprintf (stderr, "tracing_execve: %s\n",
|
||||
strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
else if (after_fork (pid))
|
||||
{
|
||||
fprintf (stderr, "after_fork: %s\n",
|
||||
strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Now start waiting for child processes to exit. */
|
||||
|
||||
while (true)
|
||||
{
|
||||
child = exec_waitpid (-1, &sig, 0);
|
||||
|
||||
/* If pid is -1, a system call has been handled. */
|
||||
|
||||
if (child == -1)
|
||||
continue;
|
||||
|
||||
/* If the main process exits, then exit as well. */
|
||||
|
||||
if (child == pid && !WIFSTOPPED (sig))
|
||||
return (WIFEXITED (sig)
|
||||
? WEXITSTATUS (sig)
|
||||
: WTERMSIG (sig));
|
||||
}
|
||||
}
|
||||
1420
exec/trace.c
Normal file
1420
exec/trace.c
Normal file
File diff suppressed because it is too large
Load diff
201
java/AndroidManifest.xml.in
Normal file
201
java/AndroidManifest.xml.in
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
<!-- @configure_input@
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. -->
|
||||
|
||||
<!-- targetSandboxVersion must be 1. Otherwise, fascist security
|
||||
restrictions prevent Emacs from making HTTP connections. -->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.gnu.emacs"
|
||||
android:targetSandboxVersion="1"
|
||||
android:installLocation="auto"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:versionCode="@emacs_major_version@"
|
||||
android:versionName="@version@">
|
||||
|
||||
<!-- Paste in every permission in existence so Emacs can do
|
||||
anything. -->
|
||||
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SMS"/>
|
||||
<uses-permission android:name="android.permission.READ_SMS"/>
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.TRANSMIT_IR" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- This is required on Android 11 or later to access /sdcard. -->
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
|
||||
android:targetSdkVersion="33"/>
|
||||
|
||||
<application android:name="org.gnu.emacs.EmacsApplication"
|
||||
android:label="Emacs"
|
||||
android:icon="@drawable/emacs"
|
||||
android:hardwareAccelerated="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/EmacsStyle"
|
||||
android:debuggable="@ANDROID_DEBUGGABLE@"
|
||||
@ANDROID_SHARED_USER_ID@
|
||||
android:extractNativeLibs="true">
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsOpenActivity"
|
||||
android:taskAffinity="open.dialog"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true">
|
||||
|
||||
<!-- Allow Emacs to open all kinds of files known to Android. -->
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.intent.action.EDIT"/>
|
||||
<action android:name="android.intent.action.PICK"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:mimeType="image/aces"/>
|
||||
<data android:mimeType="image/avci"/>
|
||||
<data android:mimeType="image/avcs"/>
|
||||
<data android:mimeType="image/avif"/>
|
||||
<data android:mimeType="image/bmp"/>
|
||||
<data android:mimeType="image/cgm"/>
|
||||
<data android:mimeType="image/dicom-rle"/>
|
||||
<data android:mimeType="image/dpx"/>
|
||||
<data android:mimeType="image/emf"/>
|
||||
<data android:mimeType="image/example"/>
|
||||
<data android:mimeType="image/fits"/>
|
||||
<data android:mimeType="image/g3fax"/>
|
||||
<data android:mimeType="image/heic"/>
|
||||
<data android:mimeType="image/heic-sequence"/>
|
||||
<data android:mimeType="image/heif"/>
|
||||
<data android:mimeType="image/heif-sequence"/>
|
||||
<data android:mimeType="image/hej2k"/>
|
||||
<data android:mimeType="image/hsj2"/>
|
||||
<data android:mimeType="image/jls"/>
|
||||
<data android:mimeType="image/jp2"/>
|
||||
<data android:mimeType="image/jph"/>
|
||||
<data android:mimeType="image/jphc"/>
|
||||
<data android:mimeType="image/jpm"/>
|
||||
<data android:mimeType="image/jpx"/>
|
||||
<data android:mimeType="image/jxr"/>
|
||||
<data android:mimeType="image/jxrA"/>
|
||||
<data android:mimeType="image/jxrS"/>
|
||||
<data android:mimeType="image/jxs"/>
|
||||
<data android:mimeType="image/jxsc"/>
|
||||
<data android:mimeType="image/jxsi"/>
|
||||
<data android:mimeType="image/jxss"/>
|
||||
<data android:mimeType="image/ktx"/>
|
||||
<data android:mimeType="image/ktx2"/>
|
||||
<data android:mimeType="image/naplps"/>
|
||||
<data android:mimeType="image/png"/>
|
||||
<data android:mimeType="image/prs.btif"/>
|
||||
<data android:mimeType="image/prs.pti"/>
|
||||
<data android:mimeType="image/pwg-raster"/>
|
||||
<data android:mimeType="image/svg+xml"/>
|
||||
<data android:mimeType="image/t38"/>
|
||||
<data android:mimeType="image/tiff"/>
|
||||
<data android:mimeType="image/tiff-fx"/>
|
||||
<data android:mimeType="image/xpm"/>
|
||||
<data android:mimeType="text/*"/>
|
||||
<data android:mimeType="application/*xml"/>
|
||||
<data android:mimeType="application/atom+xml"/>
|
||||
<data android:mimeType="application/dxf"/>
|
||||
<data android:mimeType="application/ecmascript"/>
|
||||
<data android:mimeType="application/javascript"/>
|
||||
<data android:mimeType="application/json"/>
|
||||
<data android:mimeType="application/*log*"/>
|
||||
<data android:mimeType="application/octet-stream"/>
|
||||
<data android:mimeType="application/soap+xm"/>
|
||||
<data android:mimeType="application/x-caramel"/>
|
||||
<data android:mimeType="application/x-klaunch"/>
|
||||
<data android:mimeType="application/x-latex"/>
|
||||
<data android:mimeType="application/x-sh"/>
|
||||
<data android:mimeType="application/x-tcl"/>
|
||||
<data android:mimeType="application/x-tex*"/>
|
||||
<data android:mimeType="application/x-troff*"/>
|
||||
<data android:mimeType="application/xhtml+xml"/>
|
||||
<data android:mimeType="application/xml*"/>
|
||||
<data android:mimeType="application/zip"/>
|
||||
<data android:mimeType="application/x-zip-compressed"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
|
||||
|
||||
<activity android:autoRemoveFromRecents="true"
|
||||
android:label="Emacs options"
|
||||
android:exported="true"
|
||||
android:name=".EmacsPreferencesActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider android:name="org.gnu.emacs.EmacsDocumentsProvider"
|
||||
android:authorities="org.gnu.emacs"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
android:permission="android.permission.MANAGE_DOCUMENTS"
|
||||
android:enabled="@bool/isAtLeastKitKat">
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.content.action.DOCUMENTS_PROVIDER"/>
|
||||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<service android:name="org.gnu.emacs.EmacsService"
|
||||
android:directBootAware="false"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:label="GNU Emacs service"/>
|
||||
</application>
|
||||
</manifest>
|
||||
968
java/INSTALL
Normal file
968
java/INSTALL
Normal file
|
|
@ -0,0 +1,968 @@
|
|||
Installation instructions for Android
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
See the end of the file for license conditions.
|
||||
|
||||
Please read the entirety of this file before attempting to build Emacs
|
||||
as an application package which can run on Android devices.
|
||||
|
||||
When building from the source repository, make sure to read
|
||||
INSTALL.REPO as well.
|
||||
|
||||
|
||||
|
||||
Android is an unusual operating system in that program binaries cannot
|
||||
be produced on computers running Android themselves. Instead, they
|
||||
must be built on some other computer using a set of tools known as the
|
||||
``Android SDK'' (Software Development Kit) and the ``Android NDK''
|
||||
(Native Development Kit.) Appropriate versions of both must be
|
||||
obtained to build GNU Emacs; after being built, the generated binaries
|
||||
will work on almost all Android devices. This document does not
|
||||
elaborate on how both sets of tools can be obtained. However, for
|
||||
your freedom's sake, you should use the Android SDK provided by the
|
||||
Debian project.
|
||||
|
||||
In addition to the Android SDK and Android NDK, Emacs also requires
|
||||
the Java compiler from OpenJDK 1.7.0 to be installed on your system,
|
||||
along with a working `m4' macro processor. Building on GNU systems is
|
||||
all that is officially supported. We are told that Mac OS works too,
|
||||
and other Unix systems will likely work as well, but MS Windows and
|
||||
Cygwin will not.
|
||||
|
||||
Once all of those tools are obtained, you may invoke the `configure'
|
||||
script like so:
|
||||
|
||||
./configure --with-android=/path/to/android.jar \
|
||||
ANDROID_CC=/path/to/android/ndk/cc \
|
||||
SDK_BUILD_TOOLS=/path/to/sdk/build/tools
|
||||
|
||||
Replacing the paths in the command line above with:
|
||||
|
||||
- the path to the `android.jar' headers which come with the Android
|
||||
SDK. They must correspond to Android version 13 (API level 33) or
|
||||
later.
|
||||
|
||||
- the path to the C compiler in the Android NDK, for the kind of CPU
|
||||
you are building Emacs to run on.
|
||||
|
||||
- the path to the directory in the Android SDK containing binaries
|
||||
such as `aapt', `apksigner', and `d8'. These are used to build
|
||||
the application package.
|
||||
|
||||
Where the type of CPU can either be `armeabi', `armv7*', `i686',
|
||||
`x86_64', `mips', or `mips64'.
|
||||
|
||||
After the configuration process completes, you may run:
|
||||
|
||||
make all
|
||||
|
||||
Once `make' finishes, there should be a file in the `java' directory
|
||||
named along the lines of:
|
||||
|
||||
emacs-<version>-<api-version>-<abi>.apk
|
||||
|
||||
where <api-version> is the oldest version of Android that the package
|
||||
will run on, and <abi> is the type of Android machine the package was
|
||||
built for.
|
||||
|
||||
The generated package can be uploaded onto an SD card (or similar
|
||||
medium) and installed on-device.
|
||||
|
||||
|
||||
BUILDING WITH OLD NDK VERSIONS
|
||||
|
||||
Building Emacs with an old version of the Android NDK requires special
|
||||
setup. This is because there is no separate C compiler binary for
|
||||
each version of Android in those versions of the NDK.
|
||||
|
||||
Before running `configure', you must identify three variables:
|
||||
|
||||
- What kind of Android system you are building Emacs for.
|
||||
|
||||
- The minimum API version of Android you want to build Emacs for.
|
||||
|
||||
- The locations of the system root and include files for that
|
||||
version of Android in the NDK.
|
||||
|
||||
That information must then be specified as arguments to the NDK C
|
||||
compiler. For example:
|
||||
|
||||
./configure [...] \
|
||||
ANDROID_CC="i686-linux-android-gcc \
|
||||
--sysroot=/path/to/ndk/platforms/android-14/arch-x86/"
|
||||
ANDROID_CFLAGS="-isystem /path/to/ndk/sysroot/usr/include \
|
||||
-isystem /path/to/ndk/sysroot/usr/include/i686-linux-android \
|
||||
-D__ANDROID_API__=14"
|
||||
|
||||
Where __ANDROID_API__ and the version identifier in
|
||||
"platforms/android-14" defines the version of Android you are building
|
||||
for, and the include directories specify the paths to the relevant
|
||||
Android headers. In addition, it may be necessary to specify
|
||||
"-gdwarf-2", due to a bug in the Android NDK.
|
||||
|
||||
Even older versions of the Android SDK do not require the extra
|
||||
`-isystem' directives.
|
||||
|
||||
Emacs is known to run on Android 2.2 (API version 8) or later, with
|
||||
the NDK r10b or later. We wanted to make Emacs work on even older
|
||||
versions of Android, but they are missing the required JNI graphics
|
||||
library that allows Emacs to display text from C code.
|
||||
|
||||
Due to an extremely nasty bug in the Android 2.2 system, the generated
|
||||
Emacs package cannot be compressed in builds for Android 2.2. As a
|
||||
result, the Emacs package will be approximately 100 megabytes larger
|
||||
than a compressed package for a newer version of Android.
|
||||
|
||||
|
||||
BUILDING C++ DEPENDENCIES
|
||||
|
||||
With a new version of the NDK, dependencies containing C++ code should
|
||||
build without any futher configuration. However, older versions
|
||||
require that you use the ``make_standalone_toolchain.py'' script in
|
||||
the NDK distribution to create a ``standalone toolchain'', and use
|
||||
that instead, in order for C++ headers to be found.
|
||||
|
||||
See https://developer.android.com/ndk/guides/standalone_toolchain for
|
||||
more details; when a ``standalone toolchain'' is specified, the
|
||||
configure script will try to determine the location of the C++
|
||||
compiler based on the C compiler specified. If that automatic
|
||||
detection does not work, you can specify a C++ compiler yourself, like
|
||||
so:
|
||||
|
||||
./configure --with-ndk-cxx=/path/to/toolchain/bin/i686-linux-android-g++
|
||||
|
||||
Some versions of the NDK have a bug, where GCC fails to locate
|
||||
``stddef.h'' after being copied to a standalone toolchain. To work
|
||||
around this problem (which normally exhibits itself when building C++
|
||||
code), add:
|
||||
|
||||
-isystem /path/to/toolchain/include/c++/4.9.x
|
||||
|
||||
to ANDROID_CFLAGS.
|
||||
|
||||
|
||||
DEBUG AND RELEASE BUILDS
|
||||
|
||||
Android makes a distinction between ``debug'' and ``release'' builds
|
||||
of applications. With ``release'' builds, the system will apply
|
||||
stronger optimizations to the application at the cost of being unable
|
||||
to debug them with the steps in etc/DEBUG.
|
||||
|
||||
Emacs is built as a debuggable package by default, but:
|
||||
|
||||
./configure --without-android-debug
|
||||
|
||||
will create a release build of Emacs instead. This may be useful when
|
||||
running Emacs on resource constrained machines.
|
||||
|
||||
If you are building an Emacs package for redistribution, we urge you
|
||||
to provide both debug and release versions.
|
||||
|
||||
|
||||
BUILDING WITH A SHARED USER ID
|
||||
|
||||
Sometimes it may be desirable to build Emacs so that it is able to
|
||||
access executables from another program. To achieve this, that other
|
||||
program must have a ``shared user ID'', and be signed with the same
|
||||
signing key used to sign Emacs (normally `emacs.keystore'.)
|
||||
|
||||
Once you have both that signing key and its ``shared user ID'', you
|
||||
can give it to configure:
|
||||
|
||||
./configure --with-shared-user-id=MY.SHARED.USER.ID
|
||||
|
||||
Don't do this if you already have Emacs installed with a different
|
||||
shared user ID, as the system does not allow programs to change their
|
||||
user IDs after being installed.
|
||||
|
||||
|
||||
BUILDING WITH THIRD PARTY LIBRARIES
|
||||
|
||||
The Android NDK does not support the usual ways of locating third
|
||||
party libraries, especially not via `pkg-config'. Instead, it uses
|
||||
its own system called `ndk-build'. The one exception to this rule is
|
||||
zlib, which is considered a part of the Android OS itself and is
|
||||
available on all devices running Android.
|
||||
|
||||
Android also requires that each application include its own
|
||||
dependencies, as the system makes no guarantee about the existence of
|
||||
any particular library.
|
||||
|
||||
Emacs is not built with the `ndk-build' system. Instead, it is built
|
||||
with Autoconf and Make.
|
||||
|
||||
However, it supports building and including dependencies which use the
|
||||
similarly Make-based `ndk-build' system.
|
||||
|
||||
To use dependencies built through `ndk-build', you must specify a list
|
||||
of directories within which Emacs will search for ``Android.mk''
|
||||
files, like so:
|
||||
|
||||
./configure "--with-ndk-path=directory1 directory2"
|
||||
|
||||
If `configure' complains about not being able to find
|
||||
``libc++_shared.so'', then you must locate that file in your copy of
|
||||
the NDK, and specify it like so:
|
||||
|
||||
./configure --with-ndk-cxx-shared=/path/to/sysroot/libc++_shared.so
|
||||
|
||||
Emacs will then read the ``Android.mk'' file in each directory, and
|
||||
automatically build and use those modules.
|
||||
|
||||
When building for Intel systems, some ``ndk-build'' modules require
|
||||
the Netwide Assembler, usually installed under ``nasm'', to be present
|
||||
on the system that is building Emacs.
|
||||
|
||||
Google, Inc. has adapted many common Emacs dependencies to use the
|
||||
`ndk-build' system. Here is a non-exhaustive list of what is known to
|
||||
work, along with what has to be patched to make them work:
|
||||
|
||||
libpng - https://android.googlesource.com/platform/external/libpng
|
||||
libwebp - https://android.googlesource.com/platform/external/webp
|
||||
(You must apply the patch at the end of this file for the resulting
|
||||
binary to work on armv7 devices.)
|
||||
giflib - https://android.googlesource.com/platform/external/giflib
|
||||
(You must add LOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH) before
|
||||
its Android.mk includes $(BUILD_STATIC_LIBRARY))
|
||||
libjpeg-turbo - https://android.googlesource.com/platform/external/libjpeg-turbo
|
||||
(You must add LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) before
|
||||
its Android.mk includes $(BUILD_SHARED_LIBRARY))
|
||||
libxml2 - https://android.googlesource.com/platform/external/libxml2/
|
||||
(You must also place the dependency icu4c in ``--with-ndk-path'',
|
||||
and apply the patch at the end of this file.)
|
||||
icu4c - https://android.googlesource.com/platform/external/icu/
|
||||
(You must apply the patch at the end of this file.)
|
||||
sqlite3 - https://android.googlesource.com/platform/external/sqlite/
|
||||
(You must apply the patch at the end of this file, and add the `dist'
|
||||
directory to ``--with-ndk-path''.)
|
||||
libselinux - https://android.googlesource.com/platform/external/libselinux
|
||||
(You must apply the patches at the end of the file, and obtain
|
||||
the following three dependencies.)
|
||||
libpackagelistparser
|
||||
https://android.googlesource.com/platform/system/core/+/refs/heads/nougat-mr1-dev/libpackagelistparser/
|
||||
libpcre - https://android.googlesource.com/platform/external/pcre
|
||||
libcrypto - https://android.googlesource.com/platform/external/boringssl
|
||||
(You must apply the patch at the end of this file when building for
|
||||
ARM systems.)
|
||||
|
||||
Many of these dependencies have been migrated over to the
|
||||
``Android.bp'' build system now used to build Android itself.
|
||||
However, the old ``Android.mk'' Makefiles are still present in older
|
||||
branches, and can be easily adapte to newer versions.
|
||||
|
||||
In addition, some Emacs dependencies provide `ndk-build' support
|
||||
themselves:
|
||||
|
||||
libjansson - https://github.com/akheron/jansson
|
||||
(You must add LOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES) before
|
||||
its Android.mk includes $(BUILD_SHARED_LIBRARY), then copy
|
||||
android/jansson_config.h to android/jansson_private_config.h.)
|
||||
|
||||
Emacs developers have ported the following dependencies to ARM Android
|
||||
systems:
|
||||
|
||||
gnutls, gmp - https://sourceforge.net/projects/android-ports-for-gnu-emacs
|
||||
(Please see the section GNUTLS near the end of this file.)
|
||||
libtiff - https://sourceforge.net/projects/android-ports-for-gnu-emacs
|
||||
(Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.)
|
||||
tree-sitter - https://sourceforge.net/projects/android-ports-for-gnu-emacs
|
||||
(Please see the section TREE-SITTER near the end of this file.)
|
||||
harfbuzz - https://sourceforge.net/projects/android-ports-for-gnu-emacs
|
||||
(Please see the section HARFBUZZ near the end of this file.)
|
||||
|
||||
And other developers have ported the following dependencies to Android
|
||||
systems:
|
||||
|
||||
ImageMagick, lcms2 - https://github.com/MolotovCherry/Android-ImageMagick7
|
||||
(Please see the section IMAGEMAGICK near the end of this file.)
|
||||
|
||||
We anticipate that most untested non-trivial ndk-build dependencies
|
||||
will need adjustments in Emacs to work, as the Emacs build system
|
||||
which emulates ndk-build is in an extremely early state.
|
||||
|
||||
|
||||
GNUTLS
|
||||
|
||||
Modified copies of GnuTLS and its dependencies (such as libgmp,
|
||||
libtasn1, p11-kit) which can be built with the ndk-build system can be
|
||||
found at https://sourceforge.net/projects/android-ports-for-gnu-emacs.
|
||||
|
||||
They have only been tested on arm64 Android systems running Android
|
||||
5.0 or later, and armv7l systems running Android 13 or later, so your
|
||||
mileage may vary, especially if you are trying to build Emacs for
|
||||
another kind of machine.
|
||||
|
||||
To build Emacs with GnuTLS, you must unpack each of the following tar
|
||||
archives in that site:
|
||||
|
||||
gmp-6.2.1-emacs.tgz
|
||||
gnutls-3.7.8-emacs.tar.gz
|
||||
libtasn1-4.19.0-emacs.tar.gz
|
||||
p11-kit-0.24.1-emacs.tar.gz
|
||||
nettle-3.8-emacs.tar.gz
|
||||
|
||||
and add the resulting folders to ``--with-ndk-path''. Note that you
|
||||
should not try to build these packages separately using any
|
||||
`configure' script or Makefiles inside.
|
||||
|
||||
|
||||
TREE-SITTER
|
||||
|
||||
A copy of tree-sitter modified to build with the ndk-build system can
|
||||
also be found that URL. To build Emacs with tree-sitter, you must
|
||||
unpack the following tar archive in that site:
|
||||
|
||||
tree-sitter-0.20.7-emacs.tar.gz
|
||||
|
||||
and add the resulting folder to ``--with-ndk-build''.
|
||||
|
||||
|
||||
HARFBUZZ
|
||||
|
||||
A copy of HarfBuzz modified to build with the ndk-build system can
|
||||
also be found at that URL. To build Emacs with HarfBuzz, you must
|
||||
unpack the following tar archive in that site:
|
||||
|
||||
harfbuzz-7.1.0-emacs.tar.gz
|
||||
|
||||
and add the resulting folder to ``--with-ndk-build''.
|
||||
|
||||
|
||||
IMAGEMAGICK
|
||||
|
||||
There is a third party port of ImageMagick to Android. Unfortunately,
|
||||
the port also uses its own patched versions of libpng, libjpeg,
|
||||
libtiff and libwebp, which conflict with those used by Emacs. Its
|
||||
Makefiles were also written for MS Windows, so you must also apply the
|
||||
patch at the end of this file.
|
||||
|
||||
|
||||
|
||||
PATCH FOR LIBXML2
|
||||
|
||||
This patch must be applied to the Android.mk in Google's version of
|
||||
libxml2 before it can be built for Emacs. In addition, you must also
|
||||
revert the commit `edb5870767fed8712a9b77ef34097209b61ab2db'.
|
||||
|
||||
diff --git a/Android.mk b/Android.mk
|
||||
index 07c7b372..24f67e49 100644
|
||||
--- a/Android.mk
|
||||
+++ b/Android.mk
|
||||
@@ -80,6 +80,7 @@ LOCAL_SHARED_LIBRARIES := libicuuc
|
||||
LOCAL_MODULE:= libxml2
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
|
||||
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# For the host
|
||||
@@ -94,3 +95,5 @@ LOCAL_MODULE := libxml2
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
+
|
||||
+$(call import-module,libicuuc)
|
||||
|
||||
PATCH FOR ICU
|
||||
|
||||
This patch must be applied to icu4j/Android.mk in Google's version of
|
||||
icu before it can be built for Emacs.
|
||||
|
||||
diff --git a/icu4j/Android.mk b/icu4j/Android.mk
|
||||
index d1ab3d5..69eff81 100644
|
||||
--- a/icu4j/Android.mk
|
||||
+++ b/icu4j/Android.mk
|
||||
@@ -69,7 +69,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
# Path to the ICU4C data files in the Android device file system:
|
||||
icu4c_data := /system/usr/icu
|
||||
icu4j_config_root := $(LOCAL_PATH)/main/classes/core/src
|
||||
-include external/icu/icu4j/adjust_icudt_path.mk
|
||||
+include $(LOCAL_PATH)/adjust_icudt_path.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(icu4j_src_files)
|
||||
|
||||
diff --git a/icu4c/source/common/Android.mk b/icu4c/source/common/Android.mk
|
||||
index 8e5f757..44bb130 100644
|
||||
--- a/icu4c/source/common/Android.mk
|
||||
+++ b/icu4c/source/common/Android.mk
|
||||
@@ -231,7 +231,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES += $(src_files)
|
||||
LOCAL_C_INCLUDES += $(c_includes) $(optional_android_logging_includes)
|
||||
LOCAL_CFLAGS += $(local_cflags) -DPIC -fPIC
|
||||
-LOCAL_SHARED_LIBRARIES += libdl $(optional_android_logging_libraries)
|
||||
+LOCAL_SHARED_LIBRARIES += libdl libstdc++ $(optional_android_logging_libraries)
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE := libicuuc
|
||||
LOCAL_RTTI_FLAG := -frtti
|
||||
|
||||
PATCH FOR SQLITE3
|
||||
|
||||
diff --git a/dist/Android.mk b/dist/Android.mk
|
||||
index bf277d2..36734d9 100644
|
||||
--- a/dist/Android.mk
|
||||
+++ b/dist/Android.mk
|
||||
@@ -141,6 +141,7 @@ include $(BUILD_HOST_EXECUTABLE)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(common_src_files)
|
||||
LOCAL_CFLAGS += $(minimal_sqlite_flags)
|
||||
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
|
||||
LOCAL_MODULE:= libsqlite_static_minimal
|
||||
LOCAL_SDK_VERSION := 23
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
diff --git a/dist/sqlite3.c b/dist/sqlite3.c
|
||||
index b0536a4..8fa1ee9 100644
|
||||
--- a/dist/sqlite3.c
|
||||
+++ b/dist/sqlite3.c
|
||||
@@ -26474,7 +26474,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
|
||||
*/
|
||||
#if !defined(HAVE_POSIX_FALLOCATE) \
|
||||
&& (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
|
||||
-# define HAVE_POSIX_FALLOCATE 1
|
||||
+/* # define HAVE_POSIX_FALLOCATE 1 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
PATCH FOR WEBP
|
||||
|
||||
diff --git a/Android.mk b/Android.mk
|
||||
index c7bcb0f5..d4da1704 100644
|
||||
--- a/Android.mk
|
||||
+++ b/Android.mk
|
||||
@@ -28,9 +28,10 @@ ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
||||
# Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
|
||||
# instructions to be generated for armv7a code. Instead target the neon code
|
||||
# specifically.
|
||||
- NEON := c.neon
|
||||
- USE_CPUFEATURES := yes
|
||||
- WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
|
||||
+ # NEON := c.neon
|
||||
+ # USE_CPUFEATURES := yes
|
||||
+ # WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
|
||||
+ NEON := c
|
||||
else
|
||||
NEON := c
|
||||
endif
|
||||
|
||||
PATCHES FOR SELINUX
|
||||
|
||||
diff --git a/Android.mk b/Android.mk
|
||||
index 659232e..1e64fd6 100644
|
||||
--- a/Android.mk
|
||||
+++ b/Android.mk
|
||||
@@ -116,3 +116,7 @@ LOCAL_STATIC_LIBRARIES := libselinux
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
|
||||
LOCAL_C_INCLUDES := external/pcre
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
+
|
||||
+$(call import-module,libpcre)
|
||||
+$(call import-module,libpackagelistparser)
|
||||
+$(call import-module,libcrypto)
|
||||
|
||||
diff --git a/src/android.c b/src/android.c
|
||||
index 5206a9f..b351ffc 100644
|
||||
--- a/src/android.c
|
||||
+++ b/src/android.c
|
||||
@@ -21,8 +21,7 @@
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/avc.h>
|
||||
#include <openssl/sha.h>
|
||||
-#include <private/android_filesystem_config.h>
|
||||
-#include <log/log.h>
|
||||
+#include <android/log.h>
|
||||
#include "policy.h"
|
||||
#include "callbacks.h"
|
||||
#include "selinux_internal.h"
|
||||
@@ -686,6 +685,7 @@ static int seapp_context_lookup(enum seapp_kind kind,
|
||||
seinfo = parsedseinfo;
|
||||
}
|
||||
|
||||
+#if 0
|
||||
userid = uid / AID_USER;
|
||||
isOwner = (userid == 0);
|
||||
appid = uid % AID_USER;
|
||||
@@ -702,9 +702,13 @@ static int seapp_context_lookup(enum seapp_kind kind,
|
||||
username = "_app";
|
||||
appid -= AID_APP;
|
||||
} else {
|
||||
+#endif
|
||||
username = "_isolated";
|
||||
+ appid = 0;
|
||||
+#if 0
|
||||
appid -= AID_ISOLATED_START;
|
||||
}
|
||||
+#endif
|
||||
|
||||
if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
|
||||
goto err;
|
||||
@@ -1662,8 +1666,10 @@ int selinux_log_callback(int type, const char *fmt, ...)
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&strp, fmt, ap) != -1) {
|
||||
+#if 0
|
||||
LOG_PRI(priority, "SELinux", "%s", strp);
|
||||
LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
|
||||
+#endif
|
||||
free(strp);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
PATCH FOR BORINGSSL
|
||||
|
||||
diff --git a/Android.mk b/Android.mk
|
||||
index 3e3ef2a..277d4a9 100644
|
||||
--- a/Android.mk
|
||||
+++ b/Android.mk
|
||||
@@ -27,7 +27,9 @@ LOCAL_MODULE := libcrypto
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
|
||||
LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
|
||||
+LOCAL_CFLAGS_arm = -DOPENSSL_STATIC_ARMCAP -DOPENSSL_NO_ASM
|
||||
LOCAL_SDK_VERSION := 9
|
||||
+LOCAL_LDFLAGS = --no-undefined
|
||||
# sha256-armv4.S does not compile with clang.
|
||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||
LOCAL_CLANG_ASFLAGS_arm64 += -march=armv8-a+crypto
|
||||
diff --git a/sources.mk b/sources.mk
|
||||
index e82f3d5..be3a3c4 100644
|
||||
--- a/sources.mk
|
||||
+++ b/sources.mk
|
||||
@@ -337,20 +337,20 @@ linux_aarch64_sources := \
|
||||
linux-aarch64/crypto/sha/sha256-armv8.S\
|
||||
linux-aarch64/crypto/sha/sha512-armv8.S\
|
||||
|
||||
-linux_arm_sources := \
|
||||
- linux-arm/crypto/aes/aes-armv4.S\
|
||||
- linux-arm/crypto/aes/aesv8-armx32.S\
|
||||
- linux-arm/crypto/aes/bsaes-armv7.S\
|
||||
- linux-arm/crypto/bn/armv4-mont.S\
|
||||
- linux-arm/crypto/modes/ghash-armv4.S\
|
||||
- linux-arm/crypto/modes/ghashv8-armx32.S\
|
||||
- linux-arm/crypto/sha/sha1-armv4-large.S\
|
||||
- linux-arm/crypto/sha/sha256-armv4.S\
|
||||
- linux-arm/crypto/sha/sha512-armv4.S\
|
||||
- src/crypto/chacha/chacha_vec_arm.S\
|
||||
- src/crypto/cpu-arm-asm.S\
|
||||
- src/crypto/curve25519/asm/x25519-asm-arm.S\
|
||||
- src/crypto/poly1305/poly1305_arm_asm.S\
|
||||
+# linux_arm_sources := \
|
||||
+# linux-arm/crypto/aes/aes-armv4.S\
|
||||
+# linux-arm/crypto/aes/aesv8-armx32.S\
|
||||
+# linux-arm/crypto/aes/bsaes-armv7.S\
|
||||
+# linux-arm/crypto/bn/armv4-mont.S\
|
||||
+# linux-arm/crypto/modes/ghash-armv4.S\
|
||||
+# linux-arm/crypto/modes/ghashv8-armx32.S\
|
||||
+# linux-arm/crypto/sha/sha1-armv4-large.S\
|
||||
+# linux-arm/crypto/sha/sha256-armv4.S\
|
||||
+# linux-arm/crypto/sha/sha512-armv4.S\
|
||||
+# src/crypto/chacha/chacha_vec_arm.S\
|
||||
+# src/crypto/cpu-arm-asm.S\
|
||||
+# src/crypto/curve25519/asm/x25519-asm-arm.S\
|
||||
+# src/crypto/poly1305/poly1305_arm_asm.S\
|
||||
|
||||
linux_x86_sources := \
|
||||
linux-x86/crypto/aes/aes-586.S\
|
||||
|
||||
PATCH FOR IMAGEMAGICK
|
||||
|
||||
diff --git a/Android.mk b/Android.mk
|
||||
index 5ab6699..4441417 100644
|
||||
--- a/Android.mk
|
||||
+++ b/Android.mk
|
||||
@@ -52,6 +52,20 @@ LZMA_LIB_PATH := $(LOCAL_PATH)/xz-5.2.4
|
||||
BZLIB_LIB_PATH := $(LOCAL_PATH)/bzip-1.0.8
|
||||
LCMS_LIB_PATH := $(LOCAL_PATH)/liblcms2-2.9
|
||||
|
||||
+LIBBZ2_ENABLED := true
|
||||
+LIBFFTW_ENABLED := true
|
||||
+LIBFREETYPE2_ENABLED := true
|
||||
+LIBJPEG_TURBO_ENABLED := true
|
||||
+LIBLZMA_ENABLED := true
|
||||
+LIBOPENJPEG_ENABLED := true
|
||||
+LIBPNG_ENABLED := true
|
||||
+LIBTIFF_ENABLED := true
|
||||
+LIBWEBP_ENABLED := true
|
||||
+LIBXML2_ENABLED := true
|
||||
+LIBZLIB_ENABLED := true
|
||||
+LIBLCMS2_ENABLED := true
|
||||
+BUILD_MAGICKWAND := true
|
||||
+
|
||||
#-------------------------------------------------------------
|
||||
# Include all modules
|
||||
#-------------------------------------------------------------
|
||||
@@ -68,6 +82,9 @@ include $(MAKE_PATH)/libjpeg-turbo.mk
|
||||
# libopenjpeg
|
||||
include $(MAKE_PATH)/libopenjpeg.mk
|
||||
|
||||
+# libwebp
|
||||
+include $(MAKE_PATH)/libwebp.mk
|
||||
+
|
||||
# libtiff
|
||||
include $(MAKE_PATH)/libtiff.mk
|
||||
|
||||
@@ -77,9 +94,6 @@ include $(MAKE_PATH)/libpng.mk
|
||||
# libfreetype2
|
||||
include $(MAKE_PATH)/libfreetype2.mk
|
||||
|
||||
-# libwebp
|
||||
-include $(MAKE_PATH)/libwebp.mk
|
||||
-
|
||||
# libfftw
|
||||
include $(MAKE_PATH)/libfftw.mk
|
||||
|
||||
diff --git a/libjpeg-turbo-2.0.2/jconfig.h b/libjpeg-turbo-2.0.2/jconfig.h
|
||||
index 47d14c9..5c6f8ee 100644
|
||||
--- a/libjpeg-turbo-2.0.2/jconfig.h
|
||||
+++ b/libjpeg-turbo-2.0.2/jconfig.h
|
||||
@@ -1,57 +1,43 @@
|
||||
-/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */
|
||||
+/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */
|
||||
#ifndef JPEG_LIB_VERSION
|
||||
#define JPEG_LIB_VERSION 62
|
||||
#endif
|
||||
-
|
||||
#ifndef LIBJPEG_TURBO_VERSION
|
||||
#define LIBJPEG_TURBO_VERSION 2.0.2
|
||||
#endif
|
||||
-
|
||||
#ifndef LIBJPEG_TURBO_VERSION_NUMBER
|
||||
#define LIBJPEG_TURBO_VERSION_NUMBER 202
|
||||
#endif
|
||||
-
|
||||
#ifndef C_ARITH_CODING_SUPPORTED
|
||||
#define C_ARITH_CODING_SUPPORTED
|
||||
#endif
|
||||
-
|
||||
#ifndef D_ARITH_CODING_SUPPORTED
|
||||
#define D_ARITH_CODING_SUPPORTED
|
||||
#endif
|
||||
-
|
||||
#ifndef MEM_SRCDST_SUPPORTED
|
||||
#define MEM_SRCDST_SUPPORTED
|
||||
#endif
|
||||
-
|
||||
#ifndef WITH_SIMD
|
||||
#define WITH_SIMD
|
||||
#endif
|
||||
-
|
||||
#ifndef BITS_IN_JSAMPLE
|
||||
#define BITS_IN_JSAMPLE 8
|
||||
#endif
|
||||
-
|
||||
#ifndef HAVE_LOCALE_H
|
||||
#define HAVE_LOCALE_H
|
||||
#endif
|
||||
-
|
||||
#ifndef HAVE_STDDEF_H
|
||||
#define HAVE_STDDEF_H
|
||||
#endif
|
||||
-
|
||||
#ifndef HAVE_STDLIB_H
|
||||
#define HAVE_STDLIB_H
|
||||
#endif
|
||||
-
|
||||
#ifndef NEED_SYS_TYPES_H
|
||||
#define NEED_SYS_TYPES_H
|
||||
#endif
|
||||
-
|
||||
#ifndef HAVE_UNSIGNED_CHAR
|
||||
#define HAVE_UNSIGNED_CHAR
|
||||
#endif
|
||||
-
|
||||
#ifndef HAVE_UNSIGNED_SHORT
|
||||
#define HAVE_UNSIGNED_SHORT
|
||||
#endif
|
||||
-
|
||||
diff --git a/libxml2-2.9.9/encoding.c b/libxml2-2.9.9/encoding.c
|
||||
index a3aaf10..60f165b 100644
|
||||
--- a/libxml2-2.9.9/encoding.c
|
||||
+++ b/libxml2-2.9.9/encoding.c
|
||||
@@ -2394,7 +2394,6 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init)
|
||||
{
|
||||
int ret;
|
||||
size_t written;
|
||||
- size_t writtentot = 0;
|
||||
size_t toconv;
|
||||
int c_in;
|
||||
int c_out;
|
||||
@@ -2451,7 +2450,6 @@ retry:
|
||||
xmlBufContent(in), &c_in);
|
||||
xmlBufShrink(in, c_in);
|
||||
xmlBufAddLen(out, c_out);
|
||||
- writtentot += c_out;
|
||||
if (ret == -1) {
|
||||
if (c_out > 0) {
|
||||
/* Can be a limitation of iconv or uconv */
|
||||
@@ -2536,7 +2534,6 @@ retry:
|
||||
}
|
||||
|
||||
xmlBufAddLen(out, c_out);
|
||||
- writtentot += c_out;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
@@ -2567,9 +2564,7 @@ xmlCharEncOutFunc(xmlCharEncodingHandler *handler, xmlBufferPtr out,
|
||||
xmlBufferPtr in) {
|
||||
int ret;
|
||||
int written;
|
||||
- int writtentot = 0;
|
||||
int toconv;
|
||||
- int output = 0;
|
||||
|
||||
if (handler == NULL) return(-1);
|
||||
if (out == NULL) return(-1);
|
||||
@@ -2612,7 +2607,6 @@ retry:
|
||||
in->content, &toconv);
|
||||
xmlBufferShrink(in, toconv);
|
||||
out->use += written;
|
||||
- writtentot += written;
|
||||
out->content[out->use] = 0;
|
||||
if (ret == -1) {
|
||||
if (written > 0) {
|
||||
@@ -2622,8 +2616,6 @@ retry:
|
||||
ret = -3;
|
||||
}
|
||||
|
||||
- if (ret >= 0) output += ret;
|
||||
-
|
||||
/*
|
||||
* Attempt to handle error cases
|
||||
*/
|
||||
@@ -2700,7 +2692,6 @@ retry:
|
||||
}
|
||||
|
||||
out->use += written;
|
||||
- writtentot += written;
|
||||
out->content[out->use] = 0;
|
||||
goto retry;
|
||||
}
|
||||
diff --git a/libxml2-2.9.9/xpath.c b/libxml2-2.9.9/xpath.c
|
||||
index 5e3bb9f..505ec82 100644
|
||||
--- a/libxml2-2.9.9/xpath.c
|
||||
+++ b/libxml2-2.9.9/xpath.c
|
||||
@@ -10547,7 +10547,7 @@ xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
|
||||
|
||||
static xmlChar *
|
||||
xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
|
||||
- int len = 0, l;
|
||||
+ int l;
|
||||
int c;
|
||||
const xmlChar *cur;
|
||||
xmlChar *ret;
|
||||
@@ -10567,7 +10567,6 @@ xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
|
||||
(c == '_') || (c == ':') ||
|
||||
(IS_COMBINING(c)) ||
|
||||
(IS_EXTENDER(c)))) {
|
||||
- len += l;
|
||||
NEXTL(l);
|
||||
c = CUR_CHAR(l);
|
||||
}
|
||||
diff --git a/make/libicu4c.mk b/make/libicu4c.mk
|
||||
index 21ec121..8b77865 100644
|
||||
--- a/make/libicu4c.mk
|
||||
+++ b/make/libicu4c.mk
|
||||
@@ -250,7 +250,7 @@ LOCAL_MODULE := libicuuc
|
||||
LOCAL_SRC_FILES := $(src_files)
|
||||
|
||||
# when built in android, they require uconfig_local (because of android project), but we don't need this
|
||||
-$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /* Autogenerated stub file to make libicuuc build happy */) \
|
||||
+$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /\* Autogenerated stub file to make libicuuc build happy \*/) \
|
||||
|
||||
ifeq ($(LIBXML2_ENABLED),true)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
diff --git a/make/libjpeg-turbo.mk b/make/libjpeg-turbo.mk
|
||||
index d39dd41..fdebcf3 100644
|
||||
--- a/make/libjpeg-turbo.mk
|
||||
+++ b/make/libjpeg-turbo.mk
|
||||
@@ -230,30 +230,30 @@ JCONFIG_FLAGS += \
|
||||
HAVE_UNSIGNED_SHORT
|
||||
|
||||
JCONFIGINT_FLAGS += \
|
||||
- BUILD="20190814" \
|
||||
- PACKAGE_NAME="libjpeg-turbo" \
|
||||
- VERSION="2.0.2"
|
||||
+ BUILD=\"20190814\" \
|
||||
+ PACKAGE_NAME=\"libjpeg-turbo\" \
|
||||
+ VERSION=\"2.0.2\"
|
||||
|
||||
# originally defined in jconfigint.h, but the substitution has problems with spaces
|
||||
LOCAL_CFLAGS := \
|
||||
-DINLINE="inline __attribute__((always_inline))"
|
||||
|
||||
# create definition file jconfig.h, needed in order to build
|
||||
-$(shell echo /* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */ > $(JPEG_LIB_PATH)/jconfig.h)
|
||||
+$(shell echo \/\* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS \*\/ > $(JPEG_LIB_PATH)/jconfig.h)
|
||||
$(foreach name,$(JCONFIG_FLAGS), \
|
||||
$(if $(findstring =,$(name)), \
|
||||
- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(firstword $(subst =, ,$(name)))) \
|
||||
+ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(firstword $(subst =, ,$(name)))) \
|
||||
, \
|
||||
- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(name)) \
|
||||
+ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(name)) \
|
||||
) \
|
||||
- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #define $(subst =, ,$(name))) \
|
||||
- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #endif) \
|
||||
+ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#define $(subst =, ,$(name))) \
|
||||
+ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#endif) \
|
||||
$(shell >> $(JPEG_LIB_PATH)/jconfig.h echo.) \
|
||||
)
|
||||
|
||||
# create definition file jconfigint.h, needed in order to build
|
||||
-$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /* autogenerated jconfigint.h based on Android.mk vars JCONFIGINT_FLAGS */)
|
||||
-$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h echo #define $(subst =, ,$(name))))
|
||||
+$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /\* autogenerated jconfigint.h based on Android.mk vars JCONFIGINT_FLAGS \*/)
|
||||
+$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h echo \#define $(subst =, ,$(name))))
|
||||
|
||||
ifeq ($(LIBJPEG_TURBO_ENABLED),true)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
diff --git a/make/liblcms2.mk b/make/liblcms2.mk
|
||||
index e1fd3b9..29ca791 100644
|
||||
--- a/make/liblcms2.mk
|
||||
+++ b/make/liblcms2.mk
|
||||
@@ -10,6 +10,10 @@ LOCAL_C_INCLUDES := \
|
||||
$(LCMS_LIB_PATH)/include \
|
||||
$(LCMS_LIB_PATH)/src
|
||||
|
||||
+LOCAL_EXPORT_C_INCLUDES := \
|
||||
+ $(LCMS_LIB_PATH) \
|
||||
+ $(LCMS_LIB_PATH)/include \
|
||||
+ $(LCMS_LIB_PATH)/src
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
-DHAVE_FUNC_ATTRIBUTE_VISIBILITY=1 \
|
||||
diff --git a/make/libmagick++-7.mk b/make/libmagick++-7.mk
|
||||
index 5352ccb..929396d 100644
|
||||
--- a/make/libmagick++-7.mk
|
||||
+++ b/make/libmagick++-7.mk
|
||||
@@ -12,7 +12,7 @@ LOCAL_C_INCLUDES := \
|
||||
|
||||
ifneq ($(STATIC_BUILD),true)
|
||||
LOCAL_LDFLAGS += -fexceptions
|
||||
- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
|
||||
+ LOCAL_LDLIBS := -llog -lz
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
diff --git a/make/libmagickcore-7.mk b/make/libmagickcore-7.mk
|
||||
index 81293b2..d51fced 100644
|
||||
--- a/make/libmagickcore-7.mk
|
||||
+++ b/make/libmagickcore-7.mk
|
||||
@@ -25,6 +25,7 @@ else ifeq ($(TARGET_ARCH_ABI),x86_64)
|
||||
|
||||
endif
|
||||
|
||||
+LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(IMAGE_MAGICK) \
|
||||
@@ -45,10 +46,9 @@ LOCAL_C_INCLUDES += \
|
||||
$(BZLIB_LIB_PATH) \
|
||||
$(LCMS_LIB_PATH)/include
|
||||
|
||||
-
|
||||
ifneq ($(STATIC_BUILD),true)
|
||||
# ignored in static library builds
|
||||
- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
|
||||
+ LOCAL_LDLIBS := -llog -lz
|
||||
endif
|
||||
|
||||
|
||||
diff --git a/make/libmagickwand-7.mk b/make/libmagickwand-7.mk
|
||||
index 7be2fb6..0bbcca5 100644
|
||||
--- a/make/libmagickwand-7.mk
|
||||
+++ b/make/libmagickwand-7.mk
|
||||
@@ -14,7 +14,7 @@ LOCAL_C_INCLUDES := \
|
||||
|
||||
# always ignored in static builds
|
||||
ifneq ($(STATIC_BUILD),true)
|
||||
- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
|
||||
+ LOCAL_LDLIBS := -llog -lz
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
@@ -54,6 +54,29 @@ ifeq ($(OPENCL_BUILD),true)
|
||||
LOCAL_SHARED_LIBRARIES += libopencl
|
||||
endif
|
||||
|
||||
+LOCAL_SHARED_LIBRARIES += libstdc++
|
||||
+
|
||||
+ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
||||
+ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
|
||||
+ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
|
||||
+else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
||||
+ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
|
||||
+ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
|
||||
+else ifeq ($(TARGET_ARCH_ABI),x86)
|
||||
+ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
|
||||
+ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
|
||||
+else ifeq ($(TARGET_ARCH_ABI),x86_64)
|
||||
+ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
|
||||
+ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
|
||||
+
|
||||
+ ifneq ($(STATIC_BUILD),true)
|
||||
+ LOCAL_LDFLAGS += -latomic
|
||||
+ endif
|
||||
+
|
||||
+endif
|
||||
+
|
||||
+LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
|
||||
+
|
||||
ifeq ($(BUILD_MAGICKWAND),true)
|
||||
ifeq ($(STATIC_BUILD),true)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
diff --git a/make/libpng.mk b/make/libpng.mk
|
||||
index 24fb8ac..dda05fd 100644
|
||||
--- a/make/libpng.mk
|
||||
+++ b/make/libpng.mk
|
||||
@@ -30,6 +30,7 @@ ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
|
||||
endif # TARGET_ARCH_ABI == arm64-v8a
|
||||
|
||||
|
||||
+LOCAL_EXPORT_C_INCLUDES := $(PNG_LIB_PATH)
|
||||
LOCAL_C_INCLUDES := $(PNG_LIB_PATH)
|
||||
|
||||
LOCAL_SRC_FILES += \
|
||||
diff --git a/make/libtiff.mk b/make/libtiff.mk
|
||||
index ca43f25..2b17508 100644
|
||||
--- a/make/libtiff.mk
|
||||
+++ b/make/libtiff.mk
|
||||
@@ -12,6 +12,9 @@ LOCAL_C_INCLUDES := \
|
||||
$(LZMA_LIB_PATH)/liblzma/api \
|
||||
$(WEBP_LIB_PATH)/src
|
||||
|
||||
+LOCAL_EXPORT_C_INCLUDES := \
|
||||
+ $(TIFF_LIB_PATH)
|
||||
+
|
||||
ifeq ($(LIBLZMA_ENABLED),true)
|
||||
LOCAL_CFLAGS += -DLZMA_SUPPORT=1
|
||||
endif
|
||||
diff --git a/make/magick.mk b/make/magick.mk
|
||||
index 3ba4b1d..5471608 100644
|
||||
--- a/make/magick.mk
|
||||
+++ b/make/magick.mk
|
||||
@@ -18,7 +18,7 @@ LOCAL_C_INCLUDES := \
|
||||
$(FREETYPE_LIB_PATH)/include
|
||||
|
||||
|
||||
-LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
|
||||
+LOCAL_LDLIBS := -llog -lz
|
||||
LOCAL_SRC_FILES := \
|
||||
$(IMAGE_MAGICK)/utilities/magick.c \
|
||||
|
||||
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
330
java/Makefile.in
Normal file
330
java/Makefile.in
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
### @configure_input@
|
||||
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU Emacs.
|
||||
|
||||
# GNU Emacs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# GNU Emacs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
builddir = @builddir@
|
||||
version = @version@
|
||||
|
||||
# Don't install movemail if mailutils are to be used.
|
||||
emacs_use_mailutils = @emacs_use_mailutils@
|
||||
|
||||
# This is the host lib-src and lib, not the cross compiler's lib-src.
|
||||
libsrc = ../lib-src
|
||||
EXEEXT = @EXEEXT@
|
||||
|
||||
-include ${top_builddir}/src/verbose.mk
|
||||
|
||||
SHELL = @SHELL@
|
||||
JAVAC = @JAVAC@
|
||||
AAPT = @AAPT@
|
||||
D8 = @D8@
|
||||
ZIPALIGN = @ZIPALIGN@
|
||||
JARSIGNER = @JARSIGNER@
|
||||
APKSIGNER = @APKSIGNER@
|
||||
JARSIGNER_FLAGS =
|
||||
ANDROID_JAR = @ANDROID_JAR@
|
||||
ANDROID_ABI = @ANDROID_ABI@
|
||||
ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
|
||||
ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
|
||||
WARN_JAVAFLAGS = @WARN_JAVAFLAGS@
|
||||
JAVAFLAGS = $(WARN_JAVAFLAGS) -classpath "$(ANDROID_JAR):$(srcdir)"
|
||||
FIND_DELETE = @FIND_DELETE@
|
||||
|
||||
# Android 4.3 and earlier require Emacs to be signed with a different
|
||||
# digital signature algorithm.
|
||||
|
||||
ifneq (,$(ANDROID_SDK_18_OR_EARLIER))
|
||||
JARSIGNER_FLAGS = -sigalg MD5withRSA -digestalg SHA1
|
||||
else
|
||||
JARSIGNER_FLAGS =
|
||||
endif
|
||||
|
||||
# When building Emacs for Android 2.2, assets must not be compressed.
|
||||
# Otherwise, the asset manager fails to extract files larger than 1
|
||||
# MB.
|
||||
|
||||
ifneq (,$(ANDROID_SDK_8_OR_EARLIER))
|
||||
AAPT_ASSET_ARGS = -0 ""
|
||||
else
|
||||
AAPT_ASSET_ARGS =
|
||||
endif
|
||||
|
||||
SIGN_EMACS = -keystore $(srcdir)/emacs.keystore -storepass \
|
||||
emacs1 $(JARSIGNER_FLAGS)
|
||||
SIGN_EMACS_V2 = sign --v2-signing-enabled --ks \
|
||||
$(srcdir)/emacs.keystore -debuggable-apk-permitted \
|
||||
--ks-pass pass:emacs1
|
||||
|
||||
JAVA_FILES := $(wildcard $(srcdir)/org/gnu/emacs/*.java)
|
||||
RESOURCE_FILES := $(foreach file,$(wildcard $(srcdir)/res/*), \
|
||||
$(wildcard $(file)/*))
|
||||
|
||||
# R.java is a file generated by the `aapt' utility containing
|
||||
# constants that can then be used to locate ``resource identifiers''.
|
||||
# It is not a regular file and should not be compiled as Java source
|
||||
# code. Instead, it is automatically included by the Java compiler.
|
||||
RESOURCE_FILE := $(srcdir)/org/gnu/emacs/R.java
|
||||
|
||||
# CLASS_FILES is what should actually be built and included in the
|
||||
# resulting Emacs executable. The Java compiler might generate more
|
||||
# than one class file for each source file, so this only serves as a
|
||||
# list of dependencies for Make.
|
||||
CLASS_FILES := $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
|
||||
|
||||
# Remove RESOURCE_FILE from JAVA_FILES, if it is already present.
|
||||
JAVA_FILES := $(filter-out $(RESOURCE_FILE),$(JAVA_FILES))
|
||||
|
||||
# Compute the name for the Emacs application package. This should be:
|
||||
# emacs-<version>-<min-sdk>-<abi>.apk
|
||||
|
||||
ANDROID_MIN_SDK := @ANDROID_MIN_SDK@
|
||||
APK_NAME := emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
|
||||
|
||||
# How this stuff works.
|
||||
|
||||
# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
|
||||
# containing the following files:
|
||||
# lib/$(ANDROID_ABI)/libemacs.so
|
||||
# lib/$(ANDROID_ABI)/libandroid-emacs.so
|
||||
# lib/$(ANDROID_ABI)/libctags.so
|
||||
# lib/$(ANDROID_ABI)/libetags.so
|
||||
# lib/$(ANDROID_ABI)/libhexl.so
|
||||
# lib/$(ANDROID_ABI)/libmovemail.so
|
||||
# lib/$(ANDROID_ABI)/librcs2log.so
|
||||
# lib/$(ANDROID_ABI)/libebrowse.so
|
||||
# assets/info/
|
||||
# assets/etc/
|
||||
# assets/lisp/
|
||||
|
||||
.PHONY: emacs.apk-in all
|
||||
all: $(APK_NAME)
|
||||
|
||||
# Binaries to cross-compile.
|
||||
CROSS_SRC_BINS := $(top_builddir)/cross/src/android-emacs
|
||||
CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/ctags \
|
||||
$(top_builddir)/cross/lib-src/hexl \
|
||||
$(top_builddir)/cross/lib-src/ebrowse \
|
||||
$(top_builddir)/cross/lib-src/emacsclient \
|
||||
$(top_builddir)/cross/lib-src/etags
|
||||
CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail
|
||||
CROSS_EXEC_BINS := $(top_builddir)/exec/exec1 $(top_builddir)/exec/loader
|
||||
CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) $(CROSS_EXEC_BINS)
|
||||
|
||||
ifneq ($(emacs_use_mailutils),yes)
|
||||
CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL)
|
||||
endif
|
||||
|
||||
# Libraries to cross-compile.
|
||||
CROSS_LIBS = $(top_builddir)/cross/src/libemacs.so
|
||||
|
||||
# Make sure gnulib is built first!
|
||||
# If not, then the recursive invocations of make below will try to
|
||||
# build gnulib at the same time.
|
||||
CROSS_ARCHIVES = $(top_builddir)/cross/lib/libgnu.a
|
||||
|
||||
# Third party libraries to compile.
|
||||
-include $(top_builddir)/cross/ndk-build/ndk-build.mk
|
||||
|
||||
.PHONY: $(CROSS_BINS) $(CROSS_LIBS) $(CROSS_ARCHIVES)
|
||||
|
||||
# There should only be a single invocation of $(MAKE) -C
|
||||
# $(top_srcdir)/cross for each directory under $(top_srcdir)/cross.
|
||||
$(CROSS_SRC_BINS) $(CROSS_LIBS) &: $(CROSS_ARCHIVES)
|
||||
$(MAKE) -C $(top_builddir)/cross $(foreach file, \
|
||||
$(CROSS_SRC_BINS) \
|
||||
$(CROSS_LIBS), \
|
||||
src/$(notdir $(file)))
|
||||
|
||||
$(CROSS_LIBSRC_BINS) &: $(CROSS_ARCHIVES)
|
||||
$(MAKE) -C $(top_builddir)/cross $(foreach file, \
|
||||
$(CROSS_LIBSRC_BINS), \
|
||||
lib-src/$(notdir $(file)))
|
||||
|
||||
$(CROSS_ARCHIVES):
|
||||
$(MAKE) -C $(top_builddir)/cross lib/libgnu.a
|
||||
|
||||
# These two binaries are helpers used to execute binaries on Android
|
||||
# 10 and later.
|
||||
|
||||
$(CROSS_EXEC_BINS) &:
|
||||
$(MAKE) -C $(top_builddir)/exec $(notdir $(CROSS_EXEC_BINS))
|
||||
|
||||
# This is needed to generate the ``.directory-tree'' file used by the
|
||||
# Android emulations of readdir and faccessat.
|
||||
|
||||
$(libsrc)/asset-directory-tool:
|
||||
$(MAKE) -C $(libsrc) $(notdir $@)
|
||||
|
||||
# install_tmp is a directory used to generate emacs.apk-in.
|
||||
# That is then packaged into $(APK_NAME).
|
||||
# There is no need to depend on NDK_BUILD_SHARED as libemacs.so
|
||||
# does already.
|
||||
|
||||
.PHONY: install_temp install_temp/assets/directory-tree
|
||||
install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
|
||||
$(AM_V_GEN)
|
||||
# Make the working directory for this stuff
|
||||
$(AM_V_SILENT) rm -rf install_temp
|
||||
$(AM_V_SILENT) mkdir -p install_temp/lib/$(ANDROID_ABI)
|
||||
$(AM_V_SILENT) mkdir -p install_temp/assets/etc
|
||||
$(AM_V_SILENT) mkdir -p install_temp/assets/lisp
|
||||
$(AM_V_SILENT) mkdir -p install_temp/assets/info
|
||||
# Install architecture independents to assets/etc and assets/lisp
|
||||
$(AM_V_SILENT) cp -r $(top_srcdir)/lisp install_temp/assets
|
||||
$(AM_V_SILENT) cp -r $(top_srcdir)/etc install_temp/assets
|
||||
$(AM_V_SILENT) cp -r $(top_srcdir)/info install_temp/assets
|
||||
# Remove undesirable files from those directories.
|
||||
$(AM_V_SILENT) \
|
||||
for subdir in `find install_temp -type d -print`; do \
|
||||
chmod a+rx $${subdir} ; \
|
||||
rm -rf $${subdir}/.gitignore ; \
|
||||
rm -rf $${subdir}/.DS_Store ; \
|
||||
rm -rf $${subdir}/#* ; \
|
||||
rm -rf $${subdir}/.#* ; \
|
||||
rm -rf $${subdir}/*~ ; \
|
||||
rm -rf $${subdir}/*.orig ; \
|
||||
rm -rf $${subdir}/ChangeLog* ; \
|
||||
rm -rf $${subdir}/[mM]akefile*[.-]in ; \
|
||||
rm -rf $${subdir}/Makefile; \
|
||||
done
|
||||
# Generate the directory tree for those directories.
|
||||
# Install architecture dependents to lib/$(ANDROID_ABI). This
|
||||
# perculiar naming scheme is required to make Android preserve these
|
||||
# binaries upon installation.
|
||||
$(AM_V_SILENT) \
|
||||
for file in $(CROSS_BINS); do \
|
||||
if [ -x $$file ]; then \
|
||||
filename=`basename $$file`; \
|
||||
cp -f $$file install_temp/lib/$(ANDROID_ABI)/lib$${filename}.so; \
|
||||
fi \
|
||||
done
|
||||
$(AM_V_SILENT) \
|
||||
for file in $(CROSS_LIBS); do \
|
||||
if [ -x $$file ]; then \
|
||||
cp -f $$file install_temp/lib/$(ANDROID_ABI); \
|
||||
fi \
|
||||
done
|
||||
ifneq ($(NDK_BUILD_SHARED),)
|
||||
$(AM_V_SILENT) cp -f $(NDK_BUILD_SHARED) \
|
||||
install_temp/lib/$(ANDROID_ABI)
|
||||
endif
|
||||
|
||||
install_temp/assets/directory-tree: $(libsrc)/asset-directory-tool \
|
||||
install_temp install_temp/assets/version
|
||||
$(AM_V_GEN) $(libsrc)/asset-directory-tool install_temp/assets \
|
||||
install_temp/assets/directory-tree
|
||||
|
||||
install_temp/assets/version: install_temp
|
||||
$(AM_V_GEN) { (git rev-parse HEAD || echo "Unknown") \
|
||||
&& (git rev-parse --abbrev-ref HEAD \
|
||||
|| echo "Unknown") } 2> /dev/null > $@
|
||||
|
||||
install_temp/assets/build_info: install_temp
|
||||
$(AM_V_GEN) { hostname; date +%s; } > $@
|
||||
|
||||
emacs.apk-in: install_temp install_temp/assets/directory-tree \
|
||||
install_temp/assets/version install_temp/assets/build_info \
|
||||
AndroidManifest.xml
|
||||
# Package everything. Specifying the assets on this command line is
|
||||
# necessary for AAssetManager_getNextFileName to work on old versions
|
||||
# of Android. Make sure not to generate R.java, as it's already been
|
||||
# generated.
|
||||
$(AM_V_AAPT) $(AAPT) p -I "$(ANDROID_JAR)" -F $@ \
|
||||
-f -M AndroidManifest.xml $(AAPT_ASSET_ARGS) \
|
||||
-A install_temp/assets \
|
||||
-S $(top_srcdir)/java/res -J install_temp
|
||||
$(AM_V_SILENT) pushd install_temp &> /dev/null; \
|
||||
$(AAPT) add ../$@ `find lib -type f`; \
|
||||
popd &> /dev/null
|
||||
$(AM_V_SILENT) rm -rf install_temp
|
||||
|
||||
# Makefile itself.
|
||||
.PRECIOUS: $(top_srcdir)/config.status Makefile
|
||||
$(top_srcdir)/config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
|
||||
$(MAKE) -C $(dir $@) $(notdir $@)
|
||||
Makefile: $(top_srcdir)/config.status $(top_srcdir)/java/Makefile.in
|
||||
$(MAKE) -C .. java/$@
|
||||
|
||||
# AndroidManifest.xml:
|
||||
AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
|
||||
$(srcdir)/AndroidManifest.xml.in
|
||||
pushd ..; ./config.status java/AndroidManifest.xml; popd
|
||||
|
||||
# R.java:
|
||||
$(RESOURCE_FILE): $(RESOURCE_FILES)
|
||||
$(AM_V_GEN) $(AAPT) p -I "$(ANDROID_JAR)" -f \
|
||||
-J $(dir $@) -M AndroidManifest.xml \
|
||||
-S $(top_srcdir)/java/res
|
||||
|
||||
# Make all class files depend on R.java being built.
|
||||
$(CLASS_FILES): $(RESOURCE_FILE)
|
||||
|
||||
.SUFFIXES: .java .class
|
||||
$(CLASS_FILES) &: $(JAVA_FILES)
|
||||
$(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $(JAVA_FILES)
|
||||
$(AM_V_SILENT) touch $(CLASS_FILES)
|
||||
|
||||
# N.B. that find must be called all over again in case javac generated
|
||||
# nested classes.
|
||||
|
||||
classes.dex: $(CLASS_FILES)
|
||||
$(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
|
||||
$(subst $$,\$$,$(shell find $(srcdir) -type f \
|
||||
-name *.class)) --output $(builddir)
|
||||
|
||||
# When emacs.keystore expires, regenerate it with:
|
||||
#
|
||||
# keytool -genkey -v -keystore emacs.keystore -alias "Emacs keystore" \
|
||||
# -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 100000
|
||||
|
||||
.PHONY: clean maintainer-clean
|
||||
|
||||
$(APK_NAME): classes.dex emacs.apk-in $(srcdir)/emacs.keystore
|
||||
$(AM_V_GEN)
|
||||
$(AM_V_SILENT) cp -f emacs.apk-in $@.unaligned
|
||||
$(AM_V_SILENT) $(AAPT) add $@.unaligned classes.dex
|
||||
$(AM_V_SILENT) $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
|
||||
$(AM_V_SILENT) $(ZIPALIGN) -f 4 $@.unaligned $@
|
||||
# Signing must happen after alignment!
|
||||
$(AM_V_SILENT) $(APKSIGNER) $(SIGN_EMACS_V2) $@
|
||||
$(AM_V_SILENT) rm -f $@.unaligned *.idsig
|
||||
|
||||
# TAGS generation.
|
||||
|
||||
ETAGS = $(top_builddir)/lib-src/etags
|
||||
|
||||
$(ETAGS): FORCE
|
||||
$(MAKE) -C ../lib-src $(notdir $@)
|
||||
|
||||
tagsfiles = $(JAVA_FILES) $(RESOURCE_FILE)
|
||||
|
||||
.PHONY: tags FORCE
|
||||
tags: TAGS
|
||||
TAGS: $(ETAGS) $(tagsfiles)
|
||||
$(AM_V_GEN) $(ETAGS) $(tagsfiles)
|
||||
|
||||
clean:
|
||||
rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig
|
||||
rm -rf install-temp $(RESOURCE_FILE) TAGS
|
||||
find . -name '*.class' $(FIND_DELETE)
|
||||
|
||||
maintainer-clean distclean bootstrap-clean: clean
|
||||
rm -f Makefile ndk-build.mk
|
||||
908
java/README
Normal file
908
java/README
Normal file
|
|
@ -0,0 +1,908 @@
|
|||
This directory holds the Java sources of the port of GNU Emacs to
|
||||
Android-like systems, along with files needed to create an application
|
||||
package out of them. If you need to build this port, please read the
|
||||
file INSTALL in this directory.
|
||||
|
||||
The ``org/gnu/emacs'' subdirectory contains the Java sources under the
|
||||
``org.gnu.emacs'' package identifier.
|
||||
|
||||
``AndroidManifest.xml'' contains a manifest describing the Java
|
||||
sources to the system.
|
||||
|
||||
The ``res'' directory contains resources, mainly the Emacs icon and
|
||||
several ``boolean resources'' which are used as a form of conditional
|
||||
evaluation for manifest entries.
|
||||
|
||||
`emacs.keystore' is the signing key used to build Emacs. It is kept
|
||||
here, and we encourage all people redistributing Emacs to use this
|
||||
key. It holds no security value, and otherwise it will be impossible
|
||||
to install different builds of Emacs on top of each other.
|
||||
|
||||
Please keep the Java code indented with tabs and formatted according
|
||||
to the rules for C code in the GNU coding standards. Always use
|
||||
C-style comments.
|
||||
|
||||
======================================================================
|
||||
|
||||
OVERVIEW OF JAVA
|
||||
|
||||
Emacs developers do not know Java, and there is no reason they should
|
||||
have to. Thus, the code in this directory is confined to what is
|
||||
strictly necessary to support Emacs, and only uses a subset of Java
|
||||
written in a way that is easily understandable to C programmers.
|
||||
|
||||
Java is required because the entire Android runtime is based around
|
||||
Java, and there is no way to write an Android program which runs
|
||||
without Java.
|
||||
|
||||
This text exists to prime other Emacs developers, already familar with
|
||||
C, on the basic architecture of the Android port, and to teach them
|
||||
how to read and write the Java code found in this directory.
|
||||
|
||||
Java is an object oriented language with automatic memory management
|
||||
compiled down to bytecode, which is then subject to interpretation by
|
||||
a Java virtual machine.
|
||||
|
||||
What that means, is that:
|
||||
|
||||
struct emacs_window
|
||||
{
|
||||
int some_fields;
|
||||
int of_emacs_window;
|
||||
};
|
||||
|
||||
static void
|
||||
do_something_with_emacs_window (struct emacs_window *a, int n)
|
||||
{
|
||||
a->some_fields = a->of_emacs_window + n;
|
||||
}
|
||||
|
||||
would be written:
|
||||
|
||||
public class EmacsWindow
|
||||
{
|
||||
public int someFields;
|
||||
public int ofEmacsWindow;
|
||||
|
||||
public void
|
||||
doSomething (int n)
|
||||
{
|
||||
someFields = ofEmacsWindow + n;
|
||||
}
|
||||
}
|
||||
|
||||
and instead of doing:
|
||||
|
||||
do_something_with_emacs_window (my_window, 1);
|
||||
|
||||
you say:
|
||||
|
||||
myWindow.doSomething (1);
|
||||
|
||||
In addition to functions associated with an object of a given class
|
||||
(such as EmacsWindow), Java also has two other kinds of functions.
|
||||
|
||||
The first are so-called ``static'' functions (the static means
|
||||
something entirely different from what it does in C.)
|
||||
|
||||
A static function, while still having to be defined within a class,
|
||||
can be called without any object. Instead of the object, you write
|
||||
the name of the Java class within which it is defined. For example,
|
||||
the following C code:
|
||||
|
||||
int
|
||||
multiply_a_with_b_and_then_add_c (int a, int b, int c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
would be:
|
||||
|
||||
public class EmacsSomething
|
||||
{
|
||||
public static int
|
||||
multiplyAWithBAndThenAddC (int a, int b, int c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
};
|
||||
|
||||
Then, instead of calling:
|
||||
|
||||
int foo;
|
||||
|
||||
foo = multiply_a_with_b_then_add_c (1, 2, 3);
|
||||
|
||||
you say:
|
||||
|
||||
int foo;
|
||||
|
||||
foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
|
||||
|
||||
In Java, ``static'' does not mean that the function is only used
|
||||
within its compilation unit! Instead, the ``private'' qualifier is
|
||||
used to mean more or less the same thing:
|
||||
|
||||
static void
|
||||
this_procedure_is_only_used_within_this_file (void)
|
||||
{
|
||||
do_something ();
|
||||
}
|
||||
|
||||
becomes
|
||||
|
||||
public class EmacsSomething
|
||||
{
|
||||
private static void
|
||||
thisProcedureIsOnlyUsedWithinThisClass ()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
the other kind are called ``constructors''. They are functions that
|
||||
must be called to allocate memory to hold a class:
|
||||
|
||||
public class EmacsFoo
|
||||
{
|
||||
int bar;
|
||||
|
||||
public
|
||||
EmacsFoo (int tokenA, int tokenB)
|
||||
{
|
||||
bar = tokenA + tokenB;
|
||||
}
|
||||
}
|
||||
|
||||
now, the following statement:
|
||||
|
||||
EmacsFoo foo;
|
||||
|
||||
foo = new EmacsFoo (1, 2);
|
||||
|
||||
becomes more or less equivalent to the following C code:
|
||||
|
||||
struct emacs_foo
|
||||
{
|
||||
int bar;
|
||||
};
|
||||
|
||||
struct emacs_foo *
|
||||
make_emacs_foo (int token_a, int token_b)
|
||||
{
|
||||
struct emacs_foo *foo;
|
||||
|
||||
foo = xmalloc (sizeof *foo);
|
||||
foo->bar = token_a + token_b;
|
||||
|
||||
return foo;
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
struct emacs_foo *foo;
|
||||
|
||||
foo = make_emacs_foo (1, 2);
|
||||
|
||||
A class may have any number of constructors, or no constructors at
|
||||
all, in which case the compiler inserts an empty constructor.
|
||||
|
||||
|
||||
|
||||
Sometimes, you will see Java code that looks like this:
|
||||
|
||||
allFiles = filesDirectory.listFiles (new FileFilter () {
|
||||
@Override
|
||||
public boolean
|
||||
accept (File file)
|
||||
{
|
||||
return (!file.isDirectory ()
|
||||
&& file.getName ().endsWith (".pdmp"));
|
||||
}
|
||||
});
|
||||
|
||||
This is Java's version of GCC's nested function extension. The major
|
||||
difference is that the nested function may still be called even after
|
||||
it goes out of scope, and always retains a reference to the class and
|
||||
local variables around where it was called.
|
||||
|
||||
Being an object-oriented language, Java also allows defining that a
|
||||
class ``extends'' another class. The following C code:
|
||||
|
||||
struct a
|
||||
{
|
||||
long thirty_two;
|
||||
};
|
||||
|
||||
struct b
|
||||
{
|
||||
struct a a;
|
||||
long long sixty_four;
|
||||
};
|
||||
|
||||
extern void do_something (struct a *);
|
||||
|
||||
void
|
||||
my_function (struct b *b)
|
||||
{
|
||||
do_something (&b->a);
|
||||
}
|
||||
|
||||
is roughly equivalent to the following Java code, split into two
|
||||
files:
|
||||
|
||||
A.java
|
||||
|
||||
public class A
|
||||
{
|
||||
int thirtyTwo;
|
||||
|
||||
public void
|
||||
doSomething ()
|
||||
{
|
||||
etcEtcEtc ();
|
||||
}
|
||||
};
|
||||
|
||||
B.java
|
||||
|
||||
public class B extends A
|
||||
{
|
||||
long sixty_four;
|
||||
|
||||
public static void
|
||||
myFunction (B b)
|
||||
{
|
||||
b.doSomething ();
|
||||
}
|
||||
}
|
||||
|
||||
the Java runtime has transformed the call to ``b.doSomething'' to
|
||||
``((A) b).doSomething''.
|
||||
|
||||
However, Java also allows overriding this behavior, by specifying the
|
||||
@Override keyword:
|
||||
|
||||
public class B extends A
|
||||
{
|
||||
long sixty_four;
|
||||
|
||||
@Override
|
||||
public void
|
||||
doSomething ()
|
||||
{
|
||||
Something.doSomethingTwo ();
|
||||
super.doSomething ();
|
||||
}
|
||||
}
|
||||
|
||||
now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
|
||||
will end up calling ``Something.doSomethingTwo'', before calling back
|
||||
to ``A.doSomething''. This override also applies in reverse; that is
|
||||
to say, even if you write:
|
||||
|
||||
((A) b).doSomething ();
|
||||
|
||||
B's version of doSomething will still be called, if ``b'' was created
|
||||
using ``new B ()''.
|
||||
|
||||
This mechanism is used extensively throughout the Java language and
|
||||
Android windowing APIs.
|
||||
|
||||
Elsewhere, you will encounter Java code that defines arrays:
|
||||
|
||||
public class EmacsFrobinicator
|
||||
{
|
||||
public static void
|
||||
emacsFrobinicate (int something)
|
||||
{
|
||||
int[] primesFromSomething;
|
||||
|
||||
primesFromSomething = new int[numberOfPrimes];
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
Java arrays are similar to C arrays in that they can not grow. But
|
||||
they are very much unlike C arrays in that they are always references
|
||||
(as opposed to decaying into pointers in only some situations), and
|
||||
contain information about their length.
|
||||
|
||||
If another function named ``frobinicate1'' takes an array as an
|
||||
argument, then it need not take the length of the array.
|
||||
|
||||
Instead, it may simply iterate over the array like so:
|
||||
|
||||
int i, k;
|
||||
|
||||
for (i = 0; i < array.length; ++i)
|
||||
{
|
||||
k = array[i];
|
||||
|
||||
Whatever.doSomethingWithK (k);
|
||||
}
|
||||
|
||||
The syntax used to define arrays is also slightly different. As
|
||||
arrays are always references, there is no way for you to tell the
|
||||
runtime to allocate an array of size N in a structure (class.)
|
||||
|
||||
Instead, if you need an array of that size, you must declare a field
|
||||
with the type of the array, and allocate the array inside the class's
|
||||
constructor, like so:
|
||||
|
||||
public class EmacsArrayContainer
|
||||
{
|
||||
public int[] myArray;
|
||||
|
||||
public
|
||||
EmacsArrayContainer ()
|
||||
{
|
||||
myArray = new array[10];
|
||||
}
|
||||
}
|
||||
|
||||
while in C, you could just have written:
|
||||
|
||||
struct emacs_array_container
|
||||
{
|
||||
int my_array[10];
|
||||
};
|
||||
|
||||
or, possibly even better,
|
||||
|
||||
typedef int emacs_array_container[10];
|
||||
|
||||
Alas, Java has no equivalent of `typedef'.
|
||||
|
||||
Like in C, Java string literals are delimited by double quotes.
|
||||
Unlike C, however, strings are not NULL-terminated arrays of
|
||||
characters, but a distinct type named ``String''. They store their
|
||||
own length, characters in Java's 16-bit ``char'' type, and are capable
|
||||
of holding NULL bytes.
|
||||
|
||||
Instead of writing:
|
||||
|
||||
wchar_t character;
|
||||
extern char *s;
|
||||
size_t s;
|
||||
|
||||
for (/* determine n, s in a loop. */)
|
||||
s += mbstowc (&character, s, n);
|
||||
|
||||
or:
|
||||
|
||||
const char *byte;
|
||||
|
||||
for (byte = my_string; *byte; ++byte)
|
||||
/* do something with *byte. */;
|
||||
|
||||
or perhaps even:
|
||||
|
||||
size_t length, i;
|
||||
char foo;
|
||||
|
||||
length = strlen (my_string);
|
||||
|
||||
for (i = 0; i < length; ++i)
|
||||
foo = my_string[i];
|
||||
|
||||
you write:
|
||||
|
||||
char foo;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < myString.length (); ++i)
|
||||
foo = myString.charAt (0);
|
||||
|
||||
Java also has stricter rules on what can be used as a truth value in a
|
||||
conditional. While in C, any non-zero value is true, Java requires
|
||||
that every truth value be of the boolean type ``boolean''.
|
||||
|
||||
What this means is that instead of simply writing:
|
||||
|
||||
if (foo || bar)
|
||||
|
||||
where foo can either be 1 or 0, and bar can either be NULL or a
|
||||
pointer to something, you must explicitly write:
|
||||
|
||||
if (foo != 0 || bar != null)
|
||||
|
||||
in Java.
|
||||
|
||||
JAVA NATIVE INTERFACE
|
||||
|
||||
Java also provides an interface for C code to interface with Java.
|
||||
|
||||
C functions exported from a shared library become static Java
|
||||
functions within a class, like so:
|
||||
|
||||
public class EmacsNative
|
||||
{
|
||||
/* Obtain the fingerprint of this build of Emacs. The fingerprint
|
||||
can be used to determine the dump file name. */
|
||||
public static native String getFingerprint ();
|
||||
|
||||
/* Set certain parameters before initializing Emacs.
|
||||
|
||||
assetManager must be the asset manager associated with the
|
||||
context that is loading Emacs. It is saved and remains for the
|
||||
remainder the lifetime of the Emacs process.
|
||||
|
||||
filesDir must be the package's data storage location for the
|
||||
current Android user.
|
||||
|
||||
libDir must be the package's data storage location for native
|
||||
libraries. It is used as PATH.
|
||||
|
||||
cacheDir must be the package's cache directory. It is used as
|
||||
the `temporary-file-directory'.
|
||||
|
||||
pixelDensityX and pixelDensityY are the DPI values that will be
|
||||
used by Emacs.
|
||||
|
||||
classPath must be the classpath of this app_process process, or
|
||||
NULL.
|
||||
|
||||
emacsService must be the EmacsService singleton, or NULL. */
|
||||
public static native void setEmacsParams (AssetManager assetManager,
|
||||
String filesDir,
|
||||
String libDir,
|
||||
String cacheDir,
|
||||
float pixelDensityX,
|
||||
float pixelDensityY,
|
||||
String classPath,
|
||||
EmacsService emacsService);
|
||||
}
|
||||
|
||||
Where the corresponding C functions are located in android.c, and
|
||||
loaded by the special invocation:
|
||||
|
||||
static
|
||||
{
|
||||
System.loadLibrary ("emacs");
|
||||
};
|
||||
|
||||
where ``static'' defines a section of code which will be run upon the
|
||||
object (containing class) being loaded. This is like:
|
||||
|
||||
__attribute__((constructor))
|
||||
|
||||
on systems where shared object constructors are supported.
|
||||
|
||||
See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
|
||||
for more details.
|
||||
|
||||
|
||||
|
||||
OVERVIEW OF ANDROID
|
||||
|
||||
When the Android system starts an application, it does not actually
|
||||
call the application's ``main'' function. It may not even start the
|
||||
application's process if one is already running.
|
||||
|
||||
Instead, Android is organized around components. When the user opens
|
||||
the ``Emacs'' icon, the Android system looks up and starts the
|
||||
component associated with the ``Emacs'' icon. In this case, the
|
||||
component is called an activity, and is declared in
|
||||
the AndroidManifest.xml in this directory:
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
This tells Android to start the activity defined in ``EmacsActivity''
|
||||
(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
|
||||
Android class ``Activity''.
|
||||
|
||||
To do so, the Android system creates an instance of ``EmacsActivity''
|
||||
and the window system window associated with it, and eventually calls:
|
||||
|
||||
Activity activity;
|
||||
|
||||
activity.onCreate (...);
|
||||
|
||||
But which ``onCreate'' is really called?
|
||||
It is actually the ``onCreate'' defined in EmacsActivity.java, as
|
||||
it overrides the ``onCreate'' defined in Android's own Activity class:
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
FrameLayout.LayoutParams params;
|
||||
Intent intent;
|
||||
|
||||
Then, this is what happens step-by-step within the ``onCreate''
|
||||
function:
|
||||
|
||||
/* See if Emacs should be started with -Q. */
|
||||
intent = getIntent ();
|
||||
EmacsService.needDashQ
|
||||
= intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
|
||||
false);
|
||||
|
||||
Here, Emacs obtains the intent (a request to start a component) which
|
||||
was used to start Emacs, and sets a special flag if it contains a
|
||||
request for Emacs to start with the ``-Q'' command-line argument.
|
||||
|
||||
/* Set the theme to one without a title bar. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
|
||||
else
|
||||
setTheme (android.R.style.Theme_NoTitleBar);
|
||||
|
||||
Next, Emacs sets an appropriate theme for the activity's associated
|
||||
window decorations.
|
||||
|
||||
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
|
||||
/* Make the frame layout. */
|
||||
layout = new FrameLayout (this);
|
||||
layout.setLayoutParams (params);
|
||||
|
||||
/* Set it as the content view. */
|
||||
setContentView (layout);
|
||||
|
||||
Then, Emacs creates a ``FrameLayout'', a widget that holds a single
|
||||
other widget, and makes it the activity's ``content view''.
|
||||
|
||||
The activity itself is a ``FrameLayout'', so the ``layout parameters''
|
||||
here apply to the FrameLayout itself, and not its children.
|
||||
|
||||
/* Maybe start the Emacs service if necessary. */
|
||||
EmacsService.startEmacsService (this);
|
||||
|
||||
And after that, Emacs calls the static function ``startEmacsService'',
|
||||
defined in the class ``EmacsService''. This starts the Emacs service
|
||||
component if necessary.
|
||||
|
||||
/* Add this activity to the list of available activities. */
|
||||
EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
|
||||
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
Finally, Emacs registers that this activity is now ready to receive
|
||||
top-level frames (windows) created from Lisp.
|
||||
|
||||
Activities come and go, but Emacs has to stay running in the mean
|
||||
time. Thus, Emacs also defines a ``service'', which is a long-running
|
||||
component that the Android system allows to run in the background.
|
||||
|
||||
Let us go back and review the definition of ``startEmacsService'':
|
||||
|
||||
public static void
|
||||
startEmacsService (Context context)
|
||||
{
|
||||
if (EmacsService.SERVICE == null)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
/* Start the Emacs service now. */
|
||||
context.startService (new Intent (context,
|
||||
EmacsService.class));
|
||||
else
|
||||
/* Display the permanant notification and start Emacs as a
|
||||
foreground service. */
|
||||
context.startForegroundService (new Intent (context,
|
||||
EmacsService.class));
|
||||
}
|
||||
}
|
||||
|
||||
If ``EmacsService.SERVICE'' does not yet exist, what this does is to
|
||||
tell the ``context'' (the equivalent of an Xlib Display *) to start a
|
||||
service defined by the class ``EmacsService''. Eventually, this
|
||||
results in ``EmacsService.onCreate'' being called:
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate ()
|
||||
{
|
||||
AssetManager manager;
|
||||
Context app_context;
|
||||
String filesDir, libDir, cacheDir, classPath;
|
||||
double pixelDensityX;
|
||||
double pixelDensityY;
|
||||
|
||||
Here is what this function does, step-by-step:
|
||||
|
||||
SERVICE = this;
|
||||
|
||||
First, it sets the special static variable ``SERVICE'' to ``this'',
|
||||
which is a pointer to the ``EmacsService' object that was created.
|
||||
|
||||
handler = new Handler (Looper.getMainLooper ());
|
||||
|
||||
Next, it creates a ``Handler'' object for the ``main looper''.
|
||||
This is a helper structure which allows executing code on the Android
|
||||
user interface thread.
|
||||
|
||||
manager = getAssets ();
|
||||
app_context = getApplicationContext ();
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
pixelDensityX = metrics.xdpi;
|
||||
pixelDensityY = metrics.ydpi;
|
||||
|
||||
Finally, it obtains:
|
||||
|
||||
- the asset manager, which is used to retrieve assets packaged
|
||||
into the Emacs application package.
|
||||
|
||||
- the application context, used to obtain application specific
|
||||
information.
|
||||
|
||||
- the display metrics, and from them, the X and Y densities in dots
|
||||
per inch.
|
||||
|
||||
Then, inside a ``try'' block:
|
||||
|
||||
try
|
||||
{
|
||||
/* Configure Emacs with the asset manager and other necessary
|
||||
parameters. */
|
||||
filesDir = app_context.getFilesDir ().getCanonicalPath ();
|
||||
libDir = getLibraryDirectory ();
|
||||
cacheDir = app_context.getCacheDir ().getCanonicalPath ();
|
||||
|
||||
It obtains the names of the Emacs home, shared library, and temporary
|
||||
file directories.
|
||||
|
||||
/* Now provide this application's apk file, so a recursive
|
||||
invocation of app_process (through android-emacs) can
|
||||
find EmacsNoninteractive. */
|
||||
classPath = getApkFile ();
|
||||
|
||||
The name of the Emacs application package.
|
||||
|
||||
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
|
||||
+ ", libDir = " + libDir + ", and classPath = " + classPath);
|
||||
|
||||
Prints a debug message to the Android system log with this
|
||||
information.
|
||||
|
||||
EmacsNative.setEmacsParams (manager, filesDir, libDir,
|
||||
cacheDir, (float) pixelDensityX,
|
||||
(float) pixelDensityY,
|
||||
classPath, this);
|
||||
|
||||
And calls the native function ``setEmacsParams'' (defined in
|
||||
android.c) to configure Emacs with this information.
|
||||
|
||||
/* Start the thread that runs Emacs. */
|
||||
thread = new EmacsThread (this, needDashQ);
|
||||
thread.start ();
|
||||
|
||||
Then, it allocates an ``EmacsThread'' object, and starts that thread.
|
||||
Inside that thread is where Emacs's C code runs.
|
||||
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
return;
|
||||
|
||||
And here is the purpose of the ``try'' block. Functions related to
|
||||
file names in Java will signal errors of various types upon failure.
|
||||
|
||||
This ``catch'' block means that the Java virtual machine will abort
|
||||
execution of the contents of the ``try'' block as soon as an error of
|
||||
type ``IOException'' is encountered, and begin executing the contents
|
||||
of the ``catch'' block.
|
||||
|
||||
Any failure of that type here is a crash, and
|
||||
``EmacsNative.emacsAbort'' is called to quickly abort the process to
|
||||
get a useful backtrace.
|
||||
}
|
||||
}
|
||||
|
||||
Now, let us look at the definition of the class ``EmacsThread'', found
|
||||
in org/gnu/emacs/EmacsThread.java:
|
||||
|
||||
public class EmacsThread extends Thread
|
||||
{
|
||||
/* Whether or not Emacs should be started -Q. */
|
||||
private boolean startDashQ;
|
||||
|
||||
public
|
||||
EmacsThread (EmacsService service, boolean startDashQ)
|
||||
{
|
||||
super ("Emacs main thread");
|
||||
this.startDashQ = startDashQ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
String args[];
|
||||
|
||||
if (!startDashQ)
|
||||
args = new String[] { "libandroid-emacs.so", };
|
||||
else
|
||||
args = new String[] { "libandroid-emacs.so", "-Q", };
|
||||
|
||||
/* Run the native code now. */
|
||||
EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
|
||||
}
|
||||
};
|
||||
|
||||
The class itself defines a single field, ``startDashQ'', a constructor
|
||||
with an unused argument of the type ``EmacsService'' (which is useful
|
||||
while debugging) and a flag ``startDashQ'', and a single function
|
||||
``run'', overriding the same function in the class ``Thread''.
|
||||
|
||||
When ``thread.start'' is called, the Java virtual machine creates a
|
||||
new thread, and then calls the function ``run'' within that thread.
|
||||
|
||||
This function then computes a suitable argument vector, and calls
|
||||
``EmacsNative.initEmacs'' (defined in android.c), which then calls a
|
||||
modified version of the regular Emacs ``main'' function.
|
||||
|
||||
At that point, Emacs initialization proceeds as usual:
|
||||
Vinitial_window_system is set, loadup.el calls `normal-top-level',
|
||||
which calls `command-line', and finally
|
||||
`window-system-initialization', which initializes the `android'
|
||||
terminal interface as usual.
|
||||
|
||||
What happens here is the same as on other platforms. Now, here is
|
||||
what happens when the initial frame is created: Fx_create_frame calls
|
||||
`android_create_frame_window' to create a top level window:
|
||||
|
||||
static void
|
||||
android_create_frame_window (struct frame *f)
|
||||
{
|
||||
struct android_set_window_attributes attributes;
|
||||
enum android_window_value_mask attribute_mask;
|
||||
|
||||
attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
|
||||
attribute_mask = ANDROID_CW_BACK_PIXEL;
|
||||
|
||||
block_input ();
|
||||
FRAME_ANDROID_WINDOW (f)
|
||||
= android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
|
||||
f->left_pos,
|
||||
f->top_pos,
|
||||
FRAME_PIXEL_WIDTH (f),
|
||||
FRAME_PIXEL_HEIGHT (f),
|
||||
attribute_mask, &attributes);
|
||||
unblock_input ();
|
||||
}
|
||||
|
||||
This calls the function `android_create_window' with some arguments
|
||||
whose meanings are identical to the arguments to `XCreateWindow'.
|
||||
|
||||
Here is the definition of `android_create_window', in android.c:
|
||||
|
||||
android_window
|
||||
android_create_window (android_window parent, int x, int y,
|
||||
int width, int height,
|
||||
enum android_window_value_mask value_mask,
|
||||
struct android_set_window_attributes *attrs)
|
||||
{
|
||||
static jclass class;
|
||||
static jmethodID constructor;
|
||||
jobject object, parent_object, old;
|
||||
android_window window;
|
||||
android_handle prev_max_handle;
|
||||
bool override_redirect;
|
||||
|
||||
What does it do? First, some context:
|
||||
|
||||
At any time, there can be at most 65535 Java objects referred to by
|
||||
the rest of Emacs through the Java native interface. Each such object
|
||||
is assigned a ``handle'' (similar to an XID on X) and given a unique
|
||||
type. The function `android_resolve_handle' returns the JNI `jobject'
|
||||
associated with a given handle.
|
||||
|
||||
parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
|
||||
|
||||
Here, it is being used to look up the `jobject' associated with the
|
||||
`parent' handle.
|
||||
|
||||
prev_max_handle = max_handle;
|
||||
window = android_alloc_id ();
|
||||
|
||||
Next, `max_handle' is saved, and a new handle is allocated for
|
||||
`window'.
|
||||
|
||||
if (!window)
|
||||
error ("Out of window handles!");
|
||||
|
||||
An error is signalled if Emacs runs out of available handles.
|
||||
|
||||
if (!class)
|
||||
{
|
||||
class = (*android_java_env)->FindClass (android_java_env,
|
||||
"org/gnu/emacs/EmacsWindow");
|
||||
assert (class != NULL);
|
||||
|
||||
Then, if this initialization has not yet been completed, Emacs
|
||||
proceeds to find the Java class named ``EmacsWindow''.
|
||||
|
||||
constructor
|
||||
= (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
|
||||
"(SLorg/gnu/emacs/EmacsWindow;"
|
||||
"IIIIZ)V");
|
||||
assert (constructor != NULL);
|
||||
|
||||
And it tries to look up the constructor, which should take seven
|
||||
arguments:
|
||||
|
||||
S - a short. (the handle ID)
|
||||
Lorg/gnu/Emacs/EmacsWindow; - an instance of the EmacsWindow
|
||||
class. (the parent)
|
||||
IIII - four ints. (the window geometry.)
|
||||
Z - a boolean. (whether or not the
|
||||
window is override-redirect; see
|
||||
XChangeWindowAttributes.)
|
||||
|
||||
old = class;
|
||||
class = (*android_java_env)->NewGlobalRef (android_java_env, class);
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
ANDROID_DELETE_LOCAL_REF (old);
|
||||
|
||||
Next, it saves a global reference to the class and deletes the local
|
||||
reference. Global references will never be deallocated by the Java
|
||||
virtual machine as long as they still exist.
|
||||
|
||||
if (!class)
|
||||
memory_full (0);
|
||||
}
|
||||
|
||||
/* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
|
||||
creation time. */
|
||||
override_redirect = ((value_mask
|
||||
& ANDROID_CW_OVERRIDE_REDIRECT)
|
||||
&& attrs->override_redirect);
|
||||
|
||||
object = (*android_java_env)->NewObject (android_java_env, class,
|
||||
constructor, (jshort) window,
|
||||
parent_object, (jint) x, (jint) y,
|
||||
(jint) width, (jint) height,
|
||||
(jboolean) override_redirect);
|
||||
|
||||
Then, it creates an instance of the ``EmacsWindow'' class with the
|
||||
appropriate arguments and previously determined constructor.
|
||||
|
||||
if (!object)
|
||||
{
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
|
||||
max_handle = prev_max_handle;
|
||||
memory_full (0);
|
||||
|
||||
If creating the object fails, Emacs clears the ``pending exception''
|
||||
and signals that it is out of memory.
|
||||
}
|
||||
|
||||
android_handles[window].type = ANDROID_HANDLE_WINDOW;
|
||||
android_handles[window].handle
|
||||
= (*android_java_env)->NewGlobalRef (android_java_env,
|
||||
object);
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
ANDROID_DELETE_LOCAL_REF (object);
|
||||
|
||||
Otherwise, it associates a new global reference to the object with the
|
||||
handle, and deletes the local reference returned from the JNI
|
||||
NewObject function.
|
||||
|
||||
if (!android_handles[window].handle)
|
||||
memory_full (0);
|
||||
|
||||
If allocating the global reference fails, Emacs signals that it is out
|
||||
of memory.
|
||||
|
||||
android_change_window_attributes (window, value_mask, attrs);
|
||||
return window;
|
||||
|
||||
Otherwise, it applies the specified window attributes and returns the
|
||||
handle of the new window.
|
||||
}
|
||||
364
java/debug.sh
Executable file
364
java/debug.sh
Executable file
|
|
@ -0,0 +1,364 @@
|
|||
#!/bin/bash
|
||||
### Run Emacs under GDB or JDB on Android.
|
||||
|
||||
## Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
## This file is part of GNU Emacs.
|
||||
|
||||
## GNU Emacs is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
|
||||
## GNU Emacs is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
set -m
|
||||
oldpwd=`pwd`
|
||||
cd `dirname $0`
|
||||
|
||||
devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
|
||||
device=
|
||||
progname=$0
|
||||
package=org.gnu.emacs
|
||||
activity=org.gnu.emacs.EmacsActivity
|
||||
gdb_port=5039
|
||||
jdb_port=64013
|
||||
jdb=no
|
||||
attach_existing=no
|
||||
gdbserver=
|
||||
gdb=gdb
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
## This option specifies the serial number of a device to use.
|
||||
"--device" )
|
||||
device="$2"
|
||||
if [ -z device ]; then
|
||||
echo "You must specify an argument to --device"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
"--help" )
|
||||
echo "Usage: $progname [options] -- [gdb options]"
|
||||
echo ""
|
||||
echo " --device DEVICE run Emacs on the specified device"
|
||||
echo " --port PORT run the GDB server on a specific port"
|
||||
echo " --jdb-port PORT run the JDB server on a specific port"
|
||||
echo " --jdb run JDB instead of GDB"
|
||||
echo " --gdb use specified GDB binary"
|
||||
echo " --attach-existing attach to an existing process"
|
||||
echo " --gdbserver BINARY upload and use the specified gdbserver binary"
|
||||
echo " --help print this message"
|
||||
echo ""
|
||||
echo "Available devices:"
|
||||
for device in $devices; do
|
||||
echo " " $device
|
||||
done
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
"--jdb" )
|
||||
jdb=yes
|
||||
;;
|
||||
"--gdb" )
|
||||
shift
|
||||
gdb=$1
|
||||
;;
|
||||
"--gdbserver" )
|
||||
shift
|
||||
gdbserver=$1
|
||||
;;
|
||||
"--port" )
|
||||
shift
|
||||
gdb_port=$1
|
||||
;;
|
||||
"--jdb-port" )
|
||||
shift
|
||||
jdb_port=$1
|
||||
;;
|
||||
"--attach-existing" )
|
||||
attach_existing=yes
|
||||
;;
|
||||
"--" )
|
||||
shift
|
||||
gdbargs=$@
|
||||
break;
|
||||
;;
|
||||
* )
|
||||
echo "$progname: Unrecognized argument $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$devices" ]; then
|
||||
echo "No devices are available."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $device ]; then
|
||||
device=$devices
|
||||
fi
|
||||
|
||||
if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
|
||||
echo "Multiple devices are available. Please pick one using"
|
||||
echo "--device and try again."
|
||||
fi
|
||||
|
||||
echo "Looking for $package on device $device"
|
||||
|
||||
# Find the application data directory
|
||||
app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
|
||||
|
||||
if [ -z $app_data_dir ]; then
|
||||
echo "The data directory for the package $package was not found."
|
||||
echo "Is it installed?"
|
||||
fi
|
||||
|
||||
echo "Found application data directory at" "$app_data_dir"
|
||||
|
||||
# Generate an awk script to extract PIDs from Android ps output. It
|
||||
# is enough to run `ps' as the package user on newer versions of
|
||||
# Android, but that doesn't work on Android 2.3.
|
||||
cat << EOF > tmp.awk
|
||||
BEGIN {
|
||||
pid = 0;
|
||||
pid_column = 2;
|
||||
}
|
||||
|
||||
{
|
||||
# Remove any trailing carriage return from the input line.
|
||||
gsub ("\r", "", \$NF)
|
||||
|
||||
# If this is line 1, figure out which column contains the PID.
|
||||
if (NR == 1)
|
||||
{
|
||||
for (n = 1; n <= NF; ++n)
|
||||
{
|
||||
if (\$n == "PID")
|
||||
pid_column=n;
|
||||
}
|
||||
}
|
||||
else if (\$NF == "$package")
|
||||
print \$pid_column
|
||||
}
|
||||
EOF
|
||||
|
||||
# Make sure that file disappears once this script exits.
|
||||
trap "rm -f $(pwd)/tmp.awk" 0
|
||||
|
||||
# First, run ps to fetch the list of process IDs.
|
||||
package_pids=`adb -s $device shell ps`
|
||||
|
||||
# Next, extract the list of PIDs currently running.
|
||||
package_pids=`awk -f tmp.awk <<< $package_pids`
|
||||
|
||||
if [ "$attach_existing" != "yes" ]; then
|
||||
# Finally, kill each existing process.
|
||||
for pid in $package_pids; do
|
||||
echo "Killing existing process $pid..."
|
||||
adb -s $device shell run-as $package kill -9 $pid &> /dev/null
|
||||
done
|
||||
|
||||
# Now run the main activity. This must be done as the adb user and
|
||||
# not as the package user.
|
||||
echo "Starting activity $activity and attaching debugger"
|
||||
|
||||
# Exit if the activity could not be started.
|
||||
adb -s $device shell am start -D -n "$package/$activity"
|
||||
if [ ! $? ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Sleep for a bit. Otherwise, the process may not have started
|
||||
# yet.
|
||||
sleep 1
|
||||
|
||||
# Now look for processes matching the package again.
|
||||
package_pids=`adb -s $device shell ps`
|
||||
|
||||
# Next, remove lines matching "ps" itself.
|
||||
package_pids=`awk -f tmp.awk <<< $package_pids`
|
||||
fi
|
||||
|
||||
pid=$package_pids
|
||||
num_pids=`wc -w <<< "$package_pids"`
|
||||
|
||||
if [ $num_pids -gt 1 ]; then
|
||||
echo "More than one process was started:"
|
||||
echo ""
|
||||
adb -s $device shell run-as $package ps | awk -- "{
|
||||
if (!match (\$0, /ps/) && match (\$0, /$package/))
|
||||
print \$0
|
||||
}"
|
||||
echo ""
|
||||
printf "Which one do you want to attach to? "
|
||||
read pid
|
||||
elif [ -z $package_pids ]; then
|
||||
echo "No processes were found to attach to."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If either --jdb was specified or debug.sh is not connecting to an
|
||||
# existing process, then store a suitable JDB invocation in
|
||||
# jdb_command. GDB will then run JDB to unblock the application from
|
||||
# the wait dialog after startup.
|
||||
|
||||
if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
|
||||
adb -s $device forward --remove-all
|
||||
adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
|
||||
|
||||
if [ ! $? ]; then
|
||||
echo "Failed to forward jdwp:$pid to $jdb_port!"
|
||||
echo "Perhaps you need to specify a different port with --port?"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
jdb_command="jdb -connect \
|
||||
com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
|
||||
|
||||
if [ $jdb = "yes" ]; then
|
||||
# Just start JDB and then exit
|
||||
$jdb_command
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$jdb_command" ]; then
|
||||
echo "Starting JDB to unblock application."
|
||||
|
||||
# Start JDB to unblock the application.
|
||||
coproc JDB { $jdb_command; }
|
||||
|
||||
# Tell JDB to first suspend all threads.
|
||||
echo "suspend" >&${JDB[1]}
|
||||
|
||||
# Tell JDB to print a magic string once the program is
|
||||
# initialized.
|
||||
echo "print \"__verify_jdb_has_started__\"" >&${JDB[1]}
|
||||
|
||||
# Now wait for JDB to give the string back.
|
||||
line=
|
||||
while :; do
|
||||
read -u ${JDB[0]} line
|
||||
if [ ! $? ]; then
|
||||
echo "Failed to read JDB output"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$line" in
|
||||
*__verify_jdb_has_started__*)
|
||||
# Android only polls for a Java debugger every 200ms, so
|
||||
# the debugger must be connected for at least that long.
|
||||
echo "Pausing 1 second for the program to continue."
|
||||
sleep 1
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Note that JDB does not exit until GDB is fully attached!
|
||||
fi
|
||||
|
||||
# See if gdbserver has to be uploaded
|
||||
gdbserver_cmd=
|
||||
is_root=
|
||||
if [ -z "$gdbserver" ]; then
|
||||
gdbserver_bin=/system/bin/gdbserver
|
||||
else
|
||||
gdbserver_bin=/data/local/tmp/gdbserver
|
||||
gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
|
||||
\"tee gdbserver > /dev/null\""
|
||||
|
||||
# Upload the specified gdbserver binary to the device.
|
||||
adb -s $device push "$gdbserver" "$gdbserver_bin"
|
||||
|
||||
if (adb -s $device shell ls /system/bin | grep -G tee); then
|
||||
# Copy it to the user directory.
|
||||
adb -s $device shell "$gdbserver_cat"
|
||||
adb -s $device shell "run-as $package chmod 777 gdbserver"
|
||||
gdbserver_cmd="./gdbserver"
|
||||
else
|
||||
# Hopefully this is an old version of Android which allows
|
||||
# execution from /data/local/tmp. Its `chmod' doesn't support
|
||||
# `+x' either.
|
||||
adb -s $device shell "chmod 777 $gdbserver_bin"
|
||||
gdbserver_cmd="$gdbserver_bin"
|
||||
|
||||
# If the user is root, then there is no need to open any kind
|
||||
# of TCP socket.
|
||||
if (adb -s $device shell id | grep -G root); then
|
||||
gdbserver=
|
||||
is_root=yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Now start gdbserver on the device asynchronously.
|
||||
|
||||
echo "Attaching gdbserver to $pid on $device..."
|
||||
exec 5<> /tmp/file-descriptor-stamp
|
||||
rm -f /tmp/file-descriptor-stamp
|
||||
|
||||
if [ -z "$gdbserver" ]; then
|
||||
if [ "$is_root" = "yes" ]; then
|
||||
adb -s $device shell $gdbserver_bin --once \
|
||||
"+/data/local/tmp/debug.$package.socket" --attach $pid >&5 &
|
||||
gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
|
||||
else
|
||||
adb -s $device shell run-as $package $gdbserver_bin --once \
|
||||
"+debug.$package.socket" --attach $pid >&5 &
|
||||
gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
|
||||
fi
|
||||
else
|
||||
# Normally the program cannot access $gdbserver_bin when it is
|
||||
# placed in /data/local/tmp.
|
||||
adb -s $device shell run-as $package $gdbserver_cmd --once \
|
||||
"0.0.0.0:7654" --attach $pid >&5 &
|
||||
gdb_socket="tcp:7654"
|
||||
fi
|
||||
|
||||
# Wait until gdbserver successfully runs.
|
||||
line=
|
||||
while read -u 5 line; do
|
||||
case "$line" in
|
||||
*Attached* )
|
||||
break;
|
||||
;;
|
||||
*error* | *Error* | failed )
|
||||
echo "GDB error:" $line
|
||||
exit 1
|
||||
;;
|
||||
* )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Now that GDB is attached, tell the Java debugger to resume execution
|
||||
# and then exit.
|
||||
|
||||
if [ -n "$jdb_command" ]; then
|
||||
echo "resume" >&${JDB[1]}
|
||||
echo "exit" >&${JDB[1]}
|
||||
fi
|
||||
|
||||
# Forward the gdb server port here.
|
||||
adb -s $device forward "tcp:$gdb_port" $gdb_socket
|
||||
if [ ! $? ]; then
|
||||
echo "Failed to forward $app_data_dir/debug.$package.socket"
|
||||
echo "to $gdb_port! Perhaps you need to specify a different port"
|
||||
echo "with --port?"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Finally, start gdb with any extra arguments needed.
|
||||
cd "$oldpwd"
|
||||
$gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
|
||||
BIN
java/emacs.keystore
Normal file
BIN
java/emacs.keystore
Normal file
Binary file not shown.
434
java/org/gnu/emacs/EmacsActivity.java
Normal file
434
java/org/gnu/emacs/EmacsActivity.java
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.lang.IllegalStateException;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class EmacsActivity extends Activity
|
||||
implements EmacsWindowAttachmentManager.WindowConsumer,
|
||||
ViewTreeObserver.OnGlobalLayoutListener
|
||||
{
|
||||
public static final String TAG = "EmacsActivity";
|
||||
|
||||
/* The currently attached EmacsWindow, or null if none. */
|
||||
private EmacsWindow window;
|
||||
|
||||
/* The frame layout associated with the activity. */
|
||||
private FrameLayout layout;
|
||||
|
||||
/* List of activities with focus. */
|
||||
public static List<EmacsActivity> focusedActivities;
|
||||
|
||||
/* The last activity to have been focused. */
|
||||
public static EmacsActivity lastFocusedActivity;
|
||||
|
||||
/* The currently focused window. */
|
||||
public static EmacsWindow focusedWindow;
|
||||
|
||||
/* Whether or not this activity is paused. */
|
||||
private boolean isPaused;
|
||||
|
||||
/* Whether or not this activity is fullscreen. */
|
||||
private boolean isFullscreen;
|
||||
|
||||
/* The last context menu to be closed. */
|
||||
private Menu lastClosedMenu;
|
||||
|
||||
static
|
||||
{
|
||||
focusedActivities = new ArrayList<EmacsActivity> ();
|
||||
};
|
||||
|
||||
public static void
|
||||
invalidateFocus1 (EmacsWindow window)
|
||||
{
|
||||
if (window.view.isFocused ())
|
||||
focusedWindow = window;
|
||||
|
||||
for (EmacsWindow child : window.children)
|
||||
invalidateFocus1 (child);
|
||||
}
|
||||
|
||||
public static void
|
||||
invalidateFocus ()
|
||||
{
|
||||
EmacsWindow oldFocus;
|
||||
|
||||
/* Walk through each focused activity and assign the window focus
|
||||
to the bottom-most focused window within. Record the old focus
|
||||
as well. */
|
||||
oldFocus = focusedWindow;
|
||||
focusedWindow = null;
|
||||
|
||||
for (EmacsActivity activity : focusedActivities)
|
||||
{
|
||||
if (activity.window != null)
|
||||
invalidateFocus1 (activity.window);
|
||||
}
|
||||
|
||||
/* Send focus in- and out- events to the previous and current
|
||||
focus. */
|
||||
|
||||
if (oldFocus != null)
|
||||
EmacsNative.sendFocusOut (oldFocus.handle,
|
||||
System.currentTimeMillis ());
|
||||
|
||||
if (focusedWindow != null)
|
||||
EmacsNative.sendFocusIn (focusedWindow.handle,
|
||||
System.currentTimeMillis ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
detachWindow ()
|
||||
{
|
||||
syncFullscreenWith (null);
|
||||
|
||||
if (window == null)
|
||||
Log.w (TAG, "detachWindow called, but there is no window");
|
||||
else
|
||||
{
|
||||
/* Clear the window's pointer to this activity and remove the
|
||||
window's view. */
|
||||
window.setConsumer (null);
|
||||
|
||||
/* The window can't be iconified any longer. */
|
||||
window.noticeDeiconified ();
|
||||
layout.removeView (window.view);
|
||||
window = null;
|
||||
|
||||
invalidateFocus ();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
attachWindow (EmacsWindow child)
|
||||
{
|
||||
Log.d (TAG, "attachWindow: " + child);
|
||||
|
||||
if (window != null)
|
||||
throw new IllegalStateException ("trying to attach window when one"
|
||||
+ " already exists");
|
||||
|
||||
syncFullscreenWith (child);
|
||||
|
||||
/* Record and attach the view. */
|
||||
|
||||
window = child;
|
||||
layout.addView (window.view);
|
||||
child.setConsumer (this);
|
||||
|
||||
/* If the window isn't no-focus-on-map, focus its view. */
|
||||
if (!child.getDontFocusOnMap ())
|
||||
window.view.requestFocus ();
|
||||
|
||||
/* If the activity is iconified, send that to the window. */
|
||||
if (isPaused)
|
||||
window.noticeIconified ();
|
||||
|
||||
/* Invalidate the focus. */
|
||||
invalidateFocus ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
destroy ()
|
||||
{
|
||||
finish ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final EmacsWindow
|
||||
getAttachedWindow ()
|
||||
{
|
||||
return window;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
FrameLayout.LayoutParams params;
|
||||
Intent intent;
|
||||
View decorView;
|
||||
ViewTreeObserver observer;
|
||||
int matchParent;
|
||||
|
||||
/* See if Emacs should be started with -Q. */
|
||||
intent = getIntent ();
|
||||
EmacsService.needDashQ
|
||||
= intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
|
||||
false);
|
||||
|
||||
matchParent = FrameLayout.LayoutParams.MATCH_PARENT;
|
||||
params
|
||||
= new FrameLayout.LayoutParams (matchParent,
|
||||
matchParent);
|
||||
|
||||
/* Make the frame layout. */
|
||||
layout = new FrameLayout (this);
|
||||
layout.setLayoutParams (params);
|
||||
|
||||
/* Set it as the content view. */
|
||||
setContentView (layout);
|
||||
|
||||
/* Maybe start the Emacs service if necessary. */
|
||||
EmacsService.startEmacsService (this);
|
||||
|
||||
/* Add this activity to the list of available activities. */
|
||||
EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
|
||||
|
||||
/* Start observing global layout changes between Jelly Bean and Q.
|
||||
This is required to restore the fullscreen state whenever the
|
||||
on screen keyboard is displayed, as there is otherwise no way
|
||||
to determine when the on screen keyboard becomes visible. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
|
||||
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
{
|
||||
decorView = getWindow ().getDecorView ();
|
||||
observer = decorView.getViewTreeObserver ();
|
||||
observer.addOnGlobalLayoutListener (this);
|
||||
}
|
||||
|
||||
super.onCreate (savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onGlobalLayout ()
|
||||
{
|
||||
syncFullscreenWith (window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onDestroy ()
|
||||
{
|
||||
EmacsWindowAttachmentManager manager;
|
||||
boolean isMultitask;
|
||||
|
||||
manager = EmacsWindowAttachmentManager.MANAGER;
|
||||
|
||||
/* The activity will die shortly hereafter. If there is a window
|
||||
attached, close it now. */
|
||||
Log.d (TAG, "onDestroy " + this);
|
||||
isMultitask = this instanceof EmacsMultitaskActivity;
|
||||
manager.removeWindowConsumer (this, isMultitask || isFinishing ());
|
||||
focusedActivities.remove (this);
|
||||
invalidateFocus ();
|
||||
|
||||
/* Remove this activity from the static field, lest it leak. */
|
||||
if (lastFocusedActivity == this)
|
||||
lastFocusedActivity = null;
|
||||
|
||||
super.onDestroy ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onWindowFocusChanged (boolean isFocused)
|
||||
{
|
||||
Log.d (TAG, ("onWindowFocusChanged: "
|
||||
+ (isFocused ? "YES" : "NO")));
|
||||
|
||||
if (isFocused && !focusedActivities.contains (this))
|
||||
{
|
||||
focusedActivities.add (this);
|
||||
lastFocusedActivity = this;
|
||||
|
||||
/* Update the window insets as the focus change may have
|
||||
changed the window insets as well, and the system does not
|
||||
automatically restore visibility flags. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
|
||||
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.R
|
||||
&& isFullscreen)
|
||||
syncFullscreenWith (window);
|
||||
}
|
||||
else
|
||||
focusedActivities.remove (this);
|
||||
|
||||
invalidateFocus ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onPause ()
|
||||
{
|
||||
isPaused = true;
|
||||
|
||||
EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
|
||||
super.onPause ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onResume ()
|
||||
{
|
||||
isPaused = false;
|
||||
|
||||
EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
|
||||
super.onResume ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onContextMenuClosed (Menu menu)
|
||||
{
|
||||
int serial;
|
||||
|
||||
Log.d (TAG, "onContextMenuClosed: " + menu);
|
||||
|
||||
/* See the comment inside onMenuItemClick. */
|
||||
|
||||
if (((EmacsContextMenu.wasSubmenuSelected == -2)
|
||||
|| (EmacsContextMenu.wasSubmenuSelected >= 0
|
||||
&& ((System.currentTimeMillis ()
|
||||
- EmacsContextMenu.wasSubmenuSelected)
|
||||
<= 300)))
|
||||
|| menu == lastClosedMenu)
|
||||
{
|
||||
EmacsContextMenu.wasSubmenuSelected = -1;
|
||||
lastClosedMenu = menu;
|
||||
return;
|
||||
}
|
||||
|
||||
/* lastClosedMenu is set because Android apparently calls this
|
||||
function twice. */
|
||||
|
||||
lastClosedMenu = null;
|
||||
|
||||
/* Send a context menu event given that no menu item has already
|
||||
been selected. */
|
||||
if (!EmacsContextMenu.itemAlreadySelected)
|
||||
{
|
||||
serial = EmacsContextMenu.lastMenuEventSerial;
|
||||
EmacsNative.sendContextMenu ((short) 0, 0,
|
||||
serial);
|
||||
}
|
||||
|
||||
super.onContextMenuClosed (menu);
|
||||
}
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
public final void
|
||||
syncFullscreenWith (EmacsWindow emacsWindow)
|
||||
{
|
||||
WindowInsetsController controller;
|
||||
Window window;
|
||||
int behavior, flags;
|
||||
View view;
|
||||
|
||||
if (emacsWindow != null)
|
||||
isFullscreen = emacsWindow.fullscreen;
|
||||
else
|
||||
isFullscreen = false;
|
||||
|
||||
/* On Android 11 or later, use the window insets controller to
|
||||
control whether or not the view is fullscreen. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
{
|
||||
window = getWindow ();
|
||||
|
||||
/* If there is no attached window, return immediately. */
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
behavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
|
||||
controller = window.getInsetsController ();
|
||||
controller.setSystemBarsBehavior (behavior);
|
||||
|
||||
if (isFullscreen)
|
||||
controller.hide (WindowInsets.Type.statusBars ()
|
||||
| WindowInsets.Type.navigationBars ());
|
||||
else
|
||||
controller.show (WindowInsets.Type.statusBars ()
|
||||
| WindowInsets.Type.navigationBars ());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
{
|
||||
/* On Android 4.1 or later, use `setSystemUiVisibility'. */
|
||||
|
||||
window = getWindow ();
|
||||
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
view = window.getDecorView ();
|
||||
|
||||
if (isFullscreen)
|
||||
{
|
||||
flags = 0;
|
||||
flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
{
|
||||
/* These flags means that Emacs will be full screen as
|
||||
long as the state flag is set. */
|
||||
flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE;
|
||||
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
}
|
||||
|
||||
/* Apply the given flags. */
|
||||
view.setSystemUiVisibility (flags);
|
||||
}
|
||||
else
|
||||
view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void
|
||||
onAttachedToWindow ()
|
||||
{
|
||||
super.onAttachedToWindow ();
|
||||
|
||||
/* Update the window insets. */
|
||||
syncFullscreenWith (window);
|
||||
}
|
||||
};
|
||||
81
java/org/gnu/emacs/EmacsApplication.java
Normal file
81
java/org/gnu/emacs/EmacsApplication.java
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
public final class EmacsApplication extends Application
|
||||
{
|
||||
private static final String TAG = "EmacsApplication";
|
||||
|
||||
/* The name of the dump file to use. */
|
||||
public static String dumpFileName;
|
||||
|
||||
public static void
|
||||
findDumpFile (Context context)
|
||||
{
|
||||
File filesDirectory;
|
||||
File[] allFiles;
|
||||
String wantedDumpFile;
|
||||
int i;
|
||||
|
||||
wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
|
||||
+ ".pdmp");
|
||||
|
||||
/* Obtain a list of all files ending with ``.pdmp''. Then, look
|
||||
for a file named ``emacs-<fingerprint>.pdmp'' and delete the
|
||||
rest. */
|
||||
filesDirectory = context.getFilesDir ();
|
||||
|
||||
allFiles = filesDirectory.listFiles (new FileFilter () {
|
||||
@Override
|
||||
public boolean
|
||||
accept (File file)
|
||||
{
|
||||
return (!file.isDirectory ()
|
||||
&& file.getName ().endsWith (".pdmp"));
|
||||
}
|
||||
});
|
||||
|
||||
/* Now try to find the right dump file. */
|
||||
for (i = 0; i < allFiles.length; ++i)
|
||||
{
|
||||
if (allFiles[i].getName ().equals (wantedDumpFile))
|
||||
dumpFileName = allFiles[i].getAbsolutePath ();
|
||||
else
|
||||
/* Delete this outdated dump file. */
|
||||
allFiles[i].delete ();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate ()
|
||||
{
|
||||
findDumpFile (this);
|
||||
super.onCreate ();
|
||||
}
|
||||
};
|
||||
47
java/org/gnu/emacs/EmacsClipboard.java
Normal file
47
java/org/gnu/emacs/EmacsClipboard.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/* This class provides helper code for accessing the clipboard,
|
||||
abstracting between the different interfaces on API 8 and 11. */
|
||||
|
||||
public abstract class EmacsClipboard
|
||||
{
|
||||
public abstract void setClipboard (byte[] bytes);
|
||||
public abstract int ownsClipboard ();
|
||||
public abstract boolean clipboardExists ();
|
||||
public abstract byte[] getClipboard ();
|
||||
|
||||
public abstract byte[][] getClipboardTargets ();
|
||||
public abstract long[] getClipboardData (byte[] target);
|
||||
|
||||
/* Create the correct kind of clipboard for this system. */
|
||||
|
||||
public static EmacsClipboard
|
||||
makeClipboard ()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
return new EmacsSdk11Clipboard ();
|
||||
else
|
||||
return new EmacsSdk8Clipboard ();
|
||||
}
|
||||
};
|
||||
374
java/org/gnu/emacs/EmacsContextMenu.java
Normal file
374
java/org/gnu/emacs/EmacsContextMenu.java
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.SubMenu;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/* Context menu implementation. This object is built from JNI and
|
||||
describes a menu hiearchy. Then, `inflate' can turn it into an
|
||||
Android menu, which can be turned into a popup (or other kind of)
|
||||
menu. */
|
||||
|
||||
public final class EmacsContextMenu
|
||||
{
|
||||
private static final String TAG = "EmacsContextMenu";
|
||||
|
||||
/* Whether or not an item was selected. */
|
||||
public static boolean itemAlreadySelected;
|
||||
|
||||
/* Whether or not a submenu was selected.
|
||||
Value is -1 if no; value is -2 if yes, and a context menu
|
||||
close event will definitely be sent. Any other value is
|
||||
the timestamp when the submenu was selected. */
|
||||
public static long wasSubmenuSelected;
|
||||
|
||||
/* The serial ID of the last context menu to be displayed. */
|
||||
public static int lastMenuEventSerial;
|
||||
|
||||
/* The last group ID used for a menu item. */
|
||||
public int lastGroupId;
|
||||
|
||||
private static class Item implements MenuItem.OnMenuItemClickListener
|
||||
{
|
||||
public int itemID;
|
||||
public String itemName, tooltip;
|
||||
public EmacsContextMenu subMenu;
|
||||
public boolean isEnabled, isCheckable, isChecked;
|
||||
public EmacsView inflatedView;
|
||||
public boolean isRadio;
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
onMenuItemClick (MenuItem item)
|
||||
{
|
||||
Log.d (TAG, "onMenuItemClick: " + itemName + " (" + itemID + ")");
|
||||
|
||||
if (subMenu != null)
|
||||
{
|
||||
/* Android 6.0 and earlier don't support nested submenus
|
||||
properly, so display the submenu popup by hand. */
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
{
|
||||
Log.d (TAG, "onMenuItemClick: displaying submenu " + subMenu);
|
||||
|
||||
/* Still set wasSubmenuSelected -- if not set, the
|
||||
dismissal of this context menu will result in a
|
||||
context menu event being sent. */
|
||||
wasSubmenuSelected = -2;
|
||||
|
||||
/* Running a popup menu from inside a click handler
|
||||
doesn't work, so make sure it is displayed
|
||||
outside. */
|
||||
|
||||
inflatedView.post (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
inflatedView.popupMenu (subMenu, 0, 0, true);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* After opening a submenu within a submenu, Android will
|
||||
send onContextMenuClosed for a ContextMenuBuilder. This
|
||||
will normally confuse Emacs into thinking that the
|
||||
context menu has been dismissed. Wrong!
|
||||
|
||||
Setting this flag makes EmacsActivity to only handle
|
||||
SubMenuBuilder being closed, which always means the menu
|
||||
has actually been dismissed.
|
||||
|
||||
However, these extraneous events aren't sent on devices
|
||||
where submenus display without dismissing their parents.
|
||||
Thus, only ignore the close event if it happens within
|
||||
300 milliseconds of the submenu being selected. */
|
||||
wasSubmenuSelected = System.currentTimeMillis ();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send a context menu event. */
|
||||
EmacsNative.sendContextMenu ((short) 0, itemID,
|
||||
lastMenuEventSerial);
|
||||
|
||||
/* Say that an item has already been selected. */
|
||||
itemAlreadySelected = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public List<Item> menuItems;
|
||||
public String title;
|
||||
private EmacsContextMenu parent;
|
||||
|
||||
/* Create a context menu with no items inside and the title TITLE,
|
||||
which may be NULL. */
|
||||
|
||||
public static EmacsContextMenu
|
||||
createContextMenu (String title)
|
||||
{
|
||||
EmacsContextMenu menu;
|
||||
|
||||
menu = new EmacsContextMenu ();
|
||||
menu.menuItems = new ArrayList<Item> ();
|
||||
menu.title = title;
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
/* Add a normal menu item to the context menu with the id ITEMID and
|
||||
the name ITEMNAME. Enable it if ISENABLED, else keep it
|
||||
disabled.
|
||||
|
||||
If this is not a submenu and ISCHECKABLE is set, make the item
|
||||
checkable. Likewise, if ISCHECKED is set, make the item
|
||||
checked.
|
||||
|
||||
If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP.
|
||||
|
||||
If ISRADIO, then display the check mark as a radio button. */
|
||||
|
||||
public void
|
||||
addItem (int itemID, String itemName, boolean isEnabled,
|
||||
boolean isCheckable, boolean isChecked,
|
||||
String tooltip, boolean isRadio)
|
||||
{
|
||||
Item item;
|
||||
|
||||
item = new Item ();
|
||||
item.itemID = itemID;
|
||||
item.itemName = itemName;
|
||||
item.isEnabled = isEnabled;
|
||||
item.isCheckable = isCheckable;
|
||||
item.isChecked = isChecked;
|
||||
item.tooltip = tooltip;
|
||||
item.isRadio = isRadio;
|
||||
|
||||
menuItems.add (item);
|
||||
}
|
||||
|
||||
/* Create a disabled menu item with the name ITEMNAME. */
|
||||
|
||||
public void
|
||||
addPane (String itemName)
|
||||
{
|
||||
Item item;
|
||||
|
||||
item = new Item ();
|
||||
item.itemName = itemName;
|
||||
|
||||
menuItems.add (item);
|
||||
}
|
||||
|
||||
/* Add a submenu to the context menu with the specified title and
|
||||
item name. */
|
||||
|
||||
public EmacsContextMenu
|
||||
addSubmenu (String itemName, String title, String tooltip)
|
||||
{
|
||||
EmacsContextMenu submenu;
|
||||
Item item;
|
||||
|
||||
item = new Item ();
|
||||
item.itemID = 0;
|
||||
item.itemName = itemName;
|
||||
item.tooltip = tooltip;
|
||||
item.subMenu = createContextMenu (title);
|
||||
item.subMenu.parent = this;
|
||||
|
||||
menuItems.add (item);
|
||||
return item.subMenu;
|
||||
}
|
||||
|
||||
/* Add the contents of this menu to MENU. Assume MENU will be
|
||||
displayed in INFLATEDVIEW. */
|
||||
|
||||
private void
|
||||
inflateMenuItems (Menu menu, EmacsView inflatedView)
|
||||
{
|
||||
Intent intent;
|
||||
MenuItem menuItem;
|
||||
SubMenu submenu;
|
||||
|
||||
for (Item item : menuItems)
|
||||
{
|
||||
if (item.subMenu != null)
|
||||
{
|
||||
/* This is a submenu. On versions of Android which
|
||||
support doing so, create the submenu and add the
|
||||
contents of the menu to it.
|
||||
|
||||
Note that Android 4.0 and later technically supports
|
||||
having multiple layers of nested submenus, but if they
|
||||
are used, onContextMenuClosed becomes unreliable. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
{
|
||||
submenu = menu.addSubMenu (item.itemName);
|
||||
item.subMenu.inflateMenuItems (submenu, inflatedView);
|
||||
|
||||
/* This is still needed to set wasSubmenuSelected. */
|
||||
menuItem = submenu.getItem ();
|
||||
}
|
||||
else
|
||||
menuItem = menu.add (item.itemName);
|
||||
|
||||
item.inflatedView = inflatedView;
|
||||
menuItem.setOnMenuItemClickListener (item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.isRadio)
|
||||
menuItem = menu.add (++lastGroupId, Menu.NONE, Menu.NONE,
|
||||
item.itemName);
|
||||
else
|
||||
menuItem = menu.add (item.itemName);
|
||||
menuItem.setOnMenuItemClickListener (item);
|
||||
|
||||
/* If the item ID is zero, then disable the item. */
|
||||
if (item.itemID == 0 || !item.isEnabled)
|
||||
menuItem.setEnabled (false);
|
||||
|
||||
/* Now make the menu item display a checkmark as
|
||||
appropriate. */
|
||||
|
||||
if (item.isCheckable)
|
||||
menuItem.setCheckable (true);
|
||||
|
||||
if (item.isChecked)
|
||||
menuItem.setChecked (true);
|
||||
|
||||
/* Define an exclusively checkable group if the item is a
|
||||
radio button. */
|
||||
|
||||
if (item.isRadio)
|
||||
menu.setGroupCheckable (lastGroupId, true, true);
|
||||
|
||||
/* If the tooltip text is set and the system is new enough
|
||||
to support menu item tooltips, set it on the item. */
|
||||
|
||||
if (item.tooltip != null
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
menuItem.setTooltipText (item.tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter the items in this context menu to MENU.
|
||||
Assume that MENU will be displayed in VIEW; this may lead to
|
||||
popupMenu being called on VIEW if a submenu is selected. */
|
||||
|
||||
public void
|
||||
expandTo (Menu menu, EmacsView view)
|
||||
{
|
||||
inflateMenuItems (menu, view);
|
||||
}
|
||||
|
||||
/* Return the parent or NULL. */
|
||||
|
||||
public EmacsContextMenu
|
||||
parent ()
|
||||
{
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
/* Like display, but does the actual work and runs in the main
|
||||
thread. */
|
||||
|
||||
private boolean
|
||||
display1 (EmacsWindow window, int xPosition, int yPosition)
|
||||
{
|
||||
/* Set this flag to false. It is used to decide whether or not to
|
||||
send 0 in response to the context menu being closed. */
|
||||
itemAlreadySelected = false;
|
||||
|
||||
/* No submenu has been selected yet. */
|
||||
wasSubmenuSelected = -1;
|
||||
|
||||
return window.view.popupMenu (this, xPosition, yPosition,
|
||||
false);
|
||||
}
|
||||
|
||||
/* Display this context menu on WINDOW, at xPosition and yPosition.
|
||||
SERIAL is a number that will be returned in any menu event
|
||||
generated to identify this context menu. */
|
||||
|
||||
public boolean
|
||||
display (final EmacsWindow window, final int xPosition,
|
||||
final int yPosition, final int serial)
|
||||
{
|
||||
Runnable runnable;
|
||||
final Holder<Boolean> rc;
|
||||
|
||||
rc = new Holder<Boolean> ();
|
||||
|
||||
runnable = new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
lastMenuEventSerial = serial;
|
||||
rc.thing = display1 (window, xPosition, yPosition);
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmacsService.syncRunnable (runnable);
|
||||
return rc.thing;
|
||||
}
|
||||
|
||||
/* Dismiss this context menu. WINDOW is the window where the
|
||||
context menu is being displayed. */
|
||||
|
||||
public void
|
||||
dismiss (final EmacsWindow window)
|
||||
{
|
||||
Runnable runnable;
|
||||
|
||||
EmacsService.SERVICE.runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
window.view.cancelPopupMenu ();
|
||||
itemAlreadySelected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
206
java/org/gnu/emacs/EmacsCopyArea.java
Normal file
206
java/org/gnu/emacs/EmacsCopyArea.java
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Xfermode;
|
||||
|
||||
public final class EmacsCopyArea
|
||||
{
|
||||
private static Xfermode overAlu;
|
||||
|
||||
static
|
||||
{
|
||||
overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
|
||||
};
|
||||
|
||||
private static void
|
||||
insetRectBy (Rect rect, int left, int top, int right,
|
||||
int bottom)
|
||||
{
|
||||
rect.left += left;
|
||||
rect.top += top;
|
||||
rect.right -= right;
|
||||
rect.bottom -= bottom;
|
||||
}
|
||||
|
||||
public static void
|
||||
perform (EmacsDrawable source, EmacsGC gc,
|
||||
EmacsDrawable destination,
|
||||
int src_x, int src_y, int width, int height,
|
||||
int dest_x, int dest_y)
|
||||
{
|
||||
int i;
|
||||
Bitmap bitmap;
|
||||
Paint maskPaint, paint;
|
||||
Canvas maskCanvas, canvas;
|
||||
Bitmap srcBitmap, maskBitmap, clipBitmap;
|
||||
Rect rect, maskRect, srcRect, dstRect, maskDestRect;
|
||||
boolean needFill;
|
||||
|
||||
/* TODO implement stippling. */
|
||||
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
|
||||
return;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
|
||||
canvas = destination.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
/* A copy must be created or drawBitmap could end up overwriting
|
||||
itself. */
|
||||
srcBitmap = source.getBitmap ();
|
||||
|
||||
/* If srcBitmap is out of bounds, then adjust the source rectangle
|
||||
to be within bounds. Note that tiling on windows with
|
||||
backgrounds is unimplemented. */
|
||||
|
||||
if (src_x < 0)
|
||||
{
|
||||
width += src_x;
|
||||
dest_x -= src_x;
|
||||
src_x = 0;
|
||||
}
|
||||
|
||||
if (src_y < 0)
|
||||
{
|
||||
height += src_y;
|
||||
dest_y -= src_y;
|
||||
src_y = 0;
|
||||
}
|
||||
|
||||
if (src_x + width > srcBitmap.getWidth ())
|
||||
width = srcBitmap.getWidth () - src_x;
|
||||
|
||||
if (src_y + height > srcBitmap.getHeight ())
|
||||
height = srcBitmap.getHeight () - src_y;
|
||||
|
||||
/* If width and height are empty or negative, then skip the entire
|
||||
CopyArea operation lest createBitmap throw an exception. */
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
rect = new Rect (dest_x, dest_y, dest_x + width,
|
||||
dest_y + height);
|
||||
|
||||
if (gc.clip_mask == null)
|
||||
{
|
||||
if (source == destination)
|
||||
{
|
||||
/* Create a copy of the bitmap, since Android can't handle
|
||||
overlapping copies. */
|
||||
bitmap = Bitmap.createBitmap (srcBitmap,
|
||||
src_x, src_y, width,
|
||||
height);
|
||||
canvas.drawBitmap (bitmap, null, rect, paint);
|
||||
bitmap.recycle ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* But here the bitmaps are known to not overlap, so avoid
|
||||
that extra consing overhead. */
|
||||
|
||||
srcRect = new Rect (src_x, src_y, src_x + width,
|
||||
src_y + height);
|
||||
canvas.drawBitmap (srcBitmap, srcRect, rect, paint);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Drawing with a clip mask involves calculating the
|
||||
intersection of the clip mask with the dst rect, and
|
||||
extrapolating the corresponding part of the src rect. */
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
dstRect = new Rect (dest_x, dest_y,
|
||||
dest_x + width,
|
||||
dest_y + height);
|
||||
maskRect = new Rect (gc.clip_x_origin,
|
||||
gc.clip_y_origin,
|
||||
(gc.clip_x_origin
|
||||
+ clipBitmap.getWidth ()),
|
||||
(gc.clip_y_origin
|
||||
+ clipBitmap.getHeight ()));
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
|
||||
if (!maskRect.setIntersect (dstRect, maskRect))
|
||||
/* There is no intersection between the clip mask and the
|
||||
dest rect. */
|
||||
return;
|
||||
|
||||
/* Now figure out which part of the source corresponds to
|
||||
maskRect and return it relative to srcBitmap. */
|
||||
srcRect = new Rect (src_x, src_y, src_x + width,
|
||||
src_y + height);
|
||||
insetRectBy (srcRect, maskRect.left - dstRect.left,
|
||||
maskRect.top - dstRect.top,
|
||||
maskRect.right - dstRect.right,
|
||||
maskRect.bottom - dstRect.bottom);
|
||||
|
||||
/* Finally, create a temporary bitmap that is the size of
|
||||
maskRect. */
|
||||
|
||||
maskBitmap
|
||||
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
/* Draw the mask onto the maskBitmap. */
|
||||
maskCanvas = new Canvas (maskBitmap);
|
||||
maskPaint = new Paint ();
|
||||
maskRect.offset (-gc.clip_x_origin,
|
||||
-gc.clip_y_origin);
|
||||
maskCanvas.drawBitmap (gc.clip_mask.bitmap,
|
||||
maskRect,
|
||||
new Rect (0, 0,
|
||||
maskRect.width (),
|
||||
maskRect.height ()),
|
||||
maskPaint);
|
||||
maskRect.offset (gc.clip_x_origin,
|
||||
gc.clip_y_origin);
|
||||
|
||||
/* Set the transfer mode to SRC_IN to preserve only the parts
|
||||
of the source that overlap with the mask. */
|
||||
maskPaint.setXfermode (EmacsGC.srcInAlu);
|
||||
|
||||
/* Draw the source. */
|
||||
maskDestRect = new Rect (0, 0, srcRect.width (),
|
||||
srcRect.height ());
|
||||
maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect,
|
||||
maskPaint);
|
||||
|
||||
/* Finally, draw the mask bitmap to the destination. */
|
||||
paint.setXfermode (overAlu);
|
||||
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
|
||||
gc.resetXfermode ();
|
||||
|
||||
/* Recycle this unused bitmap. */
|
||||
maskBitmap.recycle ();
|
||||
}
|
||||
|
||||
destination.damageRect (rect);
|
||||
}
|
||||
}
|
||||
47
java/org/gnu/emacs/EmacsCursor.java
Normal file
47
java/org/gnu/emacs/EmacsCursor.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.view.PointerIcon;
|
||||
import android.os.Build;
|
||||
|
||||
/* Cursor wrapper. Note that pointer icons are not supported prior to
|
||||
Android 24. */
|
||||
|
||||
public final class EmacsCursor extends EmacsHandleObject
|
||||
{
|
||||
/* The pointer icon associated with this cursor. */
|
||||
public final PointerIcon icon;
|
||||
|
||||
public
|
||||
EmacsCursor (short handle, int glyph)
|
||||
{
|
||||
super (handle);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
{
|
||||
icon = null;
|
||||
return;
|
||||
}
|
||||
|
||||
icon = PointerIcon.getSystemIcon (EmacsService.SERVICE,
|
||||
glyph);
|
||||
}
|
||||
};
|
||||
372
java/org/gnu/emacs/EmacsDialog.java
Normal file
372
java/org/gnu/emacs/EmacsDialog.java
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import android.provider.Settings;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/* Toolkit dialog implementation. This object is built from JNI and
|
||||
describes a single alert dialog. Then, `inflate' turns it into
|
||||
AlertDialog. */
|
||||
|
||||
public final class EmacsDialog implements DialogInterface.OnDismissListener
|
||||
{
|
||||
private static final String TAG = "EmacsDialog";
|
||||
|
||||
/* List of buttons in this dialog. */
|
||||
private List<EmacsButton> buttons;
|
||||
|
||||
/* Dialog title. */
|
||||
private String title;
|
||||
|
||||
/* Dialog text. */
|
||||
private String text;
|
||||
|
||||
/* Whether or not a selection has already been made. */
|
||||
private boolean wasButtonClicked;
|
||||
|
||||
/* Dialog to dismiss after click. */
|
||||
private AlertDialog dismissDialog;
|
||||
|
||||
/* The menu serial associated with this dialog box. */
|
||||
private int menuEventSerial;
|
||||
|
||||
private class EmacsButton implements View.OnClickListener,
|
||||
DialogInterface.OnClickListener
|
||||
{
|
||||
/* Name of this button. */
|
||||
public String name;
|
||||
|
||||
/* ID of this button. */
|
||||
public int id;
|
||||
|
||||
/* Whether or not the button is enabled. */
|
||||
public boolean enabled;
|
||||
|
||||
@Override
|
||||
public void
|
||||
onClick (View view)
|
||||
{
|
||||
Log.d (TAG, "onClicked " + this);
|
||||
|
||||
wasButtonClicked = true;
|
||||
EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
|
||||
dismissDialog.dismiss ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onClick (DialogInterface dialog, int which)
|
||||
{
|
||||
Log.d (TAG, "onClicked " + this);
|
||||
|
||||
wasButtonClicked = true;
|
||||
EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
|
||||
}
|
||||
};
|
||||
|
||||
/* Create a popup dialog with the title TITLE and the text TEXT.
|
||||
TITLE may be NULL. MENUEVENTSERIAL is a number which will
|
||||
identify this popup dialog inside events it sends. */
|
||||
|
||||
public static EmacsDialog
|
||||
createDialog (String title, String text, int menuEventSerial)
|
||||
{
|
||||
EmacsDialog dialog;
|
||||
|
||||
dialog = new EmacsDialog ();
|
||||
dialog.buttons = new ArrayList<EmacsButton> ();
|
||||
dialog.title = title;
|
||||
dialog.text = text;
|
||||
dialog.menuEventSerial = menuEventSerial;
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/* Add a button named NAME, with the identifier ID. If DISABLE,
|
||||
disable the button. */
|
||||
|
||||
public void
|
||||
addButton (String name, int id, boolean disable)
|
||||
{
|
||||
EmacsButton button;
|
||||
|
||||
button = new EmacsButton ();
|
||||
button.name = name;
|
||||
button.id = id;
|
||||
button.enabled = !disable;
|
||||
buttons.add (button);
|
||||
}
|
||||
|
||||
/* Turn this dialog into an AlertDialog for the specified
|
||||
CONTEXT.
|
||||
|
||||
Upon a button being selected, the dialog will send an
|
||||
ANDROID_CONTEXT_MENU event with the id of that button.
|
||||
|
||||
Upon the dialog being dismissed, an ANDROID_CONTEXT_MENU event
|
||||
will be sent with an id of 0. */
|
||||
|
||||
public AlertDialog
|
||||
toAlertDialog (Context context)
|
||||
{
|
||||
AlertDialog dialog;
|
||||
int size;
|
||||
EmacsButton button;
|
||||
LinearLayout layout;
|
||||
Button buttonView;
|
||||
ViewGroup.LayoutParams layoutParams;
|
||||
|
||||
size = buttons.size ();
|
||||
|
||||
if (size <= 3)
|
||||
{
|
||||
dialog = new AlertDialog.Builder (context).create ();
|
||||
dialog.setMessage (text);
|
||||
dialog.setCancelable (true);
|
||||
dialog.setOnDismissListener (this);
|
||||
|
||||
if (title != null)
|
||||
dialog.setTitle (title);
|
||||
|
||||
/* There are less than 4 buttons. Add the buttons the way
|
||||
Android intends them to be added. */
|
||||
|
||||
if (size >= 1)
|
||||
{
|
||||
button = buttons.get (0);
|
||||
dialog.setButton (DialogInterface.BUTTON_POSITIVE,
|
||||
button.name, button);
|
||||
}
|
||||
|
||||
if (size >= 2)
|
||||
{
|
||||
button = buttons.get (1);
|
||||
dialog.setButton (DialogInterface.BUTTON_NEGATIVE,
|
||||
button.name, button);
|
||||
}
|
||||
|
||||
if (size >= 3)
|
||||
{
|
||||
button = buttons.get (2);
|
||||
dialog.setButton (DialogInterface.BUTTON_NEUTRAL,
|
||||
button.name, button);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are more than 4 buttons. Add them all to a
|
||||
LinearLayout. */
|
||||
layout = new LinearLayout (context);
|
||||
layoutParams
|
||||
= new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
for (EmacsButton emacsButton : buttons)
|
||||
{
|
||||
buttonView = new Button (context);
|
||||
buttonView.setText (emacsButton.name);
|
||||
buttonView.setOnClickListener (emacsButton);
|
||||
buttonView.setLayoutParams (layoutParams);
|
||||
buttonView.setEnabled (emacsButton.enabled);
|
||||
layout.addView (buttonView);
|
||||
}
|
||||
|
||||
layoutParams
|
||||
= new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
layout.setLayoutParams (layoutParams);
|
||||
|
||||
/* Add that layout to the dialog's custom view.
|
||||
|
||||
android.R.id.custom is documented to work. But looking it
|
||||
up returns NULL, so setView must be used instead. */
|
||||
|
||||
dialog = new AlertDialog.Builder (context).setView (layout).create ();
|
||||
dialog.setMessage (text);
|
||||
dialog.setCancelable (true);
|
||||
dialog.setOnDismissListener (this);
|
||||
|
||||
if (title != null)
|
||||
dialog.setTitle (title);
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/* Internal helper for display run on the main thread. */
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean
|
||||
display1 ()
|
||||
{
|
||||
Context context;
|
||||
int size, type;
|
||||
Button buttonView;
|
||||
EmacsButton button;
|
||||
AlertDialog dialog;
|
||||
Window window;
|
||||
|
||||
if (EmacsActivity.focusedActivities.isEmpty ())
|
||||
{
|
||||
/* If focusedActivities is empty then this dialog may have
|
||||
been displayed immediately after a popup dialog is
|
||||
dismissed. Or Emacs might legitimately be in the
|
||||
background. Try the service context first if possible. */
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||||
|| Settings.canDrawOverlays (EmacsService.SERVICE))
|
||||
context = EmacsService.SERVICE;
|
||||
else
|
||||
context = EmacsActivity.lastFocusedActivity;
|
||||
|
||||
if (context == null)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
/* Display using the activity context when Emacs is in the
|
||||
foreground, as this allows the dialog to be dismissed more
|
||||
consistently. */
|
||||
context = EmacsActivity.focusedActivities.get (0);
|
||||
|
||||
Log.d (TAG, "display1: using context " + context);
|
||||
|
||||
dialog = dismissDialog = toAlertDialog (context);
|
||||
|
||||
try
|
||||
{
|
||||
if (context == EmacsService.SERVICE)
|
||||
{
|
||||
/* Apply the system alert window type to make sure this
|
||||
dialog can be displayed. */
|
||||
|
||||
window = dialog.getWindow ();
|
||||
type = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||
: WindowManager.LayoutParams.TYPE_PHONE);
|
||||
window.setType (type);
|
||||
}
|
||||
|
||||
dismissDialog.show ();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
/* This can happen when the system decides Emacs is not in the
|
||||
foreground any longer. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If there are less than four buttons, then they must be
|
||||
individually enabled or disabled after the dialog is
|
||||
displayed. */
|
||||
size = buttons.size ();
|
||||
|
||||
if (size <= 3)
|
||||
{
|
||||
if (size >= 1)
|
||||
{
|
||||
button = buttons.get (0);
|
||||
buttonView
|
||||
= dialog.getButton (DialogInterface.BUTTON_POSITIVE);
|
||||
buttonView.setEnabled (button.enabled);
|
||||
}
|
||||
|
||||
if (size >= 2)
|
||||
{
|
||||
button = buttons.get (1);
|
||||
buttonView
|
||||
= dialog.getButton (DialogInterface.BUTTON_NEGATIVE);
|
||||
buttonView.setEnabled (button.enabled);
|
||||
}
|
||||
|
||||
if (size >= 3)
|
||||
{
|
||||
button = buttons.get (2);
|
||||
buttonView
|
||||
= dialog.getButton (DialogInterface.BUTTON_NEUTRAL);
|
||||
buttonView.setEnabled (button.enabled);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Display this dialog for a suitable activity.
|
||||
Value is false if the dialog could not be displayed,
|
||||
and true otherwise. */
|
||||
|
||||
public boolean
|
||||
display ()
|
||||
{
|
||||
Runnable runnable;
|
||||
final Holder<Boolean> rc;
|
||||
|
||||
rc = new Holder<Boolean> ();
|
||||
runnable = new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
rc.thing = display1 ();
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmacsService.syncRunnable (runnable);
|
||||
return rc.thing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void
|
||||
onDismiss (DialogInterface dialog)
|
||||
{
|
||||
Log.d (TAG, "onDismiss: " + this);
|
||||
|
||||
if (wasButtonClicked)
|
||||
return;
|
||||
|
||||
EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial);
|
||||
}
|
||||
};
|
||||
463
java/org/gnu/emacs/EmacsDocumentsProvider.java
Normal file
463
java/org/gnu/emacs/EmacsDocumentsProvider.java
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import static android.provider.DocumentsContract.buildChildDocumentsUri;
|
||||
import android.provider.DocumentsProvider;
|
||||
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
/* ``Documents provider''. This allows Emacs's home directory to be
|
||||
modified by other programs holding permissions to manage system
|
||||
storage, which is useful to (for example) correct misconfigurations
|
||||
which prevent Emacs from starting up.
|
||||
|
||||
This functionality is only available on Android 19 and later. */
|
||||
|
||||
public final class EmacsDocumentsProvider extends DocumentsProvider
|
||||
{
|
||||
/* Home directory. This is the directory whose contents are
|
||||
initially returned to requesting applications. */
|
||||
private File baseDir;
|
||||
|
||||
/* The default projection for requests for the root directory. */
|
||||
private static final String[] DEFAULT_ROOT_PROJECTION;
|
||||
|
||||
/* The default projection for requests for a file. */
|
||||
private static final String[] DEFAULT_DOCUMENT_PROJECTION;
|
||||
|
||||
static
|
||||
{
|
||||
DEFAULT_ROOT_PROJECTION = new String[] {
|
||||
Root.COLUMN_ROOT_ID,
|
||||
Root.COLUMN_MIME_TYPES,
|
||||
Root.COLUMN_FLAGS,
|
||||
Root.COLUMN_ICON,
|
||||
Root.COLUMN_TITLE,
|
||||
Root.COLUMN_SUMMARY,
|
||||
Root.COLUMN_DOCUMENT_ID,
|
||||
Root.COLUMN_AVAILABLE_BYTES,
|
||||
};
|
||||
|
||||
DEFAULT_DOCUMENT_PROJECTION = new String[] {
|
||||
Document.COLUMN_DOCUMENT_ID,
|
||||
Document.COLUMN_MIME_TYPE,
|
||||
Document.COLUMN_DISPLAY_NAME,
|
||||
Document.COLUMN_LAST_MODIFIED,
|
||||
Document.COLUMN_FLAGS,
|
||||
Document.COLUMN_SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
onCreate ()
|
||||
{
|
||||
/* Set the base directory to Emacs's files directory. */
|
||||
baseDir = getContext ().getFilesDir ();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor
|
||||
queryRoots (String[] projection)
|
||||
{
|
||||
MatrixCursor result;
|
||||
MatrixCursor.RowBuilder row;
|
||||
|
||||
/* If the requestor asked for nothing at all, then it wants some
|
||||
data by default. */
|
||||
|
||||
if (projection == null)
|
||||
projection = DEFAULT_ROOT_PROJECTION;
|
||||
|
||||
result = new MatrixCursor (projection);
|
||||
row = result.newRow ();
|
||||
|
||||
/* Now create and add a row for each file in the base
|
||||
directory. */
|
||||
row.add (Root.COLUMN_ROOT_ID, baseDir.getAbsolutePath ());
|
||||
row.add (Root.COLUMN_SUMMARY, "Emacs home directory");
|
||||
|
||||
/* Add the appropriate flags. */
|
||||
|
||||
row.add (Root.COLUMN_FLAGS, (Root.FLAG_SUPPORTS_CREATE
|
||||
| Root.FLAG_SUPPORTS_IS_CHILD));
|
||||
row.add (Root.COLUMN_ICON, R.drawable.emacs);
|
||||
row.add (Root.FLAG_LOCAL_ONLY);
|
||||
row.add (Root.COLUMN_TITLE, "Emacs");
|
||||
row.add (Root.COLUMN_DOCUMENT_ID, baseDir.getAbsolutePath ());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Uri
|
||||
getNotificationUri (File file)
|
||||
{
|
||||
Uri updatedUri;
|
||||
Context context;
|
||||
|
||||
context = getContext ();
|
||||
updatedUri
|
||||
= buildChildDocumentsUri ("org.gnu.emacs",
|
||||
file.getAbsolutePath ());
|
||||
|
||||
return updatedUri;
|
||||
}
|
||||
|
||||
/* Inform the system that FILE's contents (or FILE itself) has
|
||||
changed. */
|
||||
|
||||
private void
|
||||
notifyChange (File file)
|
||||
{
|
||||
Uri updatedUri;
|
||||
Context context;
|
||||
|
||||
context = getContext ();
|
||||
updatedUri
|
||||
= buildChildDocumentsUri ("org.gnu.emacs",
|
||||
file.getAbsolutePath ());
|
||||
context.getContentResolver ().notifyChange (updatedUri, null);
|
||||
}
|
||||
|
||||
/* Return the MIME type of a file FILE. */
|
||||
|
||||
private String
|
||||
getMimeType (File file)
|
||||
{
|
||||
String name, extension, mime;
|
||||
int extensionSeparator;
|
||||
MimeTypeMap singleton;
|
||||
|
||||
if (file.isDirectory ())
|
||||
return Document.MIME_TYPE_DIR;
|
||||
|
||||
/* Abuse WebView stuff to get the file's MIME type. */
|
||||
name = file.getName ();
|
||||
extensionSeparator = name.lastIndexOf ('.');
|
||||
|
||||
if (extensionSeparator > 0)
|
||||
{
|
||||
singleton = MimeTypeMap.getSingleton ();
|
||||
extension = name.substring (extensionSeparator + 1);
|
||||
mime = singleton.getMimeTypeFromExtension (extension);
|
||||
|
||||
if (mime != null)
|
||||
return mime;
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/* Append the specified FILE to the query result RESULT.
|
||||
Handle both directories and ordinary files. */
|
||||
|
||||
private void
|
||||
queryDocument1 (MatrixCursor result, File file)
|
||||
{
|
||||
MatrixCursor.RowBuilder row;
|
||||
String fileName, displayName, mimeType;
|
||||
int flags;
|
||||
|
||||
row = result.newRow ();
|
||||
flags = 0;
|
||||
|
||||
/* fileName is a string that the system will ask for some time in
|
||||
the future. Here, it is just the absolute name of the file. */
|
||||
fileName = file.getAbsolutePath ();
|
||||
|
||||
/* If file is a directory, add the right flags for that. */
|
||||
|
||||
if (file.isDirectory ())
|
||||
{
|
||||
if (file.canWrite ())
|
||||
{
|
||||
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||
flags |= Document.FLAG_SUPPORTS_DELETE;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
flags |= Document.FLAG_SUPPORTS_RENAME;
|
||||
}
|
||||
}
|
||||
else if (file.canWrite ())
|
||||
{
|
||||
/* Apply the correct flags for a writable file. */
|
||||
flags |= Document.FLAG_SUPPORTS_WRITE;
|
||||
flags |= Document.FLAG_SUPPORTS_DELETE;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
flags |= Document.FLAG_SUPPORTS_RENAME;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
flags |= Document.FLAG_SUPPORTS_REMOVE;
|
||||
}
|
||||
|
||||
displayName = file.getName ();
|
||||
mimeType = getMimeType (file);
|
||||
|
||||
row.add (Document.COLUMN_DOCUMENT_ID, fileName);
|
||||
row.add (Document.COLUMN_DISPLAY_NAME, displayName);
|
||||
row.add (Document.COLUMN_SIZE, file.length ());
|
||||
row.add (Document.COLUMN_MIME_TYPE, mimeType);
|
||||
row.add (Document.COLUMN_LAST_MODIFIED, file.lastModified ());
|
||||
row.add (Document.COLUMN_FLAGS, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor
|
||||
queryDocument (String documentId, String[] projection)
|
||||
throws FileNotFoundException
|
||||
{
|
||||
MatrixCursor result;
|
||||
File file;
|
||||
Context context;
|
||||
|
||||
file = new File (documentId);
|
||||
context = getContext ();
|
||||
|
||||
if (projection == null)
|
||||
projection = DEFAULT_DOCUMENT_PROJECTION;
|
||||
|
||||
result = new MatrixCursor (projection);
|
||||
queryDocument1 (result, file);
|
||||
|
||||
/* Now allow interested applications to detect changes. */
|
||||
result.setNotificationUri (context.getContentResolver (),
|
||||
getNotificationUri (file));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor
|
||||
queryChildDocuments (String parentDocumentId, String[] projection,
|
||||
String sortOrder) throws FileNotFoundException
|
||||
{
|
||||
MatrixCursor result;
|
||||
File directory;
|
||||
Context context;
|
||||
|
||||
if (projection == null)
|
||||
projection = DEFAULT_DOCUMENT_PROJECTION;
|
||||
|
||||
result = new MatrixCursor (projection);
|
||||
|
||||
/* Try to open the file corresponding to the location being
|
||||
requested. */
|
||||
directory = new File (parentDocumentId);
|
||||
|
||||
/* Now add each child. */
|
||||
for (File child : directory.listFiles ())
|
||||
queryDocument1 (result, child);
|
||||
|
||||
context = getContext ();
|
||||
|
||||
/* Now allow interested applications to detect changes. */
|
||||
result.setNotificationUri (context.getContentResolver (),
|
||||
getNotificationUri (directory));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor
|
||||
openDocument (String documentId, String mode,
|
||||
CancellationSignal signal) throws FileNotFoundException
|
||||
{
|
||||
return ParcelFileDescriptor.open (new File (documentId),
|
||||
ParcelFileDescriptor.parseMode (mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
createDocument (String documentId, String mimeType,
|
||||
String displayName) throws FileNotFoundException
|
||||
{
|
||||
File file, parentFile;
|
||||
boolean rc;
|
||||
|
||||
file = new File (documentId, displayName);
|
||||
|
||||
try
|
||||
{
|
||||
rc = false;
|
||||
|
||||
if (Document.MIME_TYPE_DIR.equals (mimeType))
|
||||
{
|
||||
file.mkdirs ();
|
||||
|
||||
if (file.isDirectory ())
|
||||
rc = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
file.createNewFile ();
|
||||
|
||||
if (file.isFile ()
|
||||
&& file.setWritable (true)
|
||||
&& file.setReadable (true))
|
||||
rc = true;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
throw new FileNotFoundException ("rc != 1");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new FileNotFoundException (e.toString ());
|
||||
}
|
||||
|
||||
parentFile = file.getParentFile ();
|
||||
|
||||
if (parentFile != null)
|
||||
notifyChange (parentFile);
|
||||
|
||||
return file.getAbsolutePath ();
|
||||
}
|
||||
|
||||
private void
|
||||
deleteDocument1 (File child)
|
||||
{
|
||||
File[] children;
|
||||
|
||||
/* Don't delete symlinks recursively.
|
||||
|
||||
Calling readlink or stat is problematic due to file name
|
||||
encoding problems, so try to delete the file first, and only
|
||||
try to delete files recursively afterword. */
|
||||
|
||||
if (child.delete ())
|
||||
return;
|
||||
|
||||
children = child.listFiles ();
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File file : children)
|
||||
deleteDocument1 (file);
|
||||
}
|
||||
|
||||
child.delete ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
deleteDocument (String documentId)
|
||||
throws FileNotFoundException
|
||||
{
|
||||
File file, parent;
|
||||
File[] children;
|
||||
Context context;
|
||||
|
||||
/* Java makes recursively deleting a file hard. File name
|
||||
encoding issues also prevent easily calling into C... */
|
||||
|
||||
context = getContext ();
|
||||
file = new File (documentId);
|
||||
parent = file.getParentFile ();
|
||||
|
||||
if (parent == null)
|
||||
throw new RuntimeException ("trying to delete file without"
|
||||
+ " parent!");
|
||||
|
||||
if (file.delete ())
|
||||
{
|
||||
/* Tell the system about the change. */
|
||||
notifyChange (parent);
|
||||
return;
|
||||
}
|
||||
|
||||
children = file.listFiles ();
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File child : children)
|
||||
deleteDocument1 (child);
|
||||
}
|
||||
|
||||
if (file.delete ())
|
||||
/* Tell the system about the change. */
|
||||
notifyChange (parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
removeDocument (String documentId, String parentDocumentId)
|
||||
throws FileNotFoundException
|
||||
{
|
||||
deleteDocument (documentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getDocumentType (String documentId)
|
||||
{
|
||||
return getMimeType (new File (documentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
renameDocument (String documentId, String displayName)
|
||||
throws FileNotFoundException
|
||||
{
|
||||
File file, newName;
|
||||
File parent;
|
||||
|
||||
file = new File (documentId);
|
||||
parent = file.getParentFile ();
|
||||
newName = new File (parent, displayName);
|
||||
|
||||
if (parent == null)
|
||||
throw new FileNotFoundException ("parent is null");
|
||||
|
||||
file = new File (documentId);
|
||||
|
||||
if (!file.renameTo (newName))
|
||||
return null;
|
||||
|
||||
notifyChange (parent);
|
||||
return newName.getAbsolutePath ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
isChildDocument (String parentDocumentId, String documentId)
|
||||
{
|
||||
return documentId.startsWith (parentDocumentId);
|
||||
}
|
||||
}
|
||||
66
java/org/gnu/emacs/EmacsDrawLine.java
Normal file
66
java/org/gnu/emacs/EmacsDrawLine.java
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.lang.Math;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
public final class EmacsDrawLine
|
||||
{
|
||||
public static void
|
||||
perform (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int x2, int y2)
|
||||
{
|
||||
Rect rect;
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
|
||||
/* TODO implement stippling. */
|
||||
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
|
||||
return;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
rect = new Rect (Math.min (x, x2 + 1),
|
||||
Math.min (y, y2 + 1),
|
||||
Math.max (x2 + 1, x),
|
||||
Math.max (y2 + 1, y));
|
||||
canvas = drawable.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
paint.setStyle (Paint.Style.STROKE);
|
||||
|
||||
/* Since drawLine has PostScript style behavior, adjust the
|
||||
coordinates appropriately. */
|
||||
|
||||
if (gc.clip_mask == null)
|
||||
canvas.drawLine ((float) x, (float) y + 0.5f,
|
||||
(float) x2 + 0.5f, (float) y2 + 0.5f,
|
||||
paint);
|
||||
|
||||
/* DrawLine with clip mask not implemented; it is not used by
|
||||
Emacs. */
|
||||
drawable.damageRect (rect);
|
||||
}
|
||||
}
|
||||
31
java/org/gnu/emacs/EmacsDrawPoint.java
Normal file
31
java/org/gnu/emacs/EmacsDrawPoint.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
public final class EmacsDrawPoint
|
||||
{
|
||||
public static void
|
||||
perform (EmacsDrawable drawable,
|
||||
EmacsGC immutableGC, int x, int y)
|
||||
{
|
||||
EmacsDrawRectangle.perform (drawable, immutableGC,
|
||||
x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
123
java/org/gnu/emacs/EmacsDrawRectangle.java
Normal file
123
java/org/gnu/emacs/EmacsDrawRectangle.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public final class EmacsDrawRectangle
|
||||
{
|
||||
public static void
|
||||
perform (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
Paint maskPaint, paint;
|
||||
Canvas maskCanvas;
|
||||
Bitmap maskBitmap;
|
||||
Rect rect;
|
||||
Rect maskRect, dstRect;
|
||||
Canvas canvas;
|
||||
Bitmap clipBitmap;
|
||||
|
||||
/* TODO implement stippling. */
|
||||
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
|
||||
return;
|
||||
|
||||
canvas = drawable.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
paint.setStyle (Paint.Style.STROKE);
|
||||
rect = new Rect (x, y, x + width, y + height);
|
||||
|
||||
if (gc.clip_mask == null)
|
||||
/* Use canvas.drawRect with a RectF. That seems to reliably
|
||||
get PostScript behavior. */
|
||||
canvas.drawRect (new RectF (x + 0.5f, y + 0.5f,
|
||||
x + width + 0.5f,
|
||||
y + height + 0.5f),
|
||||
paint);
|
||||
else
|
||||
{
|
||||
/* Drawing with a clip mask involves calculating the
|
||||
intersection of the clip mask with the dst rect, and
|
||||
extrapolating the corresponding part of the src rect. */
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
dstRect = new Rect (x, y, x + width, y + height);
|
||||
maskRect = new Rect (gc.clip_x_origin,
|
||||
gc.clip_y_origin,
|
||||
(gc.clip_x_origin
|
||||
+ clipBitmap.getWidth ()),
|
||||
(gc.clip_y_origin
|
||||
+ clipBitmap.getHeight ()));
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
|
||||
if (!maskRect.setIntersect (dstRect, maskRect))
|
||||
/* There is no intersection between the clip mask and the
|
||||
dest rect. */
|
||||
return;
|
||||
|
||||
/* Finally, create a temporary bitmap that is the size of
|
||||
maskRect. */
|
||||
|
||||
maskBitmap
|
||||
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
/* Draw the mask onto the maskBitmap. */
|
||||
maskCanvas = new Canvas (maskBitmap);
|
||||
maskRect.offset (-gc.clip_x_origin,
|
||||
-gc.clip_y_origin);
|
||||
maskCanvas.drawBitmap (gc.clip_mask.bitmap,
|
||||
maskRect, new Rect (0, 0,
|
||||
maskRect.width (),
|
||||
maskRect.height ()),
|
||||
paint);
|
||||
maskRect.offset (gc.clip_x_origin,
|
||||
gc.clip_y_origin);
|
||||
|
||||
/* Set the transfer mode to SRC_IN to preserve only the parts
|
||||
of the source that overlap with the mask. */
|
||||
maskPaint = new Paint ();
|
||||
maskPaint.setXfermode (EmacsGC.srcInAlu);
|
||||
maskPaint.setStyle (Paint.Style.STROKE);
|
||||
|
||||
/* Draw the source. */
|
||||
maskCanvas.drawRect (maskRect, maskPaint);
|
||||
|
||||
/* Finally, draw the mask bitmap to the destination. */
|
||||
paint.setXfermode (null);
|
||||
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
|
||||
|
||||
/* Recycle this unused bitmap. */
|
||||
maskBitmap.recycle ();
|
||||
}
|
||||
|
||||
drawable.damageRect (new Rect (x, y, x + width + 1,
|
||||
y + height + 1));
|
||||
}
|
||||
}
|
||||
32
java/org/gnu/emacs/EmacsDrawable.java
Normal file
32
java/org/gnu/emacs/EmacsDrawable.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
|
||||
public interface EmacsDrawable
|
||||
{
|
||||
public Canvas lockCanvas (EmacsGC gc);
|
||||
public void damageRect (Rect damageRect);
|
||||
public Bitmap getBitmap ();
|
||||
public boolean isDestroyed ();
|
||||
};
|
||||
82
java/org/gnu/emacs/EmacsFillPolygon.java
Normal file
82
java/org/gnu/emacs/EmacsFillPolygon.java
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.lang.Math;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
public final class EmacsFillPolygon
|
||||
{
|
||||
public static void
|
||||
perform (EmacsDrawable drawable, EmacsGC gc, Point points[])
|
||||
{
|
||||
Canvas canvas;
|
||||
Path path;
|
||||
Paint paint;
|
||||
Rect rect;
|
||||
RectF rectF;
|
||||
int i;
|
||||
|
||||
canvas = drawable.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
|
||||
/* Build the path from the given array of points. */
|
||||
path = new Path ();
|
||||
|
||||
if (points.length >= 1)
|
||||
{
|
||||
path.moveTo (points[0].x, points[0].y);
|
||||
|
||||
for (i = 1; i < points.length; ++i)
|
||||
path.lineTo (points[i].x, points[i].y);
|
||||
|
||||
path.close ();
|
||||
}
|
||||
|
||||
/* Compute the damage rectangle. */
|
||||
rectF = new RectF (0, 0, 0, 0);
|
||||
path.computeBounds (rectF, true);
|
||||
|
||||
rect = new Rect ((int) Math.floor (rectF.left),
|
||||
(int) Math.floor (rectF.top),
|
||||
(int) Math.ceil (rectF.right),
|
||||
(int) Math.ceil (rectF.bottom));
|
||||
|
||||
paint.setStyle (Paint.Style.FILL);
|
||||
|
||||
if (gc.clip_mask == null)
|
||||
canvas.drawPath (path, paint);
|
||||
|
||||
drawable.damageRect (rect);
|
||||
|
||||
/* FillPolygon with clip mask not implemented; it is not used by
|
||||
Emacs. */
|
||||
}
|
||||
}
|
||||
116
java/org/gnu/emacs/EmacsFillRectangle.java
Normal file
116
java/org/gnu/emacs/EmacsFillRectangle.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public final class EmacsFillRectangle
|
||||
{
|
||||
public static void
|
||||
perform (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
Paint maskPaint, paint;
|
||||
Canvas maskCanvas;
|
||||
Bitmap maskBitmap;
|
||||
Rect rect;
|
||||
Rect maskRect, dstRect;
|
||||
Canvas canvas;
|
||||
Bitmap clipBitmap;
|
||||
|
||||
/* TODO implement stippling. */
|
||||
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
|
||||
return;
|
||||
|
||||
canvas = drawable.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
rect = new Rect (x, y, x + width, y + height);
|
||||
|
||||
paint.setStyle (Paint.Style.FILL);
|
||||
|
||||
if (gc.clip_mask == null)
|
||||
canvas.drawRect (rect, paint);
|
||||
else
|
||||
{
|
||||
/* Drawing with a clip mask involves calculating the
|
||||
intersection of the clip mask with the dst rect, and
|
||||
extrapolating the corresponding part of the src rect. */
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
dstRect = new Rect (x, y, x + width, y + height);
|
||||
maskRect = new Rect (gc.clip_x_origin,
|
||||
gc.clip_y_origin,
|
||||
(gc.clip_x_origin
|
||||
+ clipBitmap.getWidth ()),
|
||||
(gc.clip_y_origin
|
||||
+ clipBitmap.getHeight ()));
|
||||
clipBitmap = gc.clip_mask.bitmap;
|
||||
|
||||
if (!maskRect.setIntersect (dstRect, maskRect))
|
||||
/* There is no intersection between the clip mask and the
|
||||
dest rect. */
|
||||
return;
|
||||
|
||||
/* Finally, create a temporary bitmap that is the size of
|
||||
maskRect. */
|
||||
|
||||
maskBitmap
|
||||
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
/* Draw the mask onto the maskBitmap. */
|
||||
maskCanvas = new Canvas (maskBitmap);
|
||||
maskRect.offset (-gc.clip_x_origin,
|
||||
-gc.clip_y_origin);
|
||||
maskCanvas.drawBitmap (gc.clip_mask.bitmap,
|
||||
maskRect, new Rect (0, 0,
|
||||
maskRect.width (),
|
||||
maskRect.height ()),
|
||||
paint);
|
||||
maskRect.offset (gc.clip_x_origin,
|
||||
gc.clip_y_origin);
|
||||
|
||||
/* Set the transfer mode to SRC_IN to preserve only the parts
|
||||
of the source that overlap with the mask. */
|
||||
maskPaint = new Paint ();
|
||||
maskPaint.setXfermode (EmacsGC.srcInAlu);
|
||||
|
||||
/* Draw the source. */
|
||||
maskCanvas.drawRect (maskRect, maskPaint);
|
||||
|
||||
/* Finally, draw the mask bitmap to the destination. */
|
||||
paint.setXfermode (null);
|
||||
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
|
||||
|
||||
/* Recycle this unused bitmap. */
|
||||
maskBitmap.recycle ();
|
||||
}
|
||||
|
||||
drawable.damageRect (rect);
|
||||
}
|
||||
}
|
||||
173
java/org/gnu/emacs/EmacsFontDriver.java
Normal file
173
java/org/gnu/emacs/EmacsFontDriver.java
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/* This code is mostly unused. See sfntfont-android.c for the code
|
||||
that is actually used. */
|
||||
|
||||
public abstract class EmacsFontDriver
|
||||
{
|
||||
/* Font weights. */
|
||||
public static final int THIN = 0;
|
||||
public static final int ULTRA_LIGHT = 40;
|
||||
public static final int LIGHT = 50;
|
||||
public static final int SEMI_LIGHT = 55;
|
||||
public static final int REGULAR = 80;
|
||||
public static final int MEDIUM = 100;
|
||||
public static final int SEMI_BOLD = 180;
|
||||
public static final int BOLD = 200;
|
||||
public static final int EXTRA_BOLD = 205;
|
||||
public static final int BLACK = 210;
|
||||
public static final int ULTRA_HEAVY = 250;
|
||||
|
||||
/* Font slants. */
|
||||
public static final int REVERSE_OBLIQUE = 0;
|
||||
public static final int REVERSE_ITALIC = 10;
|
||||
public static final int NORMAL = 100;
|
||||
public static final int ITALIC = 200;
|
||||
public static final int OBLIQUE = 210;
|
||||
|
||||
/* Font widths. */
|
||||
public static final int ULTRA_CONDENSED = 50;
|
||||
public static final int EXTRA_CONDENSED = 63;
|
||||
public static final int CONDENSED = 75;
|
||||
public static final int SEMI_CONDENSED = 87;
|
||||
public static final int UNSPECIFIED = 100;
|
||||
public static final int SEMI_EXPANDED = 113;
|
||||
public static final int EXPANDED = 125;
|
||||
public static final int EXTRA_EXPANDED = 150;
|
||||
public static final int ULTRA_EXPANDED = 200;
|
||||
|
||||
/* Font spacings. */
|
||||
public static final int PROPORTIONAL = 0;
|
||||
public static final int DUAL = 90;
|
||||
public static final int MONO = 100;
|
||||
public static final int CHARCELL = 110;
|
||||
|
||||
public static class FontSpec
|
||||
{
|
||||
/* The fields below mean the same as they do in enum
|
||||
font_property_index in font.h. */
|
||||
|
||||
public String foundry;
|
||||
public String family;
|
||||
public String adstyle;
|
||||
public String registry;
|
||||
public Integer width;
|
||||
public Integer weight;
|
||||
public Integer slant;
|
||||
public Integer size;
|
||||
public Integer spacing;
|
||||
public Integer avgwidth;
|
||||
public Integer dpi;
|
||||
|
||||
@Override
|
||||
public String
|
||||
toString ()
|
||||
{
|
||||
return ("foundry: " + foundry
|
||||
+ " family: " + family
|
||||
+ " adstyle: " + adstyle
|
||||
+ " registry: " + registry
|
||||
+ " width: " + width
|
||||
+ " weight: " + weight
|
||||
+ " slant: " + slant
|
||||
+ " spacing: " + spacing
|
||||
+ " avgwidth: " + avgwidth
|
||||
+ " dpi: " + dpi);
|
||||
}
|
||||
};
|
||||
|
||||
public static class FontMetrics
|
||||
{
|
||||
public short lbearing;
|
||||
public short rbearing;
|
||||
public short width;
|
||||
public short ascent;
|
||||
public short descent;
|
||||
|
||||
@Override
|
||||
public String
|
||||
toString ()
|
||||
{
|
||||
return ("lbearing " + lbearing
|
||||
+ " rbearing " + rbearing
|
||||
+ " width " + width
|
||||
+ " ascent " + ascent
|
||||
+ " descent " + descent);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FontEntity extends FontSpec
|
||||
{
|
||||
/* No extra fields here. */
|
||||
};
|
||||
|
||||
public abstract class FontObject extends FontSpec
|
||||
{
|
||||
public int minWidth;
|
||||
public int maxWidth;
|
||||
public int pixelSize;
|
||||
public int height;
|
||||
public int spaceWidth;
|
||||
public int averageWidth;
|
||||
public int ascent;
|
||||
public int descent;
|
||||
public int underlineThickness;
|
||||
public int underlinePosition;
|
||||
public int baselineOffset;
|
||||
public int relativeCompose;
|
||||
public int defaultAscent;
|
||||
public int encodingCharset;
|
||||
public int repertoryCharset;
|
||||
|
||||
public
|
||||
FontObject ()
|
||||
{
|
||||
encodingCharset = -1;
|
||||
repertoryCharset = -1;
|
||||
}
|
||||
};
|
||||
|
||||
/* These mean the same as they do in struct font_driver. */
|
||||
public abstract FontEntity[] list (FontSpec fontSpec);
|
||||
public abstract FontEntity match (FontSpec fontSpec);
|
||||
public abstract String[] listFamilies ();
|
||||
public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
|
||||
public abstract int hasChar (FontSpec font, char charCode);
|
||||
public abstract void textExtents (FontObject font, int code[],
|
||||
FontMetrics fontMetrics);
|
||||
public abstract int encodeChar (FontObject fontObject, char charCode);
|
||||
public abstract int draw (FontObject fontObject, EmacsGC gc,
|
||||
EmacsDrawable drawable, int[] chars,
|
||||
int x, int y, int backgroundWidth,
|
||||
boolean withBackground);
|
||||
|
||||
public static EmacsFontDriver
|
||||
createFontDriver ()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
return new EmacsSdk23FontDriver ();
|
||||
|
||||
return new EmacsSdk7FontDriver ();
|
||||
}
|
||||
};
|
||||
121
java/org/gnu/emacs/EmacsGC.java
Normal file
121
java/org/gnu/emacs/EmacsGC.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Xfermode;
|
||||
|
||||
/* X like graphics context structures. Keep the enums in synch with
|
||||
androidgui.h! */
|
||||
|
||||
public final class EmacsGC extends EmacsHandleObject
|
||||
{
|
||||
public static final int GC_COPY = 0;
|
||||
public static final int GC_XOR = 1;
|
||||
|
||||
public static final int GC_FILL_SOLID = 0;
|
||||
public static final int GC_FILL_OPAQUE_STIPPLED = 1;
|
||||
|
||||
public static final Xfermode xorAlu, srcInAlu;
|
||||
|
||||
public int function, fill_style;
|
||||
public int foreground, background;
|
||||
public int clip_x_origin, clip_y_origin;
|
||||
public int ts_origin_x, ts_origin_y;
|
||||
public Rect clip_rects[], real_clip_rects[];
|
||||
public EmacsPixmap clip_mask, stipple;
|
||||
public Paint gcPaint;
|
||||
|
||||
/* ID incremented every time the clipping rectangles of any GC
|
||||
changes. */
|
||||
private static long clip_serial;
|
||||
|
||||
/* The value of clipRectID after the last time this GCs clip
|
||||
rectangles changed. 0 if there are no clip rectangles. */
|
||||
public long clipRectID;
|
||||
|
||||
static
|
||||
{
|
||||
xorAlu = new PorterDuffXfermode (Mode.XOR);
|
||||
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
|
||||
}
|
||||
|
||||
/* The following fields are only set on immutable GCs. */
|
||||
|
||||
public
|
||||
EmacsGC (short handle)
|
||||
{
|
||||
/* For historical reasons the C code has an extra layer of
|
||||
indirection above this GC handle. struct android_gc is the GC
|
||||
used by Emacs code, while android_gcontext is the type of the
|
||||
handle. */
|
||||
super (handle);
|
||||
|
||||
fill_style = GC_FILL_SOLID;
|
||||
function = GC_COPY;
|
||||
foreground = 0;
|
||||
background = 0xffffff;
|
||||
gcPaint = new Paint ();
|
||||
}
|
||||
|
||||
/* Mark this GC as dirty. Apply parameters to the paint and
|
||||
recompute real_clip_rects. */
|
||||
|
||||
public void
|
||||
markDirty (boolean clipRectsChanged)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (clipRectsChanged)
|
||||
{
|
||||
if ((ts_origin_x != 0 || ts_origin_y != 0)
|
||||
&& clip_rects != null)
|
||||
{
|
||||
real_clip_rects = new Rect[clip_rects.length];
|
||||
|
||||
for (i = 0; i < clip_rects.length; ++i)
|
||||
{
|
||||
real_clip_rects[i] = new Rect (clip_rects[i]);
|
||||
real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
|
||||
}
|
||||
}
|
||||
else
|
||||
real_clip_rects = clip_rects;
|
||||
|
||||
clipRectID = ++clip_serial;
|
||||
}
|
||||
|
||||
gcPaint.setStrokeWidth (1f);
|
||||
gcPaint.setColor (foreground | 0xff000000);
|
||||
gcPaint.setXfermode (function == GC_XOR
|
||||
? xorAlu : srcInAlu);
|
||||
}
|
||||
|
||||
public void
|
||||
resetXfermode ()
|
||||
{
|
||||
gcPaint.setXfermode (function == GC_XOR
|
||||
? xorAlu : srcInAlu);
|
||||
}
|
||||
};
|
||||
59
java/org/gnu/emacs/EmacsHandleObject.java
Normal file
59
java/org/gnu/emacs/EmacsHandleObject.java
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.lang.IllegalStateException;
|
||||
|
||||
/* This defines something that is a so-called ``handle''. Handles
|
||||
must be created by C code, and will remain existing until
|
||||
destroyHandle is called. C code then refers to the handle by a
|
||||
number which maps into the Java object representing the handle.
|
||||
|
||||
All handle operations must be done from the Emacs thread. */
|
||||
|
||||
public abstract class EmacsHandleObject
|
||||
{
|
||||
/* Whether or not this handle has been destroyed. */
|
||||
volatile boolean destroyed;
|
||||
|
||||
/* The handle associated with this object. */
|
||||
public short handle;
|
||||
|
||||
public
|
||||
EmacsHandleObject (short handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public void
|
||||
destroyHandle () throws IllegalStateException
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean
|
||||
isDestroyed ()
|
||||
{
|
||||
return destroyed;
|
||||
}
|
||||
};
|
||||
348
java/org/gnu/emacs/EmacsInputConnection.java
Normal file
348
java/org/gnu/emacs/EmacsInputConnection.java
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.TextSnapshot;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/* Android input methods, take number six. See textconv.c for more
|
||||
details; this is more-or-less a thin wrapper around that file. */
|
||||
|
||||
public final class EmacsInputConnection extends BaseInputConnection
|
||||
{
|
||||
private static final String TAG = "EmacsInputConnection";
|
||||
private EmacsView view;
|
||||
private short windowHandle;
|
||||
|
||||
/* Whether or not to synchronize and call `updateIC' with the
|
||||
selection position after committing text.
|
||||
|
||||
This helps with on screen keyboard programs found in some vendor
|
||||
versions of Android, which rely on immediate updates to the point
|
||||
position after text is commited in order to place the cursor
|
||||
within that text. */
|
||||
|
||||
private static boolean syncAfterCommit;
|
||||
|
||||
/* Whether or not to return empty text with the offset set to zero
|
||||
if a request arrives that has no flags set and has requested no
|
||||
characters at all.
|
||||
|
||||
This is necessary with on screen keyboard programs found in some
|
||||
vendor versions of Android which don't rely on the documented
|
||||
meaning of `ExtractedText.startOffset', and instead take the
|
||||
selection offset inside at face value. */
|
||||
|
||||
private static boolean extractAbsoluteOffsets;
|
||||
|
||||
static
|
||||
{
|
||||
if (Build.MANUFACTURER.equalsIgnoreCase ("Huawei")
|
||||
|| Build.MANUFACTURER.equalsIgnoreCase ("Honor"))
|
||||
extractAbsoluteOffsets = syncAfterCommit = true;
|
||||
};
|
||||
|
||||
public
|
||||
EmacsInputConnection (EmacsView view)
|
||||
{
|
||||
super (view, true);
|
||||
|
||||
this.view = view;
|
||||
this.windowHandle = view.window.handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
beginBatchEdit ()
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "beginBatchEdit");
|
||||
|
||||
EmacsNative.beginBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
endBatchEdit ()
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "endBatchEdit");
|
||||
|
||||
EmacsNative.endBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
commitCompletion (CompletionInfo info)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitCompletion: " + info);
|
||||
|
||||
EmacsNative.commitCompletion (windowHandle,
|
||||
info.getText ().toString (),
|
||||
info.getPosition ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
commitText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
int[] selection;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
|
||||
|
||||
EmacsNative.commitText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
|
||||
if (syncAfterCommit)
|
||||
{
|
||||
/* Synchronize with the Emacs thread, obtain the new
|
||||
selection, and report it immediately. */
|
||||
|
||||
selection = EmacsNative.getSelection (windowHandle);
|
||||
|
||||
if (EmacsService.DEBUG_IC && selection != null)
|
||||
Log.d (TAG, "commitText: new selection is " + selection[0]
|
||||
+ ", by " + selection[1]);
|
||||
|
||||
if (selection != null)
|
||||
/* N.B. that the composing region is removed after text is
|
||||
committed. */
|
||||
view.imManager.updateSelection (view, selection[0],
|
||||
selection[1], -1, -1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
deleteSurroundingText (int leftLength, int rightLength)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("deleteSurroundingText: "
|
||||
+ leftLength + " " + rightLength));
|
||||
|
||||
EmacsNative.deleteSurroundingText (windowHandle, leftLength,
|
||||
rightLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
finishComposingText ()
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "finishComposingText");
|
||||
|
||||
EmacsNative.finishComposingText (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getSelectedText (int flags)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getSelectedText: " + flags);
|
||||
|
||||
return EmacsNative.getSelectedText (windowHandle, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getTextAfterCursor (int length, int flags)
|
||||
{
|
||||
String string;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
|
||||
|
||||
string = EmacsNative.getTextAfterCursor (windowHandle, length,
|
||||
flags);
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, " --> " + string);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getTextBeforeCursor (int length, int flags)
|
||||
{
|
||||
String string;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
|
||||
|
||||
string = EmacsNative.getTextBeforeCursor (windowHandle, length,
|
||||
flags);
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, " --> " + string);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
setComposingText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("setComposingText: "
|
||||
+ text + " ## " + newCursorPosition));
|
||||
|
||||
EmacsNative.setComposingText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
setComposingRegion (int start, int end)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setComposingRegion: " + start + " " + end);
|
||||
|
||||
EmacsNative.setComposingRegion (windowHandle, start, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
performEditorAction (int editorAction)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "performEditorAction: " + editorAction);
|
||||
|
||||
EmacsNative.performEditorAction (windowHandle, editorAction);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtractedText
|
||||
getExtractedText (ExtractedTextRequest request, int flags)
|
||||
{
|
||||
ExtractedText text;
|
||||
int[] selection;
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
|
||||
+ request.hintMaxLines + " " + flags);
|
||||
|
||||
/* If a request arrives with hintMaxChars, hintMaxLines and flags
|
||||
set to 0, and the system is known to be buggy, return an empty
|
||||
extracted text object with the absolute selection positions. */
|
||||
|
||||
if (extractAbsoluteOffsets
|
||||
&& request.hintMaxChars == 0
|
||||
&& request.hintMaxLines == 0
|
||||
&& flags == 0)
|
||||
{
|
||||
/* Obtain the selection. */
|
||||
selection = EmacsNative.getSelection (windowHandle);
|
||||
if (selection == null)
|
||||
return null;
|
||||
|
||||
/* Create the workaround extracted text. */
|
||||
text = new ExtractedText ();
|
||||
text.partialStartOffset = -1;
|
||||
text.partialEndOffset = -1;
|
||||
text.text = "";
|
||||
text.selectionStart = selection[0];
|
||||
text.selectionEnd = selection[1];
|
||||
}
|
||||
else
|
||||
text = EmacsNative.getExtractedText (windowHandle, request,
|
||||
flags);
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getExtractedText: " + text.text + " @"
|
||||
+ text.startOffset + ":" + text.selectionStart
|
||||
+ ", " + text.selectionEnd);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
setSelection (int start, int end)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setSelection: " + start + " " + end);
|
||||
|
||||
EmacsNative.setSelection (windowHandle, start, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
sendKeyEvent (KeyEvent key)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "sendKeyEvent: " + key);
|
||||
|
||||
return super.sendKeyEvent (key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
|
||||
{
|
||||
/* This can be implemented the same way as
|
||||
deleteSurroundingText. */
|
||||
return this.deleteSurroundingText (beforeLength, afterLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
requestCursorUpdates (int cursorUpdateMode)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
|
||||
|
||||
EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Override functions which are not implemented. */
|
||||
|
||||
@Override
|
||||
public TextSnapshot
|
||||
takeSnapshot ()
|
||||
{
|
||||
Log.d (TAG, "takeSnapshot");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
29
java/org/gnu/emacs/EmacsMultitaskActivity.java
Normal file
29
java/org/gnu/emacs/EmacsMultitaskActivity.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
/* This class only exists because EmacsActivity is already defined as
|
||||
an activity, and the system wants a new class in order to define a
|
||||
new activity. */
|
||||
|
||||
public final class EmacsMultitaskActivity extends EmacsActivity
|
||||
{
|
||||
|
||||
}
|
||||
254
java/org/gnu/emacs/EmacsNative.java
Normal file
254
java/org/gnu/emacs/EmacsNative.java
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
|
||||
public final class EmacsNative
|
||||
{
|
||||
/* List of native libraries that must be loaded during class
|
||||
initialization. */
|
||||
private static final String[] libraryDeps;
|
||||
|
||||
|
||||
/* Like `dup' in C. */
|
||||
public static native int dup (int fd);
|
||||
|
||||
/* Obtain the fingerprint of this build of Emacs. The fingerprint
|
||||
can be used to determine the dump file name. */
|
||||
public static native String getFingerprint ();
|
||||
|
||||
/* Set certain parameters before initializing Emacs.
|
||||
|
||||
assetManager must be the asset manager associated with the
|
||||
context that is loading Emacs. It is saved and remains for the
|
||||
remainder the lifetime of the Emacs process.
|
||||
|
||||
filesDir must be the package's data storage location for the
|
||||
current Android user.
|
||||
|
||||
libDir must be the package's data storage location for native
|
||||
libraries. It is used as PATH.
|
||||
|
||||
cacheDir must be the package's cache directory. It is used as
|
||||
the `temporary-file-directory'.
|
||||
|
||||
pixelDensityX and pixelDensityY are the DPI values that will be
|
||||
used by Emacs.
|
||||
|
||||
classPath must be the classpath of this app_process process, or
|
||||
NULL.
|
||||
|
||||
emacsService must be the EmacsService singleton, or NULL. */
|
||||
public static native void setEmacsParams (AssetManager assetManager,
|
||||
String filesDir,
|
||||
String libDir,
|
||||
String cacheDir,
|
||||
float pixelDensityX,
|
||||
float pixelDensityY,
|
||||
String classPath,
|
||||
EmacsService emacsService);
|
||||
|
||||
/* Initialize Emacs with the argument array ARGV. Each argument
|
||||
must contain a NULL terminated string, or else the behavior is
|
||||
undefined.
|
||||
|
||||
DUMPFILE is the dump file to use, or NULL if Emacs is to load
|
||||
loadup.el itself.
|
||||
|
||||
APILEVEL is the version of Android being used. */
|
||||
public static native void initEmacs (String argv[], String dumpFile,
|
||||
int apiLevel);
|
||||
|
||||
/* Abort and generate a native core dump. */
|
||||
public static native void emacsAbort ();
|
||||
|
||||
/* Set Vquit_flag to t, resulting in Emacs quitting as soon as
|
||||
possible. */
|
||||
public static native void quit ();
|
||||
|
||||
/* Send an ANDROID_CONFIGURE_NOTIFY event. The values of all the
|
||||
functions below are the serials of the events sent. */
|
||||
public static native long sendConfigureNotify (short window, long time,
|
||||
int x, int y, int width,
|
||||
int height);
|
||||
|
||||
/* Send an ANDROID_KEY_PRESS event. */
|
||||
public static native long sendKeyPress (short window, long time, int state,
|
||||
int keyCode, int unicodeChar);
|
||||
|
||||
/* Send an ANDROID_KEY_RELEASE event. */
|
||||
public static native long sendKeyRelease (short window, long time, int state,
|
||||
int keyCode, int unicodeChar);
|
||||
|
||||
/* Send an ANDROID_FOCUS_IN event. */
|
||||
public static native long sendFocusIn (short window, long time);
|
||||
|
||||
/* Send an ANDROID_FOCUS_OUT event. */
|
||||
public static native long sendFocusOut (short window, long time);
|
||||
|
||||
/* Send an ANDROID_WINDOW_ACTION event. */
|
||||
public static native long sendWindowAction (short window, int action);
|
||||
|
||||
/* Send an ANDROID_ENTER_NOTIFY event. */
|
||||
public static native long sendEnterNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_LEAVE_NOTIFY event. */
|
||||
public static native long sendLeaveNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_MOTION_NOTIFY event. */
|
||||
public static native long sendMotionNotify (short window, int x, int y,
|
||||
long time);
|
||||
|
||||
/* Send an ANDROID_BUTTON_PRESS event. */
|
||||
public static native long sendButtonPress (short window, int x, int y,
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_BUTTON_RELEASE event. */
|
||||
public static native long sendButtonRelease (short window, int x, int y,
|
||||
long time, int state,
|
||||
int button);
|
||||
|
||||
/* Send an ANDROID_TOUCH_DOWN event. */
|
||||
public static native long sendTouchDown (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_UP event. */
|
||||
public static native long sendTouchUp (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_TOUCH_MOVE event. */
|
||||
public static native long sendTouchMove (short window, int x, int y,
|
||||
long time, int pointerID);
|
||||
|
||||
/* Send an ANDROID_WHEEL event. */
|
||||
public static native long sendWheel (short window, int x, int y,
|
||||
long time, int state,
|
||||
float xDelta, float yDelta);
|
||||
|
||||
/* Send an ANDROID_ICONIFIED event. */
|
||||
public static native long sendIconified (short window);
|
||||
|
||||
/* Send an ANDROID_DEICONIFIED event. */
|
||||
public static native long sendDeiconified (short window);
|
||||
|
||||
/* Send an ANDROID_CONTEXT_MENU event. */
|
||||
public static native long sendContextMenu (short window, int menuEventID,
|
||||
int menuEventSerial);
|
||||
|
||||
/* Send an ANDROID_EXPOSE event. */
|
||||
public static native long sendExpose (short window, int x, int y,
|
||||
int width, int height);
|
||||
|
||||
/* Return the file name associated with the specified file
|
||||
descriptor, or NULL if there is none. */
|
||||
public static native byte[] getProcName (int fd);
|
||||
|
||||
/* Notice that the Emacs thread will now start waiting for the main
|
||||
thread's looper to respond. */
|
||||
public static native void beginSynchronous ();
|
||||
|
||||
/* Notice that the Emacs thread will has finished waiting for the
|
||||
main thread's looper to respond. */
|
||||
public static native void endSynchronous ();
|
||||
|
||||
/* Return whether or not KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP and
|
||||
KEYCODE_VOLUME_MUTE should be forwarded to Emacs. */
|
||||
public static native boolean shouldForwardMultimediaButtons ();
|
||||
|
||||
|
||||
|
||||
/* Input connection functions. These mostly correspond to their
|
||||
counterparts in Android's InputConnection. */
|
||||
|
||||
public static native void beginBatchEdit (short window);
|
||||
public static native void endBatchEdit (short window);
|
||||
public static native void commitCompletion (short window, String text,
|
||||
int position);
|
||||
public static native void commitText (short window, String text,
|
||||
int position);
|
||||
public static native void deleteSurroundingText (short window,
|
||||
int leftLength,
|
||||
int rightLength);
|
||||
public static native void finishComposingText (short window);
|
||||
public static native String getSelectedText (short window, int flags);
|
||||
public static native String getTextAfterCursor (short window, int length,
|
||||
int flags);
|
||||
public static native String getTextBeforeCursor (short window, int length,
|
||||
int flags);
|
||||
public static native void setComposingText (short window, String text,
|
||||
int newCursorPosition);
|
||||
public static native void setComposingRegion (short window, int start,
|
||||
int end);
|
||||
public static native void setSelection (short window, int start, int end);
|
||||
public static native void performEditorAction (short window,
|
||||
int editorAction);
|
||||
public static native ExtractedText getExtractedText (short window,
|
||||
ExtractedTextRequest req,
|
||||
int flags);
|
||||
public static native void requestSelectionUpdate (short window);
|
||||
public static native void requestCursorUpdates (short window, int mode);
|
||||
|
||||
|
||||
/* Return the current value of the selection, or -1 upon
|
||||
failure. */
|
||||
public static native int[] getSelection (short window);
|
||||
|
||||
static
|
||||
{
|
||||
/* Older versions of Android cannot link correctly with shared
|
||||
libraries that link with other shared libraries built along
|
||||
Emacs unless all requisite shared libraries are explicitly
|
||||
loaded from Java.
|
||||
|
||||
Every time you add a new shared library dependency to Emacs,
|
||||
please add it here as well. */
|
||||
|
||||
libraryDeps = new String[] { "png_emacs", "selinux_emacs",
|
||||
"crypto_emacs", "pcre_emacs",
|
||||
"packagelistparser_emacs",
|
||||
"gnutls_emacs", "gmp_emacs",
|
||||
"nettle_emacs", "p11-kit_emacs",
|
||||
"tasn1_emacs", "hogweed_emacs",
|
||||
"jansson_emacs", "jpeg_emacs",
|
||||
"tiff_emacs", "xml2_emacs",
|
||||
"icuuc_emacs",
|
||||
"tree-sitter_emacs", };
|
||||
|
||||
for (String dependency : libraryDeps)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.loadLibrary (dependency);
|
||||
}
|
||||
catch (UnsatisfiedLinkError exception)
|
||||
{
|
||||
/* Ignore this exception. */
|
||||
}
|
||||
}
|
||||
|
||||
System.loadLibrary ("emacs");
|
||||
};
|
||||
};
|
||||
203
java/org/gnu/emacs/EmacsNoninteractive.java
Normal file
203
java/org/gnu/emacs/EmacsNoninteractive.java
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.Build;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/* Noninteractive Emacs.
|
||||
|
||||
This is the class that libandroid-emacs.so starts.
|
||||
libandroid-emacs.so figures out the system classpath, then starts
|
||||
dalvikvm with the framework jars.
|
||||
|
||||
At that point, dalvikvm calls main, which sets up the main looper,
|
||||
creates an ActivityThread and attaches it to the main thread.
|
||||
|
||||
Then, it obtains an application context for the LoadedApk in the
|
||||
application thread.
|
||||
|
||||
Finally, it obtains the necessary context specific objects and
|
||||
initializes Emacs. */
|
||||
|
||||
@SuppressWarnings ("unchecked")
|
||||
public final class EmacsNoninteractive
|
||||
{
|
||||
public static void
|
||||
main (String[] args)
|
||||
{
|
||||
Object activityThread, loadedApk;
|
||||
Class activityThreadClass, loadedApkClass, contextImplClass;
|
||||
Class compatibilityInfoClass;
|
||||
Method method;
|
||||
Context context;
|
||||
AssetManager assets;
|
||||
String filesDir, libDir, cacheDir;
|
||||
|
||||
Looper.prepare ();
|
||||
context = null;
|
||||
assets = null;
|
||||
filesDir = libDir = cacheDir = null;
|
||||
|
||||
try
|
||||
{
|
||||
/* Get the activity thread. */
|
||||
activityThreadClass = Class.forName ("android.app.ActivityThread");
|
||||
|
||||
/* Get the systemMain method. */
|
||||
method = activityThreadClass.getMethod ("systemMain");
|
||||
|
||||
/* Create and attach the activity thread. */
|
||||
activityThread = method.invoke (null);
|
||||
context = null;
|
||||
|
||||
/* Now get an LoadedApk. */
|
||||
|
||||
try
|
||||
{
|
||||
loadedApkClass = Class.forName ("android.app.LoadedApk");
|
||||
}
|
||||
catch (ClassNotFoundException exception)
|
||||
{
|
||||
/* Android 2.2 has no LoadedApk class, but fortunately it
|
||||
does not need to be used, since contexts can be
|
||||
directly created. */
|
||||
|
||||
loadedApkClass = null;
|
||||
contextImplClass = Class.forName ("android.app.ContextImpl");
|
||||
|
||||
method = activityThreadClass.getDeclaredMethod ("getSystemContext");
|
||||
context = (Context) method.invoke (activityThread);
|
||||
method = contextImplClass.getDeclaredMethod ("createPackageContext",
|
||||
String.class,
|
||||
int.class);
|
||||
method.setAccessible (true);
|
||||
context = (Context) method.invoke (context, "org.gnu.emacs",
|
||||
0);
|
||||
}
|
||||
|
||||
/* If the context has not already been created, then do what
|
||||
is appropriate for newer versions of Android. */
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
/* Get a LoadedApk. How to do this varies by Android version.
|
||||
On Android 2.3.3 and earlier, there is no
|
||||
``compatibilityInfo'' argument to getPackageInfo. */
|
||||
|
||||
if (Build.VERSION.SDK_INT
|
||||
<= Build.VERSION_CODES.GINGERBREAD_MR1)
|
||||
{
|
||||
method
|
||||
= activityThreadClass.getMethod ("getPackageInfo",
|
||||
String.class,
|
||||
int.class);
|
||||
loadedApk = method.invoke (activityThread, "org.gnu.emacs",
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
compatibilityInfoClass
|
||||
= Class.forName ("android.content.res.CompatibilityInfo");
|
||||
|
||||
method
|
||||
= activityThreadClass.getMethod ("getPackageInfo",
|
||||
String.class,
|
||||
compatibilityInfoClass,
|
||||
int.class);
|
||||
loadedApk = method.invoke (activityThread, "org.gnu.emacs",
|
||||
null, 0);
|
||||
}
|
||||
|
||||
if (loadedApk == null)
|
||||
throw new RuntimeException ("getPackageInfo returned NULL");
|
||||
|
||||
/* Now, get a context. */
|
||||
contextImplClass = Class.forName ("android.app.ContextImpl");
|
||||
|
||||
try
|
||||
{
|
||||
method
|
||||
= contextImplClass.getDeclaredMethod ("createAppContext",
|
||||
activityThreadClass,
|
||||
loadedApkClass);
|
||||
method.setAccessible (true);
|
||||
context = (Context) method.invoke (null, activityThread,
|
||||
loadedApk);
|
||||
}
|
||||
catch (NoSuchMethodException exception)
|
||||
{
|
||||
/* Older Android versions don't have createAppContext, but
|
||||
instead require creating a ContextImpl, and then
|
||||
calling createPackageContext. */
|
||||
method
|
||||
= activityThreadClass.getDeclaredMethod ("getSystemContext");
|
||||
context = (Context) method.invoke (activityThread);
|
||||
method
|
||||
= contextImplClass.getDeclaredMethod ("createPackageContext",
|
||||
String.class,
|
||||
int.class);
|
||||
method.setAccessible (true);
|
||||
context = (Context) method.invoke (context, "org.gnu.emacs",
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't actually start the looper or anything. Instead, obtain
|
||||
an AssetManager. */
|
||||
assets = context.getAssets ();
|
||||
|
||||
/* Now configure Emacs. The class path should already be set. */
|
||||
|
||||
filesDir = context.getFilesDir ().getCanonicalPath ();
|
||||
libDir = EmacsService.getLibraryDirectory (context);
|
||||
cacheDir = context.getCacheDir ().getCanonicalPath ();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.err.println ("Internal error: " + e);
|
||||
System.err.println ("This means that the Android platform changed,");
|
||||
System.err.println ("and that Emacs needs adjustments in order to");
|
||||
System.err.println ("obtain required system internal resources.");
|
||||
System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
|
||||
e.printStackTrace ();
|
||||
|
||||
System.exit (1);
|
||||
}
|
||||
|
||||
EmacsNative.setEmacsParams (assets, filesDir,
|
||||
libDir, cacheDir, 0.0f,
|
||||
0.0f, null, null);
|
||||
|
||||
/* Now find the dump file that Emacs should use, if it has already
|
||||
been dumped. */
|
||||
EmacsApplication.findDumpFile (context);
|
||||
|
||||
/* Start Emacs. */
|
||||
EmacsNative.initEmacs (args, EmacsApplication.dumpFileName,
|
||||
Build.VERSION.SDK_INT);
|
||||
}
|
||||
};
|
||||
472
java/org/gnu/emacs/EmacsOpenActivity.java
Normal file
472
java/org/gnu/emacs/EmacsOpenActivity.java
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
/* This class makes the Emacs server work reasonably on Android.
|
||||
|
||||
There is no way to make the Unix socket publicly available on
|
||||
Android.
|
||||
|
||||
Instead, this activity tries to connect to the Emacs server, to
|
||||
make it open files the system asks Emacs to open, and to emulate
|
||||
some reasonable behavior when Emacs has not yet started.
|
||||
|
||||
First, Emacs registers itself as an application that can open text
|
||||
and image files.
|
||||
|
||||
Then, when the user is asked to open a file and selects ``Emacs''
|
||||
as the application that will open the file, the system pops up a
|
||||
window, this activity, and calls the `onCreate' function.
|
||||
|
||||
`onCreate' then tries very to find the file name of the file that
|
||||
was selected, and give it to emacsclient.
|
||||
|
||||
If emacsclient successfully opens the file, then this activity
|
||||
starts EmacsActivity (to bring it on to the screen); otherwise, it
|
||||
displays the output of emacsclient or any error message that occurs
|
||||
and exits. */
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Activity;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public final class EmacsOpenActivity extends Activity
|
||||
implements DialogInterface.OnClickListener,
|
||||
DialogInterface.OnCancelListener
|
||||
{
|
||||
private static final String TAG = "EmacsOpenActivity";
|
||||
public static String fileToOpen;
|
||||
|
||||
private class EmacsClientThread extends Thread
|
||||
{
|
||||
private ProcessBuilder builder;
|
||||
|
||||
public
|
||||
EmacsClientThread (ProcessBuilder processBuilder)
|
||||
{
|
||||
builder = processBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
Process process;
|
||||
InputStream error;
|
||||
String errorText;
|
||||
|
||||
try
|
||||
{
|
||||
/* Start emacsclient. */
|
||||
process = builder.start ();
|
||||
process.waitFor ();
|
||||
|
||||
/* Now figure out whether or not starting the process was
|
||||
successful. */
|
||||
if (process.exitValue () == 0)
|
||||
finishSuccess ();
|
||||
else
|
||||
finishFailure ("Error opening file", null);
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
finishFailure ("Internal error", exception.toString ());
|
||||
}
|
||||
catch (InterruptedException exception)
|
||||
{
|
||||
finishFailure ("Internal error", exception.toString ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onClick (DialogInterface dialog, int which)
|
||||
{
|
||||
finish ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCancel (DialogInterface dialog)
|
||||
{
|
||||
finish ();
|
||||
}
|
||||
|
||||
public String
|
||||
readEmacsClientLog ()
|
||||
{
|
||||
File file, cache;
|
||||
FileReader reader;
|
||||
char[] buffer;
|
||||
int rc;
|
||||
String what;
|
||||
|
||||
/* Because the ProcessBuilder functions necessary to redirect
|
||||
process output are not implemented on Android 7 and earlier,
|
||||
print a generic error message. */
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
return ("This is likely because the Emacs server"
|
||||
+ " is not running, or because you did"
|
||||
+ " not grant Emacs permission to access"
|
||||
+ " external storage.");
|
||||
|
||||
cache = getCacheDir ();
|
||||
file = new File (cache, "emacsclient.log");
|
||||
what = "";
|
||||
|
||||
try
|
||||
{
|
||||
reader = new FileReader (file);
|
||||
buffer = new char[2048];
|
||||
|
||||
while ((rc = reader.read (buffer, 0, 2048)) != -1)
|
||||
what += String.valueOf (buffer, 0, 2048);
|
||||
|
||||
reader.close ();
|
||||
return what;
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
return ("Couldn't read emacsclient.log: "
|
||||
+ exception.toString ());
|
||||
}
|
||||
}
|
||||
|
||||
private void
|
||||
displayFailureDialog (String title, String text)
|
||||
{
|
||||
AlertDialog.Builder builder;
|
||||
AlertDialog dialog;
|
||||
|
||||
builder = new AlertDialog.Builder (this);
|
||||
dialog = builder.create ();
|
||||
dialog.setTitle (title);
|
||||
|
||||
if (text == null)
|
||||
/* Read in emacsclient.log instead. */
|
||||
text = readEmacsClientLog ();
|
||||
|
||||
dialog.setMessage (text);
|
||||
dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this);
|
||||
dialog.setOnCancelListener (this);
|
||||
dialog.show ();
|
||||
}
|
||||
|
||||
/* Check that the specified FILE is readable. If Android 4.4 or
|
||||
later is being used, return URI formatted into a `/content/' file
|
||||
name.
|
||||
|
||||
If it is not, then copy the file in FD to a location in the
|
||||
system cache directory and return the name of that file. */
|
||||
|
||||
private String
|
||||
checkReadableOrCopy (String file, ParcelFileDescriptor fd,
|
||||
Uri uri)
|
||||
throws IOException, FileNotFoundException
|
||||
{
|
||||
File inFile;
|
||||
FileOutputStream outStream;
|
||||
InputStream stream;
|
||||
byte buffer[];
|
||||
int read;
|
||||
String content;
|
||||
|
||||
Log.d (TAG, "checkReadableOrCopy: " + file);
|
||||
|
||||
inFile = new File (file);
|
||||
|
||||
if (inFile.canRead ())
|
||||
return file;
|
||||
|
||||
Log.d (TAG, "checkReadableOrCopy: NO");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
{
|
||||
content = "/content/" + uri.getEncodedAuthority ();
|
||||
|
||||
for (String segment : uri.getPathSegments ())
|
||||
content += "/" + Uri.encode (segment);
|
||||
|
||||
/* Append the URI query. */
|
||||
|
||||
if (uri.getEncodedQuery () != null)
|
||||
content += "?" + uri.getEncodedQuery ();
|
||||
|
||||
Log.d (TAG, "checkReadableOrCopy: " + content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/* inFile is now the file being written to. */
|
||||
inFile = new File (getCacheDir (), inFile.getName ());
|
||||
buffer = new byte[4098];
|
||||
outStream = new FileOutputStream (inFile);
|
||||
stream = new FileInputStream (fd.getFileDescriptor ());
|
||||
|
||||
try
|
||||
{
|
||||
while ((read = stream.read (buffer)) >= 0)
|
||||
outStream.write (buffer, 0, read);
|
||||
}
|
||||
finally
|
||||
{
|
||||
/* Note that this does not close FD.
|
||||
|
||||
Keep in mind that execution is transferred to ``finally''
|
||||
even if an exception happens inside the while loop
|
||||
above. */
|
||||
stream.close ();
|
||||
outStream.close ();
|
||||
}
|
||||
|
||||
return inFile.getCanonicalPath ();
|
||||
}
|
||||
|
||||
/* Finish this activity in response to emacsclient having
|
||||
successfully opened a file.
|
||||
|
||||
In the main thread, close this window, and open a window
|
||||
belonging to an Emacs frame. */
|
||||
|
||||
public void
|
||||
finishSuccess ()
|
||||
{
|
||||
runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
intent = new Intent (EmacsOpenActivity.this,
|
||||
EmacsActivity.class);
|
||||
|
||||
/* This means only an existing frame will be displayed. */
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
startActivity (intent);
|
||||
|
||||
EmacsOpenActivity.this.finish ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Finish this activity after displaying a dialog associated with
|
||||
failure to open a file.
|
||||
|
||||
Use TITLE as the title of the dialog. If TEXT is non-NULL,
|
||||
display that text in the dialog. Otherwise, use the contents of
|
||||
emacsclient.log in the cache directory instead, or describe why
|
||||
that file cannot be read. */
|
||||
|
||||
public void
|
||||
finishFailure (final String title, final String text)
|
||||
{
|
||||
runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
displayFailureDialog (title, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void
|
||||
startEmacsClient (String fileName)
|
||||
{
|
||||
String libDir;
|
||||
ProcessBuilder builder;
|
||||
Process process;
|
||||
EmacsClientThread thread;
|
||||
File file;
|
||||
Intent intent;
|
||||
|
||||
/* If the Emacs service is not running, then start Emacs and make
|
||||
it open this file. */
|
||||
|
||||
if (EmacsService.SERVICE == null)
|
||||
{
|
||||
fileToOpen = fileName;
|
||||
intent = new Intent (EmacsOpenActivity.this,
|
||||
EmacsActivity.class);
|
||||
finish ();
|
||||
startActivity (intent);
|
||||
return;
|
||||
}
|
||||
|
||||
libDir = EmacsService.getLibraryDirectory (this);
|
||||
builder = new ProcessBuilder (libDir + "/libemacsclient.so",
|
||||
fileName, "--reuse-frame",
|
||||
"--timeout=10", "--no-wait");
|
||||
|
||||
/* Redirection is unfortunately not possible in Android 7 and
|
||||
earlier. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
file = new File (getCacheDir (), "emacsclient.log");
|
||||
|
||||
/* Redirect standard error to a file so that errors can be
|
||||
meaningfully reported. */
|
||||
|
||||
if (file.exists ())
|
||||
file.delete ();
|
||||
|
||||
builder.redirectError (file);
|
||||
}
|
||||
|
||||
/* Track process output in a new thread, since this is the UI
|
||||
thread and doing so here can cause deadlocks when EmacsService
|
||||
decides to wait for something. */
|
||||
|
||||
thread = new EmacsClientThread (builder);
|
||||
thread.start ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
String action, fileName;
|
||||
Intent intent;
|
||||
Uri uri;
|
||||
ContentResolver resolver;
|
||||
ParcelFileDescriptor fd;
|
||||
byte[] names;
|
||||
String errorBlurb;
|
||||
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
/* Obtain the intent that started Emacs. */
|
||||
intent = getIntent ();
|
||||
action = intent.getAction ();
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
finish ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now see if the action specified is supported by Emacs. */
|
||||
|
||||
if (action.equals ("android.intent.action.VIEW")
|
||||
|| action.equals ("android.intent.action.EDIT")
|
||||
|| action.equals ("android.intent.action.PICK"))
|
||||
{
|
||||
/* Obtain the URI of the action. */
|
||||
uri = intent.getData ();
|
||||
|
||||
if (uri == null)
|
||||
{
|
||||
finish ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now, try to get the file name. */
|
||||
|
||||
if (uri.getScheme ().equals ("file"))
|
||||
fileName = uri.getPath ();
|
||||
else
|
||||
{
|
||||
fileName = null;
|
||||
|
||||
if (uri.getScheme ().equals ("content"))
|
||||
{
|
||||
/* This is one of the annoying Android ``content''
|
||||
URIs. Most of the time, there is actually an
|
||||
underlying file, but it cannot be found without
|
||||
opening the file and doing readlink on its file
|
||||
descriptor in /proc/self/fd. */
|
||||
resolver = getContentResolver ();
|
||||
fd = null;
|
||||
|
||||
try
|
||||
{
|
||||
fd = resolver.openFileDescriptor (uri, "r");
|
||||
names = EmacsNative.getProcName (fd.getFd ());
|
||||
|
||||
/* What is the right encoding here? */
|
||||
|
||||
if (names != null)
|
||||
fileName = new String (names, "UTF-8");
|
||||
|
||||
fileName = checkReadableOrCopy (fileName, fd, uri);
|
||||
}
|
||||
catch (FileNotFoundException exception)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
if (fd != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fd.close ();
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileName == null)
|
||||
{
|
||||
errorBlurb = ("The URI: " + uri + " could not be opened"
|
||||
+ ", as it does not encode file name inform"
|
||||
+ "ation.");
|
||||
displayFailureDialog ("Error opening file", errorBlurb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* And start emacsclient. */
|
||||
startEmacsClient (fileName);
|
||||
}
|
||||
else
|
||||
finish ();
|
||||
}
|
||||
}
|
||||
192
java/org/gnu/emacs/EmacsPixmap.java
Normal file
192
java/org/gnu/emacs/EmacsPixmap.java
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.lang.IllegalArgumentException;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/* Drawable backed by bitmap. */
|
||||
|
||||
public final class EmacsPixmap extends EmacsHandleObject
|
||||
implements EmacsDrawable
|
||||
{
|
||||
/* The depth of the bitmap. This is not actually used, just defined
|
||||
in order to be consistent with X. */
|
||||
public int depth, width, height;
|
||||
|
||||
/* The bitmap itself. */
|
||||
public Bitmap bitmap;
|
||||
|
||||
/* The canvas used to draw to BITMAP. */
|
||||
public Canvas canvas;
|
||||
|
||||
/* Whether or not GC should be explicitly triggered upon
|
||||
release. */
|
||||
private boolean needCollect;
|
||||
|
||||
/* ID used to determine whether or not the GC clip rects
|
||||
changed. */
|
||||
private long gcClipRectID;
|
||||
|
||||
public
|
||||
EmacsPixmap (short handle, int colors[], int width,
|
||||
int height, int depth)
|
||||
{
|
||||
super (handle);
|
||||
|
||||
if (depth != 1 && depth != 24)
|
||||
throw new IllegalArgumentException ("Invalid depth specified"
|
||||
+ " for pixmap: " + depth);
|
||||
|
||||
switch (depth)
|
||||
{
|
||||
case 1:
|
||||
bitmap = Bitmap.createBitmap (colors, width, height,
|
||||
Bitmap.Config.ALPHA_8);
|
||||
break;
|
||||
|
||||
case 24:
|
||||
bitmap = Bitmap.createBitmap (colors, width, height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
bitmap.setHasAlpha (false);
|
||||
break;
|
||||
}
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
public
|
||||
EmacsPixmap (short handle, int width, int height, int depth)
|
||||
{
|
||||
super (handle);
|
||||
|
||||
if (depth != 1 && depth != 24)
|
||||
throw new IllegalArgumentException ("Invalid depth specified"
|
||||
+ " for pixmap: " + depth);
|
||||
|
||||
switch (depth)
|
||||
{
|
||||
case 1:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
bitmap = Bitmap.createBitmap (width, height,
|
||||
Bitmap.Config.ALPHA_8,
|
||||
false);
|
||||
else
|
||||
bitmap = Bitmap.createBitmap (width, height,
|
||||
Bitmap.Config.ALPHA_8);
|
||||
break;
|
||||
|
||||
case 24:
|
||||
|
||||
/* Emacs doesn't just use the first kind of `createBitmap'
|
||||
because the latter allows specifying that the pixmap is
|
||||
always opaque, which really increases efficiency. */
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
bitmap = Bitmap.createBitmap (width, height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
else
|
||||
bitmap = Bitmap.createBitmap (width, height,
|
||||
Bitmap.Config.ARGB_8888,
|
||||
false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||
/* On these old versions of Android, Bitmap.recycle frees bitmap
|
||||
contents immediately. */
|
||||
needCollect = false;
|
||||
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
|
||||
needCollect = (bitmap.getByteCount ()
|
||||
>= 1024 * 512);
|
||||
else
|
||||
needCollect = (bitmap.getAllocationByteCount ()
|
||||
>= 1024 * 512);
|
||||
|
||||
bitmap.eraseColor (0xff000000);
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Canvas
|
||||
lockCanvas (EmacsGC gc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (canvas == null)
|
||||
{
|
||||
canvas = new Canvas (bitmap);
|
||||
canvas.save ();
|
||||
}
|
||||
|
||||
/* Now see if clipping has to be redone. */
|
||||
if (gc.clipRectID == gcClipRectID)
|
||||
return canvas;
|
||||
|
||||
/* It does have to be redone. Reapply gc.real_clip_rects. */
|
||||
canvas.restore ();
|
||||
canvas.save ();
|
||||
|
||||
if (gc.real_clip_rects != null)
|
||||
{
|
||||
for (i = 0; i < gc.real_clip_rects.length; ++i)
|
||||
canvas.clipRect (gc.real_clip_rects[i]);
|
||||
}
|
||||
|
||||
/* Save the clip rect ID again. */
|
||||
gcClipRectID = gc.clipRectID;
|
||||
return canvas;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
damageRect (Rect damageRect)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap
|
||||
getBitmap ()
|
||||
{
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
destroyHandle ()
|
||||
{
|
||||
bitmap.recycle ();
|
||||
bitmap = null;
|
||||
|
||||
/* Collect the bitmap storage if the bitmap is big. */
|
||||
if (needCollect)
|
||||
Runtime.getRuntime ().gc ();
|
||||
}
|
||||
};
|
||||
141
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
141
java/org/gnu/emacs/EmacsPreferencesActivity.java
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import android.preference.*;
|
||||
|
||||
/* This module provides a ``preferences'' display for Emacs. It is
|
||||
supposed to be launched from inside the Settings application to
|
||||
perform various actions, such as starting Emacs with the ``-Q''
|
||||
option, which would not be possible otherwise, as there is no
|
||||
command line on Android.
|
||||
|
||||
Android provides a preferences activity, but it is deprecated.
|
||||
Unfortunately, there is no alternative that looks the same way. */
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
public final class EmacsPreferencesActivity extends PreferenceActivity
|
||||
{
|
||||
/* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and
|
||||
tell the system to EmacsActivity with some parameters later. */
|
||||
|
||||
private void
|
||||
startEmacsQ ()
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
intent = new Intent (this, EmacsActivity.class);
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
intent.putExtra ("org.gnu.emacs.START_DASH_Q", true);
|
||||
startActivity (intent);
|
||||
System.exit (0);
|
||||
}
|
||||
|
||||
/* Erase Emacs's dump file. */
|
||||
|
||||
private void
|
||||
eraseDumpFile ()
|
||||
{
|
||||
String wantedDumpFile;
|
||||
File file;
|
||||
Toast toast;
|
||||
|
||||
wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
|
||||
+ ".pdmp");
|
||||
file = new File (getFilesDir (), wantedDumpFile);
|
||||
|
||||
if (file.exists ())
|
||||
file.delete ();
|
||||
|
||||
/* Make sure to clear EmacsApplication.dumpFileName, or
|
||||
starting Emacs without restarting this program will
|
||||
make Emacs try to load a nonexistent dump file. */
|
||||
EmacsApplication.dumpFileName = null;
|
||||
|
||||
/* Display a message stating that the dump file has been
|
||||
erased. */
|
||||
toast = Toast.makeText (this, "Dump file removed",
|
||||
Toast.LENGTH_SHORT);
|
||||
toast.show ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
Preference tem;
|
||||
Preference.OnPreferenceClickListener listener;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
setTheme (android.R.style.Theme_DeviceDefault_Settings);
|
||||
else if (Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
setTheme (android.R.style.Theme_DeviceDefault);
|
||||
|
||||
/* This must come before using any preference APIs. */
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
/* Add preferences from the XML file where they are defined. */
|
||||
addPreferencesFromResource (R.xml.preferences);
|
||||
|
||||
/* Now, set up on click handlers for each of the preferences
|
||||
items. */
|
||||
|
||||
tem = findPreference ("start_quick");
|
||||
|
||||
listener = new Preference.OnPreferenceClickListener () {
|
||||
@Override
|
||||
public boolean
|
||||
onPreferenceClick (Preference preference)
|
||||
{
|
||||
startEmacsQ ();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
tem.setOnPreferenceClickListener (listener);
|
||||
|
||||
tem = findPreference ("erase_dump");
|
||||
|
||||
listener = new Preference.OnPreferenceClickListener () {
|
||||
@Override
|
||||
public boolean
|
||||
onPreferenceClick (Preference preference)
|
||||
{
|
||||
eraseDumpFile ();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
tem.setOnPreferenceClickListener (listener);
|
||||
}
|
||||
};
|
||||
290
java/org/gnu/emacs/EmacsSdk11Clipboard.java
Normal file
290
java/org/gnu/emacs/EmacsSdk11Clipboard.java
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/* This class implements EmacsClipboard for Android 3.0 and later
|
||||
systems. */
|
||||
|
||||
public final class EmacsSdk11Clipboard extends EmacsClipboard
|
||||
implements ClipboardManager.OnPrimaryClipChangedListener
|
||||
{
|
||||
private static final String TAG = "EmacsSdk11Clipboard";
|
||||
private ClipboardManager manager;
|
||||
private boolean ownsClipboard;
|
||||
private int clipboardChangedCount;
|
||||
private int monitoredClipboardChangedCount;
|
||||
private ContentResolver resolver;
|
||||
|
||||
public
|
||||
EmacsSdk11Clipboard ()
|
||||
{
|
||||
manager = EmacsService.SERVICE.getClipboardManager ();
|
||||
|
||||
/* The system forbids Emacs from reading clipboard data in the
|
||||
background under Android 10 or later. */
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
manager.addPrimaryClipChangedListener (this);
|
||||
|
||||
/* Now obtain the content resolver used to open file
|
||||
descriptors. */
|
||||
|
||||
resolver = EmacsService.SERVICE.getContentResolver ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void
|
||||
onPrimaryClipChanged ()
|
||||
{
|
||||
Log.d (TAG, ("onPrimaryClipChanged: "
|
||||
+ monitoredClipboardChangedCount
|
||||
+ " " + clipboardChangedCount));
|
||||
|
||||
/* Increment monitoredClipboardChangeCount. If it is now greater
|
||||
than clipboardChangedCount, then Emacs no longer owns the
|
||||
clipboard. */
|
||||
monitoredClipboardChangedCount++;
|
||||
|
||||
if (monitoredClipboardChangedCount > clipboardChangedCount)
|
||||
{
|
||||
ownsClipboard = false;
|
||||
|
||||
/* Reset both values back to 0. */
|
||||
monitoredClipboardChangedCount = 0;
|
||||
clipboardChangedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the clipboard text to CLIPBOARD, a string in UTF-8
|
||||
encoding. */
|
||||
|
||||
@Override
|
||||
public synchronized void
|
||||
setClipboard (byte[] bytes)
|
||||
{
|
||||
ClipData data;
|
||||
String string;
|
||||
|
||||
try
|
||||
{
|
||||
string = new String (bytes, "UTF-8");
|
||||
data = ClipData.newPlainText ("Emacs", string);
|
||||
manager.setPrimaryClip (data);
|
||||
ownsClipboard = true;
|
||||
|
||||
/* onPrimaryClipChanged will be called again. Use this
|
||||
variable to keep track of how many times the clipboard has
|
||||
been changed. */
|
||||
++clipboardChangedCount;
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
Log.w (TAG, "setClipboard: " + exception);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether or not Emacs owns the clipboard. Value is 1 if
|
||||
Emacs does, 0 if Emacs does not, and -1 if that information is
|
||||
unavailable. */
|
||||
|
||||
@Override
|
||||
public synchronized int
|
||||
ownsClipboard ()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
return -1;
|
||||
|
||||
return ownsClipboard ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Return whether or not clipboard content currently exists. */
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
clipboardExists ()
|
||||
{
|
||||
return manager.hasPrimaryClip ();
|
||||
}
|
||||
|
||||
/* Return the current content of the clipboard, as plain text, or
|
||||
NULL if no content is available. */
|
||||
|
||||
@Override
|
||||
public byte[]
|
||||
getClipboard ()
|
||||
{
|
||||
ClipData clip;
|
||||
CharSequence text;
|
||||
Context context;
|
||||
|
||||
clip = manager.getPrimaryClip ();
|
||||
|
||||
if (clip == null || clip.getItemCount () < 1)
|
||||
return null;
|
||||
|
||||
context = EmacsService.SERVICE;
|
||||
|
||||
try
|
||||
{
|
||||
text = clip.getItemAt (0).coerceToText (context);
|
||||
return text.toString ().getBytes ("UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
Log.w (TAG, "getClipboard: " + exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Return an array of targets currently provided by the
|
||||
clipboard, or NULL if there are none. */
|
||||
|
||||
@Override
|
||||
public byte[][]
|
||||
getClipboardTargets ()
|
||||
{
|
||||
ClipData clip;
|
||||
ClipDescription description;
|
||||
byte[][] typeArray;
|
||||
int i;
|
||||
|
||||
/* N.B. that Android calls the clipboard the ``primary clip''; it
|
||||
is not related to the X primary selection. */
|
||||
clip = manager.getPrimaryClip ();
|
||||
description = clip.getDescription ();
|
||||
i = description.getMimeTypeCount ();
|
||||
typeArray = new byte[i][i];
|
||||
|
||||
try
|
||||
{
|
||||
for (i = 0; i < description.getMimeTypeCount (); ++i)
|
||||
typeArray[i] = description.getMimeType (i).getBytes ("UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return typeArray;
|
||||
}
|
||||
|
||||
/* Return the clipboard data for the given target, or NULL if it
|
||||
does not exist.
|
||||
|
||||
Value is normally an array of three longs: the file descriptor,
|
||||
the start offset of the data, and its length; length may be
|
||||
AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
|
||||
from that offset to the end of the file.
|
||||
|
||||
Do not use this function to open text targets; use `getClipboard'
|
||||
for that instead, as it will handle selection data consisting
|
||||
solely of a URI. */
|
||||
|
||||
@Override
|
||||
public long[]
|
||||
getClipboardData (byte[] target)
|
||||
{
|
||||
ClipData data;
|
||||
String mimeType;
|
||||
int fd;
|
||||
AssetFileDescriptor assetFd;
|
||||
Uri uri;
|
||||
long[] value;
|
||||
|
||||
/* Decode the target given by Emacs. */
|
||||
try
|
||||
{
|
||||
mimeType = new String (target, "UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Log.d (TAG, "getClipboardData: "+ mimeType);
|
||||
|
||||
/* Now obtain the clipboard data and the data corresponding to
|
||||
that MIME type. */
|
||||
|
||||
data = manager.getPrimaryClip ();
|
||||
|
||||
if (data.getItemCount () < 1)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
uri = data.getItemAt (0).getUri ();
|
||||
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
Log.d (TAG, "getClipboardData: "+ uri);
|
||||
|
||||
/* Now open the file descriptor. */
|
||||
assetFd = resolver.openTypedAssetFileDescriptor (uri, mimeType,
|
||||
null);
|
||||
|
||||
/* Duplicate the file descriptor. */
|
||||
fd = assetFd.getParcelFileDescriptor ().getFd ();
|
||||
fd = EmacsNative.dup (fd);
|
||||
|
||||
/* Return the relevant information. */
|
||||
value = new long[] { fd, assetFd.getStartOffset (),
|
||||
assetFd.getLength (), };
|
||||
|
||||
/* Close the original offset. */
|
||||
assetFd.close ();
|
||||
|
||||
Log.d (TAG, "getClipboardData: "+ value);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Don't return value if the file descriptor couldn't be
|
||||
created. */
|
||||
|
||||
return fd != -1 ? value : null;
|
||||
}
|
||||
};
|
||||
114
java/org/gnu/emacs/EmacsSdk23FontDriver.java
Normal file
114
java/org/gnu/emacs/EmacsSdk23FontDriver.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
public final class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
|
||||
{
|
||||
private void
|
||||
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
|
||||
Paint paint, Rect bounds)
|
||||
{
|
||||
char[] text;
|
||||
|
||||
text = new char[2];
|
||||
text[0] = (char) code;
|
||||
text[1] = 'c';
|
||||
|
||||
paint.getTextBounds (text, 0, 1, bounds);
|
||||
|
||||
metrics.lbearing = (short) bounds.left;
|
||||
metrics.rbearing = (short) bounds.right;
|
||||
metrics.ascent = (short) -bounds.top;
|
||||
metrics.descent = (short) bounds.bottom;
|
||||
metrics.width
|
||||
= (short) paint.getRunAdvance (text, 0, 1, 0, 1, false, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
textExtents (FontObject font, int code[], FontMetrics fontMetrics)
|
||||
{
|
||||
int i;
|
||||
Paint paintCache;
|
||||
Rect boundsCache;
|
||||
Sdk7FontObject fontObject;
|
||||
char[] text;
|
||||
float width;
|
||||
|
||||
fontObject = (Sdk7FontObject) font;
|
||||
paintCache = fontObject.typeface.typefacePaint;
|
||||
paintCache.setTextSize (fontObject.pixelSize);
|
||||
boundsCache = new Rect ();
|
||||
|
||||
if (code.length == 0)
|
||||
{
|
||||
fontMetrics.lbearing = 0;
|
||||
fontMetrics.rbearing = 0;
|
||||
fontMetrics.ascent = 0;
|
||||
fontMetrics.descent = 0;
|
||||
fontMetrics.width = 0;
|
||||
}
|
||||
else if (code.length == 1)
|
||||
textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
|
||||
paintCache, boundsCache);
|
||||
else
|
||||
{
|
||||
text = new char[code.length + 1];
|
||||
|
||||
for (i = 0; i < code.length; ++i)
|
||||
text[i] = (char) code[i];
|
||||
|
||||
text[code.length] = 'c';
|
||||
|
||||
paintCache.getTextBounds (text, 0, code.length,
|
||||
boundsCache);
|
||||
width = paintCache.getRunAdvance (text, 0, code.length, 0,
|
||||
code.length,
|
||||
false, code.length);
|
||||
|
||||
fontMetrics.lbearing = (short) boundsCache.left;
|
||||
fontMetrics.rbearing = (short) boundsCache.right;
|
||||
fontMetrics.ascent = (short) -boundsCache.top;
|
||||
fontMetrics.descent = (short) boundsCache.bottom;
|
||||
fontMetrics.width = (short) width;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int
|
||||
hasChar (FontSpec font, char charCode)
|
||||
{
|
||||
Sdk7FontObject fontObject;
|
||||
Paint paint;
|
||||
|
||||
if (font instanceof Sdk7FontObject)
|
||||
{
|
||||
fontObject = (Sdk7FontObject) font;
|
||||
paint = fontObject.typeface.typefacePaint;
|
||||
}
|
||||
else
|
||||
paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
|
||||
|
||||
return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
|
||||
}
|
||||
};
|
||||
538
java/org/gnu/emacs/EmacsSdk7FontDriver.java
Normal file
538
java/org/gnu/emacs/EmacsSdk7FontDriver.java
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.Canvas;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class EmacsSdk7FontDriver extends EmacsFontDriver
|
||||
{
|
||||
private static final String TOFU_STRING = "\uDB3F\uDFFD";
|
||||
private static final String EM_STRING = "m";
|
||||
private static final String TAG = "EmacsSdk7FontDriver";
|
||||
|
||||
protected static final class Sdk7Typeface
|
||||
{
|
||||
/* The typeface and paint. */
|
||||
public Typeface typeface;
|
||||
public Paint typefacePaint;
|
||||
public String familyName;
|
||||
public int slant, width, weight, spacing;
|
||||
|
||||
public
|
||||
Sdk7Typeface (String fileName, Typeface typeface)
|
||||
{
|
||||
String style, testString;
|
||||
int index, measured, i;
|
||||
float[] widths;
|
||||
|
||||
slant = NORMAL;
|
||||
weight = REGULAR;
|
||||
width = UNSPECIFIED;
|
||||
spacing = PROPORTIONAL;
|
||||
|
||||
this.typeface = typeface;
|
||||
|
||||
typefacePaint = new Paint ();
|
||||
typefacePaint.setAntiAlias (true);
|
||||
typefacePaint.setTypeface (typeface);
|
||||
|
||||
/* For the calls to measureText below. */
|
||||
typefacePaint.setTextSize (10.0f);
|
||||
|
||||
/* Parse the file name into some useful data. First, strip off
|
||||
the extension. */
|
||||
fileName = fileName.split ("\\.", 2)[0];
|
||||
|
||||
/* Next, split the file name by dashes. Everything before the
|
||||
last dash is part of the family name. */
|
||||
index = fileName.lastIndexOf ("-");
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
style = fileName.substring (index + 1, fileName.length ());
|
||||
familyName = fileName.substring (0, index);
|
||||
|
||||
/* Look for something describing the weight. */
|
||||
if (style.contains ("Thin"))
|
||||
weight = THIN;
|
||||
else if (style.contains ("UltraLight"))
|
||||
weight = ULTRA_LIGHT;
|
||||
else if (style.contains ("SemiLight"))
|
||||
weight = SEMI_LIGHT;
|
||||
else if (style.contains ("Light"))
|
||||
weight = LIGHT;
|
||||
else if (style.contains ("Medium"))
|
||||
weight = MEDIUM;
|
||||
else if (style.contains ("SemiBold"))
|
||||
weight = SEMI_BOLD;
|
||||
else if (style.contains ("ExtraBold"))
|
||||
weight = EXTRA_BOLD;
|
||||
else if (style.contains ("Bold"))
|
||||
weight = BOLD;
|
||||
else if (style.contains ("Black"))
|
||||
weight = BLACK;
|
||||
else if (style.contains ("UltraHeavy"))
|
||||
weight = ULTRA_HEAVY;
|
||||
|
||||
/* And the slant. */
|
||||
if (style.contains ("ReverseOblique"))
|
||||
slant = OBLIQUE;
|
||||
else if (style.contains ("ReverseItalic"))
|
||||
slant = REVERSE_ITALIC;
|
||||
else if (style.contains ("Italic"))
|
||||
slant = ITALIC;
|
||||
else if (style.contains ("Oblique"))
|
||||
slant = OBLIQUE;
|
||||
|
||||
/* Finally, the width. */
|
||||
if (style.contains ("UltraCondensed"))
|
||||
width = ULTRA_CONDENSED;
|
||||
else if (style.contains ("ExtraCondensed"))
|
||||
width = EXTRA_CONDENSED;
|
||||
else if (style.contains ("SemiCondensed"))
|
||||
width = SEMI_CONDENSED;
|
||||
else if (style.contains ("Condensed"))
|
||||
width = CONDENSED;
|
||||
else if (style.contains ("SemiExpanded"))
|
||||
width = SEMI_EXPANDED;
|
||||
else if (style.contains ("ExtraExpanded"))
|
||||
width = EXTRA_EXPANDED;
|
||||
else if (style.contains ("UltraExpanded"))
|
||||
width = ULTRA_EXPANDED;
|
||||
else if (style.contains ("Expanded"))
|
||||
width = EXPANDED;
|
||||
|
||||
/* Guess the spacing information. */
|
||||
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
widths = new float[testString.length ()];
|
||||
|
||||
measured = typefacePaint.getTextWidths (testString,
|
||||
0, testString.length (),
|
||||
widths);
|
||||
spacing = MONO;
|
||||
for (i = 0; i < measured; ++i)
|
||||
{
|
||||
if (i != 0 && widths[i - 1] != widths[i])
|
||||
/* This isn't a monospace font. */
|
||||
spacing = PROPORTIONAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
familyName = fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
toString ()
|
||||
{
|
||||
return ("Sdk7Typeface ("
|
||||
+ String.valueOf (familyName) + ", "
|
||||
+ String.valueOf (slant) + ", "
|
||||
+ String.valueOf (width) + ", "
|
||||
+ String.valueOf (weight) + ", "
|
||||
+ String.valueOf (spacing) + ")");
|
||||
}
|
||||
};
|
||||
|
||||
protected static final class Sdk7FontEntity extends FontEntity
|
||||
{
|
||||
/* The typeface. */
|
||||
public Sdk7Typeface typeface;
|
||||
|
||||
public
|
||||
Sdk7FontEntity (Sdk7Typeface typeface)
|
||||
{
|
||||
float width;
|
||||
|
||||
foundry = "Google";
|
||||
family = typeface.familyName;
|
||||
adstyle = null;
|
||||
weight = typeface.weight;
|
||||
slant = typeface.slant;
|
||||
spacing = typeface.spacing;
|
||||
width = typeface.width;
|
||||
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
|
||||
|
||||
this.typeface = typeface;
|
||||
}
|
||||
};
|
||||
|
||||
protected final class Sdk7FontObject extends FontObject
|
||||
{
|
||||
/* The typeface. */
|
||||
public Sdk7Typeface typeface;
|
||||
|
||||
public
|
||||
Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
|
||||
{
|
||||
float totalWidth;
|
||||
String testWidth, testString;
|
||||
|
||||
this.typeface = typeface;
|
||||
this.pixelSize = pixelSize;
|
||||
|
||||
family = typeface.familyName;
|
||||
adstyle = null;
|
||||
weight = typeface.weight;
|
||||
slant = typeface.slant;
|
||||
spacing = typeface.spacing;
|
||||
width = typeface.width;
|
||||
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
|
||||
|
||||
/* Compute the ascent and descent. */
|
||||
typeface.typefacePaint.setTextSize (pixelSize);
|
||||
ascent
|
||||
= Math.round (-typeface.typefacePaint.ascent ());
|
||||
descent
|
||||
= Math.round (typeface.typefacePaint.descent ());
|
||||
|
||||
/* Compute the average width. */
|
||||
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
totalWidth = typeface.typefacePaint.measureText (testString);
|
||||
|
||||
if (totalWidth > 0)
|
||||
avgwidth = Math.round (totalWidth
|
||||
/ testString.length ());
|
||||
|
||||
/* Android doesn't expose the font average width and height
|
||||
information, so this will have to do. */
|
||||
minWidth = maxWidth = avgwidth;
|
||||
|
||||
/* This is different from avgwidth in the font spec! */
|
||||
averageWidth = avgwidth;
|
||||
|
||||
/* Set the space width. */
|
||||
totalWidth = typeface.typefacePaint.measureText (" ");
|
||||
spaceWidth = Math.round (totalWidth);
|
||||
|
||||
/* Set the height and default ascent. */
|
||||
height = ascent + descent;
|
||||
defaultAscent = ascent;
|
||||
}
|
||||
};
|
||||
|
||||
private String[] fontFamilyList;
|
||||
private Sdk7Typeface[] typefaceList;
|
||||
private Sdk7Typeface fallbackTypeface;
|
||||
|
||||
public
|
||||
EmacsSdk7FontDriver ()
|
||||
{
|
||||
int i;
|
||||
File systemFontsDirectory, fontFile;
|
||||
Typeface typeface;
|
||||
|
||||
systemFontsDirectory = new File ("/system/fonts");
|
||||
|
||||
fontFamilyList = systemFontsDirectory.list ();
|
||||
typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
|
||||
|
||||
/* It would be nice to avoid opening each and every font upon
|
||||
startup. But that doesn't seem to be possible on
|
||||
Android. */
|
||||
|
||||
for (i = 0; i < fontFamilyList.length; ++i)
|
||||
{
|
||||
fontFile = new File (systemFontsDirectory,
|
||||
fontFamilyList[i]);
|
||||
typeface = Typeface.createFromFile (fontFile);
|
||||
typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
|
||||
typeface);
|
||||
}
|
||||
|
||||
/* Initialize the default monospace and serif typefaces. */
|
||||
fallbackTypeface = new Sdk7Typeface ("monospace",
|
||||
Typeface.MONOSPACE);
|
||||
typefaceList[fontFamilyList.length] = fallbackTypeface;
|
||||
|
||||
fallbackTypeface = new Sdk7Typeface ("Monospace",
|
||||
Typeface.MONOSPACE);
|
||||
typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
|
||||
|
||||
fallbackTypeface = new Sdk7Typeface ("Sans Serif",
|
||||
Typeface.DEFAULT);
|
||||
typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
|
||||
}
|
||||
|
||||
private boolean
|
||||
checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
|
||||
{
|
||||
if (fontSpec.family != null
|
||||
&& !fontSpec.family.equals (typeface.familyName))
|
||||
return false;
|
||||
|
||||
if (fontSpec.slant != null
|
||||
&& !fontSpec.weight.equals (typeface.weight))
|
||||
return false;
|
||||
|
||||
if (fontSpec.spacing != null
|
||||
&& !fontSpec.spacing.equals (typeface.spacing))
|
||||
return false;
|
||||
|
||||
if (fontSpec.weight != null
|
||||
&& !fontSpec.weight.equals (typeface.weight))
|
||||
return false;
|
||||
|
||||
if (fontSpec.width != null
|
||||
&& !fontSpec.width.equals (typeface.width))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontEntity[]
|
||||
list (FontSpec fontSpec)
|
||||
{
|
||||
LinkedList<FontEntity> list;
|
||||
int i;
|
||||
|
||||
list = new LinkedList<FontEntity> ();
|
||||
|
||||
for (i = 0; i < typefaceList.length; ++i)
|
||||
{
|
||||
if (checkMatch (typefaceList[i], fontSpec))
|
||||
list.add (new Sdk7FontEntity (typefaceList[i]));
|
||||
}
|
||||
|
||||
return list.toArray (new FontEntity[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontEntity
|
||||
match (FontSpec fontSpec)
|
||||
{
|
||||
FontEntity[] entities;
|
||||
int i;
|
||||
|
||||
entities = this.list (fontSpec);
|
||||
|
||||
if (entities.length == 0)
|
||||
return new Sdk7FontEntity (fallbackTypeface);
|
||||
|
||||
return entities[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[]
|
||||
listFamilies ()
|
||||
{
|
||||
return fontFamilyList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontObject
|
||||
openFont (FontEntity fontEntity, int pixelSize)
|
||||
{
|
||||
return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
|
||||
pixelSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int
|
||||
hasChar (FontSpec font, char charCode)
|
||||
{
|
||||
float missingGlyphWidth, emGlyphWidth, width;
|
||||
Rect rect1, rect2;
|
||||
Paint paint;
|
||||
Sdk7FontObject fontObject;
|
||||
|
||||
if (font instanceof Sdk7FontObject)
|
||||
{
|
||||
fontObject = (Sdk7FontObject) font;
|
||||
paint = fontObject.typeface.typefacePaint;
|
||||
}
|
||||
else
|
||||
paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
|
||||
|
||||
paint.setTextSize (10);
|
||||
|
||||
if (Character.isWhitespace (charCode))
|
||||
return 1;
|
||||
|
||||
missingGlyphWidth = paint.measureText (TOFU_STRING);
|
||||
emGlyphWidth = paint.measureText (EM_STRING);
|
||||
width = paint.measureText ("" + charCode);
|
||||
|
||||
if (width == 0f)
|
||||
return 0;
|
||||
|
||||
if (width != missingGlyphWidth)
|
||||
return 1;
|
||||
|
||||
rect1 = new Rect ();
|
||||
rect2 = new Rect ();
|
||||
|
||||
paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
|
||||
rect1);
|
||||
paint.getTextBounds ("" + charCode, 0, 1, rect2);
|
||||
return rect1.equals (rect2) ? 0 : 1;
|
||||
}
|
||||
|
||||
private void
|
||||
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
|
||||
Paint paint, Rect bounds)
|
||||
{
|
||||
char[] text;
|
||||
|
||||
text = new char[1];
|
||||
text[0] = (char) code;
|
||||
|
||||
paint.getTextBounds (text, 0, 1, bounds);
|
||||
|
||||
/* bounds is the bounding box of the glyph corresponding to CODE.
|
||||
Translate these into XCharStruct values.
|
||||
|
||||
The origin is at 0, 0, and lbearing is the distance counting
|
||||
rightwards from the origin to the left most pixel in the glyph
|
||||
raster. rbearing is the distance between the origin and the
|
||||
rightmost pixel in the glyph raster. ascent is the distance
|
||||
counting upwards between the the topmost pixel in the glyph
|
||||
raster. descent is the distance (once again counting
|
||||
downwards) between the origin and the bottommost pixel in the
|
||||
glyph raster.
|
||||
|
||||
width is the distance between the origin and the origin of any
|
||||
character to the right. */
|
||||
|
||||
metrics.lbearing = (short) bounds.left;
|
||||
metrics.rbearing = (short) bounds.right;
|
||||
metrics.ascent = (short) -bounds.top;
|
||||
metrics.descent = (short) bounds.bottom;
|
||||
metrics.width = (short) paint.measureText ("" + text[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
textExtents (FontObject font, int code[], FontMetrics fontMetrics)
|
||||
{
|
||||
int i;
|
||||
Paint paintCache;
|
||||
Rect boundsCache;
|
||||
Sdk7FontObject fontObject;
|
||||
char[] text;
|
||||
float width;
|
||||
|
||||
fontObject = (Sdk7FontObject) font;
|
||||
paintCache = fontObject.typeface.typefacePaint;
|
||||
paintCache.setTextSize (fontObject.pixelSize);
|
||||
boundsCache = new Rect ();
|
||||
|
||||
if (code.length == 0)
|
||||
{
|
||||
fontMetrics.lbearing = 0;
|
||||
fontMetrics.rbearing = 0;
|
||||
fontMetrics.ascent = 0;
|
||||
fontMetrics.descent = 0;
|
||||
fontMetrics.width = 0;
|
||||
}
|
||||
else if (code.length == 1)
|
||||
textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
|
||||
paintCache, boundsCache);
|
||||
else
|
||||
{
|
||||
text = new char[code.length];
|
||||
|
||||
for (i = 0; i < code.length; ++i)
|
||||
text[i] = (char) code[i];
|
||||
|
||||
paintCache.getTextBounds (text, 0, code.length,
|
||||
boundsCache);
|
||||
width = paintCache.measureText (text, 0, code.length);
|
||||
|
||||
fontMetrics.lbearing = (short) boundsCache.left;
|
||||
fontMetrics.rbearing = (short) boundsCache.right;
|
||||
fontMetrics.ascent = (short) -boundsCache.top;
|
||||
fontMetrics.descent = (short) boundsCache.bottom;
|
||||
fontMetrics.width = (short) Math.round (width);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int
|
||||
encodeChar (FontObject fontObject, char charCode)
|
||||
{
|
||||
return charCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int
|
||||
draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
|
||||
int[] chars, int x, int y, int backgroundWidth,
|
||||
boolean withBackground)
|
||||
{
|
||||
Rect backgroundRect, bounds;
|
||||
Sdk7FontObject sdk7FontObject;
|
||||
char[] charsArray;
|
||||
int i;
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
|
||||
sdk7FontObject = (Sdk7FontObject) fontObject;
|
||||
charsArray = new char[chars.length];
|
||||
|
||||
for (i = 0; i < chars.length; ++i)
|
||||
charsArray[i] = (char) chars[i];
|
||||
|
||||
backgroundRect = new Rect ();
|
||||
backgroundRect.top = y - sdk7FontObject.ascent;
|
||||
backgroundRect.left = x;
|
||||
backgroundRect.right = x + backgroundWidth;
|
||||
backgroundRect.bottom = y + sdk7FontObject.descent;
|
||||
|
||||
canvas = drawable.lockCanvas (gc);
|
||||
|
||||
if (canvas == null)
|
||||
return 0;
|
||||
|
||||
paint = gc.gcPaint;
|
||||
paint.setStyle (Paint.Style.FILL);
|
||||
|
||||
if (withBackground)
|
||||
{
|
||||
paint.setColor (gc.background | 0xff000000);
|
||||
canvas.drawRect (backgroundRect, paint);
|
||||
paint.setColor (gc.foreground | 0xff000000);
|
||||
}
|
||||
|
||||
paint.setTextSize (sdk7FontObject.pixelSize);
|
||||
paint.setTypeface (sdk7FontObject.typeface.typeface);
|
||||
paint.setAntiAlias (true);
|
||||
canvas.drawText (charsArray, 0, chars.length, x, y, paint);
|
||||
|
||||
bounds = new Rect ();
|
||||
paint.getTextBounds (charsArray, 0, chars.length, bounds);
|
||||
bounds.offset (x, y);
|
||||
bounds.union (backgroundRect);
|
||||
drawable.damageRect (bounds);
|
||||
paint.setAntiAlias (false);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
147
java/org/gnu/emacs/EmacsSdk8Clipboard.java
Normal file
147
java/org/gnu/emacs/EmacsSdk8Clipboard.java
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
/* Importing the entire package instead of just the legacy
|
||||
ClipboardManager class avoids the deprecation warning. */
|
||||
|
||||
import android.text.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/* This class implements EmacsClipboard for Android 2.2 and other
|
||||
similarly old systems. */
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
public final class EmacsSdk8Clipboard extends EmacsClipboard
|
||||
{
|
||||
private static final String TAG = "EmacsSdk8Clipboard";
|
||||
private ClipboardManager manager;
|
||||
|
||||
public
|
||||
EmacsSdk8Clipboard ()
|
||||
{
|
||||
String what;
|
||||
Context context;
|
||||
|
||||
what = Context.CLIPBOARD_SERVICE;
|
||||
context = EmacsService.SERVICE;
|
||||
manager
|
||||
= (ClipboardManager) context.getSystemService (what);
|
||||
}
|
||||
|
||||
/* Set the clipboard text to CLIPBOARD, a string in UTF-8
|
||||
encoding. */
|
||||
|
||||
@Override
|
||||
public void
|
||||
setClipboard (byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
manager.setText (new String (bytes, "UTF-8"));
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
Log.w (TAG, "setClipboard: " + exception);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether or not Emacs owns the clipboard. Value is 1 if
|
||||
Emacs does, 0 if Emacs does not, and -1 if that information is
|
||||
unavailable. */
|
||||
|
||||
@Override
|
||||
public int
|
||||
ownsClipboard ()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return whether or not clipboard content currently exists. */
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
clipboardExists ()
|
||||
{
|
||||
return manager.hasText ();
|
||||
}
|
||||
|
||||
/* Return the current content of the clipboard, as plain text, or
|
||||
NULL if no content is available. */
|
||||
|
||||
@Override
|
||||
public byte[]
|
||||
getClipboard ()
|
||||
{
|
||||
String string;
|
||||
CharSequence text;
|
||||
|
||||
text = manager.getText ();
|
||||
|
||||
if (text == null)
|
||||
return null;
|
||||
|
||||
string = text.toString ();
|
||||
|
||||
try
|
||||
{
|
||||
return string.getBytes ("UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
Log.w (TAG, "getClipboard: " + exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Return an array of targets currently provided by the
|
||||
clipboard, or NULL if there are none. */
|
||||
|
||||
@Override
|
||||
public byte[][]
|
||||
getClipboardTargets ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Return the clipboard data for the given target, or NULL if it
|
||||
does not exist.
|
||||
|
||||
Value is normally an array of three longs: the file descriptor,
|
||||
the start offset of the data, and its length; length may be
|
||||
AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
|
||||
from that offset to the end of the file.
|
||||
|
||||
Do not use this function to open text targets; use `getClipboard'
|
||||
for that instead, as it will handle selection data consisting
|
||||
solely of a URI. */
|
||||
|
||||
@Override
|
||||
public long[]
|
||||
getClipboardData (byte[] target)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
};
|
||||
914
java/org/gnu/emacs/EmacsService.java
Normal file
914
java/org/gnu/emacs/EmacsService.java
Normal file
|
|
@ -0,0 +1,914 @@
|
|||
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.CursorAnchorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.Service;
|
||||
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager.ApplicationInfoFlags;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import android.hardware.input.InputManager;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.os.IBinder;
|
||||
import android.os.Handler;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibratorManager;
|
||||
import android.os.VibrationEffect;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
class Holder<T>
|
||||
{
|
||||
T thing;
|
||||
};
|
||||
|
||||
/* EmacsService is the service that starts the thread running Emacs
|
||||
and handles requests by that Emacs instance. */
|
||||
|
||||
public final class EmacsService extends Service
|
||||
{
|
||||
public static final String TAG = "EmacsService";
|
||||
public static volatile EmacsService SERVICE;
|
||||
public static boolean needDashQ;
|
||||
|
||||
private EmacsThread thread;
|
||||
private Handler handler;
|
||||
private ContentResolver resolver;
|
||||
|
||||
/* Keep this in synch with androidgui.h. */
|
||||
public static final int IC_MODE_NULL = 0;
|
||||
public static final int IC_MODE_ACTION = 1;
|
||||
public static final int IC_MODE_TEXT = 2;
|
||||
|
||||
/* Display metrics used by font backends. */
|
||||
public DisplayMetrics metrics;
|
||||
|
||||
/* Flag that says whether or not to print verbose debugging
|
||||
information. */
|
||||
public static final boolean DEBUG_IC = false;
|
||||
|
||||
/* Return the directory leading to the directory in which native
|
||||
library files are stored on behalf of CONTEXT. */
|
||||
|
||||
public static String
|
||||
getLibraryDirectory (Context context)
|
||||
{
|
||||
int apiLevel;
|
||||
|
||||
apiLevel = Build.VERSION.SDK_INT;
|
||||
|
||||
if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
|
||||
return context.getApplicationInfo ().nativeLibraryDir;
|
||||
|
||||
return context.getApplicationInfo ().dataDir + "/lib";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int
|
||||
onStartCommand (Intent intent, int flags, int startId)
|
||||
{
|
||||
Notification notification;
|
||||
NotificationManager manager;
|
||||
NotificationChannel channel;
|
||||
String infoBlurb;
|
||||
Object tem;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
tem = getSystemService (Context.NOTIFICATION_SERVICE);
|
||||
manager = (NotificationManager) tem;
|
||||
infoBlurb = ("This notification is displayed to keep Emacs"
|
||||
+ " running while it is in the background. You"
|
||||
+ " may disable if you want;"
|
||||
+ " see (emacs)Android Environment.");
|
||||
channel
|
||||
= new NotificationChannel ("emacs", "Emacs persistent notification",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
manager.createNotificationChannel (channel);
|
||||
notification = (new Notification.Builder (this, "emacs")
|
||||
.setContentTitle ("Emacs")
|
||||
.setContentText (infoBlurb)
|
||||
.setSmallIcon (android.R.drawable.sym_def_app_icon)
|
||||
.build ());
|
||||
manager.notify (1, notification);
|
||||
startForeground (1, notification);
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder
|
||||
onBind (Intent intent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
private String
|
||||
getApkFile ()
|
||||
{
|
||||
PackageManager manager;
|
||||
ApplicationInfo info;
|
||||
|
||||
manager = getPackageManager ();
|
||||
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
|
||||
info = manager.getApplicationInfo ("org.gnu.emacs", 0);
|
||||
else
|
||||
info = manager.getApplicationInfo ("org.gnu.emacs",
|
||||
ApplicationInfoFlags.of (0));
|
||||
|
||||
/* Return an empty string upon failure. */
|
||||
|
||||
if (info.sourceDir != null)
|
||||
return info.sourceDir;
|
||||
|
||||
return "";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate ()
|
||||
{
|
||||
final AssetManager manager;
|
||||
Context app_context;
|
||||
final String filesDir, libDir, cacheDir, classPath;
|
||||
final double pixelDensityX;
|
||||
final double pixelDensityY;
|
||||
|
||||
SERVICE = this;
|
||||
handler = new Handler (Looper.getMainLooper ());
|
||||
manager = getAssets ();
|
||||
app_context = getApplicationContext ();
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
pixelDensityX = metrics.xdpi;
|
||||
pixelDensityY = metrics.ydpi;
|
||||
resolver = getContentResolver ();
|
||||
|
||||
try
|
||||
{
|
||||
/* Configure Emacs with the asset manager and other necessary
|
||||
parameters. */
|
||||
filesDir = app_context.getFilesDir ().getCanonicalPath ();
|
||||
libDir = getLibraryDirectory (this);
|
||||
cacheDir = app_context.getCacheDir ().getCanonicalPath ();
|
||||
|
||||
/* Now provide this application's apk file, so a recursive
|
||||
invocation of app_process (through android-emacs) can
|
||||
find EmacsNoninteractive. */
|
||||
classPath = getApkFile ();
|
||||
|
||||
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
|
||||
+ ", libDir = " + libDir + ", and classPath = " + classPath
|
||||
+ "; fileToOpen = " + EmacsOpenActivity.fileToOpen);
|
||||
|
||||
/* Start the thread that runs Emacs. */
|
||||
thread = new EmacsThread (this, new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
EmacsNative.setEmacsParams (manager, filesDir, libDir,
|
||||
cacheDir, (float) pixelDensityX,
|
||||
(float) pixelDensityY,
|
||||
classPath, EmacsService.this);
|
||||
}
|
||||
}, needDashQ,
|
||||
/* If any file needs to be opened, open it now. */
|
||||
EmacsOpenActivity.fileToOpen);
|
||||
thread.start ();
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Functions from here on must only be called from the Emacs
|
||||
thread. */
|
||||
|
||||
public void
|
||||
runOnUiThread (Runnable runnable)
|
||||
{
|
||||
handler.post (runnable);
|
||||
}
|
||||
|
||||
public EmacsView
|
||||
getEmacsView (final EmacsWindow window, final int visibility,
|
||||
final boolean isFocusedByDefault)
|
||||
{
|
||||
Runnable runnable;
|
||||
final Holder<EmacsView> view;
|
||||
|
||||
view = new Holder<EmacsView> ();
|
||||
|
||||
runnable = new Runnable () {
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
view.thing = new EmacsView (window);
|
||||
view.thing.setVisibility (visibility);
|
||||
|
||||
/* The following function is only present on Android 26
|
||||
or later. */
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
view.thing.setFocusedByDefault (isFocusedByDefault);
|
||||
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncRunnable (runnable);
|
||||
return view.thing;
|
||||
}
|
||||
|
||||
public void
|
||||
getLocationOnScreen (final EmacsView view, final int[] coordinates)
|
||||
{
|
||||
Runnable runnable;
|
||||
|
||||
runnable = new Runnable () {
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
view.getLocationOnScreen (coordinates);
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncRunnable (runnable);
|
||||
}
|
||||
|
||||
public void
|
||||
fillRectangle (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
EmacsFillRectangle.perform (drawable, gc, x, y,
|
||||
width, height);
|
||||
}
|
||||
|
||||
public void
|
||||
fillPolygon (EmacsDrawable drawable, EmacsGC gc,
|
||||
Point points[])
|
||||
{
|
||||
EmacsFillPolygon.perform (drawable, gc, points);
|
||||
}
|
||||
|
||||
public void
|
||||
drawRectangle (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
EmacsDrawRectangle.perform (drawable, gc, x, y,
|
||||
width, height);
|
||||
}
|
||||
|
||||
public void
|
||||
drawLine (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y, int x2, int y2)
|
||||
{
|
||||
EmacsDrawLine.perform (drawable, gc, x, y,
|
||||
x2, y2);
|
||||
}
|
||||
|
||||
public void
|
||||
drawPoint (EmacsDrawable drawable, EmacsGC gc,
|
||||
int x, int y)
|
||||
{
|
||||
EmacsDrawPoint.perform (drawable, gc, x, y);
|
||||
}
|
||||
|
||||
public void
|
||||
copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable,
|
||||
EmacsGC gc,
|
||||
int srcX, int srcY, int width, int height, int destX,
|
||||
int destY)
|
||||
{
|
||||
EmacsCopyArea.perform (srcDrawable, gc, dstDrawable,
|
||||
srcX, srcY, width, height, destX,
|
||||
destY);
|
||||
}
|
||||
|
||||
public void
|
||||
clearWindow (EmacsWindow window)
|
||||
{
|
||||
window.clearWindow ();
|
||||
}
|
||||
|
||||
public void
|
||||
clearArea (EmacsWindow window, int x, int y, int width,
|
||||
int height)
|
||||
{
|
||||
window.clearArea (x, y, width, height);
|
||||
}
|
||||
|
||||
@SuppressWarnings ("deprecation")
|
||||
public void
|
||||
ringBell ()
|
||||
{
|
||||
Vibrator vibrator;
|
||||
VibrationEffect effect;
|
||||
VibratorManager vibratorManager;
|
||||
Object tem;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
{
|
||||
tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
|
||||
vibratorManager = (VibratorManager) tem;
|
||||
vibrator = vibratorManager.getDefaultVibrator ();
|
||||
}
|
||||
else
|
||||
vibrator
|
||||
= (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
effect
|
||||
= VibrationEffect.createOneShot (50,
|
||||
VibrationEffect.DEFAULT_AMPLITUDE);
|
||||
vibrator.vibrate (effect);
|
||||
}
|
||||
else
|
||||
vibrator.vibrate (50);
|
||||
}
|
||||
|
||||
public short[]
|
||||
queryTree (EmacsWindow window)
|
||||
{
|
||||
short[] array;
|
||||
List<EmacsWindow> windowList;
|
||||
int i;
|
||||
|
||||
if (window == null)
|
||||
/* Just return all the windows without a parent. */
|
||||
windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
|
||||
else
|
||||
windowList = window.children;
|
||||
|
||||
array = new short[windowList.size () + 1];
|
||||
i = 1;
|
||||
|
||||
array[0] = (window == null
|
||||
? 0 : (window.parent != null
|
||||
? window.parent.handle : 0));
|
||||
|
||||
for (EmacsWindow treeWindow : windowList)
|
||||
array[i++] = treeWindow.handle;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public int
|
||||
getScreenWidth (boolean mmWise)
|
||||
{
|
||||
DisplayMetrics metrics;
|
||||
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
|
||||
if (!mmWise)
|
||||
return metrics.widthPixels;
|
||||
else
|
||||
return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
|
||||
}
|
||||
|
||||
public int
|
||||
getScreenHeight (boolean mmWise)
|
||||
{
|
||||
DisplayMetrics metrics;
|
||||
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
|
||||
if (!mmWise)
|
||||
return metrics.heightPixels;
|
||||
else
|
||||
return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
|
||||
}
|
||||
|
||||
public boolean
|
||||
detectMouse ()
|
||||
{
|
||||
InputManager manager;
|
||||
InputDevice device;
|
||||
int[] ids;
|
||||
int i;
|
||||
|
||||
if (Build.VERSION.SDK_INT
|
||||
< Build.VERSION_CODES.JELLY_BEAN)
|
||||
return false;
|
||||
|
||||
manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
|
||||
ids = manager.getInputDeviceIds ();
|
||||
|
||||
for (i = 0; i < ids.length; ++i)
|
||||
{
|
||||
device = manager.getInputDevice (ids[i]);
|
||||
|
||||
if (device == null)
|
||||
continue;
|
||||
|
||||
if (device.supportsSource (InputDevice.SOURCE_MOUSE))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String
|
||||
nameKeysym (int keysym)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||
return KeyEvent.keyCodeToString (keysym);
|
||||
|
||||
return String.valueOf (keysym);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Start the Emacs service if necessary. On Android 26 and up,
|
||||
start Emacs as a foreground service with a notification, to avoid
|
||||
it being killed by the system.
|
||||
|
||||
On older systems, simply start it as a normal background
|
||||
service. */
|
||||
|
||||
public static void
|
||||
startEmacsService (Context context)
|
||||
{
|
||||
if (EmacsService.SERVICE == null)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
/* Start the Emacs service now. */
|
||||
context.startService (new Intent (context,
|
||||
EmacsService.class));
|
||||
else
|
||||
/* Display the permanant notification and start Emacs as a
|
||||
foreground service. */
|
||||
context.startForegroundService (new Intent (context,
|
||||
EmacsService.class));
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask the system to open the specified URL.
|
||||
Value is NULL upon success, or a string describing the error
|
||||
upon failure. */
|
||||
|
||||
public String
|
||||
browseUrl (String url)
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
try
|
||||
{
|
||||
intent = new Intent (Intent.ACTION_VIEW, Uri.parse (url));
|
||||
intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity (intent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return e.toString ();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Get a SDK 11 ClipboardManager.
|
||||
|
||||
Android 4.0.x requires that this be called from the main
|
||||
thread. */
|
||||
|
||||
public ClipboardManager
|
||||
getClipboardManager ()
|
||||
{
|
||||
final Holder<ClipboardManager> manager;
|
||||
Runnable runnable;
|
||||
|
||||
manager = new Holder<ClipboardManager> ();
|
||||
|
||||
runnable = new Runnable () {
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
Object tem;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
tem = getSystemService (Context.CLIPBOARD_SERVICE);
|
||||
manager.thing = (ClipboardManager) tem;
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncRunnable (runnable);
|
||||
return manager.thing;
|
||||
}
|
||||
|
||||
public void
|
||||
restartEmacs ()
|
||||
{
|
||||
Intent intent;
|
||||
|
||||
intent = new Intent (this, EmacsActivity.class);
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity (intent);
|
||||
System.exit (0);
|
||||
}
|
||||
|
||||
/* Wait synchronously for the specified RUNNABLE to complete in the
|
||||
UI thread. Must be called from the Emacs thread. */
|
||||
|
||||
public static void
|
||||
syncRunnable (Runnable runnable)
|
||||
{
|
||||
EmacsNative.beginSynchronous ();
|
||||
|
||||
synchronized (runnable)
|
||||
{
|
||||
SERVICE.runOnUiThread (runnable);
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
runnable.wait ();
|
||||
break;
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmacsNative.endSynchronous ();
|
||||
}
|
||||
|
||||
public void
|
||||
updateIC (EmacsWindow window, int newSelectionStart,
|
||||
int newSelectionEnd, int composingRegionStart,
|
||||
int composingRegionEnd)
|
||||
{
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
|
||||
+ " " + newSelectionEnd + " "
|
||||
+ composingRegionStart + " "
|
||||
+ composingRegionEnd));
|
||||
window.view.imManager.updateSelection (window.view,
|
||||
newSelectionStart,
|
||||
newSelectionEnd,
|
||||
composingRegionStart,
|
||||
composingRegionEnd);
|
||||
}
|
||||
|
||||
public void
|
||||
resetIC (EmacsWindow window, int icMode)
|
||||
{
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, "resetIC: " + window);
|
||||
|
||||
window.view.setICMode (icMode);
|
||||
window.view.imManager.restartInput (window.view);
|
||||
}
|
||||
|
||||
public void
|
||||
updateCursorAnchorInfo (EmacsWindow window, float x,
|
||||
float y, float yBaseline,
|
||||
float yBottom)
|
||||
{
|
||||
CursorAnchorInfo info;
|
||||
CursorAnchorInfo.Builder builder;
|
||||
Matrix matrix;
|
||||
int[] offsets;
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
return;
|
||||
|
||||
offsets = new int[2];
|
||||
builder = new CursorAnchorInfo.Builder ();
|
||||
matrix = new Matrix (window.view.getMatrix ());
|
||||
window.view.getLocationOnScreen (offsets);
|
||||
matrix.postTranslate (offsets[0], offsets[1]);
|
||||
builder.setMatrix (matrix);
|
||||
builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom,
|
||||
0);
|
||||
info = builder.build ();
|
||||
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
|
||||
+ " " + yBaseline + "-" + yBottom));
|
||||
|
||||
window.view.imManager.updateCursorAnchorInfo (window.view, info);
|
||||
}
|
||||
|
||||
/* Open a content URI described by the bytes BYTES, a non-terminated
|
||||
string; make it writable if WRITABLE, and readable if READABLE.
|
||||
Truncate the file if TRUNCATE.
|
||||
|
||||
Value is the resulting file descriptor or -1 upon failure. */
|
||||
|
||||
public int
|
||||
openContentUri (byte[] bytes, boolean writable, boolean readable,
|
||||
boolean truncate)
|
||||
{
|
||||
String name, mode;
|
||||
ParcelFileDescriptor fd;
|
||||
int i;
|
||||
|
||||
/* Figure out the file access mode. */
|
||||
|
||||
mode = "";
|
||||
|
||||
if (readable)
|
||||
mode += "r";
|
||||
|
||||
if (writable)
|
||||
mode += "w";
|
||||
|
||||
if (truncate)
|
||||
mode += "t";
|
||||
|
||||
/* Try to open an associated ParcelFileDescriptor. */
|
||||
|
||||
try
|
||||
{
|
||||
/* The usual file name encoding question rears its ugly head
|
||||
again. */
|
||||
name = new String (bytes, "UTF-8");
|
||||
Log.d (TAG, "openContentUri: " + Uri.parse (name));
|
||||
|
||||
fd = resolver.openFileDescriptor (Uri.parse (name), mode);
|
||||
|
||||
/* Use detachFd on newer versions of Android or plain old
|
||||
dup. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||
{
|
||||
i = fd.detachFd ();
|
||||
fd.close ();
|
||||
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = EmacsNative.dup (fd.getFd ());
|
||||
fd.close ();
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean
|
||||
checkContentUri (byte[] string, boolean readable, boolean writable)
|
||||
{
|
||||
String mode, name;
|
||||
ParcelFileDescriptor fd;
|
||||
|
||||
/* Decode this into a URI. */
|
||||
|
||||
try
|
||||
{
|
||||
/* The usual file name encoding question rears its ugly head
|
||||
again. */
|
||||
name = new String (string, "UTF-8");
|
||||
Log.d (TAG, "checkContentUri: " + Uri.parse (name));
|
||||
}
|
||||
catch (UnsupportedEncodingException exception)
|
||||
{
|
||||
name = null;
|
||||
throw new RuntimeException (exception);
|
||||
}
|
||||
|
||||
mode = "r";
|
||||
|
||||
if (writable)
|
||||
mode += "w";
|
||||
|
||||
Log.d (TAG, "checkContentUri: checking against mode " + mode);
|
||||
|
||||
try
|
||||
{
|
||||
fd = resolver.openFileDescriptor (Uri.parse (name), mode);
|
||||
fd.close ();
|
||||
|
||||
Log.d (TAG, "checkContentUri: YES");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.d (TAG, "checkContentUri: NO");
|
||||
Log.d (TAG, exception.toString ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private long[]
|
||||
queryBattery19 ()
|
||||
{
|
||||
IntentFilter filter;
|
||||
Intent battery;
|
||||
long capacity, chargeCounter, currentAvg, currentNow;
|
||||
long status, remaining, plugged, temp;
|
||||
|
||||
filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
|
||||
battery = registerReceiver (null, filter);
|
||||
|
||||
if (battery == null)
|
||||
return null;
|
||||
|
||||
capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0);
|
||||
chargeCounter
|
||||
= (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0)
|
||||
/ battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100);
|
||||
currentAvg = 0;
|
||||
currentNow = 0;
|
||||
status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
|
||||
remaining = -1;
|
||||
plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
|
||||
temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
|
||||
|
||||
return new long[] { capacity, chargeCounter, currentAvg,
|
||||
currentNow, remaining, status, plugged,
|
||||
temp, };
|
||||
}
|
||||
|
||||
/* Return the status of the battery. See struct
|
||||
android_battery_status for the order of the elements
|
||||
returned.
|
||||
|
||||
Value may be null upon failure. */
|
||||
|
||||
public long[]
|
||||
queryBattery ()
|
||||
{
|
||||
Object tem;
|
||||
BatteryManager manager;
|
||||
long capacity, chargeCounter, currentAvg, currentNow;
|
||||
long status, remaining, plugged, temp;
|
||||
int prop;
|
||||
IntentFilter filter;
|
||||
Intent battery;
|
||||
|
||||
/* Android 4.4 or earlier require applications to use a different
|
||||
API to query the battery status. */
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
return queryBattery19 ();
|
||||
|
||||
tem = getSystemService (Context.BATTERY_SERVICE);
|
||||
manager = (BatteryManager) tem;
|
||||
remaining = -1;
|
||||
|
||||
prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
|
||||
capacity = manager.getLongProperty (prop);
|
||||
prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
|
||||
chargeCounter = manager.getLongProperty (prop);
|
||||
prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
|
||||
currentAvg = manager.getLongProperty (prop);
|
||||
prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
|
||||
currentNow = manager.getLongProperty (prop);
|
||||
|
||||
/* Return the battery status. N.B. that Android 7.1 and earlier
|
||||
only return ``charging'' or ``discharging''. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
status
|
||||
= manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
status = (manager.isCharging ()
|
||||
? BatteryManager.BATTERY_STATUS_CHARGING
|
||||
: BatteryManager.BATTERY_STATUS_DISCHARGING);
|
||||
else
|
||||
status = (currentNow > 0
|
||||
? BatteryManager.BATTERY_STATUS_CHARGING
|
||||
: BatteryManager.BATTERY_STATUS_DISCHARGING);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
remaining = manager.computeChargeTimeRemaining ();
|
||||
|
||||
plugged = -1;
|
||||
temp = -1;
|
||||
|
||||
/* Now obtain additional information from the battery manager. */
|
||||
|
||||
filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
|
||||
battery = registerReceiver (null, filter);
|
||||
|
||||
if (battery != null)
|
||||
{
|
||||
plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
|
||||
temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
|
||||
|
||||
/* Make status more reliable. */
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
|
||||
}
|
||||
|
||||
return new long[] { capacity, chargeCounter, currentAvg,
|
||||
currentNow, remaining, status, plugged,
|
||||
temp, };
|
||||
}
|
||||
|
||||
/* Display the specified STRING in a small dialog box on the main
|
||||
thread. */
|
||||
|
||||
public void
|
||||
displayToast (final String string)
|
||||
{
|
||||
runOnUiThread (new Runnable () {
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
Toast toast;
|
||||
|
||||
toast = Toast.makeText (getApplicationContext (),
|
||||
string, Toast.LENGTH_SHORT);
|
||||
toast.show ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void
|
||||
updateExtractedText (EmacsWindow window, ExtractedText text,
|
||||
int token)
|
||||
{
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
|
||||
|
||||
window.view.imManager.updateExtractedText (window.view,
|
||||
token, text);
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue