Commit | Line | Data |
---|---|---|
f3a0bf5c | 1 | /* X Selection processing for emacs |
ffd56f97 | 2 | Copyright (C) 1990, 1992 Free Software Foundation. |
f3a0bf5c JB |
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 | |
ffd56f97 | 8 | the Free Software Foundation; either version 2, or (at your option) |
f3a0bf5c JB |
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" | |
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. */ |
33 | unsigned long last_event_timestamp; | |
ffd56f97 | 34 | |
f3a0bf5c | 35 | /* t if a mouse button is depressed. */ |
f3a0bf5c JB |
36 | extern Lisp_Object Vmouse_grabbed; |
37 | ||
38 | /* When emacs became the PRIMARY selection owner. */ | |
39 | Time x_begin_selection_own; | |
40 | ||
3c254570 JA |
41 | /* When emacs became the SECONDARY selection owner. */ |
42 | Time x_begin_secondary_selection_own; | |
43 | ||
f3a0bf5c JB |
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 | ||
3c254570 JA |
53 | /* The value of the current SECONDARY selection. */ |
54 | Lisp_Object Vx_secondary_selection_value; | |
55 | ||
234a804b JB |
56 | /* Types of selections we may make. */ |
57 | Lisp_Object Qprimary, Qsecondary, Qclipboard; | |
3c254570 JA |
58 | |
59 | /* Emacs' selection property identifiers. */ | |
f3a0bf5c | 60 | Atom Xatom_emacs_selection; |
3c254570 | 61 | Atom Xatom_emacs_secondary_selection; |
f3a0bf5c JB |
62 | |
63 | /* Clipboard selection atom. */ | |
64 | Atom Xatom_clipboard_selection; | |
65 | ||
66 | /* Clipboard atom. */ | |
67 | Atom Xatom_clipboard; | |
68 | ||
69 | /* Atom for indicating incremental selection transfer. */ | |
70 | Atom Xatom_incremental; | |
71 | ||
72 | /* Atom for indicating multiple selection request list */ | |
73 | Atom Xatom_multiple; | |
74 | ||
75 | /* Atom for what targets emacs handles. */ | |
76 | Atom Xatom_targets; | |
77 | ||
78 | /* Atom for indicating timstamp selection request */ | |
79 | Atom Xatom_timestamp; | |
80 | ||
81 | /* Atom requesting we delete our selection. */ | |
82 | Atom Xatom_delete; | |
83 | ||
84 | /* Selection magic. */ | |
85 | Atom Xatom_insert_selection; | |
86 | ||
87 | /* Type of property for INSERT_SELECTION. */ | |
88 | Atom Xatom_pair; | |
89 | ||
90 | /* More selection magic. */ | |
91 | Atom Xatom_insert_property; | |
92 | ||
93 | /* Atom for indicating property type TEXT */ | |
94 | Atom Xatom_text; | |
95 | ||
3c254570 JA |
96 | /* Kinds of protocol things we may receive. */ |
97 | Atom Xatom_wm_take_focus; | |
98 | Atom Xatom_wm_save_yourself; | |
99 | Atom Xatom_wm_delete_window; | |
100 | ||
101 | /* Communication with window managers. */ | |
102 | Atom Xatom_wm_protocols; | |
103 | ||
f3a0bf5c JB |
104 | /* These are to handle incremental selection transfer. */ |
105 | Window incr_requestor; | |
106 | Atom incr_property; | |
107 | int incr_nbytes; | |
108 | unsigned char *incr_value; | |
109 | unsigned 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. */ | |
129 | static 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. */ | |
137 | static 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__ | |
151 | volatile | |
152 | #endif | |
153 | static 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 | ||
161 | static int | |
162 | own_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 |
191 | DEFUN ("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 |
194 | but optional second argument TYPE may specify secondary or clipboard.\n\ |
195 | \n\ | |
196 | TYPE may also be cut-buffer0, indicating that Emacs should set the X\n\ | |
197 | cut buffer 0 to STRING. This is for compatibility with older X\n\ | |
198 | applications which still use the cut buffers; new applications should\n\ | |
199 | use 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 | ||
276 | void | |
277 | x_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 |
311 | int x_selection_alloc_error; |
312 | int 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 | ||
323 | void | |
324 | x_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 | ||
533 | void | |
534 | x_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 |
576 | static Lisp_Object x_selection_arrival (); |
577 | ||
f3a0bf5c JB |
578 | /* Predicate function used to match a requested event. */ |
579 | ||
580 | Bool | |
581 | XCheckSelectionEvent (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 |
596 | static Lisp_Object |
597 | get_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 |
624 | DEFUN ("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\ | |
627 | selection, 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 | 676 | static Lisp_Object |
f3a0bf5c JB |
677 | x_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 | ||
792 | DEFUN ("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\ | |
794 | If N is omitted, it defaults to zero.\n\ | |
795 | Note that cut buffers have some problems that selections don't; try to\n\ | |
796 | write your code to use cut buffers only for backward compatibility,\n\ | |
797 | and 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 | ||
847 | DEFUN ("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\ | |
849 | Note that cut buffers have some problems that selections don't; try to\n\ | |
850 | write your code to use cut buffers only for backward compatibility,\n\ | |
851 | and 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 | ||
900 | void | |
901 | x_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. */ | |
908 | void | |
909 | x_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 |
931 | void |
932 | syms_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 */ |