1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.X11; 27 28 import java.awt.datatransfer.Transferable; 29 30 import java.awt.dnd.DnDConstants; 31 import java.awt.dnd.InvalidDnDOperationException; 32 33 import java.util.Map; 34 35 import sun.util.logging.PlatformLogger; 36 37 import sun.misc.Unsafe; 38 39 /** 40 * XDragSourceProtocol implementation for XDnD protocol. 41 * 42 * @since 1.5 43 */ 44 class XDnDDragSourceProtocol extends XDragSourceProtocol { 45 private static final PlatformLogger logger = 46 PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDragSourceProtocol"); 47 48 private static final Unsafe unsafe = XlibWrapper.unsafe; 49 XDnDDragSourceProtocol(XDragSourceProtocolListener listener)50 protected XDnDDragSourceProtocol(XDragSourceProtocolListener listener) { 51 super(listener); 52 } 53 54 /** 55 * Creates an instance associated with the specified listener. 56 * 57 * @throws NullPointerException if listener is <code>null</code>. 58 */ createInstance(XDragSourceProtocolListener listener)59 static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) { 60 return new XDnDDragSourceProtocol(listener); 61 } 62 getProtocolName()63 public String getProtocolName() { 64 return XDragAndDropProtocols.XDnD; 65 } 66 67 /** 68 * Performs protocol-specific drag initialization. 69 * 70 * @returns true if the initialized successfully. 71 */ initializeDragImpl(int actions, Transferable contents, Map formatMap, long[] formats)72 protected void initializeDragImpl(int actions, Transferable contents, 73 Map formatMap, long[] formats) 74 throws InvalidDnDOperationException, 75 IllegalArgumentException, XException { 76 assert XToolkit.isAWTLockHeldByCurrentThread(); 77 78 long window = XDragSourceProtocol.getDragSourceWindow(); 79 80 long data = Native.allocateLongArray(3); 81 int action_count = 0; 82 try { 83 if ((actions & DnDConstants.ACTION_COPY) != 0) { 84 Native.putLong(data, action_count, 85 XDnDConstants.XA_XdndActionCopy.getAtom()); 86 action_count++; 87 } 88 if ((actions & DnDConstants.ACTION_MOVE) != 0) { 89 Native.putLong(data, action_count, 90 XDnDConstants.XA_XdndActionMove.getAtom()); 91 action_count++; 92 } 93 if ((actions & DnDConstants.ACTION_LINK) != 0) { 94 Native.putLong(data, action_count, 95 XDnDConstants.XA_XdndActionLink.getAtom()); 96 action_count++; 97 } 98 99 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 100 XDnDConstants.XA_XdndActionList.setAtomData(window, 101 XAtom.XA_ATOM, 102 data, action_count); 103 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 104 105 if ((XErrorHandlerUtil.saved_error) != null && 106 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 107 cleanup(); 108 throw new XException("Cannot write XdndActionList property"); 109 } 110 } finally { 111 unsafe.freeMemory(data); 112 data = 0; 113 } 114 115 data = Native.allocateLongArray(formats.length); 116 117 try { 118 Native.put(data, formats); 119 120 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 121 XDnDConstants.XA_XdndTypeList.setAtomData(window, 122 XAtom.XA_ATOM, 123 data, formats.length); 124 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 125 126 if ((XErrorHandlerUtil.saved_error != null) && 127 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 128 cleanup(); 129 throw new XException("Cannot write XdndActionList property"); 130 } 131 } finally { 132 unsafe.freeMemory(data); 133 data = 0; 134 } 135 136 if (!XDnDConstants.XDnDSelection.setOwner(contents, formatMap, formats, 137 XConstants.CurrentTime)) { 138 cleanup(); 139 throw new InvalidDnDOperationException("Cannot acquire selection ownership"); 140 } 141 } 142 processXdndStatus(XClientMessageEvent xclient)143 private boolean processXdndStatus(XClientMessageEvent xclient) { 144 int action = DnDConstants.ACTION_NONE; 145 146 /* Ignore XDnD messages from all other windows. */ 147 if (xclient.get_data(0) != getTargetWindow()) { 148 return true; 149 } 150 151 if ((xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0) { 152 /* This feature is new in XDnD version 2, but we can use it as XDnD 153 compliance only requires supporting version 3 and up. */ 154 action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(4)); 155 } 156 157 getProtocolListener().handleDragReply(action); 158 159 return true; 160 } 161 processXdndFinished(XClientMessageEvent xclient)162 private boolean processXdndFinished(XClientMessageEvent xclient) { 163 /* Ignore XDnD messages from all other windows. */ 164 if (xclient.get_data(0) != getTargetWindow()) { 165 return true; 166 } 167 168 if (getTargetProtocolVersion() >= 5) { 169 boolean success = (xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0; 170 int action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(2)); 171 getProtocolListener().handleDragFinished(success, action); 172 } else { 173 getProtocolListener().handleDragFinished(); 174 } 175 176 finalizeDrop(); 177 178 return true; 179 } 180 processClientMessage(XClientMessageEvent xclient)181 public boolean processClientMessage(XClientMessageEvent xclient) { 182 if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom()) { 183 return processXdndStatus(xclient); 184 } else if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { 185 return processXdndFinished(xclient); 186 } else { 187 return false; 188 } 189 } 190 getTargetWindowInfo(long window)191 public TargetWindowInfo getTargetWindowInfo(long window) { 192 assert XToolkit.isAWTLockHeldByCurrentThread(); 193 194 WindowPropertyGetter wpg1 = 195 new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1, 196 false, XConstants.AnyPropertyType); 197 198 int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 199 200 if (status == XConstants.Success && 201 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) { 202 203 int targetVersion = (int)Native.getLong(wpg1.getData()); 204 205 wpg1.dispose(); 206 207 if (targetVersion >= XDnDConstants.XDND_MIN_PROTOCOL_VERSION) { 208 long proxy = 0; 209 int protocolVersion = 210 targetVersion < XDnDConstants.XDND_PROTOCOL_VERSION ? 211 targetVersion : XDnDConstants.XDND_PROTOCOL_VERSION; 212 213 WindowPropertyGetter wpg2 = 214 new WindowPropertyGetter(window, XDnDConstants.XA_XdndProxy, 215 0, 1, false, XAtom.XA_WINDOW); 216 217 try { 218 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 219 220 if (status == XConstants.Success && 221 wpg2.getData() != 0 && 222 wpg2.getActualType() == XAtom.XA_WINDOW) { 223 224 proxy = Native.getLong(wpg2.getData()); 225 } 226 } finally { 227 wpg2.dispose(); 228 } 229 230 if (proxy != 0) { 231 WindowPropertyGetter wpg3 = 232 new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy, 233 0, 1, false, XAtom.XA_WINDOW); 234 235 try { 236 status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 237 238 if (status != XConstants.Success || 239 wpg3.getData() == 0 || 240 wpg3.getActualType() != XAtom.XA_WINDOW || 241 Native.getLong(wpg3.getData()) != proxy) { 242 243 proxy = 0; 244 } else { 245 WindowPropertyGetter wpg4 = 246 new WindowPropertyGetter(proxy, 247 XDnDConstants.XA_XdndAware, 248 0, 1, false, 249 XConstants.AnyPropertyType); 250 251 try { 252 status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 253 254 if (status != XConstants.Success || 255 wpg4.getData() == 0 || 256 wpg4.getActualType() != XAtom.XA_ATOM) { 257 258 proxy = 0; 259 } 260 } finally { 261 wpg4.dispose(); 262 } 263 } 264 } finally { 265 wpg3.dispose(); 266 } 267 } 268 269 return new TargetWindowInfo(proxy, protocolVersion); 270 } 271 } else { 272 wpg1.dispose(); 273 } 274 275 return null; 276 } 277 278 public void sendEnterMessage(long[] formats, 279 int sourceAction, int sourceActions, long time) { 280 assert XToolkit.isAWTLockHeldByCurrentThread(); 281 assert getTargetWindow() != 0; 282 assert formats != null; 283 284 XClientMessageEvent msg = new XClientMessageEvent(); 285 try { 286 msg.set_type((int)XConstants.ClientMessage); 287 msg.set_window(getTargetWindow()); 288 msg.set_format(32); 289 msg.set_message_type(XDnDConstants.XA_XdndEnter.getAtom()); 290 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); 291 long data1 = 292 getTargetProtocolVersion() << XDnDConstants.XDND_PROTOCOL_SHIFT; 293 data1 |= formats.length > 3 ? XDnDConstants.XDND_DATA_TYPES_BIT : 0; 294 msg.set_data(1, data1); 295 msg.set_data(2, formats.length > 0 ? formats[0] : 0); 296 msg.set_data(3, formats.length > 1 ? formats[1] : 0); 297 msg.set_data(4, formats.length > 2 ? formats[2] : 0); 298 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 299 getTargetProxyWindow(), 300 false, XConstants.NoEventMask, 301 msg.pData); 302 } finally { 303 msg.dispose(); 304 } 305 } 306 sendMoveMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)307 public void sendMoveMessage(int xRoot, int yRoot, 308 int sourceAction, int sourceActions, long time) { 309 assert XToolkit.isAWTLockHeldByCurrentThread(); 310 assert getTargetWindow() != 0; 311 312 XClientMessageEvent msg = new XClientMessageEvent(); 313 try { 314 msg.set_type((int)XConstants.ClientMessage); 315 msg.set_window(getTargetWindow()); 316 msg.set_format(32); 317 msg.set_message_type(XDnDConstants.XA_XdndPosition.getAtom()); 318 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); 319 msg.set_data(1, 0); /* flags */ 320 msg.set_data(2, xRoot << 16 | yRoot); 321 msg.set_data(3, time); 322 msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(sourceAction)); 323 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 324 getTargetProxyWindow(), 325 false, XConstants.NoEventMask, 326 msg.pData); 327 } finally { 328 msg.dispose(); 329 } 330 } 331 sendLeaveMessage(long time)332 public void sendLeaveMessage(long time) { 333 assert XToolkit.isAWTLockHeldByCurrentThread(); 334 assert getTargetWindow() != 0; 335 336 XClientMessageEvent msg = new XClientMessageEvent(); 337 try { 338 msg.set_type((int)XConstants.ClientMessage); 339 msg.set_window(getTargetWindow()); 340 msg.set_format(32); 341 msg.set_message_type(XDnDConstants.XA_XdndLeave.getAtom()); 342 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); 343 msg.set_data(1, 0); 344 msg.set_data(2, 0); 345 msg.set_data(3, 0); 346 msg.set_data(4, 0); 347 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 348 getTargetProxyWindow(), 349 false, XConstants.NoEventMask, 350 msg.pData); 351 } finally { 352 msg.dispose(); 353 } 354 } 355 sendDropMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)356 public void sendDropMessage(int xRoot, int yRoot, 357 int sourceAction, int sourceActions, 358 long time) { 359 assert XToolkit.isAWTLockHeldByCurrentThread(); 360 assert getTargetWindow() != 0; 361 362 XClientMessageEvent msg = new XClientMessageEvent(); 363 try { 364 msg.set_type((int)XConstants.ClientMessage); 365 msg.set_window(getTargetWindow()); 366 msg.set_format(32); 367 msg.set_message_type(XDnDConstants.XA_XdndDrop.getAtom()); 368 msg.set_data(0, XDragSourceProtocol.getDragSourceWindow()); 369 msg.set_data(1, 0); /* flags */ 370 msg.set_data(2, time); 371 msg.set_data(3, 0); 372 msg.set_data(4, 0); 373 XlibWrapper.XSendEvent(XToolkit.getDisplay(), 374 getTargetProxyWindow(), 375 false, XConstants.NoEventMask, 376 msg.pData); 377 } finally { 378 msg.dispose(); 379 } 380 } 381 processProxyModeEvent(XClientMessageEvent xclient, long sourceWindow)382 public boolean processProxyModeEvent(XClientMessageEvent xclient, 383 long sourceWindow) { 384 if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom() || 385 xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { 386 387 if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) { 388 XDragSourceContextPeer.setProxyModeSourceWindow(0); 389 } 390 391 // This can happen if the drag operation started in the XEmbed server. 392 // In this case there is no need to forward it elsewhere, we should 393 // process it here. 394 if (xclient.get_window() == sourceWindow) { 395 return false; 396 } 397 398 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 399 logger.finest(" sourceWindow=" + sourceWindow + 400 " get_window=" + xclient.get_window() + 401 " xclient=" + xclient); 402 } 403 xclient.set_data(0, xclient.get_window()); 404 xclient.set_window(sourceWindow); 405 406 assert XToolkit.isAWTLockHeldByCurrentThread(); 407 408 XlibWrapper.XSendEvent(XToolkit.getDisplay(), sourceWindow, 409 false, XConstants.NoEventMask, 410 xclient.pData); 411 412 return true; 413 } 414 415 return false; 416 } 417 418 // TODO: register this runnable with XDnDSelection. run()419 public void run() { 420 // XdndSelection ownership lost. 421 cleanup(); 422 } 423 } 424