DragAndDrop : support multiwindow dnd on single process
[platform/core/uifw/dali-adaptor.git] / dali / internal / drag-and-drop / tizen-wayland / drag-and-drop-impl-ecore-wl2.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/drag-and-drop/tizen-wayland/drag-and-drop-impl-ecore-wl2.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/internal/adaptor/tizen-wayland/dali-ecore-wl2.h>
25 #include <unistd.h>
26
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
28 // DragAndDrop
29 ///////////////////////////////////////////////////////////////////////////////////////////////////
30
31 namespace Dali
32 {
33 namespace Internal
34 {
35 namespace Adaptor
36 {
37 static bool IsIntersection(int px, int py, int tx, int ty, int tw, int th)
38 {
39   if(px > tx && py > ty && px < (tx + tw) && py < (ty + th))
40   {
41     return true;
42   }
43   return false;
44 }
45
46 static Eina_Bool EcoreEventDataSend(void* data, int type, void* event)
47 {
48   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
49   dndImpl->SendData(event);
50
51   return ECORE_CALLBACK_PASS_ON;
52 }
53
54 static Eina_Bool EcoreEventDataSourceEnd(void* data, int type, void* event)
55 {
56   Ecore_Wl2_Event_Data_Source_End *ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_End*>(event);
57   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
58   if(ev->cancelled)
59   {
60     dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::CANCEL);
61   }
62   else
63   {
64     dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::ACCEPT);
65   }
66
67   return ECORE_CALLBACK_PASS_ON;
68 }
69
70 static Eina_Bool EcoreEventDataSourceDrop(void* data, int type, void* event)
71 {
72   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
73   dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::FINISH);
74   return ECORE_CALLBACK_PASS_ON;
75 }
76
77 static Eina_Bool EcoreEventOfferDataReady(void* data, int type, void* event)
78 {
79   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
80   dndImpl->ReceiveData(event);
81
82   return ECORE_CALLBACK_PASS_ON;
83 }
84
85 static Eina_Bool EcoreEventDataMotion(void* data, int type, void* event)
86 {
87   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
88   dndImpl->CalculateDragEvent(event);
89
90   return ECORE_CALLBACK_PASS_ON;
91 }
92
93 static Eina_Bool EcoreEventDataDrop(void* data, int type, void* event)
94 {
95   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
96   dndImpl->CalculateViewRegion(event);
97
98   return ECORE_CALLBACK_PASS_ON;
99 }
100
101 static Eina_Bool EcoreEventDataEnter(void* data, int type, void* event)
102 {
103   Ecore_Wl2_Event_Dnd_Enter* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Enter*>(event);
104
105   // Set default offer is reject
106   ecore_wl2_offer_accept(ev->offer, NULL);
107   return ECORE_CALLBACK_PASS_ON;
108 }
109
110 static Eina_Bool EcoreEventDataLeave(void* data, int type, void* event)
111 {
112   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
113   dndImpl->ResetDropTargets();
114
115   return ECORE_CALLBACK_PASS_ON;
116 }
117
118 Dali::DragAndDrop GetDragAndDrop()
119 {
120   Dali::DragAndDrop dnd;
121
122   Dali::SingletonService service(SingletonService::Get());
123   if(service)
124   {
125     // Check whether the singleton is already created
126     Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::DragAndDrop));
127     if(handle)
128     {
129       // If so, downcast the handle
130       dnd = Dali::DragAndDrop(dynamic_cast<DragAndDrop*>(handle.GetObjectPtr()));
131     }
132     else
133     {
134       // Create a singleon instance
135       DragAndDropEcoreWl* dndImpl = new DragAndDropEcoreWl();
136
137       dnd = Dali::DragAndDrop(dndImpl);
138       service.Register(typeid(Dali::DragAndDrop), dnd);
139     }
140   }
141
142   return dnd;
143 }
144
145 DragAndDropEcoreWl::DragAndDropEcoreWl()
146 {
147   // Source Events
148   mSendHandler       = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND, EcoreEventDataSend, this);
149   mSourceEndHandler  = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_END, EcoreEventDataSourceEnd, this);
150   mSourceDropHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_DROP, EcoreEventDataSourceDrop, this);
151
152   // Target Events
153   mReceiveHandler    = ecore_event_handler_add(ECORE_WL2_EVENT_OFFER_DATA_READY, EcoreEventOfferDataReady, this);
154   mMotionHandler     = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION, EcoreEventDataMotion, this);
155   mDropHandler       = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP, EcoreEventDataDrop, this);
156   mEnterHandler      = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER, EcoreEventDataEnter, this);
157   mLeaveHandler      = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE, EcoreEventDataLeave, this);
158 }
159
160 DragAndDropEcoreWl::~DragAndDropEcoreWl()
161 {
162   // Source Events
163   ecore_event_handler_del(mSendHandler);
164   ecore_event_handler_del(mSourceEndHandler);
165   ecore_event_handler_del(mSourceDropHandler);
166
167   // Target Events
168   ecore_event_handler_del(mReceiveHandler);
169   ecore_event_handler_del(mMotionHandler);
170   ecore_event_handler_del(mDropHandler);
171   ecore_event_handler_del(mEnterHandler);
172 }
173
174 bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback)
175 {
176   // Get Parent Window
177   auto parent = Dali::DevelWindow::Get(source);
178
179   // Set Drag Source Data
180   mMimeType = data.GetMimeType();
181   mData     = data.GetData();
182
183   // Set Source Event
184   mSourceCallback = callback;
185
186   // Set Drag Window
187   mDragWindow = shadowWindow;
188
189   // Start Drag and Drop
190   Ecore_Wl2_Window*  parentWindow = AnyCast<Ecore_Wl2_Window*>(parent.GetNativeHandle());
191   Ecore_Wl2_Window*  dragWindow   = AnyCast<Ecore_Wl2_Window*>(mDragWindow.GetNativeHandle());
192   Ecore_Wl2_Display* display      = ecore_wl2_connected_display_get(NULL);
193   Ecore_Wl2_Input*   input        = ecore_wl2_input_default_input_get(display);
194
195   // Disable Default Cursor
196   ecore_wl2_input_pointer_set(input, NULL, 0, 0);
197
198   // Set mime type for drag and drop
199   const char* mimeTypes[2];
200   mimeTypes[0] = mMimeType.c_str();
201   mimeTypes[1] = NULL;
202
203   // Set mime type
204   ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
205
206   // Start wayland drag and drop
207   mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
208
209   // Call Start Event
210   CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
211
212   return true;
213 }
214
215 bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
216 {
217   std::vector<DropTarget>::iterator itr;
218   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
219   {
220     if((*itr).target == target)
221     {
222       return false;
223     }
224   }
225
226   auto parent = Dali::DevelWindow::Get(target);
227   Ecore_Wl2_Window*  parentWindow = AnyCast<Ecore_Wl2_Window*>(parent.GetNativeHandle());
228   if(parentWindow == nullptr)
229   {
230     return false;
231   }
232
233   DropTarget targetData;
234   targetData.target   = target;
235   targetData.callback = callback;
236   targetData.inside   = false;
237   targetData.parentWindowId = ecore_wl2_window_id_get(parentWindow);
238
239   mDropTargets.push_back(targetData);
240
241   return true;
242 }
243
244 bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
245 {
246   std::vector<DropTarget>::iterator itr;
247   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
248   {
249     if((*itr).target == target)
250     {
251       mDropTargets.erase(itr);
252       break;
253     }
254   }
255
256   return true;
257 }
258
259 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
260 {
261   if(mSourceCallback)
262   {
263     mSourceCallback(type);
264   }
265 }
266
267 void DragAndDropEcoreWl::ResetDropTargets()
268 {
269   for(std::size_t i = 0; i < mDropTargets.size(); i++)
270   {
271      mDropTargets[i].inside = false;
272   }
273 }
274
275
276 void DragAndDropEcoreWl::SendData(void* event)
277 {
278   Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
279   if(ev->serial != mSerial)
280   {
281     return;
282   }
283
284   int dataLength = strlen(mData.c_str());
285   int bufferSize = dataLength;
286   if((mMimeType.find("text") != std::string::npos) ||
287      (mMimeType.find("markup") != std::string::npos) ||
288      (mMimeType.find("image") != std::string::npos))
289   {
290     bufferSize += 1;
291   }
292
293   char* buffer = new char[bufferSize];
294   if(!buffer)
295   {
296     return;
297   }
298
299   memcpy(buffer, mData.c_str(), dataLength);
300   buffer[dataLength] = '\0';
301
302   auto ret = write(ev->fd, buffer, bufferSize);
303   if(DALI_UNLIKELY(ret != bufferSize))
304   {
305     DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
306   }
307
308   close(ev->fd);
309
310   if(mDragWindow)
311   {
312     mDragWindow.Hide();
313   }
314
315   delete[] buffer;
316 }
317
318 void DragAndDropEcoreWl::ReceiveData(void* event)
319 {
320   Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
321
322   if(mTargetIndex != -1)
323   {
324     Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->mimetype, ev->data);
325     mDropTargets[mTargetIndex].callback(dragEvent);
326     mDropTargets[mTargetIndex].inside = false;
327     ecore_wl2_offer_finish(ev->offer);
328   }
329   mTargetIndex = -1;
330 }
331
332 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
333 {
334   Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
335
336   Dali::DragAndDrop::DragEvent dragEvent;
337   Dali::Vector2                curPosition(ev->x, ev->y);
338
339   for(std::size_t i = 0; i < mDropTargets.size(); i++)
340   {
341     if(ev->win != mDropTargets[i].parentWindowId)
342     {
343       continue;
344     }
345
346     Vector2 position      = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::POSITION);
347     Vector2 size          = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
348     bool    currentInside = IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height);
349
350     // Calculate Drag Enter, Leave, Move Event
351     if(currentInside && !mDropTargets[i].inside)
352     {
353       mDropTargets[i].inside = true;
354       // Call Enter Event
355       dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
356       dragEvent.SetPosition(curPosition);
357       mDropTargets[i].callback(dragEvent);
358       // Accept Offer
359       ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
360     }
361     else if(!currentInside && mDropTargets[i].inside)
362     {
363       mDropTargets[i].inside = false;
364       // Call Leave Event
365       dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
366       dragEvent.SetPosition(curPosition);
367       mDropTargets[i].callback(dragEvent);
368       // Reject Offer
369       ecore_wl2_offer_accept(ev->offer, NULL);
370     }
371     else if(currentInside && mDropTargets[i].inside)
372     {
373       // Call Move Event
374       dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
375       dragEvent.SetPosition(curPosition);
376       mDropTargets[i].callback(dragEvent);
377     }
378   }
379
380   return true;
381 }
382
383 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
384 {
385   Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
386
387   // Check the target object region
388   mTargetIndex = -1;
389
390   for(std::size_t i = 0; i < mDropTargets.size(); i++)
391   {
392     if(ev->win != mDropTargets[i].parentWindowId)
393     {
394       continue;
395     }
396
397     Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::POSITION);
398     Vector2 size     = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
399     // If the drop position is in the target object region, request drop data to the source object
400     if(IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height))
401     {
402       mTargetIndex        = i;
403       mPosition           = position;
404       Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
405
406       char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
407       if(mimetype)
408       {
409         ecore_wl2_offer_receive(ev->offer, mimetype);
410         Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
411         Ecore_Wl2_Input*   input   = ecore_wl2_input_default_input_get(display);
412         ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
413       }
414       return true;
415     }
416   }
417
418   return false;
419 }
420
421 } // namespace Adaptor
422
423 } // namespace Internal
424
425 } // namespace Dali