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) 2023 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 namespace
38 {
39 static constexpr int32_t DEFAULT_POSITION            = -1;
40 static constexpr int32_t INVALID_ECORE_WL2_WINDOW_ID = -1;
41 } // namespace
42
43 static bool IsIntersection(int px, int py, int tx, int ty, int tw, int th)
44 {
45   if(px > tx && py > ty && px < (tx + tw) && py < (ty + th))
46   {
47     return true;
48   }
49   return false;
50 }
51
52 static Eina_Bool EcoreEventDataSend(void* data, int type, void* event)
53 {
54   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
55   dndImpl->SendData(event);
56
57   return ECORE_CALLBACK_PASS_ON;
58 }
59
60 static Eina_Bool EcoreEventDataSourceEnd(void* data, int type, void* event)
61 {
62   Ecore_Wl2_Event_Data_Source_End* ev      = reinterpret_cast<Ecore_Wl2_Event_Data_Source_End*>(event);
63   DragAndDropEcoreWl*              dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
64   if(ev->cancelled)
65   {
66     dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::CANCEL);
67   }
68   else
69   {
70     dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::ACCEPT);
71   }
72
73   return ECORE_CALLBACK_PASS_ON;
74 }
75
76 static Eina_Bool EcoreEventDataSourceDrop(void* data, int type, void* event)
77 {
78   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
79   dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::FINISH);
80   return ECORE_CALLBACK_PASS_ON;
81 }
82
83 static Eina_Bool EcoreEventOfferDataReady(void* data, int type, void* event)
84 {
85   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
86   dndImpl->ReceiveData(event);
87
88   return ECORE_CALLBACK_PASS_ON;
89 }
90
91 static Eina_Bool EcoreEventDataMotion(void* data, int type, void* event)
92 {
93   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
94   dndImpl->CalculateDragEvent(event);
95
96   return ECORE_CALLBACK_PASS_ON;
97 }
98
99 static Eina_Bool EcoreEventDataDrop(void* data, int type, void* event)
100 {
101   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
102   dndImpl->CalculateViewRegion(event);
103
104   return ECORE_CALLBACK_PASS_ON;
105 }
106
107 static Eina_Bool EcoreEventDataEnter(void* data, int type, void* event)
108 {
109   Ecore_Wl2_Event_Dnd_Enter* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Enter*>(event);
110
111   // Set default offer is reject
112   ecore_wl2_offer_accept(ev->offer, NULL);
113   return ECORE_CALLBACK_PASS_ON;
114 }
115
116 static Eina_Bool EcoreEventDataLeave(void* data, int type, void* event)
117 {
118   DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
119   dndImpl->ResetDropTargets();
120
121   return ECORE_CALLBACK_PASS_ON;
122 }
123
124 Dali::DragAndDrop GetDragAndDrop()
125 {
126   Dali::DragAndDrop dnd;
127
128   Dali::SingletonService service(SingletonService::Get());
129   if(service)
130   {
131     // Check whether the singleton is already created
132     Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::DragAndDrop));
133     if(handle)
134     {
135       // If so, downcast the handle
136       dnd = Dali::DragAndDrop(dynamic_cast<DragAndDrop*>(handle.GetObjectPtr()));
137     }
138     else
139     {
140       // Create a singleon instance
141       DragAndDropEcoreWl* dndImpl = new DragAndDropEcoreWl();
142
143       dnd = Dali::DragAndDrop(dndImpl);
144       service.Register(typeid(Dali::DragAndDrop), dnd);
145     }
146   }
147
148   return dnd;
149 }
150
151 DragAndDropEcoreWl::DragAndDropEcoreWl()
152 {
153   // Source Events
154   mSendHandler       = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND, EcoreEventDataSend, this);
155   mSourceEndHandler  = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_END, EcoreEventDataSourceEnd, this);
156   mSourceDropHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_DROP, EcoreEventDataSourceDrop, this);
157
158   // Target Events
159   mReceiveHandler = ecore_event_handler_add(ECORE_WL2_EVENT_OFFER_DATA_READY, EcoreEventOfferDataReady, this);
160   mMotionHandler  = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION, EcoreEventDataMotion, this);
161   mDropHandler    = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP, EcoreEventDataDrop, this);
162   mEnterHandler   = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER, EcoreEventDataEnter, this);
163   mLeaveHandler   = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE, EcoreEventDataLeave, this);
164 }
165
166 DragAndDropEcoreWl::~DragAndDropEcoreWl()
167 {
168   // Source Events
169   ecore_event_handler_del(mSendHandler);
170   ecore_event_handler_del(mSourceEndHandler);
171   ecore_event_handler_del(mSourceDropHandler);
172
173   // Target Events
174   ecore_event_handler_del(mReceiveHandler);
175   ecore_event_handler_del(mMotionHandler);
176   ecore_event_handler_del(mDropHandler);
177   ecore_event_handler_del(mEnterHandler);
178 }
179
180 bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback)
181 {
182   // Get Parent Window
183   auto parent = Dali::DevelWindow::Get(source);
184
185   // Set Drag Source Data
186   mMimeType = data.GetMimeType();
187   mData     = data.GetData();
188
189   // Set Source Event
190   mSourceCallback = callback;
191
192   // Set Drag Window
193   mDragWindow = shadowWindow;
194
195   // Start Drag and Drop
196   Ecore_Wl2_Window*  parentWindow = AnyCast<Ecore_Wl2_Window*>(parent.GetNativeHandle());
197   Ecore_Wl2_Window*  dragWindow   = AnyCast<Ecore_Wl2_Window*>(mDragWindow.GetNativeHandle());
198   Ecore_Wl2_Display* display      = ecore_wl2_connected_display_get(NULL);
199   Ecore_Wl2_Input*   input        = ecore_wl2_input_default_input_get(display);
200
201   // Set mime type for drag and drop
202   const char* mimeTypes[2];
203   mimeTypes[0] = mMimeType.c_str();
204   mimeTypes[1] = NULL;
205
206   // Set mime type
207   ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
208
209   // Start wayland drag and drop
210   mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
211
212   // Call Start Event
213   CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
214
215   return true;
216 }
217
218 bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
219 {
220   std::vector<DropTarget>::iterator itr;
221   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
222   {
223     if((*itr).target == target)
224     {
225       return false;
226     }
227   }
228
229   auto window = Dali::DevelWindow::Get(target);
230
231   int parentWindowId = INVALID_ECORE_WL2_WINDOW_ID;
232
233   if(!window)
234   {
235     // Target is stil not scene-on
236     // Add dummy target data, and wait until target is on scene.
237     target.OnSceneSignal().Connect(this, &DragAndDropEcoreWl::DropTargetSceneOn);
238   }
239   else
240   {
241     Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
242     if(parentWindow == nullptr)
243     {
244       return false;
245     }
246     parentWindowId = ecore_wl2_window_id_get(parentWindow);
247   }
248
249   DropTarget targetData;
250   targetData.target         = target;
251   targetData.callback       = callback;
252   targetData.inside         = false;
253   targetData.parentWindowId = parentWindowId;
254
255   mDropTargets.push_back(targetData);
256
257   return true;
258 }
259
260 bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
261 {
262   std::vector<DropTarget>::iterator itr;
263   for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
264   {
265     if((*itr).target == target)
266     {
267       mDropTargets.erase(itr);
268       break;
269     }
270   }
271
272   return true;
273 }
274
275 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
276 {
277   if(mSourceCallback)
278   {
279     mSourceCallback(type);
280   }
281 }
282
283 void DragAndDropEcoreWl::ResetDropTargets()
284 {
285   for(std::size_t i = 0; i < mDropTargets.size(); i++)
286   {
287     if(mDropTargets[i].inside)
288     {
289       Dali::DragAndDrop::DragEvent dragEvent;
290       dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
291       Dali::Vector2 position(DEFAULT_POSITION, DEFAULT_POSITION);
292       dragEvent.SetPosition(position);
293       mDropTargets[i].callback(dragEvent);
294     }
295     mDropTargets[i].inside = false;
296   }
297 }
298
299 void DragAndDropEcoreWl::SendData(void* event)
300 {
301   Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
302   if(ev->serial != mSerial)
303   {
304     return;
305   }
306
307   int dataLength = strlen(mData.c_str());
308   int bufferSize = dataLength;
309   if((mMimeType.find("text") != std::string::npos) ||
310      (mMimeType.find("markup") != std::string::npos) ||
311      (mMimeType.find("image") != std::string::npos))
312   {
313     bufferSize += 1;
314   }
315
316   char* buffer = new char[bufferSize];
317   if(!buffer)
318   {
319     return;
320   }
321
322   memcpy(buffer, mData.c_str(), dataLength);
323   buffer[dataLength] = '\0';
324
325   auto ret = write(ev->fd, buffer, bufferSize);
326   if(DALI_UNLIKELY(ret != bufferSize))
327   {
328     DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
329   }
330
331   close(ev->fd);
332
333   if(mDragWindow)
334   {
335     mDragWindow.Hide();
336   }
337
338   delete[] buffer;
339 }
340
341 void DragAndDropEcoreWl::ReceiveData(void* event)
342 {
343   Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
344
345   if(mTargetIndex != -1)
346   {
347     Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->mimetype, ev->data);
348     mDropTargets[mTargetIndex].callback(dragEvent);
349     mDropTargets[mTargetIndex].inside = false;
350     ecore_wl2_offer_finish(ev->offer);
351   }
352   mTargetIndex = -1;
353 }
354
355 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
356 {
357   Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
358
359   Dali::DragAndDrop::DragEvent dragEvent;
360   Dali::Vector2                curPosition(ev->x, ev->y);
361
362   for(std::size_t i = 0; i < mDropTargets.size(); i++)
363   {
364     if(ev->win != mDropTargets[i].parentWindowId)
365     {
366       continue;
367     }
368
369     Vector2 position      = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
370     Vector2 size          = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
371     bool    currentInside = IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height);
372
373     // Calculate Drag Enter, Leave, Move Event
374     if(currentInside && !mDropTargets[i].inside)
375     {
376       mDropTargets[i].inside = true;
377       // Call Enter Event
378       dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
379       dragEvent.SetPosition(curPosition);
380       mDropTargets[i].callback(dragEvent);
381       // Accept Offer
382       ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
383     }
384     else if(!currentInside && mDropTargets[i].inside)
385     {
386       mDropTargets[i].inside = false;
387       // Call Leave Event
388       dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
389       dragEvent.SetPosition(curPosition);
390       mDropTargets[i].callback(dragEvent);
391       // Reject Offer
392       ecore_wl2_offer_accept(ev->offer, NULL);
393     }
394     else if(currentInside && mDropTargets[i].inside)
395     {
396       // Call Move Event
397       dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
398       dragEvent.SetPosition(curPosition);
399       mDropTargets[i].callback(dragEvent);
400     }
401   }
402
403   return true;
404 }
405
406 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
407 {
408   Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
409
410   // Check the target object region
411   mTargetIndex = -1;
412
413   for(std::size_t i = 0; i < mDropTargets.size(); i++)
414   {
415     if(ev->win != mDropTargets[i].parentWindowId)
416     {
417       continue;
418     }
419
420     Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
421     Vector2 size     = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
422     // If the drop position is in the target object region, request drop data to the source object
423     if(IsIntersection(ev->x, ev->y, position.x, position.y, size.width, size.height))
424     {
425       mTargetIndex        = i;
426       mPosition           = position;
427       Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
428
429       char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
430       if(mimetype)
431       {
432         ecore_wl2_offer_receive(ev->offer, mimetype);
433         Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
434         Ecore_Wl2_Input*   input   = ecore_wl2_input_default_input_get(display);
435         ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
436       }
437       return true;
438     }
439   }
440
441   return false;
442 }
443
444 void DragAndDropEcoreWl::DropTargetSceneOn(Dali::Actor target)
445 {
446   // Disconnect scene on signal
447   target.OnSceneSignal().Disconnect(this, &DragAndDropEcoreWl::DropTargetSceneOn);
448
449   for(auto iter = mDropTargets.begin(), iterEnd = mDropTargets.end(); iter != iterEnd; iter++)
450   {
451     if((*iter).target == target)
452     {
453       auto window = Dali::DevelWindow::Get(target);
454
455       Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
456       if(parentWindow == nullptr)
457       {
458         return;
459       }
460
461       (*iter).parentWindowId = ecore_wl2_window_id_get(parentWindow);
462       break;
463     }
464   }
465 }
466
467 } // namespace Adaptor
468
469 } // namespace Internal
470
471 } // namespace Dali