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::RemoveListener(Dali::Actor target)
266 std::vector<DropTarget>::iterator itr;
267 for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
269 if((*itr).target == target)
271 mDropTargets.erase(itr);
279 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
283 mSourceCallback(type);
284 if(type != Dali::DragAndDrop::SourceEventType::START)
291 void DragAndDropEcoreWl::ResetDropTargets()
293 for(std::size_t i = 0; i < mDropTargets.size(); i++)
295 if(mDropTargets[i].inside)
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);
303 mDropTargets[i].inside = false;
307 void DragAndDropEcoreWl::SendData(void* event)
309 Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
310 if(ev->serial != mSerial)
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))
324 char* buffer = new char[bufferSize];
330 memcpy(buffer, mData.c_str(), dataLength);
331 buffer[dataLength] = '\0';
333 auto ret = write(ev->fd, buffer, bufferSize);
334 if(DALI_UNLIKELY(ret != bufferSize))
336 DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
349 void DragAndDropEcoreWl::ReceiveData(void* event)
351 Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
353 if(mTargetIndex != -1)
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);
363 Vector2 DragAndDropEcoreWl::RecalculatePositionByOrientation(int x, int y, Dali::Window window)
365 int screenWidth, screenHeight;
366 Internal::Adaptor::WindowSystem::GetScreenSize(screenWidth, screenHeight);
367 int angle = DevelWindow::GetPhysicalOrientation(window);
370 Dali::Vector2 newPosition;
374 newX = screenHeight - y;
377 else if(angle == 180)
379 newX = screenWidth - x;
380 newY = screenHeight - y;
382 else if(angle == 270)
385 newY = screenWidth - x;
393 newPosition.x = newX;
394 newPosition.y = newY;
399 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
401 Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
403 Dali::DragAndDrop::DragEvent dragEvent;
404 Dali::Vector2 curPosition(ev->x, ev->y);
406 for(std::size_t i = 0; i < mDropTargets.size(); i++)
408 if(ev->win != mDropTargets[i].parentWindowId)
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);
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);
420 bool currentInside = IsIntersection(cursor.x, cursor.y, position.x, position.y, size.width, size.height);
422 // Calculate Drag Enter, Leave, Move Event
423 if(currentInside && !mDropTargets[i].inside)
425 mDropTargets[i].inside = true;
427 dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
428 dragEvent.SetPosition(curPosition);
429 mDropTargets[i].callback(dragEvent);
431 ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
433 else if(!currentInside && mDropTargets[i].inside)
435 mDropTargets[i].inside = false;
437 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
438 dragEvent.SetPosition(curPosition);
439 mDropTargets[i].callback(dragEvent);
441 ecore_wl2_offer_accept(ev->offer, NULL);
443 else if(currentInside && mDropTargets[i].inside)
446 dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
447 dragEvent.SetPosition(curPosition);
448 mDropTargets[i].callback(dragEvent);
455 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
457 Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
459 // Check the target object region
462 for(std::size_t i = 0; i < mDropTargets.size(); i++)
464 if(ev->win != mDropTargets[i].parentWindowId)
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);
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);
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))
480 mPosition = position;
481 Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
483 char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
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));
498 void DragAndDropEcoreWl::DropTargetSceneOn(Dali::Actor target)
500 // Disconnect scene on signal
501 target.OnSceneSignal().Disconnect(this, &DragAndDropEcoreWl::DropTargetSceneOn);
503 for(auto iter = mDropTargets.begin(), iterEnd = mDropTargets.end(); iter != iterEnd; iter++)
505 if((*iter).target == target)
507 auto window = Dali::DevelWindow::Get(target);
509 Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
510 if(parentWindow == nullptr)
515 (*iter).parentWindowId = ecore_wl2_window_id_get(parentWindow);
521 } // namespace Adaptor
523 } // namespace Internal