1 /*
2  * This file is part of MPlayer.
3  *
4  * MPlayer is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * MPlayer is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * based on: WindowMaker implementation,
19  *           adopted for MPlayer
20  */
21 
22 #include <X11/Xlib.h>
23 #include "wsxdnd.h"
24 #include "gui/app/gui.h"
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <X11/Xatom.h>
31 
32 #include "mp_msg.h"
33 #include "help_mp.h"
34 
35 #define XDND_VERSION 3L
36 
37 Atom XA_XdndAware;
38 Atom XA_XdndEnter;
39 Atom XA_XdndLeave;
40 Atom XA_XdndDrop;
41 Atom XA_XdndPosition;
42 Atom XA_XdndStatus;
43 Atom XA_XdndActionCopy;
44 Atom XA_XdndSelection;
45 Atom XA_XdndFinished;
46 Atom XA_XdndTypeList;
47 
48 Atom atom_support;
49 
wsXDNDInitialize(void)50 void wsXDNDInitialize(void)
51 {
52 
53     XA_XdndAware = XInternAtom(wsDisplay, "XdndAware", False);
54     XA_XdndEnter = XInternAtom(wsDisplay, "XdndEnter", False);
55     XA_XdndLeave = XInternAtom(wsDisplay, "XdndLeave", False);
56     XA_XdndDrop = XInternAtom(wsDisplay, "XdndDrop", False);
57     XA_XdndPosition = XInternAtom(wsDisplay, "XdndPosition", False);
58     XA_XdndStatus = XInternAtom(wsDisplay, "XdndStatus", False);
59     XA_XdndActionCopy = XInternAtom(wsDisplay, "XdndActionCopy", False);
60     XA_XdndSelection = XInternAtom(wsDisplay, "XdndSelection", False);
61     XA_XdndFinished = XInternAtom(wsDisplay, "XdndFinished", False);
62     XA_XdndTypeList = XInternAtom(wsDisplay, "XdndTypeList", False);
63 }
64 
wsXDNDMakeAwareness(wsWindow * win)65 void wsXDNDMakeAwareness(wsWindow* win) {
66     long int xdnd_version = XDND_VERSION;
67     XChangeProperty (wsDisplay, win->WindowID, XA_XdndAware, XA_ATOM,
68             32, PropModeAppend, (char *)&xdnd_version, 1);
69 }
70 
wsXDNDClearAwareness(wsWindow * win)71 void wsXDNDClearAwareness(wsWindow* win) {
72     XDeleteProperty (wsDisplay, win->WindowID, XA_XdndAware);
73 }
74 
75 #define MAX_DND_FILES 64
76 Bool
wsXDNDProcessSelection(wsWindow * win,XEvent * event)77 wsXDNDProcessSelection(wsWindow* win, XEvent *event)
78 {
79     Atom ret_type;
80     int ret_format;
81     unsigned long ret_items;
82     unsigned long remain_byte;
83     char * delme;
84     XEvent xevent;
85 
86     Window selowner = XGetSelectionOwner(wsDisplay,XA_XdndSelection);
87 
88     XGetWindowProperty(wsDisplay, event->xselection.requestor,
89             event->xselection.property,
90             0, 65536, True, atom_support, &ret_type, &ret_format,
91             &ret_items, &remain_byte, (unsigned char **)&delme);
92 
93     /*send finished*/
94     memset (&xevent, 0, sizeof(xevent));
95     xevent.xany.type = ClientMessage;
96     xevent.xany.display = wsDisplay;
97     xevent.xclient.window = selowner;
98     xevent.xclient.message_type = XA_XdndFinished;
99     xevent.xclient.format = 32;
100     XDND_FINISHED_TARGET_WIN(&xevent) = win->WindowID;
101     XSendEvent(wsDisplay, selowner, 0, 0, &xevent);
102 
103     if (!delme){
104       mp_msg( MSGT_GPLAYER,MSGL_WARN,_(MSGTR_GUI_MSG_DragAndDropNothing) );
105       return False;
106     }
107 
108     {
109       /* Handle dropped files */
110       char * retain = delme;
111       char * files[MAX_DND_FILES];
112       int num = 0;
113 
114       while(retain < delme + ret_items) {
115         if (!strncmp(retain,"file:",5)) {
116           /* add more 2 chars while removing 5 is harmless */
117           retain+=5;
118         }
119 
120         /* add the "retain" to the list */
121         files[num++]=retain;
122 
123 
124         /* now check for special characters */
125         {
126           int newone = 0;
127           while(retain < (delme + ret_items)){
128             if(*retain == '\r' || *retain == '\n'){
129               *retain=0;
130               newone = 1;
131             } else {
132               if (newone)
133                 break;
134             }
135             retain++;
136           }
137         }
138 
139         if (num >= MAX_DND_FILES)
140           break;
141       }
142 
143       /* Handle the files */
144       if(win->DNDHandler){
145         win->DNDHandler(num,files);
146       }
147     }
148 
149     free(delme);
150     return True;
151 }
152 
153 Bool
wsXDNDProcessClientMessage(XClientMessageEvent * event)154 wsXDNDProcessClientMessage(XClientMessageEvent *event)
155 {
156   /* test */
157   /*{
158     char * name = XGetAtomName(wsDisplay, event->message_type);
159     printf("Got %s\n",name);
160     XFree(name);
161     }*/
162 
163   if (event->message_type == XA_XdndEnter) {
164     Atom ok = XInternAtom(wsDisplay, "text/uri-list", False);
165     atom_support = None;
166     if ((event->data.l[1] & 1) == 0){
167       int index;
168       for(index = 0; index <= 2 ; index++){
169         if ((Atom) event->data.l[2+index] == ok) {
170           atom_support = ok;
171         }
172       }
173       if (atom_support == None) {
174         mp_msg( MSGT_GPLAYER,MSGL_WARN,_(MSGTR_GUI_MSG_NotAFile0) );
175       }
176     } else {
177       /* need to check the whole list here */
178       unsigned long ret_left = 1;
179       int offset = 0;
180       Atom* ret_buff;
181       Atom ret_type;
182       int ret_format;
183       unsigned long ret_items;
184 
185       /* while there is data left...*/
186       while(ret_left && atom_support == None){
187         XGetWindowProperty(wsDisplay,event->data.l[0],XA_XdndTypeList,
188                            offset,256,False,XA_ATOM,&ret_type,
189                            &ret_format,&ret_items,&ret_left,
190                            (unsigned char**)&ret_buff);
191 
192         /* sanity checks...*/
193         if(ret_buff == NULL || ret_type != XA_ATOM || ret_format != 8*sizeof(Atom)){
194           XFree(ret_buff);
195           break;
196         }
197         /* now chek what we've got */
198         {
199           unsigned long i;
200           for(i=0; i<ret_items; i++){
201             if(ret_buff[i] == ok){
202               atom_support = ok;
203               break;
204             }
205           }
206         }
207         /* maybe next time ... */
208         XFree(ret_buff);
209         offset += 256;
210       }
211     }
212     return True;
213   }
214 
215   if (event->message_type == XA_XdndLeave) {
216     return True;
217   }
218 
219   if (event->message_type == XA_XdndDrop) {
220     if ((Window) event->data.l[0] != XGetSelectionOwner(wsDisplay, XA_XdndSelection)){
221       mp_msg( MSGT_GPLAYER,MSGL_WARN,_(MSGTR_GUI_MSG_DragAndDropOwner) );
222     }
223     if (atom_support != None) {
224       XConvertSelection(wsDisplay, XA_XdndSelection, atom_support,
225                         XA_XdndSelection, event->window,
226                         CurrentTime);
227     }
228     return True;
229   }
230 
231   if (event->message_type == XA_XdndPosition) {
232     Window srcwin = event->data.l[0];
233     if (atom_support == None){
234       return True;
235     }
236 
237     /* send response */
238     {
239       XEvent xevent;
240       memset (&xevent, 0, sizeof(xevent));
241       xevent.xany.type = ClientMessage;
242       xevent.xany.display = wsDisplay;
243       xevent.xclient.window = srcwin;
244       xevent.xclient.message_type = XA_XdndStatus;
245       xevent.xclient.format = 32;
246 
247       XDND_STATUS_TARGET_WIN (&xevent) = event->window;
248       XDND_STATUS_WILL_ACCEPT_SET (&xevent, True);
249       XDND_STATUS_WANT_POSITION_SET(&xevent, True);
250       /* actually need smth real here */
251       XDND_STATUS_RECT_SET(&xevent, 0, 0, 1024,768);
252       XDND_STATUS_ACTION(&xevent) = XA_XdndActionCopy;
253 
254       XSendEvent(wsDisplay, srcwin, 0, 0, &xevent);
255     }
256     return True;
257   }
258 
259   return False;
260 }
261