entered into RCS
[bpt/emacs.git] / src / xselect.c.old
1 /* X Selection processing for emacs
2 Copyright (C) 1990, 1992 Free Software Foundation.
3
4 This file is part of GNU Emacs.
5
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)
9 any later version.
10
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.
15
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. */
19
20 #include "config.h"
21 #include "lisp.h"
22 #include "xterm.h"
23 #include "buffer.h"
24 #include "frame.h"
25
26 #ifdef HAVE_X11
27
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))
31
32 /* The timestamp of the last input event we received from the X server. */
33 unsigned long last_event_timestamp;
34
35 /* t if a mouse button is depressed. */
36 extern Lisp_Object Vmouse_grabbed;
37
38 /* When emacs became the PRIMARY selection owner. */
39 Time x_begin_selection_own;
40
41 /* When emacs became the SECONDARY selection owner. */
42 Time x_begin_secondary_selection_own;
43
44 /* When emacs became the CLIPBOARD selection owner. */
45 Time x_begin_clipboard_own;
46
47 /* The value of the current CLIPBOARD selection. */
48 Lisp_Object Vx_clipboard_value;
49
50 /* The value of the current PRIMARY selection. */
51 Lisp_Object Vx_selection_value;
52
53 /* The value of the current SECONDARY selection. */
54 Lisp_Object Vx_secondary_selection_value;
55
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;
60
61 /* Emacs' selection property identifiers. */
62 Atom Xatom_emacs_selection;
63 Atom Xatom_emacs_secondary_selection;
64
65 /* Clipboard selection atom. */
66 Atom Xatom_clipboard_selection;
67
68 /* Clipboard atom. */
69 Atom Xatom_clipboard;
70
71 /* Atom for indicating incremental selection transfer. */
72 Atom Xatom_incremental;
73
74 /* Atom for indicating multiple selection request list */
75 Atom Xatom_multiple;
76
77 /* Atom for what targets emacs handles. */
78 Atom Xatom_targets;
79
80 /* Atom for indicating timstamp selection request */
81 Atom Xatom_timestamp;
82
83 /* Atom requesting we delete our selection. */
84 Atom Xatom_delete;
85
86 /* Selection magic. */
87 Atom Xatom_insert_selection;
88
89 /* Type of property for INSERT_SELECTION. */
90 Atom Xatom_pair;
91
92 /* More selection magic. */
93 Atom Xatom_insert_property;
94
95 /* Atom for indicating property type TEXT */
96 Atom Xatom_text;
97
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;
102
103 /* Communication with window managers. */
104 Atom Xatom_wm_protocols;
105
106 /* These are to handle incremental selection transfer. */
107 Window incr_requestor;
108 Atom incr_property;
109 int incr_nbytes;
110 unsigned char *incr_value;
111 unsigned char *incr_ptr;
112
113 /* SELECTION OWNER CODE */
114
115
116 /* Request selection ownership if we do not already have it. */
117
118 static int
119 own_selection (selection_type, time)
120 Atom selection_type;
121 Time time;
122 {
123 Window owner_window, selecting_window;
124
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)))
128 return 1;
129
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);
134
135 if (owner_window != selecting_window)
136 return 0;
137
138 return 1;
139 }
140
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. */
144
145 DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
146 1, 2, "",
147 "Make STRING the selection value. Default is the primary selection,\n\
148 but optional second argument TYPE may specify secondary or clipboard.\n\
149 \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\
153 use X selections.")
154 (string, type)
155 register Lisp_Object string, type;
156 {
157 Atom selection_type;
158 Lisp_Object val;
159 Time event_time = last_event_timestamp;
160 CHECK_STRING (string, 0);
161
162 val = Qnil;
163
164 if (NILP (type) || EQ (type, Qprimary))
165 {
166 BLOCK_INPUT;
167 if (own_selection (XA_PRIMARY, event_time))
168 {
169 x_begin_selection_own = event_time;
170 val = Vx_selection_value = string;
171 }
172 UNBLOCK_INPUT;
173 }
174 else if (EQ (type, Qsecondary))
175 {
176 BLOCK_INPUT;
177 if (own_selection (XA_SECONDARY, event_time))
178 {
179 x_begin_secondary_selection_own = event_time;
180 val = Vx_secondary_selection_value = string;
181 }
182 UNBLOCK_INPUT;
183 }
184 else if (EQ (type, Qclipboard))
185 {
186 BLOCK_INPUT;
187 if (own_selection (Xatom_clipboard, event_time))
188 {
189 x_begin_clipboard_own = event_time;
190 val = Vx_clipboard_value = string;
191 }
192 UNBLOCK_INPUT;
193 }
194 else if (EQ (type, Qcut_buffer0))
195 {
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.
200
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. */
204 BLOCK_INPUT;
205 if (XSTRING (string)->size > MAX_SELECTION (x_current_display))
206 {
207 XStoreBytes (x_current_display, (char *) 0, 0);
208 val = Qnil;
209 }
210 else
211 {
212 XStoreBytes (x_current_display,
213 (char *) XSTRING (string)->data,
214 XSTRING (string)->size);
215 val = string;
216 }
217 UNBLOCK_INPUT;
218 }
219 else
220 error ("Invalid X selection type");
221
222 return val;
223 }
224
225 /* Clear our selection ownership data, as some other client has
226 become the owner. */
227
228 void
229 x_disown_selection (old_owner, selection, changed_owner_time)
230 Window *old_owner;
231 Atom selection;
232 Time changed_owner_time;
233 {
234 struct frame *s = x_window_to_frame (old_owner);
235
236 if (s) /* We are the owner */
237 {
238 if (selection == XA_PRIMARY)
239 {
240 x_begin_selection_own = 0;
241 Vx_selection_value = Qnil;
242 }
243 else if (selection == XA_SECONDARY)
244 {
245 x_begin_secondary_selection_own = 0;
246 Vx_secondary_selection_value = Qnil;
247 }
248 else if (selection == Xatom_clipboard)
249 {
250 x_begin_clipboard_own = 0;
251 Vx_clipboard_value = Qnil;
252 }
253 else
254 abort ();
255 }
256 else
257 abort (); /* Inconsistent state. */
258 }
259
260 int x_selection_alloc_error;
261 int x_converting_selection;
262
263 /* Reply to some client's request for our selection data. Data is
264 placed in a property supplied by the requesting window.
265
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.
269
270 But first, check for all the other crufty stuff we could get. */
271
272 void
273 x_answer_selection_request (event)
274 XSelectionRequestEvent event;
275 {
276 Time emacs_own_time;
277 Lisp_Object selection_value;
278 XSelectionEvent evt;
279 int format = 8; /* We have only byte sized (text) data. */
280
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;
287
288 if (event.selection == XA_PRIMARY)
289 {
290 emacs_own_time = x_begin_selection_own;
291 selection_value = Vx_selection_value;
292 }
293 else if (event.selection == XA_SECONDARY)
294 {
295 emacs_own_time = x_begin_secondary_selection_own;
296 selection_value = Vx_secondary_selection_value;
297 }
298 else if (event.selection == Xatom_clipboard)
299 {
300 emacs_own_time = x_begin_clipboard_own;
301 selection_value = Vx_clipboard_value;
302 }
303 else
304 abort ();
305
306 if (event.time != CurrentTime
307 && event.time < emacs_own_time)
308 evt.property = None;
309 else
310 {
311 if (event.property == None) /* obsolete client */
312 evt.property = event.target;
313 else
314 evt.property = event.property;
315 }
316
317 if (event.target == Xatom_targets) /* Send List of target atoms */
318 {
319 }
320 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
321 {
322 Atom type;
323 int return_format;
324 unsigned long items, bytes_left;
325 unsigned char *data;
326 int result, i;
327
328 if (event.property == 0 /* 0 == NILP */
329 || event.property == None)
330 return;
331
332 result = XGetWindowProperty (event.display, event.requestor,
333 event.property, 0L, 10000000L,
334 True, Xatom_pair, &type, &return_format,
335 &items, &bytes_left, &data);
336
337 if (result == Success && type == Xatom_pair)
338 for (i = items; i > 0; i--)
339 {
340 /* Convert each element of the list. */
341 }
342
343 (void) XSendEvent (x_current_display, evt.requestor, False,
344 0L, (XEvent *) &evt);
345 return;
346 }
347 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
348 {
349 if (! emacs_own_time)
350 abort ();
351
352 format = 32;
353 XChangeProperty (evt.display, evt.requestor, evt.property,
354 evt.target, format, PropModeReplace,
355 (unsigned char *) &emacs_own_time, 1);
356 return;
357 }
358 else if (event.target == Xatom_delete) /* Delete our selection. */
359 {
360 if (EQ (Qnil, selection_value))
361 abort ();
362
363 x_disown_selection (event.owner, event.selection, event.time);
364
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);
368 return;
369 }
370 else if (event.target == Xatom_insert_selection)
371 {
372 Atom type;
373 int return_format;
374 unsigned long items, bytes_left;
375 unsigned char *data;
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)
381 {
382 /* Convert the first atom to (a selection) to the target
383 indicated by the second atom. */
384 }
385 }
386 else if (event.target == Xatom_insert_property)
387 {
388 Atom type;
389 int return_format;
390 unsigned long items, bytes_left;
391 unsigned char *data;
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);
396
397 if (result == Success && type == XA_STRING && return_format == 8)
398 {
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);
405 else
406 abort ();
407 }
408
409 return;
410 }
411 else if ((event.target == Xatom_text
412 || event.target == XA_STRING))
413 {
414 int size = XSTRING (selection_value)->size;
415 unsigned char *data = XSTRING (selection_value)->data;
416
417 if (EQ (Qnil, selection_value))
418 abort ();
419
420 /* Place data on requestor window's property. */
421 if (SELECTION_LENGTH (size, format)
422 <= MAX_SELECTION (x_current_display))
423 {
424 x_converting_selection = 1;
425 XChangeProperty (evt.display, evt.requestor, evt.property,
426 evt.target, format, PropModeReplace,
427 data, size);
428 if (x_selection_alloc_error)
429 {
430 x_selection_alloc_error = 0;
431 abort ();
432 }
433 x_converting_selection = 0;
434 }
435 else /* Send incrementally */
436 {
437 evt.target = Xatom_incremental;
438 incr_requestor = evt.requestor;
439 incr_property = evt.property;
440 x_converting_selection = 1;
441
442 /* Need to handle Alloc errors on these requests. */
443 XChangeProperty (evt.display, incr_requestor, incr_property,
444 Xatom_incremental, 32,
445 PropModeReplace,
446 (unsigned char *) &size, 1);
447 if (x_selection_alloc_error)
448 {
449 x_selection_alloc_error = 0;
450 x_converting_selection = 0;
451 abort ();
452 /* Now abort the send. */
453 }
454
455 incr_nbytes = size;
456 incr_value = data;
457 incr_ptr = data;
458
459 /* Ask for notification when requestor deletes property. */
460 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
461
462 /* If we're sending incrementally, perhaps block here
463 until all sent? */
464 }
465 }
466 else
467 evt.property = None;
468
469 /* Don't do this if there was an Alloc error: abort the transfer
470 by sending None. */
471 (void) XSendEvent (x_current_display, evt.requestor, False,
472 0L, (XEvent *) &evt);
473 }
474
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.
479
480 When there is no more data to send, we send a zero-length increment. */
481
482 void
483 x_send_incremental (event)
484 XPropertyEvent event;
485 {
486 if (incr_requestor
487 && incr_requestor == event.window
488 && incr_property == event.atom
489 && event.state == PropertyDelete)
490 {
491 int format = 8;
492 int length = MAX_SELECTION (x_current_display);
493 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
494
495 if (length > bytes_left) /* Also sends 0 len when finished. */
496 length = bytes_left;
497 XChangeProperty (x_current_display, incr_requestor,
498 incr_property, XA_STRING, format,
499 PropModeAppend, incr_ptr, length);
500 if (x_selection_alloc_error)
501 {
502 x_selection_alloc_error = 0;
503 x_converting_selection = 0;
504 /* Abandon the transmission. */
505 abort ();
506 }
507 if (length > 0)
508 incr_ptr += length;
509 else
510 { /* Everything's sent */
511 XSelectInput (x_current_display, incr_requestor, 0L);
512 incr_requestor = (Window) 0;
513 incr_property = (Atom) 0;
514 incr_nbytes = 0;
515 incr_value = (unsigned char *) 0;
516 incr_ptr = (unsigned char *) 0;
517 x_converting_selection = 0;
518 }
519 }
520 }
521
522 /* SELECTION REQUESTOR CODE */
523
524 /* Predicate function used to match a requested event. */
525
526 Bool
527 XCheckSelectionEvent (dpy, event, window)
528 Display *dpy;
529 XEvent *event;
530 char *window;
531 {
532 if (event->type == SelectionNotify)
533 if (event->xselection.requestor == (Window) window)
534 return True;
535
536 return False;
537 }
538
539 /* Request a selection value from its owner. This will block until
540 all the data is arrived. */
541
542 static Lisp_Object
543 get_selection_value (type)
544 Atom type;
545 {
546 XEvent event;
547 Lisp_Object val;
548 Time requestor_time; /* Timestamp of selection request. */
549 Window requestor_window;
550
551 BLOCK_INPUT;
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,
557 &event,
558 XCheckSelectionEvent,
559 (char *) requestor_window);
560 val = x_selection_arrival (&event, requestor_window, requestor_time);
561 UNBLOCK_INPUT;
562
563 return val;
564 }
565
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. */
569
570 DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
571 0, 1, "",
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.")
574 (type)
575 register Lisp_Object type;
576 {
577 Atom selection_type;
578
579 if (NILP (type) || EQ (type, Qprimary))
580 {
581 if (!NILP (Vx_selection_value))
582 return Vx_selection_value;
583
584 return get_selection_value (XA_PRIMARY);
585 }
586 else if (EQ (type, Qsecondary))
587 {
588 if (!NILP (Vx_secondary_selection_value))
589 return Vx_secondary_selection_value;
590
591 return get_selection_value (XA_SECONDARY);
592 }
593 else if (EQ (type, Qclipboard))
594 {
595 if (!NILP (Vx_clipboard_value))
596 return Vx_clipboard_value;
597
598 return get_selection_value (Xatom_clipboard);
599 }
600 else if (EQ (type, Qcut_buffer0))
601 {
602 char *data;
603 int size;
604 Lisp_Object string;
605
606 BLOCK_INPUT;
607 data = XFetchBytes (x_current_display, &size);
608 if (data == 0)
609 string = Qnil;
610 else
611 string = make_string (data, size);
612 UNBLOCK_INPUT;
613
614 return string;
615 }
616 else
617 error ("Invalid X selection type");
618 }
619
620 Lisp_Object
621 x_selection_arrival (event, requestor_window, requestor_time)
622 register XSelectionEvent *event;
623 Window requestor_window;
624 Time requestor_time;
625 {
626 int result;
627 Atom type, selection;
628 int format;
629 unsigned long items;
630 unsigned long bytes_left;
631 unsigned char *data = 0;
632 int offset = 0;
633
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;
640 else
641 abort ();
642
643 if (event->requestor == requestor_window
644 && event->time == requestor_time
645 && event->property != None)
646 if (event->target != Xatom_incremental)
647 {
648 unsigned char *return_string =
649 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
650
651 do
652 {
653 result = XGetWindowProperty (x_current_display, requestor_window,
654 event->property, 0L,
655 10000000L, True, XA_STRING,
656 &type, &format, &items,
657 &bytes_left, &data);
658 if (result == Success && type == XA_STRING && format == 8
659 && offset < MAX_SELECTION (x_current_display))
660 {
661 bcopy (data, return_string + offset, items);
662 offset += items;
663 }
664 XFree ((char *) data);
665 }
666 while (bytes_left);
667
668 return make_string (return_string, offset);
669 }
670 else /* Prepare incremental transfer. */
671 {
672 unsigned char *increment_value;
673 unsigned char *increment_ptr;
674 int total_size;
675 int *increment_nbytes = 0;
676
677 result = XGetWindowProperty (x_current_display, requestor_window,
678 selection, 0L, 10000000L, False,
679 event->property, &type, &format,
680 &items, &bytes_left,
681 (unsigned char **) &increment_nbytes);
682 if (result == Success)
683 {
684 XPropertyEvent property_event;
685
686 total_size = *increment_nbytes;
687 increment_value = (unsigned char *) alloca (total_size);
688 increment_ptr = increment_value;
689
690 XDeleteProperty (x_current_display, event->requestor,
691 event->property);
692 XFlush (x_current_display);
693 XFree ((char *) increment_nbytes);
694
695 do
696 { /* NOTE: this blocks. */
697 XWindowEvent (x_current_display, requestor_window,
698 PropertyChangeMask,
699 (XEvent *) &property_event);
700
701 if (property_event.atom == selection
702 && property_event.state == PropertyNewValue)
703 do
704 {
705 result = XGetWindowProperty (x_current_display,
706 requestor_window,
707 selection, 0L,
708 10000000L, True,
709 AnyPropertyType,
710 &type, &format,
711 &items, &bytes_left,
712 &data);
713 if (result == Success && type == XA_STRING
714 && format == 8)
715 {
716 bcopy (data, increment_ptr, items);
717 increment_ptr += items;
718 }
719 }
720 while (bytes_left);
721
722 }
723 while (increment_ptr < (increment_value + total_size));
724
725 return make_string (increment_value,
726 (increment_ptr - increment_value));
727 }
728 }
729
730 return Qnil;
731 }
732
733 void
734 syms_of_xselect ()
735 {
736 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
737 "The value of emacs' last cut-string.");
738 Vx_selection_value = Qnil;
739
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;
743
744 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
745 "The string emacs last sent to the clipboard.");
746 Vx_clipboard_value = Qnil;
747
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);
756
757 defsubr (&Sx_own_selection);
758 defsubr (&Sx_selection_value);
759 }
760 #endif /* X11 */