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.misc.Unsafe; 37 38 /** 39 * XDropTargetProtocol implementation for Motif DnD protocol. 40 * 41 * @since 1.5 42 */ 43 class MotifDnDDropTargetProtocol extends XDropTargetProtocol { 44 private static final Unsafe unsafe = XlibWrapper.unsafe; 45 46 private long sourceWindow = 0; 47 private long sourceWindowMask = 0; 48 private int sourceProtocolVersion = 0; 49 private int sourceActions = DnDConstants.ACTION_NONE; 50 private long[] sourceFormats = null; 51 private long sourceAtom = 0; 52 private int userAction = DnDConstants.ACTION_NONE; 53 private int sourceX = 0; 54 private int sourceY = 0; 55 private XWindow targetXWindow = null; 56 private boolean topLevelLeavePostponed = false; 57 58 protected MotifDnDDropTargetProtocol(XDropTargetProtocolListener listener) { 59 super(listener); 60 } 61 62 /** 63 * Creates an instance associated with the specified listener. 64 * 65 * @throws NullPointerException if listener is <code>null</code>. 66 */ 67 static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) { 68 return new MotifDnDDropTargetProtocol(listener); 69 } 70 71 public String getProtocolName() { 72 return XDragAndDropProtocols.MotifDnD; 73 } 74 75 public void registerDropTarget(long window) { 76 assert XToolkit.isAWTLockHeldByCurrentThread(); 77 78 MotifDnDConstants.writeDragReceiverInfoStruct(window); 79 } 80 81 public void unregisterDropTarget(long window) { 82 assert XToolkit.isAWTLockHeldByCurrentThread(); 83 84 MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window); 85 } 86 87 public void registerEmbedderDropSite(long embedder) { 88 assert XToolkit.isAWTLockHeldByCurrentThread(); 89 90 boolean overriden = false; 91 int version = 0; 92 long proxy = 0; 93 long newProxy = XDropTargetRegistry.getDnDProxyWindow(); 94 int status = 0; 95 long data = 0; 96 int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE; 97 98 WindowPropertyGetter wpg = 99 new WindowPropertyGetter(embedder, 100 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 101 0, 0xFFFF, false, 102 XConstants.AnyPropertyType); 103 104 try { 105 status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 106 107 /* 108 * DragICCI.h: 109 * 110 * typedef struct _xmDragReceiverInfoStruct{ 111 * BYTE byte_order; 112 * BYTE protocol_version; 113 * BYTE drag_protocol_style; 114 * BYTE pad1; 115 * CARD32 proxy_window B32; 116 * CARD16 num_drop_sites B16; 117 * CARD16 pad2 B16; 118 * CARD32 heap_offset B32; 119 * } xmDragReceiverInfoStruct; 120 */ 121 if (status == XConstants.Success && wpg.getData() != 0 && 122 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && 123 wpg.getNumberOfItems() >= 124 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { 125 126 overriden = true; 127 data = wpg.getData(); 128 dataSize = wpg.getNumberOfItems(); 129 130 byte byteOrderByte = unsafe.getByte(data); 131 132 { 133 int tproxy = unsafe.getInt(data + 4); 134 if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { 135 tproxy = MotifDnDConstants.Swapper.swap(tproxy); 136 } 137 proxy = tproxy; 138 } 139 140 if (proxy == newProxy) { 141 // Embedder already registered. 142 return; 143 } 144 145 { 146 int tproxy = (int)newProxy; 147 if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { 148 tproxy = MotifDnDConstants.Swapper.swap(tproxy); 149 } 150 unsafe.putInt(data + 4, tproxy); 151 } 152 } else { 153 data = unsafe.allocateMemory(dataSize); 154 155 unsafe.putByte(data, MotifDnDConstants.getByteOrderByte()); /* byte order */ 156 unsafe.putByte(data + 1, MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION); /* protocol version */ 157 unsafe.putByte(data + 2, (byte)MotifDnDConstants.MOTIF_DYNAMIC_STYLE); /* protocol style */ 158 unsafe.putByte(data + 3, (byte)0); /* pad */ 159 unsafe.putInt(data + 4, (int)newProxy); /* proxy window */ 160 unsafe.putShort(data + 8, (short)0); /* num_drop_sites */ 161 unsafe.putShort(data + 10, (short)0); /* pad */ 162 unsafe.putInt(data + 12, dataSize); 163 } 164 165 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 166 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder, 167 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 168 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 169 8, XConstants.PropModeReplace, 170 data, dataSize); 171 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 172 173 if ((XErrorHandlerUtil.saved_error != null) && 174 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 175 throw new XException("Cannot write Motif receiver info property"); 176 } 177 } finally { 178 if (!overriden) { 179 unsafe.freeMemory(data); 180 data = 0; 181 } 182 wpg.dispose(); 183 } 184 185 putEmbedderRegistryEntry(embedder, overriden, version, proxy); 186 } 187 188 public void unregisterEmbedderDropSite(long embedder) { 189 assert XToolkit.isAWTLockHeldByCurrentThread(); 190 191 EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder); 192 193 if (entry == null) { 194 return; 195 } 196 197 if (entry.isOverriden()) { 198 int status = 0; 199 200 WindowPropertyGetter wpg = 201 new WindowPropertyGetter(embedder, 202 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 203 0, 0xFFFF, false, 204 XConstants.AnyPropertyType); 205 206 try { 207 status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 208 209 /* 210 * DragICCI.h: 211 * 212 * typedef struct _xmDragReceiverInfoStruct{ 213 * BYTE byte_order; 214 * BYTE protocol_version; 215 * BYTE drag_protocol_style; 216 * BYTE pad1; 217 * CARD32 proxy_window B32; 218 * CARD16 num_drop_sites B16; 219 * CARD16 pad2 B16; 220 * CARD32 heap_offset B32; 221 * } xmDragReceiverInfoStruct; 222 */ 223 if (status == XConstants.Success && wpg.getData() != 0 && 224 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && 225 wpg.getNumberOfItems() >= 226 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { 227 228 int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE; 229 long data = wpg.getData(); 230 byte byteOrderByte = unsafe.getByte(data); 231 232 int tproxy = (int)entry.getProxy(); 233 if (MotifDnDConstants.getByteOrderByte() != byteOrderByte) { 234 tproxy = MotifDnDConstants.Swapper.swap(tproxy); 235 } 236 237 unsafe.putInt(data + 4, tproxy); 238 239 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 240 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder, 241 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 242 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 243 8, XConstants.PropModeReplace, 244 data, dataSize); 245 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 246 247 if ((XErrorHandlerUtil.saved_error != null) && 248 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 249 throw new XException("Cannot write Motif receiver info property"); 250 } 251 } 252 } finally { 253 wpg.dispose(); 254 } 255 } else { 256 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.DeleteProperty(embedder); 257 } 258 } 259 260 /* 261 * Gets and stores in the registry the embedder's Motif DnD drop site info 262 * from the embedded. 263 */ 264 public void registerEmbeddedDropSite(long embedded) { 265 assert XToolkit.isAWTLockHeldByCurrentThread(); 266 267 boolean overriden = false; 268 int version = 0; 269 long proxy = 0; 270 int status = 0; 271 272 WindowPropertyGetter wpg = 273 new WindowPropertyGetter(embedded, 274 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 275 0, 0xFFFF, false, 276 XConstants.AnyPropertyType); 277 278 try { 279 status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 280 281 /* 282 * DragICCI.h: 283 * 284 * typedef struct _xmDragReceiverInfoStruct{ 285 * BYTE byte_order; 286 * BYTE protocol_version; 287 * BYTE drag_protocol_style; 288 * BYTE pad1; 289 * CARD32 proxy_window B32; 290 * CARD16 num_drop_sites B16; 291 * CARD16 pad2 B16; 292 * CARD32 heap_offset B32; 293 * } xmDragReceiverInfoStruct; 294 */ 295 if (status == XConstants.Success && wpg.getData() != 0 && 296 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && 297 wpg.getNumberOfItems() >= 298 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { 299 300 overriden = true; 301 long data = wpg.getData(); 302 303 byte byteOrderByte = unsafe.getByte(data); 304 305 { 306 int tproxy = unsafe.getInt(data + 4); 307 if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { 308 tproxy = MotifDnDConstants.Swapper.swap(tproxy); 309 } 310 proxy = tproxy; 311 } 312 } 313 } finally { 314 wpg.dispose(); 315 } 316 317 putEmbedderRegistryEntry(embedded, overriden, version, proxy); 318 } 319 320 public boolean isProtocolSupported(long window) { 321 WindowPropertyGetter wpg = 322 new WindowPropertyGetter(window, 323 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 324 0, 0xFFFF, false, 325 XConstants.AnyPropertyType); 326 327 try { 328 int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 329 330 if (status == XConstants.Success && wpg.getData() != 0 && 331 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && 332 wpg.getNumberOfItems() >= 333 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { 334 return true; 335 } else { 336 return false; 337 } 338 } finally { 339 wpg.dispose(); 340 } 341 } 342 343 private boolean processTopLevelEnter(XClientMessageEvent xclient) { 344 assert XToolkit.isAWTLockHeldByCurrentThread(); 345 346 if (targetXWindow != null || sourceWindow != 0) { 347 return false; 348 } 349 350 if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow) 351 && getEmbedderRegistryEntry(xclient.get_window()) == null) { 352 return false; 353 } 354 355 long source_win = 0; 356 long source_win_mask = 0; 357 int protocol_version = 0; 358 long property_atom = 0; 359 long[] formats = null; 360 361 { 362 long data = xclient.get_data(); 363 byte eventByteOrder = unsafe.getByte(data + 1); 364 source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder); 365 property_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); 366 } 367 368 /* Extract the available data types. */ 369 { 370 WindowPropertyGetter wpg = 371 new WindowPropertyGetter(source_win, 372 XAtom.get(property_atom), 373 0, 0xFFFF, 374 false, 375 MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom()); 376 377 try { 378 int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 379 380 if (status == XConstants.Success && wpg.getData() != 0 && 381 wpg.getActualType() == 382 MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom() && 383 wpg.getActualFormat() == 8 && 384 wpg.getNumberOfItems() == 385 MotifDnDConstants.MOTIF_INITIATOR_INFO_SIZE) { 386 387 long data = wpg.getData(); 388 byte propertyByteOrder = unsafe.getByte(data); 389 390 protocol_version = unsafe.getByte(data + 1); 391 392 if (protocol_version != 393 MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION) { 394 return false; 395 } 396 397 int index = 398 MotifDnDConstants.Swapper.getShort(data + 2, propertyByteOrder); 399 400 formats = MotifDnDConstants.getTargetListForIndex(index); 401 } else { 402 formats = new long[0]; 403 } 404 } finally { 405 wpg.dispose(); 406 } 407 } 408 409 /* 410 * Select for StructureNotifyMask to receive DestroyNotify in case of source 411 * crash. 412 */ 413 XWindowAttributes wattr = new XWindowAttributes(); 414 try { 415 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 416 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 417 source_win, wattr.pData); 418 419 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 420 421 if ((status == 0) || 422 ((XErrorHandlerUtil.saved_error != null) && 423 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) { 424 throw new XException("XGetWindowAttributes failed"); 425 } 426 427 source_win_mask = wattr.get_your_event_mask(); 428 } finally { 429 wattr.dispose(); 430 } 431 432 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 433 XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win, 434 source_win_mask | 435 XConstants.StructureNotifyMask); 436 437 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 438 439 if ((XErrorHandlerUtil.saved_error != null) && 440 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 441 throw new XException("XSelectInput failed"); 442 } 443 444 sourceWindow = source_win; 445 sourceWindowMask = source_win_mask; 446 sourceProtocolVersion = protocol_version; 447 /* 448 * TOP_LEVEL_ENTER doesn't communicate the list of supported actions 449 * They are provided in DRAG_MOTION. 450 */ 451 sourceActions = DnDConstants.ACTION_NONE; 452 sourceFormats = formats; 453 sourceAtom = property_atom; 454 455 return true; 456 } 457 458 private boolean processDragMotion(XClientMessageEvent xclient) { 459 long data = xclient.get_data(); 460 byte eventByteOrder = unsafe.getByte(data + 1); 461 byte eventReason = (byte)(unsafe.getByte(data) & 462 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 463 int x = 0; 464 int y = 0; 465 466 short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder); 467 468 int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >> 469 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; 470 int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >> 471 MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT; 472 473 int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action); 474 int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions); 475 476 /* Append source window id to the event data, so that we can send the 477 response properly. */ 478 { 479 int win = (int)sourceWindow; 480 if (eventByteOrder != MotifDnDConstants.getByteOrderByte()) { 481 win = MotifDnDConstants.Swapper.swap(win); 482 } 483 unsafe.putInt(data + 12, win); 484 } 485 486 XWindow xwindow = null; 487 { 488 XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); 489 if (xbasewindow instanceof XWindow) { 490 xwindow = (XWindow)xbasewindow; 491 } 492 } 493 494 if (eventReason == MotifDnDConstants.OPERATION_CHANGED) { 495 /* OPERATION_CHANGED event doesn't provide coordinates, so we use 496 previously stored position and component ref. */ 497 x = sourceX; 498 y = sourceY; 499 500 if (xwindow == null) { 501 xwindow = targetXWindow; 502 } 503 } else { 504 x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder); 505 y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder); 506 507 if (xwindow == null) { 508 long receiver = 509 XDropTargetRegistry.getRegistry().getEmbeddedDropSite( 510 xclient.get_window(), x, y); 511 512 if (receiver != 0) { 513 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); 514 if (xbasewindow instanceof XWindow) { 515 xwindow = (XWindow)xbasewindow; 516 } 517 } 518 } 519 520 if (xwindow != null) { 521 Point p = xwindow.toLocal(x, y); 522 x = p.x; 523 y = p.y; 524 } 525 } 526 527 if (xwindow == null) { 528 if (targetXWindow != null) { 529 notifyProtocolListener(targetXWindow, x, y, 530 DnDConstants.ACTION_NONE, java_actions, 531 xclient, MouseEvent.MOUSE_EXITED); 532 } 533 } else { 534 int java_event_id = 0; 535 536 if (targetXWindow == null) { 537 java_event_id = MouseEvent.MOUSE_ENTERED; 538 } else { 539 java_event_id = MouseEvent.MOUSE_DRAGGED; 540 } 541 542 notifyProtocolListener(xwindow, x, y, java_action, java_actions, 543 xclient, java_event_id); 544 } 545 546 sourceActions = java_actions; 547 userAction = java_action; 548 sourceX = x; 549 sourceY = y; 550 targetXWindow = xwindow; 551 552 return true; 553 } 554 555 private boolean processTopLevelLeave(XClientMessageEvent xclient) { 556 assert XToolkit.isAWTLockHeldByCurrentThread(); 557 558 long data = xclient.get_data(); 559 byte eventByteOrder = unsafe.getByte(data + 1); 560 561 long source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder); 562 563 /* Ignore Motif DnD messages from all other windows. */ 564 if (source_win != sourceWindow) { 565 return false; 566 } 567 568 /* 569 * Postpone upcall to java, so that we can abort it in case 570 * if drop immediatelly follows (see BugTraq ID 4395290). 571 * Send a dummy ClientMessage event to guarantee that a postponed java 572 * upcall will be processed. 573 */ 574 topLevelLeavePostponed = true; 575 { 576 long proxy; 577 578 /* 579 * If this is an embedded drop site, the event should go to the 580 * awt_root_window as this is a proxy for all embedded drop sites. 581 * Otherwise the event should go to the event->window, as we don't use 582 * proxies for normal drop sites. 583 */ 584 if (getEmbedderRegistryEntry(xclient.get_window()) != null) { 585 proxy = XDropTargetRegistry.getDnDProxyWindow(); 586 } else { 587 proxy = xclient.get_window(); 588 } 589 590 XClientMessageEvent dummy = new XClientMessageEvent(); 591 592 try { 593 dummy.set_type(XConstants.ClientMessage); 594 dummy.set_window(xclient.get_window()); 595 dummy.set_format(32); 596 dummy.set_message_type(0); 597 dummy.set_data(0, 0); 598 dummy.set_data(1, 0); 599 dummy.set_data(2, 0); 600 dummy.set_data(3, 0); 601 dummy.set_data(4, 0); 602 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 603 proxy, false, XConstants.NoEventMask, 604 dummy.pData); 605 } finally { 606 dummy.dispose(); 607 } 608 } 609 return true; 610 } 611 612 private boolean processDropStart(XClientMessageEvent xclient) { 613 long data = xclient.get_data(); 614 byte eventByteOrder = unsafe.getByte(data + 1); 615 616 long source_win = 617 MotifDnDConstants.Swapper.getInt(data + 16, eventByteOrder); 618 619 /* Ignore Motif DnD messages from all other windows. */ 620 if (source_win != sourceWindow) { 621 return false; 622 } 623 624 long property_atom = 625 MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); 626 627 short flags = 628 MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder); 629 630 int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >> 631 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; 632 int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >> 633 MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT; 634 635 int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action); 636 int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions); 637 638 int x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder); 639 int y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder); 640 641 XWindow xwindow = null; 642 { 643 XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); 644 if (xbasewindow instanceof XWindow) { 645 xwindow = (XWindow)xbasewindow; 646 } 647 } 648 649 if (xwindow == null) { 650 long receiver = 651 XDropTargetRegistry.getRegistry().getEmbeddedDropSite( 652 xclient.get_window(), x, y); 653 654 if (receiver != 0) { 655 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); 656 if (xbasewindow instanceof XWindow) { 657 xwindow = (XWindow)xbasewindow; 658 } 659 } 660 } 661 662 if (xwindow != null) { 663 Point p = xwindow.toLocal(x, y); 664 x = p.x; 665 y = p.y; 666 } 667 668 if (xwindow != null) { 669 notifyProtocolListener(xwindow, x, y, java_action, java_actions, 670 xclient, MouseEvent.MOUSE_RELEASED); 671 } else if (targetXWindow != null) { 672 notifyProtocolListener(targetXWindow, x, y, 673 DnDConstants.ACTION_NONE, java_actions, 674 xclient, MouseEvent.MOUSE_EXITED); 675 } 676 677 return true; 678 } 679 680 public int getMessageType(XClientMessageEvent xclient) { 681 if (xclient.get_message_type() != 682 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { 683 684 return UNKNOWN_MESSAGE; 685 } 686 687 long data = xclient.get_data(); 688 byte reason = (byte)(unsafe.getByte(data) & 689 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 690 691 switch (reason) { 692 case MotifDnDConstants.TOP_LEVEL_ENTER : 693 return ENTER_MESSAGE; 694 case MotifDnDConstants.DRAG_MOTION : 695 case MotifDnDConstants.OPERATION_CHANGED : 696 return MOTION_MESSAGE; 697 case MotifDnDConstants.TOP_LEVEL_LEAVE : 698 return LEAVE_MESSAGE; 699 case MotifDnDConstants.DROP_START : 700 return DROP_MESSAGE; 701 default: 702 return UNKNOWN_MESSAGE; 703 } 704 } 705 706 protected boolean processClientMessageImpl(XClientMessageEvent xclient) { 707 if (xclient.get_message_type() != 708 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { 709 if (topLevelLeavePostponed) { 710 topLevelLeavePostponed = false; 711 cleanup(); 712 } 713 714 return false; 715 } 716 717 long data = xclient.get_data(); 718 byte reason = (byte)(unsafe.getByte(data) & 719 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 720 byte origin = (byte)(unsafe.getByte(data) & 721 MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); 722 723 if (topLevelLeavePostponed) { 724 topLevelLeavePostponed = false; 725 if (reason != MotifDnDConstants.DROP_START) { 726 cleanup(); 727 } 728 } 729 730 /* Only initiator messages should be handled. */ 731 if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { 732 return false; 733 } 734 735 switch (reason) { 736 case MotifDnDConstants.TOP_LEVEL_ENTER : 737 return processTopLevelEnter(xclient); 738 case MotifDnDConstants.DRAG_MOTION : 739 case MotifDnDConstants.OPERATION_CHANGED : 740 return processDragMotion(xclient); 741 case MotifDnDConstants.TOP_LEVEL_LEAVE : 742 return processTopLevelLeave(xclient); 743 case MotifDnDConstants.DROP_START : 744 return processDropStart(xclient); 745 default: 746 return false; 747 } 748 } 749 750 /* 751 * Currently we don't synthesize enter/leave messages for Motif DnD 752 * protocol. See comments in XDropTargetProtocol.postProcessClientMessage. 753 */ 754 protected void sendEnterMessageToToplevel(long win, 755 XClientMessageEvent xclient) { 756 throw new Error("UNIMPLEMENTED"); 757 } 758 759 protected void sendLeaveMessageToToplevel(long win, 760 XClientMessageEvent xclient) { 761 throw new Error("UNIMPLEMENTED"); 762 } 763 764 public boolean forwardEventToEmbedded(long embedded, long ctxt, 765 int eventID) { 766 // UNIMPLEMENTED. 767 return false; 768 } 769 770 public boolean isXEmbedSupported() { 771 return false; 772 } 773 774 public boolean sendResponse(long ctxt, int eventID, int action) { 775 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 776 if (xclient.get_message_type() != 777 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { 778 return false; 779 } 780 781 long data = xclient.get_data(); 782 byte reason = (byte)(unsafe.getByte(data) & 783 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 784 byte origin = (byte)(unsafe.getByte(data) & 785 MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); 786 byte eventByteOrder = unsafe.getByte(data + 1); 787 byte response_reason = (byte)0; 788 789 /* Only initiator messages should be handled. */ 790 if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { 791 return false; 792 } 793 794 switch (reason) { 795 case MotifDnDConstants.TOP_LEVEL_ENTER: 796 case MotifDnDConstants.TOP_LEVEL_LEAVE: 797 /* Receiver shouldn't rely to these messages. */ 798 return false; 799 case MotifDnDConstants.DRAG_MOTION: 800 switch (eventID) { 801 case MouseEvent.MOUSE_ENTERED: 802 response_reason = MotifDnDConstants.DROP_SITE_ENTER; 803 break; 804 case MouseEvent.MOUSE_DRAGGED: 805 response_reason = MotifDnDConstants.DRAG_MOTION; 806 break; 807 case MouseEvent.MOUSE_EXITED: 808 response_reason = MotifDnDConstants.DROP_SITE_LEAVE; 809 break; 810 } 811 break; 812 case MotifDnDConstants.OPERATION_CHANGED: 813 case MotifDnDConstants.DROP_START: 814 response_reason = reason; 815 break; 816 default: 817 // Unknown reason. Shouldn't get here. 818 assert false; 819 } 820 821 XClientMessageEvent msg = new XClientMessageEvent(); 822 823 try { 824 msg.set_type(XConstants.ClientMessage); 825 msg.set_window(MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder)); 826 msg.set_format(8); 827 msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()); 828 829 long responseData = msg.get_data(); 830 831 unsafe.putByte(responseData, (byte)(response_reason | 832 MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER)); 833 unsafe.putByte(responseData + 1, MotifDnDConstants.getByteOrderByte()); 834 835 int response_flags = 0; 836 837 if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) { 838 short flags = MotifDnDConstants.Swapper.getShort(data + 2, 839 eventByteOrder); 840 byte dropSiteStatus = (action == DnDConstants.ACTION_NONE) ? 841 MotifDnDConstants.MOTIF_INVALID_DROP_SITE : 842 MotifDnDConstants.MOTIF_VALID_DROP_SITE; 843 844 /* Clear action and drop site status bits. */ 845 response_flags = flags & 846 ~MotifDnDConstants.MOTIF_DND_ACTION_MASK & 847 ~MotifDnDConstants.MOTIF_DND_STATUS_MASK; 848 /* Fill in new action and drop site status. */ 849 response_flags |= 850 MotifDnDConstants.getMotifActionsForJavaActions(action) << 851 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; 852 response_flags |= 853 dropSiteStatus << MotifDnDConstants.MOTIF_DND_STATUS_SHIFT; 854 } else { 855 response_flags = 0; 856 } 857 858 unsafe.putShort(responseData + 2, (short)response_flags); 859 860 /* Write time stamp. */ 861 int time = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder); 862 unsafe.putInt(responseData + 4, time); 863 864 /* Write coordinates. */ 865 if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) { 866 short x = MotifDnDConstants.Swapper.getShort(data + 8, 867 eventByteOrder); 868 short y = MotifDnDConstants.Swapper.getShort(data + 10, 869 eventByteOrder); 870 unsafe.putShort(responseData + 8, x); // x 871 unsafe.putShort(responseData + 10, y); // y 872 } else { 873 unsafe.putShort(responseData + 8, (short)0); // x 874 unsafe.putShort(responseData + 10, (short)0); // y 875 } 876 877 XToolkit.awtLock(); 878 try { 879 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 880 msg.get_window(), 881 false, XConstants.NoEventMask, 882 msg.pData); 883 } finally { 884 XToolkit.awtUnlock(); 885 } 886 } finally { 887 msg.dispose(); 888 } 889 890 return true; 891 } 892 893 public Object getData(long ctxt, long format) 894 throws IllegalArgumentException, IOException { 895 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 896 897 if (xclient.get_message_type() != 898 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { 899 throw new IllegalArgumentException(); 900 } 901 902 long data = xclient.get_data(); 903 byte reason = (byte)(unsafe.getByte(data) & 904 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 905 byte origin = (byte)(unsafe.getByte(data) & 906 MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); 907 byte eventByteOrder = unsafe.getByte(data + 1); 908 909 if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { 910 throw new IOException("Cannot get data: corrupted context"); 911 } 912 913 long selatom = 0; 914 915 switch (reason) { 916 case MotifDnDConstants.DRAG_MOTION : 917 case MotifDnDConstants.OPERATION_CHANGED : 918 selatom = sourceAtom; 919 break; 920 case MotifDnDConstants.DROP_START : 921 selatom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); 922 break; 923 default: 924 throw new IOException("Cannot get data: invalid message reason"); 925 } 926 927 if (selatom == 0) { 928 throw new IOException("Cannot get data: drag source property atom unavailable"); 929 } 930 931 long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL; 932 // with correction of (32-bit unsigned to 64-bit signed) implicit conversion. 933 934 XAtom selectionAtom = XAtom.get(selatom); 935 936 XSelection selection = XSelection.getSelection(selectionAtom); 937 if (selection == null) { 938 selection = new XSelection(selectionAtom); 939 } 940 941 return selection.getData(format, time_stamp); 942 } 943 944 public boolean sendDropDone(long ctxt, boolean success, int dropAction) { 945 XClientMessageEvent xclient = new XClientMessageEvent(ctxt); 946 947 if (xclient.get_message_type() != 948 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { 949 return false; 950 } 951 952 long data = xclient.get_data(); 953 byte reason = (byte)(unsafe.getByte(data) & 954 MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); 955 byte origin = (byte)(unsafe.getByte(data) & 956 MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); 957 byte eventByteOrder = unsafe.getByte(data + 1); 958 959 if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { 960 return false; 961 } 962 963 if (reason != MotifDnDConstants.DROP_START) { 964 return false; 965 } 966 967 long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL; 968 // with correction of (32-bit unsigned to 64-bit signed) implicit conversion. 969 970 long sel_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); 971 972 long status_atom = 0; 973 974 if (success) { 975 status_atom = MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom(); 976 } else { 977 status_atom = MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom(); 978 } 979 980 XToolkit.awtLock(); 981 try { 982 XlibWrapper.XConvertSelection(XToolkit.getDisplay(), 983 sel_atom, 984 status_atom, 985 MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom(), 986 XWindow.getXAWTRootWindow().getWindow(), 987 time_stamp); 988 989 /* 990 * Flush the buffer to guarantee that the drop completion event is sent 991 * to the source before the method returns. 992 */ 993 XlibWrapper.XFlush(XToolkit.getDisplay()); 994 } finally { 995 XToolkit.awtUnlock(); 996 } 997 998 /* Trick to prevent cleanup() from posting dragExit */ 999 targetXWindow = null; 1000 1001 /* Cannot do cleanup before the drop finishes as we may need 1002 source protocol version to send drop finished message. */ 1003 cleanup(); 1004 return true; 1005 } 1006 1007 public final long getSourceWindow() { 1008 return sourceWindow; 1009 } 1010 1011 /** 1012 * Reset the state of the object. 1013 */ 1014 public void cleanup() { 1015 // Clear the reference to this protocol. 1016 XDropTargetEventProcessor.reset(); 1017 1018 if (targetXWindow != null) { 1019 notifyProtocolListener(targetXWindow, 0, 0, 1020 DnDConstants.ACTION_NONE, sourceActions, 1021 null, MouseEvent.MOUSE_EXITED); 1022 } 1023 1024 if (sourceWindow != 0) { 1025 XToolkit.awtLock(); 1026 try { 1027 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 1028 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow, 1029 sourceWindowMask); 1030 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 1031 } finally { 1032 XToolkit.awtUnlock(); 1033 } 1034 } 1035 1036 sourceWindow = 0; 1037 sourceWindowMask = 0; 1038 sourceProtocolVersion = 0; 1039 sourceActions = DnDConstants.ACTION_NONE; 1040 sourceFormats = null; 1041 sourceAtom = 0; 1042 userAction = DnDConstants.ACTION_NONE; 1043 sourceX = 0; 1044 sourceY = 0; 1045 targetXWindow = null; 1046 topLevelLeavePostponed = false; 1047 } 1048 1049 public boolean isDragOverComponent() { 1050 return targetXWindow != null; 1051 } 1052 1053 private void notifyProtocolListener(XWindow xwindow, int x, int y, 1054 int dropAction, int actions, 1055 XClientMessageEvent xclient, 1056 int eventID) { 1057 long nativeCtxt = 0; 1058 1059 // Make a copy of the passed XClientMessageEvent structure, since 1060 // the original structure can be freed before this 1061 // SunDropTargetEvent is dispatched. 1062 if (xclient != null) { 1063 int size = XClientMessageEvent.getSize(); 1064 1065 nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); 1066 1067 unsafe.copyMemory(xclient.pData, nativeCtxt, size); 1068 } 1069 1070 getProtocolListener().handleDropTargetNotification(xwindow, x, y, 1071 dropAction, 1072 actions, 1073 sourceFormats, 1074 nativeCtxt, 1075 eventID); 1076 } 1077 }