1 /*****************************************************************************
2  * x11_dragdrop.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id: 9244a2532e9c167023384abeba9489e187e83f1f $
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 
25 #ifdef X11_SKINS
26 
27 #include <X11/Xlib.h>
28 #include <X11/Xatom.h>
29 
30 #include "x11_dragdrop.hpp"
31 #include "x11_display.hpp"
32 #include "x11_factory.hpp"
33 #include "../commands/cmd_add_item.hpp"
34 #include "../events/evt_dragndrop.hpp"
35 
36 #include <string>
37 #include <list>
38 
39 
X11DragDrop(intf_thread_t * pIntf,X11Display & rDisplay,Window win,bool playOnDrop,GenericWindow * pWin)40 X11DragDrop::X11DragDrop( intf_thread_t *pIntf, X11Display &rDisplay,
41                           Window win, bool playOnDrop, GenericWindow *pWin ):
42     SkinObject( pIntf ), m_rDisplay( rDisplay ), m_wnd( win ),
43     m_playOnDrop( playOnDrop ), m_pWin( pWin ), m_xPos( -1 ), m_yPos( -1 )
44 {
45 }
46 
47 
dndEnter(ldata_t data)48 void X11DragDrop::dndEnter( ldata_t data )
49 {
50     Window src = data[0];
51     int version = data[1]>>24;
52     m_xPos = m_yPos = -1;
53     (void)version;
54 
55     // Retrieve available data types
56     std::list<std::string> dataTypes;
57     if( data[1] & 1 )   // More than 3 data types ?
58     {
59         Atom type;
60         int format;
61         unsigned long nitems, nbytes;
62         Atom *dataList;
63         Atom typeListAtom = XInternAtom( XDISPLAY, "XdndTypeList", 0 );
64         XGetWindowProperty( XDISPLAY, src, typeListAtom, 0, 65536, False,
65                             XA_ATOM, &type, &format, &nitems, &nbytes,
66                             (unsigned char**)&dataList );
67         for( unsigned long i=0; i<nitems; i++ )
68         {
69             std::string dataType = XGetAtomName( XDISPLAY, dataList[i] );
70             dataTypes.push_back( dataType );
71         }
72         XFree( (void*)dataList );
73     }
74     else
75     {
76         for( int i = 2; i < 5; i++ )
77         {
78             if( data[i] != None )
79             {
80                 std::string dataType = XGetAtomName( XDISPLAY, data[i] );
81                 dataTypes.push_back( dataType );
82             }
83         }
84     }
85 
86     // list all data types available
87     std::list<std::string>::iterator it;
88     for( it = dataTypes.begin(); it != dataTypes.end(); ++it )
89         msg_Dbg( getIntf(), "D&D data type: %s", (*it).c_str() );
90 
91     // Find the right target
92     m_target = None;
93     for( it = dataTypes.begin(); it != dataTypes.end(); ++it )
94     {
95         if( *it == "text/uri-list" ||
96             *it == "text/plain" ||
97             *it == "text/plain;charset=utf-8" ||
98             *it == "STRING" ||
99             *it == "UTF8_STRING" )
100         {
101             m_target = XInternAtom( XDISPLAY, (*it).c_str(), 0 );
102             msg_Dbg( getIntf(), "D&D data type chosen: %s", (*it).c_str() );
103             break;
104         }
105     }
106 
107     // transmit DragEnter event
108     EvtDragEnter evt( getIntf() );
109     m_pWin->processEvent( evt );
110 }
111 
112 
dndPosition(ldata_t data)113 void X11DragDrop::dndPosition( ldata_t data )
114 {
115     Window src = data[0];
116     m_xPos = data[2] >> 16;
117     m_yPos = data[2] & 0xffff;
118     Time time = data[3];
119     Atom action = data[4];
120     (void)time; (void)action;
121 
122     Atom actionAtom = XInternAtom( XDISPLAY, "XdndActionCopy", 0 );
123     Atom typeAtom = XInternAtom( XDISPLAY, "XdndStatus", 0 );
124 
125     XEvent event;
126     event.type = ClientMessage;
127     event.xclient.window = src;
128     event.xclient.display = XDISPLAY;
129     event.xclient.message_type = typeAtom;
130     event.xclient.format = 32;
131     event.xclient.data.l[0] = m_wnd;
132     // Accept the drop (1), or not (0).
133     event.xclient.data.l[1] = m_target != None ? 1 : 0;
134     event.xclient.data.l[2] = 0;
135     event.xclient.data.l[3] = 0;
136     event.xclient.data.l[4] = actionAtom;
137 
138     // Tell the source whether we accept the drop
139     XSendEvent( XDISPLAY, src, False, 0, &event );
140 
141     // transmit DragOver event
142     EvtDragOver evt( getIntf(), m_xPos, m_yPos );
143     m_pWin->processEvent( evt );
144 }
145 
146 
dndLeave(ldata_t data)147 void X11DragDrop::dndLeave( ldata_t data )
148 {
149     (void)data;
150     m_target = None;
151 
152     // transmit DragLeave event
153     EvtDragLeave evt( getIntf() );
154     m_pWin->processEvent( evt );
155 }
156 
157 
dndDrop(ldata_t data)158 void X11DragDrop::dndDrop( ldata_t data )
159 {
160     std::list<std::string> files;
161 
162     Window src = data[0];
163     Time time = data[2];
164 
165     Atom selectionAtom = XInternAtom( XDISPLAY, "XdndSelection", 0 );
166     Atom propAtom = XInternAtom( XDISPLAY, "VLC_SELECTION", 0 );
167     // Convert the selection into the given target
168     XConvertSelection( XDISPLAY, selectionAtom, m_target, propAtom, src,
169                        time );
170     // Needed to ensure XGetWindowProperty returns something
171     XSync( XDISPLAY, False );
172 
173     // Read the selection
174     Atom type;
175     int format;
176     unsigned long nitems, nbytes_after_return;
177     char *buffer;
178     long length_max = 1024;
179     XGetWindowProperty( XDISPLAY, src, propAtom, 0, length_max, False,
180                         AnyPropertyType, &type, &format, &nitems,
181                         &nbytes_after_return, (unsigned char**)&buffer );
182     if( buffer && nbytes_after_return > 0 )
183     {
184         XFree( buffer );
185         length_max += nbytes_after_return;
186         XGetWindowProperty( XDISPLAY, src, propAtom, 0, length_max, False,
187                         AnyPropertyType, &type, &format, &nitems,
188                         &nbytes_after_return, (unsigned char**)&buffer );
189     }
190     if( buffer != NULL )
191     {
192         msg_Dbg( getIntf(), "buffer received: %s", buffer );
193         char* psz_dup = strdup( buffer );
194         char* psz_new = psz_dup;
195         while( psz_new && *psz_new )
196         {
197             int skip = 0;
198             const char* sep[] = { "\r\n", "\n", NULL };
199             for( int i = 0; sep[i]; i++ )
200             {
201                 char* psz_end = strstr( psz_new, sep[i] );
202                 if( !psz_end )
203                     continue;
204                 *psz_end = '\0';
205                 skip = strlen( sep[i] );
206                 break;
207             }
208             if( *psz_new && strstr( psz_new, "://" ) )
209             {
210                 files.push_back( psz_new );
211             }
212 
213             psz_new += strlen( psz_new ) + skip;
214         }
215         free( psz_dup );
216         XFree( buffer );
217     }
218 
219     Atom actionAtom = XInternAtom( XDISPLAY, "XdndActionCopy", 0 );
220     Atom typeAtom = XInternAtom( XDISPLAY, "XdndFinished", 0 );
221 
222     // Tell the source we accepted the drop
223     XEvent event;
224     event.type = ClientMessage;
225     event.xclient.window = src;
226     event.xclient.display = XDISPLAY;
227     event.xclient.message_type = typeAtom;
228     event.xclient.format = 32;
229     event.xclient.data.l[0] = m_wnd;
230     event.xclient.data.l[1] = 1;            // drop accepted
231     event.xclient.data.l[2] = actionAtom;
232     XSendEvent( XDISPLAY, src, False, 0, &event );
233 
234     // transmit DragDrop event
235     EvtDragDrop evt( getIntf(), m_xPos, m_yPos, files );
236     m_pWin->processEvent( evt );
237 }
238 
239 #endif
240