DragAndDrop : set target coordinates based on screen position (window basis)
[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   // Set mime type for drag and drop
196   const char* mimeTypes[2];
197   mimeTypes[0] = mMimeType.c_str();
198   mimeTypes[1] = NULL;
199
200   // Set mime type
201   ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
202
203   // Start wayland drag and drop
204   mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
205
206   // Call Start Event
207   CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
208
209   return true;
210 }
211
212 bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
213 {
214   std::vector<DropTarget>::iterator itr;
215   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
216   {
217     if((*itr).target == target)
218     {
219       return false;
220     }
221   }
222
223   auto parent = Dali::DevelWindow::Get(target);
224   Ecore_Wl2_Window*  parentWindow = AnyCast<Ecore_Wl2_Window*>(parent.GetNativeHandle());
225   if(parentWindow == nullptr)
226   {
227     return false;
228   }
229
230   DropTarget targetData;
231   targetData.target   = target;
232   targetData.callback = callback;
233   targetData.inside   = false;
234   targetData.parentWindowId = ecore_wl2_window_id_get(parentWindow);
235
236   mDropTargets.push_back(targetData);
237
238   return true;
239 }
240
241 bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
242 {
243   std::vector<DropTarget>::iterator itr;
244   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
245   {
246     if((*itr).target == target)
247     {
248       mDropTargets.erase(itr);
249       break;
250     }
251   }
252
253   return true;
254 }
255
256 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
257 {
258   if(mSourceCallback)
259   {
260     mSourceCallback(type);
261   }
262 }
263
264 void DragAndDropEcoreWl::ResetDropTargets()
265 {
266   for(std::size_t i = 0; i < mDropTargets.size(); i++)
267   {
268      mDropTargets[i].inside = false;
269   }
270 }
271
272
273 void DragAndDropEcoreWl::SendData(void* event)
274 {
275   Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
276   if(ev->serial != mSerial)
277   {
278     return;
279   }
280
281   int dataLength = strlen(mData.c_str());
282   int bufferSize = dataLength;
283   if((mMimeType.find("text") != std::string::npos) ||
284      (mMimeType.find("markup") != std::string::npos) ||
285      (mMimeType.find("image") != std::string::npos))
286   {
287     bufferSize += 1;
288   }
289
290   char* buffer = new char[bufferSize];
291   if(!buffer)
292   {
293     return;
294   }
295
296   memcpy(buffer, mData.c_str(), dataLength);
297   buffer[dataLength] = '\0';
298
299   auto ret = write(ev->fd, buffer, bufferSize);
300   if(DALI_UNLIKELY(ret != bufferSize))
301   {
302     DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
303   }
304
305   close(ev->fd);
306
307   if(mDragWindow)
308   {
309     mDragWindow.Hide();
310   }
311
312   delete[] buffer;
313 }
314
315 void DragAndDropEcoreWl::ReceiveData(void* event)
316 {
317   Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
318
319   if(mTargetIndex != -1)
320   {
321     Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->mimetype, ev->data);
322     mDropTargets[mTargetIndex].callback(dragEvent);
323     mDropTargets[mTargetIndex].inside = false;
324     ecore_wl2_offer_finish(ev->offer);
325   }
326   mTargetIndex = -1;
327 }
328
329 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
330 {
331   Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
332
333   Dali::DragAndDrop::DragEvent dragEvent;
334   Dali::Vector2                curPosition(ev->x, ev->y);
335
336   for(std::size_t i = 0; i < mDropTargets.size(); i++)
337   {
338     if(ev->win != mDropTargets[i].parentWindowId)
339     {
340       continue;
341     }
342
343     Vector2 position      = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
344     Vector2 size          = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
345     bool    currentInside = IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height);
346
347     // Calculate Drag Enter, Leave, Move Event
348     if(currentInside && !mDropTargets[i].inside)
349     {
350       mDropTargets[i].inside = true;
351       // Call Enter Event
352       dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
353       dragEvent.SetPosition(curPosition);
354       mDropTargets[i].callback(dragEvent);
355       // Accept Offer
356       ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
357     }
358     else if(!currentInside && mDropTargets[i].inside)
359     {
360       mDropTargets[i].inside = false;
361       // Call Leave Event
362       dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
363       dragEvent.SetPosition(curPosition);
364       mDropTargets[i].callback(dragEvent);
365       // Reject Offer
366       ecore_wl2_offer_accept(ev->offer, NULL);
367     }
368     else if(currentInside && mDropTargets[i].inside)
369     {
370       // Call Move Event
371       dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
372       dragEvent.SetPosition(curPosition);
373       mDropTargets[i].callback(dragEvent);
374     }
375   }
376
377   return true;
378 }
379
380 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
381 {
382   Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
383
384   // Check the target object region
385   mTargetIndex = -1;
386
387   for(std::size_t i = 0; i < mDropTargets.size(); i++)
388   {
389     if(ev->win != mDropTargets[i].parentWindowId)
390     {
391       continue;
392     }
393
394     Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
395     Vector2 size     = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
396     // If the drop position is in the target object region, request drop data to the source object
397     if(IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height))
398     {
399       mTargetIndex        = i;
400       mPosition           = position;
401       Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
402
403       char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
404       if(mimetype)
405       {
406         ecore_wl2_offer_receive(ev->offer, mimetype);
407         Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
408         Ecore_Wl2_Input*   input   = ecore_wl2_input_default_input_get(display);
409         ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
410       }
411       return true;
412     }
413   }
414
415   return false;
416 }
417
418 } // namespace Adaptor
419
420 } // namespace Internal
421
422 } // namespace Dali