Blender  V2.93
GHOST_DropTargetX11.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 by the Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include "GHOST_DropTargetX11.h"
25 #include "GHOST_Debug.h"
26 
27 #include <assert.h>
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 bool GHOST_DropTargetX11::m_xdndInitialized = false;
33 DndClass GHOST_DropTargetX11::m_dndClass;
34 Atom *GHOST_DropTargetX11::m_dndTypes = NULL;
35 Atom *GHOST_DropTargetX11::m_dndActions = NULL;
36 const char *GHOST_DropTargetX11::m_dndMimeTypes[] = {
37  "url/url", "text/uri-list", "text/plain", "application/octet-stream"};
38 int GHOST_DropTargetX11::m_refCounter = 0;
39 
40 #define dndTypeURLID 0
41 #define dndTypeURIListID 1
42 #define dndTypePlainTextID 2
43 #define dndTypeOctetStreamID 3
44 
45 #define dndTypeURL m_dndTypes[dndTypeURLID]
46 #define dndTypeURIList m_dndTypes[dndTypeURIListID]
47 #define dndTypePlainText m_dndTypes[dndTypePlainTextID]
48 #define dndTypeOctetStream m_dndTypes[dndTypeOctetStreamID]
49 
50 void GHOST_DropTargetX11::Initialize(void)
51 {
52  Display *display = m_system->getXDisplay();
53  int dndTypesCount = sizeof(m_dndMimeTypes) / sizeof(char *);
54  int counter;
55 
56  xdnd_init(&m_dndClass, display);
57 
58  m_dndTypes = new Atom[dndTypesCount + 1];
59  XInternAtoms(display, (char **)m_dndMimeTypes, dndTypesCount, 0, m_dndTypes);
60  m_dndTypes[dndTypesCount] = 0;
61 
62  m_dndActions = new Atom[8];
63  counter = 0;
64 
65  m_dndActions[counter++] = m_dndClass.XdndActionCopy;
66  m_dndActions[counter++] = m_dndClass.XdndActionMove;
67 
68 #if 0 /* Not supported yet */
69  dndActions[counter++] = dnd->XdndActionLink;
70  dndActions[counter++] = dnd->XdndActionAsk;
71  dndActions[counter++] = dnd->XdndActionPrivate;
72  dndActions[counter++] = dnd->XdndActionList;
73  dndActions[counter++] = dnd->XdndActionDescription;
74 #endif
75 
76  m_dndActions[counter++] = 0;
77 }
78 
79 void GHOST_DropTargetX11::Uninitialize(void)
80 {
81  xdnd_shut(&m_dndClass);
82 
83  delete[] m_dndActions;
84  delete[] m_dndTypes;
85 }
86 
88  : m_window(window), m_system(system)
89 {
90  if (!m_xdndInitialized) {
91  Initialize();
92  m_xdndInitialized = true;
93  GHOST_PRINT("XDND initialized\n");
94  }
95 
96  Window wnd = window->getXWindow();
97 
98  xdnd_set_dnd_aware(&m_dndClass, wnd, 0);
99  xdnd_set_type_list(&m_dndClass, wnd, m_dndTypes);
100 
101  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
102  m_refCounter++;
103 }
104 
106 {
107  m_refCounter--;
108  if (m_refCounter == 0) {
109  Uninitialize();
110  m_xdndInitialized = false;
111  GHOST_PRINT("XDND uninitialized\n");
112  }
113 }
114 
115 /* based on a code from Saul Rennison
116  * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */
117 
118 typedef enum DecodeState_e {
122 
123 void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
124 {
125  unsigned int i;
126  unsigned int len = strlen(encodedIn);
128  int j;
129  unsigned int asciiCharacter;
130  char tempNumBuf[3] = {0};
131  bool bothDigits = true;
132 
133  memset(decodedOut, 0, bufferSize);
134 
135  for (i = 0; i < len; ++i) {
136  switch (state) {
137  case STATE_SEARCH:
138  if (encodedIn[i] != '%') {
139  strncat(decodedOut, &encodedIn[i], 1);
140  assert(strlen(decodedOut) < bufferSize);
141  break;
142  }
143 
144  /* We are now converting */
146  break;
147 
148  case STATE_CONVERTING:
149  bothDigits = true;
150 
151  /* Create a buffer to hold the hex. For example, if %20, this
152  * buffer would hold 20 (in ASCII) */
153  memset(tempNumBuf, 0, sizeof(tempNumBuf));
154 
155  /* Conversion complete (i.e. don't convert again next iter) */
157 
158  strncpy(tempNumBuf, &encodedIn[i], 2);
159 
160  /* Ensure both characters are hexadecimal */
161 
162  for (j = 0; j < 2; ++j) {
163  if (!isxdigit(tempNumBuf[j]))
164  bothDigits = false;
165  }
166 
167  if (!bothDigits)
168  break;
169 
170  /* Convert two hexadecimal characters into one character */
171  sscanf(tempNumBuf, "%x", &asciiCharacter);
172 
173  /* Ensure we aren't going to overflow */
174  assert(strlen(decodedOut) < bufferSize);
175 
176  /* Concatenate this character onto the output */
177  strncat(decodedOut, (char *)&asciiCharacter, 1);
178 
179  /* Skip the next character */
180  i++;
181  break;
182  }
183  }
184 }
185 
186 char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl)
187 {
188  if (strncmp(fileUrl, "file://", 7) == 0) {
189  /* assume one character of encoded URL can be expanded to 4 chars max */
190  int decodedSize = 4 * strlen(fileUrl) + 1;
191  char *decodedPath = (char *)malloc(decodedSize);
192 
193  UrlDecode(decodedPath, decodedSize, fileUrl + 7);
194 
195  return decodedPath;
196  }
197 
198  return NULL;
199 }
200 
201 void *GHOST_DropTargetX11::getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize)
202 {
203  GHOST_TStringArray *strArray = NULL;
204  int totPaths = 0, curLength = 0;
205 
206  /* Count total number of file paths in buffer. */
207  for (int i = 0; i <= dropBufferSize; i++) {
208  if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
209  if (curLength) {
210  totPaths++;
211  curLength = 0;
212  }
213  }
214  else
215  curLength++;
216  }
217 
218  strArray = (GHOST_TStringArray *)malloc(sizeof(GHOST_TStringArray));
219  strArray->count = 0;
220  strArray->strings = (GHOST_TUns8 **)malloc(totPaths * sizeof(GHOST_TUns8 *));
221 
222  curLength = 0;
223  for (int i = 0; i <= dropBufferSize; i++) {
224  if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
225  if (curLength) {
226  char *curPath = (char *)malloc(curLength + 1);
227  char *decodedPath;
228 
229  strncpy(curPath, (char *)dropBuffer + i - curLength, curLength);
230  curPath[curLength] = 0;
231 
232  decodedPath = FileUrlDecode(curPath);
233  if (decodedPath) {
234  strArray->strings[strArray->count] = (GHOST_TUns8 *)decodedPath;
235  strArray->count++;
236  }
237 
238  free(curPath);
239  curLength = 0;
240  }
241  }
242  else
243  curLength++;
244  }
245 
246  return strArray;
247 }
248 
250  unsigned char *dropBuffer,
251  int dropBufferSize)
252 {
253  void *data = NULL;
254  unsigned char *tmpBuffer = (unsigned char *)malloc(dropBufferSize + 1);
255  bool needsFree = true;
256 
257  /* ensure NULL-terminator */
258  memcpy(tmpBuffer, dropBuffer, dropBufferSize);
259  tmpBuffer[dropBufferSize] = 0;
260 
261  if (dropType == dndTypeURIList) {
262  m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
263  data = getURIListGhostData(tmpBuffer, dropBufferSize);
264  }
265  else if (dropType == dndTypeURL) {
266  /* need to be tested */
267  char *decodedPath = FileUrlDecode((char *)tmpBuffer);
268 
269  if (decodedPath) {
270  m_draggedObjectType = GHOST_kDragnDropTypeString;
271  data = decodedPath;
272  }
273  }
274  else if (dropType == dndTypePlainText || dropType == dndTypeOctetStream) {
275  m_draggedObjectType = GHOST_kDragnDropTypeString;
276  data = tmpBuffer;
277  needsFree = false;
278  }
279  else {
280  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
281  }
282 
283  if (needsFree)
284  free(tmpBuffer);
285 
286  return data;
287 }
288 
290 {
291  Atom dropType;
292  unsigned char *dropBuffer;
293  int dropBufferSize, dropX, dropY;
294 
295  if (xdnd_get_drop(m_system->getXDisplay(),
296  event,
297  m_dndTypes,
298  m_dndActions,
299  &dropBuffer,
300  &dropBufferSize,
301  &dropType,
302  &dropX,
303  &dropY)) {
304  void *data = getGhostData(dropType, dropBuffer, dropBufferSize);
305 
306  if (data)
307  m_system->pushDragDropEvent(
308  GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, dropX, dropY, data);
309 
310  free(dropBuffer);
311 
312  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
313 
314  return true;
315  }
316 
317  return false;
318 }
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:51
#define dndTypeURIList
#define dndTypePlainText
@ STATE_SEARCH
searching for an ampersand to convert
@ STATE_CONVERTING
convert the two proceeding characters from hex
#define dndTypeURL
#define dndTypeOctetStream
@ GHOST_kEventDraggingDropDone
Definition: GHOST_Types.h:208
@ GHOST_kDragnDropTypeUnknown
Definition: GHOST_Types.h:478
@ GHOST_kDragnDropTypeFilenames
Definition: GHOST_Types.h:479
@ GHOST_kDragnDropTypeString
Definition: GHOST_Types.h:480
unsigned char GHOST_TUns8
Definition: GHOST_Types.h:60
bool GHOST_HandleClientMessage(XEvent *event)
void * getGhostData(Atom dropType, unsigned char *dropBuffer, int dropBufferSize)
GHOST_DropTargetX11(GHOST_WindowX11 *window, GHOST_SystemX11 *system)
Display * getXDisplay()
static ulong state[N]
GHOST_TUns8 ** strings
Definition: GHOST_Types.h:513
uint len