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