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))
196 /* DECwindows and some other servers don't seem to like setting
197 properties to values larger than about 20k. For very large
198 values, they signal an error, but for intermediate values
199 they just seem to hang.
201 We could just truncate the request, but it's better to let
202 the user know that the strategy he/she's using isn't going to
203 work than to have it work partially, but incorrectly. */
205 if (XSTRING (string)->size > MAX_SELECTION (x_current_display))
207 XStoreBytes (x_current_display, (char *) 0, 0);
212 XStoreBytes (x_current_display,
213 (char *) XSTRING (string)->data,
214 XSTRING (string)->size);
220 error ("Invalid X selection type");
225 /* Clear our selection ownership data, as some other client has
229 x_disown_selection (old_owner, selection, changed_owner_time)
232 Time changed_owner_time;
234 struct frame *s = x_window_to_frame (old_owner);
236 if (s) /* We are the owner */
238 if (selection == XA_PRIMARY)
240 x_begin_selection_own = 0;
241 Vx_selection_value = Qnil;
243 else if (selection == XA_SECONDARY)
245 x_begin_secondary_selection_own = 0;
246 Vx_secondary_selection_value = Qnil;
248 else if (selection == Xatom_clipboard)
250 x_begin_clipboard_own = 0;
251 Vx_clipboard_value = Qnil;
257 abort (); /* Inconsistent state. */
260 int x_selection_alloc_error;
261 int x_converting_selection;
263 /* Reply to some client's request for our selection data. Data is
264 placed in a property supplied by the requesting window.
266 If the data exceeds the maximum amount the server can send,
267 then prepare to send it incrementally, and reply to the client with
268 the total size of the data.
270 But first, check for all the other crufty stuff we could get. */
273 x_answer_selection_request (event)
274 XSelectionRequestEvent event;
277 Lisp_Object selection_value;
279 int format = 8; /* We have only byte sized (text) data. */
281 evt.type = SelectionNotify; /* Construct reply event */
282 evt.display = event.display;
283 evt.requestor = event.requestor;
284 evt.selection = event.selection;
285 evt.time = event.time;
286 evt.target = event.target;
288 if (event.selection == XA_PRIMARY)
290 emacs_own_time = x_begin_selection_own;
291 selection_value = Vx_selection_value;
293 else if (event.selection == XA_SECONDARY)
295 emacs_own_time = x_begin_secondary_selection_own;
296 selection_value = Vx_secondary_selection_value;
298 else if (event.selection == Xatom_clipboard)
300 emacs_own_time = x_begin_clipboard_own;
301 selection_value = Vx_clipboard_value;
306 if (event.time != CurrentTime
307 && event.time < emacs_own_time)
311 if (event.property == None) /* obsolete client */
312 evt.property = event.target;
314 evt.property = event.property;
317 if (event.target == Xatom_targets) /* Send List of target atoms */
320 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
324 unsigned long items, bytes_left;
328 if (event.property == 0 /* 0 == NILP */
329 || event.property == None)
332 result = XGetWindowProperty (event.display, event.requestor,
333 event.property, 0L, 10000000L,
334 True, Xatom_pair, &type, &return_format,
335 &items, &bytes_left, &data);
337 if (result == Success && type == Xatom_pair)
338 for (i = items; i > 0; i--)
340 /* Convert each element of the list. */
343 (void) XSendEvent (x_current_display, evt.requestor, False,
344 0L, (XEvent *) &evt);
347 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
349 if (! emacs_own_time)
353 XChangeProperty (evt.display, evt.requestor, evt.property,
354 evt.target, format, PropModeReplace,
355 (unsigned char *) &emacs_own_time, 1);
358 else if (event.target == Xatom_delete) /* Delete our selection. */
360 if (EQ (Qnil, selection_value))
363 x_disown_selection (event.owner, event.selection, event.time);
365 /* Now return property of type NILP, length 0. */
366 XChangeProperty (event.display, event.requestor, event.property,
367 0, format, PropModeReplace, (unsigned char *) 0, 0);
370 else if (event.target == Xatom_insert_selection)
374 unsigned long items, bytes_left;
376 int result = XGetWindowProperty (event.display, event.requestor,
377 event.property, 0L, 10000000L,
378 True, Xatom_pair, &type, &return_format,
379 &items, &bytes_left, &data);
380 if (result == Success && type == Xatom_pair)
382 /* Convert the first atom to (a selection) to the target
383 indicated by the second atom. */
386 else if (event.target == Xatom_insert_property)
390 unsigned long items, bytes_left;
392 int result = XGetWindowProperty (event.display, event.requestor,
393 event.property, 0L, 10000000L,
394 True, XA_STRING, &type, &return_format,
395 &items, &bytes_left, &data);
397 if (result == Success && type == XA_STRING && return_format == 8)
399 if (event.selection == Xatom_emacs_selection)
400 Vx_selection_value = make_string (data);
401 else if (event.selection == Xatom_emacs_secondary_selection)
402 Vx_secondary_selection_value = make_string (data);
403 else if (event.selection == Xatom_clipboard_selection)
404 Vx_clipboard_value = make_string (data);
411 else if ((event.target == Xatom_text
412 || event.target == XA_STRING))
414 int size = XSTRING (selection_value)->size;
415 unsigned char *data = XSTRING (selection_value)->data;
417 if (EQ (Qnil, selection_value))
420 /* Place data on requestor window's property. */
421 if (SELECTION_LENGTH (size, format)
422 <= MAX_SELECTION (x_current_display))
424 x_converting_selection = 1;
425 XChangeProperty (evt.display, evt.requestor, evt.property,
426 evt.target, format, PropModeReplace,
428 if (x_selection_alloc_error)
430 x_selection_alloc_error = 0;
433 x_converting_selection = 0;
435 else /* Send incrementally */
437 evt.target = Xatom_incremental;
438 incr_requestor = evt.requestor;
439 incr_property = evt.property;
440 x_converting_selection = 1;
442 /* Need to handle Alloc errors on these requests. */
443 XChangeProperty (evt.display, incr_requestor, incr_property,
444 Xatom_incremental, 32,
446 (unsigned char *) &size, 1);
447 if (x_selection_alloc_error)
449 x_selection_alloc_error = 0;
450 x_converting_selection = 0;
452 /* Now abort the send. */
459 /* Ask for notification when requestor deletes property. */
460 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
462 /* If we're sending incrementally, perhaps block here
469 /* Don't do this if there was an Alloc error: abort the transfer
471 (void) XSendEvent (x_current_display, evt.requestor, False,
472 0L, (XEvent *) &evt);
475 /* Send an increment of selection data in response to a PropertyNotify event.
476 The increment is placed in a property on the requestor's window.
477 When the requestor has processed the increment, it deletes the property,
478 which sends us another PropertyNotify event.
480 When there is no more data to send, we send a zero-length increment. */
483 x_send_incremental (event)
484 XPropertyEvent event;
487 && incr_requestor == event.window
488 && incr_property == event.atom
489 && event.state == PropertyDelete)
492 int length = MAX_SELECTION (x_current_display);
493 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
495 if (length > bytes_left) /* Also sends 0 len when finished. */
497 XChangeProperty (x_current_display, incr_requestor,
498 incr_property, XA_STRING, format,
499 PropModeAppend, incr_ptr, length);
500 if (x_selection_alloc_error)
502 x_selection_alloc_error = 0;
503 x_converting_selection = 0;
504 /* Abandon the transmission. */
510 { /* Everything's sent */
511 XSelectInput (x_current_display, incr_requestor, 0L);
512 incr_requestor = (Window) 0;
513 incr_property = (Atom) 0;
515 incr_value = (unsigned char *) 0;
516 incr_ptr = (unsigned char *) 0;
517 x_converting_selection = 0;
522 /* SELECTION REQUESTOR CODE */
524 /* Predicate function used to match a requested event. */
527 XCheckSelectionEvent (dpy, event, window)
532 if (event->type == SelectionNotify)
533 if (event->xselection.requestor == (Window) window)
539 /* Request a selection value from its owner. This will block until
540 all the data is arrived. */
543 get_selection_value (type)
548 Time requestor_time; /* Timestamp of selection request. */
549 Window requestor_window;
552 requestor_time = last_event_timestamp;
553 requestor_window = selected_frame->display.x->window_desc;
554 XConvertSelection (x_current_display, type, XA_STRING,
555 Xatom_emacs_selection, requestor_window, requestor_time);
556 XIfEvent (x_current_display,
558 XCheckSelectionEvent,
559 (char *) requestor_window);
560 val = x_selection_arrival (&event, requestor_window, requestor_time);
566 /* Request a selection value from the owner. If we are the owner,
567 simply return our selection value. If we are not the owner, this
568 will block until all of the data has arrived. */
570 DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
572 "Return the value of one of the selections. Default is the primary\n\
573 selection, but optional argument TYPE may specify secondary or clipboard.")
575 register Lisp_Object type;
579 if (NILP (type) || EQ (type, Qprimary))
581 if (!NILP (Vx_selection_value))
582 return Vx_selection_value;
584 return get_selection_value (XA_PRIMARY);
586 else if (EQ (type, Qsecondary))
588 if (!NILP (Vx_secondary_selection_value))
589 return Vx_secondary_selection_value;
591 return get_selection_value (XA_SECONDARY);
593 else if (EQ (type, Qclipboard))
595 if (!NILP (Vx_clipboard_value))
596 return Vx_clipboard_value;
598 return get_selection_value (Xatom_clipboard);
600 else if (EQ (type, Qcut_buffer0))
607 data = XFetchBytes (x_current_display, &size);
611 string = make_string (data, size);
617 error ("Invalid X selection type");
621 x_selection_arrival (event, requestor_window, requestor_time)
622 register XSelectionEvent *event;
623 Window requestor_window;
627 Atom type, selection;
630 unsigned long bytes_left;
631 unsigned char *data = 0;
634 if (event->selection == XA_PRIMARY)
635 selection = Xatom_emacs_selection;
636 else if (event->selection == XA_SECONDARY)
637 selection = Xatom_emacs_secondary_selection;
638 else if (event->selection == Xatom_clipboard)
639 selection = Xatom_clipboard_selection;
643 if (event->requestor == requestor_window
644 && event->time == requestor_time
645 && event->property != None)
646 if (event->target != Xatom_incremental)
648 unsigned char *return_string =
649 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
653 result = XGetWindowProperty (x_current_display, requestor_window,
655 10000000L, True, XA_STRING,
656 &type, &format, &items,
658 if (result == Success && type == XA_STRING && format == 8
659 && offset < MAX_SELECTION (x_current_display))
661 bcopy (data, return_string + offset, items);
664 XFree ((char *) data);
668 return make_string (return_string, offset);
670 else /* Prepare incremental transfer. */
672 unsigned char *increment_value;
673 unsigned char *increment_ptr;
675 int *increment_nbytes = 0;
677 result = XGetWindowProperty (x_current_display, requestor_window,
678 selection, 0L, 10000000L, False,
679 event->property, &type, &format,
681 (unsigned char **) &increment_nbytes);
682 if (result == Success)
684 XPropertyEvent property_event;
686 total_size = *increment_nbytes;
687 increment_value = (unsigned char *) alloca (total_size);
688 increment_ptr = increment_value;
690 XDeleteProperty (x_current_display, event->requestor,
692 XFlush (x_current_display);
693 XFree ((char *) increment_nbytes);
696 { /* NOTE: this blocks. */
697 XWindowEvent (x_current_display, requestor_window,
699 (XEvent *) &property_event);
701 if (property_event.atom == selection
702 && property_event.state == PropertyNewValue)
705 result = XGetWindowProperty (x_current_display,
713 if (result == Success && type == XA_STRING
716 bcopy (data, increment_ptr, items);
717 increment_ptr += items;
723 while (increment_ptr < (increment_value + total_size));
725 return make_string (increment_value,
726 (increment_ptr - increment_value));
736 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
737 "The value of emacs' last cut-string.");
738 Vx_selection_value = Qnil;
740 DEFVAR_LISP ("x-secondary-selection-value", &Vx_secondary_selection_value,
741 "The value of emacs' last secondary cut-string.");
742 Vx_secondary_selection_value = Qnil;
744 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
745 "The string emacs last sent to the clipboard.");
746 Vx_clipboard_value = Qnil;
748 Qprimary = intern ("primary");
749 staticpro (&Qprimary);
750 Qsecondary = intern ("secondary");
751 staticpro (&Qsecondary);
752 Qclipboard = intern ("clipboard");
753 staticpro (&Qclipboard);
754 Qcut_buffer0 = intern ("cut-buffer0");
755 staticpro (&Qcut_buffer0);
757 defsubr (&Sx_own_selection);
758 defsubr (&Sx_selection_value);