1 /* X Selection processing for emacs
2 Copyright (C) 1990 Free Software Foundation.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
27 /* Macros for X Selections */
28 #define MAX_SELECTION(dpy) (((dpy)->max_request_size << 3) - 100)
29 #define SELECTION_LENGTH(len,format) ((len) * ((format) >> 3))
31 /* From keyboard.c. This is almost always the right timestamp for
32 ownership arbitration, since the last event was either the
33 keystroke bound to the command now running, or something else read
35 extern unsigned long last_event_timestamp;
37 /* t if a mouse button is depressed. */
38 extern Lisp_Object Vmouse_grabbed;
40 /* When emacs became the PRIMARY selection owner. */
41 Time x_begin_selection_own;
43 /* When emacs became the CLIPBOARD selection owner. */
44 Time x_begin_clipboard_own;
46 /* The value of the current CLIPBOARD selection. */
47 Lisp_Object Vx_clipboard_value;
49 /* The value of the current PRIMARY selection. */
50 Lisp_Object Vx_selection_value;
52 /* Emacs' selection property identifier. */
53 Atom Xatom_emacs_selection;
55 /* Clipboard selection atom. */
56 Atom Xatom_clipboard_selection;
61 /* Atom for indicating incremental selection transfer. */
62 Atom Xatom_incremental;
64 /* Atom for indicating multiple selection request list */
67 /* Atom for what targets emacs handles. */
70 /* Atom for indicating timstamp selection request */
73 /* Atom requesting we delete our selection. */
76 /* Selection magic. */
77 Atom Xatom_insert_selection;
79 /* Type of property for INSERT_SELECTION. */
82 /* More selection magic. */
83 Atom Xatom_insert_property;
85 /* Atom for indicating property type TEXT */
88 /* These are to handle incremental selection transfer. */
89 Window incr_requestor;
92 unsigned char *incr_value;
93 unsigned char *incr_ptr;
95 /* SELECTION OWNER CODE */
97 /* Become the selection owner and make our data the selection value.
98 If we are already the owner, merely change data and timestamp values.
99 This avoids generating SelectionClear events for ourselves. */
101 DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
102 1, 1, "sStore text for pasting: ",
103 "Stores string STRING for pasting in another X window.\n\
104 This is done with the X11 selection mechanism.")
106 register Lisp_Object string;
108 Window owner_window, selecting_window;
111 CHECK_STRING (string, 0);
114 selecting_window = selected_screen->display.x->window_desc;
116 if (EQ (Qnil, Vx_selection_value)) /* We are not the owner. */
118 event_time = last_event_timestamp;
119 XSetSelectionOwner (x_current_display, XA_PRIMARY,
120 selecting_window, event_time);
121 owner_window = XGetSelectionOwner (x_current_display, XA_PRIMARY);
123 if (owner_window != selecting_window)
126 error ("X error: could not acquire selection ownership");
130 x_begin_selection_own = event_time;
131 Vx_selection_value = string;
137 /* CLIPBOARD OWNERSHIP */
139 DEFUN ("x-own-clipboard", Fx_own_clipboard, Sx_own_clipboard,
140 1, 1, "sCLIPBOARD string: ",
141 "Assert X clipboard ownership with value STRING.")
143 register Lisp_Object string;
145 Window owner_window, selecting_window;
148 CHECK_STRING (string, 0);
151 selecting_window = selected_screen->display.x->window_desc;
153 if (EQ (Qnil, Vx_clipboard_value))
155 event_time = last_event_timestamp;
156 XSetSelectionOwner (x_current_display, Xatom_clipboard,
157 selecting_window, event_time);
158 owner_window = XGetSelectionOwner (x_current_display, Xatom_clipboard);
160 if (owner_window != selecting_window)
163 error ("X error: could not acquire selection ownership");
167 x_begin_clipboard_own = event_time;
168 Vx_clipboard_value = string;
174 /* Clear our selection ownership data, as some other client has
178 x_disown_selection (old_owner, selection, changed_owner_time)
181 Time changed_owner_time;
183 struct screen *s = x_window_to_screen (old_owner);
185 if (s) /* We are the owner */
187 if (selection == XA_PRIMARY)
189 x_begin_selection_own = 0;
190 Vx_selection_value = Qnil;
192 else if (selection == Xatom_clipboard)
194 x_begin_clipboard_own = 0;
195 Vx_clipboard_value = Qnil;
201 abort (); /* Inconsistent state. */
204 int x_selection_alloc_error;
205 int x_converting_selection;
207 /* Reply to some client's request for our selection data. Data is
208 placed in a propery supplied by the requesting window.
210 If the data exceeds the maximum amount the server can send,
211 then prepare to send it incrementally, and reply to the client with
212 the total size of the data.
214 But first, check for all the other crufty stuff we could get. */
217 x_answer_selection_request (event)
218 XSelectionRequestEvent event;
221 Lisp_Object selection_value;
223 int format = 8; /* We have only byte sized (text) data. */
225 evt.type = SelectionNotify; /* Construct reply event */
226 evt.display = event.display;
227 evt.requestor = event.requestor;
228 evt.selection = event.selection;
229 evt.time = event.time;
230 evt.target = event.target;
232 if (event.selection == XA_PRIMARY)
234 emacs_own_time = x_begin_selection_own;
235 selection_value = Vx_selection_value;
237 else if (event.selection == Xatom_clipboard)
239 emacs_own_time = x_begin_clipboard_own;
240 selection_value = Vx_clipboard_value;
245 if (event.time != CurrentTime
246 && event.time < emacs_own_time)
250 if (event.property == None) /* obsolete client */
251 evt.property = event.target;
253 evt.property = event.property;
256 if (event.target == Xatom_targets) /* Send List of target atoms */
259 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
263 unsigned long items, bytes_left;
267 if (event.property == 0 /* 0 == NULL */
268 || event.property == None)
271 result = XGetWindowProperty (event.display, event.requestor,
272 event.property, 0L, 10000000L,
273 True, Xatom_pair, &type, &return_format,
274 &items, &bytes_left, &data);
276 if (result == Success && type == Xatom_pair)
277 for (i = items; i > 0; i--)
279 /* Convert each element of the list. */
282 (void) XSendEvent (x_current_display, evt.requestor, False,
283 0L, (XEvent *) &evt);
286 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
288 if (! emacs_own_time)
292 XChangeProperty (evt.display, evt.requestor, evt.property,
293 evt.target, format, PropModeReplace,
294 (unsigned char *) &emacs_own_time, format);
297 else if (event.target == Xatom_delete) /* Delete our selection. */
299 if (EQ (Qnil, selection_value))
302 x_disown_selection (event.owner, event.selection, event.time);
304 /* Now return property of type NULL, length 0. */
305 XChangeProperty (event.display, event.requestor, event.property,
306 0, format, PropModeReplace, (unsigned char *) 0, 0);
309 else if (event.target == Xatom_insert_selection)
313 unsigned long items, bytes_left;
315 int result = XGetWindowProperty (event.display, event.requestor,
316 event.property, 0L, 10000000L,
317 True, Xatom_pair, &type, &return_format,
318 &items, &bytes_left, &data);
319 if (result == Success && type == Xatom_pair)
321 /* Convert the first atom to (a selection) to the target
322 indicated by the second atom. */
325 else if (event.target == Xatom_insert_property)
329 unsigned long items, bytes_left;
331 int result = XGetWindowProperty (event.display, event.requestor,
332 event.property, 0L, 10000000L,
333 True, XA_STRING, &type, &return_format,
334 &items, &bytes_left, &data);
336 if (result == Success && type == XA_STRING && return_format == 8)
338 if (event.selection == Xatom_emacs_selection)
339 Vx_selection_value = make_string (data);
340 else if (event.selection == Xatom_clipboard_selection)
341 Vx_clipboard_value = make_string (data);
348 else if ((event.target == Xatom_text
349 || event.target == XA_STRING))
351 int size = XSTRING (selection_value)->size;
352 unsigned char *data = XSTRING (selection_value)->data;
354 if (EQ (Qnil, selection_value))
357 /* Place data on requestor window's property. */
358 if (SELECTION_LENGTH (size, format)
359 <= MAX_SELECTION (x_current_display))
361 x_converting_selection = 1;
362 XChangeProperty (evt.display, evt.requestor, evt.property,
363 evt.target, format, PropModeReplace,
365 if (x_selection_alloc_error)
367 x_selection_alloc_error = 0;
370 x_converting_selection = 0;
372 else /* Send incrementally */
374 evt.target = Xatom_incremental;
375 incr_requestor = evt.requestor;
376 incr_property = evt.property;
377 x_converting_selection = 1;
379 /* Need to handle Alloc errors on these requests. */
380 XChangeProperty (evt.display, incr_requestor, incr_property,
381 Xatom_incremental, 32,
383 (unsigned char *) &size, 1);
384 if (x_selection_alloc_error)
386 x_selection_alloc_error = 0;
387 x_converting_selection = 0;
389 /* Now abort the send. */
396 /* Ask for notification when requestor deletes property. */
397 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
399 /* If we're sending incrementally, perhaps block here
406 /* Don't do this if there was an Alloc error: abort the transfer
408 (void) XSendEvent (x_current_display, evt.requestor, False,
409 0L, (XEvent *) &evt);
412 /* Send an increment of selection data in response to a PropertyNotify event.
413 The increment is placed in a property on the requestor's window.
414 When the requestor has processed the increment, it deletes the property,
415 which sends us another PropertyNotify event.
417 When there is no more data to send, we send a zero-length increment. */
420 x_send_incremental (event)
421 XPropertyEvent event;
424 && incr_requestor == event.window
425 && incr_property == event.atom
426 && event.state == PropertyDelete)
429 int length = MAX_SELECTION (x_current_display);
430 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
432 if (length > bytes_left) /* Also sends 0 len when finished. */
434 XChangeProperty (x_current_display, incr_requestor,
435 incr_property, XA_STRING, format,
436 PropModeAppend, incr_ptr, length);
437 if (x_selection_alloc_error)
439 x_selection_alloc_error = 0;
440 x_converting_selection = 0;
441 /* Abandon the transmission. */
447 { /* Everything's sent */
448 XSelectInput (x_current_display, incr_requestor, 0L);
449 incr_requestor = (Window) 0;
450 incr_property = (Atom) 0;
452 incr_value = (unsigned char *) 0;
453 incr_ptr = (unsigned char *) 0;
454 x_converting_selection = 0;
459 /* SELECTION REQUESTOR CODE */
461 /* Predicate function used to match a requested event. */
464 XCheckSelectionEvent (dpy, event, window)
469 if (event->type == SelectionNotify)
470 if (event->xselection.requestor == (Window) window)
476 /* Request the selection value from the owner. If we are the owner,
477 simply return our selection value. If we are not the owner, this
478 will block until all of the data has arrived. */
480 DEFUN ("x-get-selection", Fx_get_selection, Sx_get_selection, 0, 0, 0,
481 "Return text selected from some X window.\n\
482 This is done with the X11 selection mechanism.")
487 Time requestor_time; /* Timestamp of selection request. */
488 Window requestor_window;
490 if (!EQ (Qnil, Vx_selection_value)) /* We are the owner */
491 return Vx_selection_value;
494 requestor_time = last_event_timestamp;
495 requestor_window = selected_screen->display.x->window_desc;
496 XConvertSelection (x_current_display, XA_PRIMARY, XA_STRING,
497 Xatom_emacs_selection, requestor_window, requestor_time);
498 XIfEvent (x_current_display,
500 XCheckSelectionEvent,
501 (char *) requestor_window);
502 val = x_selection_arrival (&event, requestor_window, requestor_time);
508 /* Request the clipboard contents from its owner. If we are the owner,
509 simply return the clipboard string. */
511 DEFUN ("x-get-clipboard", Fx_get_clipboard, Sx_get_clipboard, 0, 0, 0,
512 "Return text pasted to the clipboard.\n\
513 This is done with the X11 selection mechanism.")
518 Time requestor_time; /* Timestamp of selection request. */
519 Window requestor_window;
521 if (!EQ (Qnil, Vx_clipboard_value)) /* We are the owner */
522 return Vx_selection_value;
525 requestor_time = last_event_timestamp;
526 requestor_window = selected_screen->display.x->window_desc;
527 XConvertSelection (x_current_display, Xatom_clipboard, XA_STRING,
528 Xatom_clipboard_selection,
529 requestor_window, requestor_time);
530 XIfEvent (x_current_display,
532 XCheckSelectionEvent,
533 (char *) requestor_window);
534 val = x_selection_arrival (&event, requestor_window, requestor_time);
541 x_selection_arrival (event, requestor_window, requestor_time)
542 register XSelectionEvent *event;
543 Window requestor_window;
547 Atom type, selection;
550 unsigned long bytes_left;
551 unsigned char *data = 0;
554 if (event->selection == XA_PRIMARY)
555 selection = Xatom_emacs_selection;
556 else if (event->selection == Xatom_clipboard)
557 selection = Xatom_clipboard_selection;
561 if (event->requestor == requestor_window
562 && event->time == requestor_time
563 && event->property != None)
564 if (event->target != Xatom_incremental)
566 unsigned char *return_string =
567 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
571 result = XGetWindowProperty (x_current_display,
574 10000000L, True, XA_STRING,
575 &type, &format, &items,
577 if (result == Success && type == XA_STRING && format == 8
578 && offset < MAX_SELECTION (x_current_display))
580 bcopy (data, return_string + offset, items);
583 XFree ((char *) data);
587 return make_string (return_string, offset);
589 else /* Prepare incremental transfer. */
591 unsigned char *increment_value;
592 unsigned char *increment_ptr;
594 int *increment_nbytes = 0;
596 result = XGetWindowProperty (x_current_display, requestor_window,
597 selection, 0L, 10000000L, False,
598 event->property, &type, &format,
600 (unsigned char **) &increment_nbytes);
601 if (result == Success)
603 XPropertyEvent property_event;
605 total_size = *increment_nbytes;
606 increment_value = (unsigned char *) alloca (total_size);
607 increment_ptr = increment_value;
609 XDeleteProperty (x_current_display, event->requestor,
611 XFlush (x_current_display);
612 XFree ((char *) increment_nbytes);
615 { /* NOTE: this blocks. */
616 XWindowEvent (x_current_display, requestor_window,
618 (XEvent *) &property_event);
620 if (property_event.atom == selection
621 && property_event.state == PropertyNewValue)
624 result = XGetWindowProperty (x_current_display,
632 if (result == Success && type == XA_STRING
635 bcopy (data, increment_ptr, items);
636 increment_ptr += items;
642 while (increment_ptr < (increment_value + total_size));
644 return make_string (increment_value,
645 (increment_ptr - increment_value));
655 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
656 "The value of emacs' last cut-string.");
657 Vx_selection_value = Qnil;
659 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
660 "The string emacs last sent to the clipboard.");
661 Vx_clipboard_value = Qnil;
663 defsubr (&Sx_own_selection);
664 defsubr (&Sx_get_selection);
665 defsubr (&Sx_own_clipboard);
666 defsubr (&Sx_get_clipboard);