[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] Changes to emacs/src/xselect.c [gnus-5_10-branch]
From: |
Miles Bader |
Subject: |
[Emacs-diffs] Changes to emacs/src/xselect.c [gnus-5_10-branch] |
Date: |
Sat, 04 Sep 2004 08:18:32 -0400 |
Index: emacs/src/xselect.c
diff -c /dev/null emacs/src/xselect.c:1.135.2.1
*** /dev/null Sat Sep 4 12:03:46 2004
--- emacs/src/xselect.c Sat Sep 4 12:01:20 2004
***************
*** 0 ****
--- 1,2775 ----
+ /* X Selection processing for Emacs.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2003, 2004
+ Free Software Foundation.
+
+ 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 2, 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+
+ /* Rewritten by jwz */
+
+ #include <config.h>
+ #include <stdio.h> /* termhooks.h needs this */
+ #include "lisp.h"
+ #include "xterm.h" /* for all of the X includes */
+ #include "dispextern.h" /* frame.h seems to want this */
+ #include "frame.h" /* Need this to get the X window of selected_frame */
+ #include "blockinput.h"
+ #include "buffer.h"
+ #include "process.h"
+ #include "termhooks.h"
+
+ #include <X11/Xproto.h>
+
+ struct prop_location;
+
+ static Lisp_Object x_atom_to_symbol P_ ((Display *dpy, Atom atom));
+ static Atom symbol_to_x_atom P_ ((struct x_display_info *, Display *,
+ Lisp_Object));
+ static void x_own_selection P_ ((Lisp_Object, Lisp_Object));
+ static Lisp_Object x_get_local_selection P_ ((Lisp_Object, Lisp_Object, int));
+ static void x_decline_selection_request P_ ((struct input_event *));
+ static Lisp_Object x_selection_request_lisp_error P_ ((Lisp_Object));
+ static Lisp_Object queue_selection_requests_unwind P_ ((Lisp_Object));
+ static Lisp_Object some_frame_on_display P_ ((struct x_display_info *));
+ static void x_reply_selection_request P_ ((struct input_event *, int,
+ unsigned char *, int, Atom));
+ static int waiting_for_other_props_on_window P_ ((Display *, Window));
+ static struct prop_location *expect_property_change P_ ((Display *, Window,
+ Atom, int));
+ static void unexpect_property_change P_ ((struct prop_location *));
+ static Lisp_Object wait_for_property_change_unwind P_ ((Lisp_Object));
+ static void wait_for_property_change P_ ((struct prop_location *));
+ static Lisp_Object x_get_foreign_selection P_ ((Lisp_Object,
+ Lisp_Object,
+ Lisp_Object));
+ static void x_get_window_property P_ ((Display *, Window, Atom,
+ unsigned char **, int *,
+ Atom *, int *, unsigned long *, int));
+ static void receive_incremental_selection P_ ((Display *, Window, Atom,
+ Lisp_Object, unsigned,
+ unsigned char **, int *,
+ Atom *, int *, unsigned long *));
+ static Lisp_Object x_get_window_property_as_lisp_data P_ ((Display *,
+ Window, Atom,
+ Lisp_Object, Atom));
+ static Lisp_Object selection_data_to_lisp_data P_ ((Display *, unsigned char
*,
+ int, Atom, int));
+ static void lisp_data_to_selection_data P_ ((Display *, Lisp_Object,
+ unsigned char **, Atom *,
+ unsigned *, int *, int *));
+ static Lisp_Object clean_local_selection_data P_ ((Lisp_Object));
+ static void initialize_cut_buffers P_ ((Display *, Window));
+
+
+ /* Printing traces to stderr. */
+
+ #ifdef TRACE_SELECTION
+ #define TRACE0(fmt) \
+ fprintf (stderr, "%d: " fmt "\n", getpid ())
+ #define TRACE1(fmt, a0) \
+ fprintf (stderr, "%d: " fmt "\n", getpid (), a0)
+ #define TRACE2(fmt, a0, a1) \
+ fprintf (stderr, "%d: " fmt "\n", getpid (), a0, a1)
+ #else
+ #define TRACE0(fmt) (void) 0
+ #define TRACE1(fmt, a0) (void) 0
+ #define TRACE2(fmt, a0, a1) (void) 0
+ #endif
+
+
+ #define CUT_BUFFER_SUPPORT
+
+ Lisp_Object QPRIMARY, QSECONDARY, QSTRING, QINTEGER, QCLIPBOARD, QTIMESTAMP,
+ QTEXT, QDELETE, QMULTIPLE, QINCR, QEMACS_TMP, QTARGETS, QATOM, QNULL,
+ QATOM_PAIR;
+
+ Lisp_Object QCOMPOUND_TEXT; /* This is a type of selection. */
+ Lisp_Object QUTF8_STRING; /* This is a type of selection. */
+
+ Lisp_Object Qcompound_text_with_extensions;
+
+ #ifdef CUT_BUFFER_SUPPORT
+ Lisp_Object QCUT_BUFFER0, QCUT_BUFFER1, QCUT_BUFFER2, QCUT_BUFFER3,
+ QCUT_BUFFER4, QCUT_BUFFER5, QCUT_BUFFER6, QCUT_BUFFER7;
+ #endif
+
+ static Lisp_Object Vx_lost_selection_hooks;
+ static Lisp_Object Vx_sent_selection_hooks;
+ /* Coding system for communicating with other X clients via cutbuffer,
+ selection, and clipboard. */
+ static Lisp_Object Vselection_coding_system;
+
+ /* Coding system for the next communicating with other X clients. */
+ static Lisp_Object Vnext_selection_coding_system;
+
+ static Lisp_Object Qforeign_selection;
+
+ /* If this is a smaller number than the max-request-size of the display,
+ emacs will use INCR selection transfer when the selection is larger
+ than this. The max-request-size is usually around 64k, so if you want
+ emacs to use incremental selection transfers when the selection is
+ smaller than that, set this. I added this mostly for debugging the
+ incremental transfer stuff, but it might improve server performance. */
+ #define MAX_SELECTION_QUANTUM 0xFFFFFF
+
+ #ifdef HAVE_X11R4
+ #define SELECTION_QUANTUM(dpy) ((XMaxRequestSize(dpy) << 2) - 100)
+ #else
+ #define SELECTION_QUANTUM(dpy) (((dpy)->max_request_size << 2) - 100)
+ #endif
+
+ /* The timestamp of the last input event Emacs received from the X server. */
+ /* Defined in keyboard.c. */
+ extern unsigned long last_event_timestamp;
+
+ /* This is an association list whose elements are of the form
+ ( SELECTION-NAME SELECTION-VALUE SELECTION-TIMESTAMP FRAME)
+ SELECTION-NAME is a lisp symbol, whose name is the name of an X Atom.
+ SELECTION-VALUE is the value that emacs owns for that selection.
+ It may be any kind of Lisp object.
+ SELECTION-TIMESTAMP is the time at which emacs began owning this selection,
+ as a cons of two 16-bit numbers (making a 32 bit time.)
+ FRAME is the frame for which we made the selection.
+ If there is an entry in this alist, then it can be assumed that Emacs owns
+ that selection.
+ The only (eq) parts of this list that are visible from Lisp are the
+ selection-values. */
+ static Lisp_Object Vselection_alist;
+
+ /* This is an alist whose CARs are selection-types (whose names are the same
+ as the names of X Atoms) and whose CDRs are the names of Lisp functions to
+ call to convert the given Emacs selection value to a string representing
+ the given selection type. This is for Lisp-level extension of the emacs
+ selection handling. */
+ static Lisp_Object Vselection_converter_alist;
+
+ /* If the selection owner takes too long to reply to a selection request,
+ we give up on it. This is in milliseconds (0 = no timeout.) */
+ static EMACS_INT x_selection_timeout;
+
+ /* Utility functions */
+
+ static void lisp_data_to_selection_data ();
+ static Lisp_Object selection_data_to_lisp_data ();
+ static Lisp_Object x_get_window_property_as_lisp_data ();
+
+ /* This converts a Lisp symbol to a server Atom, avoiding a server
+ roundtrip whenever possible. */
+
+ static Atom
+ symbol_to_x_atom (dpyinfo, display, sym)
+ struct x_display_info *dpyinfo;
+ Display *display;
+ Lisp_Object sym;
+ {
+ Atom val;
+ if (NILP (sym)) return 0;
+ if (EQ (sym, QPRIMARY)) return XA_PRIMARY;
+ if (EQ (sym, QSECONDARY)) return XA_SECONDARY;
+ if (EQ (sym, QSTRING)) return XA_STRING;
+ if (EQ (sym, QINTEGER)) return XA_INTEGER;
+ if (EQ (sym, QATOM)) return XA_ATOM;
+ if (EQ (sym, QCLIPBOARD)) return dpyinfo->Xatom_CLIPBOARD;
+ if (EQ (sym, QTIMESTAMP)) return dpyinfo->Xatom_TIMESTAMP;
+ if (EQ (sym, QTEXT)) return dpyinfo->Xatom_TEXT;
+ if (EQ (sym, QCOMPOUND_TEXT)) return dpyinfo->Xatom_COMPOUND_TEXT;
+ if (EQ (sym, QUTF8_STRING)) return dpyinfo->Xatom_UTF8_STRING;
+ if (EQ (sym, QDELETE)) return dpyinfo->Xatom_DELETE;
+ if (EQ (sym, QMULTIPLE)) return dpyinfo->Xatom_MULTIPLE;
+ if (EQ (sym, QINCR)) return dpyinfo->Xatom_INCR;
+ if (EQ (sym, QEMACS_TMP)) return dpyinfo->Xatom_EMACS_TMP;
+ if (EQ (sym, QTARGETS)) return dpyinfo->Xatom_TARGETS;
+ if (EQ (sym, QNULL)) return dpyinfo->Xatom_NULL;
+ #ifdef CUT_BUFFER_SUPPORT
+ if (EQ (sym, QCUT_BUFFER0)) return XA_CUT_BUFFER0;
+ if (EQ (sym, QCUT_BUFFER1)) return XA_CUT_BUFFER1;
+ if (EQ (sym, QCUT_BUFFER2)) return XA_CUT_BUFFER2;
+ if (EQ (sym, QCUT_BUFFER3)) return XA_CUT_BUFFER3;
+ if (EQ (sym, QCUT_BUFFER4)) return XA_CUT_BUFFER4;
+ if (EQ (sym, QCUT_BUFFER5)) return XA_CUT_BUFFER5;
+ if (EQ (sym, QCUT_BUFFER6)) return XA_CUT_BUFFER6;
+ if (EQ (sym, QCUT_BUFFER7)) return XA_CUT_BUFFER7;
+ #endif
+ if (!SYMBOLP (sym)) abort ();
+
+ TRACE1 (" XInternAtom %s", (char *) SDATA (SYMBOL_NAME (sym)));
+ BLOCK_INPUT;
+ val = XInternAtom (display, (char *) SDATA (SYMBOL_NAME (sym)), False);
+ UNBLOCK_INPUT;
+ return val;
+ }
+
+
+ /* This converts a server Atom to a Lisp symbol, avoiding server roundtrips
+ and calls to intern whenever possible. */
+
+ static Lisp_Object
+ x_atom_to_symbol (dpy, atom)
+ Display *dpy;
+ Atom atom;
+ {
+ struct x_display_info *dpyinfo;
+ char *str;
+ Lisp_Object val;
+
+ if (! atom)
+ return Qnil;
+
+ switch (atom)
+ {
+ case XA_PRIMARY:
+ return QPRIMARY;
+ case XA_SECONDARY:
+ return QSECONDARY;
+ case XA_STRING:
+ return QSTRING;
+ case XA_INTEGER:
+ return QINTEGER;
+ case XA_ATOM:
+ return QATOM;
+ #ifdef CUT_BUFFER_SUPPORT
+ case XA_CUT_BUFFER0:
+ return QCUT_BUFFER0;
+ case XA_CUT_BUFFER1:
+ return QCUT_BUFFER1;
+ case XA_CUT_BUFFER2:
+ return QCUT_BUFFER2;
+ case XA_CUT_BUFFER3:
+ return QCUT_BUFFER3;
+ case XA_CUT_BUFFER4:
+ return QCUT_BUFFER4;
+ case XA_CUT_BUFFER5:
+ return QCUT_BUFFER5;
+ case XA_CUT_BUFFER6:
+ return QCUT_BUFFER6;
+ case XA_CUT_BUFFER7:
+ return QCUT_BUFFER7;
+ #endif
+ }
+
+ dpyinfo = x_display_info_for_display (dpy);
+ if (atom == dpyinfo->Xatom_CLIPBOARD)
+ return QCLIPBOARD;
+ if (atom == dpyinfo->Xatom_TIMESTAMP)
+ return QTIMESTAMP;
+ if (atom == dpyinfo->Xatom_TEXT)
+ return QTEXT;
+ if (atom == dpyinfo->Xatom_COMPOUND_TEXT)
+ return QCOMPOUND_TEXT;
+ if (atom == dpyinfo->Xatom_UTF8_STRING)
+ return QUTF8_STRING;
+ if (atom == dpyinfo->Xatom_DELETE)
+ return QDELETE;
+ if (atom == dpyinfo->Xatom_MULTIPLE)
+ return QMULTIPLE;
+ if (atom == dpyinfo->Xatom_INCR)
+ return QINCR;
+ if (atom == dpyinfo->Xatom_EMACS_TMP)
+ return QEMACS_TMP;
+ if (atom == dpyinfo->Xatom_TARGETS)
+ return QTARGETS;
+ if (atom == dpyinfo->Xatom_NULL)
+ return QNULL;
+
+ BLOCK_INPUT;
+ str = XGetAtomName (dpy, atom);
+ UNBLOCK_INPUT;
+ TRACE1 ("XGetAtomName --> %s", str);
+ if (! str) return Qnil;
+ val = intern (str);
+ BLOCK_INPUT;
+ /* This was allocated by Xlib, so use XFree. */
+ XFree (str);
+ UNBLOCK_INPUT;
+ return val;
+ }
+
+ /* Do protocol to assert ourself as a selection owner.
+ Update the Vselection_alist so that we can reply to later requests for
+ our selection. */
+
+ static void
+ x_own_selection (selection_name, selection_value)
+ Lisp_Object selection_name, selection_value;
+ {
+ struct frame *sf = SELECTED_FRAME ();
+ Window selecting_window = FRAME_X_WINDOW (sf);
+ Display *display = FRAME_X_DISPLAY (sf);
+ Time time = last_event_timestamp;
+ Atom selection_atom;
+ struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (sf);
+ int count;
+
+ CHECK_SYMBOL (selection_name);
+ selection_atom = symbol_to_x_atom (dpyinfo, display, selection_name);
+
+ BLOCK_INPUT;
+ count = x_catch_errors (display);
+ XSetSelectionOwner (display, selection_atom, selecting_window, time);
+ x_check_errors (display, "Can't set selection: %s");
+ x_uncatch_errors (display, count);
+ UNBLOCK_INPUT;
+
+ /* Now update the local cache */
+ {
+ Lisp_Object selection_time;
+ Lisp_Object selection_data;
+ Lisp_Object prev_value;
+
+ selection_time = long_to_cons ((unsigned long) time);
+ selection_data = Fcons (selection_name,
+ Fcons (selection_value,
+ Fcons (selection_time,
+ Fcons (selected_frame, Qnil))));
+ prev_value = assq_no_quit (selection_name, Vselection_alist);
+
+ Vselection_alist = Fcons (selection_data, Vselection_alist);
+
+ /* If we already owned the selection, remove the old selection data.
+ Perhaps we should destructively modify it instead.
+ Don't use Fdelq as that may QUIT. */
+ if (!NILP (prev_value))
+ {
+ Lisp_Object rest; /* we know it's not the CAR, so it's easy. */
+ for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
+ if (EQ (prev_value, Fcar (XCDR (rest))))
+ {
+ XSETCDR (rest, Fcdr (XCDR (rest)));
+ break;
+ }
+ }
+ }
+ }
+
+ /* Given a selection-name and desired type, look up our local copy of
+ the selection value and convert it to the type.
+ The value is nil or a string.
+ This function is used both for remote requests (LOCAL_REQUEST is zero)
+ and for local x-get-selection-internal (LOCAL_REQUEST is nonzero).
+
+ This calls random Lisp code, and may signal or gc. */
+
+ static Lisp_Object
+ x_get_local_selection (selection_symbol, target_type, local_request)
+ Lisp_Object selection_symbol, target_type;
+ int local_request;
+ {
+ Lisp_Object local_value;
+ Lisp_Object handler_fn, value, type, check;
+ int count;
+
+ local_value = assq_no_quit (selection_symbol, Vselection_alist);
+
+ if (NILP (local_value)) return Qnil;
+
+ /* TIMESTAMP and MULTIPLE are special cases 'cause that's easiest. */
+ if (EQ (target_type, QTIMESTAMP))
+ {
+ handler_fn = Qnil;
+ value = XCAR (XCDR (XCDR (local_value)));
+ }
+ #if 0
+ else if (EQ (target_type, QDELETE))
+ {
+ handler_fn = Qnil;
+ Fx_disown_selection_internal
+ (selection_symbol,
+ XCAR (XCDR (XCDR (local_value))));
+ value = QNULL;
+ }
+ #endif
+
+ #if 0 /* #### MULTIPLE doesn't work yet */
+ else if (CONSP (target_type)
+ && XCAR (target_type) == QMULTIPLE)
+ {
+ Lisp_Object pairs;
+ int size;
+ int i;
+ pairs = XCDR (target_type);
+ size = XVECTOR (pairs)->size;
+ /* If the target is MULTIPLE, then target_type looks like
+ (MULTIPLE . [[SELECTION1 TARGET1] [SELECTION2 TARGET2] ... ])
+ We modify the second element of each pair in the vector and
+ return it as [[SELECTION1 <value1>] [SELECTION2 <value2>] ... ]
+ */
+ for (i = 0; i < size; i++)
+ {
+ Lisp_Object pair;
+ pair = XVECTOR (pairs)->contents [i];
+ XVECTOR (pair)->contents [1]
+ = x_get_local_selection (XVECTOR (pair)->contents [0],
+ XVECTOR (pair)->contents [1],
+ local_request);
+ }
+ return pairs;
+ }
+ #endif
+ else
+ {
+ /* Don't allow a quit within the converter.
+ When the user types C-g, he would be surprised
+ if by luck it came during a converter. */
+ count = SPECPDL_INDEX ();
+ specbind (Qinhibit_quit, Qt);
+
+ CHECK_SYMBOL (target_type);
+ handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
+ /* gcpro is not needed here since nothing but HANDLER_FN
+ is live, and that ought to be a symbol. */
+
+ if (!NILP (handler_fn))
+ value = call3 (handler_fn,
+ selection_symbol, (local_request ? Qnil : target_type),
+ XCAR (XCDR (local_value)));
+ else
+ value = Qnil;
+ unbind_to (count, Qnil);
+ }
+
+ /* Make sure this value is of a type that we could transmit
+ to another X client. */
+
+ check = value;
+ if (CONSP (value)
+ && SYMBOLP (XCAR (value)))
+ type = XCAR (value),
+ check = XCDR (value);
+
+ if (STRINGP (check)
+ || VECTORP (check)
+ || SYMBOLP (check)
+ || INTEGERP (check)
+ || NILP (value))
+ return value;
+ /* Check for a value that cons_to_long could handle. */
+ else if (CONSP (check)
+ && INTEGERP (XCAR (check))
+ && (INTEGERP (XCDR (check))
+ ||
+ (CONSP (XCDR (check))
+ && INTEGERP (XCAR (XCDR (check)))
+ && NILP (XCDR (XCDR (check))))))
+ return value;
+ else
+ return
+ Fsignal (Qerror,
+ Fcons (build_string ("invalid data returned by
selection-conversion function"),
+ Fcons (handler_fn, Fcons (value, Qnil))));
+ }
+
+ /* Subroutines of x_reply_selection_request. */
+
+ /* Send a SelectionNotify event to the requestor with property=None,
+ meaning we were unable to do what they wanted. */
+
+ static void
+ x_decline_selection_request (event)
+ struct input_event *event;
+ {
+ XSelectionEvent reply;
+ int count;
+
+ reply.type = SelectionNotify;
+ reply.display = SELECTION_EVENT_DISPLAY (event);
+ reply.requestor = SELECTION_EVENT_REQUESTOR (event);
+ reply.selection = SELECTION_EVENT_SELECTION (event);
+ reply.time = SELECTION_EVENT_TIME (event);
+ reply.target = SELECTION_EVENT_TARGET (event);
+ reply.property = None;
+
+ /* The reason for the error may be that the receiver has
+ died in the meantime. Handle that case. */
+ BLOCK_INPUT;
+ count = x_catch_errors (reply.display);
+ XSendEvent (reply.display, reply.requestor, False, 0L, (XEvent *) &reply);
+ XFlush (reply.display);
+ x_uncatch_errors (reply.display, count);
+ UNBLOCK_INPUT;
+ }
+
+ /* This is the selection request currently being processed.
+ It is set to zero when the request is fully processed. */
+ static struct input_event *x_selection_current_request;
+
+ /* Display info in x_selection_request. */
+
+ static struct x_display_info *selection_request_dpyinfo;
+
+ /* Used as an unwind-protect clause so that, if a selection-converter signals
+ an error, we tell the requester that we were unable to do what they wanted
+ before we throw to top-level or go into the debugger or whatever. */
+
+ static Lisp_Object
+ x_selection_request_lisp_error (ignore)
+ Lisp_Object ignore;
+ {
+ if (x_selection_current_request != 0
+ && selection_request_dpyinfo->display)
+ x_decline_selection_request (x_selection_current_request);
+ return Qnil;
+ }
+
+
+ /* This stuff is so that INCR selections are reentrant (that is, so we can
+ be servicing multiple INCR selection requests simultaneously.) I haven't
+ actually tested that yet. */
+
+ /* Keep a list of the property changes that are awaited. */
+
+ struct prop_location
+ {
+ int identifier;
+ Display *display;
+ Window window;
+ Atom property;
+ int desired_state;
+ int arrived;
+ struct prop_location *next;
+ };
+
+ static struct prop_location *expect_property_change ();
+ static void wait_for_property_change ();
+ static void unexpect_property_change ();
+ static int waiting_for_other_props_on_window ();
+
+ static int prop_location_identifier;
+
+ static Lisp_Object property_change_reply;
+
+ static struct prop_location *property_change_reply_object;
+
+ static struct prop_location *property_change_wait_list;
+
+ static Lisp_Object
+ queue_selection_requests_unwind (frame)
+ Lisp_Object frame;
+ {
+ FRAME_PTR f = XFRAME (frame);
+
+ if (! NILP (frame))
+ x_stop_queuing_selection_requests (FRAME_X_DISPLAY (f));
+ return Qnil;
+ }
+
+ /* Return some frame whose display info is DPYINFO.
+ Return nil if there is none. */
+
+ static Lisp_Object
+ some_frame_on_display (dpyinfo)
+ struct x_display_info *dpyinfo;
+ {
+ Lisp_Object list, frame;
+
+ FOR_EACH_FRAME (list, frame)
+ {
+ if (FRAME_X_DISPLAY_INFO (XFRAME (frame)) == dpyinfo)
+ return frame;
+ }
+
+ return Qnil;
+ }
+
+ /* Send the reply to a selection request event EVENT.
+ TYPE is the type of selection data requested.
+ DATA and SIZE describe the data to send, already converted.
+ FORMAT is the unit-size (in bits) of the data to be transmitted. */
+
+ static void
+ x_reply_selection_request (event, format, data, size, type)
+ struct input_event *event;
+ int format, size;
+ unsigned char *data;
+ Atom type;
+ {
+ XSelectionEvent reply;
+ Display *display = SELECTION_EVENT_DISPLAY (event);
+ Window window = SELECTION_EVENT_REQUESTOR (event);
+ int bytes_remaining;
+ int format_bytes = format/8;
+ int max_bytes = SELECTION_QUANTUM (display);
+ struct x_display_info *dpyinfo = x_display_info_for_display (display);
+ int count;
+
+ if (max_bytes > MAX_SELECTION_QUANTUM)
+ max_bytes = MAX_SELECTION_QUANTUM;
+
+ reply.type = SelectionNotify;
+ reply.display = display;
+ reply.requestor = window;
+ reply.selection = SELECTION_EVENT_SELECTION (event);
+ reply.time = SELECTION_EVENT_TIME (event);
+ reply.target = SELECTION_EVENT_TARGET (event);
+ reply.property = SELECTION_EVENT_PROPERTY (event);
+ if (reply.property == None)
+ reply.property = reply.target;
+
+ /* #### XChangeProperty can generate BadAlloc, and we must handle it! */
+ BLOCK_INPUT;
+ count = x_catch_errors (display);
+
+ /* Store the data on the requested property.
+ If the selection is large, only store the first N bytes of it.
+ */
+ bytes_remaining = size * format_bytes;
+ if (bytes_remaining <= max_bytes)
+ {
+ /* Send all the data at once, with minimal handshaking. */
+ TRACE1 ("Sending all %d bytes", bytes_remaining);
+ XChangeProperty (display, window, reply.property, type, format,
+ PropModeReplace, data, size);
+ /* At this point, the selection was successfully stored; ack it. */
+ XSendEvent (display, window, False, 0L, (XEvent *) &reply);
+ }
+ else
+ {
+ /* Send an INCR selection. */
+ struct prop_location *wait_object;
+ int had_errors;
+ Lisp_Object frame;
+
+ frame = some_frame_on_display (dpyinfo);
+
+ /* If the display no longer has frames, we can't expect
+ to get many more selection requests from it, so don't
+ bother trying to queue them. */
+ if (!NILP (frame))
+ {
+ x_start_queuing_selection_requests (display);
+
+ record_unwind_protect (queue_selection_requests_unwind,
+ frame);
+ }
+
+ if (x_window_to_frame (dpyinfo, window)) /* #### debug */
+ error ("Attempt to transfer an INCR to ourself!");
+
+ TRACE2 ("Start sending %d bytes incrementally (%s)",
+ bytes_remaining, XGetAtomName (display, reply.property));
+ wait_object = expect_property_change (display, window, reply.property,
+ PropertyDelete);
+
+ TRACE1 ("Set %s to number of bytes to send",
+ XGetAtomName (display, reply.property));
+ XChangeProperty (display, window, reply.property, dpyinfo->Xatom_INCR,
+ 32, PropModeReplace,
+ (unsigned char *) &bytes_remaining, 1);
+ XSelectInput (display, window, PropertyChangeMask);
+
+ /* Tell 'em the INCR data is there... */
+ TRACE0 ("Send SelectionNotify event");
+ XSendEvent (display, window, False, 0L, (XEvent *) &reply);
+ XFlush (display);
+
+ had_errors = x_had_errors_p (display);
+ UNBLOCK_INPUT;
+
+ /* First, wait for the requester to ack by deleting the property.
+ This can run random lisp code (process handlers) or signal. */
+ if (! had_errors)
+ {
+ TRACE1 ("Waiting for ACK (deletion of %s)",
+ XGetAtomName (display, reply.property));
+ wait_for_property_change (wait_object);
+ }
+
+ TRACE0 ("Got ACK");
+ while (bytes_remaining)
+ {
+ int i = ((bytes_remaining < max_bytes)
+ ? bytes_remaining
+ : max_bytes);
+
+ BLOCK_INPUT;
+
+ wait_object
+ = expect_property_change (display, window, reply.property,
+ PropertyDelete);
+
+ TRACE1 ("Sending increment of %d bytes", i);
+ TRACE1 ("Set %s to increment data",
+ XGetAtomName (display, reply.property));
+
+ /* Append the next chunk of data to the property. */
+ XChangeProperty (display, window, reply.property, type, format,
+ PropModeAppend, data, i / format_bytes);
+ bytes_remaining -= i;
+ data += i;
+ XFlush (display);
+ had_errors = x_had_errors_p (display);
+ UNBLOCK_INPUT;
+
+ if (had_errors)
+ break;
+
+ /* Now wait for the requester to ack this chunk by deleting the
+ property. This can run random lisp code or signal. */
+ TRACE1 ("Waiting for increment ACK (deletion of %s)",
+ XGetAtomName (display, reply.property));
+ wait_for_property_change (wait_object);
+ }
+
+ /* Now write a zero-length chunk to the property to tell the
+ requester that we're done. */
+ BLOCK_INPUT;
+ if (! waiting_for_other_props_on_window (display, window))
+ XSelectInput (display, window, 0L);
+
+ TRACE1 ("Set %s to a 0-length chunk to indicate EOF",
+ XGetAtomName (display, reply.property));
+ XChangeProperty (display, window, reply.property, type, format,
+ PropModeReplace, data, 0);
+ TRACE0 ("Done sending incrementally");
+ }
+
+ /* rms, 2003-01-03: I think I have fixed this bug. */
+ /* The window we're communicating with may have been deleted
+ in the meantime (that's a real situation from a bug report).
+ In this case, there may be events in the event queue still
+ refering to the deleted window, and we'll get a BadWindow error
+ in XTread_socket when processing the events. I don't have
+ an idea how to fix that. gerd, 2001-01-98. */
+ XFlush (display);
+ x_uncatch_errors (display, count);
+ UNBLOCK_INPUT;
+ }
+
+ /* Handle a SelectionRequest event EVENT.
+ This is called from keyboard.c when such an event is found in the queue.
*/
+
+ void
+ x_handle_selection_request (event)
+ struct input_event *event;
+ {
+ struct gcpro gcpro1, gcpro2, gcpro3;
+ Lisp_Object local_selection_data;
+ Lisp_Object selection_symbol;
+ Lisp_Object target_symbol;
+ Lisp_Object converted_selection;
+ Time local_selection_time;
+ Lisp_Object successful_p;
+ int count;
+ struct x_display_info *dpyinfo
+ = x_display_info_for_display (SELECTION_EVENT_DISPLAY (event));
+
+ local_selection_data = Qnil;
+ target_symbol = Qnil;
+ converted_selection = Qnil;
+ successful_p = Qnil;
+
+ GCPRO3 (local_selection_data, converted_selection, target_symbol);
+
+ selection_symbol = x_atom_to_symbol (SELECTION_EVENT_DISPLAY (event),
+ SELECTION_EVENT_SELECTION (event));
+
+ local_selection_data = assq_no_quit (selection_symbol, Vselection_alist);
+
+ if (NILP (local_selection_data))
+ {
+ /* Someone asked for the selection, but we don't have it any more.
+ */
+ x_decline_selection_request (event);
+ goto DONE;
+ }
+
+ local_selection_time = (Time)
+ cons_to_long (XCAR (XCDR (XCDR (local_selection_data))));
+
+ if (SELECTION_EVENT_TIME (event) != CurrentTime
+ && local_selection_time > SELECTION_EVENT_TIME (event))
+ {
+ /* Someone asked for the selection, and we have one, but not the one
+ they're looking for.
+ */
+ x_decline_selection_request (event);
+ goto DONE;
+ }
+
+ x_selection_current_request = event;
+ count = SPECPDL_INDEX ();
+ selection_request_dpyinfo = dpyinfo;
+ record_unwind_protect (x_selection_request_lisp_error, Qnil);
+
+ target_symbol = x_atom_to_symbol (SELECTION_EVENT_DISPLAY (event),
+ SELECTION_EVENT_TARGET (event));
+
+ #if 0 /* #### MULTIPLE doesn't work yet */
+ if (EQ (target_symbol, QMULTIPLE))
+ target_symbol = fetch_multiple_target (event);
+ #endif
+
+ /* Convert lisp objects back into binary data */
+
+ converted_selection
+ = x_get_local_selection (selection_symbol, target_symbol, 0);
+
+ if (! NILP (converted_selection))
+ {
+ unsigned char *data;
+ unsigned int size;
+ int format;
+ Atom type;
+ int nofree;
+
+ lisp_data_to_selection_data (SELECTION_EVENT_DISPLAY (event),
+ converted_selection,
+ &data, &type, &size, &format, &nofree);
+
+ x_reply_selection_request (event, format, data, size, type);
+ successful_p = Qt;
+
+ /* Indicate we have successfully processed this event. */
+ x_selection_current_request = 0;
+
+ /* Use xfree, not XFree, because lisp_data_to_selection_data
+ calls xmalloc itself. */
+ if (!nofree)
+ xfree (data);
+ }
+ unbind_to (count, Qnil);
+
+ DONE:
+
+ /* Let random lisp code notice that the selection has been asked for. */
+ {
+ Lisp_Object rest;
+ rest = Vx_sent_selection_hooks;
+ if (!EQ (rest, Qunbound))
+ for (; CONSP (rest); rest = Fcdr (rest))
+ call3 (Fcar (rest), selection_symbol, target_symbol, successful_p);
+ }
+
+ UNGCPRO;
+ }
+
+ /* Handle a SelectionClear event EVENT, which indicates that some
+ client cleared out our previously asserted selection.
+ This is called from keyboard.c when such an event is found in the queue.
*/
+
+ void
+ x_handle_selection_clear (event)
+ struct input_event *event;
+ {
+ Display *display = SELECTION_EVENT_DISPLAY (event);
+ Atom selection = SELECTION_EVENT_SELECTION (event);
+ Time changed_owner_time = SELECTION_EVENT_TIME (event);
+
+ Lisp_Object selection_symbol, local_selection_data;
+ Time local_selection_time;
+ struct x_display_info *dpyinfo = x_display_info_for_display (display);
+ struct x_display_info *t_dpyinfo;
+
+ /* If the new selection owner is also Emacs,
+ don't clear the new selection. */
+ BLOCK_INPUT;
+ /* Check each display on the same terminal,
+ to see if this Emacs job now owns the selection
+ through that display. */
+ for (t_dpyinfo = x_display_list; t_dpyinfo; t_dpyinfo = t_dpyinfo->next)
+ if (t_dpyinfo->kboard == dpyinfo->kboard)
+ {
+ Window owner_window
+ = XGetSelectionOwner (t_dpyinfo->display, selection);
+ if (x_window_to_frame (t_dpyinfo, owner_window) != 0)
+ {
+ UNBLOCK_INPUT;
+ return;
+ }
+ }
+ UNBLOCK_INPUT;
+
+ selection_symbol = x_atom_to_symbol (display, selection);
+
+ local_selection_data = assq_no_quit (selection_symbol, Vselection_alist);
+
+ /* Well, we already believe that we don't own it, so that's just fine. */
+ if (NILP (local_selection_data)) return;
+
+ local_selection_time = (Time)
+ cons_to_long (XCAR (XCDR (XCDR (local_selection_data))));
+
+ /* This SelectionClear is for a selection that we no longer own, so we can
+ disregard it. (That is, we have reasserted the selection since this
+ request was generated.) */
+
+ if (changed_owner_time != CurrentTime
+ && local_selection_time > changed_owner_time)
+ return;
+
+ /* Otherwise, we're really honest and truly being told to drop it.
+ Don't use Fdelq as that may QUIT;. */
+
+ if (EQ (local_selection_data, Fcar (Vselection_alist)))
+ Vselection_alist = Fcdr (Vselection_alist);
+ else
+ {
+ Lisp_Object rest;
+ for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
+ if (EQ (local_selection_data, Fcar (XCDR (rest))))
+ {
+ XSETCDR (rest, Fcdr (XCDR (rest)));
+ break;
+ }
+ }
+
+ /* Let random lisp code notice that the selection has been stolen. */
+
+ {
+ Lisp_Object rest;
+ rest = Vx_lost_selection_hooks;
+ if (!EQ (rest, Qunbound))
+ {
+ for (; CONSP (rest); rest = Fcdr (rest))
+ call1 (Fcar (rest), selection_symbol);
+ prepare_menu_bars ();
+ redisplay_preserve_echo_area (20);
+ }
+ }
+ }
+
+ /* Clear all selections that were made from frame F.
+ We do this when about to delete a frame. */
+
+ void
+ x_clear_frame_selections (f)
+ FRAME_PTR f;
+ {
+ Lisp_Object frame;
+ Lisp_Object rest;
+
+ XSETFRAME (frame, f);
+
+ /* Otherwise, we're really honest and truly being told to drop it.
+ Don't use Fdelq as that may QUIT;. */
+
+ /* Delete elements from the beginning of Vselection_alist. */
+ while (!NILP (Vselection_alist)
+ && EQ (frame, Fcar (Fcdr (Fcdr (Fcdr (Fcar (Vselection_alist)))))))
+ {
+ /* Let random Lisp code notice that the selection has been stolen. */
+ Lisp_Object hooks, selection_symbol;
+
+ hooks = Vx_lost_selection_hooks;
+ selection_symbol = Fcar (Fcar (Vselection_alist));
+
+ if (!EQ (hooks, Qunbound))
+ {
+ for (; CONSP (hooks); hooks = Fcdr (hooks))
+ call1 (Fcar (hooks), selection_symbol);
+ #if 0 /* This can crash when deleting a frame
+ from x_connection_closed. Anyway, it seems unnecessary;
+ something else should cause a redisplay. */
+ redisplay_preserve_echo_area (21);
+ #endif
+ }
+
+ Vselection_alist = Fcdr (Vselection_alist);
+ }
+
+ /* Delete elements after the beginning of Vselection_alist. */
+ for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
+ if (EQ (frame, Fcar (Fcdr (Fcdr (Fcdr (Fcar (XCDR (rest))))))))
+ {
+ /* Let random Lisp code notice that the selection has been stolen. */
+ Lisp_Object hooks, selection_symbol;
+
+ hooks = Vx_lost_selection_hooks;
+ selection_symbol = Fcar (Fcar (XCDR (rest)));
+
+ if (!EQ (hooks, Qunbound))
+ {
+ for (; CONSP (hooks); hooks = Fcdr (hooks))
+ call1 (Fcar (hooks), selection_symbol);
+ #if 0 /* See above */
+ redisplay_preserve_echo_area (22);
+ #endif
+ }
+ XSETCDR (rest, Fcdr (XCDR (rest)));
+ break;
+ }
+ }
+
+ /* Nonzero if any properties for DISPLAY and WINDOW
+ are on the list of what we are waiting for. */
+
+ static int
+ waiting_for_other_props_on_window (display, window)
+ Display *display;
+ Window window;
+ {
+ struct prop_location *rest = property_change_wait_list;
+ while (rest)
+ if (rest->display == display && rest->window == window)
+ return 1;
+ else
+ rest = rest->next;
+ return 0;
+ }
+
+ /* Add an entry to the list of property changes we are waiting for.
+ DISPLAY, WINDOW, PROPERTY, STATE describe what we will wait for.
+ The return value is a number that uniquely identifies
+ this awaited property change. */
+
+ static struct prop_location *
+ expect_property_change (display, window, property, state)
+ Display *display;
+ Window window;
+ Atom property;
+ int state;
+ {
+ struct prop_location *pl = (struct prop_location *) xmalloc (sizeof *pl);
+ pl->identifier = ++prop_location_identifier;
+ pl->display = display;
+ pl->window = window;
+ pl->property = property;
+ pl->desired_state = state;
+ pl->next = property_change_wait_list;
+ pl->arrived = 0;
+ property_change_wait_list = pl;
+ return pl;
+ }
+
+ /* Delete an entry from the list of property changes we are waiting for.
+ IDENTIFIER is the number that uniquely identifies the entry. */
+
+ static void
+ unexpect_property_change (location)
+ struct prop_location *location;
+ {
+ struct prop_location *prev = 0, *rest = property_change_wait_list;
+ while (rest)
+ {
+ if (rest == location)
+ {
+ if (prev)
+ prev->next = rest->next;
+ else
+ property_change_wait_list = rest->next;
+ xfree (rest);
+ return;
+ }
+ prev = rest;
+ rest = rest->next;
+ }
+ }
+
+ /* Remove the property change expectation element for IDENTIFIER. */
+
+ static Lisp_Object
+ wait_for_property_change_unwind (identifierval)
+ Lisp_Object identifierval;
+ {
+ unexpect_property_change ((struct prop_location *)
+ (XFASTINT (XCAR (identifierval)) << 16
+ | XFASTINT (XCDR (identifierval))));
+ return Qnil;
+ }
+
+ /* Actually wait for a property change.
+ IDENTIFIER should be the value that expect_property_change returned. */
+
+ static void
+ wait_for_property_change (location)
+ struct prop_location *location;
+ {
+ int secs, usecs;
+ int count = SPECPDL_INDEX ();
+ Lisp_Object tem;
+
+ tem = Fcons (Qnil, Qnil);
+ XSETCARFASTINT (tem, (EMACS_UINT)location >> 16);
+ XSETCDRFASTINT (tem, (EMACS_UINT)location & 0xffff);
+
+ /* Make sure to do unexpect_property_change if we quit or err. */
+ record_unwind_protect (wait_for_property_change_unwind, tem);
+
+ XSETCAR (property_change_reply, Qnil);
+
+ property_change_reply_object = location;
+ /* If the event we are waiting for arrives beyond here, it will set
+ property_change_reply, because property_change_reply_object says so. */
+ if (! location->arrived)
+ {
+ secs = x_selection_timeout / 1000;
+ usecs = (x_selection_timeout % 1000) * 1000;
+ TRACE2 (" Waiting %d secs, %d usecs", secs, usecs);
+ wait_reading_process_output (secs, usecs, 0, 0,
+ property_change_reply, NULL, 0);
+
+ if (NILP (XCAR (property_change_reply)))
+ {
+ TRACE0 (" Timed out");
+ error ("Timed out waiting for property-notify event");
+ }
+ }
+
+ unbind_to (count, Qnil);
+ }
+
+ /* Called from XTread_socket in response to a PropertyNotify event. */
+
+ void
+ x_handle_property_notify (event)
+ XPropertyEvent *event;
+ {
+ struct prop_location *prev = 0, *rest = property_change_wait_list;
+
+ while (rest)
+ {
+ if (rest->property == event->atom
+ && rest->window == event->window
+ && rest->display == event->display
+ && rest->desired_state == event->state)
+ {
+ TRACE2 ("Expected %s of property %s",
+ (event->state == PropertyDelete ? "deletion" : "change"),
+ XGetAtomName (event->display, event->atom));
+
+ rest->arrived = 1;
+
+ /* If this is the one wait_for_property_change is waiting for,
+ tell it to wake up. */
+ if (rest == property_change_reply_object)
+ XSETCAR (property_change_reply, Qt);
+
+ if (prev)
+ prev->next = rest->next;
+ else
+ property_change_wait_list = rest->next;
+ xfree (rest);
+ return;
+ }
+
+ prev = rest;
+ rest = rest->next;
+ }
+ }
+
+
+
+ #if 0 /* #### MULTIPLE doesn't work yet */
+
+ static Lisp_Object
+ fetch_multiple_target (event)
+ XSelectionRequestEvent *event;
+ {
+ Display *display = event->display;
+ Window window = event->requestor;
+ Atom target = event->target;
+ Atom selection_atom = event->selection;
+ int result;
+
+ return
+ Fcons (QMULTIPLE,
+ x_get_window_property_as_lisp_data (display, window, target,
+ QMULTIPLE, selection_atom));
+ }
+
+ static Lisp_Object
+ copy_multiple_data (obj)
+ Lisp_Object obj;
+ {
+ Lisp_Object vec;
+ int i;
+ int size;
+ if (CONSP (obj))
+ return Fcons (XCAR (obj), copy_multiple_data (XCDR (obj)));
+
+ CHECK_VECTOR (obj);
+ vec = Fmake_vector (size = XVECTOR (obj)->size, Qnil);
+ for (i = 0; i < size; i++)
+ {
+ Lisp_Object vec2 = XVECTOR (obj)->contents [i];
+ CHECK_VECTOR (vec2);
+ if (XVECTOR (vec2)->size != 2)
+ /* ??? Confusing error message */
+ Fsignal (Qerror, Fcons (build_string ("vectors must be of length 2"),
+ Fcons (vec2, Qnil)));
+ XVECTOR (vec)->contents [i] = Fmake_vector (2, Qnil);
+ XVECTOR (XVECTOR (vec)->contents [i])->contents [0]
+ = XVECTOR (vec2)->contents [0];
+ XVECTOR (XVECTOR (vec)->contents [i])->contents [1]
+ = XVECTOR (vec2)->contents [1];
+ }
+ return vec;
+ }
+
+ #endif
+
+
+ /* Variables for communication with x_handle_selection_notify. */
+ static Atom reading_which_selection;
+ static Lisp_Object reading_selection_reply;
+ static Window reading_selection_window;
+
+ /* Do protocol to read selection-data from the server.
+ Converts this to Lisp data and returns it. */
+
+ static Lisp_Object
+ x_get_foreign_selection (selection_symbol, target_type, time_stamp)
+ Lisp_Object selection_symbol, target_type, time_stamp;
+ {
+ struct frame *sf = SELECTED_FRAME ();
+ Window requestor_window = FRAME_X_WINDOW (sf);
+ Display *display = FRAME_X_DISPLAY (sf);
+ struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (sf);
+ Time requestor_time = last_event_timestamp;
+ Atom target_property = dpyinfo->Xatom_EMACS_TMP;
+ Atom selection_atom = symbol_to_x_atom (dpyinfo, display, selection_symbol);
+ Atom type_atom;
+ int secs, usecs;
+ int count;
+ Lisp_Object frame;
+
+ if (CONSP (target_type))
+ type_atom = symbol_to_x_atom (dpyinfo, display, XCAR (target_type));
+ else
+ type_atom = symbol_to_x_atom (dpyinfo, display, target_type);
+
+ if (! NILP (time_stamp))
+ {
+ if (CONSP (time_stamp))
+ requestor_time = (Time) cons_to_long (time_stamp);
+ else if (INTEGERP (time_stamp))
+ requestor_time = (Time) XUINT (time_stamp);
+ else if (FLOATP (time_stamp))
+ requestor_time = (Time) XFLOAT (time_stamp);
+ else
+ error ("TIME_STAMP must be cons or number");
+ }
+
+ BLOCK_INPUT;
+
+ count = x_catch_errors (display);
+
+ TRACE2 ("Get selection %s, type %s",
+ XGetAtomName (display, type_atom),
+ XGetAtomName (display, target_property));
+
+ XConvertSelection (display, selection_atom, type_atom, target_property,
+ requestor_window, requestor_time);
+ XFlush (display);
+
+ /* Prepare to block until the reply has been read. */
+ reading_selection_window = requestor_window;
+ reading_which_selection = selection_atom;
+ XSETCAR (reading_selection_reply, Qnil);
+
+ frame = some_frame_on_display (dpyinfo);
+
+ /* If the display no longer has frames, we can't expect
+ to get many more selection requests from it, so don't
+ bother trying to queue them. */
+ if (!NILP (frame))
+ {
+ x_start_queuing_selection_requests (display);
+
+ record_unwind_protect (queue_selection_requests_unwind,
+ frame);
+ }
+ UNBLOCK_INPUT;
+
+ /* This allows quits. Also, don't wait forever. */
+ secs = x_selection_timeout / 1000;
+ usecs = (x_selection_timeout % 1000) * 1000;
+ TRACE1 (" Start waiting %d secs for SelectionNotify", secs);
+ wait_reading_process_output (secs, usecs, 0, 0,
+ reading_selection_reply, NULL, 0);
+ TRACE1 (" Got event = %d", !NILP (XCAR (reading_selection_reply)));
+
+ BLOCK_INPUT;
+ x_check_errors (display, "Cannot get selection: %s");
+ x_uncatch_errors (display, count);
+ UNBLOCK_INPUT;
+
+ if (NILP (XCAR (reading_selection_reply)))
+ error ("Timed out waiting for reply from selection owner");
+ if (EQ (XCAR (reading_selection_reply), Qlambda))
+ error ("No `%s' selection", SDATA (SYMBOL_NAME (selection_symbol)));
+
+ /* Otherwise, the selection is waiting for us on the requested property. */
+ return
+ x_get_window_property_as_lisp_data (display, requestor_window,
+ target_property, target_type,
+ selection_atom);
+ }
+
+ /* Subroutines of x_get_window_property_as_lisp_data */
+
+ /* Use xfree, not XFree, to free the data obtained with this function. */
+
+ static void
+ x_get_window_property (display, window, property, data_ret, bytes_ret,
+ actual_type_ret, actual_format_ret, actual_size_ret,
+ delete_p)
+ Display *display;
+ Window window;
+ Atom property;
+ unsigned char **data_ret;
+ int *bytes_ret;
+ Atom *actual_type_ret;
+ int *actual_format_ret;
+ unsigned long *actual_size_ret;
+ int delete_p;
+ {
+ int total_size;
+ unsigned long bytes_remaining;
+ int offset = 0;
+ unsigned char *tmp_data = 0;
+ int result;
+ int buffer_size = SELECTION_QUANTUM (display);
+
+ if (buffer_size > MAX_SELECTION_QUANTUM)
+ buffer_size = MAX_SELECTION_QUANTUM;
+
+ BLOCK_INPUT;
+
+ /* First probe the thing to find out how big it is. */
+ result = XGetWindowProperty (display, window, property,
+ 0L, 0L, False, AnyPropertyType,
+ actual_type_ret, actual_format_ret,
+ actual_size_ret,
+ &bytes_remaining, &tmp_data);
+ if (result != Success)
+ {
+ UNBLOCK_INPUT;
+ *data_ret = 0;
+ *bytes_ret = 0;
+ return;
+ }
+
+ /* This was allocated by Xlib, so use XFree. */
+ XFree ((char *) tmp_data);
+
+ if (*actual_type_ret == None || *actual_format_ret == 0)
+ {
+ UNBLOCK_INPUT;
+ return;
+ }
+
+ total_size = bytes_remaining + 1;
+ *data_ret = (unsigned char *) xmalloc (total_size);
+
+ /* Now read, until we've gotten it all. */
+ while (bytes_remaining)
+ {
+ #ifdef TRACE_SELECTION
+ int last = bytes_remaining;
+ #endif
+ result
+ = XGetWindowProperty (display, window, property,
+ (long)offset/4, (long)buffer_size/4,
+ False,
+ AnyPropertyType,
+ actual_type_ret, actual_format_ret,
+ actual_size_ret, &bytes_remaining, &tmp_data);
+
+ TRACE2 ("Read %ld bytes from property %s",
+ last - bytes_remaining,
+ XGetAtomName (display, property));
+
+ /* If this doesn't return Success at this point, it means that
+ some clod deleted the selection while we were in the midst of
+ reading it. Deal with that, I guess.... */
+ if (result != Success)
+ break;
+ *actual_size_ret *= *actual_format_ret / 8;
+ bcopy (tmp_data, (*data_ret) + offset, *actual_size_ret);
+ offset += *actual_size_ret;
+
+ /* This was allocated by Xlib, so use XFree. */
+ XFree ((char *) tmp_data);
+ }
+
+ XFlush (display);
+ UNBLOCK_INPUT;
+ *bytes_ret = offset;
+ }
+
+ /* Use xfree, not XFree, to free the data obtained with this function. */
+
+ static void
+ receive_incremental_selection (display, window, property, target_type,
+ min_size_bytes, data_ret, size_bytes_ret,
+ type_ret, format_ret, size_ret)
+ Display *display;
+ Window window;
+ Atom property;
+ Lisp_Object target_type; /* for error messages only */
+ unsigned int min_size_bytes;
+ unsigned char **data_ret;
+ int *size_bytes_ret;
+ Atom *type_ret;
+ unsigned long *size_ret;
+ int *format_ret;
+ {
+ int offset = 0;
+ struct prop_location *wait_object;
+ *size_bytes_ret = min_size_bytes;
+ *data_ret = (unsigned char *) xmalloc (*size_bytes_ret);
+
+ TRACE1 ("Read %d bytes incrementally", min_size_bytes);
+
+ /* At this point, we have read an INCR property.
+ Delete the property to ack it.
+ (But first, prepare to receive the next event in this handshake.)
+
+ Now, we must loop, waiting for the sending window to put a value on
+ that property, then reading the property, then deleting it to ack.
+ We are done when the sender places a property of length 0.
+ */
+ BLOCK_INPUT;
+ XSelectInput (display, window, STANDARD_EVENT_SET | PropertyChangeMask);
+ TRACE1 (" Delete property %s",
+ XSYMBOL (x_atom_to_symbol (display, property))->name->data);
+ XDeleteProperty (display, window, property);
+ TRACE1 (" Expect new value of property %s",
+ XSYMBOL (x_atom_to_symbol (display, property))->name->data);
+ wait_object = expect_property_change (display, window, property,
+ PropertyNewValue);
+ XFlush (display);
+ UNBLOCK_INPUT;
+
+ while (1)
+ {
+ unsigned char *tmp_data;
+ int tmp_size_bytes;
+
+ TRACE0 (" Wait for property change");
+ wait_for_property_change (wait_object);
+
+ /* expect it again immediately, because x_get_window_property may
+ .. no it won't, I don't get it.
+ .. Ok, I get it now, the Xt code that implements INCR is broken. */
+ TRACE0 (" Get property value");
+ x_get_window_property (display, window, property,
+ &tmp_data, &tmp_size_bytes,
+ type_ret, format_ret, size_ret, 1);
+
+ TRACE1 (" Read increment of %d bytes", tmp_size_bytes);
+
+ if (tmp_size_bytes == 0) /* we're done */
+ {
+ TRACE0 ("Done reading incrementally");
+
+ if (! waiting_for_other_props_on_window (display, window))
+ XSelectInput (display, window, STANDARD_EVENT_SET);
+ unexpect_property_change (wait_object);
+ /* Use xfree, not XFree, because x_get_window_property
+ calls xmalloc itself. */
+ if (tmp_data) xfree (tmp_data);
+ break;
+ }
+
+ BLOCK_INPUT;
+ TRACE1 (" ACK by deleting property %s",
+ XGetAtomName (display, property));
+ XDeleteProperty (display, window, property);
+ wait_object = expect_property_change (display, window, property,
+ PropertyNewValue);
+ XFlush (display);
+ UNBLOCK_INPUT;
+
+ if (*size_bytes_ret < offset + tmp_size_bytes)
+ {
+ *size_bytes_ret = offset + tmp_size_bytes;
+ *data_ret = (unsigned char *) xrealloc (*data_ret, *size_bytes_ret);
+ }
+
+ bcopy (tmp_data, (*data_ret) + offset, tmp_size_bytes);
+ offset += tmp_size_bytes;
+
+ /* Use xfree, not XFree, because x_get_window_property
+ calls xmalloc itself. */
+ xfree (tmp_data);
+ }
+ }
+
+
+ /* Once a requested selection is "ready" (we got a SelectionNotify event),
+ fetch value from property PROPERTY of X window WINDOW on display DISPLAY.
+ TARGET_TYPE and SELECTION_ATOM are used in error message if this fails. */
+
+ static Lisp_Object
+ x_get_window_property_as_lisp_data (display, window, property, target_type,
+ selection_atom)
+ Display *display;
+ Window window;
+ Atom property;
+ Lisp_Object target_type; /* for error messages only */
+ Atom selection_atom; /* for error messages only */
+ {
+ Atom actual_type;
+ int actual_format;
+ unsigned long actual_size;
+ unsigned char *data = 0;
+ int bytes = 0;
+ Lisp_Object val;
+ struct x_display_info *dpyinfo = x_display_info_for_display (display);
+
+ TRACE0 ("Reading selection data");
+
+ x_get_window_property (display, window, property, &data, &bytes,
+ &actual_type, &actual_format, &actual_size, 1);
+ if (! data)
+ {
+ int there_is_a_selection_owner;
+ BLOCK_INPUT;
+ there_is_a_selection_owner
+ = XGetSelectionOwner (display, selection_atom);
+ UNBLOCK_INPUT;
+ Fsignal (Qerror,
+ there_is_a_selection_owner
+ ? Fcons (build_string ("selection owner couldn't convert"),
+ actual_type
+ ? Fcons (target_type,
+ Fcons (x_atom_to_symbol (display,
+ actual_type),
+ Qnil))
+ : Fcons (target_type, Qnil))
+ : Fcons (build_string ("no selection"),
+ Fcons (x_atom_to_symbol (display,
+ selection_atom),
+ Qnil)));
+ }
+
+ if (actual_type == dpyinfo->Xatom_INCR)
+ {
+ /* That wasn't really the data, just the beginning. */
+
+ unsigned int min_size_bytes = * ((unsigned int *) data);
+ BLOCK_INPUT;
+ /* Use xfree, not XFree, because x_get_window_property
+ calls xmalloc itself. */
+ xfree ((char *) data);
+ UNBLOCK_INPUT;
+ receive_incremental_selection (display, window, property, target_type,
+ min_size_bytes, &data, &bytes,
+ &actual_type, &actual_format,
+ &actual_size);
+ }
+
+ BLOCK_INPUT;
+ TRACE1 (" Delete property %s", XGetAtomName (display, property));
+ XDeleteProperty (display, window, property);
+ XFlush (display);
+ UNBLOCK_INPUT;
+
+ /* It's been read. Now convert it to a lisp object in some semi-rational
+ manner. */
+ val = selection_data_to_lisp_data (display, data, bytes,
+ actual_type, actual_format);
+
+ /* Use xfree, not XFree, because x_get_window_property
+ calls xmalloc itself. */
+ xfree ((char *) data);
+ return val;
+ }
+
+ /* These functions convert from the selection data read from the server into
+ something that we can use from Lisp, and vice versa.
+
+ Type: Format: Size: Lisp Type:
+ ----- ------- ----- -----------
+ * 8 * String
+ ATOM 32 1 Symbol
+ ATOM 32 > 1 Vector of Symbols
+ * 16 1 Integer
+ * 16 > 1 Vector of Integers
+ * 32 1 if <=16 bits: Integer
+ if > 16 bits: Cons of top16, bot16
+ * 32 > 1 Vector of the above
+
+ When converting a Lisp number to C, it is assumed to be of format 16 if
+ it is an integer, and of format 32 if it is a cons of two integers.
+
+ When converting a vector of numbers from Lisp to C, it is assumed to be
+ of format 16 if every element in the vector is an integer, and is assumed
+ to be of format 32 if any element is a cons of two integers.
+
+ When converting an object to C, it may be of the form (SYMBOL . <data>)
+ where SYMBOL is what we should claim that the type is. Format and
+ representation are as above. */
+
+
+
+ static Lisp_Object
+ selection_data_to_lisp_data (display, data, size, type, format)
+ Display *display;
+ unsigned char *data;
+ Atom type;
+ int size, format;
+ {
+ struct x_display_info *dpyinfo = x_display_info_for_display (display);
+
+ if (type == dpyinfo->Xatom_NULL)
+ return QNULL;
+
+ /* Convert any 8-bit data to a string, for compactness. */
+ else if (format == 8)
+ {
+ Lisp_Object str, lispy_type;
+
+ str = make_unibyte_string ((char *) data, size);
+ /* Indicate that this string is from foreign selection by a text
+ property `foreign-selection' so that the caller of
+ x-get-selection-internal (usually x-get-selection) can know
+ that the string must be decode. */
+ if (type == dpyinfo->Xatom_COMPOUND_TEXT)
+ lispy_type = QCOMPOUND_TEXT;
+ else if (type == dpyinfo->Xatom_UTF8_STRING)
+ lispy_type = QUTF8_STRING;
+ else
+ lispy_type = QSTRING;
+ Fput_text_property (make_number (0), make_number (size),
+ Qforeign_selection, lispy_type, str);
+ return str;
+ }
+ /* Convert a single atom to a Lisp_Symbol. Convert a set of atoms to
+ a vector of symbols.
+ */
+ else if (type == XA_ATOM)
+ {
+ int i;
+ if (size == sizeof (Atom))
+ return x_atom_to_symbol (display, *((Atom *) data));
+ else
+ {
+ Lisp_Object v = Fmake_vector (make_number (size / sizeof (Atom)),
+ make_number (0));
+ for (i = 0; i < size / sizeof (Atom); i++)
+ Faset (v, make_number (i),
+ x_atom_to_symbol (display, ((Atom *) data) [i]));
+ return v;
+ }
+ }
+
+ /* Convert a single 16 or small 32 bit number to a Lisp_Int.
+ If the number is > 16 bits, convert it to a cons of integers,
+ 16 bits in each half.
+ */
+ else if (format == 32 && size == sizeof (int))
+ return long_to_cons (((unsigned int *) data) [0]);
+ else if (format == 16 && size == sizeof (short))
+ return make_number ((int) (((unsigned short *) data) [0]));
+
+ /* Convert any other kind of data to a vector of numbers, represented
+ as above (as an integer, or a cons of two 16 bit integers.)
+ */
+ else if (format == 16)
+ {
+ int i;
+ Lisp_Object v;
+ v = Fmake_vector (make_number (size / 2), make_number (0));
+ for (i = 0; i < size / 2; i++)
+ {
+ int j = (int) ((unsigned short *) data) [i];
+ Faset (v, make_number (i), make_number (j));
+ }
+ return v;
+ }
+ else
+ {
+ int i;
+ Lisp_Object v = Fmake_vector (make_number (size / 4), make_number (0));
+ for (i = 0; i < size / 4; i++)
+ {
+ unsigned int j = ((unsigned int *) data) [i];
+ Faset (v, make_number (i), long_to_cons (j));
+ }
+ return v;
+ }
+ }
+
+
+ /* Use xfree, not XFree, to free the data obtained with this function. */
+
+ static void
+ lisp_data_to_selection_data (display, obj,
+ data_ret, type_ret, size_ret,
+ format_ret, nofree_ret)
+ Display *display;
+ Lisp_Object obj;
+ unsigned char **data_ret;
+ Atom *type_ret;
+ unsigned int *size_ret;
+ int *format_ret;
+ int *nofree_ret;
+ {
+ Lisp_Object type = Qnil;
+ struct x_display_info *dpyinfo = x_display_info_for_display (display);
+
+ *nofree_ret = 0;
+
+ if (CONSP (obj) && SYMBOLP (XCAR (obj)))
+ {
+ type = XCAR (obj);
+ obj = XCDR (obj);
+ if (CONSP (obj) && NILP (XCDR (obj)))
+ obj = XCAR (obj);
+ }
+
+ if (EQ (obj, QNULL) || (EQ (type, QNULL)))
+ { /* This is not the same as declining */
+ *format_ret = 32;
+ *size_ret = 0;
+ *data_ret = 0;
+ type = QNULL;
+ }
+ else if (STRINGP (obj))
+ {
+ xassert (! STRING_MULTIBYTE (obj));
+ if (NILP (type))
+ type = QSTRING;
+ *format_ret = 8;
+ *size_ret = SBYTES (obj);
+ *data_ret = SDATA (obj);
+ *nofree_ret = 1;
+ }
+ else if (SYMBOLP (obj))
+ {
+ *format_ret = 32;
+ *size_ret = 1;
+ *data_ret = (unsigned char *) xmalloc (sizeof (Atom) + 1);
+ (*data_ret) [sizeof (Atom)] = 0;
+ (*(Atom **) data_ret) [0] = symbol_to_x_atom (dpyinfo, display, obj);
+ if (NILP (type)) type = QATOM;
+ }
+ else if (INTEGERP (obj)
+ && XINT (obj) < 0xFFFF
+ && XINT (obj) > -0xFFFF)
+ {
+ *format_ret = 16;
+ *size_ret = 1;
+ *data_ret = (unsigned char *) xmalloc (sizeof (short) + 1);
+ (*data_ret) [sizeof (short)] = 0;
+ (*(short **) data_ret) [0] = (short) XINT (obj);
+ if (NILP (type)) type = QINTEGER;
+ }
+ else if (INTEGERP (obj)
+ || (CONSP (obj) && INTEGERP (XCAR (obj))
+ && (INTEGERP (XCDR (obj))
+ || (CONSP (XCDR (obj))
+ && INTEGERP (XCAR (XCDR (obj)))))))
+ {
+ *format_ret = 32;
+ *size_ret = 1;
+ *data_ret = (unsigned char *) xmalloc (sizeof (long) + 1);
+ (*data_ret) [sizeof (long)] = 0;
+ (*(unsigned long **) data_ret) [0] = cons_to_long (obj);
+ if (NILP (type)) type = QINTEGER;
+ }
+ else if (VECTORP (obj))
+ {
+ /* Lisp_Vectors may represent a set of ATOMs;
+ a set of 16 or 32 bit INTEGERs;
+ or a set of ATOM_PAIRs (represented as [[A1 A2] [A3 A4] ...]
+ */
+ int i;
+
+ if (SYMBOLP (XVECTOR (obj)->contents [0]))
+ /* This vector is an ATOM set */
+ {
+ if (NILP (type)) type = QATOM;
+ *size_ret = XVECTOR (obj)->size;
+ *format_ret = 32;
+ *data_ret = (unsigned char *) xmalloc ((*size_ret) * sizeof (Atom));
+ for (i = 0; i < *size_ret; i++)
+ if (SYMBOLP (XVECTOR (obj)->contents [i]))
+ (*(Atom **) data_ret) [i]
+ = symbol_to_x_atom (dpyinfo, display, XVECTOR (obj)->contents
[i]);
+ else
+ Fsignal (Qerror, /* Qselection_error */
+ Fcons (build_string
+ ("all elements of selection vector must have same type"),
+ Fcons (obj, Qnil)));
+ }
+ #if 0 /* #### MULTIPLE doesn't work yet */
+ else if (VECTORP (XVECTOR (obj)->contents [0]))
+ /* This vector is an ATOM_PAIR set */
+ {
+ if (NILP (type)) type = QATOM_PAIR;
+ *size_ret = XVECTOR (obj)->size;
+ *format_ret = 32;
+ *data_ret = (unsigned char *)
+ xmalloc ((*size_ret) * sizeof (Atom) * 2);
+ for (i = 0; i < *size_ret; i++)
+ if (VECTORP (XVECTOR (obj)->contents [i]))
+ {
+ Lisp_Object pair = XVECTOR (obj)->contents [i];
+ if (XVECTOR (pair)->size != 2)
+ Fsignal (Qerror,
+ Fcons (build_string
+ ("elements of the vector must be vectors of exactly two elements"),
+ Fcons (pair, Qnil)));
+
+ (*(Atom **) data_ret) [i * 2]
+ = symbol_to_x_atom (dpyinfo, display,
+ XVECTOR (pair)->contents [0]);
+ (*(Atom **) data_ret) [(i * 2) + 1]
+ = symbol_to_x_atom (dpyinfo, display,
+ XVECTOR (pair)->contents [1]);
+ }
+ else
+ Fsignal (Qerror,
+ Fcons (build_string
+ ("all elements of the vector must be of the same type"),
+ Fcons (obj, Qnil)));
+
+ }
+ #endif
+ else
+ /* This vector is an INTEGER set, or something like it */
+ {
+ *size_ret = XVECTOR (obj)->size;
+ if (NILP (type)) type = QINTEGER;
+ *format_ret = 16;
+ for (i = 0; i < *size_ret; i++)
+ if (CONSP (XVECTOR (obj)->contents [i]))
+ *format_ret = 32;
+ else if (!INTEGERP (XVECTOR (obj)->contents [i]))
+ Fsignal (Qerror, /* Qselection_error */
+ Fcons (build_string
+ ("elements of selection vector must be integers or conses of integers"),
+ Fcons (obj, Qnil)));
+
+ *data_ret = (unsigned char *) xmalloc (*size_ret * (*format_ret/8));
+ for (i = 0; i < *size_ret; i++)
+ if (*format_ret == 32)
+ (*((unsigned long **) data_ret)) [i]
+ = cons_to_long (XVECTOR (obj)->contents [i]);
+ else
+ (*((unsigned short **) data_ret)) [i]
+ = (unsigned short) cons_to_long (XVECTOR (obj)->contents [i]);
+ }
+ }
+ else
+ Fsignal (Qerror, /* Qselection_error */
+ Fcons (build_string ("unrecognised selection data"),
+ Fcons (obj, Qnil)));
+
+ *type_ret = symbol_to_x_atom (dpyinfo, display, type);
+ }
+
+ static Lisp_Object
+ clean_local_selection_data (obj)
+ Lisp_Object obj;
+ {
+ if (CONSP (obj)
+ && INTEGERP (XCAR (obj))
+ && CONSP (XCDR (obj))
+ && INTEGERP (XCAR (XCDR (obj)))
+ && NILP (XCDR (XCDR (obj))))
+ obj = Fcons (XCAR (obj), XCDR (obj));
+
+ if (CONSP (obj)
+ && INTEGERP (XCAR (obj))
+ && INTEGERP (XCDR (obj)))
+ {
+ if (XINT (XCAR (obj)) == 0)
+ return XCDR (obj);
+ if (XINT (XCAR (obj)) == -1)
+ return make_number (- XINT (XCDR (obj)));
+ }
+ if (VECTORP (obj))
+ {
+ int i;
+ int size = XVECTOR (obj)->size;
+ Lisp_Object copy;
+ if (size == 1)
+ return clean_local_selection_data (XVECTOR (obj)->contents [0]);
+ copy = Fmake_vector (make_number (size), Qnil);
+ for (i = 0; i < size; i++)
+ XVECTOR (copy)->contents [i]
+ = clean_local_selection_data (XVECTOR (obj)->contents [i]);
+ return copy;
+ }
+ return obj;
+ }
+
+ /* Called from XTread_socket to handle SelectionNotify events.
+ If it's the selection we are waiting for, stop waiting
+ by setting the car of reading_selection_reply to non-nil.
+ We store t there if the reply is successful, lambda if not. */
+
+ void
+ x_handle_selection_notify (event)
+ XSelectionEvent *event;
+ {
+ if (event->requestor != reading_selection_window)
+ return;
+ if (event->selection != reading_which_selection)
+ return;
+
+ TRACE0 ("Received SelectionNotify");
+ XSETCAR (reading_selection_reply,
+ (event->property != 0 ? Qt : Qlambda));
+ }
+
+
+ DEFUN ("x-own-selection-internal", Fx_own_selection_internal,
+ Sx_own_selection_internal, 2, 2, 0,
+ doc: /* Assert an X selection of the given TYPE with the given VALUE.
+ TYPE is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+ \(Those are literal upper-case symbol names, since that's what X expects.)
+ VALUE is typically a string, or a cons of two markers, but may be
+ anything that the functions on `selection-converter-alist' know about. */)
+ (selection_name, selection_value)
+ Lisp_Object selection_name, selection_value;
+ {
+ check_x ();
+ CHECK_SYMBOL (selection_name);
+ if (NILP (selection_value)) error ("selection-value may not be nil");
+ x_own_selection (selection_name, selection_value);
+ return selection_value;
+ }
+
+
+ /* Request the selection value from the owner. If we are the owner,
+ simply return our selection value. If we are not the owner, this
+ will block until all of the data has arrived. */
+
+ DEFUN ("x-get-selection-internal", Fx_get_selection_internal,
+ Sx_get_selection_internal, 2, 3, 0,
+ doc: /* Return text selected from some X window.
+ SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+ \(Those are literal upper-case symbol names, since that's what X expects.)
+ TYPE is the type of data desired, typically `STRING'.
+ TIME_STAMP is the time to use in the XConvertSelection call for foreign
+ selections. If omitted, defaults to the time for the last event. */)
+ (selection_symbol, target_type, time_stamp)
+ Lisp_Object selection_symbol, target_type, time_stamp;
+ {
+ Lisp_Object val = Qnil;
+ struct gcpro gcpro1, gcpro2;
+ GCPRO2 (target_type, val); /* we store newly consed data into these */
+ check_x ();
+ CHECK_SYMBOL (selection_symbol);
+
+ #if 0 /* #### MULTIPLE doesn't work yet */
+ if (CONSP (target_type)
+ && XCAR (target_type) == QMULTIPLE)
+ {
+ CHECK_VECTOR (XCDR (target_type));
+ /* So we don't destructively modify this... */
+ target_type = copy_multiple_data (target_type);
+ }
+ else
+ #endif
+ CHECK_SYMBOL (target_type);
+
+ val = x_get_local_selection (selection_symbol, target_type, 1);
+
+ if (NILP (val))
+ {
+ val = x_get_foreign_selection (selection_symbol, target_type,
time_stamp);
+ goto DONE;
+ }
+
+ if (CONSP (val)
+ && SYMBOLP (XCAR (val)))
+ {
+ val = XCDR (val);
+ if (CONSP (val) && NILP (XCDR (val)))
+ val = XCAR (val);
+ }
+ val = clean_local_selection_data (val);
+ DONE:
+ UNGCPRO;
+ return val;
+ }
+
+ DEFUN ("x-disown-selection-internal", Fx_disown_selection_internal,
+ Sx_disown_selection_internal, 1, 2, 0,
+ doc: /* If we own the selection SELECTION, disown it.
+ Disowning it means there is no such selection. */)
+ (selection, time)
+ Lisp_Object selection;
+ Lisp_Object time;
+ {
+ Time timestamp;
+ Atom selection_atom;
+ struct selection_input_event event;
+ Display *display;
+ struct x_display_info *dpyinfo;
+ struct frame *sf = SELECTED_FRAME ();
+
+ check_x ();
+ display = FRAME_X_DISPLAY (sf);
+ dpyinfo = FRAME_X_DISPLAY_INFO (sf);
+ CHECK_SYMBOL (selection);
+ if (NILP (time))
+ timestamp = last_event_timestamp;
+ else
+ timestamp = cons_to_long (time);
+
+ if (NILP (assq_no_quit (selection, Vselection_alist)))
+ return Qnil; /* Don't disown the selection when we're not the owner. */
+
+ selection_atom = symbol_to_x_atom (dpyinfo, display, selection);
+
+ BLOCK_INPUT;
+ XSetSelectionOwner (display, selection_atom, None, timestamp);
+ UNBLOCK_INPUT;
+
+ /* It doesn't seem to be guaranteed that a SelectionClear event will be
+ generated for a window which owns the selection when that window sets
+ the selection owner to None. The NCD server does, the MIT Sun4 server
+ doesn't. So we synthesize one; this means we might get two, but
+ that's ok, because the second one won't have any effect. */
+ SELECTION_EVENT_DISPLAY (&event) = display;
+ SELECTION_EVENT_SELECTION (&event) = selection_atom;
+ SELECTION_EVENT_TIME (&event) = timestamp;
+ x_handle_selection_clear ((struct input_event *) &event);
+
+ return Qt;
+ }
+
+ /* Get rid of all the selections in buffer BUFFER.
+ This is used when we kill a buffer. */
+
+ void
+ x_disown_buffer_selections (buffer)
+ Lisp_Object buffer;
+ {
+ Lisp_Object tail;
+ struct buffer *buf = XBUFFER (buffer);
+
+ for (tail = Vselection_alist; CONSP (tail); tail = XCDR (tail))
+ {
+ Lisp_Object elt, value;
+ elt = XCAR (tail);
+ value = XCDR (elt);
+ if (CONSP (value) && MARKERP (XCAR (value))
+ && XMARKER (XCAR (value))->buffer == buf)
+ Fx_disown_selection_internal (XCAR (elt), Qnil);
+ }
+ }
+
+ DEFUN ("x-selection-owner-p", Fx_selection_owner_p, Sx_selection_owner_p,
+ 0, 1, 0,
+ doc: /* Whether the current Emacs process owns the given X Selection.
+ The arg should be the name of the selection in question, typically one of
+ the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+ \(Those are literal upper-case symbol names, since that's what X expects.)
+ For convenience, the symbol nil is the same as `PRIMARY',
+ and t is the same as `SECONDARY'. */)
+ (selection)
+ Lisp_Object selection;
+ {
+ check_x ();
+ CHECK_SYMBOL (selection);
+ if (EQ (selection, Qnil)) selection = QPRIMARY;
+ if (EQ (selection, Qt)) selection = QSECONDARY;
+
+ if (NILP (Fassq (selection, Vselection_alist)))
+ return Qnil;
+ return Qt;
+ }
+
+ DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
+ 0, 1, 0,
+ doc: /* Whether there is an owner for the given X Selection.
+ The arg should be the name of the selection in question, typically one of
+ the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+ \(Those are literal upper-case symbol names, since that's what X expects.)
+ For convenience, the symbol nil is the same as `PRIMARY',
+ and t is the same as `SECONDARY'. */)
+ (selection)
+ Lisp_Object selection;
+ {
+ Window owner;
+ Atom atom;
+ Display *dpy;
+ struct frame *sf = SELECTED_FRAME ();
+
+ /* It should be safe to call this before we have an X frame. */
+ if (! FRAME_X_P (sf))
+ return Qnil;
+
+ dpy = FRAME_X_DISPLAY (sf);
+ CHECK_SYMBOL (selection);
+ if (!NILP (Fx_selection_owner_p (selection)))
+ return Qt;
+ if (EQ (selection, Qnil)) selection = QPRIMARY;
+ if (EQ (selection, Qt)) selection = QSECONDARY;
+ atom = symbol_to_x_atom (FRAME_X_DISPLAY_INFO (sf), dpy, selection);
+ if (atom == 0)
+ return Qnil;
+ BLOCK_INPUT;
+ owner = XGetSelectionOwner (dpy, atom);
+ UNBLOCK_INPUT;
+ return (owner ? Qt : Qnil);
+ }
+
+
+ #ifdef CUT_BUFFER_SUPPORT
+
+ /* Ensure that all 8 cut buffers exist. ICCCM says we gotta... */
+ static void
+ initialize_cut_buffers (display, window)
+ Display *display;
+ Window window;
+ {
+ unsigned char *data = (unsigned char *) "";
+ BLOCK_INPUT;
+ #define FROB(atom) XChangeProperty (display, window, atom, XA_STRING, 8, \
+ PropModeAppend, data, 0)
+ FROB (XA_CUT_BUFFER0);
+ FROB (XA_CUT_BUFFER1);
+ FROB (XA_CUT_BUFFER2);
+ FROB (XA_CUT_BUFFER3);
+ FROB (XA_CUT_BUFFER4);
+ FROB (XA_CUT_BUFFER5);
+ FROB (XA_CUT_BUFFER6);
+ FROB (XA_CUT_BUFFER7);
+ #undef FROB
+ UNBLOCK_INPUT;
+ }
+
+
+ #define CHECK_CUT_BUFFER(symbol) \
+ { CHECK_SYMBOL ((symbol)); \
+ if (!EQ((symbol), QCUT_BUFFER0) && !EQ((symbol), QCUT_BUFFER1) \
+ && !EQ((symbol), QCUT_BUFFER2) && !EQ((symbol), QCUT_BUFFER3) \
+ && !EQ((symbol), QCUT_BUFFER4) && !EQ((symbol), QCUT_BUFFER5) \
+ && !EQ((symbol), QCUT_BUFFER6) && !EQ((symbol), QCUT_BUFFER7)) \
+ Fsignal (Qerror,
\
+ Fcons (build_string ("doesn't name a cut buffer"), \
+ Fcons ((symbol), Qnil))); \
+ }
+
+ DEFUN ("x-get-cut-buffer-internal", Fx_get_cut_buffer_internal,
+ Sx_get_cut_buffer_internal, 1, 1, 0,
+ doc: /* Returns the value of the named cut buffer (typically
CUT_BUFFER0). */)
+ (buffer)
+ Lisp_Object buffer;
+ {
+ Window window;
+ Atom buffer_atom;
+ unsigned char *data;
+ int bytes;
+ Atom type;
+ int format;
+ unsigned long size;
+ Lisp_Object ret;
+ Display *display;
+ struct x_display_info *dpyinfo;
+ struct frame *sf = SELECTED_FRAME ();
+
+ check_x ();
+ display = FRAME_X_DISPLAY (sf);
+ dpyinfo = FRAME_X_DISPLAY_INFO (sf);
+ window = RootWindow (display, 0); /* Cut buffers are on screen 0 */
+ CHECK_CUT_BUFFER (buffer);
+ buffer_atom = symbol_to_x_atom (dpyinfo, display, buffer);
+
+ x_get_window_property (display, window, buffer_atom, &data, &bytes,
+ &type, &format, &size, 0);
+ if (!data || !format)
+ return Qnil;
+
+ if (format != 8 || type != XA_STRING)
+ Fsignal (Qerror,
+ Fcons (build_string ("cut buffer doesn't contain 8-bit data"),
+ Fcons (x_atom_to_symbol (display, type),
+ Fcons (make_number (format), Qnil))));
+
+ ret = (bytes ? make_string ((char *) data, bytes) : Qnil);
+ /* Use xfree, not XFree, because x_get_window_property
+ calls xmalloc itself. */
+ xfree (data);
+ return ret;
+ }
+
+
+ DEFUN ("x-store-cut-buffer-internal", Fx_store_cut_buffer_internal,
+ Sx_store_cut_buffer_internal, 2, 2, 0,
+ doc: /* Sets the value of the named cut buffer (typically
CUT_BUFFER0). */)
+ (buffer, string)
+ Lisp_Object buffer, string;
+ {
+ Window window;
+ Atom buffer_atom;
+ unsigned char *data;
+ int bytes;
+ int bytes_remaining;
+ int max_bytes;
+ Display *display;
+ struct frame *sf = SELECTED_FRAME ();
+
+ check_x ();
+ display = FRAME_X_DISPLAY (sf);
+ window = RootWindow (display, 0); /* Cut buffers are on screen 0 */
+
+ max_bytes = SELECTION_QUANTUM (display);
+ if (max_bytes > MAX_SELECTION_QUANTUM)
+ max_bytes = MAX_SELECTION_QUANTUM;
+
+ CHECK_CUT_BUFFER (buffer);
+ CHECK_STRING (string);
+ buffer_atom = symbol_to_x_atom (FRAME_X_DISPLAY_INFO (sf),
+ display, buffer);
+ data = (unsigned char *) SDATA (string);
+ bytes = SBYTES (string);
+ bytes_remaining = bytes;
+
+ if (! FRAME_X_DISPLAY_INFO (sf)->cut_buffers_initialized)
+ {
+ initialize_cut_buffers (display, window);
+ FRAME_X_DISPLAY_INFO (sf)->cut_buffers_initialized = 1;
+ }
+
+ BLOCK_INPUT;
+
+ /* Don't mess up with an empty value. */
+ if (!bytes_remaining)
+ XChangeProperty (display, window, buffer_atom, XA_STRING, 8,
+ PropModeReplace, data, 0);
+
+ while (bytes_remaining)
+ {
+ int chunk = (bytes_remaining < max_bytes
+ ? bytes_remaining : max_bytes);
+ XChangeProperty (display, window, buffer_atom, XA_STRING, 8,
+ (bytes_remaining == bytes
+ ? PropModeReplace
+ : PropModeAppend),
+ data, chunk);
+ data += chunk;
+ bytes_remaining -= chunk;
+ }
+ UNBLOCK_INPUT;
+ return string;
+ }
+
+
+ DEFUN ("x-rotate-cut-buffers-internal", Fx_rotate_cut_buffers_internal,
+ Sx_rotate_cut_buffers_internal, 1, 1, 0,
+ doc: /* Rotate the values of the cut buffers by the given number of
step.
+ Positive means shift the values forward, negative means backward. */)
+ (n)
+ Lisp_Object n;
+ {
+ Window window;
+ Atom props[8];
+ Display *display;
+ struct frame *sf = SELECTED_FRAME ();
+
+ check_x ();
+ display = FRAME_X_DISPLAY (sf);
+ window = RootWindow (display, 0); /* Cut buffers are on screen 0 */
+ CHECK_NUMBER (n);
+ if (XINT (n) == 0)
+ return n;
+ if (! FRAME_X_DISPLAY_INFO (sf)->cut_buffers_initialized)
+ {
+ initialize_cut_buffers (display, window);
+ FRAME_X_DISPLAY_INFO (sf)->cut_buffers_initialized = 1;
+ }
+
+ props[0] = XA_CUT_BUFFER0;
+ props[1] = XA_CUT_BUFFER1;
+ props[2] = XA_CUT_BUFFER2;
+ props[3] = XA_CUT_BUFFER3;
+ props[4] = XA_CUT_BUFFER4;
+ props[5] = XA_CUT_BUFFER5;
+ props[6] = XA_CUT_BUFFER6;
+ props[7] = XA_CUT_BUFFER7;
+ BLOCK_INPUT;
+ XRotateWindowProperties (display, window, props, 8, XINT (n));
+ UNBLOCK_INPUT;
+ return n;
+ }
+
+ #endif
+
+ /***********************************************************************
+ Drag and drop support
+ ***********************************************************************/
+ /* Check that lisp values are of correct type for x_fill_property_data.
+ That is, number, string or a cons with two numbers (low and high 16
+ bit parts of a 32 bit number). */
+
+ int
+ x_check_property_data (data)
+ Lisp_Object data;
+ {
+ Lisp_Object iter;
+ int size = 0;
+
+ for (iter = data; CONSP (iter) && size != -1; iter = XCDR (iter), ++size)
+ {
+ Lisp_Object o = XCAR (iter);
+
+ if (! NUMBERP (o) && ! STRINGP (o) && ! CONSP (o))
+ size = -1;
+ else if (CONSP (o) &&
+ (! NUMBERP (XCAR (o)) || ! NUMBERP (XCDR (o))))
+ size = -1;
+ }
+
+ return size;
+ }
+
+ /* Convert lisp values to a C array. Values may be a number, a string
+ which is taken as an X atom name and converted to the atom value, or
+ a cons containing the two 16 bit parts of a 32 bit number.
+
+ DPY is the display use to look up X atoms.
+ DATA is a Lisp list of values to be converted.
+ RET is the C array that contains the converted values. It is assumed
+ it is big enough to hol all values.
+ FORMAT is 8, 16 or 32 and gives the size in bits for each C value to
+ be stored in RET. */
+
+ void
+ x_fill_property_data (dpy, data, ret, format)
+ Display *dpy;
+ Lisp_Object data;
+ void *ret;
+ int format;
+ {
+ CARD32 val;
+ CARD32 *d32 = (CARD32 *) ret;
+ CARD16 *d16 = (CARD16 *) ret;
+ CARD8 *d08 = (CARD8 *) ret;
+ Lisp_Object iter;
+
+ for (iter = data; CONSP (iter); iter = XCDR (iter))
+ {
+ Lisp_Object o = XCAR (iter);
+
+ if (INTEGERP (o))
+ val = (CARD32) XFASTINT (o);
+ else if (FLOATP (o))
+ val = (CARD32) XFLOAT (o);
+ else if (CONSP (o))
+ val = (CARD32) cons_to_long (o);
+ else if (STRINGP (o))
+ {
+ BLOCK_INPUT;
+ val = XInternAtom (dpy, (char *) SDATA (o), False);
+ UNBLOCK_INPUT;
+ }
+ else
+ error ("Wrong type, must be string, number or cons");
+
+ if (format == 8)
+ *d08++ = (CARD8) val;
+ else if (format == 16)
+ *d16++ = (CARD16) val;
+ else
+ *d32++ = val;
+ }
+ }
+
+ /* Convert an array of C values to a Lisp list.
+ F is the frame to be used to look up X atoms if the TYPE is XA_ATOM.
+ DATA is a C array of values to be converted.
+ TYPE is the type of the data. Only XA_ATOM is special, it converts
+ each number in DATA to its corresponfing X atom as a symbol.
+ FORMAT is 8, 16 or 32 and gives the size in bits for each C value to
+ be stored in RET.
+ SIZE is the number of elements in DATA.
+
+ Also see comment for selection_data_to_lisp_data above. */
+
+ Lisp_Object
+ x_property_data_to_lisp (f, data, type, format, size)
+ struct frame *f;
+ unsigned char *data;
+ Atom type;
+ int format;
+ unsigned long size;
+ {
+ return selection_data_to_lisp_data (FRAME_X_DISPLAY (f),
+ data, size*format/8, type, format);
+ }
+
+ /* Get the mouse position frame relative coordinates. */
+
+ static void
+ mouse_position_for_drop (f, x, y)
+ FRAME_PTR f;
+ int *x;
+ int *y;
+ {
+ Window root, dummy_window;
+ int dummy;
+
+ BLOCK_INPUT;
+
+ XQueryPointer (FRAME_X_DISPLAY (f),
+ DefaultRootWindow (FRAME_X_DISPLAY (f)),
+
+ /* The root window which contains the pointer. */
+ &root,
+
+ /* Window pointer is on, not used */
+ &dummy_window,
+
+ /* The position on that root window. */
+ x, y,
+
+ /* x/y in dummy_window coordinates, not used. */
+ &dummy, &dummy,
+
+ /* Modifier keys and pointer buttons, about which
+ we don't care. */
+ (unsigned int *) &dummy);
+
+
+ /* Absolute to relative. */
+ *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+ *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+
+ UNBLOCK_INPUT;
+ }
+
+ DEFUN ("x-get-atom-name", Fx_get_atom_name,
+ Sx_get_atom_name, 1, 2, 0,
+ doc: /* Return the X atom name for VALUE as a string.
+ VALUE may be a number or a cons where the car is the upper 16 bits and
+ the cdr is the lower 16 bits of a 32 bit value.
+ Use the display for FRAME or the current frame if FRAME is not given or nil.
+
+ If the value is 0 or the atom is not known, return the empty string. */)
+ (value, frame)
+ Lisp_Object value, frame;
+ {
+ struct frame *f = check_x_frame (frame);
+ char *name = 0;
+ Lisp_Object ret = Qnil;
+ int count;
+ Display *dpy = FRAME_X_DISPLAY (f);
+ Atom atom;
+
+ if (INTEGERP (value))
+ atom = (Atom) XUINT (value);
+ else if (FLOATP (value))
+ atom = (Atom) XFLOAT (value);
+ else if (CONSP (value))
+ atom = (Atom) cons_to_long (value);
+ else
+ error ("Wrong type, value must be number or cons");
+
+ BLOCK_INPUT;
+ count = x_catch_errors (dpy);
+
+ name = atom ? XGetAtomName (dpy, atom) : "";
+
+ if (! x_had_errors_p (dpy))
+ ret = make_string (name, strlen (name));
+
+ x_uncatch_errors (dpy, count);
+
+ if (atom && name) XFree (name);
+ if (NILP (ret)) ret = make_string ("", 0);
+
+ UNBLOCK_INPUT;
+
+ return ret;
+ }
+
+ /* Convert an XClientMessageEvent to a Lisp event of type DRAG_N_DROP_EVENT.
+ TODO: Check if this client event really is a DND event? */
+
+ int
+ x_handle_dnd_message (f, event, dpyinfo, bufp)
+ struct frame *f;
+ XClientMessageEvent *event;
+ struct x_display_info *dpyinfo;
+ struct input_event *bufp;
+ {
+ Lisp_Object vec;
+ Lisp_Object frame;
+ unsigned long size = (8*sizeof (event->data))/event->format;
+ int x, y;
+
+ XSETFRAME (frame, f);
+
+ vec = Fmake_vector (make_number (4), Qnil);
+ AREF (vec, 0) = SYMBOL_NAME (x_atom_to_symbol (FRAME_X_DISPLAY (f),
+ event->message_type));
+ AREF (vec, 1) = frame;
+ AREF (vec, 2) = make_number (event->format);
+ AREF (vec, 3) = x_property_data_to_lisp (f,
+ event->data.b,
+ event->message_type,
+ event->format,
+ size);
+
+ mouse_position_for_drop (f, &x, &y);
+ bufp->kind = DRAG_N_DROP_EVENT;
+ bufp->frame_or_window = Fcons (frame, vec);
+ bufp->timestamp = CurrentTime;
+ bufp->x = make_number (x);
+ bufp->y = make_number (y);
+ bufp->arg = Qnil;
+ bufp->modifiers = 0;
+
+ return 1;
+ }
+
+ DEFUN ("x-send-client-message", Fx_send_client_event,
+ Sx_send_client_message, 6, 6, 0,
+ doc: /* Send a client message of MESSAGE-TYPE to window DEST on
DISPLAY.
+
+ For DISPLAY, specify either a frame or a display name (a string).
+ If DISPLAY is nil, that stands for the selected frame's display.
+ DEST may be a number, in which case it is a Window id. The value 0 may
+ be used to send to the root window of the DISPLAY.
+ If DEST is a cons, it is converted to a 32 bit number
+ with the high 16 bits from the car and the lower 16 bit from the cdr. That
+ number is then used as a window id.
+ If DEST is a frame the event is sent to the outer window of that frame.
+ Nil means the currently selected frame.
+ If DEST is the string "PointerWindow" the event is sent to the window that
+ contains the pointer. If DEST is the string "InputFocus" the event is
+ sent to the window that has the input focus.
+ FROM is the frame sending the event. Use nil for currently selected frame.
+ MESSAGE-TYPE is the name of an Atom as a string.
+ FORMAT must be one of 8, 16 or 32 and determines the size of the values in
+ bits. VALUES is a list of numbers, cons and/or strings containing the values
+ to send. If a value is a string, it is converted to an Atom and the value of
+ the Atom is sent. If a value is a cons, it is converted to a 32 bit number
+ with the high 16 bits from the car and the lower 16 bit from the cdr.
+ If more values than fits into the event is given, the excessive values
+ are ignored. */)
+ (display, dest, from, message_type, format, values)
+ Lisp_Object display, dest, from, message_type, format, values;
+ {
+ struct x_display_info *dpyinfo = check_x_display_info (display);
+ Window wdest;
+ XEvent event;
+ Lisp_Object cons;
+ int size;
+ struct frame *f = check_x_frame (from);
+ int count;
+ int to_root;
+
+ CHECK_STRING (message_type);
+ CHECK_NUMBER (format);
+ CHECK_CONS (values);
+
+ if (x_check_property_data (values) == -1)
+ error ("Bad data in VALUES, must be number, cons or string");
+
+ event.xclient.type = ClientMessage;
+ event.xclient.format = XFASTINT (format);
+
+ if (event.xclient.format != 8 && event.xclient.format != 16
+ && event.xclient.format != 32)
+ error ("FORMAT must be one of 8, 16 or 32");
+
+ if (FRAMEP (dest) || NILP (dest))
+ {
+ struct frame *fdest = check_x_frame (dest);
+ wdest = FRAME_OUTER_WINDOW (fdest);
+ }
+ else if (STRINGP (dest))
+ {
+ if (strcmp (SDATA (dest), "PointerWindow") == 0)
+ wdest = PointerWindow;
+ else if (strcmp (SDATA (dest), "InputFocus") == 0)
+ wdest = InputFocus;
+ else
+ error ("DEST as a string must be one of PointerWindow or InputFocus");
+ }
+ else if (INTEGERP (dest))
+ wdest = (Window) XFASTINT (dest);
+ else if (FLOATP (dest))
+ wdest = (Window) XFLOAT (dest);
+ else if (CONSP (dest))
+ {
+ if (! NUMBERP (XCAR (dest)) || ! NUMBERP (XCDR (dest)))
+ error ("Both car and cdr for DEST must be numbers");
+ else
+ wdest = (Window) cons_to_long (dest);
+ }
+ else
+ error ("DEST must be a frame, nil, string, number or cons");
+
+ if (wdest == 0) wdest = dpyinfo->root_window;
+ to_root = wdest == dpyinfo->root_window;
+
+ for (cons = values, size = 0; CONSP (cons); cons = XCDR (cons), ++size)
+ ;
+
+ BLOCK_INPUT;
+
+ event.xclient.message_type
+ = XInternAtom (dpyinfo->display, SDATA (message_type), False);
+ event.xclient.display = dpyinfo->display;
+
+ /* Some clients (metacity for example) expects sending window to be here
+ when sending to the root window. */
+ event.xclient.window = to_root ? FRAME_OUTER_WINDOW (f) : wdest;
+
+ memset (event.xclient.data.b, 0, sizeof (event.xclient.data.b));
+ x_fill_property_data (dpyinfo->display, values, event.xclient.data.b,
+ event.xclient.format);
+
+ /* If event mask is 0 the event is sent to the client that created
+ the destination window. But if we are sending to the root window,
+ there is no such client. Then we set the event mask to 0xffff. The
+ event then goes to clients selecting for events on the root window. */
+ count = x_catch_errors (dpyinfo->display);
+ {
+ int propagate = to_root ? False : True;
+ unsigned mask = to_root ? 0xffff : 0;
+ XSendEvent (dpyinfo->display, wdest, propagate, mask, &event);
+ XFlush (dpyinfo->display);
+ }
+ x_uncatch_errors (dpyinfo->display, count);
+ UNBLOCK_INPUT;
+
+ return Qnil;
+ }
+
+
+ void
+ syms_of_xselect ()
+ {
+ defsubr (&Sx_get_selection_internal);
+ defsubr (&Sx_own_selection_internal);
+ defsubr (&Sx_disown_selection_internal);
+ defsubr (&Sx_selection_owner_p);
+ defsubr (&Sx_selection_exists_p);
+
+ #ifdef CUT_BUFFER_SUPPORT
+ defsubr (&Sx_get_cut_buffer_internal);
+ defsubr (&Sx_store_cut_buffer_internal);
+ defsubr (&Sx_rotate_cut_buffers_internal);
+ #endif
+
+ defsubr (&Sx_get_atom_name);
+ defsubr (&Sx_send_client_message);
+
+ reading_selection_reply = Fcons (Qnil, Qnil);
+ staticpro (&reading_selection_reply);
+ reading_selection_window = 0;
+ reading_which_selection = 0;
+
+ property_change_wait_list = 0;
+ prop_location_identifier = 0;
+ property_change_reply = Fcons (Qnil, Qnil);
+ staticpro (&property_change_reply);
+
+ Vselection_alist = Qnil;
+ staticpro (&Vselection_alist);
+
+ DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist,
+ doc: /* An alist associating X Windows selection-types with
functions.
+ These functions are called to convert the selection, with three args:
+ the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');
+ a desired type to which the selection should be converted;
+ and the local selection value (whatever was given to `x-own-selection').
+
+ The function should return the value to send to the X server
+ \(typically a string). A return value of nil
+ means that the conversion could not be done.
+ A return value which is the symbol `NULL'
+ means that a side-effect was executed,
+ and there is no meaningful selection value. */);
+ Vselection_converter_alist = Qnil;
+
+ DEFVAR_LISP ("x-lost-selection-hooks", &Vx_lost_selection_hooks,
+ doc: /* A list of functions to be called when Emacs loses an X
selection.
+ \(This happens when some other X client makes its own selection
+ or when a Lisp program explicitly clears the selection.)
+ The functions are called with one argument, the selection type
+ \(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'). */);
+ Vx_lost_selection_hooks = Qnil;
+
+ DEFVAR_LISP ("x-sent-selection-hooks", &Vx_sent_selection_hooks,
+ doc: /* A list of functions to be called when Emacs answers a
selection request.
+ The functions are called with four arguments:
+ - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');
+ - the selection-type which Emacs was asked to convert the
+ selection into before sending (for example, `STRING' or `LENGTH');
+ - a flag indicating success or failure for responding to the request.
+ We might have failed (and declined the request) for any number of reasons,
+ including being asked for a selection that we no longer own, or being asked
+ to convert into a type that we don't know about or that is inappropriate.
+ This hook doesn't let you change the behavior of Emacs's selection replies,
+ it merely informs you that they have happened. */);
+ Vx_sent_selection_hooks = Qnil;
+
+ DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system,
+ doc: /* Coding system for communicating with other X clients.
+ When sending or receiving text via cut_buffer, selection, and clipboard,
+ the text is encoded or decoded by this coding system.
+ The default value is `compound-text-with-extensions'. */);
+ Vselection_coding_system = intern ("compound-text-with-extensions");
+
+ DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system,
+ doc: /* Coding system for the next communication with other X
clients.
+ Usually, `selection-coding-system' is used for communicating with
+ other X clients. But, if this variable is set, it is used for the
+ next communication only. After the communication, this variable is
+ set to nil. */);
+ Vnext_selection_coding_system = Qnil;
+
+ DEFVAR_INT ("x-selection-timeout", &x_selection_timeout,
+ doc: /* Number of milliseconds to wait for a selection reply.
+ If the selection owner doesn't reply in this time, we give up.
+ A value of 0 means wait as long as necessary. This is initialized from the
+ \"*selectionTimeout\" resource. */);
+ x_selection_timeout = 0;
+
+ QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY);
+ QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY);
+ QSTRING = intern ("STRING"); staticpro (&QSTRING);
+ QINTEGER = intern ("INTEGER"); staticpro (&QINTEGER);
+ QCLIPBOARD = intern ("CLIPBOARD"); staticpro (&QCLIPBOARD);
+ QTIMESTAMP = intern ("TIMESTAMP"); staticpro (&QTIMESTAMP);
+ QTEXT = intern ("TEXT"); staticpro (&QTEXT);
+ QCOMPOUND_TEXT = intern ("COMPOUND_TEXT"); staticpro (&QCOMPOUND_TEXT);
+ QUTF8_STRING = intern ("UTF8_STRING"); staticpro (&QUTF8_STRING);
+ QTIMESTAMP = intern ("TIMESTAMP"); staticpro (&QTIMESTAMP);
+ QDELETE = intern ("DELETE"); staticpro (&QDELETE);
+ QMULTIPLE = intern ("MULTIPLE"); staticpro (&QMULTIPLE);
+ QINCR = intern ("INCR"); staticpro (&QINCR);
+ QEMACS_TMP = intern ("_EMACS_TMP_"); staticpro (&QEMACS_TMP);
+ QTARGETS = intern ("TARGETS"); staticpro (&QTARGETS);
+ QATOM = intern ("ATOM"); staticpro (&QATOM);
+ QATOM_PAIR = intern ("ATOM_PAIR"); staticpro (&QATOM_PAIR);
+ QNULL = intern ("NULL"); staticpro (&QNULL);
+ Qcompound_text_with_extensions = intern ("compound-text-with-extensions");
+ staticpro (&Qcompound_text_with_extensions);
+
+ #ifdef CUT_BUFFER_SUPPORT
+ QCUT_BUFFER0 = intern ("CUT_BUFFER0"); staticpro (&QCUT_BUFFER0);
+ QCUT_BUFFER1 = intern ("CUT_BUFFER1"); staticpro (&QCUT_BUFFER1);
+ QCUT_BUFFER2 = intern ("CUT_BUFFER2"); staticpro (&QCUT_BUFFER2);
+ QCUT_BUFFER3 = intern ("CUT_BUFFER3"); staticpro (&QCUT_BUFFER3);
+ QCUT_BUFFER4 = intern ("CUT_BUFFER4"); staticpro (&QCUT_BUFFER4);
+ QCUT_BUFFER5 = intern ("CUT_BUFFER5"); staticpro (&QCUT_BUFFER5);
+ QCUT_BUFFER6 = intern ("CUT_BUFFER6"); staticpro (&QCUT_BUFFER6);
+ QCUT_BUFFER7 = intern ("CUT_BUFFER7"); staticpro (&QCUT_BUFFER7);
+ #endif
+
+ Qforeign_selection = intern ("foreign-selection");
+ staticpro (&Qforeign_selection);
+ }
+
+ /* arch-tag: 7c293b0f-9918-4f69-8ac7-03e142307236
+ (do not change this comment) */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] Changes to emacs/src/xselect.c [gnus-5_10-branch],
Miles Bader <=