Blender  V3.3
GHOST_DropTargetX11.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2012 Blender Foundation. All rights reserved. */
3 
8 #include "GHOST_DropTargetX11.h"
9 #include "GHOST_Debug.h"
10 #include "GHOST_utildefines.h"
11 
12 #include <cassert>
13 #include <cctype>
14 #include <cstdio>
15 #include <cstring>
16 
17 bool GHOST_DropTargetX11::m_xdndInitialized = false;
18 DndClass GHOST_DropTargetX11::m_dndClass;
19 Atom *GHOST_DropTargetX11::m_dndTypes = nullptr;
20 Atom *GHOST_DropTargetX11::m_dndActions = nullptr;
21 const char *GHOST_DropTargetX11::m_dndMimeTypes[] = {
22  "url/url", "text/uri-list", "text/plain", "application/octet-stream"};
23 int GHOST_DropTargetX11::m_refCounter = 0;
24 
25 #define dndTypeURLID 0
26 #define dndTypeURIListID 1
27 #define dndTypePlainTextID 2
28 #define dndTypeOctetStreamID 3
29 
30 #define dndTypeURL m_dndTypes[dndTypeURLID]
31 #define dndTypeURIList m_dndTypes[dndTypeURIListID]
32 #define dndTypePlainText m_dndTypes[dndTypePlainTextID]
33 #define dndTypeOctetStream m_dndTypes[dndTypeOctetStreamID]
34 
35 void GHOST_DropTargetX11::Initialize()
36 {
37  Display *display = m_system->getXDisplay();
38  int dndTypesCount = ARRAY_SIZE(m_dndMimeTypes);
39  int counter;
40 
41  xdnd_init(&m_dndClass, display);
42 
43  m_dndTypes = new Atom[dndTypesCount + 1];
44  XInternAtoms(display, (char **)m_dndMimeTypes, dndTypesCount, 0, m_dndTypes);
45  m_dndTypes[dndTypesCount] = 0;
46 
47  m_dndActions = new Atom[8];
48  counter = 0;
49 
50  m_dndActions[counter++] = m_dndClass.XdndActionCopy;
51  m_dndActions[counter++] = m_dndClass.XdndActionMove;
52 
53 #if 0 /* Not supported yet */
54  dndActions[counter++] = dnd->XdndActionLink;
55  dndActions[counter++] = dnd->XdndActionAsk;
56  dndActions[counter++] = dnd->XdndActionPrivate;
57  dndActions[counter++] = dnd->XdndActionList;
58  dndActions[counter++] = dnd->XdndActionDescription;
59 #endif
60 
61  m_dndActions[counter++] = 0;
62 }
63 
64 void GHOST_DropTargetX11::Uninitialize()
65 {
66  xdnd_shut(&m_dndClass);
67 
68  delete[] m_dndActions;
69  delete[] m_dndTypes;
70 }
71 
73  : m_window(window), m_system(system)
74 {
75  if (!m_xdndInitialized) {
76  Initialize();
77  m_xdndInitialized = true;
78  GHOST_PRINT("XDND initialized\n");
79  }
80 
81  Window wnd = window->getXWindow();
82 
83  xdnd_set_dnd_aware(&m_dndClass, wnd, 0);
84  xdnd_set_type_list(&m_dndClass, wnd, m_dndTypes);
85 
86  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
87  m_refCounter++;
88 }
89 
91 {
92  m_refCounter--;
93  if (m_refCounter == 0) {
94  Uninitialize();
95  m_xdndInitialized = false;
96  GHOST_PRINT("XDND uninitialized\n");
97  }
98 }
99 
100 /* Based on: https://stackoverflow.com/a/2766963/432509 */
101 
104  STATE_SEARCH = 0,
106  STATE_CONVERTING
107 };
108 
109 void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
110 {
111  unsigned int i;
112  unsigned int len = strlen(encodedIn);
113  DecodeState_e state = STATE_SEARCH;
114  int j;
115  unsigned int asciiCharacter;
116  char tempNumBuf[3] = {0};
117  bool bothDigits = true;
118 
119  memset(decodedOut, 0, bufferSize);
120 
121  for (i = 0; i < len; ++i) {
122  switch (state) {
123  case STATE_SEARCH:
124  if (encodedIn[i] != '%') {
125  strncat(decodedOut, &encodedIn[i], 1);
126  assert((int)strlen(decodedOut) < bufferSize);
127  break;
128  }
129 
130  /* We are now converting */
131  state = STATE_CONVERTING;
132  break;
133 
134  case STATE_CONVERTING:
135  bothDigits = true;
136 
137  /* Create a buffer to hold the hex. For example, if %20, this
138  * buffer would hold 20 (in ASCII) */
139  memset(tempNumBuf, 0, sizeof(tempNumBuf));
140 
141  /* Conversion complete (i.e. don't convert again next iter) */
142  state = STATE_SEARCH;
143 
144  strncpy(tempNumBuf, &encodedIn[i], 2);
145 
146  /* Ensure both characters are hexadecimal */
147 
148  for (j = 0; j < 2; ++j) {
149  if (!isxdigit(tempNumBuf[j])) {
150  bothDigits = false;
151  }
152  }
153 
154  if (!bothDigits) {
155  break;
156  }
157  /* Convert two hexadecimal characters into one character */
158  sscanf(tempNumBuf, "%x", &asciiCharacter);
159 
160  /* Ensure we aren't going to overflow */
161  assert((int)strlen(decodedOut) < bufferSize);
162 
163  /* Concatenate this character onto the output */
164  strncat(decodedOut, (char *)&asciiCharacter, 1);
165 
166  /* Skip the next character */
167  i++;
168  break;
169  }
170  }
171 }
172 
173 char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl)
174 {
175  if (strncmp(fileUrl, "file://", 7) == 0) {
176  /* assume one character of encoded URL can be expanded to 4 chars max */
177  int decodedSize = 4 * strlen(fileUrl) + 1;
178  char *decodedPath = (char *)malloc(decodedSize);
179 
180  UrlDecode(decodedPath, decodedSize, fileUrl + 7);
181 
182  return decodedPath;
183  }
184 
185  return nullptr;
186 }
187 
188 void *GHOST_DropTargetX11::getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize)
189 {
190  GHOST_TStringArray *strArray = nullptr;
191  int totPaths = 0, curLength = 0;
192 
193  /* Count total number of file paths in buffer. */
194  for (int i = 0; i <= dropBufferSize; i++) {
195  if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
196  if (curLength) {
197  totPaths++;
198  curLength = 0;
199  }
200  }
201  else {
202  curLength++;
203  }
204  }
205 
206  strArray = (GHOST_TStringArray *)malloc(sizeof(GHOST_TStringArray));
207  strArray->count = 0;
208  strArray->strings = (uint8_t **)malloc(totPaths * sizeof(uint8_t *));
209 
210  curLength = 0;
211  for (int i = 0; i <= dropBufferSize; i++) {
212  if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
213  if (curLength) {
214  char *curPath = (char *)malloc(curLength + 1);
215  char *decodedPath;
216 
217  strncpy(curPath, (char *)dropBuffer + i - curLength, curLength);
218  curPath[curLength] = 0;
219 
220  decodedPath = FileUrlDecode(curPath);
221  if (decodedPath) {
222  strArray->strings[strArray->count] = (uint8_t *)decodedPath;
223  strArray->count++;
224  }
225 
226  free(curPath);
227  curLength = 0;
228  }
229  }
230  else {
231  curLength++;
232  }
233  }
234 
235  return strArray;
236 }
237 
239  unsigned char *dropBuffer,
240  int dropBufferSize)
241 {
242  void *data = nullptr;
243  unsigned char *tmpBuffer = (unsigned char *)malloc(dropBufferSize + 1);
244  bool needsFree = true;
245 
246  /* Ensure nil-terminator. */
247  memcpy(tmpBuffer, dropBuffer, dropBufferSize);
248  tmpBuffer[dropBufferSize] = 0;
249 
250  if (dropType == dndTypeURIList) {
251  m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
252  data = getURIListGhostData(tmpBuffer, dropBufferSize);
253  }
254  else if (dropType == dndTypeURL) {
255  /* need to be tested */
256  char *decodedPath = FileUrlDecode((char *)tmpBuffer);
257 
258  if (decodedPath) {
259  m_draggedObjectType = GHOST_kDragnDropTypeString;
260  data = decodedPath;
261  }
262  }
263  else if (dropType == dndTypePlainText || dropType == dndTypeOctetStream) {
264  m_draggedObjectType = GHOST_kDragnDropTypeString;
265  data = tmpBuffer;
266  needsFree = false;
267  }
268  else {
269  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
270  }
271 
272  if (needsFree) {
273  free(tmpBuffer);
274  }
275 
276  return data;
277 }
278 
280 {
281  Atom dropType;
282  unsigned char *dropBuffer;
283  int dropBufferSize, dropX, dropY;
284 
285  if (xdnd_get_drop(m_system->getXDisplay(),
286  event,
287  m_dndTypes,
288  m_dndActions,
289  &dropBuffer,
290  &dropBufferSize,
291  &dropType,
292  &dropX,
293  &dropY)) {
294  void *data = getGhostData(dropType, dropBuffer, dropBufferSize);
295 
296  if (data) {
297  m_system->pushDragDropEvent(
298  GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, dropX, dropY, data);
299  }
300 
301  free(dropBuffer);
302 
303  m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
304 
305  return true;
306  }
307 
308  return false;
309 }
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
#define ARRAY_SIZE(arr)
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:35
#define dndTypeURIList
#define dndTypePlainText
#define dndTypeURL
#define dndTypeOctetStream
enum DecodeState_e { STATE_SEARCH=0, STATE_CONVERTING } DecodeState_e
@ GHOST_kEventDraggingDropDone
Definition: GHOST_Types.h:200
@ GHOST_kDragnDropTypeUnknown
Definition: GHOST_Types.h:475
@ GHOST_kDragnDropTypeFilenames
Definition: GHOST_Types.h:476
@ GHOST_kDragnDropTypeString
Definition: GHOST_Types.h:477
bool GHOST_HandleClientMessage(XEvent *event)
void * getGhostData(Atom dropType, unsigned char *dropBuffer, int dropBufferSize)
GHOST_DropTargetX11(GHOST_WindowX11 *window, GHOST_SystemX11 *system)
Display * getXDisplay()
int len
Definition: draw_manager.c:108
ccl_gpu_kernel_postfix ccl_global int * counter
const int state
unsigned char uint8_t
Definition: stdint.h:78
uint8_t ** strings
Definition: GHOST_Types.h:508