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