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 
XDnDDragSourceProtocol(XDragSourceProtocolListener listener)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}.
59      */
createInstance(XDragSourceProtocolListener listener)60     static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
61         return new XDnDDragSourceProtocol(listener);
62     }
63 
getProtocolName()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      */
initializeDragImpl(int actions, Transferable contents, Map<Long, DataFlavor> formatMap, long[] formats)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 
processXdndStatus(XClientMessageEvent xclient)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 
processXdndFinished(XClientMessageEvent xclient)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 
processClientMessage(XClientMessageEvent xclient)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 
getTargetWindowInfo(long window)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 
sendMoveMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)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 
sendLeaveMessage(long time)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 
sendDropMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)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 
processProxyModeEvent(XClientMessageEvent xclient, long sourceWindow)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.
run()420     public void run() {
421         // XdndSelection ownership lost.
422         cleanup();
423     }
424 }
425