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