blob: 9759a8175ca49c79ff6dcd6853ef9b5fee53aa08 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.X11;
27
28import java.awt.datatransfer.Transferable;
29
30import java.io.ByteArrayOutputStream;
31import java.io.IOException;
32
33import java.util.Hashtable;
34import java.util.Map;
35import java.util.Set;
36import java.util.HashSet;
37import java.util.Collections;
38
39import sun.awt.AppContext;
40import sun.awt.SunToolkit;
41import sun.awt.UNIXToolkit;
42
43import sun.awt.datatransfer.DataTransferer;
44
45/**
46 * A class which interfaces with the X11 selection service.
47 */
48public class XSelection {
49
50 /* Maps atoms to XSelection instances. */
51 private static final Hashtable<XAtom, XSelection> table = new Hashtable<XAtom, XSelection>();
52 /* Prevents from parallel selection data request processing. */
53 private static final Object lock = new Object();
54 /* The property in which the owner should place the requested data. */
55 private static final XAtom selectionPropertyAtom = XAtom.get("XAWT_SELECTION");
56 /* The maximal length of the property data. */
57 public static final long MAX_LENGTH = 1000000;
58 /*
59 * The maximum data size for ChangeProperty request.
60 * 100 is for the structure prepended to the request.
61 */
62 public static final int MAX_PROPERTY_SIZE;
63 static {
64 XToolkit.awtLock();
65 try {
66 MAX_PROPERTY_SIZE =
67 (int)(XlibWrapper.XMaxRequestSize(XToolkit.getDisplay()) * 4 - 100);
68 } finally {
69 XToolkit.awtUnlock();
70 }
71 }
72 /* The selection timeout. */
73 private static long SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
74
75 /* The PropertyNotify event handler for incremental data transfer. */
76 private static final XEventDispatcher incrementalTransferHandler =
77 new IncrementalTransferHandler();
78 /* The context for the current request - protected with awtLock. */
79 private static WindowPropertyGetter propertyGetter = null;
80
81 // The orders of the lock acquisition:
82 // XClipboard -> XSelection -> awtLock.
83 // lock -> awtLock.
84
85 /* The X atom for the underlying selection. */
86 private final XAtom selectionAtom;
87 /*
88 * XClipboard.run() is to be called when we lose ownership.
89 * XClipbioard.checkChange() is to be called when tracking changes of flavors.
90 */
91 private final XClipboard clipboard;
92
93 /*
94 * Owner-related variables - protected with synchronized (this).
95 */
96
97 /* The contents supplied by the current owner. */
98 private Transferable contents = null;
99 /* The format-to-flavor map for the current owner. */
100 private Map formatMap = null;
101 /* The formats supported by the current owner was set. */
102 private long[] formats = null;
103 /* The AppContext in which the current owner was set. */
104 private AppContext appContext = null;
105 // The X server time of the last XConvertSelection() call;
106 // protected with 'lock' and awtLock.
107 private static long lastRequestServerTime;
108 /* The time at which the current owner was set. */
109 private long ownershipTime = 0;
110 // True if we are the owner of this selection.
111 private boolean isOwner;
112 // The property in which the owner should place requested targets
113 // when tracking changes of available data flavors (practically targets).
114 private volatile XAtom targetsPropertyAtom;
115 // A set of these property atoms.
116 private static volatile Set targetsPropertyAtoms;
117 // The flag used not to call XConvertSelection() if the previous SelectionNotify
118 // has not been processed by checkChange().
119 private volatile boolean isSelectionNotifyProcessed;
120 // Time of calling XConvertSelection().
121 private long convertSelectionTime;
122
123
124 static {
125 XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
126 new SelectionEventHandler());
127 }
128
129 /*
130 * Returns the XSelection object for the specified selection atom or
131 * <code>null</code> if none exists.
132 */
133 static XSelection getSelection(XAtom atom) {
134 return table.get(atom);
135 }
136
137 /**
138 * Creates a selection object.
139 *
140 * @param atom the selection atom.
141 * @param clpbrd the corresponding clipoboard
142 * @exception NullPointerException if atom is <code>null</code>.
143 */
144 public XSelection(XAtom atom, XClipboard clpbrd) {
145 if (atom == null) {
146 throw new NullPointerException("Null atom");
147 }
148 selectionAtom = atom;
149 clipboard = clpbrd;
150 table.put(selectionAtom, this);
151 }
152
153 public XAtom getSelectionAtom() {
154 return selectionAtom;
155 }
156
157 void initializeSelectionForTrackingChanges() {
158 targetsPropertyAtom = XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selectionAtom.getName());
159 if (targetsPropertyAtoms == null) {
160 targetsPropertyAtoms = Collections.synchronizedSet(new HashSet(2));
161 }
162 targetsPropertyAtoms.add(Long.valueOf(targetsPropertyAtom.getAtom()));
163 // for XConvertSelection() to be called for the first time in getTargetsDelayed()
164 isSelectionNotifyProcessed = true;
165 }
166
167 void deinitializeSelectionForTrackingChanges() {
168 if (targetsPropertyAtoms != null && targetsPropertyAtom != null) {
169 targetsPropertyAtoms.remove(Long.valueOf(targetsPropertyAtom.getAtom()));
170 }
171 isSelectionNotifyProcessed = false;
172 }
173
174 public synchronized boolean setOwner(Transferable contents, Map formatMap,
175 long[] formats, long time) {
176 long owner = XWindow.getXAWTRootWindow().getWindow();
177 long selection = selectionAtom.getAtom();
178
179 // ICCCM prescribes that CurrentTime should not be used for SetSelectionOwner.
180 if (time == XlibWrapper.CurrentTime) {
181 time = XToolkit.getCurrentServerTime();
182 }
183
184 this.contents = contents;
185 this.formatMap = formatMap;
186 this.formats = formats;
187 this.appContext = AppContext.getAppContext();
188 this.ownershipTime = time;
189
190 XToolkit.awtLock();
191 try {
192 XlibWrapper.XSetSelectionOwner(XToolkit.getDisplay(),
193 selection, owner, time);
194 if (XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
195 selection) != owner) {
196
197 reset();
198 return false;
199 }
200 isOwner = true;
201 if (clipboard != null) {
202 clipboard.checkChangeHere(contents);
203 }
204 return true;
205 } finally {
206 XToolkit.awtUnlock();
207 }
208 }
209
210 /**
211 * Blocks the current thread till SelectionNotify or PropertyNotify (in case of INCR transfer) arrives.
212 */
213 private static void waitForSelectionNotify(WindowPropertyGetter dataGetter) throws InterruptedException {
214 long startTime = System.currentTimeMillis();
215 XToolkit.awtLock();
216 try {
217 do {
218 DataTransferer.getInstance().processDataConversionRequests();
219 XToolkit.awtLockWait(250);
220 } while (propertyGetter == dataGetter && System.currentTimeMillis() < startTime + SELECTION_TIMEOUT);
221 } finally {
222 XToolkit.awtUnlock();
223 }
224 }
225
226 /*
227 * Returns the list of atoms that represent the targets for which an attempt
228 * to convert the current selection will succeed.
229 */
230 public long[] getTargets(long time) {
231 if (XToolkit.isToolkitThread()) {
232 throw new Error("UNIMPLEMENTED");
233 }
234
235 long[] formats = null;
236
237 synchronized (lock) {
238 SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
239
240 WindowPropertyGetter targetsGetter =
241 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
242 selectionPropertyAtom, 0, MAX_LENGTH,
243 true, XlibWrapper.AnyPropertyType);
244
245 try {
246 XToolkit.awtLock();
247 try {
248 propertyGetter = targetsGetter;
249 lastRequestServerTime = time;
250
251 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
252 getSelectionAtom().getAtom(),
253 XDataTransferer.TARGETS_ATOM.getAtom(),
254 selectionPropertyAtom.getAtom(),
255 XWindow.getXAWTRootWindow().getWindow(),
256 time);
257
258 // If the owner doesn't respond within the
259 // SELECTION_TIMEOUT, we report conversion failure.
260 try {
261 waitForSelectionNotify(targetsGetter);
262 } catch (InterruptedException ie) {
263 return new long[0];
264 } finally {
265 propertyGetter = null;
266 }
267 } finally {
268 XToolkit.awtUnlock();
269 }
270 formats = getFormats(targetsGetter);
271 } finally {
272 targetsGetter.dispose();
273 }
274 }
275 return formats;
276 }
277
278 private static long[] getFormats(WindowPropertyGetter targetsGetter) {
279 long[] formats = null;
280
281 if (targetsGetter.isExecuted() && !targetsGetter.isDisposed() &&
282 (targetsGetter.getActualType() == XAtom.XA_ATOM ||
283 targetsGetter.getActualType() == XDataTransferer.TARGETS_ATOM.getAtom()) &&
284 targetsGetter.getActualFormat() == 32) {
285
286 int count = (int)targetsGetter.getNumberOfItems();
287 if (count > 0) {
288 long atoms = targetsGetter.getData();
289 formats = new long[count];
290 for (int index = 0; index < count; index++) {
291 formats[index] =
292 Native.getLong(atoms+index*XAtom.getAtomSize());
293 }
294 }
295 }
296
297 return formats != null ? formats : new long[0];
298 }
299
300 // checkChange() will be called on SelectionNotify
301 void getTargetsDelayed() {
302 XToolkit.awtLock();
303 try {
304 long curTime = System.currentTimeMillis();
305 if (isSelectionNotifyProcessed || curTime >= convertSelectionTime + SELECTION_TIMEOUT) {
306 convertSelectionTime = curTime;
307 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
308 getSelectionAtom().getAtom(),
309 XDataTransferer.TARGETS_ATOM.getAtom(),
310 targetsPropertyAtom.getAtom(),
311 XWindow.getXAWTRootWindow().getWindow(),
312 XlibWrapper.CurrentTime);
313 isSelectionNotifyProcessed = false;
314 }
315 } finally {
316 XToolkit.awtUnlock();
317 }
318 }
319
320 /*
321 * Requests the selection data in the specified format and returns
322 * the data provided by the owner.
323 */
324 public byte[] getData(long format, long time) throws IOException {
325 if (XToolkit.isToolkitThread()) {
326 throw new Error("UNIMPLEMENTED");
327 }
328
329 byte[] data = null;
330
331 synchronized (lock) {
332 SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
333
334 WindowPropertyGetter dataGetter =
335 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
336 selectionPropertyAtom, 0, MAX_LENGTH,
337 false, // don't delete to handle INCR properly.
338 XlibWrapper.AnyPropertyType);
339
340 try {
341 XToolkit.awtLock();
342 try {
343 propertyGetter = dataGetter;
344 lastRequestServerTime = time;
345
346 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
347 getSelectionAtom().getAtom(),
348 format,
349 selectionPropertyAtom.getAtom(),
350 XWindow.getXAWTRootWindow().getWindow(),
351 time);
352
353 // If the owner doesn't respond within the
354 // SELECTION_TIMEOUT, we report conversion failure.
355 try {
356 waitForSelectionNotify(dataGetter);
357 } catch (InterruptedException ie) {
358 return new byte[0];
359 } finally {
360 propertyGetter = null;
361 }
362 } finally {
363 XToolkit.awtUnlock();
364 }
365 if (!dataGetter.isExecuted()) {
366 throw new IOException("Owner timed out");
367 }
368
369 if (dataGetter.isDisposed()) {
370 throw new IOException("Owner failed to convert data");
371 }
372
373 // Handle incremental transfer.
374 if (dataGetter.getActualType() ==
375 XDataTransferer.INCR_ATOM.getAtom()) {
376
377 if (dataGetter.getActualFormat() != 32) {
378 throw new IOException("Unsupported INCR format: " +
379 dataGetter.getActualFormat());
380 }
381
382 int count = (int)dataGetter.getNumberOfItems();
383
384 if (count <= 0) {
385 throw new IOException("INCR data is missed.");
386 }
387
388 long ptr = dataGetter.getData();
389
390 int len = 0;
391
392 {
393 // Following Xt sources use the last element.
394 long longLength = Native.getLong(ptr, count-1);
395
396 if (longLength <= 0) {
397 return new byte[0];
398 }
399
400 if (longLength > Integer.MAX_VALUE) {
401 throw new IOException("Can't handle large data block: "
402 + longLength + " bytes");
403 }
404
405 len = (int)longLength;
406 }
407
408 dataGetter.dispose();
409
410 ByteArrayOutputStream dataStream = new ByteArrayOutputStream(len);
411
412 while (true) {
413 WindowPropertyGetter incrDataGetter =
414 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
415 selectionPropertyAtom,
416 0, MAX_LENGTH, false,
417 XlibWrapper.AnyPropertyType);
418
419 try {
420 XToolkit.awtLock();
421 XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
422 incrementalTransferHandler);
423
424 propertyGetter = incrDataGetter;
425
426 try {
427 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
428 XWindow.getXAWTRootWindow().getWindow(),
429 selectionPropertyAtom.getAtom());
430
431 // If the owner doesn't respond within the
432 // SELECTION_TIMEOUT, we terminate incremental
433 // transfer.
434 waitForSelectionNotify(incrDataGetter);
435 } catch (InterruptedException ie) {
436 break;
437 } finally {
438 propertyGetter = null;
439 XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
440 incrementalTransferHandler);
441 XToolkit.awtUnlock();
442 }
443
444 // The owner didn't respond - terminate the transfer.
445 if (!incrDataGetter.isExecuted()) {
446 throw new IOException("Owner timed out");
447 }
448
449 if (incrDataGetter.isDisposed()) {
450 throw new IOException("Owner failed to convert data");
451 }
452
453 if (incrDataGetter.getActualFormat() != 8) {
454 throw new IOException("Unsupported data format: " +
455 incrDataGetter.getActualFormat());
456 }
457
458 count = (int)incrDataGetter.getNumberOfItems();
459
460 if (count == 0) {
461 break;
462 }
463
464 if (count > 0) {
465 ptr = incrDataGetter.getData();
466 for (int index = 0; index < count; index++) {
467 dataStream.write(Native.getByte(ptr + index));
468 }
469 }
470
471 data = dataStream.toByteArray();
472
473 } finally {
474 incrDataGetter.dispose();
475 }
476 }
477 } else {
478 XToolkit.awtLock();
479 try {
480 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
481 XWindow.getXAWTRootWindow().getWindow(),
482 selectionPropertyAtom.getAtom());
483 } finally {
484 XToolkit.awtUnlock();
485 }
486
487 if (dataGetter.getActualFormat() != 8) {
488 throw new IOException("Unsupported data format: " +
489 dataGetter.getActualFormat());
490 }
491
492 int count = (int)dataGetter.getNumberOfItems();
493 if (count > 0) {
494 data = new byte[count];
495 long ptr = dataGetter.getData();
496 for (int index = 0; index < count; index++) {
497 data[index] = Native.getByte(ptr + index);
498 }
499 }
500 }
501 } finally {
502 dataGetter.dispose();
503 }
504 }
505
506 return data != null ? data : new byte[0];
507 }
508
509 // To be MT-safe this method should be called under awtLock.
510 boolean isOwner() {
511 return isOwner;
512 }
513
514 public void lostOwnership() {
515 isOwner = false;
516 if (clipboard != null) {
517 clipboard.run();
518 }
519 }
520
521 public synchronized void reset() {
522 contents = null;
523 formatMap = null;
524 formats = null;
525 appContext = null;
526 ownershipTime = 0;
527 }
528
529 // Converts the data to the 'format' and if the conversion succeeded stores
530 // the data in the 'property' on the 'requestor' window.
531 // Returns true if the conversion succeeded.
532 private boolean convertAndStore(long requestor, long format, long property) {
533 int dataFormat = 8; /* Can choose between 8,16,32. */
534 byte[] byteData = null;
535 long nativeDataPtr = 0;
536 int count = 0;
537
538 try {
539 SunToolkit.insertTargetMapping(this, appContext);
540
541 byteData = DataTransferer.getInstance().convertData(this,
542 contents,
543 format,
544 formatMap,
545 XToolkit.isToolkitThread());
546 } catch (IOException ioe) {
547 return false;
548 }
549
550 if (byteData == null) {
551 return false;
552 }
553
554 count = byteData.length;
555
556 try {
557 if (count > 0) {
558 if (count <= MAX_PROPERTY_SIZE) {
559 nativeDataPtr = Native.toData(byteData);
560 } else {
561 // Initiate incremental data transfer.
562 new IncrementalDataProvider(requestor, property, format, 8,
563 byteData);
564
565 nativeDataPtr =
566 XlibWrapper.unsafe.allocateMemory(XAtom.getAtomSize());
567
568 Native.putLong(nativeDataPtr, (long)count);
569
570 format = XDataTransferer.INCR_ATOM.getAtom();
571 dataFormat = 32;
572 count = 1;
573 }
574
575 }
576
577 XToolkit.awtLock();
578 try {
579 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor, property,
580 format, dataFormat,
581 XlibWrapper.PropModeReplace,
582 nativeDataPtr, count);
583 } finally {
584 XToolkit.awtUnlock();
585 }
586 } finally {
587 if (nativeDataPtr != 0) {
588 XlibWrapper.unsafe.freeMemory(nativeDataPtr);
589 nativeDataPtr = 0;
590 }
591 }
592
593 return true;
594 }
595
596 private void handleSelectionRequest(XSelectionRequestEvent xsre) {
597 long property = xsre.get_property();
598 long requestor = xsre.get_requestor();
599 long requestTime = xsre.get_time();
600 long format = xsre.get_target();
601 int dataFormat = 0;
602 boolean conversionSucceeded = false;
603
604 if (ownershipTime != 0 &&
605 (requestTime == XlibWrapper.CurrentTime ||
606 requestTime >= ownershipTime)) {
607
608 property = xsre.get_property();
609
610 // Handle MULTIPLE requests as per ICCCM.
611 if (format == XDataTransferer.MULTIPLE_ATOM.getAtom()) {
612 // The property cannot be 0 for a MULTIPLE request.
613 if (property != 0) {
614 // First retrieve the list of requested targets.
615 WindowPropertyGetter wpg =
616 new WindowPropertyGetter(requestor, XAtom.get(property), 0,
617 MAX_LENGTH, false,
618 XlibWrapper.AnyPropertyType);
619 try {
620 wpg.execute();
621
622 if (wpg.getActualFormat() == 32 &&
623 (wpg.getNumberOfItems() % 2) == 0) {
624 long count = wpg.getNumberOfItems() / 2;
625 long pairsPtr = wpg.getData();
626 boolean writeBack = false;
627 for (int i = 0; i < count; i++) {
628 long target = Native.getLong(pairsPtr, 2*i);
629 long prop = Native.getLong(pairsPtr, 2*i + 1);
630
631 if (!convertAndStore(requestor, target, prop)) {
632 // To report failure, we should replace the
633 // target atom with 0 in the MULTIPLE property.
634 Native.putLong(pairsPtr, 2*i, 0);
635 writeBack = true;
636 }
637 }
638 if (writeBack) {
639 XToolkit.awtLock();
640 try {
641 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
642 property,
643 wpg.getActualType(),
644 wpg.getActualFormat(),
645 XlibWrapper.PropModeReplace,
646 wpg.getData(),
647 wpg.getNumberOfItems());
648 } finally {
649 XToolkit.awtUnlock();
650 }
651 }
652 conversionSucceeded = true;
653 }
654 } finally {
655 wpg.dispose();
656 }
657 }
658 } else {
659
660 // Support for obsolete clients as per ICCCM.
661 if (property == 0) {
662 property = format;
663 }
664
665 if (format == XDataTransferer.TARGETS_ATOM.getAtom()) {
666 long nativeDataPtr = 0;
667 int count = 0;
668 dataFormat = 32;
669
670 // Use a local copy to avoid synchronization.
671 long[] formatsLocal = formats;
672
673 if (formatsLocal == null) {
674 throw new IllegalStateException("Not an owner.");
675 }
676
677 count = formatsLocal.length;
678
679 try {
680 if (count > 0) {
681 nativeDataPtr = Native.allocateLongArray(count);
682 Native.put(nativeDataPtr, formatsLocal);
683 }
684
685 conversionSucceeded = true;
686
687 XToolkit.awtLock();
688 try {
689 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
690 property, format, dataFormat,
691 XlibWrapper.PropModeReplace,
692 nativeDataPtr, count);
693 } finally {
694 XToolkit.awtUnlock();
695 }
696 } finally {
697 if (nativeDataPtr != 0) {
698 XlibWrapper.unsafe.freeMemory(nativeDataPtr);
699 nativeDataPtr = 0;
700 }
701 }
702 } else {
703 conversionSucceeded = convertAndStore(requestor, format,
704 property);
705 }
706 }
707 }
708
709 if (!conversionSucceeded) {
710 // Zero property indicates conversion failure.
711 property = 0;
712 }
713
714 XSelectionEvent xse = new XSelectionEvent();
715 try {
716 xse.set_type((int)XlibWrapper.SelectionNotify);
717 xse.set_send_event(true);
718 xse.set_requestor(requestor);
719 xse.set_selection(selectionAtom.getAtom());
720 xse.set_target(format);
721 xse.set_property(property);
722 xse.set_time(requestTime);
723
724 XToolkit.awtLock();
725 try {
726 XlibWrapper.XSendEvent(XToolkit.getDisplay(), requestor, false,
727 XlibWrapper.NoEventMask, xse.pData);
728 } finally {
729 XToolkit.awtUnlock();
730 }
731 } finally {
732 xse.dispose();
733 }
734 }
735
736 private static void checkChange(XSelectionEvent xse) {
737 if (targetsPropertyAtoms == null || targetsPropertyAtoms.isEmpty()) {
738 // We are not tracking changes.
739 return;
740 }
741
742 long propertyAtom = xse.get_property();
743 long[] formats = null;
744
745 if (propertyAtom == XlibWrapper.None) {
746 // We threat None property atom as "empty selection".
747 formats = new long[0];
748 } else if (!targetsPropertyAtoms.contains(Long.valueOf(propertyAtom))) {
749 return;
750 } else {
751 WindowPropertyGetter targetsGetter =
752 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
753 XAtom.get(propertyAtom), 0, MAX_LENGTH,
754 true, XlibWrapper.AnyPropertyType);
755 try {
756 targetsGetter.execute();
757 formats = getFormats(targetsGetter);
758 } finally {
759 targetsGetter.dispose();
760 }
761 }
762
763 XAtom selectionAtom = XAtom.get(xse.get_selection());
764 XSelection selection = getSelection(selectionAtom);
765 if (selection != null) {
766 selection.isSelectionNotifyProcessed = true;
767 if (selection.clipboard != null) {
768 selection.clipboard.checkChange(formats);
769 }
770 }
771 }
772
773 private static class SelectionEventHandler implements XEventDispatcher {
774 public void dispatchEvent(XEvent ev) {
775 switch (ev.get_type()) {
776 case XlibWrapper.SelectionNotify: {
777 XSelectionEvent xse = ev.get_xselection();
778 checkChange(xse);
779 XToolkit.awtLock();
780 try {
781 // Ignore the SelectionNotify event if it is not the response to our last request.
782 if (propertyGetter != null && xse.get_time() == lastRequestServerTime) {
783 // The property will be None in case of convertion failure.
784 if (xse.get_property() == selectionPropertyAtom.getAtom()) {
785 propertyGetter.execute();
786 propertyGetter = null;
787 } else if (xse.get_property() == 0) {
788 propertyGetter.dispose();
789 propertyGetter = null;
790 }
791 }
792 XToolkit.awtLockNotifyAll();
793 } finally {
794 XToolkit.awtUnlock();
795 }
796 break;
797 }
798 case XlibWrapper.SelectionRequest: {
799 XSelectionRequestEvent xsre = ev.get_xselectionrequest();
800 long atom = xsre.get_selection();
801 XSelection selection = XSelection.getSelection(XAtom.get(atom));
802
803 if (selection != null) {
804 selection.handleSelectionRequest(xsre);
805 }
806 break;
807 }
808 case XlibWrapper.SelectionClear: {
809 XSelectionClearEvent xsce = ev.get_xselectionclear();
810 long atom = xsce.get_selection();
811 XSelection selection = XSelection.getSelection(XAtom.get(atom));
812
813 if (selection != null) {
814 selection.lostOwnership();
815 }
816
817 XToolkit.awtLock();
818 try {
819 XToolkit.awtLockNotifyAll();
820 } finally {
821 XToolkit.awtUnlock();
822 }
823 break;
824 }
825 }
826 }
827 };
828
829 private static class IncrementalDataProvider implements XEventDispatcher {
830 private final long requestor;
831 private final long property;
832 private final long target;
833 private final int format;
834 private final byte[] data;
835 private int offset = 0;
836
837 // NOTE: formats other than 8 are not supported.
838 public IncrementalDataProvider(long requestor, long property,
839 long target, int format, byte[] data) {
840 if (format != 8) {
841 throw new IllegalArgumentException("Unsupported format: " + format);
842 }
843
844 this.requestor = requestor;
845 this.property = property;
846 this.target = target;
847 this.format = format;
848 this.data = data;
849
850 XWindowAttributes wattr = new XWindowAttributes();
851 try {
852 XToolkit.awtLock();
853 try {
854 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), requestor,
855 wattr.pData);
856 XlibWrapper.XSelectInput(XToolkit.getDisplay(), requestor,
857 wattr.get_your_event_mask() |
858 XlibWrapper.PropertyChangeMask);
859 } finally {
860 XToolkit.awtUnlock();
861 }
862 } finally {
863 wattr.dispose();
864 }
865 XToolkit.addEventDispatcher(requestor, this);
866 }
867
868 public void dispatchEvent(XEvent ev) {
869 switch (ev.get_type()) {
870 case XlibWrapper.PropertyNotify:
871 XPropertyEvent xpe = ev.get_xproperty();
872 if (xpe.get_window() == requestor &&
873 xpe.get_state() == XlibWrapper.PropertyDelete &&
874 xpe.get_atom() == property) {
875
876 int count = data.length - offset;
877 long nativeDataPtr = 0;
878 if (count > MAX_PROPERTY_SIZE) {
879 count = MAX_PROPERTY_SIZE;
880 }
881
882 if (count > 0) {
883 nativeDataPtr = XlibWrapper.unsafe.allocateMemory(count);
884 for (int i = 0; i < count; i++) {
885 Native.putByte(nativeDataPtr+i, data[offset + i]);
886 }
887 } else {
888 assert (count == 0);
889 // All data has been transferred.
890 // This zero-length data indicates end of transfer.
891 XToolkit.removeEventDispatcher(requestor, this);
892 }
893
894 XToolkit.awtLock();
895 try {
896 XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
897 requestor, property,
898 target, format,
899 XlibWrapper.PropModeReplace,
900 nativeDataPtr, count);
901 } finally {
902 XToolkit.awtUnlock();
903 }
904 if (nativeDataPtr != 0) {
905 XlibWrapper.unsafe.freeMemory(nativeDataPtr);
906 nativeDataPtr = 0;
907 }
908
909 offset += count;
910 }
911 }
912 }
913 }
914
915 private static class IncrementalTransferHandler implements XEventDispatcher {
916 public void dispatchEvent(XEvent ev) {
917 switch (ev.get_type()) {
918 case XlibWrapper.PropertyNotify:
919 XPropertyEvent xpe = ev.get_xproperty();
920 if (xpe.get_state() == XlibWrapper.PropertyNewValue &&
921 xpe.get_atom() == selectionPropertyAtom.getAtom()) {
922 XToolkit.awtLock();
923 try {
924 if (propertyGetter != null) {
925 propertyGetter.execute();
926 propertyGetter = null;
927 }
928 XToolkit.awtLockNotifyAll();
929 } finally {
930 XToolkit.awtUnlock();
931 }
932 }
933 break;
934 }
935 }
936 };
937}