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