*** empty log message ***
[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 BLOCK_INPUT;
197 XStoreBytes (x_current_display,
198 (char *) XSTRING (string)->data,
199 XSTRING (string)->size);
200 val = string;
201 UNBLOCK_INPUT;
202 }
203 else
204 error ("Invalid X selection type");
205
206 return val;
207 }
208
209 /* Clear our selection ownership data, as some other client has
210 become the owner. */
211
212 void
213 x_disown_selection (old_owner, selection, changed_owner_time)
214 Window *old_owner;
215 Atom selection;
216 Time changed_owner_time;
217 {
218 struct frame *s = x_window_to_frame (old_owner);
219
220 if (s) /* We are the owner */
221 {
222 if (selection == XA_PRIMARY)
223 {
224 x_begin_selection_own = 0;
225 Vx_selection_value = Qnil;
226 }
227 else if (selection == XA_SECONDARY)
228 {
229 x_begin_secondary_selection_own = 0;
230 Vx_secondary_selection_value = Qnil;
231 }
232 else if (selection == Xatom_clipboard)
233 {
234 x_begin_clipboard_own = 0;
235 Vx_clipboard_value = Qnil;
236 }
237 else
238 abort ();
239 }
240 else
241 abort (); /* Inconsistent state. */
242 }
243
244 int x_selection_alloc_error;
245 int x_converting_selection;
246
247 /* Reply to some client's request for our selection data. Data is
248 placed in a property supplied by the requesting window.
249
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.
253
254 But first, check for all the other crufty stuff we could get. */
255
256 void
257 x_answer_selection_request (event)
258 XSelectionRequestEvent event;
259 {
260 Time emacs_own_time;
261 Lisp_Object selection_value;
262 XSelectionEvent evt;
263 int format = 8; /* We have only byte sized (text) data. */
264
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;
271
272 if (event.selection == XA_PRIMARY)
273 {
274 emacs_own_time = x_begin_selection_own;
275 selection_value = Vx_selection_value;
276 }
277 else if (event.selection == XA_SECONDARY)
278 {
279 emacs_own_time = x_begin_secondary_selection_own;
280 selection_value = Vx_secondary_selection_value;
281 }
282 else if (event.selection == Xatom_clipboard)
283 {
284 emacs_own_time = x_begin_clipboard_own;
285 selection_value = Vx_clipboard_value;
286 }
287 else
288 abort ();
289
290 if (event.time != CurrentTime
291 && event.time < emacs_own_time)
292 evt.property = None;
293 else
294 {
295 if (event.property == None) /* obsolete client */
296 evt.property = event.target;
297 else
298 evt.property = event.property;
299 }
300
301 if (event.target == Xatom_targets) /* Send List of target atoms */
302 {
303 }
304 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
305 {
306 Atom type;
307 int return_format;
308 unsigned long items, bytes_left;
309 unsigned char *data;
310 int result, i;
311
312 if (event.property == 0 /* 0 == NILP */
313 || event.property == None)
314 return;
315
316 result = XGetWindowProperty (event.display, event.requestor,
317 event.property, 0L, 10000000L,
318 True, Xatom_pair, &type, &return_format,
319 &items, &bytes_left, &data);
320
321 if (result == Success && type == Xatom_pair)
322 for (i = items; i > 0; i--)
323 {
324 /* Convert each element of the list. */
325 }
326
327 (void) XSendEvent (x_current_display, evt.requestor, False,
328 0L, (XEvent *) &evt);
329 return;
330 }
331 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
332 {
333 if (! emacs_own_time)
334 abort ();
335
336 format = 32;
337 XChangeProperty (evt.display, evt.requestor, evt.property,
338 evt.target, format, PropModeReplace,
339 (unsigned char *) &emacs_own_time, 1);
340 return;
341 }
342 else if (event.target == Xatom_delete) /* Delete our selection. */
343 {
344 if (EQ (Qnil, selection_value))
345 abort ();
346
347 x_disown_selection (event.owner, event.selection, event.time);
348
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);
352 return;
353 }
354 else if (event.target == Xatom_insert_selection)
355 {
356 Atom type;
357 int return_format;
358 unsigned long items, bytes_left;
359 unsigned char *data;
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)
365 {
366 /* Convert the first atom to (a selection) to the target
367 indicated by the second atom. */
368 }
369 }
370 else if (event.target == Xatom_insert_property)
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, XA_STRING, &type, &return_format,
379 &items, &bytes_left, &data);
380
381 if (result == Success && type == XA_STRING && return_format == 8)
382 {
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);
389 else
390 abort ();
391 }
392
393 return;
394 }
395 else if ((event.target == Xatom_text
396 || event.target == XA_STRING))
397 {
398 int size = XSTRING (selection_value)->size;
399 unsigned char *data = XSTRING (selection_value)->data;
400
401 if (EQ (Qnil, selection_value))
402 abort ();
403
404 /* Place data on requestor window's property. */
405 if (SELECTION_LENGTH (size, format)
406 <= MAX_SELECTION (x_current_display))
407 {
408 x_converting_selection = 1;
409 XChangeProperty (evt.display, evt.requestor, evt.property,
410 evt.target, format, PropModeReplace,
411 data, size);
412 if (x_selection_alloc_error)
413 {
414 x_selection_alloc_error = 0;
415 abort ();
416 }
417 x_converting_selection = 0;
418 }
419 else /* Send incrementally */
420 {
421 evt.target = Xatom_incremental;
422 incr_requestor = evt.requestor;
423 incr_property = evt.property;
424 x_converting_selection = 1;
425
426 /* Need to handle Alloc errors on these requests. */
427 XChangeProperty (evt.display, incr_requestor, incr_property,
428 Xatom_incremental, 32,
429 PropModeReplace,
430 (unsigned char *) &size, 1);
431 if (x_selection_alloc_error)
432 {
433 x_selection_alloc_error = 0;
434 x_converting_selection = 0;
435 abort ();
436 /* Now abort the send. */
437 }
438
439 incr_nbytes = size;
440 incr_value = data;
441 incr_ptr = data;
442
443 /* Ask for notification when requestor deletes property. */
444 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
445
446 /* If we're sending incrementally, perhaps block here
447 until all sent? */
448 }
449 }
450 else
451 evt.property = None;
452
453 /* Don't do this if there was an Alloc error: abort the transfer
454 by sending None. */
455 (void) XSendEvent (x_current_display, evt.requestor, False,
456 0L, (XEvent *) &evt);
457 }
458
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.
463
464 When there is no more data to send, we send a zero-length increment. */
465
466 void
467 x_send_incremental (event)
468 XPropertyEvent event;
469 {
470 if (incr_requestor
471 && incr_requestor == event.window
472 && incr_property == event.atom
473 && event.state == PropertyDelete)
474 {
475 int format = 8;
476 int length = MAX_SELECTION (x_current_display);
477 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
478
479 if (length > bytes_left) /* Also sends 0 len when finished. */
480 length = bytes_left;
481 XChangeProperty (x_current_display, incr_requestor,
482 incr_property, XA_STRING, format,
483 PropModeAppend, incr_ptr, length);
484 if (x_selection_alloc_error)
485 {
486 x_selection_alloc_error = 0;
487 x_converting_selection = 0;
488 /* Abandon the transmission. */
489 abort ();
490 }
491 if (length > 0)
492 incr_ptr += length;
493 else
494 { /* Everything's sent */
495 XSelectInput (x_current_display, incr_requestor, 0L);
496 incr_requestor = (Window) 0;
497 incr_property = (Atom) 0;
498 incr_nbytes = 0;
499 incr_value = (unsigned char *) 0;
500 incr_ptr = (unsigned char *) 0;
501 x_converting_selection = 0;
502 }
503 }
504 }
505
506 /* SELECTION REQUESTOR CODE */
507
508 /* Predicate function used to match a requested event. */
509
510 Bool
511 XCheckSelectionEvent (dpy, event, window)
512 Display *dpy;
513 XEvent *event;
514 char *window;
515 {
516 if (event->type == SelectionNotify)
517 if (event->xselection.requestor == (Window) window)
518 return True;
519
520 return False;
521 }
522
523 /* Request a selection value from its owner. This will block until
524 all the data is arrived. */
525
526 static Lisp_Object
527 get_selection_value (type)
528 Atom type;
529 {
530 XEvent event;
531 Lisp_Object val;
532 Time requestor_time; /* Timestamp of selection request. */
533 Window requestor_window;
534
535 BLOCK_INPUT;
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,
541 &event,
542 XCheckSelectionEvent,
543 (char *) requestor_window);
544 val = x_selection_arrival (&event, requestor_window, requestor_time);
545 UNBLOCK_INPUT;
546
547 return val;
548 }
549
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. */
553
554 DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
555 0, 1, "",
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.")
558 (type)
559 register Lisp_Object type;
560 {
561 Atom selection_type;
562
563 if (NILP (type) || EQ (type, Qprimary))
564 {
565 if (!NILP (Vx_selection_value))
566 return Vx_selection_value;
567
568 return get_selection_value (XA_PRIMARY);
569 }
570 else if (EQ (type, Qsecondary))
571 {
572 if (!NILP (Vx_secondary_selection_value))
573 return Vx_secondary_selection_value;
574
575 return get_selection_value (XA_SECONDARY);
576 }
577 else if (EQ (type, Qclipboard))
578 {
579 if (!NILP (Vx_clipboard_value))
580 return Vx_clipboard_value;
581
582 return get_selection_value (Xatom_clipboard);
583 }
584 else if (EQ (type, Qcut_buffer0))
585 {
586 char *data;
587 int size;
588 Lisp_Object string;
589
590 BLOCK_INPUT;
591 data = XFetchBytes (x_current_display, &size);
592 if (data == 0)
593 string = Qnil;
594 else
595 string = make_string (data, size);
596 UNBLOCK_INPUT;
597
598 return string;
599 }
600 else
601 error ("Invalid X selection type");
602 }
603
604 Lisp_Object
605 x_selection_arrival (event, requestor_window, requestor_time)
606 register XSelectionEvent *event;
607 Window requestor_window;
608 Time requestor_time;
609 {
610 int result;
611 Atom type, selection;
612 int format;
613 unsigned long items;
614 unsigned long bytes_left;
615 unsigned char *data = 0;
616 int offset = 0;
617
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;
624 else
625 abort ();
626
627 if (event->requestor == requestor_window
628 && event->time == requestor_time
629 && event->property != None)
630 if (event->target != Xatom_incremental)
631 {
632 unsigned char *return_string =
633 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
634
635 do
636 {
637 result = XGetWindowProperty (x_current_display, requestor_window,
638 event->property, 0L,
639 10000000L, True, XA_STRING,
640 &type, &format, &items,
641 &bytes_left, &data);
642 if (result == Success && type == XA_STRING && format == 8
643 && offset < MAX_SELECTION (x_current_display))
644 {
645 bcopy (data, return_string + offset, items);
646 offset += items;
647 }
648 XFree ((char *) data);
649 }
650 while (bytes_left);
651
652 return make_string (return_string, offset);
653 }
654 else /* Prepare incremental transfer. */
655 {
656 unsigned char *increment_value;
657 unsigned char *increment_ptr;
658 int total_size;
659 int *increment_nbytes = 0;
660
661 result = XGetWindowProperty (x_current_display, requestor_window,
662 selection, 0L, 10000000L, False,
663 event->property, &type, &format,
664 &items, &bytes_left,
665 (unsigned char **) &increment_nbytes);
666 if (result == Success)
667 {
668 XPropertyEvent property_event;
669
670 total_size = *increment_nbytes;
671 increment_value = (unsigned char *) alloca (total_size);
672 increment_ptr = increment_value;
673
674 XDeleteProperty (x_current_display, event->requestor,
675 event->property);
676 XFlush (x_current_display);
677 XFree ((char *) increment_nbytes);
678
679 do
680 { /* NOTE: this blocks. */
681 XWindowEvent (x_current_display, requestor_window,
682 PropertyChangeMask,
683 (XEvent *) &property_event);
684
685 if (property_event.atom == selection
686 && property_event.state == PropertyNewValue)
687 do
688 {
689 result = XGetWindowProperty (x_current_display,
690 requestor_window,
691 selection, 0L,
692 10000000L, True,
693 AnyPropertyType,
694 &type, &format,
695 &items, &bytes_left,
696 &data);
697 if (result == Success && type == XA_STRING
698 && format == 8)
699 {
700 bcopy (data, increment_ptr, items);
701 increment_ptr += items;
702 }
703 }
704 while (bytes_left);
705
706 }
707 while (increment_ptr < (increment_value + total_size));
708
709 return make_string (increment_value,
710 (increment_ptr - increment_value));
711 }
712 }
713
714 return Qnil;
715 }
716
717 void
718 syms_of_xselect ()
719 {
720 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
721 "The value of emacs' last cut-string.");
722 Vx_selection_value = Qnil;
723
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;
727
728 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
729 "The string emacs last sent to the clipboard.");
730 Vx_clipboard_value = Qnil;
731
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);
740
741 defsubr (&Sx_own_selection);
742 defsubr (&Sx_selection_value);
743 }
744 #endif /* X11 */