*** empty log message ***
[emacs.git] / src / xselect.c.old
blob81f069e341e045e75dd02364c38d8627b7796a6f
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)
9 any later version.
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.  */
20 #include "config.h"
21 #include "lisp.h"
22 #include "xterm.h"
23 #include "screen.h"
25 #ifdef HAVE_X11
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
34    by that command.  */
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;
58 /* Clipboard atom. */
59 Atom Xatom_clipboard;
61 /* Atom for indicating incremental selection transfer. */
62 Atom Xatom_incremental;
64 /* Atom for indicating multiple selection request list */
65 Atom Xatom_multiple;
67 /* Atom for what targets emacs handles. */
68 Atom Xatom_targets;
70 /* Atom for indicating timstamp selection request */
71 Atom Xatom_timestamp;
73 /* Atom requesting we delete our selection. */
74 Atom Xatom_delete;
76 /* Selection magic. */
77 Atom Xatom_insert_selection;
79 /* Type of property for INSERT_SELECTION. */
80 Atom Xatom_pair;
82 /* More selection magic. */
83 Atom Xatom_insert_property;
85 /* Atom for indicating property type TEXT */
86 Atom Xatom_text;
88 /* These are to handle incremental selection transfer. */
89 Window incr_requestor;
90 Atom incr_property;
91 int incr_nbytes;
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.")
105   (string)
106      register Lisp_Object string;
108   Window owner_window, selecting_window;
109   Time event_time;
111   CHECK_STRING (string, 0);
113   BLOCK_INPUT;
114   selecting_window = selected_screen->display.x->window_desc;
116   if (EQ (Qnil, Vx_selection_value)) /* We are not the owner. */
117     {
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)
124         {
125           UNBLOCK_INPUT;
126           error ("X error: could not acquire selection ownership");
127         }
128     }
130   x_begin_selection_own = event_time;
131   Vx_selection_value = string;
132   UNBLOCK_INPUT;
134   return Qnil;
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.")
142   (string)
143      register Lisp_Object string;
145   Window owner_window, selecting_window;
146   Time event_time;
148   CHECK_STRING (string, 0);
150   BLOCK_INPUT;
151   selecting_window = selected_screen->display.x->window_desc;
153   if (EQ (Qnil, Vx_clipboard_value))
154     {
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)
161         {
162           UNBLOCK_INPUT;
163           error ("X error: could not acquire selection ownership");
164         }
165     }
167   x_begin_clipboard_own = event_time;
168   Vx_clipboard_value = string;
169   UNBLOCK_INPUT;
171   return Qnil;
174 /* Clear our selection ownership data, as some other client has
175    become the owner. */
177 void
178 x_disown_selection (old_owner, selection, changed_owner_time)
179      Window *old_owner;
180      Atom selection;
181      Time changed_owner_time;
183   struct screen *s = x_window_to_screen (old_owner);
185   if (s)                        /* We are the owner */
186     {
187       if (selection == XA_PRIMARY)
188         {
189           x_begin_selection_own = 0;
190           Vx_selection_value = Qnil;
191         }
192       else if (selection == Xatom_clipboard)
193         {
194           x_begin_clipboard_own = 0;
195           Vx_clipboard_value = Qnil;
196         }
197       else
198         abort ();
199     }
200   else
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. */
216 void
217 x_answer_selection_request (event)
218      XSelectionRequestEvent event;
220   Time emacs_own_time;
221   Lisp_Object selection_value;
222   XSelectionEvent evt;
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)
233     {
234       emacs_own_time = x_begin_selection_own;
235       selection_value = Vx_selection_value;
236     }
237   else if (event.selection == Xatom_clipboard)
238     {
239       emacs_own_time = x_begin_clipboard_own;
240       selection_value = Vx_clipboard_value;
241     }
242   else
243     abort ();
245   if (event.time != CurrentTime
246       && event.time < emacs_own_time)
247     evt.property = None;
248   else
249     {
250       if (event.property == None)       /* obsolete client */
251         evt.property = event.target;
252       else
253         evt.property = event.property;
254     }
256   if (event.target == Xatom_targets)          /* Send List of target atoms */
257     {
258     }
259   else if (event.target == Xatom_multiple)    /* Recvd list: <target, prop> */
260     {
261       Atom type;
262       int return_format;
263       unsigned long items, bytes_left;
264       unsigned char *data;
265       int result, i;
267       if (event.property == 0   /* 0 == NULL */
268           || event.property == None)
269         return;
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--)
278           {
279             /* Convert each element of the list. */
280           }
282       (void) XSendEvent (x_current_display, evt.requestor, False,
283                          0L, (XEvent *) &evt);
284       return;
285     }
286   else if (event.target == Xatom_timestamp)   /* Send ownership timestamp */
287     {
288       if (! emacs_own_time)
289         abort ();
291       format = 32;
292       XChangeProperty (evt.display, evt.requestor, evt.property,
293                        evt.target, format, PropModeReplace,
294                        (unsigned char *) &emacs_own_time, format);
295       return;
296     }
297   else if (event.target == Xatom_delete)      /* Delete our selection. */
298     {
299       if (EQ (Qnil, selection_value))
300         abort ();
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);
307       return;
308     }
309   else if (event.target == Xatom_insert_selection)
310     {
311       Atom type;
312       int return_format;
313       unsigned long items, bytes_left;
314       unsigned char *data;
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)
320         {
321           /* Convert the first atom to (a selection) to the target
322              indicated by the second atom. */
323         }
324     }
325   else if (event.target == Xatom_insert_property)
326     {
327       Atom type;
328       int return_format;
329       unsigned long items, bytes_left;
330       unsigned char *data;
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)
337         {
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);
342           else
343             abort ();
344         }
346       return;
347     }
348   else if ((event.target == Xatom_text
349             || event.target == XA_STRING))
350     {
351       int size = XSTRING (selection_value)->size;
352       unsigned char *data = XSTRING (selection_value)->data;
353                 
354       if (EQ (Qnil, selection_value))
355         abort ();
357       /* Place data on requestor window's property. */
358       if (SELECTION_LENGTH (size, format)
359           <= MAX_SELECTION (x_current_display))
360         {
361           x_converting_selection = 1;
362           XChangeProperty (evt.display, evt.requestor, evt.property,
363                            evt.target, format, PropModeReplace,
364                            data, size);
365           if (x_selection_alloc_error)
366             {
367               x_selection_alloc_error = 0;
368               abort ();
369             }
370           x_converting_selection = 0;
371         }
372       else  /* Send incrementally */
373         {
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,
382                            PropModeReplace,
383                            (unsigned char *) &size, 1);
384           if (x_selection_alloc_error)
385             {
386               x_selection_alloc_error = 0;
387               x_converting_selection = 0;
388               abort ();
389               /* Now abort the send. */
390             }
392           incr_nbytes = size;
393           incr_value = data;
394           incr_ptr = data;
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
400              until all sent? */
401         }
402     }
403   else
404     evt.property = None;
406   /* Don't do this if there was an Alloc error:  abort the transfer
407      by sending None. */
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. */
419 void
420 x_send_incremental (event)
421      XPropertyEvent event;
423   if (incr_requestor
424       && incr_requestor == event.window
425       && incr_property == event.atom
426       && event.state == PropertyDelete)
427     {
428       int format = 8;
429       int length = MAX_SELECTION (x_current_display);
430       int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
431                 
432       if (length > bytes_left)  /* Also sends 0 len when finished. */
433         length = bytes_left;
434       XChangeProperty (x_current_display, incr_requestor,
435                        incr_property, XA_STRING, format,
436                        PropModeAppend, incr_ptr, length);
437       if (x_selection_alloc_error)
438         {
439           x_selection_alloc_error = 0;
440           x_converting_selection = 0;
441           /* Abandon the transmission. */
442           abort ();
443         }
444       if (length > 0)
445         incr_ptr += length;
446       else
447         {                       /* Everything's sent */
448           XSelectInput (x_current_display, incr_requestor, 0L);
449           incr_requestor = (Window) 0;
450           incr_property = (Atom) 0;
451           incr_nbytes = 0;
452           incr_value = (unsigned char *) 0;
453           incr_ptr = (unsigned char *) 0;
454           x_converting_selection = 0;
455         }
456     }
459 /* SELECTION REQUESTOR CODE */
461 /* Predicate function used to match a requested event. */
463 Bool
464 XCheckSelectionEvent (dpy, event, window)
465      Display *dpy;
466      XEvent *event;
467      char *window;
469   if (event->type == SelectionNotify)
470     if (event->xselection.requestor == (Window) window)
471       return True;
473   return False;
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.")
483   ()
485   XEvent event;
486   Lisp_Object val;
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;
493   BLOCK_INPUT;
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,
499             &event,
500             XCheckSelectionEvent,
501             (char *) requestor_window);
502   val = x_selection_arrival (&event, requestor_window, requestor_time);
503   UNBLOCK_INPUT;
505   return val;
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.")
514   ()
516   XEvent event;
517   Lisp_Object val;
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;
524   BLOCK_INPUT;
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,
531             &event,
532             XCheckSelectionEvent,
533             (char *) requestor_window);
534   val = x_selection_arrival (&event, requestor_window, requestor_time);
535   UNBLOCK_INPUT;
537   return val;
540 Lisp_Object
541 x_selection_arrival (event, requestor_window, requestor_time)
542      register XSelectionEvent *event;
543      Window requestor_window;
544      Time requestor_time;
546   int result;
547   Atom type, selection;
548   int format;
549   unsigned long items;
550   unsigned long bytes_left;
551   unsigned char *data = 0;
552   int offset = 0;
554   if (event->selection == XA_PRIMARY)
555     selection = Xatom_emacs_selection;
556   else if (event->selection == Xatom_clipboard)
557     selection = Xatom_clipboard_selection;
558   else
559     abort ();
561   if (event->requestor == requestor_window
562       && event->time == requestor_time
563       && event->property != None)
564     if (event->target != Xatom_incremental)
565       {
566         unsigned char *return_string =
567           (unsigned char *) alloca (MAX_SELECTION (x_current_display));
569         do
570           {
571             result = XGetWindowProperty (x_current_display,
572                                          requestor_window,
573                                          event->property, 0L,
574                                          10000000L, True, XA_STRING,
575                                          &type, &format, &items,
576                                          &bytes_left, &data);
577             if (result == Success && type == XA_STRING && format == 8
578                 && offset < MAX_SELECTION (x_current_display))
579               {
580                 bcopy (data, return_string + offset, items);
581                 offset += items;
582               }
583             XFree ((char *) data);
584           }
585         while (bytes_left);
587         return make_string (return_string, offset);
588       }
589     else  /* Prepare incremental transfer. */
590       {
591         unsigned char *increment_value;
592         unsigned char *increment_ptr;
593         int total_size;
594         int *increment_nbytes = 0;
596         result = XGetWindowProperty (x_current_display, requestor_window,
597                                      selection, 0L, 10000000L, False,
598                                      event->property, &type, &format,
599                                      &items, &bytes_left,
600                                      (unsigned char **) &increment_nbytes);
601         if (result == Success)
602           {
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,
610                              event->property);
611             XFlush (x_current_display);
612             XFree ((char *) increment_nbytes);
614             do
615               {                 /* NOTE: this blocks. */
616                 XWindowEvent (x_current_display, requestor_window,
617                               PropertyChangeMask,
618                               (XEvent *) &property_event);
620                 if (property_event.atom == selection
621                     && property_event.state == PropertyNewValue)
622                   do
623                     {
624                       result = XGetWindowProperty (x_current_display,
625                                                    requestor_window,
626                                                    selection, 0L,
627                                                    10000000L, True,
628                                                    AnyPropertyType,
629                                                    &type, &format,
630                                                    &items, &bytes_left,
631                                                    &data);
632                       if (result == Success && type == XA_STRING
633                           && format == 8)
634                         {
635                           bcopy (data, increment_ptr, items);
636                           increment_ptr += items;
637                         }
638                     }
639                 while (bytes_left);
641               }
642             while (increment_ptr < (increment_value + total_size));
644             return make_string (increment_value,
645                                 (increment_ptr - increment_value));
646           }
647       }
649   return Qnil;
652 void
653 syms_of_xselect ()
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);
668 #endif  /* X11 */