1 /* 2 * Copyright (c) 2003, 2008, 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.Point; 29 30 import java.awt.dnd.DnDConstants; 31 32 import java.awt.event.MouseEvent; 33 34 import java.io.IOException; 35 36 import sun.util.logging.PlatformLogger; 37 38 import sun.misc.Unsafe; 39 40 /** 41 * XDropTargetProtocol implementation for XDnD protocol. 42 * 43 * @since 1.5 44 */ 45 class XDnDDropTargetProtocol extends XDropTargetProtocol { 46 private static final PlatformLogger logger = 47 PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDropTargetProtocol"); 48 49 private static final Unsafe unsafe = XlibWrapper.unsafe; 50 51 private long sourceWindow = 0; 52 private long sourceWindowMask = 0; 53 private int sourceProtocolVersion = 0; 54 private int sourceActions = DnDConstants.ACTION_NONE; 55 private long[] sourceFormats = null; 56 private boolean trackSourceActions = false; 57 private int userAction = DnDConstants.ACTION_NONE; 58 private int sourceX = 0; 59 private int sourceY = 0; 60 private XWindow targetXWindow = null; 61 62 // XEmbed stuff. 63 private long prevCtxt = 0; 64 private boolean overXEmbedClient = false; 65 66 protected XDnDDropTargetProtocol(XDropTargetProtocolListener listener) { 67 super(listener); 68 } 69 70 /** 71 * Creates an instance associated with the specified listener. 72 * 73 * @throws NullPointerException if listener is <code>null</code>. 74 */ 75 static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) { 76 return new XDnDDropTargetProtocol(listener); 77 } 78 79 public String getProtocolName() { 80 return XDragAndDropProtocols.XDnD; 81 } 82 83 public void registerDropTarget(long window) { 84 assert XToolkit.isAWTLockHeldByCurrentThread(); 85 86 long data = Native.allocateLongArray(1); 87 88 try { 89 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); 90 91 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 92 XDnDConstants.XA_XdndAware.setAtomData(window, XAtom.XA_ATOM, data, 1); 93 XToolkit.RESTORE_XERROR_HANDLER(); 94 95 if (XToolkit.saved_error != null && 96 XToolkit.saved_error.get_error_code() != XConstants.Success) { 97 throw new XException("Cannot write XdndAware property"); 98 } 99 } finally { 100 unsafe.freeMemory(data); 101 data = 0; 102 } 103 } 104 105 public void unregisterDropTarget(long window) { 106 assert XToolkit.isAWTLockHeldByCurrentThread(); 107 108 XDnDConstants.XA_XdndAware.DeleteProperty(window); 109 } 110 111 public void registerEmbedderDropSite(long embedder) { 112 assert XToolkit.isAWTLockHeldByCurrentThread(); 113 114 boolean overriden = false; 115 int version = 0; 116 long proxy = 0; 117 long newProxy = XDropTargetRegistry.getDnDProxyWindow(); 118 int status = 0; 119 120 WindowPropertyGetter wpg1 = 121 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndAware, 0, 1, 122 false, XConstants.AnyPropertyType); 123 124 try { 125 status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 126 127 if (status == XConstants.Success && 128 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { 129 130 overriden = true; 131 version = (int)Native.getLong(wpg1.getData()); 132 } 133 } finally { 134 wpg1.dispose(); 135 } 136 137 /* XdndProxy is not supported for prior to XDnD version 4 */ 138 if (overriden && version >= 4) { 139 WindowPropertyGetter wpg2 = 140 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndProxy, 141 0, 1, false, XAtom.XA_WINDOW); 142 143 try { 144 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 145 146 if (status == XConstants.Success && 147 wpg2.getData() != 0 && 148 wpg2.getActualType() == XAtom.XA_WINDOW) { 149 150 proxy = Native.getLong(wpg2.getData()); 151 } 152 } finally { 153 wpg2.dispose(); 154 } 155 156 if (proxy != 0) { 157 WindowPropertyGetter wpg3 = 158 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy, 159 0, 1, false, XAtom.XA_WINDOW); 160 161 try { 162 status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 163 164 if (status != XConstants.Success || 165 wpg3.getData() == 0 || 166 wpg3.getActualType() != XAtom.XA_WINDOW || 167 Native.getLong(wpg3.getData()) != proxy) { 168 169 proxy = 0; 170 } else { 171 WindowPropertyGetter wpg4 = 172 new WindowPropertyGetter(proxy, 173 XDnDConstants.XA_XdndAware, 174 0, 1, false, 175 XConstants.AnyPropertyType); 176 177 try { 178 status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 179 180 if (status != XConstants.Success || 181 wpg4.getData() == 0 || 182 wpg4.getActualType() != XAtom.XA_ATOM) { 183 184 proxy = 0; 185 } 186 } finally { 187 wpg4.dispose(); 188 } 189 } 190 } finally { 191 wpg3.dispose(); 192 } 193 } 194 } 195 196 if (proxy == newProxy) { 197 // Embedder already registered. 198 return; 199 } 200 201 long data = Native.allocateLongArray(1); 202 203 try { 204 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); 205 206 /* The proxy window must have the XdndAware set, as XDnD protocol 207 prescribes to check the proxy window for XdndAware. */ 208 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 209 XDnDConstants.XA_XdndAware.setAtomData(newProxy, XAtom.XA_ATOM, 210 data, 1); 211 XToolkit.RESTORE_XERROR_HANDLER(); 212 213 if (XToolkit.saved_error != null && 214 XToolkit.saved_error.get_error_code() != 215 XConstants.Success) { 216 throw new XException("Cannot write XdndAware property"); 217 } 218 219 Native.putLong(data, 0, newProxy); 220 221 /* The proxy window must have the XdndProxy set to point to itself.*/ 222 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 223 XDnDConstants.XA_XdndProxy.setAtomData(newProxy, XAtom.XA_WINDOW, 224 data, 1); 225 XToolkit.RESTORE_XERROR_HANDLER(); 226 227 if (XToolkit.saved_error != null && 228 XToolkit.saved_error.get_error_code() != 229 XConstants.Success) { 230 throw new XException("Cannot write XdndProxy property"); 231 } 232 233 Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION); 234 235 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 236 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM, 237 data, 1); 238 XToolkit.RESTORE_XERROR_HANDLER(); 239 240 if (XToolkit.saved_error != null && 241 XToolkit.saved_error.get_error_code() != 242 XConstants.Success) { 243 throw new XException("Cannot write XdndAware property"); 244 } 245 246 Native.putLong(data, 0, newProxy); 247 248 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 249 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW, 250 data, 1); 251 XToolkit.RESTORE_XERROR_HANDLER(); 252 253 if (XToolkit.saved_error != null && 254 XToolkit.saved_error.get_error_code() != 255 XConstants.Success) { 256 throw new XException("Cannot write XdndProxy property"); 257 } 258 } finally { 259 unsafe.freeMemory(data); 260 data = 0; 261 } 262 263 putEmbedderRegistryEntry(embedder, overriden, version, proxy); 264 } 265 266 public void unregisterEmbedderDropSite(long embedder) { 267 assert XToolkit.isAWTLockHeldByCurrentThread(); 268 269 EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder); 270 271 if (entry == null) { 272 return; 273 } 274 275 if (entry.isOverriden()) { 276 long data = Native.allocateLongArray(1); 277 278 try { 279 Native.putLong(data, 0, entry.getVersion()); 280 281 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 282 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM, 283 data, 1); 284 XToolkit.RESTORE_XERROR_HANDLER(); 285 286 if (XToolkit.saved_error != null && 287 XToolkit.saved_error.get_error_code() != 288 XConstants.Success) { 289 throw new XException("Cannot write XdndAware property"); 290 } 291 292 Native.putLong(data, 0, (int)entry.getProxy()); 293 294 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 295 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW, 296 data, 1); 297 XToolkit.RESTORE_XERROR_HANDLER(); 298 299 if (XToolkit.saved_error != null && 300 XToolkit.saved_error.get_error_code() != 301 XConstants.Success) { 302 throw new XException("Cannot write XdndProxy property"); 303 } 304 } finally { 305 unsafe.freeMemory(data); 306 data = 0; 307 } 308 } else { 309 XDnDConstants.XA_XdndAware.DeleteProperty(embedder); 310 XDnDConstants.XA_XdndProxy.DeleteProperty(embedder); 311 } 312 } 313 314 /* 315 * Gets and stores in the registry the embedder's XDnD drop site info 316 * from the embedded. 317 */ 318 public void registerEmbeddedDropSite(long embedded) { 319 assert XToolkit.isAWTLockHeldByCurrentThread(); 320 321 boolean overriden = false; 322 int version = 0; 323 long proxy = 0; 324 long newProxy = XDropTargetRegistry.getDnDProxyWindow(); 325 int status = 0; 326 327 WindowPropertyGetter wpg1 = 328 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndAware, 0, 1, 329 false, XConstants.AnyPropertyType); 330 331 try { 332 status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 333 334 if (status == XConstants.Success && 335 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { 336 337 overriden = true; 338 version = (int)Native.getLong(wpg1.getData()); 339 } 340 } finally { 341 wpg1.dispose(); 342 } 343 344 /* XdndProxy is not supported for prior to XDnD version 4 */ 345 if (overriden && version >= 4) { 346 WindowPropertyGetter wpg2 = 347 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndProxy, 348 0, 1, false, XAtom.XA_WINDOW); 349 350 try { 351 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 352 353 if (status == XConstants.Success && 354 wpg2.getData() != 0 && 355 wpg2.getActualType() == XAtom.XA_WINDOW) { 356 357 proxy = Native.getLong(wpg2.getData()); 358 } 359 } finally { 360 wpg2.dispose(); 361 } 362 363 if (proxy != 0) { 364 WindowPropertyGetter wpg3 = 365 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy, 366 0, 1, false, XAtom.XA_WINDOW); 367 368 try { 369 status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 370 371 if (status != XConstants.Success || 372 wpg3.getData() == 0 || 373 wpg3.getActualType() != XAtom.XA_WINDOW || 374 Native.getLong(wpg3.getData()) != proxy) { 375 376 proxy = 0; 377 } else { 378 WindowPropertyGetter wpg4 = 379 new WindowPropertyGetter(proxy, 380 XDnDConstants.XA_XdndAware, 381 0, 1, false, 382 XConstants.AnyPropertyType); 383 384 try { 385 status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 386 387 if (status != XConstants.Success || 388 wpg4.getData() == 0 || 389 wpg4.getActualType() != XAtom.XA_ATOM) { 390 391 proxy = 0; 392 } 393 } finally { 394 wpg4.dispose(); 395 } 396 } 397 } finally { 398 wpg3.dispose(); 399 } 400 } 401 } 402 403 putEmbedderRegistryEntry(embedded, overriden, version, proxy); 404 } 405 406 public boolean isProtocolSupported(long window) { 407 assert XToolkit.isAWTLockHeldByCurrentThread(); 408 409 WindowPropertyGetter wpg1 = 410 new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1, 411 false, XConstants.AnyPropertyType); 412 413 try { 414 int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 415 416 if (status == XConstants.Success && 417 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { 418 419 return true; 420 } else { 421 return false; 422 } 423 } finally { 424 wpg1.dispose(); 425 } 426 } 427 428 private boolean processXdndEnter(XClientMessageEvent xclient) { 429 long source_win = 0; 430 long source_win_mask = 0; 431 int protocol_version = 0; 432 int actions = DnDConstants.ACTION_NONE; 433 boolean track = true; 434 long[] formats = null; 435 436 if (getSourceWindow() != 0) { 437 return false; 438 } 439 440 if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow) 441 && getEmbedderRegistryEntry(xclient.get_window()) == null) { 442 return false; 443 } 444 445 if (xclient.get_message_type() != XDnDConstants.XA_XdndEnter.getAtom()){ 446 return false; 447 } 448 449 protocol_version = 450 (int)((xclient.get_data(1) & XDnDConstants.XDND_PROTOCOL_MASK) >> 451 XDnDConstants.XDND_PROTOCOL_SHIFT); 452 453 /* XDnD compliance only requires supporting version 3 and up. */ 454 if (protocol_version < XDnDConstants.XDND_MIN_PROTOCOL_VERSION) { 455 return false; 456 } 457 458 /* Ignore the source if the protocol version is higher than we support. */ 459 if (protocol_version > XDnDConstants.XDND_PROTOCOL_VERSION) { 460 return false; 461 } 462 463 source_win = xclient.get_data(0); 464 465 /* Extract the list of supported actions. */ 466 if (protocol_version < 2) { 467 /* Prior to XDnD version 2 only COPY action was supported. */ 468 actions = DnDConstants.ACTION_COPY; 469 } else { 470 WindowPropertyGetter wpg = 471 new WindowPropertyGetter(source_win, 472 XDnDConstants.XA_XdndActionList, 473 0, 0xFFFF, false, 474 XAtom.XA_ATOM); 475 try { 476 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 477 478 if (wpg.getActualType() == XAtom.XA_ATOM && 479 wpg.getActualFormat() == 32) { 480 long data = wpg.getData(); 481 482 for (int i = 0; i < wpg.getNumberOfItems(); i++) { 483 actions |= 484 XDnDConstants.getJavaActionForXDnDAction(Native.getLong(data, i)); 485 } 486 } else { 487 /* 488 * According to XDnD protocol, XdndActionList is optional. 489 * If XdndActionList is not set we try to guess which actions are 490 * supported. 491 */ 492 actions = DnDConstants.ACTION_COPY; 493 track = true; 494 } 495 } finally { 496 wpg.dispose(); 497 } 498 } 499 500 /* Extract the available data types. */ 501 if ((xclient.get_data(1) & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) { 502 WindowPropertyGetter wpg = 503 new WindowPropertyGetter(source_win, 504 XDnDConstants.XA_XdndTypeList, 505 0, 0xFFFF, false, 506 XAtom.XA_ATOM); 507 try { 508 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 509 510 if (wpg.getActualType() == XAtom.XA_ATOM && 511 wpg.getActualFormat() == 32) { 512 formats = Native.toLongs(wpg.getData(), 513 wpg.getNumberOfItems()); 514 } else { 515 formats = new long[0]; 516 } 517 } finally { 518 wpg.dispose(); 519 } 520 } else { 521 int countFormats = 0; 522 long[] formats3 = new long[3]; 523 524 for (int i = 0; i < 3; i++) { 525 long j; 526 if ((j = xclient.get_data(2 + i)) != XConstants.None) { 527 formats3[countFormats++] = j; 528 } 529 } 530 531 formats = new long[countFormats]; 532 533 System.arraycopy(formats3, 0, formats, 0, countFormats); 534 } 535 536 assert XToolkit.isAWTLockHeldByCurrentThread(); 537 538 /* 539 * Select for StructureNotifyMask to receive DestroyNotify in case of source 540 * crash. 541 */ 542 XWindowAttributes wattr = new XWindowAttributes(); 543 try { 544 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 545 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 546 source_win, wattr.pData); 547 548 XToolkit.RESTORE_XERROR_HANDLER(); 549 550 if (status == 0 || 551 (XToolkit.saved_error != null && 552 XToolkit.saved_error.get_error_code() != XConstants.Success)) { 553 throw new XException("XGetWindowAttributes failed"); 554 } 555 556 source_win_mask = wattr.get_your_event_mask(); 557 } finally { 558 wattr.dispose(); 559 } 560 561 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 562 XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win, 563 source_win_mask | 564 XConstants.StructureNotifyMask); 565 566 XToolkit.RESTORE_XERROR_HANDLER(); 567 568 if (XToolkit.saved_error != null && 569 XToolkit.saved_error.get_error_code() != XConstants.Success) { 570 throw new XException("XSelectInput failed"); 571 } 572 573 sourceWindow = source_win; 574 sourceWindowMask = source_win_mask; 575 sourceProtocolVersion = protocol_version; 576 sourceActions = actions; 577 sourceFormats = formats; 578 trackSourceActions = track; 579 580 return true; 581 } 582 583 private boolean processXdndPosition(XClientMessageEvent xclient) { 584 long time_stamp = (int)XConstants.CurrentTime; 585 long xdnd_action = 0; 586 int java_action = DnDConstants.ACTION_NONE; 587 int x = 0; 588 int y = 0; 589 590 /* Ignore XDnD messages from all other windows. */ 591 if (sourceWindow != xclient.get_data(0)) { 592 return false; 593 } 594 595 XWindow xwindow = null; 596 { 597 XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); 598 if (xbasewindow instanceof XWindow) { 599 xwindow = (XWindow)xbasewindow; 600 } 601 } 602 603 x = (int)(xclient.get_data(2) >> 16); 604 y = (int)(xclient.get_data(2) & 0xFFFF); 605 606 if (xwindow == null) { 607 long receiver = 608 XDropTargetRegistry.getRegistry().getEmbeddedDropSite( 609 xclient.get_window(), x, y); 610 611 if (receiver != 0) { 612 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); 613 if (xbasewindow instanceof XWindow) { 614 xwindow = (XWindow)xbasewindow; 615 } 616 } 617 } 618 619 if (xwindow != null) { 620 /* Translate mouse position from root coordinates 621 to the target window coordinates. */ 622 Point p = xwindow.toLocal(x, y); 623 x = p.x; 624 y = p.y; 625 } 626 627 /* Time stamp - new in XDnD version 1. */ 628 if (sourceProtocolVersion > 0) { 629 time_stamp = xclient.get_data(3); 630 } 631 632 /* User action - new in XDnD version 2. */ 633 if (sourceProtocolVersion > 1) { 634 xdnd_action = xclient.get_data(4); 635 } else { 636 /* The default action is XdndActionCopy */ 637 xdnd_action = XDnDConstants.XA_XdndActionCopy.getAtom(); 638 } 639 640 java_action = XDnDConstants.getJavaActionForXDnDAction(xdnd_action); 641 642 if (trackSourceActions) { 643 sourceActions |= java_action; 644 } 645 646 if (xwindow == null) { 647 if (targetXWindow != null) { 648 notifyProtocolListener(targetXWindow, x, y, 649 DnDConstants.ACTION_NONE, xclient, 650 MouseEvent.MOUSE_EXITED); 651 } 652 } else { 653 int java_event_id = 0; 654 655 if (targetXWindow == null) { 656 java_event_id = MouseEvent.MOUSE_ENTERED; 657 } else { 658 java_event_id = MouseEvent.MOUSE_DRAGGED; 659 } 660 661 notifyProtocolListener(xwindow, x, y, java_action, xclient, 662 java_event_id); 663 } 664 665 userAction = java_action; 666 sourceX = x; 667 sourceY = y; 668 targetXWindow = xwindow; 669 670 return true; 671 } 672 673 private boolean processXdndLeave(XClientMessageEvent xclient) { 674 /* Ignore XDnD messages from all other windows. */ 675 if (sourceWindow != xclient.get_data(0)) { 676 return false; 677 } 678 679 cleanup(); 680 681 return true; 682 } 683 684 private boolean processXdndDrop(XClientMessageEvent xclient) { 685 /* Ignore XDnD messages from all other windows. */ 686 if (sourceWindow != xclient.get_data(0)) { 687 return false; 688 } 689 690 if (targetXWindow != null) { 691 notifyProtocolListener(targetXWindow, sourceX, sourceY, userAction, 692 xclient, MouseEvent.MOUSE_RELEASED); 693 } 694 695 return true; 696 } 697 698 public int getMessageType(XClientMessageEvent xclient) { 699 long messageType = xclient.get_message_type(); 700 701 if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) { 702 return ENTER_MESSAGE; 703 } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) { 704 return MOTION_MESSAGE; 705 } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) { 706 return LEAVE_MESSAGE; 707 } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) { 708 return DROP_MESSAGE; 709 } else { 710 return UNKNOWN_MESSAGE; 711 } 712 } 713 714 protected boolean processClientMessageImpl(XClientMessageEvent xclient) { 715 long messageType = xclient.get_message_type(); 716 717 if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) { 718 return processXdndEnter(xclient); 719 } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) { 720 return processXdndPosition(xclient); 721 } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) { 722 return processXdndLeave(xclient); 723 } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) { 724 return processXdndDrop(xclient); 725 } else { 726 return false; 727 } 728 } 729 730 protected void sendEnterMessageToToplevel(long toplevel, 731 XClientMessageEvent xclient) { 732 /* flags */ 733 long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT; 734 if (sourceFormats != null && sourceFormats.length > 3) { 735 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; 736 } 737 long data2 = sourceFormats.length > 0 ? sourceFormats[0] : 0; 738 long data3 = sourceFormats.length > 1 ? sourceFormats[1] : 0; 739 long data4 = sourceFormats.length > 2 ? sourceFormats[2] : 0; 740 sendEnterMessageToToplevelImpl(toplevel, xclient.get_data(0), 741 data1, data2, data3, data4); 742 743 } 744 745 private void sendEnterMessageToToplevelImpl(long toplevel, 746 long sourceWindow, 747 long data1, long data2, 748 long data3, long data4) { 749 XClientMessageEvent enter = new XClientMessageEvent(); 750 try { 751 enter.set_type((int)XConstants.ClientMessage); 752 enter.set_window(toplevel); 753 enter.set_format(32); 754 enter.set_message_type(XDnDConstants.XA_XdndEnter.getAtom()); 755 /* XID of the source window */ 756 enter.set_data(0, sourceWindow); 757 enter.set_data(1, data1); 758 enter.set_data(2, data2); 759 enter.set_data(3, data3); 760 enter.set_data(4, data4); 761 762 forwardClientMessageToToplevel(toplevel, enter); 763 } finally { 764 enter.dispose(); 765 } 766 } 767 768 protected void sendLeaveMessageToToplevel(long toplevel, 769 XClientMessageEvent xclient) { 770 sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0)); 771 } 772 773 protected void sendLeaveMessageToToplevelImpl(long toplevel, 774 long sourceWindow) { 775 XClientMessageEvent leave = new XClientMessageEvent(); 776 try { 777 leave.set_type((int)XConstants.ClientMessage); 778 leave.set_window(toplevel); 779 leave.set_format(32); 780 leave.set_message_type(XDnDConstants.XA_XdndLeave.getAtom()); 781 /* XID of the source window */ 782 leave.set_data(0, sourceWindow); 783 /* flags */ 784 leave.set_data(1, 0); 785 786 forwardClientMessageToToplevel(toplevel, leave); 787 } finally { 788 leave.dispose(); 789 } 790 } 791 792 public boolean sendResponse(long ctxt, int eventID, int action) { 793 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 794 795 if (xclient.get_message_type() != 796 XDnDConstants.XA_XdndPosition.getAtom()) { 797 798 return false; 799 } 800 801 if (eventID == MouseEvent.MOUSE_EXITED) { 802 action = DnDConstants.ACTION_NONE; 803 } 804 805 XClientMessageEvent msg = new XClientMessageEvent(); 806 try { 807 msg.set_type((int)XConstants.ClientMessage); 808 msg.set_window(xclient.get_data(0)); 809 msg.set_format(32); 810 msg.set_message_type(XDnDConstants.XA_XdndStatus.getAtom()); 811 /* target window */ 812 msg.set_data(0, xclient.get_window()); 813 /* flags */ 814 long flags = 0; 815 if (action != DnDConstants.ACTION_NONE) { 816 flags |= XDnDConstants.XDND_ACCEPT_DROP_FLAG; 817 } 818 msg.set_data(1, flags); 819 /* specify an empty rectangle */ 820 msg.set_data(2, 0); /* x, y */ 821 msg.set_data(3, 0); /* w, h */ 822 /* action accepted by the target */ 823 msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(action)); 824 825 XToolkit.awtLock(); 826 try { 827 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 828 xclient.get_data(0), 829 false, XConstants.NoEventMask, 830 msg.pData); 831 } finally { 832 XToolkit.awtUnlock(); 833 } 834 } finally { 835 msg.dispose(); 836 } 837 838 return true; 839 } 840 841 public Object getData(long ctxt, long format) 842 throws IllegalArgumentException, IOException { 843 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 844 long message_type = xclient.get_message_type(); 845 long time_stamp = XConstants.CurrentTime; 846 847 // NOTE: we assume that the source supports at least version 1, so we 848 // can use the time stamp 849 if (message_type == XDnDConstants.XA_XdndPosition.getAtom()) { 850 // X server time is an unsigned 32-bit number! 851 time_stamp = xclient.get_data(3) & 0xFFFFFFFFL; 852 } else if (message_type == XDnDConstants.XA_XdndDrop.getAtom()) { 853 // X server time is an unsigned 32-bit number! 854 time_stamp = xclient.get_data(2) & 0xFFFFFFFFL; 855 } else { 856 throw new IllegalArgumentException(); 857 } 858 859 return XDnDConstants.XDnDSelection.getData(format, time_stamp); 860 } 861 862 public boolean sendDropDone(long ctxt, boolean success, int dropAction) { 863 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 864 865 if (xclient.get_message_type() != 866 XDnDConstants.XA_XdndDrop.getAtom()) { 867 return false; 868 } 869 870 /* 871 * The XDnD protocol recommends that the target requests the special 872 * target DELETE in case if the drop action is XdndActionMove. 873 */ 874 if (dropAction == DnDConstants.ACTION_MOVE && success) { 875 876 long time_stamp = xclient.get_data(2); 877 long xdndSelectionAtom = 878 XDnDConstants.XDnDSelection.getSelectionAtom().getAtom(); 879 880 XToolkit.awtLock(); 881 try { 882 XlibWrapper.XConvertSelection(XToolkit.getDisplay(), 883 xdndSelectionAtom, 884 XAtom.get("DELETE").getAtom(), 885 XAtom.get("XAWT_SELECTION").getAtom(), 886 XWindow.getXAWTRootWindow().getWindow(), 887 time_stamp); 888 } finally { 889 XToolkit.awtUnlock(); 890 } 891 } 892 893 XClientMessageEvent msg = new XClientMessageEvent(); 894 try { 895 msg.set_type((int)XConstants.ClientMessage); 896 msg.set_window(xclient.get_data(0)); 897 msg.set_format(32); 898 msg.set_message_type(XDnDConstants.XA_XdndFinished.getAtom()); 899 msg.set_data(0, xclient.get_window()); /* target window */ 900 msg.set_data(1, 0); /* flags */ 901 /* specify an empty rectangle */ 902 msg.set_data(2, 0); 903 if (sourceProtocolVersion >= 5) { 904 if (success) { 905 msg.set_data(1, XDnDConstants.XDND_ACCEPT_DROP_FLAG); 906 } 907 /* action performed by the target */ 908 msg.set_data(2, XDnDConstants.getXDnDActionForJavaAction(dropAction)); 909 } 910 msg.set_data(3, 0); 911 msg.set_data(4, 0); 912 913 XToolkit.awtLock(); 914 try { 915 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 916 xclient.get_data(0), 917 false, XConstants.NoEventMask, 918 msg.pData); 919 } finally { 920 XToolkit.awtUnlock(); 921 } 922 } finally { 923 msg.dispose(); 924 } 925 926 /* 927 * Flush the buffer to guarantee that the drop completion event is sent 928 * to the source before the method returns. 929 */ 930 XToolkit.awtLock(); 931 try { 932 XlibWrapper.XFlush(XToolkit.getDisplay()); 933 } finally { 934 XToolkit.awtUnlock(); 935 } 936 937 /* Trick to prevent cleanup() from posting dragExit */ 938 targetXWindow = null; 939 940 /* Cannot do cleanup before the drop finishes as we may need 941 source protocol version to send drop finished message. */ 942 cleanup(); 943 return true; 944 } 945 946 public final long getSourceWindow() { 947 return sourceWindow; 948 } 949 950 /** 951 * Reset the state of the object. 952 */ 953 public void cleanup() { 954 // Clear the reference to this protocol. 955 XDropTargetEventProcessor.reset(); 956 957 if (targetXWindow != null) { 958 notifyProtocolListener(targetXWindow, 0, 0, 959 DnDConstants.ACTION_NONE, null, 960 MouseEvent.MOUSE_EXITED); 961 } 962 963 if (sourceWindow != 0) { 964 XToolkit.awtLock(); 965 try { 966 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 967 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow, 968 sourceWindowMask); 969 XToolkit.RESTORE_XERROR_HANDLER(); 970 } finally { 971 XToolkit.awtUnlock(); 972 } 973 } 974 975 sourceWindow = 0; 976 sourceWindowMask = 0; 977 sourceProtocolVersion = 0; 978 sourceActions = DnDConstants.ACTION_NONE; 979 sourceFormats = null; 980 trackSourceActions = false; 981 userAction = DnDConstants.ACTION_NONE; 982 sourceX = 0; 983 sourceY = 0; 984 targetXWindow = null; 985 } 986 987 public boolean isDragOverComponent() { 988 return targetXWindow != null; 989 } 990 991 public void adjustEventForForwarding(XClientMessageEvent xclient, 992 EmbedderRegistryEntry entry) { 993 /* Adjust the event to match the XDnD protocol version. */ 994 int version = entry.getVersion(); 995 if (xclient.get_message_type() == XDnDConstants.XA_XdndEnter.getAtom()) { 996 int min_version = sourceProtocolVersion < version ? 997 sourceProtocolVersion : version; 998 long data1 = min_version << XDnDConstants.XDND_PROTOCOL_SHIFT; 999 if (sourceFormats != null && sourceFormats.length > 3) { 1000 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; 1001 } 1002 if (logger.isLoggable(PlatformLogger.FINEST)) { 1003 logger.finest(" " 1004 + " entryVersion=" + version 1005 + " sourceProtocolVersion=" + 1006 sourceProtocolVersion 1007 + " sourceFormats.length=" + 1008 (sourceFormats != null ? sourceFormats.length : 0)); 1009 } 1010 xclient.set_data(1, data1); 1011 } 1012 } 1013 1014 private void notifyProtocolListener(XWindow xwindow, int x, int y, 1015 int dropAction, 1016 XClientMessageEvent xclient, 1017 int eventID) { 1018 long nativeCtxt = 0; 1019 1020 // Make a copy of the passed XClientMessageEvent structure, since 1021 // the original structure can be freed before this 1022 // SunDropTargetEvent is dispatched. 1023 if (xclient != null) { 1024 int size = new XClientMessageEvent(nativeCtxt).getSize(); 1025 1026 nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); 1027 1028 unsafe.copyMemory(xclient.pData, nativeCtxt, size); 1029 1030 long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT; 1031 if (sourceFormats != null && sourceFormats.length > 3) { 1032 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT; 1033 } 1034 // Append information from the latest XdndEnter event. 1035 Native.putLong(nativeCtxt + size, data1); 1036 Native.putLong(nativeCtxt + size + Native.getLongSize(), 1037 sourceFormats.length > 0 ? sourceFormats[0] : 0); 1038 Native.putLong(nativeCtxt + size + 2 * Native.getLongSize(), 1039 sourceFormats.length > 1 ? sourceFormats[1] : 0); 1040 Native.putLong(nativeCtxt + size + 3 * Native.getLongSize(), 1041 sourceFormats.length > 2 ? sourceFormats[2] : 0); 1042 } 1043 1044 getProtocolListener().handleDropTargetNotification(xwindow, x, y, 1045 dropAction, 1046 sourceActions, 1047 sourceFormats, 1048 nativeCtxt, 1049 eventID); 1050 } 1051 1052 /* 1053 * The methods/fields defined below are executed/accessed only on 1054 * the toolkit thread. 1055 * The methods/fields defined below are executed/accessed only on the event 1056 * dispatch thread. 1057 */ 1058 1059 public boolean forwardEventToEmbedded(long embedded, long ctxt, 1060 int eventID) { 1061 if (logger.isLoggable(PlatformLogger.FINEST)) { 1062 logger.finest(" ctxt=" + ctxt + 1063 " type=" + (ctxt != 0 ? 1064 getMessageType(new 1065 XClientMessageEvent(ctxt)) : 0) + 1066 " prevCtxt=" + prevCtxt + 1067 " prevType=" + (prevCtxt != 0 ? 1068 getMessageType(new 1069 XClientMessageEvent(prevCtxt)) : 0)); 1070 } 1071 if ((ctxt == 0 || 1072 getMessageType(new XClientMessageEvent(ctxt)) == UNKNOWN_MESSAGE) && 1073 (prevCtxt == 0 || 1074 getMessageType(new XClientMessageEvent(prevCtxt)) == UNKNOWN_MESSAGE)) { 1075 return false; 1076 } 1077 1078 // The size of XClientMessageEvent structure. 1079 int size = XClientMessageEvent.getSize(); 1080 1081 if (ctxt != 0) { 1082 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 1083 if (!overXEmbedClient) { 1084 long data1 = Native.getLong(ctxt + size); 1085 long data2 = Native.getLong(ctxt + size + Native.getLongSize()); 1086 long data3 = Native.getLong(ctxt + size + 2 * Native.getLongSize()); 1087 long data4 = Native.getLong(ctxt + size + 3 * Native.getLongSize()); 1088 1089 if (logger.isLoggable(PlatformLogger.FINEST)) { 1090 logger.finest(" 1 " 1091 + " embedded=" + embedded 1092 + " source=" + xclient.get_data(0) 1093 + " data1=" + data1 1094 + " data2=" + data2 1095 + " data3=" + data3 1096 + " data4=" + data4); 1097 } 1098 1099 // Copy XdndTypeList from source to proxy. 1100 if ((data1 & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) { 1101 WindowPropertyGetter wpg = 1102 new WindowPropertyGetter(xclient.get_data(0), 1103 XDnDConstants.XA_XdndTypeList, 1104 0, 0xFFFF, false, 1105 XAtom.XA_ATOM); 1106 try { 1107 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 1108 1109 if (wpg.getActualType() == XAtom.XA_ATOM && 1110 wpg.getActualFormat() == 32) { 1111 1112 XToolkit.awtLock(); 1113 try { 1114 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 1115 XDnDConstants.XA_XdndTypeList.setAtomData(xclient.get_window(), 1116 XAtom.XA_ATOM, 1117 wpg.getData(), 1118 wpg.getNumberOfItems()); 1119 XToolkit.RESTORE_XERROR_HANDLER(); 1120 1121 if (XToolkit.saved_error != null && 1122 XToolkit.saved_error.get_error_code() != XConstants.Success) { 1123 if (logger.isLoggable(PlatformLogger.WARNING)) { 1124 logger.warning("Cannot set XdndTypeList on the proxy window"); 1125 } 1126 } 1127 } finally { 1128 XToolkit.awtUnlock(); 1129 } 1130 } else { 1131 if (logger.isLoggable(PlatformLogger.WARNING)) { 1132 logger.warning("Cannot read XdndTypeList from the source window"); 1133 } 1134 } 1135 } finally { 1136 wpg.dispose(); 1137 } 1138 } 1139 XDragSourceContextPeer.setProxyModeSourceWindow(xclient.get_data(0)); 1140 1141 sendEnterMessageToToplevelImpl(embedded, xclient.get_window(), 1142 data1, data2, data3, data4); 1143 overXEmbedClient = true; 1144 } 1145 1146 if (logger.isLoggable(PlatformLogger.FINEST)) { 1147 logger.finest(" 2 " 1148 + " embedded=" + embedded 1149 + " xclient=" + xclient); 1150 } 1151 1152 /* Make a copy of the original event, since we are going to modify the 1153 event while it still can be referenced from other Java events. */ 1154 { 1155 XClientMessageEvent copy = new XClientMessageEvent(); 1156 unsafe.copyMemory(xclient.pData, copy.pData, copy.getSize()); 1157 1158 copy.set_data(0, xclient.get_window()); 1159 1160 forwardClientMessageToToplevel(embedded, copy); 1161 } 1162 } 1163 1164 if (eventID == MouseEvent.MOUSE_EXITED) { 1165 if (overXEmbedClient) { 1166 if (ctxt != 0 || prevCtxt != 0) { 1167 // Last chance to send XdndLeave to the XEmbed client. 1168 XClientMessageEvent xclient = ctxt != 0 ? 1169 new XClientMessageEvent(ctxt) : 1170 new XClientMessageEvent(prevCtxt); 1171 sendLeaveMessageToToplevelImpl(embedded, xclient.get_window()); 1172 } 1173 overXEmbedClient = false; 1174 // We have to clear the proxy mode source window here, 1175 // when the drag exits the XEmbedCanvasPeer. 1176 // NOTE: at this point the XEmbed client still might have some 1177 // drag notifications to process and it will send responses to 1178 // us. With the proxy mode source window cleared we won't be 1179 // able to forward these responses to the actual source. This is 1180 // not a problem if the drag operation was initiated in this 1181 // JVM. However, if it was initiated in another processes the 1182 // responses will be lost. We bear with it for now, as it seems 1183 // there is no other reliable point to clear. 1184 XDragSourceContextPeer.setProxyModeSourceWindow(0); 1185 } 1186 } 1187 1188 if (eventID == MouseEvent.MOUSE_RELEASED) { 1189 overXEmbedClient = false; 1190 cleanup(); 1191 } 1192 1193 if (prevCtxt != 0) { 1194 unsafe.freeMemory(prevCtxt); 1195 prevCtxt = 0; 1196 } 1197 1198 if (ctxt != 0 && overXEmbedClient) { 1199 prevCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); 1200 1201 unsafe.copyMemory(ctxt, prevCtxt, size + 4 * Native.getLongSize()); 1202 } 1203 1204 return true; 1205 } 1206 1207 public boolean isXEmbedSupported() { 1208 return true; 1209 } 1210 }