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