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