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