2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/drag-and-drop/tizen-wayland/drag-and-drop-impl-ecore-wl2.h>
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/integration-api/debug.h>
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>
31 ///////////////////////////////////////////////////////////////////////////////////////////////////
33 ///////////////////////////////////////////////////////////////////////////////////////////////////
43 static constexpr int32_t DEFAULT_POSITION = -1;
44 static constexpr int32_t INVALID_ECORE_WL2_WINDOW_ID = -1;
47 static bool IsIntersection(int px, int py, int tx, int ty, int tw, int th)
49 if(px > tx && py > ty && px < (tx + tw) && py < (ty + th))
56 static Eina_Bool EcoreEventDataSend(void* data, int type, void* event)
58 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
59 dndImpl->SendData(event);
61 return ECORE_CALLBACK_PASS_ON;
64 static Eina_Bool EcoreEventDataSourceEnd(void* data, int type, void* event)
66 Ecore_Wl2_Event_Data_Source_End* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_End*>(event);
67 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
70 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::CANCEL);
74 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::ACCEPT);
77 return ECORE_CALLBACK_PASS_ON;
80 static Eina_Bool EcoreEventDataSourceDrop(void* data, int type, void* event)
82 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
83 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::FINISH);
84 return ECORE_CALLBACK_PASS_ON;
87 static Eina_Bool EcoreEventOfferDataReady(void* data, int type, void* event)
89 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
90 dndImpl->ReceiveData(event);
92 return ECORE_CALLBACK_PASS_ON;
95 static Eina_Bool EcoreEventDataMotion(void* data, int type, void* event)
97 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
98 dndImpl->CalculateDragEvent(event);
100 return ECORE_CALLBACK_PASS_ON;
103 static Eina_Bool EcoreEventDataDrop(void* data, int type, void* event)
105 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
106 dndImpl->CalculateViewRegion(event);
108 return ECORE_CALLBACK_PASS_ON;
111 static Eina_Bool EcoreEventDataEnter(void* data, int type, void* event)
113 Ecore_Wl2_Event_Dnd_Enter* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Enter*>(event);
115 // Set default offer is reject
116 ecore_wl2_offer_accept(ev->offer, NULL);
117 return ECORE_CALLBACK_PASS_ON;
120 static Eina_Bool EcoreEventDataLeave(void* data, int type, void* event)
122 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
123 dndImpl->ResetDropTargets();
125 return ECORE_CALLBACK_PASS_ON;
128 Dali::DragAndDrop GetDragAndDrop()
130 Dali::DragAndDrop dnd;
132 Dali::SingletonService service(SingletonService::Get());
135 // Check whether the singleton is already created
136 Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::DragAndDrop));
139 // If so, downcast the handle
140 dnd = Dali::DragAndDrop(dynamic_cast<DragAndDrop*>(handle.GetObjectPtr()));
144 // Create a singleon instance
145 DragAndDropEcoreWl* dndImpl = new DragAndDropEcoreWl();
147 dnd = Dali::DragAndDrop(dndImpl);
148 service.Register(typeid(Dali::DragAndDrop), dnd);
155 DragAndDropEcoreWl::DragAndDropEcoreWl()
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);
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);
170 DragAndDropEcoreWl::~DragAndDropEcoreWl()
173 ecore_event_handler_del(mSendHandler);
174 ecore_event_handler_del(mSourceEndHandler);
175 ecore_event_handler_del(mSourceDropHandler);
178 ecore_event_handler_del(mReceiveHandler);
179 ecore_event_handler_del(mMotionHandler);
180 ecore_event_handler_del(mDropHandler);
181 ecore_event_handler_del(mEnterHandler);
184 bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback)
187 auto parent = Dali::DevelWindow::Get(source);
189 // Set Drag Source Data
190 mMimeType = data.GetMimeType();
191 mData = data.GetData();
194 mSourceCallback = callback;
197 mDragWindow = shadowWindow;
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);
205 // Set mime type for drag and drop
206 const char* mimeTypes[2];
207 mimeTypes[0] = mMimeType.c_str();
211 ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
213 // Start wayland drag and drop
214 mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
217 CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
222 bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
224 std::vector<DropTarget>::iterator itr;
225 for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
227 if((*itr).target == target)
233 auto window = Dali::DevelWindow::Get(target);
235 int parentWindowId = INVALID_ECORE_WL2_WINDOW_ID;
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);
245 Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
246 if(parentWindow == nullptr)
250 parentWindowId = ecore_wl2_window_id_get(parentWindow);
253 DropTarget targetData;
254 targetData.target = target;
255 targetData.callback = callback;
256 targetData.inside = false;
257 targetData.parentWindowId = parentWindowId;
259 mDropTargets.push_back(targetData);
264 bool DragAndDropEcoreWl::AddListener(Dali::Window target, Dali::DragAndDrop::DragAndDropFunction callback)
266 std::vector<DropWindowTarget>::iterator itr;
267 for(itr = mDropWindowTargets.begin(); itr < mDropWindowTargets.end(); itr++)
269 if((*itr).target == target)
275 int windowId = INVALID_ECORE_WL2_WINDOW_ID;
277 Ecore_Wl2_Window* window = AnyCast<Ecore_Wl2_Window*>(target.GetNativeHandle());
278 if(window == nullptr)
282 windowId = ecore_wl2_window_id_get(window);
284 DropWindowTarget targetData;
285 targetData.target = target;
286 targetData.callback = callback;
287 targetData.inside = false;
288 targetData.windowId = windowId;
290 mDropWindowTargets.push_back(targetData);
295 bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
297 std::vector<DropTarget>::iterator itr;
298 for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
300 if((*itr).target == target)
302 mDropTargets.erase(itr);
310 bool DragAndDropEcoreWl::RemoveListener(Dali::Window target)
312 std::vector<DropWindowTarget>::iterator itr;
313 for(itr = mDropWindowTargets.begin(); itr < mDropWindowTargets.end(); itr++)
315 if((*itr).target == target)
317 mDropWindowTargets.erase(itr);
325 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
329 mSourceCallback(type);
330 if(type != Dali::DragAndDrop::SourceEventType::START)
337 void DragAndDropEcoreWl::ResetDropTargets()
339 for(std::size_t i = 0; i < mDropTargets.size(); i++)
341 if(mDropTargets[i].inside)
343 Dali::DragAndDrop::DragEvent dragEvent;
344 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
345 Dali::Vector2 position(DEFAULT_POSITION, DEFAULT_POSITION);
346 dragEvent.SetPosition(position);
347 mDropTargets[i].callback(dragEvent);
349 mDropTargets[i].inside = false;
352 for(std::size_t i = 0; i < mDropWindowTargets.size(); i++)
354 if(mDropWindowTargets[i].inside)
356 Dali::DragAndDrop::DragEvent dragEvent;
357 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
358 Dali::Vector2 position(DEFAULT_POSITION, DEFAULT_POSITION);
359 dragEvent.SetPosition(position);
360 mDropWindowTargets[i].callback(dragEvent);
362 mDropWindowTargets[i].inside = false;
366 void DragAndDropEcoreWl::SendData(void* event)
368 Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
369 if(ev->serial != mSerial)
374 int dataLength = strlen(mData.c_str());
375 int bufferSize = dataLength;
376 if((mMimeType.find("text") != std::string::npos) ||
377 (mMimeType.find("markup") != std::string::npos) ||
378 (mMimeType.find("image") != std::string::npos))
383 char* buffer = new char[bufferSize];
389 memcpy(buffer, mData.c_str(), dataLength);
390 buffer[dataLength] = '\0';
392 auto ret = write(ev->fd, buffer, bufferSize);
393 if(DALI_UNLIKELY(ret != bufferSize))
395 DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
408 void DragAndDropEcoreWl::ReceiveData(void* event)
410 Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
412 if(mTargetIndex != -1)
414 Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->mimetype, ev->data);
415 mDropTargets[mTargetIndex].callback(dragEvent);
416 mDropTargets[mTargetIndex].inside = false;
420 if(mWindowTargetIndex != -1)
422 Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mWindowPosition, ev->mimetype, ev->data);
423 mDropWindowTargets[mWindowTargetIndex].callback(dragEvent);
424 mDropWindowTargets[mWindowTargetIndex].inside = false;
426 mWindowTargetIndex = -1;
430 Vector2 DragAndDropEcoreWl::RecalculatePositionByOrientation(int x, int y, Dali::Window window)
432 int screenWidth, screenHeight;
433 Internal::Adaptor::WindowSystem::GetScreenSize(screenWidth, screenHeight);
434 int angle = DevelWindow::GetPhysicalOrientation(window);
437 Dali::Vector2 newPosition;
441 newX = screenHeight - y;
444 else if(angle == 180)
446 newX = screenWidth - x;
447 newY = screenHeight - y;
449 else if(angle == 270)
452 newY = screenWidth - x;
460 newPosition.x = newX;
461 newPosition.y = newY;
466 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
468 Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
470 Dali::DragAndDrop::DragEvent dragEvent;
471 Dali::Vector2 curPosition(ev->x, ev->y);
473 for(std::size_t i = 0; i < mDropTargets.size(); i++)
475 if(ev->win != mDropTargets[i].parentWindowId)
480 Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
481 Vector2 size = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
483 // Recalculate Cursor by Orientation
484 Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
485 Dali::Vector2 cursor = RecalculatePositionByOrientation(ev->x, ev->y, window);
487 bool currentInside = IsIntersection(cursor.x, cursor.y, position.x, position.y, size.width, size.height);
489 // Calculate Drag Enter, Leave, Move Event
490 if(currentInside && !mDropTargets[i].inside)
492 mDropTargets[i].inside = true;
494 dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
495 dragEvent.SetPosition(curPosition);
496 mDropTargets[i].callback(dragEvent);
498 ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
500 else if(!currentInside && mDropTargets[i].inside)
502 mDropTargets[i].inside = false;
504 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
505 dragEvent.SetPosition(curPosition);
506 mDropTargets[i].callback(dragEvent);
508 ecore_wl2_offer_accept(ev->offer, NULL);
510 else if(currentInside && mDropTargets[i].inside)
513 dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
514 dragEvent.SetPosition(curPosition);
515 mDropTargets[i].callback(dragEvent);
519 for(std::size_t i = 0; i < mDropWindowTargets.size(); i++)
521 if(ev->win != mDropWindowTargets[i].windowId)
526 // Recalculate Cursor by Orientation
527 Dali::Window window = mDropWindowTargets[i].target;
528 Dali::Window::WindowPosition position = window.GetPosition();
529 Dali::Window::WindowSize size = window.GetSize();
531 bool currentInside = IsIntersection(ev->x + position.GetX(), ev->y + position.GetY(), position.GetX(), position.GetY(), size.GetWidth(), size.GetHeight());
533 // Calculate Drag Enter, Leave, Move Event
534 if(currentInside && !mDropWindowTargets[i].inside)
536 mDropWindowTargets[i].inside = true;
538 dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
539 dragEvent.SetPosition(curPosition);
540 mDropWindowTargets[i].callback(dragEvent);
542 ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
544 else if(!currentInside && mDropWindowTargets[i].inside)
546 mDropWindowTargets[i].inside = false;
548 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
549 dragEvent.SetPosition(curPosition);
550 mDropWindowTargets[i].callback(dragEvent);
552 ecore_wl2_offer_accept(ev->offer, NULL);
554 else if(currentInside && mDropWindowTargets[i].inside)
557 dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
558 dragEvent.SetPosition(curPosition);
559 mDropWindowTargets[i].callback(dragEvent);
566 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
568 Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
570 // Check the target object region
572 mWindowTargetIndex = -1;
574 for(std::size_t i = 0; i < mDropTargets.size(); i++)
576 if(ev->win != mDropTargets[i].parentWindowId)
581 Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
582 Vector2 size = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
584 // Recalculate Cursor by Orientation
585 Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
586 Dali::Vector2 cursor = RecalculatePositionByOrientation(ev->x, ev->y, window);
588 // If the drop position is in the target object region, request drop data to the source object
589 if(IsIntersection(cursor.x, cursor.y, position.x, position.y, size.width, size.height))
592 mPosition = position;
593 Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
595 char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
598 ecore_wl2_offer_receive(ev->offer, mimetype);
599 Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
600 Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display);
601 ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
607 for(std::size_t i = 0; i < mDropWindowTargets.size(); i++)
609 if(ev->win != mDropWindowTargets[i].windowId)
614 // Recalculate Cursor by Orientation
615 Dali::Window window = mDropWindowTargets[i].target;
616 Dali::Window::WindowPosition position = window.GetPosition();
617 Dali::Window::WindowSize size = window.GetSize();
619 // If the drop position is in the target object region, request drop data to the source object
620 if(IsIntersection(ev->x + position.GetX(), ev->y + position.GetY(), position.GetX(), position.GetY(), size.GetWidth(), size.GetHeight()))
622 mWindowTargetIndex = i;
623 mWindowPosition = Dali::Vector2(position.GetX(), position.GetY());
625 char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
628 ecore_wl2_offer_receive(ev->offer, mimetype);
629 Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
630 Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display);
631 ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
640 void DragAndDropEcoreWl::DropTargetSceneOn(Dali::Actor target)
642 // Disconnect scene on signal
643 target.OnSceneSignal().Disconnect(this, &DragAndDropEcoreWl::DropTargetSceneOn);
645 for(auto iter = mDropTargets.begin(), iterEnd = mDropTargets.end(); iter != iterEnd; iter++)
647 if((*iter).target == target)
649 auto window = Dali::DevelWindow::Get(target);
651 Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
652 if(parentWindow == nullptr)
657 (*iter).parentWindowId = ecore_wl2_window_id_get(parentWindow);
663 } // namespace Adaptor
665 } // namespace Internal