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 /* The last 23 bits of the timestamp of the last mouse button event. */
32 extern Time mouse_timestamp;
34 /* 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 CLIPBOARD selection owner. */
42 Time x_begin_clipboard_own;
44 /* The value of the current CLIPBOARD selection. */
45 Lisp_Object Vx_clipboard_value;
47 /* The value of the current PRIMARY selection. */
48 Lisp_Object Vx_selection_value;
50 /* Emacs' selection property identifier. */
51 Atom Xatom_emacs_selection;
53 /* Clipboard selection atom. */
54 Atom Xatom_clipboard_selection;
59 /* Atom for indicating incremental selection transfer. */
60 Atom Xatom_incremental;
62 /* Atom for indicating multiple selection request list */
65 /* Atom for what targets emacs handles. */
68 /* Atom for indicating timstamp selection request */
71 /* Atom requesting we delete our selection. */
74 /* Selection magic. */
75 Atom Xatom_insert_selection;
77 /* Type of property for INSERT_SELECTION. */
80 /* More selection magic. */
81 Atom Xatom_insert_property;
83 /* Atom for indicating property type TEXT */
86 /* These are to handle incremental selection transfer. */
87 Window incr_requestor;
90 unsigned char *incr_value;
91 unsigned char *incr_ptr;
93 /* SELECTION OWNER CODE */
95 /* Become the selection owner and make our data the selection value.
96 If we are already the owner, merely change data and timestamp values.
97 This avoids generating SelectionClear events for ourselves. */
99 DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
100 1, 1, "sStore text for pasting: ",
101 "Stores string STRING for pasting in another X window.\n\
102 This is done with the X11 selection mechanism.")
104 register Lisp_Object string;
106 Window owner_window, selecting_window;
109 CHECK_STRING (string, 0);
112 selecting_window = selected_screen->display.x->window_desc;
114 if (EQ (Qnil, Vx_selection_value)) /* We are not the owner. */
116 event_time = mouse_timestamp;
117 XSetSelectionOwner (x_current_display, XA_PRIMARY,
118 selecting_window, event_time);
119 owner_window = XGetSelectionOwner (x_current_display, XA_PRIMARY);
121 if (owner_window != selecting_window)
124 error ("X error: could not acquire selection ownership");
128 x_begin_selection_own = event_time;
129 Vx_selection_value = string;
135 /* CLIPBOARD OWNERSHIP */
137 DEFUN ("x-own-clipboard", Fx_own_clipboard, Sx_own_clipboard,
138 1, 1, "sCLIPBOARD string: ",
139 "Assert X clipboard ownership with value STRING.")
141 register Lisp_Object string;
143 Window owner_window, selecting_window;
146 CHECK_STRING (string, 0);
149 selecting_window = selected_screen->display.x->window_desc;
151 if (EQ (Qnil, Vx_clipboard_value))
153 event_time = mouse_timestamp;
154 XSetSelectionOwner (x_current_display, Xatom_clipboard,
155 selecting_window, event_time);
156 owner_window = XGetSelectionOwner (x_current_display, Xatom_clipboard);
158 if (owner_window != selecting_window)
161 error ("X error: could not acquire selection ownership");
165 x_begin_clipboard_own = event_time;
166 Vx_clipboard_value = string;
172 /* Clear our selection ownership data, as some other client has
176 x_disown_selection (old_owner, selection, changed_owner_time)
179 Time changed_owner_time;
181 struct screen *s = x_window_to_screen (old_owner);
183 if (s) /* We are the owner */
185 if (selection == XA_PRIMARY)
187 x_begin_selection_own = 0;
188 Vx_selection_value = Qnil;
190 else if (selection == Xatom_clipboard)
192 x_begin_clipboard_own = 0;
193 Vx_clipboard_value = Qnil;
199 abort (); /* Inconsistent state. */
202 int x_selection_alloc_error;
203 int x_converting_selection;
205 /* Reply to some client's request for our selection data. Data is
206 placed in a propery supplied by the requesting window.
208 If the data exceeds the maximum amount the server can send,
209 then prepare to send it incrementally, and reply to the client with
210 the total size of the data.
212 But first, check for all the other crufty stuff we could get. */
215 x_answer_selection_request (event)
216 XSelectionRequestEvent event;
219 Lisp_Object selection_value;
221 int format = 8; /* We have only byte sized (text) data. */
223 evt.type = SelectionNotify; /* Construct reply event */
224 evt.display = event.display;
225 evt.requestor = event.requestor;
226 evt.selection = event.selection;
227 evt.time = event.time;
228 evt.target = event.target;
230 if (event.selection == XA_PRIMARY)
232 emacs_own_time = x_begin_selection_own;
233 selection_value = Vx_selection_value;
235 else if (event.selection == Xatom_clipboard)
237 emacs_own_time = x_begin_clipboard_own;
238 selection_value = Vx_clipboard_value;
243 if (event.time != CurrentTime
244 && event.time < emacs_own_time)
248 if (event.property == None) /* obsolete client */
249 evt.property = event.target;
251 evt.property = event.property;
254 if (event.target == Xatom_targets) /* Send List of target atoms */
257 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
261 unsigned long items, bytes_left;
265 if (event.property == 0 /* 0 == NULL */
266 || event.property == None)
269 result = XGetWindowProperty (event.display, event.requestor,
270 event.property, 0L, 10000000L,
271 True, Xatom_pair, &type, &return_format,
272 &items, &bytes_left, &data);
274 if (result == Success && type == Xatom_pair)
275 for (i = items; i > 0; i--)
277 /* Convert each element of the list. */
280 (void) XSendEvent (x_current_display, evt.requestor, False,
281 0L, (XEvent *) &evt);
284 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
286 if (! emacs_own_time)
290 XChangeProperty (evt.display, evt.requestor, evt.property,
291 evt.target, format, PropModeReplace,
292 (unsigned char *) &emacs_own_time, format);
295 else if (event.target == Xatom_delete) /* Delete our selection. */
297 if (EQ (Qnil, selection_value))
300 x_disown_selection (event.owner, event.selection, event.time);
302 /* Now return property of type NULL, length 0. */
303 XChangeProperty (event.display, event.requestor, event.property,
304 0, format, PropModeReplace, (unsigned char *) 0, 0);
307 else if (event.target == Xatom_insert_selection)
311 unsigned long items, bytes_left;
313 int result = XGetWindowProperty (event.display, event.requestor,
314 event.property, 0L, 10000000L,
315 True, Xatom_pair, &type, &return_format,
316 &items, &bytes_left, &data);
317 if (result == Success && type == Xatom_pair)
319 /* Convert the first atom to (a selection) to the target
320 indicated by the second atom. */
323 else if (event.target == Xatom_insert_property)
327 unsigned long items, bytes_left;
329 int result = XGetWindowProperty (event.display, event.requestor,
330 event.property, 0L, 10000000L,
331 True, XA_STRING, &type, &return_format,
332 &items, &bytes_left, &data);
334 if (result == Success && type == XA_STRING && return_format == 8)
336 if (event.selection == Xatom_emacs_selection)
337 Vx_selection_value = make_string (data);
338 else if (event.selection == Xatom_clipboard_selection)
339 Vx_clipboard_value = make_string (data);
346 else if ((event.target == Xatom_text
347 || event.target == XA_STRING))
349 int size = XSTRING (selection_value)->size;
350 unsigned char *data = XSTRING (selection_value)->data;
352 if (EQ (Qnil, selection_value))
355 /* Place data on requestor window's property. */
356 if (SELECTION_LENGTH (size, format)
357 <= MAX_SELECTION (x_current_display))
359 x_converting_selection = 1;
360 XChangeProperty (evt.display, evt.requestor, evt.property,
361 evt.target, format, PropModeReplace,
363 if (x_selection_alloc_error)
365 x_selection_alloc_error = 0;
368 x_converting_selection = 0;
370 else /* Send incrementally */
372 evt.target = Xatom_incremental;
373 incr_requestor = evt.requestor;
374 incr_property = evt.property;
375 x_converting_selection = 1;
377 /* Need to handle Alloc errors on these requests. */
378 XChangeProperty (evt.display, incr_requestor, incr_property,
379 Xatom_incremental, 32,
381 (unsigned char *) &size, 1);
382 if (x_selection_alloc_error)
384 x_selection_alloc_error = 0;
385 x_converting_selection = 0;
387 /* Now abort the send. */
394 /* Ask for notification when requestor deletes property. */
395 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
397 /* If we're sending incrementally, perhaps block here
404 /* Don't do this if there was an Alloc error: abort the transfer
406 (void) XSendEvent (x_current_display, evt.requestor, False,
407 0L, (XEvent *) &evt);
410 /* Send an increment of selection data in response to a PropertyNotify event.
411 The increment is placed in a property on the requestor's window.
412 When the requestor has processed the increment, it deletes the property,
413 which sends us another PropertyNotify event.
415 When there is no more data to send, we send a zero-length increment. */
418 x_send_incremental (event)
419 XPropertyEvent event;
422 && incr_requestor == event.window
423 && incr_property == event.atom
424 && event.state == PropertyDelete)
427 int length = MAX_SELECTION (x_current_display);
428 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
430 if (length > bytes_left) /* Also sends 0 len when finished. */
432 XChangeProperty (x_current_display, incr_requestor,
433 incr_property, XA_STRING, format,
434 PropModeAppend, incr_ptr, length);
435 if (x_selection_alloc_error)
437 x_selection_alloc_error = 0;
438 x_converting_selection = 0;
439 /* Abandon the transmission. */
445 { /* Everything's sent */
446 XSelectInput (x_current_display, incr_requestor, 0L);
447 incr_requestor = (Window) 0;
448 incr_property = (Atom) 0;
450 incr_value = (unsigned char *) 0;
451 incr_ptr = (unsigned char *) 0;
452 x_converting_selection = 0;
457 /* SELECTION REQUESTOR CODE */
459 /* Predicate function used to match a requested event. */
462 XCheckSelectionEvent (dpy, event, window)
467 if (event->type == SelectionNotify)
468 if (event->xselection.requestor == (Window) window)
474 /* Request the selection value from the owner. If we are the owner,
475 simply return our selection value. If we are not the owner, this
476 will block until all of the data has arrived. */
478 DEFUN ("x-get-selection", Fx_get_selection, Sx_get_selection, 0, 0, 0,
479 "Return text selected from some X window.\n\
480 This is done with the X11 selection mechanism.")
485 Time requestor_time; /* Timestamp of selection request. */
486 Window requestor_window;
488 if (!EQ (Qnil, Vx_selection_value)) /* We are the owner */
489 return Vx_selection_value;
492 requestor_time = mouse_timestamp;
493 requestor_window = selected_screen->display.x->window_desc;
494 XConvertSelection (x_current_display, XA_PRIMARY, XA_STRING,
495 Xatom_emacs_selection, requestor_window, requestor_time);
496 XIfEvent (x_current_display,
498 XCheckSelectionEvent,
499 (char *) requestor_window);
500 val = x_selection_arrival (&event, requestor_window, requestor_time);
506 /* Request the clipboard contents from its owner. If we are the owner,
507 simply return the clipboard string. */
509 DEFUN ("x-get-clipboard", Fx_get_clipboard, Sx_get_clipboard, 0, 0, 0,
510 "Return text pasted to the clipboard.\n\
511 This is done with the X11 selection mechanism.")
516 Time requestor_time; /* Timestamp of selection request. */
517 Window requestor_window;
519 if (!EQ (Qnil, Vx_clipboard_value)) /* We are the owner */
520 return Vx_selection_value;
523 requestor_time = mouse_timestamp;
524 requestor_window = selected_screen->display.x->window_desc;
525 XConvertSelection (x_current_display, Xatom_clipboard, XA_STRING,
526 Xatom_clipboard_selection,
527 requestor_window, requestor_time);
528 XIfEvent (x_current_display,
530 XCheckSelectionEvent,
531 (char *) requestor_window);
532 val = x_selection_arrival (&event, requestor_window, requestor_time);
539 x_selection_arrival (event, requestor_window, requestor_time)
540 register XSelectionEvent *event;
541 Window requestor_window;
545 Atom type, selection;
548 unsigned long bytes_left;
549 unsigned char *data = 0;
552 if (event->selection == XA_PRIMARY)
553 selection = Xatom_emacs_selection;
554 else if (event->selection == Xatom_clipboard)
555 selection = Xatom_clipboard_selection;
559 if (event->requestor == requestor_window
560 && event->time == requestor_time
561 && event->property != None)
562 if (event->target != Xatom_incremental)
564 unsigned char *return_string =
565 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
569 result = XGetWindowProperty (x_current_display,
572 10000000L, True, XA_STRING,
573 &type, &format, &items,
575 if (result == Success && type == XA_STRING && format == 8
576 && offset < MAX_SELECTION (x_current_display))
578 bcopy (data, return_string + offset, items);
581 XFree ((char *) data);
585 return make_string (return_string, offset);
587 else /* Prepare incremental transfer. */
589 unsigned char *increment_value;
590 unsigned char *increment_ptr;
592 int *increment_nbytes = 0;
594 result = XGetWindowProperty (x_current_display, requestor_window,
595 selection, 0L, 10000000L, False,
596 event->property, &type, &format,
598 (unsigned char **) &increment_nbytes);
599 if (result == Success)
601 XPropertyEvent property_event;
603 total_size = *increment_nbytes;
604 increment_value = (unsigned char *) alloca (total_size);
605 increment_ptr = increment_value;
607 XDeleteProperty (x_current_display, event->requestor,
609 XFlush (x_current_display);
610 XFree ((char *) increment_nbytes);
613 { /* NOTE: this blocks. */
614 XWindowEvent (x_current_display, requestor_window,
616 (XEvent *) &property_event);
618 if (property_event.atom == selection
619 && property_event.state == PropertyNewValue)
622 result = XGetWindowProperty (x_current_display,
630 if (result == Success && type == XA_STRING
633 bcopy (data, increment_ptr, items);
634 increment_ptr += items;
640 while (increment_ptr < (increment_value + total_size));
642 return make_string (increment_value,
643 (increment_ptr - increment_value));
653 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
654 "The value of emacs' last cut-string.");
655 Vx_selection_value = Qnil;
657 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
658 "The string emacs last sent to the clipboard.");
659 Vx_clipboard_value = Qnil;
661 defsubr (&Sx_own_selection);
662 defsubr (&Sx_get_selection);
663 defsubr (&Sx_own_clipboard);
664 defsubr (&Sx_get_clipboard);