1 /* X Selection processing for emacs
2 Copyright (C) 1990, 1992 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 2, 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. */
28 /* Macros for X Selections */
29 #define MAX_SELECTION(dpy) (((dpy)->max_request_size << 2) - 100)
30 #define SELECTION_LENGTH(len,format) ((len) * ((format) >> 2))
32 /* The timestamp of the last input event we received from the X server. */
33 unsigned long last_event_timestamp;
35 /* t if a mouse button is depressed. */
36 extern Lisp_Object Vmouse_grabbed;
38 /* When emacs became the PRIMARY selection owner. */
39 Time x_begin_selection_own;
41 /* When emacs became the SECONDARY selection owner. */
42 Time x_begin_secondary_selection_own;
44 /* When emacs became the CLIPBOARD selection owner. */
45 Time x_begin_clipboard_own;
47 /* The value of the current CLIPBOARD selection. */
48 Lisp_Object Vx_clipboard_value;
50 /* The value of the current PRIMARY selection. */
51 Lisp_Object Vx_selection_value;
53 /* The value of the current SECONDARY selection. */
54 Lisp_Object Vx_secondary_selection_value;
56 /* Types of selections we may make. Note that Qcut_buffer0 isn't really
57 a selection, but it acts like one for the sake of Fx_own_selection and
58 Fx_selection_value. */
59 Lisp_Object Qprimary, Qsecondary, Qclipboard, Qcut_buffer0;
61 /* Emacs' selection property identifiers. */
62 Atom Xatom_emacs_selection;
63 Atom Xatom_emacs_secondary_selection;
65 /* Clipboard selection atom. */
66 Atom Xatom_clipboard_selection;
71 /* Atom for indicating incremental selection transfer. */
72 Atom Xatom_incremental;
74 /* Atom for indicating multiple selection request list */
77 /* Atom for what targets emacs handles. */
80 /* Atom for indicating timstamp selection request */
83 /* Atom requesting we delete our selection. */
86 /* Selection magic. */
87 Atom Xatom_insert_selection;
89 /* Type of property for INSERT_SELECTION. */
92 /* More selection magic. */
93 Atom Xatom_insert_property;
95 /* Atom for indicating property type TEXT */
98 /* Kinds of protocol things we may receive. */
99 Atom Xatom_wm_take_focus;
100 Atom Xatom_wm_save_yourself;
101 Atom Xatom_wm_delete_window;
103 /* Communication with window managers. */
104 Atom Xatom_wm_protocols;
106 /* These are to handle incremental selection transfer. */
107 Window incr_requestor;
110 unsigned char *incr_value;
111 unsigned char *incr_ptr;
113 /* SELECTION OWNER CODE */
116 /* Request selection ownership if we do not already have it. */
119 own_selection (selection_type, time)
123 Window owner_window, selecting_window;
125 if ((EQ (selection_type, Qprimary) && !NILP (Vx_selection_value))
126 || ((EQ (selection_type, Qsecondary)) && !NILP (Vx_secondary_selection_value))
127 || ((EQ (selection_type, Qclipboard)) && !NILP (Vx_clipboard_value)))
130 selecting_window = selected_frame->display.x->window_desc;
131 XSetSelectionOwner (x_current_display, selection_type,
132 selecting_window, time);
133 owner_window = XGetSelectionOwner (x_current_display, selection_type);
135 if (owner_window != selecting_window)
141 /* Become the selection owner and make our data the selection value.
142 If we are already the owner, merely change data and timestamp values.
143 This avoids generating SelectionClear events for ourselves. */
145 DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
147 "Make STRING the selection value. Default is the primary selection,\n\
148 but optional second argument TYPE may specify secondary or clipboard.\n\
150 TYPE may also be cut-buffer0, indicating that Emacs should set the X\n\
151 cut buffer 0 to STRING. This is for compatibility with older X\n\
152 applications which still use the cut buffers; new applications should\n\
155 register Lisp_Object string, type;
159 Time event_time = last_event_timestamp;
160 CHECK_STRING (string, 0);
164 if (NILP (type) || EQ (type, Qprimary))
167 if (own_selection (XA_PRIMARY, event_time))
169 x_begin_selection_own = event_time;
170 val = Vx_selection_value = string;
174 else if (EQ (type, Qsecondary))
177 if (own_selection (XA_SECONDARY, event_time))
179 x_begin_secondary_selection_own = event_time;
180 val = Vx_secondary_selection_value = string;
184 else if (EQ (type, Qclipboard))
187 if (own_selection (Xatom_clipboard, event_time))
189 x_begin_clipboard_own = event_time;
190 val = Vx_clipboard_value = string;
194 else if (EQ (type, Qcut_buffer0))
197 XStoreBytes (x_current_display,
198 (char *) XSTRING (string)->data,
199 XSTRING (string)->size);
204 error ("Invalid X selection type");
209 /* Clear our selection ownership data, as some other client has
213 x_disown_selection (old_owner, selection, changed_owner_time)
216 Time changed_owner_time;
218 struct frame *s = x_window_to_frame (old_owner);
220 if (s) /* We are the owner */
222 if (selection == XA_PRIMARY)
224 x_begin_selection_own = 0;
225 Vx_selection_value = Qnil;
227 else if (selection == XA_SECONDARY)
229 x_begin_secondary_selection_own = 0;
230 Vx_secondary_selection_value = Qnil;
232 else if (selection == Xatom_clipboard)
234 x_begin_clipboard_own = 0;
235 Vx_clipboard_value = Qnil;
241 abort (); /* Inconsistent state. */
244 int x_selection_alloc_error;
245 int x_converting_selection;
247 /* Reply to some client's request for our selection data. Data is
248 placed in a property supplied by the requesting window.
250 If the data exceeds the maximum amount the server can send,
251 then prepare to send it incrementally, and reply to the client with
252 the total size of the data.
254 But first, check for all the other crufty stuff we could get. */
257 x_answer_selection_request (event)
258 XSelectionRequestEvent event;
261 Lisp_Object selection_value;
263 int format = 8; /* We have only byte sized (text) data. */
265 evt.type = SelectionNotify; /* Construct reply event */
266 evt.display = event.display;
267 evt.requestor = event.requestor;
268 evt.selection = event.selection;
269 evt.time = event.time;
270 evt.target = event.target;
272 if (event.selection == XA_PRIMARY)
274 emacs_own_time = x_begin_selection_own;
275 selection_value = Vx_selection_value;
277 else if (event.selection == XA_SECONDARY)
279 emacs_own_time = x_begin_secondary_selection_own;
280 selection_value = Vx_secondary_selection_value;
282 else if (event.selection == Xatom_clipboard)
284 emacs_own_time = x_begin_clipboard_own;
285 selection_value = Vx_clipboard_value;
290 if (event.time != CurrentTime
291 && event.time < emacs_own_time)
295 if (event.property == None) /* obsolete client */
296 evt.property = event.target;
298 evt.property = event.property;
301 if (event.target == Xatom_targets) /* Send List of target atoms */
304 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
308 unsigned long items, bytes_left;
312 if (event.property == 0 /* 0 == NILP */
313 || event.property == None)
316 result = XGetWindowProperty (event.display, event.requestor,
317 event.property, 0L, 10000000L,
318 True, Xatom_pair, &type, &return_format,
319 &items, &bytes_left, &data);
321 if (result == Success && type == Xatom_pair)
322 for (i = items; i > 0; i--)
324 /* Convert each element of the list. */
327 (void) XSendEvent (x_current_display, evt.requestor, False,
328 0L, (XEvent *) &evt);
331 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
333 if (! emacs_own_time)
337 XChangeProperty (evt.display, evt.requestor, evt.property,
338 evt.target, format, PropModeReplace,
339 (unsigned char *) &emacs_own_time, 1);
342 else if (event.target == Xatom_delete) /* Delete our selection. */
344 if (EQ (Qnil, selection_value))
347 x_disown_selection (event.owner, event.selection, event.time);
349 /* Now return property of type NILP, length 0. */
350 XChangeProperty (event.display, event.requestor, event.property,
351 0, format, PropModeReplace, (unsigned char *) 0, 0);
354 else if (event.target == Xatom_insert_selection)
358 unsigned long items, bytes_left;
360 int result = XGetWindowProperty (event.display, event.requestor,
361 event.property, 0L, 10000000L,
362 True, Xatom_pair, &type, &return_format,
363 &items, &bytes_left, &data);
364 if (result == Success && type == Xatom_pair)
366 /* Convert the first atom to (a selection) to the target
367 indicated by the second atom. */
370 else if (event.target == Xatom_insert_property)
374 unsigned long items, bytes_left;
376 int result = XGetWindowProperty (event.display, event.requestor,
377 event.property, 0L, 10000000L,
378 True, XA_STRING, &type, &return_format,
379 &items, &bytes_left, &data);
381 if (result == Success && type == XA_STRING && return_format == 8)
383 if (event.selection == Xatom_emacs_selection)
384 Vx_selection_value = make_string (data);
385 else if (event.selection == Xatom_emacs_secondary_selection)
386 Vx_secondary_selection_value = make_string (data);
387 else if (event.selection == Xatom_clipboard_selection)
388 Vx_clipboard_value = make_string (data);
395 else if ((event.target == Xatom_text
396 || event.target == XA_STRING))
398 int size = XSTRING (selection_value)->size;
399 unsigned char *data = XSTRING (selection_value)->data;
401 if (EQ (Qnil, selection_value))
404 /* Place data on requestor window's property. */
405 if (SELECTION_LENGTH (size, format)
406 <= MAX_SELECTION (x_current_display))
408 x_converting_selection = 1;
409 XChangeProperty (evt.display, evt.requestor, evt.property,
410 evt.target, format, PropModeReplace,
412 if (x_selection_alloc_error)
414 x_selection_alloc_error = 0;
417 x_converting_selection = 0;
419 else /* Send incrementally */
421 evt.target = Xatom_incremental;
422 incr_requestor = evt.requestor;
423 incr_property = evt.property;
424 x_converting_selection = 1;
426 /* Need to handle Alloc errors on these requests. */
427 XChangeProperty (evt.display, incr_requestor, incr_property,
428 Xatom_incremental, 32,
430 (unsigned char *) &size, 1);
431 if (x_selection_alloc_error)
433 x_selection_alloc_error = 0;
434 x_converting_selection = 0;
436 /* Now abort the send. */
443 /* Ask for notification when requestor deletes property. */
444 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
446 /* If we're sending incrementally, perhaps block here
453 /* Don't do this if there was an Alloc error: abort the transfer
455 (void) XSendEvent (x_current_display, evt.requestor, False,
456 0L, (XEvent *) &evt);
459 /* Send an increment of selection data in response to a PropertyNotify event.
460 The increment is placed in a property on the requestor's window.
461 When the requestor has processed the increment, it deletes the property,
462 which sends us another PropertyNotify event.
464 When there is no more data to send, we send a zero-length increment. */
467 x_send_incremental (event)
468 XPropertyEvent event;
471 && incr_requestor == event.window
472 && incr_property == event.atom
473 && event.state == PropertyDelete)
476 int length = MAX_SELECTION (x_current_display);
477 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
479 if (length > bytes_left) /* Also sends 0 len when finished. */
481 XChangeProperty (x_current_display, incr_requestor,
482 incr_property, XA_STRING, format,
483 PropModeAppend, incr_ptr, length);
484 if (x_selection_alloc_error)
486 x_selection_alloc_error = 0;
487 x_converting_selection = 0;
488 /* Abandon the transmission. */
494 { /* Everything's sent */
495 XSelectInput (x_current_display, incr_requestor, 0L);
496 incr_requestor = (Window) 0;
497 incr_property = (Atom) 0;
499 incr_value = (unsigned char *) 0;
500 incr_ptr = (unsigned char *) 0;
501 x_converting_selection = 0;
506 /* SELECTION REQUESTOR CODE */
508 /* Predicate function used to match a requested event. */
511 XCheckSelectionEvent (dpy, event, window)
516 if (event->type == SelectionNotify)
517 if (event->xselection.requestor == (Window) window)
523 /* Request a selection value from its owner. This will block until
524 all the data is arrived. */
527 get_selection_value (type)
532 Time requestor_time; /* Timestamp of selection request. */
533 Window requestor_window;
536 requestor_time = last_event_timestamp;
537 requestor_window = selected_frame->display.x->window_desc;
538 XConvertSelection (x_current_display, type, XA_STRING,
539 Xatom_emacs_selection, requestor_window, requestor_time);
540 XIfEvent (x_current_display,
542 XCheckSelectionEvent,
543 (char *) requestor_window);
544 val = x_selection_arrival (&event, requestor_window, requestor_time);
550 /* Request a selection value from the owner. If we are the owner,
551 simply return our selection value. If we are not the owner, this
552 will block until all of the data has arrived. */
554 DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
556 "Return the value of one of the selections. Default is the primary\n\
557 selection, but optional argument TYPE may specify secondary or clipboard.")
559 register Lisp_Object type;
563 if (NILP (type) || EQ (type, Qprimary))
565 if (!NILP (Vx_selection_value))
566 return Vx_selection_value;
568 return get_selection_value (XA_PRIMARY);
570 else if (EQ (type, Qsecondary))
572 if (!NILP (Vx_secondary_selection_value))
573 return Vx_secondary_selection_value;
575 return get_selection_value (XA_SECONDARY);
577 else if (EQ (type, Qclipboard))
579 if (!NILP (Vx_clipboard_value))
580 return Vx_clipboard_value;
582 return get_selection_value (Xatom_clipboard);
584 else if (EQ (type, Qcut_buffer0))
591 data = XFetchBytes (x_current_display, &size);
595 string = make_string (data, size);
601 error ("Invalid X selection type");
605 x_selection_arrival (event, requestor_window, requestor_time)
606 register XSelectionEvent *event;
607 Window requestor_window;
611 Atom type, selection;
614 unsigned long bytes_left;
615 unsigned char *data = 0;
618 if (event->selection == XA_PRIMARY)
619 selection = Xatom_emacs_selection;
620 else if (event->selection == XA_SECONDARY)
621 selection = Xatom_emacs_secondary_selection;
622 else if (event->selection == Xatom_clipboard)
623 selection = Xatom_clipboard_selection;
627 if (event->requestor == requestor_window
628 && event->time == requestor_time
629 && event->property != None)
630 if (event->target != Xatom_incremental)
632 unsigned char *return_string =
633 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
637 result = XGetWindowProperty (x_current_display, requestor_window,
639 10000000L, True, XA_STRING,
640 &type, &format, &items,
642 if (result == Success && type == XA_STRING && format == 8
643 && offset < MAX_SELECTION (x_current_display))
645 bcopy (data, return_string + offset, items);
648 XFree ((char *) data);
652 return make_string (return_string, offset);
654 else /* Prepare incremental transfer. */
656 unsigned char *increment_value;
657 unsigned char *increment_ptr;
659 int *increment_nbytes = 0;
661 result = XGetWindowProperty (x_current_display, requestor_window,
662 selection, 0L, 10000000L, False,
663 event->property, &type, &format,
665 (unsigned char **) &increment_nbytes);
666 if (result == Success)
668 XPropertyEvent property_event;
670 total_size = *increment_nbytes;
671 increment_value = (unsigned char *) alloca (total_size);
672 increment_ptr = increment_value;
674 XDeleteProperty (x_current_display, event->requestor,
676 XFlush (x_current_display);
677 XFree ((char *) increment_nbytes);
680 { /* NOTE: this blocks. */
681 XWindowEvent (x_current_display, requestor_window,
683 (XEvent *) &property_event);
685 if (property_event.atom == selection
686 && property_event.state == PropertyNewValue)
689 result = XGetWindowProperty (x_current_display,
697 if (result == Success && type == XA_STRING
700 bcopy (data, increment_ptr, items);
701 increment_ptr += items;
707 while (increment_ptr < (increment_value + total_size));
709 return make_string (increment_value,
710 (increment_ptr - increment_value));
720 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
721 "The value of emacs' last cut-string.");
722 Vx_selection_value = Qnil;
724 DEFVAR_LISP ("x-secondary-selection-value", &Vx_secondary_selection_value,
725 "The value of emacs' last secondary cut-string.");
726 Vx_secondary_selection_value = Qnil;
728 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
729 "The string emacs last sent to the clipboard.");
730 Vx_clipboard_value = Qnil;
732 Qprimary = intern ("primary");
733 staticpro (&Qprimary);
734 Qsecondary = intern ("secondary");
735 staticpro (&Qsecondary);
736 Qclipboard = intern ("clipboard");
737 staticpro (&Qclipboard);
738 Qcut_buffer0 = intern ("cut-buffer0");
739 staticpro (&Qcut_buffer0);
741 defsubr (&Sx_own_selection);
742 defsubr (&Sx_selection_value);