Mercurial > emacs
comparison src/=xselect.c.old @ 159:e7abb99c2a38
Initial revision
author | Jim Blandy <jimb@redhat.com> |
---|---|
date | Sat, 05 Jan 1991 19:03:03 +0000 |
parents | |
children | 8c615e453683 |
comparison
equal
deleted
inserted
replaced
158:a4e766535a97 | 159:e7abb99c2a38 |
---|---|
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 */ |