(tags-loop-scan): Set default value to an error form.
[bpt/emacs.git] / src / xselect.c.old
CommitLineData
f3a0bf5c 1/* X Selection processing for emacs
ffd56f97 2 Copyright (C) 1990, 1992 Free Software Foundation.
f3a0bf5c
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
ffd56f97 8the Free Software Foundation; either version 2, or (at your option)
f3a0bf5c
JB
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config.h"
21#include "lisp.h"
22#include "xterm.h"
3c254570 23#include "buffer.h"
f676886a 24#include "frame.h"
f3a0bf5c
JB
25
26#ifdef HAVE_X11
27
28/* Macros for X Selections */
3c254570
JA
29#define MAX_SELECTION(dpy) (((dpy)->max_request_size << 2) - 100)
30#define SELECTION_LENGTH(len,format) ((len) * ((format) >> 2))
f3a0bf5c 31
1113d9db
JB
32/* The timestamp of the last input event we received from the X server. */
33unsigned long last_event_timestamp;
ffd56f97 34
f3a0bf5c 35/* t if a mouse button is depressed. */
f3a0bf5c
JB
36extern Lisp_Object Vmouse_grabbed;
37
38/* When emacs became the PRIMARY selection owner. */
39Time x_begin_selection_own;
40
3c254570
JA
41/* When emacs became the SECONDARY selection owner. */
42Time x_begin_secondary_selection_own;
43
f3a0bf5c
JB
44/* When emacs became the CLIPBOARD selection owner. */
45Time x_begin_clipboard_own;
46
47/* The value of the current CLIPBOARD selection. */
48Lisp_Object Vx_clipboard_value;
49
50/* The value of the current PRIMARY selection. */
51Lisp_Object Vx_selection_value;
52
3c254570
JA
53/* The value of the current SECONDARY selection. */
54Lisp_Object Vx_secondary_selection_value;
55
234a804b
JB
56/* Types of selections we may make. */
57Lisp_Object Qprimary, Qsecondary, Qclipboard;
3c254570
JA
58
59/* Emacs' selection property identifiers. */
f3a0bf5c 60Atom Xatom_emacs_selection;
3c254570 61Atom Xatom_emacs_secondary_selection;
f3a0bf5c
JB
62
63/* Clipboard selection atom. */
64Atom Xatom_clipboard_selection;
65
66/* Clipboard atom. */
67Atom Xatom_clipboard;
68
69/* Atom for indicating incremental selection transfer. */
70Atom Xatom_incremental;
71
72/* Atom for indicating multiple selection request list */
73Atom Xatom_multiple;
74
75/* Atom for what targets emacs handles. */
76Atom Xatom_targets;
77
78/* Atom for indicating timstamp selection request */
79Atom Xatom_timestamp;
80
81/* Atom requesting we delete our selection. */
82Atom Xatom_delete;
83
84/* Selection magic. */
85Atom Xatom_insert_selection;
86
87/* Type of property for INSERT_SELECTION. */
88Atom Xatom_pair;
89
90/* More selection magic. */
91Atom Xatom_insert_property;
92
93/* Atom for indicating property type TEXT */
94Atom Xatom_text;
95
3c254570
JA
96/* Kinds of protocol things we may receive. */
97Atom Xatom_wm_take_focus;
98Atom Xatom_wm_save_yourself;
99Atom Xatom_wm_delete_window;
100
101/* Communication with window managers. */
102Atom Xatom_wm_protocols;
103
f3a0bf5c
JB
104/* These are to handle incremental selection transfer. */
105Window incr_requestor;
106Atom incr_property;
107int incr_nbytes;
108unsigned char *incr_value;
109unsigned char *incr_ptr;
110
234a804b
JB
111/* Declarations for handling cut buffers.
112
113 Whenever we set a cut buffer or read a cut buffer's value, we cache
114 it in cut_buffer_value. We look for PropertyNotify events about
115 the CUT_BUFFER properties, and invalidate our cache accordingly.
116 We ignore PropertyNotify events that we suspect were caused by our
117 own changes to the cut buffers, so we can keep the cache valid
118 longer.
119
120 IS ALL THIS HAIR WORTH IT? Well, these functions get called every
121 time an element goes into or is retrieved from the kill ring, and
122 those ought to be quick. It's not fun in time or space to wait for
123 50k cut buffers to fly back and forth across the net. */
124
125/* The number of CUT_BUFFER properties defined under X. */
126#define NUM_CUT_BUFFERS (8)
127
128/* cut_buffer_atom[n] is the atom naming the nth cut buffer. */
129static Atom cut_buffer_atom[NUM_CUT_BUFFERS] = {
130 XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3,
131 XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7
132};
133
134/* cut_buffer_value is an eight-element vector;
135 (aref cut_buffer_value n) is the cached value of cut buffer n, or
136 Qnil if cut buffer n is unset. */
137static Lisp_Object cut_buffer_value;
138
139/* Bit N of cut_buffer_cached is true if (aref cut_buffer_value n) is
140 known to be valid. This is cleared by PropertyNotify events
141 handled by x_invalidate_cut_buffer_cache. It would be wonderful if
142 that routine could just set the appropriate element of
143 cut_buffer_value to some special value meaning "uncached", but that
144 would lose if a GC happened to be in progress.
145
146 Bit N of cut_buffer_just_set is true if cut buffer N has been set since
147 the last PropertyNotify event; since we get an event even when we set
148 the property ourselves, we should ignore one event after setting
149 a cut buffer, so we don't have to throw away our cache. */
150#ifdef __STDC__
151volatile
152#endif
153static cut_buffer_cached, cut_buffer_just_set;
154
155\f
156/* Acquiring ownership of a selection. */
f3a0bf5c 157
f3a0bf5c 158
3c254570
JA
159/* Request selection ownership if we do not already have it. */
160
161static int
162own_selection (selection_type, time)
163 Atom selection_type;
164 Time time;
f3a0bf5c
JB
165{
166 Window owner_window, selecting_window;
f3a0bf5c 167
de02ad0b
JB
168 if ((selection_type == XA_PRIMARY
169 && !NILP (Vx_selection_value))
170 || (selection_type == XA_SECONDARY
171 && !NILP (Vx_secondary_selection_value))
172 || (selection_type == Xatom_clipboard
173 && !NILP (Vx_clipboard_value)))
3c254570 174 return 1;
f3a0bf5c 175
fcb9ffc8 176 selecting_window = FRAME_X_WINDOW (selected_frame);
3c254570
JA
177 XSetSelectionOwner (x_current_display, selection_type,
178 selecting_window, time);
179 owner_window = XGetSelectionOwner (x_current_display, selection_type);
f3a0bf5c 180
ffd56f97 181 if (owner_window != selecting_window)
3c254570 182 return 0;
f3a0bf5c 183
3c254570 184 return 1;
f3a0bf5c
JB
185}
186
3c254570
JA
187/* Become the selection owner and make our data the selection value.
188 If we are already the owner, merely change data and timestamp values.
189 This avoids generating SelectionClear events for ourselves. */
f3a0bf5c 190
3c254570
JA
191DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
192 1, 2, "",
193 "Make STRING the selection value. Default is the primary selection,\n\
1113d9db
JB
194but optional second argument TYPE may specify secondary or clipboard.\n\
195\n\
196TYPE may also be cut-buffer0, indicating that Emacs should set the X\n\
197cut buffer 0 to STRING. This is for compatibility with older X\n\
198applications which still use the cut buffers; new applications should\n\
199use X selections.")
3c254570
JA
200 (string, type)
201 register Lisp_Object string, type;
f3a0bf5c 202{
3c254570
JA
203 Atom selection_type;
204 Lisp_Object val;
1113d9db 205 Time event_time = last_event_timestamp;
f3a0bf5c
JB
206 CHECK_STRING (string, 0);
207
638cb9c6
JB
208 val = Qnil;
209
3c254570 210 if (NILP (type) || EQ (type, Qprimary))
f3a0bf5c 211 {
3c254570
JA
212 BLOCK_INPUT;
213 if (own_selection (XA_PRIMARY, event_time))
f3a0bf5c 214 {
3c254570
JA
215 x_begin_selection_own = event_time;
216 val = Vx_selection_value = string;
f3a0bf5c 217 }
1113d9db 218 UNBLOCK_INPUT;
f3a0bf5c 219 }
3c254570
JA
220 else if (EQ (type, Qsecondary))
221 {
222 BLOCK_INPUT;
223 if (own_selection (XA_SECONDARY, event_time))
224 {
225 x_begin_secondary_selection_own = event_time;
226 val = Vx_secondary_selection_value = string;
227 }
228 UNBLOCK_INPUT;
229 }
230 else if (EQ (type, Qclipboard))
231 {
232 BLOCK_INPUT;
233 if (own_selection (Xatom_clipboard, event_time))
234 {
1113d9db 235 x_begin_clipboard_own = event_time;
3c254570
JA
236 val = Vx_clipboard_value = string;
237 }
1113d9db
JB
238 UNBLOCK_INPUT;
239 }
234a804b 240#if 0
1113d9db
JB
241 else if (EQ (type, Qcut_buffer0))
242 {
434e6714
JB
243 /* DECwindows and some other servers don't seem to like setting
244 properties to values larger than about 20k. For very large
245 values, they signal an error, but for intermediate values
246 they just seem to hang.
247
248 We could just truncate the request, but it's better to let
249 the user know that the strategy he/she's using isn't going to
250 work than to have it work partially, but incorrectly. */
1113d9db 251 BLOCK_INPUT;
434e6714
JB
252 if (XSTRING (string)->size > MAX_SELECTION (x_current_display))
253 {
254 XStoreBytes (x_current_display, (char *) 0, 0);
255 val = Qnil;
256 }
257 else
258 {
259 XStoreBytes (x_current_display,
260 (char *) XSTRING (string)->data,
261 XSTRING (string)->size);
262 val = string;
263 }
1113d9db 264 UNBLOCK_INPUT;
3c254570 265 }
234a804b 266#endif
3c254570
JA
267 else
268 error ("Invalid X selection type");
f3a0bf5c 269
3c254570 270 return val;
f3a0bf5c
JB
271}
272
273/* Clear our selection ownership data, as some other client has
274 become the owner. */
275
276void
277x_disown_selection (old_owner, selection, changed_owner_time)
278 Window *old_owner;
279 Atom selection;
280 Time changed_owner_time;
281{
f676886a 282 struct frame *s = x_window_to_frame (old_owner);
f3a0bf5c
JB
283
284 if (s) /* We are the owner */
285 {
286 if (selection == XA_PRIMARY)
287 {
288 x_begin_selection_own = 0;
289 Vx_selection_value = Qnil;
290 }
3c254570
JA
291 else if (selection == XA_SECONDARY)
292 {
293 x_begin_secondary_selection_own = 0;
294 Vx_secondary_selection_value = Qnil;
295 }
f3a0bf5c
JB
296 else if (selection == Xatom_clipboard)
297 {
298 x_begin_clipboard_own = 0;
299 Vx_clipboard_value = Qnil;
300 }
301 else
302 abort ();
303 }
304 else
305 abort (); /* Inconsistent state. */
306}
307
234a804b
JB
308\f
309/* Answering selection requests. */
310
f3a0bf5c
JB
311int x_selection_alloc_error;
312int x_converting_selection;
313
234a804b
JB
314/* Reply to some client's request for our selection data.
315 Data is placed in a property supplied by the requesting window.
f3a0bf5c
JB
316
317 If the data exceeds the maximum amount the server can send,
318 then prepare to send it incrementally, and reply to the client with
319 the total size of the data.
320
321 But first, check for all the other crufty stuff we could get. */
322
323void
324x_answer_selection_request (event)
325 XSelectionRequestEvent event;
326{
327 Time emacs_own_time;
328 Lisp_Object selection_value;
329 XSelectionEvent evt;
330 int format = 8; /* We have only byte sized (text) data. */
331
332 evt.type = SelectionNotify; /* Construct reply event */
333 evt.display = event.display;
334 evt.requestor = event.requestor;
335 evt.selection = event.selection;
336 evt.time = event.time;
337 evt.target = event.target;
338
339 if (event.selection == XA_PRIMARY)
340 {
341 emacs_own_time = x_begin_selection_own;
342 selection_value = Vx_selection_value;
343 }
3c254570
JA
344 else if (event.selection == XA_SECONDARY)
345 {
346 emacs_own_time = x_begin_secondary_selection_own;
347 selection_value = Vx_secondary_selection_value;
348 }
f3a0bf5c
JB
349 else if (event.selection == Xatom_clipboard)
350 {
351 emacs_own_time = x_begin_clipboard_own;
352 selection_value = Vx_clipboard_value;
353 }
354 else
355 abort ();
356
357 if (event.time != CurrentTime
358 && event.time < emacs_own_time)
359 evt.property = None;
360 else
361 {
362 if (event.property == None) /* obsolete client */
363 evt.property = event.target;
364 else
365 evt.property = event.property;
366 }
367
368 if (event.target == Xatom_targets) /* Send List of target atoms */
369 {
370 }
371 else if (event.target == Xatom_multiple) /* Recvd list: <target, prop> */
372 {
373 Atom type;
374 int return_format;
375 unsigned long items, bytes_left;
376 unsigned char *data;
377 int result, i;
378
3c254570 379 if (event.property == 0 /* 0 == NILP */
f3a0bf5c
JB
380 || event.property == None)
381 return;
382
383 result = XGetWindowProperty (event.display, event.requestor,
384 event.property, 0L, 10000000L,
385 True, Xatom_pair, &type, &return_format,
386 &items, &bytes_left, &data);
387
388 if (result == Success && type == Xatom_pair)
389 for (i = items; i > 0; i--)
390 {
391 /* Convert each element of the list. */
392 }
393
394 (void) XSendEvent (x_current_display, evt.requestor, False,
395 0L, (XEvent *) &evt);
396 return;
397 }
398 else if (event.target == Xatom_timestamp) /* Send ownership timestamp */
399 {
400 if (! emacs_own_time)
401 abort ();
402
403 format = 32;
404 XChangeProperty (evt.display, evt.requestor, evt.property,
405 evt.target, format, PropModeReplace,
3c254570 406 (unsigned char *) &emacs_own_time, 1);
f3a0bf5c
JB
407 return;
408 }
409 else if (event.target == Xatom_delete) /* Delete our selection. */
410 {
411 if (EQ (Qnil, selection_value))
412 abort ();
413
414 x_disown_selection (event.owner, event.selection, event.time);
415
3c254570 416 /* Now return property of type NILP, length 0. */
f3a0bf5c
JB
417 XChangeProperty (event.display, event.requestor, event.property,
418 0, format, PropModeReplace, (unsigned char *) 0, 0);
419 return;
420 }
421 else if (event.target == Xatom_insert_selection)
422 {
423 Atom type;
424 int return_format;
425 unsigned long items, bytes_left;
426 unsigned char *data;
427 int result = XGetWindowProperty (event.display, event.requestor,
428 event.property, 0L, 10000000L,
429 True, Xatom_pair, &type, &return_format,
430 &items, &bytes_left, &data);
431 if (result == Success && type == Xatom_pair)
432 {
433 /* Convert the first atom to (a selection) to the target
434 indicated by the second atom. */
435 }
436 }
437 else if (event.target == Xatom_insert_property)
438 {
439 Atom type;
440 int return_format;
441 unsigned long items, bytes_left;
442 unsigned char *data;
443 int result = XGetWindowProperty (event.display, event.requestor,
444 event.property, 0L, 10000000L,
445 True, XA_STRING, &type, &return_format,
446 &items, &bytes_left, &data);
447
448 if (result == Success && type == XA_STRING && return_format == 8)
449 {
450 if (event.selection == Xatom_emacs_selection)
451 Vx_selection_value = make_string (data);
3c254570
JA
452 else if (event.selection == Xatom_emacs_secondary_selection)
453 Vx_secondary_selection_value = make_string (data);
f3a0bf5c
JB
454 else if (event.selection == Xatom_clipboard_selection)
455 Vx_clipboard_value = make_string (data);
456 else
457 abort ();
458 }
459
460 return;
461 }
462 else if ((event.target == Xatom_text
463 || event.target == XA_STRING))
464 {
465 int size = XSTRING (selection_value)->size;
466 unsigned char *data = XSTRING (selection_value)->data;
467
468 if (EQ (Qnil, selection_value))
469 abort ();
470
471 /* Place data on requestor window's property. */
472 if (SELECTION_LENGTH (size, format)
473 <= MAX_SELECTION (x_current_display))
474 {
475 x_converting_selection = 1;
476 XChangeProperty (evt.display, evt.requestor, evt.property,
477 evt.target, format, PropModeReplace,
478 data, size);
479 if (x_selection_alloc_error)
480 {
481 x_selection_alloc_error = 0;
482 abort ();
483 }
484 x_converting_selection = 0;
485 }
486 else /* Send incrementally */
487 {
488 evt.target = Xatom_incremental;
489 incr_requestor = evt.requestor;
490 incr_property = evt.property;
491 x_converting_selection = 1;
492
493 /* Need to handle Alloc errors on these requests. */
494 XChangeProperty (evt.display, incr_requestor, incr_property,
495 Xatom_incremental, 32,
496 PropModeReplace,
497 (unsigned char *) &size, 1);
498 if (x_selection_alloc_error)
499 {
500 x_selection_alloc_error = 0;
501 x_converting_selection = 0;
502 abort ();
503 /* Now abort the send. */
504 }
505
506 incr_nbytes = size;
507 incr_value = data;
508 incr_ptr = data;
509
510 /* Ask for notification when requestor deletes property. */
511 XSelectInput (x_current_display, incr_requestor, PropertyChangeMask);
512
513 /* If we're sending incrementally, perhaps block here
514 until all sent? */
515 }
516 }
517 else
518 evt.property = None;
519
520 /* Don't do this if there was an Alloc error: abort the transfer
521 by sending None. */
522 (void) XSendEvent (x_current_display, evt.requestor, False,
523 0L, (XEvent *) &evt);
524}
525
526/* Send an increment of selection data in response to a PropertyNotify event.
527 The increment is placed in a property on the requestor's window.
528 When the requestor has processed the increment, it deletes the property,
529 which sends us another PropertyNotify event.
530
531 When there is no more data to send, we send a zero-length increment. */
532
533void
534x_send_incremental (event)
535 XPropertyEvent event;
536{
537 if (incr_requestor
538 && incr_requestor == event.window
539 && incr_property == event.atom
540 && event.state == PropertyDelete)
541 {
542 int format = 8;
543 int length = MAX_SELECTION (x_current_display);
544 int bytes_left = (incr_nbytes - (incr_ptr - incr_value));
545
546 if (length > bytes_left) /* Also sends 0 len when finished. */
547 length = bytes_left;
548 XChangeProperty (x_current_display, incr_requestor,
549 incr_property, XA_STRING, format,
550 PropModeAppend, incr_ptr, length);
551 if (x_selection_alloc_error)
552 {
553 x_selection_alloc_error = 0;
554 x_converting_selection = 0;
555 /* Abandon the transmission. */
556 abort ();
557 }
558 if (length > 0)
559 incr_ptr += length;
560 else
561 { /* Everything's sent */
562 XSelectInput (x_current_display, incr_requestor, 0L);
563 incr_requestor = (Window) 0;
564 incr_property = (Atom) 0;
565 incr_nbytes = 0;
566 incr_value = (unsigned char *) 0;
567 incr_ptr = (unsigned char *) 0;
568 x_converting_selection = 0;
569 }
570 }
571}
572
234a804b
JB
573\f
574/* Requesting the value of a selection. */
f3a0bf5c 575
de02ad0b
JB
576static Lisp_Object x_selection_arrival ();
577
f3a0bf5c
JB
578/* Predicate function used to match a requested event. */
579
580Bool
581XCheckSelectionEvent (dpy, event, window)
582 Display *dpy;
583 XEvent *event;
584 char *window;
585{
586 if (event->type == SelectionNotify)
587 if (event->xselection.requestor == (Window) window)
588 return True;
589
590 return False;
591}
592
3c254570
JA
593/* Request a selection value from its owner. This will block until
594 all the data is arrived. */
f3a0bf5c 595
3c254570
JA
596static Lisp_Object
597get_selection_value (type)
598 Atom type;
f3a0bf5c
JB
599{
600 XEvent event;
601 Lisp_Object val;
602 Time requestor_time; /* Timestamp of selection request. */
603 Window requestor_window;
604
f3a0bf5c 605 BLOCK_INPUT;
1113d9db 606 requestor_time = last_event_timestamp;
fcb9ffc8 607 requestor_window = FRAME_X_WINDOW (selected_frame);
3c254570 608 XConvertSelection (x_current_display, type, XA_STRING,
f3a0bf5c
JB
609 Xatom_emacs_selection, requestor_window, requestor_time);
610 XIfEvent (x_current_display,
611 &event,
612 XCheckSelectionEvent,
613 (char *) requestor_window);
614 val = x_selection_arrival (&event, requestor_window, requestor_time);
615 UNBLOCK_INPUT;
616
617 return val;
618}
619
3c254570
JA
620/* Request a selection value from the owner. If we are the owner,
621 simply return our selection value. If we are not the owner, this
622 will block until all of the data has arrived. */
f3a0bf5c 623
3c254570
JA
624DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
625 0, 1, "",
626 "Return the value of one of the selections. Default is the primary\n\
627selection, but optional argument TYPE may specify secondary or clipboard.")
628 (type)
629 register Lisp_Object type;
f3a0bf5c 630{
3c254570 631 Atom selection_type;
f3a0bf5c 632
3c254570
JA
633 if (NILP (type) || EQ (type, Qprimary))
634 {
635 if (!NILP (Vx_selection_value))
234a804b 636 return Vx_selection_value;
f3a0bf5c 637
3c254570
JA
638 return get_selection_value (XA_PRIMARY);
639 }
640 else if (EQ (type, Qsecondary))
641 {
642 if (!NILP (Vx_secondary_selection_value))
643 return Vx_secondary_selection_value;
f3a0bf5c 644
3c254570
JA
645 return get_selection_value (XA_SECONDARY);
646 }
647 else if (EQ (type, Qclipboard))
648 {
649 if (!NILP (Vx_clipboard_value))
650 return Vx_clipboard_value;
651
652 return get_selection_value (Xatom_clipboard);
653 }
234a804b 654#if 0
1113d9db
JB
655 else if (EQ (type, Qcut_buffer0))
656 {
657 char *data;
658 int size;
659 Lisp_Object string;
660
661 BLOCK_INPUT;
662 data = XFetchBytes (x_current_display, &size);
663 if (data == 0)
664 string = Qnil;
665 else
666 string = make_string (data, size);
667 UNBLOCK_INPUT;
668
669 return string;
670 }
234a804b 671#endif
3c254570
JA
672 else
673 error ("Invalid X selection type");
f3a0bf5c
JB
674}
675
de02ad0b 676static Lisp_Object
f3a0bf5c
JB
677x_selection_arrival (event, requestor_window, requestor_time)
678 register XSelectionEvent *event;
679 Window requestor_window;
680 Time requestor_time;
681{
682 int result;
683 Atom type, selection;
684 int format;
685 unsigned long items;
686 unsigned long bytes_left;
687 unsigned char *data = 0;
688 int offset = 0;
689
690 if (event->selection == XA_PRIMARY)
691 selection = Xatom_emacs_selection;
3c254570
JA
692 else if (event->selection == XA_SECONDARY)
693 selection = Xatom_emacs_secondary_selection;
f3a0bf5c
JB
694 else if (event->selection == Xatom_clipboard)
695 selection = Xatom_clipboard_selection;
696 else
697 abort ();
698
699 if (event->requestor == requestor_window
700 && event->time == requestor_time
701 && event->property != None)
702 if (event->target != Xatom_incremental)
703 {
704 unsigned char *return_string =
705 (unsigned char *) alloca (MAX_SELECTION (x_current_display));
706
707 do
708 {
3c254570 709 result = XGetWindowProperty (x_current_display, requestor_window,
f3a0bf5c
JB
710 event->property, 0L,
711 10000000L, True, XA_STRING,
712 &type, &format, &items,
713 &bytes_left, &data);
714 if (result == Success && type == XA_STRING && format == 8
715 && offset < MAX_SELECTION (x_current_display))
716 {
717 bcopy (data, return_string + offset, items);
718 offset += items;
719 }
720 XFree ((char *) data);
721 }
722 while (bytes_left);
723
724 return make_string (return_string, offset);
725 }
726 else /* Prepare incremental transfer. */
727 {
728 unsigned char *increment_value;
729 unsigned char *increment_ptr;
730 int total_size;
731 int *increment_nbytes = 0;
732
733 result = XGetWindowProperty (x_current_display, requestor_window,
734 selection, 0L, 10000000L, False,
735 event->property, &type, &format,
736 &items, &bytes_left,
737 (unsigned char **) &increment_nbytes);
738 if (result == Success)
739 {
740 XPropertyEvent property_event;
741
742 total_size = *increment_nbytes;
743 increment_value = (unsigned char *) alloca (total_size);
744 increment_ptr = increment_value;
745
746 XDeleteProperty (x_current_display, event->requestor,
747 event->property);
748 XFlush (x_current_display);
749 XFree ((char *) increment_nbytes);
750
751 do
752 { /* NOTE: this blocks. */
753 XWindowEvent (x_current_display, requestor_window,
754 PropertyChangeMask,
755 (XEvent *) &property_event);
756
757 if (property_event.atom == selection
758 && property_event.state == PropertyNewValue)
759 do
760 {
761 result = XGetWindowProperty (x_current_display,
762 requestor_window,
763 selection, 0L,
764 10000000L, True,
765 AnyPropertyType,
766 &type, &format,
767 &items, &bytes_left,
768 &data);
769 if (result == Success && type == XA_STRING
770 && format == 8)
771 {
772 bcopy (data, increment_ptr, items);
773 increment_ptr += items;
774 }
775 }
776 while (bytes_left);
777
778 }
779 while (increment_ptr < (increment_value + total_size));
780
781 return make_string (increment_value,
782 (increment_ptr - increment_value));
783 }
784 }
785
786 return Qnil;
787}
788
234a804b
JB
789\f
790/* Cut buffer management. */
791
792DEFUN ("x-get-cut-buffer", Fx_get_cut_buffer, Sx_get_cut_buffer, 0, 1, "",
793 "Return the value of cut buffer N, or nil if it is unset.\n\
794If N is omitted, it defaults to zero.\n\
795Note that cut buffers have some problems that selections don't; try to\n\
796write your code to use cut buffers only for backward compatibility,\n\
797and use selections for the serious work.")
798 (n)
799 Lisp_Object n;
800{
801 int buf_num;
802
803 if (NILP (n))
804 buf_num = 0;
805 else
806 {
807 CHECK_NUMBER (n, 0);
808 buf_num = XINT (n);
809 }
810
8e6bef18
JB
811 if (buf_num < 0 || buf_num >= NUM_CUT_BUFFERS)
812 error ("cut buffer numbers must be from zero to seven");
234a804b
JB
813
814 {
815 Lisp_Object value;
816
817 /* Note that no PropertyNotify events will be processed while
818 input is blocked. */
819 BLOCK_INPUT;
820
821 if (cut_buffer_cached & (1 << buf_num))
822 value = XVECTOR (cut_buffer_value)->contents[buf_num];
823 else
824 {
825 /* Our cache is invalid; retrieve the property's value from
826 the server. */
827 int buf_len;
828 char *buf = XFetchBuffer (x_current_display, &buf_len, buf_num);
829
830 if (buf_len == 0)
831 value = Qnil;
832 else
833 value = make_string (buf, buf_len);
834
835 XVECTOR (cut_buffer_value)->contents[buf_num] = value;
836 cut_buffer_cached |= (1 << buf_num);
837
838 XFree (buf);
839 }
840
841 UNBLOCK_INPUT;
842
843 return value;
844 }
845}
846
847DEFUN ("x-set-cut-buffer", Fx_set_cut_buffer, Sx_set_cut_buffer, 2, 2, "",
848 "Set the value of cut buffer N to STRING.\n\
849Note that cut buffers have some problems that selections don't; try to\n\
850write your code to use cut buffers only for backward compatibility,\n\
851and use selections for the serious work.")
852 (n, string)
853 Lisp_Object n, string;
854{
855 int buf_num;
856
857 CHECK_NUMBER (n, 0);
858 CHECK_STRING (string, 1);
859
860 buf_num = XINT (n);
861
8e6bef18
JB
862 if (buf_num < 0 || buf_num >= NUM_CUT_BUFFERS)
863 error ("cut buffer numbers must be from zero to seven");
234a804b
JB
864
865 BLOCK_INPUT;
866
867 /* DECwindows and some other servers don't seem to like setting
868 properties to values larger than about 20k. For very large
869 values, they signal an error, but for intermediate values they
870 just seem to hang.
871
872 We could just truncate the request, but it's better to let the
873 user know that the strategy he/she's using isn't going to work
874 than to have it work partially, but incorrectly. */
875
876 if (XSTRING (string)->size == 0
877 || XSTRING (string)->size > MAX_SELECTION (x_current_display))
878 {
879 XStoreBuffer (x_current_display, (char *) 0, 0, buf_num);
880 string = Qnil;
881 }
882 else
883 {
884 XStoreBuffer (x_current_display,
885 (char *) XSTRING (string)->data, XSTRING (string)->size,
886 buf_num);
887 }
888
889 XVECTOR (cut_buffer_value)->contents[buf_num] = string;
890 cut_buffer_cached |= (1 << buf_num);
891 cut_buffer_just_set |= (1 << buf_num);
892
893 UNBLOCK_INPUT;
894
895 return string;
896}
897
898/* Ask the server to send us an event if any cut buffer is modified. */
899
900void
901x_watch_cut_buffer_cache ()
902{
903 XSelectInput (x_current_display, ROOT_WINDOW, PropertyChangeMask);
904}
905
906/* The server has told us that a cut buffer has been modified; deal with that.
907 Note that this function is called at interrupt level. */
908void
909x_invalidate_cut_buffer_cache (XPropertyEvent *event)
910{
911 int i;
912
913 /* See which cut buffer this is about, if any. */
914 for (i = 0; i < NUM_CUT_BUFFERS; i++)
915 if (event->atom == cut_buffer_atom[i])
916 {
917 int mask = (1 << i);
918
919 if (cut_buffer_just_set & mask)
920 cut_buffer_just_set &= ~mask;
921 else
922 cut_buffer_cached &= ~mask;
923
924 break;
925 }
926}
927
928\f
929/* Bureaucracy. */
930
f3a0bf5c
JB
931void
932syms_of_xselect ()
933{
934 DEFVAR_LISP ("x-selection-value", &Vx_selection_value,
935 "The value of emacs' last cut-string.");
936 Vx_selection_value = Qnil;
937
3c254570
JA
938 DEFVAR_LISP ("x-secondary-selection-value", &Vx_secondary_selection_value,
939 "The value of emacs' last secondary cut-string.");
940 Vx_secondary_selection_value = Qnil;
941
f3a0bf5c
JB
942 DEFVAR_LISP ("x-clipboard-value", &Vx_clipboard_value,
943 "The string emacs last sent to the clipboard.");
944 Vx_clipboard_value = Qnil;
945
3c254570
JA
946 Qprimary = intern ("primary");
947 staticpro (&Qprimary);
948 Qsecondary = intern ("secondary");
949 staticpro (&Qsecondary);
950 Qclipboard = intern ("clipboard");
951 staticpro (&Qclipboard);
952
f3a0bf5c 953 defsubr (&Sx_own_selection);
3c254570 954 defsubr (&Sx_selection_value);
234a804b
JB
955
956 cut_buffer_value = Fmake_vector (make_number (NUM_CUT_BUFFERS), Qnil);
957 staticpro (&cut_buffer_value);
958
959 defsubr (&Sx_get_cut_buffer);
960 defsubr (&Sx_set_cut_buffer);
f3a0bf5c
JB
961}
962#endif /* X11 */