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