summaryrefslogtreecommitdiff
path: root/src/androidselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/androidselect.c')
-rw-r--r--src/androidselect.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/androidselect.c b/src/androidselect.c
new file mode 100644
index 00000000000..54c712ca93b
--- /dev/null
+++ b/src/androidselect.c
@@ -0,0 +1,499 @@
+/* Communication module for Android terminals.
+
+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 <assert.h>
+#include <minmax.h>
+#include <unistd.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "android.h"
+#include "androidterm.h"
+
+/* Selection support on Android is confined to copying and pasting of
+ plain text and MIME data from the clipboard. There is no primary
+ selection.
+
+ While newer versions of Android are supposed to have the necessary
+ interfaces for transferring other kinds of selection data, doing so
+ is too complicated, and involves registering ``content providers''
+ and all kinds of other stuff; for this reason, Emacs does not
+ support setting the clipboard contents to anything other than plain
+ text. */
+
+
+
+/* Structure describing the EmacsClipboard class. */
+
+struct android_emacs_clipboard
+{
+ jclass class;
+ jmethodID set_clipboard;
+ jmethodID owns_clipboard;
+ jmethodID clipboard_exists;
+ jmethodID get_clipboard;
+ jmethodID make_clipboard;
+ jmethodID get_clipboard_targets;
+ jmethodID get_clipboard_data;
+};
+
+/* Methods associated with the EmacsClipboard class. */
+static struct android_emacs_clipboard clipboard_class;
+
+/* Reference to the EmacsClipboard object. */
+static jobject clipboard;
+
+
+
+static void
+android_init_emacs_clipboard (void)
+{
+ jclass old;
+
+ clipboard_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsClipboard");
+ eassert (clipboard_class.class);
+
+ old = clipboard_class.class;
+ clipboard_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!clipboard_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ clipboard_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ clipboard_class.class, \
+ name, signature); \
+ assert (clipboard_class.c_name);
+
+ FIND_METHOD (set_clipboard, "setClipboard", "([B)V");
+ FIND_METHOD (owns_clipboard, "ownsClipboard", "()I");
+ FIND_METHOD (clipboard_exists, "clipboardExists", "()Z");
+ FIND_METHOD (get_clipboard, "getClipboard", "()[B");
+ FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
+ "()[[B");
+ FIND_METHOD (get_clipboard_data, "getClipboardData",
+ "([B)[J");
+
+ clipboard_class.make_clipboard
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ clipboard_class.class,
+ "makeClipboard",
+ "()Lorg/gnu/emacs/"
+ "EmacsClipboard;");
+ assert (clipboard_class.make_clipboard);
+
+#undef FIND_METHOD
+}
+
+
+
+
+DEFUN ("android-clipboard-owner-p", Fandroid_clipboard_owner_p,
+ Sandroid_clipboard_owner_p, 0, 0, 0,
+ doc: /* Return whether or not Emacs owns the clipboard.
+Alternatively, return the symbol `lambda' if that could not be
+determined. */)
+ (void)
+{
+ jint rc;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ block_input ();
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ clipboard,
+ clipboard_class.owns_clipboard);
+ android_exception_check ();
+ unblock_input ();
+
+ /* If rc is 0 or 1, then Emacs knows whether or not it owns the
+ clipboard. If rc is -1, then Emacs does not. */
+
+ if (rc < 0)
+ return Qlambda;
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
+ Sandroid_set_clipboard, 1, 1, 0,
+ doc: /* Set the clipboard text to STRING. */)
+ (Lisp_Object string)
+{
+ jarray bytes;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ CHECK_STRING (string);
+ string = ENCODE_UTF_8 (string);
+
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (string));
+ android_exception_check ();
+
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (string),
+ (jbyte *) SDATA (string));
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ clipboard,
+ clipboard_class.set_clipboard,
+ bytes);
+ android_exception_check_1 (bytes);
+
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ return Qnil;
+}
+
+DEFUN ("android-get-clipboard", Fandroid_get_clipboard,
+ Sandroid_get_clipboard, 0, 0, 0,
+ doc: /* Return the current contents of the clipboard.
+Value is a multibyte string containing decoded clipboard
+text.
+Alternatively, return nil if the clipboard is empty. */)
+ (void)
+{
+ Lisp_Object string;
+ jarray bytes;
+ jmethodID method;
+ size_t length;
+ jbyte *data;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ method = clipboard_class.get_clipboard;
+ bytes
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull (data, bytes);
+
+ string = make_unibyte_string ((char *) data, length);
+
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ /* Now decode the resulting string. */
+ return code_convert_string_norecord (string, Qutf_8, Qnil);
+}
+
+DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p,
+ Sandroid_clipboard_exists_p, 0, 0, 0,
+ doc: /* Return whether or not clipboard contents exist. */)
+ (void)
+{
+ jboolean rc;
+ jmethodID method;
+
+ if (!android_init_gui)
+ error ("No Android display connection");
+
+ method = clipboard_class.clipboard_exists;
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-browse-url", Fandroid_browse_url,
+ Sandroid_browse_url, 1, 1, 0,
+ doc: /* Start the system web browser.
+Then, point the web browser to URL, which should be a URL-encoded
+URL with a scheme specified. Signal an error upon failure. */)
+ (Lisp_Object url)
+{
+ Lisp_Object value;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ CHECK_STRING (url);
+ value = android_browse_url (url);
+
+ /* Signal an error upon failure. */
+ if (!NILP (value))
+ signal_error ("Error browsing URL", value);
+
+ return Qnil;
+}
+
+
+
+/* MIME clipboard support. This provides support for reading MIME
+ data (but not text) from the clipboard. */
+
+DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
+ Sandroid_get_clipboard_targets, 0, 0, 0,
+ doc: /* Return a list of data types in the clipboard.
+Value is a list of MIME types as strings, each defining a single extra
+data type available from the clipboard. */)
+ (void)
+{
+ jarray bytes_array;
+ jbyteArray bytes;
+ jmethodID method;
+ size_t length, length1, i;
+ jbyte *data;
+ Lisp_Object targets, tem;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ targets = Qnil;
+ block_input ();
+ method = clipboard_class.get_clipboard_targets;
+ bytes_array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method);
+ android_exception_check ();
+
+ if (!bytes_array)
+ goto fail;
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes_array);
+ for (i = 0; i < length; ++i)
+ {
+ /* Retireve the MIME type. */
+ bytes
+ = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ bytes_array, i);
+ android_exception_check_nonnull (bytes, bytes_array);
+
+ /* Cons it onto the list of targets. */
+ length1 = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull_1 (data, bytes, bytes_array);
+
+ /* Decode the string. */
+ tem = make_unibyte_string ((char *) data, length1);
+ tem = code_convert_string_norecord (tem, Qutf_8, Qnil);
+ targets = Fcons (tem, targets);
+
+ /* Delete the retrieved data. */
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ }
+ unblock_input ();
+
+ ANDROID_DELETE_LOCAL_REF (bytes_array);
+ return Fnreverse (targets);
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+/* Free the memory inside PTR, a pointer to a char pointer. */
+
+static void
+android_xfree_inside (void *ptr)
+{
+ xfree (*(char **) ptr);
+}
+
+DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
+ Sandroid_get_clipboard_data, 1, 1, 0,
+ doc: /* Return the clipboard data of the given MIME TYPE.
+Value is a unibyte string containing the entire contents of the
+clipboard, after its owner has converted the data to the given
+MIME type. Value is nil if the conversion fails, or if the data
+is not present.
+
+Value is also nil if the clipboard data consists of a single URL which
+does not have any corresponding data. In that case, use
+`android-get-clipboard' instead. */)
+ (Lisp_Object type)
+{
+ jlongArray array;
+ jbyteArray bytes;
+ jmethodID method;
+ int fd;
+ ptrdiff_t rc;
+ jlong offset, length, *longs;
+ specpdl_ref ref;
+ char *buffer, *start;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ /* Encode the string as UTF-8. */
+ CHECK_STRING (type);
+ type = ENCODE_UTF_8 (type);
+
+ /* Then give it to the selection code. */
+ block_input ();
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (type));
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (type),
+ (jbyte *) SDATA (type));
+ android_exception_check ();
+
+ method = clipboard_class.get_clipboard_data;
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method,
+ bytes);
+ android_exception_check_1 (bytes);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ if (!array)
+ goto fail;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_nonnull (longs, array);
+
+ /* longs[0] is the file descriptor.
+ longs[1] is an offset to apply to the file.
+ longs[2] is either -1, or the number of bytes to read from the
+ file. */
+ fd = longs[0];
+ offset = longs[1];
+ length = longs[2];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+ unblock_input ();
+
+ /* Now begin reading from longs[0]. */
+ ref = SPECPDL_INDEX ();
+ record_unwind_protect_int (close_file_unwind, fd);
+
+ if (length != -1)
+ {
+ buffer = xmalloc (MIN (length, PTRDIFF_MAX));
+ record_unwind_protect_ptr (xfree, buffer);
+
+ rc = emacs_read_quit (fd, buffer,
+ MIN (length, PTRDIFF_MAX));
+
+ /* Return nil upon an IO problem. */
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ /* Return the data as a unibyte string. */
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+ }
+
+ /* Otherwise, read BUFSIZ bytes at a time. */
+ buffer = xmalloc (BUFSIZ);
+ length = 0;
+ start = buffer;
+
+ record_unwind_protect_ptr (android_xfree_inside, &buffer);
+
+ /* Seek to the start of the data. */
+
+ if (offset)
+ {
+ if (lseek (fd, offset, SEEK_SET) < 0)
+ return unbind_to (ref, Qnil);
+ }
+
+ while (true)
+ {
+ rc = emacs_read_quit (fd, start, BUFSIZ);
+
+ if (!INT_ADD_OK (rc, length, &length)
+ || PTRDIFF_MAX - length < BUFSIZ)
+ memory_full (PTRDIFF_MAX);
+
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ if (rc < BUFSIZ)
+ break;
+
+ buffer = xrealloc (buffer, length + BUFSIZ);
+ start = buffer + length;
+ }
+
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+
+
+void
+init_androidselect (void)
+{
+ jobject tem;
+ jmethodID make_clipboard;
+
+ if (!android_init_gui)
+ return;
+
+ android_init_emacs_clipboard ();
+
+ make_clipboard = clipboard_class.make_clipboard;
+ tem
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ clipboard_class.class,
+ make_clipboard);
+ if (!tem)
+ emacs_abort ();
+
+ clipboard = (*android_java_env)->NewGlobalRef (android_java_env, tem);
+
+ if (!clipboard)
+ emacs_abort ();
+
+ ANDROID_DELETE_LOCAL_REF (tem);
+}
+
+void
+syms_of_androidselect (void)
+{
+ defsubr (&Sandroid_clipboard_owner_p);
+ defsubr (&Sandroid_set_clipboard);
+ defsubr (&Sandroid_get_clipboard);
+ defsubr (&Sandroid_clipboard_exists_p);
+ defsubr (&Sandroid_browse_url);
+ defsubr (&Sandroid_get_clipboard_targets);
+ defsubr (&Sandroid_get_clipboard_data);
+}