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