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