diff options
| author | Po Lu | 2023-04-06 09:56:23 +0800 |
|---|---|---|
| committer | Po Lu | 2023-04-06 09:56:23 +0800 |
| commit | 3b07a4b3158d024c6eb19ce0e7c67b799ae0d1fc (patch) | |
| tree | 079cb506217df20375835aa72c4bc52095c8c716 /src/androidselect.c | |
| parent | 458c6e5c9171f41f327ef88f4a4999db586f8e91 (diff) | |
| download | emacs-3b07a4b3158d024c6eb19ce0e7c67b799ae0d1fc.tar.gz emacs-3b07a4b3158d024c6eb19ce0e7c67b799ae0d1fc.zip | |
Implement `yank-media' on Android
* doc/emacs/android.texi (Android Windowing): Update selection
restrictions.
* java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New
functions `getClipboardTargets' and `getClipboardData'.
* java/org/gnu/emacs/EmacsSdk11Clipboard.java
(EmacsSdk11Clipboard, getClipboardTargets, getClipboardData):
Implement.
* java/org/gnu/emacs/EmacsSdk8Clipboard.java: Stub out new
functions.
* lisp/term/android-win.el (android-get-clipboard-1): Implement
MIME type targets.
* src/android.c (android_exception_check)
(android_exception_check_1, android_exception_check_2): Fix
punctuation in warning message.
(android_exception_check_nonnull_1): New function.
* src/android.h: Update prototypes.
* src/androidselect.c (struct android_emacs_clipboard): New
methods.
(android_init_emacs_clipboard): Initialize new methods.
(Fandroid_get_clipboard_targets, android_xfree_inside)
(Fandroid_get_clipboard_data): New functions.
(syms_of_androidselect): Define new subrs.
Diffstat (limited to 'src/androidselect.c')
| -rw-r--r-- | src/androidselect.c | 221 |
1 files changed, 219 insertions, 2 deletions
diff --git a/src/androidselect.c b/src/androidselect.c index dfbe0240941..54c712ca93b 100644 --- a/src/androidselect.c +++ b/src/androidselect.c | |||
| @@ -19,6 +19,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 19 | 19 | ||
| 20 | #include <config.h> | 20 | #include <config.h> |
| 21 | #include <assert.h> | 21 | #include <assert.h> |
| 22 | #include <minmax.h> | ||
| 23 | #include <unistd.h> | ||
| 22 | 24 | ||
| 23 | #include "lisp.h" | 25 | #include "lisp.h" |
| 24 | #include "blockinput.h" | 26 | #include "blockinput.h" |
| @@ -27,12 +29,15 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 27 | #include "androidterm.h" | 29 | #include "androidterm.h" |
| 28 | 30 | ||
| 29 | /* Selection support on Android is confined to copying and pasting of | 31 | /* Selection support on Android is confined to copying and pasting of |
| 30 | plain text from the clipboard. There is no primary selection. | 32 | plain text and MIME data from the clipboard. There is no primary |
| 33 | selection. | ||
| 31 | 34 | ||
| 32 | While newer versions of Android are supposed to have the necessary | 35 | While newer versions of Android are supposed to have the necessary |
| 33 | interfaces for transferring other kinds of selection data, doing so | 36 | interfaces for transferring other kinds of selection data, doing so |
| 34 | is too complicated, and involves registering ``content providers'' | 37 | is too complicated, and involves registering ``content providers'' |
| 35 | and all kinds of other stuff. */ | 38 | and all kinds of other stuff; for this reason, Emacs does not |
| 39 | support setting the clipboard contents to anything other than plain | ||
| 40 | text. */ | ||
| 36 | 41 | ||
| 37 | 42 | ||
| 38 | 43 | ||
| @@ -46,6 +51,8 @@ struct android_emacs_clipboard | |||
| 46 | jmethodID clipboard_exists; | 51 | jmethodID clipboard_exists; |
| 47 | jmethodID get_clipboard; | 52 | jmethodID get_clipboard; |
| 48 | jmethodID make_clipboard; | 53 | jmethodID make_clipboard; |
| 54 | jmethodID get_clipboard_targets; | ||
| 55 | jmethodID get_clipboard_data; | ||
| 49 | }; | 56 | }; |
| 50 | 57 | ||
| 51 | /* Methods associated with the EmacsClipboard class. */ | 58 | /* Methods associated with the EmacsClipboard class. */ |
| @@ -86,6 +93,10 @@ android_init_emacs_clipboard (void) | |||
| 86 | FIND_METHOD (owns_clipboard, "ownsClipboard", "()I"); | 93 | FIND_METHOD (owns_clipboard, "ownsClipboard", "()I"); |
| 87 | FIND_METHOD (clipboard_exists, "clipboardExists", "()Z"); | 94 | FIND_METHOD (clipboard_exists, "clipboardExists", "()Z"); |
| 88 | FIND_METHOD (get_clipboard, "getClipboard", "()[B"); | 95 | FIND_METHOD (get_clipboard, "getClipboard", "()[B"); |
| 96 | FIND_METHOD (get_clipboard_targets, "getClipboardTargets", | ||
| 97 | "()[[B"); | ||
| 98 | FIND_METHOD (get_clipboard_data, "getClipboardData", | ||
| 99 | "([B)[J"); | ||
| 89 | 100 | ||
| 90 | clipboard_class.make_clipboard | 101 | clipboard_class.make_clipboard |
| 91 | = (*android_java_env)->GetStaticMethodID (android_java_env, | 102 | = (*android_java_env)->GetStaticMethodID (android_java_env, |
| @@ -244,6 +255,210 @@ URL with a scheme specified. Signal an error upon failure. */) | |||
| 244 | 255 | ||
| 245 | 256 | ||
| 246 | 257 | ||
| 258 | /* MIME clipboard support. This provides support for reading MIME | ||
| 259 | data (but not text) from the clipboard. */ | ||
| 260 | |||
| 261 | DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets, | ||
| 262 | Sandroid_get_clipboard_targets, 0, 0, 0, | ||
| 263 | doc: /* Return a list of data types in the clipboard. | ||
| 264 | Value is a list of MIME types as strings, each defining a single extra | ||
| 265 | data type available from the clipboard. */) | ||
| 266 | (void) | ||
| 267 | { | ||
| 268 | jarray bytes_array; | ||
| 269 | jbyteArray bytes; | ||
| 270 | jmethodID method; | ||
| 271 | size_t length, length1, i; | ||
| 272 | jbyte *data; | ||
| 273 | Lisp_Object targets, tem; | ||
| 274 | |||
| 275 | if (!android_init_gui) | ||
| 276 | error ("No Android display connection!"); | ||
| 277 | |||
| 278 | targets = Qnil; | ||
| 279 | block_input (); | ||
| 280 | method = clipboard_class.get_clipboard_targets; | ||
| 281 | bytes_array = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 282 | clipboard, method); | ||
| 283 | android_exception_check (); | ||
| 284 | |||
| 285 | if (!bytes_array) | ||
| 286 | goto fail; | ||
| 287 | |||
| 288 | length = (*android_java_env)->GetArrayLength (android_java_env, | ||
| 289 | bytes_array); | ||
| 290 | for (i = 0; i < length; ++i) | ||
| 291 | { | ||
| 292 | /* Retireve the MIME type. */ | ||
| 293 | bytes | ||
| 294 | = (*android_java_env)->GetObjectArrayElement (android_java_env, | ||
| 295 | bytes_array, i); | ||
| 296 | android_exception_check_nonnull (bytes, bytes_array); | ||
| 297 | |||
| 298 | /* Cons it onto the list of targets. */ | ||
| 299 | length1 = (*android_java_env)->GetArrayLength (android_java_env, | ||
| 300 | bytes); | ||
| 301 | data = (*android_java_env)->GetByteArrayElements (android_java_env, | ||
| 302 | bytes, NULL); | ||
| 303 | android_exception_check_nonnull_1 (data, bytes, bytes_array); | ||
| 304 | |||
| 305 | /* Decode the string. */ | ||
| 306 | tem = make_unibyte_string ((char *) data, length1); | ||
| 307 | tem = code_convert_string_norecord (tem, Qutf_8, Qnil); | ||
| 308 | targets = Fcons (tem, targets); | ||
| 309 | |||
| 310 | /* Delete the retrieved data. */ | ||
| 311 | (*android_java_env)->ReleaseByteArrayElements (android_java_env, | ||
| 312 | bytes, data, | ||
| 313 | JNI_ABORT); | ||
| 314 | ANDROID_DELETE_LOCAL_REF (bytes); | ||
| 315 | } | ||
| 316 | unblock_input (); | ||
| 317 | |||
| 318 | ANDROID_DELETE_LOCAL_REF (bytes_array); | ||
| 319 | return Fnreverse (targets); | ||
| 320 | |||
| 321 | fail: | ||
| 322 | unblock_input (); | ||
| 323 | return Qnil; | ||
| 324 | } | ||
| 325 | |||
| 326 | /* Free the memory inside PTR, a pointer to a char pointer. */ | ||
| 327 | |||
| 328 | static void | ||
| 329 | android_xfree_inside (void *ptr) | ||
| 330 | { | ||
| 331 | xfree (*(char **) ptr); | ||
| 332 | } | ||
| 333 | |||
| 334 | DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data, | ||
| 335 | Sandroid_get_clipboard_data, 1, 1, 0, | ||
| 336 | doc: /* Return the clipboard data of the given MIME TYPE. | ||
| 337 | Value is a unibyte string containing the entire contents of the | ||
| 338 | clipboard, after its owner has converted the data to the given | ||
| 339 | MIME type. Value is nil if the conversion fails, or if the data | ||
| 340 | is not present. | ||
| 341 | |||
| 342 | Value is also nil if the clipboard data consists of a single URL which | ||
| 343 | does not have any corresponding data. In that case, use | ||
| 344 | `android-get-clipboard' instead. */) | ||
| 345 | (Lisp_Object type) | ||
| 346 | { | ||
| 347 | jlongArray array; | ||
| 348 | jbyteArray bytes; | ||
| 349 | jmethodID method; | ||
| 350 | int fd; | ||
| 351 | ptrdiff_t rc; | ||
| 352 | jlong offset, length, *longs; | ||
| 353 | specpdl_ref ref; | ||
| 354 | char *buffer, *start; | ||
| 355 | |||
| 356 | if (!android_init_gui) | ||
| 357 | error ("No Android display connection!"); | ||
| 358 | |||
| 359 | /* Encode the string as UTF-8. */ | ||
| 360 | CHECK_STRING (type); | ||
| 361 | type = ENCODE_UTF_8 (type); | ||
| 362 | |||
| 363 | /* Then give it to the selection code. */ | ||
| 364 | block_input (); | ||
| 365 | bytes = (*android_java_env)->NewByteArray (android_java_env, | ||
| 366 | SBYTES (type)); | ||
| 367 | (*android_java_env)->SetByteArrayRegion (android_java_env, bytes, | ||
| 368 | 0, SBYTES (type), | ||
| 369 | (jbyte *) SDATA (type)); | ||
| 370 | android_exception_check (); | ||
| 371 | |||
| 372 | method = clipboard_class.get_clipboard_data; | ||
| 373 | array = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 374 | clipboard, method, | ||
| 375 | bytes); | ||
| 376 | android_exception_check_1 (bytes); | ||
| 377 | ANDROID_DELETE_LOCAL_REF (bytes); | ||
| 378 | |||
| 379 | if (!array) | ||
| 380 | goto fail; | ||
| 381 | |||
| 382 | longs = (*android_java_env)->GetLongArrayElements (android_java_env, | ||
| 383 | array, NULL); | ||
| 384 | android_exception_check_nonnull (longs, array); | ||
| 385 | |||
| 386 | /* longs[0] is the file descriptor. | ||
| 387 | longs[1] is an offset to apply to the file. | ||
| 388 | longs[2] is either -1, or the number of bytes to read from the | ||
| 389 | file. */ | ||
| 390 | fd = longs[0]; | ||
| 391 | offset = longs[1]; | ||
| 392 | length = longs[2]; | ||
| 393 | |||
| 394 | (*android_java_env)->ReleaseLongArrayElements (android_java_env, | ||
| 395 | array, longs, | ||
| 396 | JNI_ABORT); | ||
| 397 | ANDROID_DELETE_LOCAL_REF (array); | ||
| 398 | unblock_input (); | ||
| 399 | |||
| 400 | /* Now begin reading from longs[0]. */ | ||
| 401 | ref = SPECPDL_INDEX (); | ||
| 402 | record_unwind_protect_int (close_file_unwind, fd); | ||
| 403 | |||
| 404 | if (length != -1) | ||
| 405 | { | ||
| 406 | buffer = xmalloc (MIN (length, PTRDIFF_MAX)); | ||
| 407 | record_unwind_protect_ptr (xfree, buffer); | ||
| 408 | |||
| 409 | rc = emacs_read_quit (fd, buffer, | ||
| 410 | MIN (length, PTRDIFF_MAX)); | ||
| 411 | |||
| 412 | /* Return nil upon an IO problem. */ | ||
| 413 | if (rc < 0) | ||
| 414 | return unbind_to (ref, Qnil); | ||
| 415 | |||
| 416 | /* Return the data as a unibyte string. */ | ||
| 417 | return unbind_to (ref, make_unibyte_string (buffer, rc)); | ||
| 418 | } | ||
| 419 | |||
| 420 | /* Otherwise, read BUFSIZ bytes at a time. */ | ||
| 421 | buffer = xmalloc (BUFSIZ); | ||
| 422 | length = 0; | ||
| 423 | start = buffer; | ||
| 424 | |||
| 425 | record_unwind_protect_ptr (android_xfree_inside, &buffer); | ||
| 426 | |||
| 427 | /* Seek to the start of the data. */ | ||
| 428 | |||
| 429 | if (offset) | ||
| 430 | { | ||
| 431 | if (lseek (fd, offset, SEEK_SET) < 0) | ||
| 432 | return unbind_to (ref, Qnil); | ||
| 433 | } | ||
| 434 | |||
| 435 | while (true) | ||
| 436 | { | ||
| 437 | rc = emacs_read_quit (fd, start, BUFSIZ); | ||
| 438 | |||
| 439 | if (!INT_ADD_OK (rc, length, &length) | ||
| 440 | || PTRDIFF_MAX - length < BUFSIZ) | ||
| 441 | memory_full (PTRDIFF_MAX); | ||
| 442 | |||
| 443 | if (rc < 0) | ||
| 444 | return unbind_to (ref, Qnil); | ||
| 445 | |||
| 446 | if (rc < BUFSIZ) | ||
| 447 | break; | ||
| 448 | |||
| 449 | buffer = xrealloc (buffer, length + BUFSIZ); | ||
| 450 | start = buffer + length; | ||
| 451 | } | ||
| 452 | |||
| 453 | return unbind_to (ref, make_unibyte_string (buffer, rc)); | ||
| 454 | |||
| 455 | fail: | ||
| 456 | unblock_input (); | ||
| 457 | return Qnil; | ||
| 458 | } | ||
| 459 | |||
| 460 | |||
| 461 | |||
| 247 | void | 462 | void |
| 248 | init_androidselect (void) | 463 | init_androidselect (void) |
| 249 | { | 464 | { |
| @@ -279,4 +494,6 @@ syms_of_androidselect (void) | |||
| 279 | defsubr (&Sandroid_get_clipboard); | 494 | defsubr (&Sandroid_get_clipboard); |
| 280 | defsubr (&Sandroid_clipboard_exists_p); | 495 | defsubr (&Sandroid_clipboard_exists_p); |
| 281 | defsubr (&Sandroid_browse_url); | 496 | defsubr (&Sandroid_browse_url); |
| 497 | defsubr (&Sandroid_get_clipboard_targets); | ||
| 498 | defsubr (&Sandroid_get_clipboard_data); | ||
| 282 | } | 499 | } |