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