mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-14 03:37:38 -08:00
760 lines
20 KiB
C
760 lines
20 KiB
C
/* X Selection processing for emacs
|
|
Copyright (C) 1990, 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#include "config.h"
|
|
#include "lisp.h"
|
|
#include "xterm.h"
|
|
#include "buffer.h"
|
|
#include "frame.h"
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
/* Macros for X Selections */
|
|
#define MAX_SELECTION(dpy) (((dpy)->max_request_size << 2) - 100)
|
|
#define SELECTION_LENGTH(len,format) ((len) * ((format) >> 2))
|
|
|
|
/* The timestamp of the last input event we received from the X server. */
|
|
unsigned long last_event_timestamp;
|
|
|
|
/* t if a mouse button is depressed. */
|
|
extern Lisp_Object Vmouse_grabbed;
|
|
|
|
/* When emacs became the PRIMARY selection owner. */
|
|
Time x_begin_selection_own;
|
|
|
|
/* When emacs became the SECONDARY selection owner. */
|
|
Time x_begin_secondary_selection_own;
|
|
|
|
/* When emacs became the CLIPBOARD selection owner. */
|
|
Time x_begin_clipboard_own;
|
|
|
|
/* The value of the current CLIPBOARD selection. */
|
|
Lisp_Object Vx_clipboard_value;
|
|
|
|
/* The value of the current PRIMARY selection. */
|
|
Lisp_Object Vx_selection_value;
|
|
|
|
/* The value of the current SECONDARY selection. */
|
|
Lisp_Object Vx_secondary_selection_value;
|
|
|
|
/* Types of selections we may make. Note that Qcut_buffer0 isn't really
|
|
a selection, but it acts like one for the sake of Fx_own_selection and
|
|
Fx_selection_value. */
|
|
Lisp_Object Qprimary, Qsecondary, Qclipboard, Qcut_buffer0;
|
|
|
|
/* Emacs' selection property identifiers. */
|
|
Atom Xatom_emacs_selection;
|
|
Atom Xatom_emacs_secondary_selection;
|
|
|
|
/* Clipboard selection atom. */
|
|
Atom Xatom_clipboard_selection;
|
|
|
|
/* Clipboard atom. */
|
|
Atom Xatom_clipboard;
|
|
|
|
/* Atom for indicating incremental selection transfer. */
|
|
Atom Xatom_incremental;
|
|
|
|
/* Atom for indicating multiple selection request list */
|
|
Atom Xatom_multiple;
|
|
|
|
/* Atom for what targets emacs handles. */
|
|
Atom Xatom_targets;
|
|
|
|
/* Atom for indicating timstamp selection request */
|
|
Atom Xatom_timestamp;
|
|
|
|
/* Atom requesting we delete our selection. */
|
|
Atom Xatom_delete;
|
|
|
|
/* Selection magic. */
|
|
Atom Xatom_insert_selection;
|
|
|
|
/* Type of property for INSERT_SELECTION. */
|
|
Atom Xatom_pair;
|
|
|
|
/* More selection magic. */
|
|
Atom Xatom_insert_property;
|
|
|
|
/* Atom for indicating property type TEXT */
|
|
Atom Xatom_text;
|
|
|
|
/* Kinds of protocol things we may receive. */
|
|
Atom Xatom_wm_take_focus;
|
|
Atom Xatom_wm_save_yourself;
|
|
Atom Xatom_wm_delete_window;
|
|
|
|
/* Communication with window managers. */
|
|
Atom Xatom_wm_protocols;
|
|
|
|
/* These are to handle incremental selection transfer. */
|
|
Window incr_requestor;
|
|
Atom incr_property;
|
|
int incr_nbytes;
|
|
unsigned char *incr_value;
|
|
unsigned char *incr_ptr;
|
|
|
|
/* SELECTION OWNER CODE */
|
|
|
|
|
|
/* Request selection ownership if we do not already have it. */
|
|
|
|
static int
|
|
own_selection (selection_type, time)
|
|
Atom selection_type;
|
|
Time time;
|
|
{
|
|
Window owner_window, selecting_window;
|
|
|
|
if ((EQ (selection_type, Qprimary) && !NILP (Vx_selection_value))
|
|
|| ((EQ (selection_type, Qsecondary)) && !NILP (Vx_secondary_selection_value))
|
|
|| ((EQ (selection_type, Qclipboard)) && !NILP (Vx_clipboard_value)))
|
|
return 1;
|
|
|
|
selecting_window = FRAME_X_WINDOW (selected_frame);
|
|
XSetSelectionOwner (x_current_display, selection_type,
|
|
selecting_window, time);
|
|
owner_window = XGetSelectionOwner (x_current_display, selection_type);
|
|
|
|
if (owner_window != selecting_window)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Become the selection owner and make our data the selection value.
|
|
If we are already the owner, merely change data and timestamp values.
|
|
This avoids generating SelectionClear events for ourselves. */
|
|
|
|
DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
|
|
1, 2, "",
|
|
"Make STRING the selection value. Default is the primary selection,\n\
|
|
but optional second argument TYPE may specify secondary or clipboard.\n\
|
|
\n\
|
|
TYPE may also be cut-buffer0, indicating that Emacs should set the X\n\
|
|
cut buffer 0 to STRING. This is for compatibility with older X\n\
|
|
applications which still use the cut buffers; new applications should\n\
|
|
use X selections.")
|
|
(string, type)
|
|
register Lisp_Object string, type;
|
|
{
|
|
Atom selection_type;
|
|
Lisp_Object val;
|
|
Time event_time = last_event_timestamp;
|
|
CHECK_STRING (string, 0);
|
|
|
|
val = Qnil;
|
|
|
|
if (NILP (type) || EQ (type, Qprimary))
|
|
{
|
|
BLOCK_INPUT;
|
|
if (own_selection (XA_PRIMARY, event_time))
|
|
{
|
|
x_begin_selection_own = event_time;
|
|
val = Vx_selection_value = string;
|
|
}
|
|
UNBLOCK_INPUT;
|
|
}
|
|
else if (EQ (type, Qsecondary))
|
|
{
|
|
BLOCK_INPUT;
|
|
if (own_selection (XA_SECONDARY, event_time))
|
|
{
|
|
x_begin_secondary_selection_own = event_time;
|
|
val = Vx_secondary_selection_value = string;
|
|
}
|
|
UNBLOCK_INPUT;
|
|
}
|
|
else if (EQ (type, Qclipboard))
|
|
{
|
|
BLOCK_INPUT;
|
|
if (own_selection (Xatom_clipboard, event_time))
|
|
{
|
|
x_begin_clipboard_own = event_time;
|
|
val = Vx_clipboard_value = string;
|
|
}
|
|
UNBLOCK_INPUT;
|
|
}
|
|
else if (EQ (type, Qcut_buffer0))
|
|
{
|
|
/* DECwindows and some other servers don't seem to like setting
|
|
properties to values larger than about 20k. For very large
|
|
values, they signal an error, but for intermediate values
|
|
they just seem to hang.
|
|
|
|
We could just truncate the request, but it's better to let
|
|
the user know that the strategy he/she's using isn't going to
|
|
work than to have it work partially, but incorrectly. */
|
|
BLOCK_INPUT;
|
|
if (XSTRING (string)->size > MAX_SELECTION (x_current_display))
|
|
{
|
|
XStoreBytes (x_current_display, (char *) 0, 0);
|
|
val = Qnil;
|
|
}
|
|
else
|
|
{
|
|
XStoreBytes (x_current_display,
|
|
(char *) XSTRING (string)->data,
|
|
XSTRING (string)->size);
|
|
val = string;
|
|
}
|
|
UNBLOCK_INPUT;
|
|
}
|
|
else
|
|
error ("Invalid X selection type");
|
|
|
|
return val;
|
|
}
|
|
|
|
/* Clear our selection ownership data, as some other client has
|
|
become the owner. */
|
|
|
|
void
|
|
x_disown_selection (old_owner, selection, changed_owner_time)
|
|
Window *old_owner;
|
|
Atom selection;
|
|
Time changed_owner_time;
|
|
{
|
|
struct frame *s = x_window_to_frame (old_owner);
|
|
|
|
if (s) /* We are the owner */
|
|
{
|
|
if (selection == XA_PRIMARY)
|
|
{
|
|
x_begin_selection_own = 0;
|
|
Vx_selection_value = Qnil;
|
|
}
|
|
else if (selection == XA_SECONDARY)
|
|
{
|
|
x_begin_secondary_selection_own = 0;
|
|
Vx_secondary_selection_value = Qnil;
|
|
}
|
|
else if (selection == Xatom_clipboard)
|
|
{
|
|
x_begin_clipboard_own = 0;
|
|
Vx_clipboard_value = Qnil;
|
|
}
|
|
else
|
|
abort ();
|
|
}
|
|
else
|
|
abort (); /* Inconsistent state. */
|
|
}
|
|
|
|
int x_selection_alloc_error;
|
|
int x_converting_selection;
|
|
|
|
/* Reply to some client's request for our selection data. Data is
|
|
placed in a property supplied by the requesting window.
|
|
|
|
If the data exceeds the maximum amount the server can send,
|
|
then prepare to send it incrementally, and reply to the client with
|
|
the total size of the data.
|
|
|
|
But first, check for all the other crufty stuff we could get. */
|
|
|
|
void
|
|
x_answer_selection_request (event)
|
|
XSelectionRequestEvent event;
|
|
{
|
|
Time emacs_own_time;
|
|
Lisp_Object selection_value;
|
|
XSelectionEvent evt;
|
|
int format = 8; /* We have only byte sized (text) data. */
|
|
|
|
evt.type = SelectionNotify; /* Construct reply event */
|
|
evt.display = event.display;
|
|
evt.requestor = event.requestor;
|
|
evt.selection = event.selection;
|
|
evt.time = event.time;
|
|
evt.target = event.target;
|
|
|
|
if (event.selection == XA_PRIMARY)
|
|
{
|
|
emacs_own_time = x_begin_selection_own;
|
|
selection_value = Vx_selection_value;
|
|
}
|
|
else if (event.selection == XA_SECONDARY)
|
|
{
|
|
emacs_own_time = x_begin_secondary_selection_own;
|
|
selection_value = Vx_secondary_selection_value;
|
|
}
|
|
else if (event.selection == Xatom_clipboard)
|
|
{
|
|
emacs_own_time = x_begin_clipboard_own;
|
|
selection_value = Vx_clipboard_value;
|
|
}
|
|
else
|
|
abort ();
|
|
|
|
if (event.time != CurrentTime
|
|
&& event.time < emacs_own_time)
|
|
evt.property = None;
|
|
else
|
|
{
|
|
if (event.property == None) /* obsolete client */
|
|
evt.property = event.target;
|
|
else
|
|
evt.property = event.property;
|
|
}
|
|
|
|
if (event.target == Xatom_targets) /* Send List of target atoms */
|
|
{
|
|
}
|
|
else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
|
|
{
|
|
Atom type;
|
|
int return_format;
|
|
unsigned long items, bytes_left;
|
|
unsigned char *data;
|
|
int result, i;
|
|
|
|
if (event.property == 0 /* 0 == NILP */
|
|
|| event.property == None)
|
|
return;
|
|
|
|
result = XGetWindowProperty (event.display, event.requestor,
|
|
event.property, 0L, 10000000L,
|
|
True, Xatom_pair, &type, &return_format,
|
|
&items, &bytes_left, &data);
|
|
|
|
if (result == Success && type == Xatom_pair)
|
|
for (i = items; i > 0; i--)
|
|
{
|
|
/* Convert each element of the list. */
|
|
}
|
|
|
|
(void) XSendEvent (x_current_display, evt.requestor, False,
|
|
0L, (XEvent *) &evt);
|
|
return;
|
|
}
|
|
else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
|
|
{
|
|
if (! emacs_own_time)
|
|
abort ();
|
|
|
|
format = 32;
|
|
XChangeProperty (evt.display, evt.requestor, evt.property,
|
|
evt.target, format, PropModeReplace,
|
|
(unsigned char *) &emacs_own_time, 1);
|
|
return;
|
|
}
|
|
else if (event.target == Xatom_delete) /* Delete our selection. */
|
|
{
|
|
if (EQ (Qnil, selection_value))
|
|
abort ();
|
|
|
|
x_disown_selection (event.owner, event.selection, event.time);
|
|
|
|
/* Now return property of type NILP, length 0. */
|
|
XChangeProperty (event.display, event.requestor, event.property,
|
|
0, format, PropModeReplace, (unsigned char *) 0, 0);
|
|
return;
|
|
}
|
|
else if (event.target == Xatom_insert_selection)
|
|
{
|
|
Atom type;
|
|
int return_format;
|
|
unsigned long items, bytes_left;
|
|
unsigned char *data;
|
|
int result = XGetWindowProperty (event.display, event.requestor,
|
|
event.property, 0L, 10000000L,
|
|
True, Xatom_pair, &type, &return_format,
|
|
&items, &bytes_left, &data);
|
|
if (result == Success && type == Xatom_pair)
|
|
{
|
|
/* Convert the first atom to (a selection) to the target
|
|
indicated by the second atom. */
|
|
}
|
|
}
|
|
else if (event.target == Xatom_insert_property)
|
|
{
|
|
Atom type;
|
|
int return_format;
|
|
unsigned long items, bytes_left;
|
|
unsigned char *data;
|
|
int result = XGetWindowProperty (event.display, event.requestor,
|
|
event.property, 0L, 10000000L,
|
|
True, XA_STRING, &type, &return_format,
|
|
&items, &bytes_left, &data);
|
|
|
|
if (result == Success && type == XA_STRING && return_format == 8)
|
|
{
|
|
if (event.selection == Xatom_emacs_selection)
|
|
Vx_selection_value = make_string (data);
|
|
else if (event.selection == Xatom_emacs_secondary_selection)
|
|
Vx_secondary_selection_value = make_string (data);
|
|
else if (event.selection == Xatom_clipboard_selection)
|
|
Vx_clipboard_value = make_string (data);
|
|
else
|
|
abort ();
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if ((event.target == Xatom_text
|
|
|| event.target == XA_STRING))
|
|
{
|
|
int size = XSTRING (selection_value)->size;
|
|
unsigned char *data = XSTRING (selection_value)->data;
|
|
|
|
if (EQ (Qnil, selection_value))
|
|
abort ();
|
|
|
|
/* Place data on requestor window's property. */
|
|
if (SELECTION_LENGTH (size, format)
|
|
<= MAX_SELECTION (x_current_display))
|
|
{
|
|
x_converting_selection = 1;
|
|
XChangeProperty (evt.display, evt.requestor, evt.property,
|
|
evt.target, format, PropModeReplace,
|
|
data, size);
|
|
if (x_selection_alloc_error)
|
|
{
|
|
x_selection_alloc_error = 0;
|
|
abort ();
|
|
}
|
|
x_converting_selection = 0;
|
|
}
|
|
else /* Send incrementally */
|
|
{
|
|
evt.target = Xatom_incremental;
|
|
incr_requestor = evt.requestor;
|
|
incr_property = evt.property;
|
|
x_converting_selection = 1;
|
|
|
|
/* Need to handle Alloc errors on these requests. */
|
|
XChangeProperty (evt.display, incr_requestor, incr_property,
|
|
Xatom_incremental, 32,
|
|
PropModeReplace,
|
|
(unsigned char *) &size, 1);
|
|
if (x_selection_alloc_error)
|
|
{
|
|
x_selection_alloc_error = 0;
|
|
x_converting_selection = 0;
|
|
abort ();
|
|
/* Now abort the send. */
|
|
}
|
|
|
|
incr_nbytes = size;
|
|
incr_value = data;
|
|
incr_ptr = data;
|
|
|
|
/* Ask for notification when requestor deletes property. */
|
|
XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
|
|
|
|
/* If we're sending incrementally, perhaps block here
|
|
until all sent? */
|
|
}
|
|
}
|
|
else
|
|
evt.property = None;
|
|
|
|
/* Don't do this if there was an Alloc error: abort the transfer
|
|
by sending None. */
|
|
(void) XSendEvent (x_current_display, evt.requestor, False,
|
|
0L, (XEvent *) &evt);
|
|
}
|
|
|
|
/* Send an increment of selection data in response to a PropertyNotify event.
|
|
The increment is placed in a property on the requestor's window.
|
|
When the requestor has processed the increment, it deletes the property,
|
|
which sends us another PropertyNotify event.
|
|
|
|
When there is no more data to send, we send a zero-length increment. */
|
|
|
|
void
|
|
x_send_incremental (event)
|
|
XPropertyEvent event;
|
|
{
|
|
if (incr_requestor
|
|
&& incr_requestor == event.window
|
|
&& incr_property == event.atom
|
|
&& event.state == PropertyDelete)
|
|
{
|
|
int format = 8;
|
|
int length = MAX_SELECTION (x_current_display);
|
|
int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
|
|
|
|
if (length > bytes_left) /* Also sends 0 len when finished. */
|
|
length = bytes_left;
|
|
XChangeProperty (x_current_display, incr_requestor,
|
|
incr_property, XA_STRING, format,
|
|
PropModeAppend, incr_ptr, length);
|
|
if (x_selection_alloc_error)
|
|
{
|
|
x_selection_alloc_error = 0;
|
|
x_converting_selection = 0;
|
|
/* Abandon the transmission. */
|
|
abort ();
|
|
}
|
|
if (length > 0)
|
|
incr_ptr += length;
|
|
else
|
|
{ /* Everything's sent */
|
|
XSelectInput (x_current_display, incr_requestor, 0L);
|
|
incr_requestor = (Window) 0;
|
|
incr_property = (Atom) 0;
|
|
incr_nbytes = 0;
|
|
incr_value = (unsigned char *) 0;
|
|
incr_ptr = (unsigned char *) 0;
|
|
x_converting_selection = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* SELECTION REQUESTOR CODE */
|
|
|
|
/* Predicate function used to match a requested event. */
|
|
|
|
Bool
|
|
XCheckSelectionEvent (dpy, event, window)
|
|
Display *dpy;
|
|
XEvent *event;
|
|
char *window;
|
|
{
|
|
if (event->type == SelectionNotify)
|
|
if (event->xselection.requestor == (Window) window)
|
|
return True;
|
|
|
|
return False;
|
|
}
|
|
|
|
/* Request a selection value from its owner. This will block until
|
|
all the data is arrived. */
|
|
|
|
static Lisp_Object
|
|
get_selection_value (type)
|
|
Atom type;
|
|
{
|
|
XEvent event;
|
|
Lisp_Object val;
|
|
Time requestor_time; /* Timestamp of selection request. */
|
|
Window requestor_window;
|
|
|
|
BLOCK_INPUT;
|
|
requestor_time = last_event_timestamp;
|
|
requestor_window = FRAME_X_WINDOW (selected_frame);
|
|
XConvertSelection (x_current_display, type, XA_STRING,
|
|
Xatom_emacs_selection, requestor_window, requestor_time);
|
|
XIfEvent (x_current_display,
|
|
&event,
|
|
XCheckSelectionEvent,
|
|
(char *) requestor_window);
|
|
val = x_selection_arrival (&event, requestor_window, requestor_time);
|
|
UNBLOCK_INPUT;
|
|
|
|
return val;
|
|
}
|
|
|
|
/* Request a 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-selection-value", Fx_selection_value, Sx_selection_value,
|
|
0, 1, "",
|
|
"Return the value of one of the selections. Default is the primary\n\
|
|
selection, but optional argument TYPE may specify secondary or clipboard.")
|
|
(type)
|
|
register Lisp_Object type;
|
|
{
|
|
Atom selection_type;
|
|
|
|
if (NILP (type) || EQ (type, Qprimary))
|
|
{
|
|
if (!NILP (Vx_selection_value))
|
|
return Vx_selection_value;
|
|
|
|
return get_selection_value (XA_PRIMARY);
|
|
}
|
|
else if (EQ (type, Qsecondary))
|
|
{
|
|
if (!NILP (Vx_secondary_selection_value))
|
|
return Vx_secondary_selection_value;
|
|
|
|
return get_selection_value (XA_SECONDARY);
|
|
}
|
|
else if (EQ (type, Qclipboard))
|
|
{
|
|
if (!NILP (Vx_clipboard_value))
|
|
return Vx_clipboard_value;
|
|
|
|
return get_selection_value (Xatom_clipboard);
|
|
}
|
|
else if (EQ (type, Qcut_buffer0))
|
|
{
|
|
char *data;
|
|
int size;
|
|
Lisp_Object string;
|
|
|
|
BLOCK_INPUT;
|
|
data = XFetchBytes (x_current_display, &size);
|
|
if (data == 0)
|
|
string = Qnil;
|
|
else
|
|
string = make_string (data, size);
|
|
UNBLOCK_INPUT;
|
|
|
|
return string;
|
|
}
|
|
else
|
|
error ("Invalid X selection type");
|
|
}
|
|
|
|
Lisp_Object
|
|
x_selection_arrival (event, requestor_window, requestor_time)
|
|
register XSelectionEvent *event;
|
|
Window requestor_window;
|
|
Time requestor_time;
|
|
{
|
|
int result;
|
|
Atom type, selection;
|
|
int format;
|
|
unsigned long items;
|
|
unsigned long bytes_left;
|
|
unsigned char *data = 0;
|
|
int offset = 0;
|
|
|
|
if (event->selection == XA_PRIMARY)
|
|
selection = Xatom_emacs_selection;
|
|
else if (event->selection == XA_SECONDARY)
|
|
selection = Xatom_emacs_secondary_selection;
|
|
else if (event->selection == Xatom_clipboard)
|
|
selection = Xatom_clipboard_selection;
|
|
else
|
|
abort ();
|
|
|
|
if (event->requestor == requestor_window
|
|
&& event->time == requestor_time
|
|
&& event->property != None)
|
|
if (event->target != Xatom_incremental)
|
|
{
|
|
unsigned char *return_string =
|
|
(unsigned char *) alloca (MAX_SELECTION (x_current_display));
|
|
|
|
do
|
|
{
|
|
result = XGetWindowProperty (x_current_display, requestor_window,
|
|
event->property, 0L,
|
|
10000000L, True, XA_STRING,
|
|
&type, &format, &items,
|
|
&bytes_left, &data);
|
|
if (result == Success && type == XA_STRING && format == 8
|
|
&& offset < MAX_SELECTION (x_current_display))
|
|
{
|
|
bcopy (data, return_string + offset, items);
|
|
offset += items;
|
|
}
|
|
XFree ((char *) data);
|
|
}
|
|
while (bytes_left);
|
|
|
|
return make_string (return_string, offset);
|
|
}
|
|
else /* Prepare incremental transfer. */
|
|
{
|
|
unsigned char *increment_value;
|
|
unsigned char *increment_ptr;
|
|
int total_size;
|
|
int *increment_nbytes = 0;
|
|
|
|
result = XGetWindowProperty (x_current_display, requestor_window,
|
|
selection, 0L, 10000000L, False,
|
|
event->property, &type, &format,
|
|
&items, &bytes_left,
|
|
(unsigned char **) &increment_nbytes);
|
|
if (result == Success)
|
|
{
|
|
XPropertyEvent property_event;
|
|
|
|
total_size = *increment_nbytes;
|
|
increment_value = (unsigned char *) alloca (total_size);
|
|
increment_ptr = increment_value;
|
|
|
|
XDeleteProperty (x_current_display, event->requestor,
|
|
event->property);
|
|
XFlush (x_current_display);
|
|
XFree ((char *) increment_nbytes);
|
|
|
|
do
|
|
{ /* NOTE: this blocks. */
|
|
XWindowEvent (x_current_display, requestor_window,
|
|
PropertyChangeMask,
|
|
(XEvent *) &property_event);
|
|
|
|
if (property_event.atom == selection
|
|
&& property_event.state == PropertyNewValue)
|
|
do
|
|
{
|
|
result = XGetWindowProperty (x_current_display,
|
|
requestor_window,
|
|
selection, 0L,
|
|
10000000L, True,
|
|
AnyPropertyType,
|
|
&type, &format,
|
|
&items, &bytes_left,
|
|
&data);
|
|
if (result == Success && type == XA_STRING
|
|
&& format == 8)
|
|
{
|
|
bcopy (data, increment_ptr, items);
|
|
increment_ptr += items;
|
|
}
|
|
}
|
|
while (bytes_left);
|
|
|
|
}
|
|
while (increment_ptr < (increment_value + total_size));
|
|
|
|
return make_string (increment_value,
|
|
(increment_ptr - increment_value));
|
|
}
|
|
}
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
void
|
|
syms_of_xselect ()
|
|
{
|
|
DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
|
|
"The value of emacs' last cut-string.");
|
|
Vx_selection_value = Qnil;
|
|
|
|
DEFVAR_LISP ("x-secondary-selection-value", &Vx_secondary_selection_value,
|
|
"The value of emacs' last secondary cut-string.");
|
|
Vx_secondary_selection_value = Qnil;
|
|
|
|
DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
|
|
"The string emacs last sent to the clipboard.");
|
|
Vx_clipboard_value = Qnil;
|
|
|
|
Qprimary = intern ("primary");
|
|
staticpro (&Qprimary);
|
|
Qsecondary = intern ("secondary");
|
|
staticpro (&Qsecondary);
|
|
Qclipboard = intern ("clipboard");
|
|
staticpro (&Qclipboard);
|
|
Qcut_buffer0 = intern ("cut-buffer0");
|
|
staticpro (&Qcut_buffer0);
|
|
|
|
defsubr (&Sx_own_selection);
|
|
defsubr (&Sx_selection_value);
|
|
}
|
|
#endif /* X11 */
|