Initial revision
[bpt/emacs.git] / src / xselect.c.old
1 /* X Selection processing for emacs
2 Copyright (C) 1990 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 1, 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 "screen.h"
24
25 #ifdef HAVE_X11
26
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))
30
31 /* The last 23 bits of the timestamp of the last mouse button event. */
32 extern Time mouse_timestamp;
33
34 /* t if a mouse button is depressed. */
35
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 CLIPBOARD selection owner. */
42 Time x_begin_clipboard_own;
43
44 /* The value of the current CLIPBOARD selection. */
45 Lisp_Object Vx_clipboard_value;
46
47 /* The value of the current PRIMARY selection. */
48 Lisp_Object Vx_selection_value;
49
50 /* Emacs' selection property identifier. */
51 Atom Xatom_emacs_selection;
52
53 /* Clipboard selection atom. */
54 Atom Xatom_clipboard_selection;
55
56 /* Clipboard atom. */
57 Atom Xatom_clipboard;
58
59 /* Atom for indicating incremental selection transfer. */
60 Atom Xatom_incremental;
61
62 /* Atom for indicating multiple selection request list */
63 Atom Xatom_multiple;
64
65 /* Atom for what targets emacs handles. */
66 Atom Xatom_targets;
67
68 /* Atom for indicating timstamp selection request */
69 Atom Xatom_timestamp;
70
71 /* Atom requesting we delete our selection. */
72 Atom Xatom_delete;
73
74 /* Selection magic. */
75 Atom Xatom_insert_selection;
76
77 /* Type of property for INSERT_SELECTION. */
78 Atom Xatom_pair;
79
80 /* More selection magic. */
81 Atom Xatom_insert_property;
82
83 /* Atom for indicating property type TEXT */
84 Atom Xatom_text;
85
86 /* These are to handle incremental selection transfer. */
87 Window incr_requestor;
88 Atom incr_property;
89 int incr_nbytes;
90 unsigned char *incr_value;
91 unsigned char *incr_ptr;
92
93 /* SELECTION OWNER CODE */
94
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. */
98
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.")
103 (string)
104 register Lisp_Object string;
105 {
106 Window owner_window, selecting_window;
107 Time event_time;
108
109 CHECK_STRING (string, 0);
110
111 BLOCK_INPUT;
112 selecting_window = selected_screen->display.x->window_desc;
113
114 if (EQ (Qnil, Vx_selection_value)) /* We are not the owner. */
115 {
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);
120
121 if (owner_window != selecting_window)
122 {
123 UNBLOCK_INPUT;
124 error ("X error: could not acquire selection ownership");
125 }
126 }
127
128 x_begin_selection_own = event_time;
129 Vx_selection_value = string;
130 UNBLOCK_INPUT;
131
132 return Qnil;
133 }
134
135 /* CLIPBOARD OWNERSHIP */
136
137 DEFUN ("x-own-clipboard", Fx_own_clipboard, Sx_own_clipboard,
138 1, 1, "sCLIPBOARD string: ",
139 "Assert X clipboard ownership with value STRING.")
140 (string)
141 register Lisp_Object string;
142 {
143 Window owner_window, selecting_window;
144 Time event_time;
145
146 CHECK_STRING (string, 0);
147
148 BLOCK_INPUT;
149 selecting_window = selected_screen->display.x->window_desc;
150
151 if (EQ (Qnil, Vx_clipboard_value))
152 {
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);
157
158 if (owner_window != selecting_window)
159 {
160 UNBLOCK_INPUT;
161 error ("X error: could not acquire selection ownership");
162 }
163 }
164
165 x_begin_clipboard_own = event_time;
166 Vx_clipboard_value = string;
167 UNBLOCK_INPUT;
168
169 return Qnil;
170 }
171
172 /* Clear our selection ownership data, as some other client has
173 become the owner. */
174
175 void
176 x_disown_selection (old_owner, selection, changed_owner_time)
177 Window *old_owner;
178 Atom selection;
179 Time changed_owner_time;
180 {
181 struct screen *s = x_window_to_screen (old_owner);
182
183 if (s) /* We are the owner */
184 {
185 if (selection == XA_PRIMARY)
186 {
187 x_begin_selection_own = 0;
188 Vx_selection_value = Qnil;
189 }
190 else if (selection == Xatom_clipboard)
191 {
192 x_begin_clipboard_own = 0;
193 Vx_clipboard_value = Qnil;
194 }
195 else
196 abort ();
197 }
198 else
199 abort (); /* Inconsistent state. */
200 }
201
202 int x_selection_alloc_error;
203 int x_converting_selection;
204
205 /* Reply to some client's request for our selection data. Data is
206 placed in a propery supplied by the requesting window.
207
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.
211
212 But first, check for all the other crufty stuff we could get. */
213
214 void
215 x_answer_selection_request (event)
216 XSelectionRequestEvent event;
217 {
218 Time emacs_own_time;
219 Lisp_Object selection_value;
220 XSelectionEvent evt;
221 int format = 8; /* We have only byte sized (text) data. */
222
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;
229
230 if (event.selection == XA_PRIMARY)
231 {
232 emacs_own_time = x_begin_selection_own;
233 selection_value = Vx_selection_value;
234 }
235 else if (event.selection == Xatom_clipboard)
236 {
237 emacs_own_time = x_begin_clipboard_own;
238 selection_value = Vx_clipboard_value;
239 }
240 else
241 abort ();
242
243 if (event.time != CurrentTime
244 && event.time < emacs_own_time)
245 evt.property = None;
246 else
247 {
248 if (event.property == None) /* obsolete client */
249 evt.property = event.target;
250 else
251 evt.property = event.property;
252 }
253
254 if (event.target == Xatom_targets) /* Send List of target atoms */
255 {
256 }
257 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
258 {
259 Atom type;
260 int return_format;
261 unsigned long items, bytes_left;
262 unsigned char *data;
263 int result, i;
264
265 if (event.property == 0 /* 0 == NULL */
266 || event.property == None)
267 return;
268
269 result = XGetWindowProperty (event.display, event.requestor,
270 event.property, 0L, 10000000L,
271 True, Xatom_pair, &type, &return_format,
272 &items, &bytes_left, &data);
273
274 if (result == Success && type == Xatom_pair)
275 for (i = items; i > 0; i--)
276 {
277 /* Convert each element of the list. */
278 }
279
280 (void) XSendEvent (x_current_display, evt.requestor, False,
281 0L, (XEvent *) &evt);
282 return;
283 }
284 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
285 {
286 if (! emacs_own_time)
287 abort ();
288
289 format = 32;
290 XChangeProperty (evt.display, evt.requestor, evt.property,
291 evt.target, format, PropModeReplace,
292 (unsigned char *) &emacs_own_time, format);
293 return;
294 }
295 else if (event.target == Xatom_delete) /* Delete our selection. */
296 {
297 if (EQ (Qnil, selection_value))
298 abort ();
299
300 x_disown_selection (event.owner, event.selection, event.time);
301
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);
305 return;
306 }
307 else if (event.target == Xatom_insert_selection)
308 {
309 Atom type;
310 int return_format;
311 unsigned long items, bytes_left;
312 unsigned char *data;
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)
318 {
319 /* Convert the first atom to (a selection) to the target
320 indicated by the second atom. */
321 }
322 }
323 else if (event.target == Xatom_insert_property)
324 {
325 Atom type;
326 int return_format;
327 unsigned long items, bytes_left;
328 unsigned char *data;
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);
333
334 if (result == Success && type == XA_STRING && return_format == 8)
335 {
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);
340 else
341 abort ();
342 }
343
344 return;
345 }
346 else if ((event.target == Xatom_text
347 || event.target == XA_STRING))
348 {
349 int size = XSTRING (selection_value)->size;
350 unsigned char *data = XSTRING (selection_value)->data;
351
352 if (EQ (Qnil, selection_value))
353 abort ();
354
355 /* Place data on requestor window's property. */
356 if (SELECTION_LENGTH (size, format)
357 <= MAX_SELECTION (x_current_display))
358 {
359 x_converting_selection = 1;
360 XChangeProperty (evt.display, evt.requestor, evt.property,
361 evt.target, format, PropModeReplace,
362 data, size);
363 if (x_selection_alloc_error)
364 {
365 x_selection_alloc_error = 0;
366 abort ();
367 }
368 x_converting_selection = 0;
369 }
370 else /* Send incrementally */
371 {
372 evt.target = Xatom_incremental;
373 incr_requestor = evt.requestor;
374 incr_property = evt.property;
375 x_converting_selection = 1;
376
377 /* Need to handle Alloc errors on these requests. */
378 XChangeProperty (evt.display, incr_requestor, incr_property,
379 Xatom_incremental, 32,
380 PropModeReplace,
381 (unsigned char *) &size, 1);
382 if (x_selection_alloc_error)
383 {
384 x_selection_alloc_error = 0;
385 x_converting_selection = 0;
386 abort ();
387 /* Now abort the send. */
388 }
389
390 incr_nbytes = size;
391 incr_value = data;
392 incr_ptr = data;
393
394 /* Ask for notification when requestor deletes property. */
395 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
396
397 /* If we're sending incrementally, perhaps block here
398 until all sent? */
399 }
400 }
401 else
402 evt.property = None;
403
404 /* Don't do this if there was an Alloc error: abort the transfer
405 by sending None. */
406 (void) XSendEvent (x_current_display, evt.requestor, False,
407 0L, (XEvent *) &evt);
408 }
409
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.
414
415 When there is no more data to send, we send a zero-length increment. */
416
417 void
418 x_send_incremental (event)
419 XPropertyEvent event;
420 {
421 if (incr_requestor
422 && incr_requestor == event.window
423 && incr_property == event.atom
424 && event.state == PropertyDelete)
425 {
426 int format = 8;
427 int length = MAX_SELECTION (x_current_display);
428 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
429
430 if (length > bytes_left) /* Also sends 0 len when finished. */
431 length = bytes_left;
432 XChangeProperty (x_current_display, incr_requestor,
433 incr_property, XA_STRING, format,
434 PropModeAppend, incr_ptr, length);
435 if (x_selection_alloc_error)
436 {
437 x_selection_alloc_error = 0;
438 x_converting_selection = 0;
439 /* Abandon the transmission. */
440 abort ();
441 }
442 if (length > 0)
443 incr_ptr += length;
444 else
445 { /* Everything's sent */
446 XSelectInput (x_current_display, incr_requestor, 0L);
447 incr_requestor = (Window) 0;
448 incr_property = (Atom) 0;
449 incr_nbytes = 0;
450 incr_value = (unsigned char *) 0;
451 incr_ptr = (unsigned char *) 0;
452 x_converting_selection = 0;
453 }
454 }
455 }
456
457 /* SELECTION REQUESTOR CODE */
458
459 /* Predicate function used to match a requested event. */
460
461 Bool
462 XCheckSelectionEvent (dpy, event, window)
463 Display *dpy;
464 XEvent *event;
465 char *window;
466 {
467 if (event->type == SelectionNotify)
468 if (event->xselection.requestor == (Window) window)
469 return True;
470
471 return False;
472 }
473
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. */
477
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.")
481 ()
482 {
483 XEvent event;
484 Lisp_Object val;
485 Time requestor_time; /* Timestamp of selection request. */
486 Window requestor_window;
487
488 if (!EQ (Qnil, Vx_selection_value)) /* We are the owner */
489 return Vx_selection_value;
490
491 BLOCK_INPUT;
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,
497 &event,
498 XCheckSelectionEvent,
499 (char *) requestor_window);
500 val = x_selection_arrival (&event, requestor_window, requestor_time);
501 UNBLOCK_INPUT;
502
503 return val;
504 }
505
506 /* Request the clipboard contents from its owner. If we are the owner,
507 simply return the clipboard string. */
508
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.")
512 ()
513 {
514 XEvent event;
515 Lisp_Object val;
516 Time requestor_time; /* Timestamp of selection request. */
517 Window requestor_window;
518
519 if (!EQ (Qnil, Vx_clipboard_value)) /* We are the owner */
520 return Vx_selection_value;
521
522 BLOCK_INPUT;
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,
529 &event,
530 XCheckSelectionEvent,
531 (char *) requestor_window);
532 val = x_selection_arrival (&event, requestor_window, requestor_time);
533 UNBLOCK_INPUT;
534
535 return val;
536 }
537
538 Lisp_Object
539 x_selection_arrival (event, requestor_window, requestor_time)
540 register XSelectionEvent *event;
541 Window requestor_window;
542 Time requestor_time;
543 {
544 int result;
545 Atom type, selection;
546 int format;
547 unsigned long items;
548 unsigned long bytes_left;
549 unsigned char *data = 0;
550 int offset = 0;
551
552 if (event->selection == XA_PRIMARY)
553 selection = Xatom_emacs_selection;
554 else if (event->selection == Xatom_clipboard)
555 selection = Xatom_clipboard_selection;
556 else
557 abort ();
558
559 if (event->requestor == requestor_window
560 && event->time == requestor_time
561 && event->property != None)
562 if (event->target != Xatom_incremental)
563 {
564 unsigned char *return_string =
565 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
566
567 do
568 {
569 result = XGetWindowProperty (x_current_display,
570 requestor_window,
571 event->property, 0L,
572 10000000L, True, XA_STRING,
573 &type, &format, &items,
574 &bytes_left, &data);
575 if (result == Success && type == XA_STRING && format == 8
576 && offset < MAX_SELECTION (x_current_display))
577 {
578 bcopy (data, return_string + offset, items);
579 offset += items;
580 }
581 XFree ((char *) data);
582 }
583 while (bytes_left);
584
585 return make_string (return_string, offset);
586 }
587 else /* Prepare incremental transfer. */
588 {
589 unsigned char *increment_value;
590 unsigned char *increment_ptr;
591 int total_size;
592 int *increment_nbytes = 0;
593
594 result = XGetWindowProperty (x_current_display, requestor_window,
595 selection, 0L, 10000000L, False,
596 event->property, &type, &format,
597 &items, &bytes_left,
598 (unsigned char **) &increment_nbytes);
599 if (result == Success)
600 {
601 XPropertyEvent property_event;
602
603 total_size = *increment_nbytes;
604 increment_value = (unsigned char *) alloca (total_size);
605 increment_ptr = increment_value;
606
607 XDeleteProperty (x_current_display, event->requestor,
608 event->property);
609 XFlush (x_current_display);
610 XFree ((char *) increment_nbytes);
611
612 do
613 { /* NOTE: this blocks. */
614 XWindowEvent (x_current_display, requestor_window,
615 PropertyChangeMask,
616 (XEvent *) &property_event);
617
618 if (property_event.atom == selection
619 && property_event.state == PropertyNewValue)
620 do
621 {
622 result = XGetWindowProperty (x_current_display,
623 requestor_window,
624 selection, 0L,
625 10000000L, True,
626 AnyPropertyType,
627 &type, &format,
628 &items, &bytes_left,
629 &data);
630 if (result == Success && type == XA_STRING
631 && format == 8)
632 {
633 bcopy (data, increment_ptr, items);
634 increment_ptr += items;
635 }
636 }
637 while (bytes_left);
638
639 }
640 while (increment_ptr < (increment_value + total_size));
641
642 return make_string (increment_value,
643 (increment_ptr - increment_value));
644 }
645 }
646
647 return Qnil;
648 }
649
650 void
651 syms_of_xselect ()
652 {
653 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
654 "The value of emacs' last cut-string.");
655 Vx_selection_value = Qnil;
656
657 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
658 "The string emacs last sent to the clipboard.");
659 Vx_clipboard_value = Qnil;
660
661 defsubr (&Sx_own_selection);
662 defsubr (&Sx_get_selection);
663 defsubr (&Sx_own_clipboard);
664 defsubr (&Sx_get_clipboard);
665 }
666 #endif /* X11 */