summaryrefslogtreecommitdiff
path: root/src/haikuselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/haikuselect.c')
-rw-r--r--src/haikuselect.c1325
1 files changed, 1325 insertions, 0 deletions
diff --git a/src/haikuselect.c b/src/haikuselect.c
new file mode 100644
index 00000000000..bd004f4900a
--- /dev/null
+++ b/src/haikuselect.c
@@ -0,0 +1,1325 @@
+/* Haiku window system selection support.
+ Copyright (C) 2021-2022 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 "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "haikuselect.h"
+#include "haikuterm.h"
+#include "haiku_support.h"
+#include "keyboard.h"
+
+#include <stdlib.h>
+
+/* The frame that is currently the source of a drag-and-drop
+ operation, or NULL if none is in progress. The reason for this
+ variable is to prevent it from being deleted, which really breaks
+ the nested event loop inside be_drag_message. */
+struct frame *haiku_dnd_frame;
+
+/* Whether or not to move the tip frame during drag-and-drop. */
+bool haiku_dnd_follow_tooltip;
+
+/* Whether or not the current DND frame is able to receive drops from
+ the current drag-and-drop operation. */
+bool haiku_dnd_allow_same_frame;
+
+static void haiku_lisp_to_message (Lisp_Object, void *);
+
+static enum haiku_clipboard
+haiku_get_clipboard_name (Lisp_Object clipboard)
+{
+ if (EQ (clipboard, QPRIMARY))
+ return CLIPBOARD_PRIMARY;
+
+ if (EQ (clipboard, QSECONDARY))
+ return CLIPBOARD_SECONDARY;
+
+ if (EQ (clipboard, QCLIPBOARD))
+ return CLIPBOARD_CLIPBOARD;
+
+ signal_error ("Invalid clipboard", clipboard);
+}
+
+DEFUN ("haiku-selection-timestamp", Fhaiku_selection_timestamp,
+ Shaiku_selection_timestamp, 1, 1, 0,
+ doc: /* Retrieve the "timestamp" of the clipboard CLIPBOARD.
+CLIPBOARD can either be the symbol `PRIMARY', `SECONDARY' or
+`CLIPBOARD'. The timestamp is returned as a number describing the
+number of times programs have put data into CLIPBOARD. */)
+ (Lisp_Object clipboard)
+{
+ enum haiku_clipboard clipboard_name;
+ int64 timestamp;
+
+ clipboard_name = haiku_get_clipboard_name (clipboard);
+ timestamp = be_get_clipboard_count (clipboard_name);
+
+ return INT_TO_INTEGER (timestamp);
+}
+
+DEFUN ("haiku-selection-data", Fhaiku_selection_data, Shaiku_selection_data,
+ 2, 2, 0,
+ doc: /* Retrieve content typed as NAME from the clipboard
+CLIPBOARD. CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or
+`CLIPBOARD'. NAME is a string describing the MIME type denoting the
+type of the data to fetch. If NAME is nil, then the entire contents
+of the clipboard will be returned instead, as a serialized system
+message in the format accepted by `haiku-drag-message', which see. */)
+ (Lisp_Object clipboard, Lisp_Object name)
+{
+ char *dat;
+ ssize_t len;
+ Lisp_Object str;
+ void *message;
+ enum haiku_clipboard clipboard_name;
+ int rc;
+
+ CHECK_SYMBOL (clipboard);
+ clipboard_name = haiku_get_clipboard_name (clipboard);
+
+ if (!NILP (name))
+ {
+ CHECK_STRING (name);
+
+ block_input ();
+ dat = be_find_clipboard_data (clipboard_name,
+ SSDATA (name), &len);
+ unblock_input ();
+
+ if (!dat)
+ return Qnil;
+
+ str = make_unibyte_string (dat, len);
+
+ /* `foreign-selection' just means that the selection has to be
+ decoded by `gui-get-selection'. It has no other meaning,
+ AFAICT. */
+ Fput_text_property (make_fixnum (0), make_fixnum (len),
+ Qforeign_selection, Qt, str);
+
+ block_input ();
+ free (dat);
+ unblock_input ();
+ }
+ else
+ {
+ block_input ();
+ rc = be_lock_clipboard_message (clipboard_name, &message, false);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Couldn't open clipboard", clipboard);
+
+ block_input ();
+ str = haiku_message_to_lisp (message);
+ be_unlock_clipboard (clipboard_name, true);
+ unblock_input ();
+ }
+
+ return str;
+}
+
+static void
+haiku_unwind_clipboard_lock (int clipboard)
+{
+ be_unlock_clipboard (clipboard, false);
+}
+
+DEFUN ("haiku-selection-put", Fhaiku_selection_put, Shaiku_selection_put,
+ 2, 4, 0,
+ doc: /* Add or remove content from the clipboard CLIPBOARD.
+CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or `CLIPBOARD'. NAME
+is a MIME type denoting the type of the data to add. DATA is the
+string that will be placed in the clipboard, or nil if the content is
+to be removed. CLEAR, if non-nil, means to erase all the previous
+contents of the clipboard.
+
+Alternatively, NAME can be a system message in the format accepted by
+`haiku-drag-message', which will replace the contents of CLIPBOARD.
+In that case, the arguments after NAME are ignored. */)
+ (Lisp_Object clipboard, Lisp_Object name, Lisp_Object data,
+ Lisp_Object clear)
+{
+ enum haiku_clipboard clipboard_name;
+ specpdl_ref ref;
+ char *dat;
+ ptrdiff_t len;
+ int rc;
+ void *message;
+
+ CHECK_SYMBOL (clipboard);
+ clipboard_name = haiku_get_clipboard_name (clipboard);
+
+ if (CONSP (name) || NILP (name))
+ {
+ be_update_clipboard_count (clipboard_name);
+
+ rc = be_lock_clipboard_message (clipboard_name,
+ &message, true);
+
+ if (rc)
+ signal_error ("Couldn't open clipboard", clipboard);
+
+ ref = SPECPDL_INDEX ();
+ record_unwind_protect_int (haiku_unwind_clipboard_lock,
+ clipboard_name);
+ haiku_lisp_to_message (name, message);
+
+ return unbind_to (ref, Qnil);
+ }
+
+ CHECK_STRING (name);
+ if (!NILP (data))
+ CHECK_STRING (data);
+
+ dat = !NILP (data) ? SSDATA (data) : NULL;
+ len = !NILP (data) ? SBYTES (data) : 0;
+
+ be_set_clipboard_data (clipboard_name, SSDATA (name), dat, len,
+ !NILP (clear));
+ return Qnil;
+}
+
+DEFUN ("haiku-selection-owner-p", Fhaiku_selection_owner_p, Shaiku_selection_owner_p,
+ 0, 1, 0,
+ doc: /* Whether the current Emacs process owns the given SELECTION.
+The arg should be the name of the selection in question, typically one
+of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'. */)
+ (Lisp_Object selection)
+{
+ bool value;
+ enum haiku_clipboard name;
+
+ block_input ();
+ name = haiku_get_clipboard_name (selection);
+ value = be_clipboard_owner_p (name);
+ unblock_input ();
+
+ return value ? Qt : Qnil;
+}
+
+/* Return the Lisp representation of MESSAGE. See Fhaiku_drag_message
+ for the format of the object returned. */
+Lisp_Object
+haiku_message_to_lisp (void *message)
+{
+ Lisp_Object list = Qnil, tem, t1, t2;
+ const char *name;
+ char *pbuf;
+ const void *buf;
+ ssize_t buf_size;
+ int32 i, j, count, type_code;
+ int rc;
+ void *msg;
+ float point_x, point_y;
+
+ for (i = 0; !be_enum_message (message, &type_code, i,
+ &count, &name); ++i)
+ {
+ tem = Qnil;
+
+ for (j = 0; j < count; ++j)
+ {
+ rc = be_get_message_data (message, name,
+ type_code, j,
+ &buf, &buf_size);
+ if (rc)
+ emacs_abort ();
+
+ switch (type_code)
+ {
+ case 'MSGG':
+ msg = be_get_message_message (message, name, j);
+ if (!msg)
+ memory_full (SIZE_MAX);
+ t1 = haiku_message_to_lisp (msg);
+ BMessage_delete (msg);
+
+ break;
+
+ case 'BOOL':
+ t1 = (*(bool *) buf) ? Qt : Qnil;
+ break;
+
+ case 'RREF':
+ rc = be_get_refs_data (message, name,
+ j, &pbuf);
+
+ if (rc)
+ {
+ t1 = Qnil;
+ break;
+ }
+
+ if (!pbuf)
+ memory_full (SIZE_MAX);
+
+ t1 = DECODE_FILE (build_string (pbuf));
+
+ free (pbuf);
+ break;
+
+ case 'BPNT':
+ rc = be_get_point_data (message, name,
+ j, &point_x,
+ &point_y);
+
+ if (rc)
+ {
+ t1 = Qnil;
+ break;
+ }
+
+ t1 = Fcons (make_float (point_x),
+ make_float (point_y));
+ break;
+
+ case 'SHRT':
+ t1 = make_fixnum (*(int16 *) buf);
+ break;
+
+ case 'LONG':
+ t1 = make_int (*(int32 *) buf);
+ break;
+
+ case 'LLNG':
+ t1 = make_int ((intmax_t) *(int64 *) buf);
+ break;
+
+ case 'BYTE':
+ case 'CHAR':
+ t1 = make_fixnum (*(int8 *) buf);
+ break;
+
+ case 'SIZT':
+ t1 = make_uint ((uintmax_t) *(size_t *) buf);
+ break;
+
+ case 'SSZT':
+ t1 = make_int ((intmax_t) *(ssize_t *) buf);
+ break;
+
+ case 'DBLE':
+ t1 = make_float (*(double *) buf);
+ break;
+
+ case 'FLOT':
+ t1 = make_float (*(float *) buf);
+ break;
+
+ case 'CSTR':
+ /* Is this even possible? */
+ if (!buf_size)
+ buf_size = 1;
+
+ t1 = make_uninit_string (buf_size - 1);
+ memcpy (SDATA (t1), buf, buf_size - 1);
+ break;
+
+ default:
+ t1 = make_uninit_string (buf_size);
+ memcpy (SDATA (t1), buf, buf_size);
+ }
+
+ tem = Fcons (t1, tem);
+ }
+
+ switch (type_code)
+ {
+ case 'CSTR':
+ t2 = Qstring;
+ break;
+
+ case 'SHRT':
+ t2 = Qshort;
+ break;
+
+ case 'LONG':
+ t2 = Qlong;
+ break;
+
+ case 'LLNG':
+ t2 = Qllong;
+ break;
+
+ case 'BYTE':
+ t2 = Qbyte;
+ break;
+
+ case 'RREF':
+ t2 = Qref;
+ break;
+
+ case 'CHAR':
+ t2 = Qchar;
+ break;
+
+ case 'BOOL':
+ t2 = Qbool;
+ break;
+
+ case 'MSGG':
+ t2 = Qmessage;
+ break;
+
+ case 'SIZT':
+ t2 = Qsize_t;
+ break;
+
+ case 'SSZT':
+ t2 = Qssize_t;
+ break;
+
+ case 'BPNT':
+ t2 = Qpoint;
+ break;
+
+ case 'DBLE':
+ t2 = Qdouble;
+ break;
+
+ case 'FLOT':
+ t2 = Qfloat;
+ break;
+
+ default:
+ t2 = make_int (type_code);
+ }
+
+ tem = Fcons (t2, tem);
+ list = Fcons (Fcons (build_string_from_utf8 (name), tem), list);
+ }
+
+ tem = Fcons (Qtype, make_uint (be_get_message_type (message)));
+ return Fcons (tem, list);
+}
+
+static int32
+lisp_to_type_code (Lisp_Object obj)
+{
+ if (BIGNUMP (obj))
+ return (int32) bignum_to_intmax (obj);
+
+ if (FIXNUMP (obj))
+ return XFIXNUM (obj);
+
+ if (EQ (obj, Qstring))
+ return 'CSTR';
+ else if (EQ (obj, Qshort))
+ return 'SHRT';
+ else if (EQ (obj, Qlong))
+ return 'LONG';
+ else if (EQ (obj, Qllong))
+ return 'LLNG';
+ else if (EQ (obj, Qbyte))
+ return 'BYTE';
+ else if (EQ (obj, Qref))
+ return 'RREF';
+ else if (EQ (obj, Qchar))
+ return 'CHAR';
+ else if (EQ (obj, Qbool))
+ return 'BOOL';
+ else if (EQ (obj, Qmessage))
+ return 'MSGG';
+ else if (EQ (obj, Qsize_t))
+ return 'SIZT';
+ else if (EQ (obj, Qssize_t))
+ return 'SSZT';
+ else if (EQ (obj, Qpoint))
+ return 'BPNT';
+ else if (EQ (obj, Qfloat))
+ return 'FLOT';
+ else if (EQ (obj, Qdouble))
+ return 'DBLE';
+ else
+ return -1;
+}
+
+static void
+haiku_lisp_to_message (Lisp_Object obj, void *message)
+{
+ Lisp_Object tem, t1, name, type_sym, t2, data;
+ int32 type_code, long_data;
+ int16 short_data;
+ int64 llong_data;
+ int8 char_data;
+ bool bool_data;
+ void *msg_data;
+ size_t sizet_data;
+ ssize_t ssizet_data;
+ intmax_t t4;
+ uintmax_t t5;
+ float t6, t7, float_data;
+ double double_data;
+ int rc;
+ specpdl_ref ref;
+
+ tem = obj;
+
+ FOR_EACH_TAIL (tem)
+ {
+ t1 = XCAR (tem);
+ CHECK_CONS (t1);
+
+ name = XCAR (t1);
+
+ if (EQ (name, Qtype))
+ {
+ t2 = XCDR (t1);
+
+ if (BIGNUMP (t2))
+ {
+ t5 = bignum_to_uintmax (t2);
+
+ if (!t5 || t5 > TYPE_MAXIMUM (uint32))
+ signal_error ("Value too large", t2);
+
+ block_input ();
+ be_set_message_type (message, t5);
+ unblock_input ();
+ }
+ else
+ {
+ if (!TYPE_RANGED_FIXNUMP (uint32, t2))
+ signal_error ("Invalid data type", t2);
+
+ block_input ();
+ be_set_message_type (message, XFIXNAT (t2));
+ unblock_input ();
+ }
+
+ continue;
+ }
+
+ CHECK_STRING (name);
+
+ t1 = XCDR (t1);
+ CHECK_CONS (t1);
+
+ type_sym = XCAR (t1);
+ type_code = lisp_to_type_code (type_sym);
+
+ if (type_code == -1)
+ signal_error ("Unknown data type", type_sym);
+
+ CHECK_LIST (t1);
+ t2 = XCDR (t1);
+ FOR_EACH_TAIL (t2)
+ {
+ data = XCAR (t2);
+
+ if (FIXNUMP (type_sym) || BIGNUMP (type_sym))
+ goto decode_normally;
+
+ switch (type_code)
+ {
+ case 'MSGG':
+ ref = SPECPDL_INDEX ();
+
+ block_input ();
+ msg_data = be_create_simple_message ();
+ unblock_input ();
+
+ record_unwind_protect_ptr (BMessage_delete, msg_data);
+ haiku_lisp_to_message (data, msg_data);
+
+ block_input ();
+ rc = be_add_message_message (message, SSDATA (name), msg_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Invalid message", data);
+ unbind_to (ref, Qnil);
+ break;
+
+ case 'RREF':
+ CHECK_STRING (data);
+
+ if (be_add_refs_data (message, SSDATA (name),
+ SSDATA (ENCODE_FILE (data)))
+ && haiku_signal_invalid_refs)
+ signal_error ("Invalid file name", data);
+ break;
+
+ case 'BPNT':
+ CHECK_CONS (data);
+ CHECK_NUMBER (XCAR (data));
+ CHECK_NUMBER (XCDR (data));
+
+ t6 = XFLOATINT (XCAR (data));
+ t7 = XFLOATINT (XCDR (data));
+
+ if (be_add_point_data (message, SSDATA (name),
+ t6, t7))
+ signal_error ("Invalid point", data);
+ break;
+
+ case 'FLOT':
+ CHECK_NUMBER (data);
+ float_data = XFLOATINT (data);
+
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &float_data,
+ sizeof float_data);
+
+ if (rc)
+ signal_error ("Failed to add float", data);
+ break;
+
+ case 'DBLE':
+ CHECK_NUMBER (data);
+ double_data = XFLOATINT (data);
+
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &double_data,
+ sizeof double_data);
+
+ if (rc)
+ signal_error ("Failed to add double", data);
+ break;
+
+ case 'SHRT':
+ if (!TYPE_RANGED_FIXNUMP (int16, data))
+ signal_error ("Invalid value", data);
+ short_data = XFIXNUM (data);
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &short_data,
+ sizeof short_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add short", data);
+ break;
+
+ case 'LONG':
+ if (BIGNUMP (data))
+ {
+ t4 = bignum_to_intmax (data);
+
+ /* We know that int32 is signed. */
+ if (!t4 || t4 > TYPE_MINIMUM (int32)
+ || t4 < TYPE_MAXIMUM (int32))
+ signal_error ("Value too large", data);
+
+ long_data = (int32) t4;
+ }
+ else
+ {
+ if (!TYPE_RANGED_FIXNUMP (int32, data))
+ signal_error ("Invalid value", data);
+
+ long_data = (int32) XFIXNUM (data);
+ }
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &long_data,
+ sizeof long_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add long", data);
+ break;
+
+ case 'LLNG':
+ if (BIGNUMP (data))
+ {
+ t4 = bignum_to_intmax (data);
+
+ if (!t4 || t4 > TYPE_MINIMUM (int64)
+ || t4 < TYPE_MAXIMUM (int64))
+ signal_error ("Value too large", data);
+
+ llong_data = (int64) t4;
+ }
+ else
+ {
+ if (!TYPE_RANGED_FIXNUMP (int64, data))
+ signal_error ("Invalid value", data);
+
+ llong_data = (int64) XFIXNUM (data);
+ }
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &llong_data,
+ sizeof llong_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add llong", data);
+ break;
+
+ case 'SIZT':
+ if (BIGNUMP (data))
+ {
+ t4 = bignum_to_intmax (data);
+
+ if (!t4 || t4 > TYPE_MAXIMUM (size_t))
+ signal_error ("Value too large", data);
+
+ sizet_data = (size_t) t4;
+ }
+ else
+ {
+ if (!TYPE_RANGED_FIXNUMP (size_t, data))
+ signal_error ("Invalid value", data);
+
+ sizet_data = (int64) XFIXNUM (data);
+ }
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &sizet_data,
+ sizeof sizet_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add sizet", data);
+ break;
+
+ case 'SSZT':
+ if (BIGNUMP (data))
+ {
+ t4 = bignum_to_intmax (data);
+
+ if (!t4 || t4 > TYPE_MINIMUM (ssize_t)
+ || t4 < TYPE_MAXIMUM (ssize_t))
+ signal_error ("Value too large", data);
+
+ ssizet_data = (ssize_t) t4;
+ }
+ else
+ {
+ if (!TYPE_RANGED_FIXNUMP (ssize_t, data))
+ signal_error ("Invalid value", data);
+
+ ssizet_data = (int64) XFIXNUM (data);
+ }
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &ssizet_data,
+ sizeof ssizet_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add ssizet", data);
+ break;
+
+ case 'CHAR':
+ case 'BYTE':
+ if (!TYPE_RANGED_FIXNUMP (int8, data))
+ signal_error ("Invalid value", data);
+ char_data = XFIXNUM (data);
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &char_data,
+ sizeof char_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add char", data);
+ break;
+
+ case 'BOOL':
+ bool_data = !NILP (data);
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, &bool_data,
+ sizeof bool_data);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add bool", data);
+ break;
+
+ case 'CSTR':
+ /* C strings must be handled specially, since they
+ include a trailing NULL byte. */
+ CHECK_STRING (data);
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, SDATA (data),
+ SBYTES (data) + 1);
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add", data);
+ break;
+
+ default:
+ decode_normally:
+ CHECK_STRING (data);
+
+ block_input ();
+ rc = be_add_message_data (message, SSDATA (name),
+ type_code, SDATA (data),
+ SBYTES (data));
+ unblock_input ();
+
+ if (rc)
+ signal_error ("Failed to add", data);
+ }
+ }
+ CHECK_LIST_END (t2, t1);
+ }
+ CHECK_LIST_END (tem, obj);
+}
+
+static bool
+haiku_should_quit_drag (void)
+{
+ return !NILP (Vquit_flag);
+}
+
+static void
+haiku_unwind_drag_message (void *message)
+{
+ haiku_dnd_frame = NULL;
+ BMessage_delete (message);
+}
+
+static void
+haiku_report_system_error (status_t code, const char *format)
+{
+ switch (code)
+ {
+ case B_BAD_VALUE:
+ error (format, "Bad value");
+ break;
+
+ case B_ENTRY_NOT_FOUND:
+ error (format, "File not found");
+ break;
+
+ case B_PERMISSION_DENIED:
+ error (format, "Permission denied");
+ break;
+
+ case B_LINK_LIMIT:
+ error (format, "Link limit reached");
+ break;
+
+ case B_BUSY:
+ error (format, "Device busy");
+ break;
+
+ case B_NO_MORE_FDS:
+ error (format, "No more file descriptors");
+ break;
+
+ case B_FILE_ERROR:
+ error (format, "File error");
+ break;
+
+ case B_NO_MEMORY:
+ memory_full (SIZE_MAX);
+ break;
+
+ default:
+ error (format, "Unknown error");
+ break;
+ }
+}
+
+DEFUN ("haiku-drag-message", Fhaiku_drag_message, Shaiku_drag_message,
+ 2, 4, 0,
+ doc: /* Begin dragging MESSAGE from FRAME.
+
+MESSAGE an alist of strings, denoting message field names, to a list
+the form (TYPE DATA ...), where TYPE is an integer denoting the system
+data type of DATA, and DATA is in the general case a unibyte string.
+
+If TYPE is a symbol instead of an integer, then DATA was specially
+decoded. If TYPE is `ref', then DATA is the absolute file name of a
+file, or nil if decoding the file name failed. If TYPE is `string',
+then DATA is a unibyte string. If TYPE is `short', then DATA is a
+16-bit signed integer. If TYPE is `long', then DATA is a 32-bit
+signed integer. If TYPE is `llong', then DATA is a 64-bit signed
+integer. If TYPE is `byte' or `char', then DATA is an 8-bit signed
+integer. If TYPE is `bool', then DATA is a boolean. If TYPE is
+`size_t', then DATA is an integer that can hold between 0 and the
+maximum value returned by the `sizeof' C operator on the current
+system. If TYPE is `ssize_t', then DATA is an integer that can hold
+values from -1 to the maximum value of the C data type `ssize_t' on
+the current system. If TYPE is `point', then DATA is a cons of float
+values describing the X and Y coordinates of an on-screen location.
+If TYPE is `float', then DATA is a low-precision floating point
+number, whose exact precision is not guaranteed. If TYPE is `double',
+then DATA is a floating point number that can represent any value a
+Lisp float can represent.
+
+If the field name is not a string but the symbol `type', then it
+associates to a 32-bit unsigned integer describing the type of the
+system message.
+
+FRAME is a window system frame that must be visible, from which the
+drag will originate.
+
+ALLOW-SAME-FRAME, if nil or not specified, means that MESSAGE will be
+ignored if it is dropped on top of FRAME.
+
+FOLLOW-TOOLTIP, if non-nil, will cause any non-system tooltip
+currently being displayed to move along with the mouse pointer. */)
+ (Lisp_Object frame, Lisp_Object message, Lisp_Object allow_same_frame,
+ Lisp_Object follow_tooltip)
+{
+ specpdl_ref idx;
+ void *be_message;
+ struct frame *f;
+ bool rc;
+
+ idx = SPECPDL_INDEX ();
+ f = decode_window_system_frame (frame);
+
+ if (!FRAME_VISIBLE_P (f))
+ error ("Frame is invisible");
+
+ haiku_dnd_frame = f;
+ haiku_dnd_follow_tooltip = !NILP (follow_tooltip);
+ haiku_dnd_allow_same_frame = !NILP (allow_same_frame);
+
+ be_message = be_create_simple_message ();
+
+ record_unwind_protect_ptr (haiku_unwind_drag_message, be_message);
+ haiku_lisp_to_message (message, be_message);
+
+ rc = be_drag_message (FRAME_HAIKU_VIEW (f), be_message,
+ !NILP (allow_same_frame),
+ block_input, unblock_input,
+ process_pending_signals,
+ haiku_should_quit_drag);
+
+ /* Don't clear the mouse grab if the user decided to quit instead
+ of the drop finishing. */
+ if (rc)
+ quit ();
+
+ /* Now dismiss the tooltip, since the drop presumably succeeded. */
+ if (!NILP (follow_tooltip))
+ Fx_hide_tip ();
+
+ FRAME_DISPLAY_INFO (f)->grabbed = 0;
+
+ return unbind_to (idx, Qnil);
+}
+
+DEFUN ("haiku-roster-launch", Fhaiku_roster_launch, Shaiku_roster_launch,
+ 2, 2, 0,
+ doc: /* Launch an application associated with FILE-OR-TYPE.
+Return the process ID of any process created, the symbol
+`already-running' if ARGS was sent to a program that's already
+running, or nil if launching the application failed because no
+application was found for FILE-OR-TYPE.
+
+Signal an error if FILE-OR-TYPE is invalid, or if ARGS is a message
+but the application doesn't accept messages.
+
+FILE-OR-TYPE can either be a string denoting a MIME type, or a list
+with one argument FILE, denoting a file whose associated application
+will be launched.
+
+ARGS can either be a vector of strings containing the arguments that
+will be passed to the application, or a system message in the form
+accepted by `haiku-drag-message' that will be sent to the application
+after it starts. */)
+ (Lisp_Object file_or_type, Lisp_Object args)
+{
+ char **cargs;
+ char *type, *file;
+ team_id team_id;
+ status_t rc;
+ ptrdiff_t i, nargs;
+ Lisp_Object tem, canonical;
+ void *message;
+ specpdl_ref depth;
+
+ type = NULL;
+ file = NULL;
+ cargs = NULL;
+ message = NULL;
+ nargs = 0;
+ depth = SPECPDL_INDEX ();
+
+ USE_SAFE_ALLOCA;
+
+ if (STRINGP (file_or_type))
+ SAFE_ALLOCA_STRING (type, file_or_type);
+ else
+ {
+ CHECK_LIST (file_or_type);
+ tem = XCAR (file_or_type);
+ canonical = Fexpand_file_name (tem, Qnil);
+
+ CHECK_STRING (tem);
+ SAFE_ALLOCA_STRING (file, ENCODE_FILE (canonical));
+ CHECK_LIST_END (XCDR (file_or_type), file_or_type);
+ }
+
+ if (VECTORP (args))
+ {
+ nargs = ASIZE (args);
+ cargs = SAFE_ALLOCA (nargs * sizeof *cargs);
+
+ for (i = 0; i < nargs; ++i)
+ {
+ tem = AREF (args, i);
+ CHECK_STRING (tem);
+ maybe_quit ();
+
+ cargs[i] = SAFE_ALLOCA (SBYTES (tem) + 1);
+ memcpy (cargs[i], SDATA (tem), SBYTES (tem) + 1);
+ }
+ }
+ else
+ {
+ message = be_create_simple_message ();
+
+ record_unwind_protect_ptr (BMessage_delete, message);
+ haiku_lisp_to_message (args, message);
+ }
+
+ block_input ();
+ rc = be_roster_launch (type, file, cargs, nargs, message,
+ &team_id);
+ unblock_input ();
+
+ /* `be_roster_launch' can potentially take a while in IO, but
+ signals from async input will interrupt that operation. If the
+ user wanted to quit, act like it. */
+ maybe_quit ();
+
+ if (rc == B_OK)
+ return SAFE_FREE_UNBIND_TO (depth,
+ make_uint (team_id));
+ else if (rc == B_ALREADY_RUNNING)
+ return Qalready_running;
+ else if (rc == B_BAD_VALUE)
+ signal_error ("Invalid type or bad arguments",
+ list2 (file_or_type, args));
+
+ return SAFE_FREE_UNBIND_TO (depth, Qnil);
+}
+
+DEFUN ("haiku-write-node-attribute", Fhaiku_write_node_attribute,
+ Shaiku_write_node_attribute, 3, 3, 0,
+ doc: /* Write a message as a file-system attribute of NODE.
+FILE should be a file name of a file on a Be File System volume, NAME
+should be a string describing the name of the attribute that will be
+written, and MESSAGE will be the attribute written to FILE, as a
+system message in the format accepted by `haiku-drag-message', which
+see. */)
+ (Lisp_Object file, Lisp_Object name, Lisp_Object message)
+{
+ void *be_message;
+ status_t rc;
+ specpdl_ref count;
+
+ CHECK_STRING (file);
+ CHECK_STRING (name);
+
+ file = ENCODE_FILE (file);
+ name = ENCODE_SYSTEM (name);
+
+ be_message = be_create_simple_message ();
+ count = SPECPDL_INDEX ();
+
+ record_unwind_protect_ptr (BMessage_delete, be_message);
+ haiku_lisp_to_message (message, be_message);
+ rc = be_write_node_message (SSDATA (file), SSDATA (name),
+ be_message);
+
+ if (rc < B_OK)
+ haiku_report_system_error (rc, "Failed to set attribute: %s");
+
+ return unbind_to (count, Qnil);
+}
+
+DEFUN ("haiku-send-message", Fhaiku_send_message, Shaiku_send_message,
+ 2, 2, 0,
+ doc: /* Send a system message to PROGRAM.
+PROGRAM must be the name of the application to which the message will
+be sent. MESSAGE is the system message, serialized in the format
+accepted by `haiku-drag-message', that will be sent to the application
+specified by PROGRAM. There is no guarantee that the message will
+arrive after this function is called. */)
+ (Lisp_Object program, Lisp_Object message)
+{
+ specpdl_ref count;
+ void *be_message;
+
+ CHECK_STRING (program);
+ program = ENCODE_SYSTEM (program);
+
+ be_message = be_create_simple_message ();
+ count = SPECPDL_INDEX ();
+
+ record_unwind_protect_ptr (BMessage_delete, be_message);
+ haiku_lisp_to_message (message, be_message);
+ be_send_message (SSDATA (program), be_message);
+
+ return unbind_to (count, Qnil);
+}
+
+static void
+haiku_dnd_compute_tip_xy (int *root_x, int *root_y)
+{
+ int min_x, min_y, max_x, max_y;
+ int width, height;
+
+ width = FRAME_PIXEL_WIDTH (XFRAME (tip_frame));
+ height = FRAME_PIXEL_HEIGHT (XFRAME (tip_frame));
+
+ min_x = 0;
+ min_y = 0;
+ be_get_screen_dimensions (&max_x, &max_y);
+
+ if (*root_y + XFIXNUM (tip_dy) <= min_y)
+ *root_y = min_y; /* Can happen for negative dy */
+ else if (*root_y + XFIXNUM (tip_dy) + height <= max_y)
+ /* It fits below the pointer */
+ *root_y += XFIXNUM (tip_dy);
+ else if (height + XFIXNUM (tip_dy) + min_y <= *root_y)
+ /* It fits above the pointer. */
+ *root_y -= height + XFIXNUM (tip_dy);
+ else
+ /* Put it on the top. */
+ *root_y = min_y;
+
+ if (*root_x + XFIXNUM (tip_dx) <= min_x)
+ *root_x = 0; /* Can happen for negative dx */
+ else if (*root_x + XFIXNUM (tip_dx) + width <= max_x)
+ /* It fits to the right of the pointer. */
+ *root_x += XFIXNUM (tip_dx);
+ else if (width + XFIXNUM (tip_dx) + min_x <= *root_x)
+ /* It fits to the left of the pointer. */
+ *root_x -= width + XFIXNUM (tip_dx);
+ else
+ /* Put it left justified on the screen -- it ought to fit that way. */
+ *root_x = min_x;
+}
+
+static Lisp_Object
+haiku_note_drag_motion_1 (void *data)
+{
+ if (!NILP (Vhaiku_drag_track_function))
+ return call0 (Vhaiku_drag_track_function);
+
+ return Qnil;
+}
+
+static Lisp_Object
+haiku_note_drag_motion_2 (enum nonlocal_exit exit, Lisp_Object error)
+{
+ return Qnil;
+}
+
+void
+haiku_note_drag_motion (void)
+{
+ struct frame *tip_f;
+ int x, y;
+
+ if (FRAMEP (tip_frame) && haiku_dnd_follow_tooltip
+ && FIXNUMP (tip_dx) && FIXNUMP (tip_dy))
+ {
+ tip_f = XFRAME (tip_frame);
+
+ if (FRAME_LIVE_P (tip_f) && FRAME_VISIBLE_P (tip_f))
+ {
+ BView_get_mouse (FRAME_HAIKU_VIEW (haiku_dnd_frame),
+ &x, &y);
+ BView_convert_to_screen (FRAME_HAIKU_VIEW (haiku_dnd_frame),
+ &x, &y);
+
+ haiku_dnd_compute_tip_xy (&x, &y);
+ BWindow_set_offset (FRAME_HAIKU_WINDOW (tip_f), x, y);
+ }
+ }
+
+ internal_catch_all (haiku_note_drag_motion_1, NULL,
+ haiku_note_drag_motion_2);
+
+ /* Redisplay this way to preserve the echo area. Otherwise, the
+ contents will abruptly disappear when the mouse moves over a
+ frame. */
+ redisplay_preserve_echo_area (34);
+}
+
+void
+haiku_note_drag_wheel (struct input_event *ie)
+{
+ bool horizontal, up;
+
+ up = false;
+ horizontal = false;
+
+ if (ie->modifiers & up_modifier)
+ up = true;
+
+ if (ie->kind == HORIZ_WHEEL_EVENT)
+ horizontal = true;
+
+ ie->kind = NO_EVENT;
+
+ if (!NILP (Vhaiku_drag_wheel_function)
+ && (haiku_dnd_allow_same_frame
+ || XFRAME (ie->frame_or_window) != haiku_dnd_frame))
+ safe_call (7, Vhaiku_drag_wheel_function, ie->frame_or_window,
+ ie->x, ie->y, horizontal ? Qt : Qnil, up ? Qt : Qnil,
+ make_int (ie->modifiers));
+
+ redisplay_preserve_echo_area (35);
+}
+
+void
+init_haiku_select (void)
+{
+ be_clipboard_init ();
+}
+
+void
+haiku_handle_selection_clear (struct input_event *ie)
+{
+ enum haiku_clipboard id;
+
+ id = haiku_get_clipboard_name (ie->arg);
+
+ if (be_selection_outdated_p (id, ie->timestamp))
+ return;
+
+ CALLN (Frun_hook_with_args,
+ Qhaiku_lost_selection_functions, ie->arg);
+
+ /* This is required for redisplay to happen if something changed the
+ display inside the selection loss functions. */
+ redisplay_preserve_echo_area (20);
+}
+
+void
+haiku_selection_disowned (enum haiku_clipboard id, int64 count)
+{
+ struct input_event ie;
+
+ EVENT_INIT (ie);
+ ie.kind = SELECTION_CLEAR_EVENT;
+
+ switch (id)
+ {
+ case CLIPBOARD_CLIPBOARD:
+ ie.arg = QCLIPBOARD;
+ break;
+
+ case CLIPBOARD_PRIMARY:
+ ie.arg = QPRIMARY;
+ break;
+
+ case CLIPBOARD_SECONDARY:
+ ie.arg = QSECONDARY;
+ break;
+ }
+
+ ie.timestamp = count;
+ kbd_buffer_store_event (&ie);
+}
+
+void
+haiku_start_watching_selections (void)
+{
+ be_start_watching_selection (CLIPBOARD_CLIPBOARD);
+ be_start_watching_selection (CLIPBOARD_PRIMARY);
+ be_start_watching_selection (CLIPBOARD_SECONDARY);
+}
+
+void
+syms_of_haikuselect (void)
+{
+ DEFVAR_BOOL ("haiku-signal-invalid-refs", haiku_signal_invalid_refs,
+ doc: /* If nil, silently ignore invalid file names in system messages.
+Otherwise, an error will be signalled if adding a file reference to a
+system message failed. */);
+ haiku_signal_invalid_refs = true;
+
+ DEFVAR_LISP ("haiku-drag-track-function", Vhaiku_drag_track_function,
+ doc: /* If non-nil, a function to call upon mouse movement while dragging a message.
+The function is called without any arguments. `mouse-position' can be
+used to retrieve the current position of the mouse. */);
+ Vhaiku_drag_track_function = Qnil;
+
+ DEFVAR_LISP ("haiku-lost-selection-functions", Vhaiku_lost_selection_functions,
+ doc: /* A list of functions to be called when Emacs loses an X selection.
+These are only called if a connection to the Haiku display was opened. */);
+ Vhaiku_lost_selection_functions = Qnil;
+
+ DEFVAR_LISP ("haiku-drag-wheel-function", Vhaiku_drag_wheel_function,
+ doc: /* Function called upon wheel movement while dragging a message.
+If non-nil, it is called with 6 arguments when the mouse wheel moves
+while a drag-and-drop operation is in progress: the frame where the
+mouse moved, the frame-relative X and Y positions where the mouse
+moved, whether or not the wheel movement was horizontal, whether or
+not the wheel moved up (or left, if the movement was horizontal), and
+keyboard modifiers currently held down. */);
+ Vhaiku_drag_wheel_function = Qnil;
+
+ DEFSYM (QSECONDARY, "SECONDARY");
+ DEFSYM (QCLIPBOARD, "CLIPBOARD");
+ DEFSYM (QSTRING, "STRING");
+ DEFSYM (QUTF8_STRING, "UTF8_STRING");
+ DEFSYM (Qforeign_selection, "foreign-selection");
+ DEFSYM (QTARGETS, "TARGETS");
+
+ DEFSYM (Qhaiku_lost_selection_functions,
+ "haiku-lost-selection-functions");
+
+ DEFSYM (Qmessage, "message");
+ DEFSYM (Qstring, "string");
+ DEFSYM (Qref, "ref");
+ DEFSYM (Qshort, "short");
+ DEFSYM (Qlong, "long");
+ DEFSYM (Qllong, "llong");
+ DEFSYM (Qbyte, "byte");
+ DEFSYM (Qchar, "char");
+ DEFSYM (Qbool, "bool");
+ DEFSYM (Qtype, "type");
+ DEFSYM (Qsize_t, "size_t");
+ DEFSYM (Qssize_t, "ssize_t");
+ DEFSYM (Qpoint, "point");
+ DEFSYM (Qfloat, "float");
+ DEFSYM (Qdouble, "double");
+ DEFSYM (Qalready_running, "already-running");
+
+ defsubr (&Shaiku_selection_data);
+ defsubr (&Shaiku_selection_timestamp);
+ defsubr (&Shaiku_selection_put);
+ defsubr (&Shaiku_selection_owner_p);
+ defsubr (&Shaiku_drag_message);
+ defsubr (&Shaiku_roster_launch);
+ defsubr (&Shaiku_write_node_attribute);
+ defsubr (&Shaiku_send_message);
+
+ haiku_dnd_frame = NULL;
+}