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