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>
24 #include <dali/internal/adaptor/tizen-wayland/dali-ecore-wl2.h>
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
29 ///////////////////////////////////////////////////////////////////////////////////////////////////
39 static constexpr int32_t DEFAULT_POSITION = -1;
40 static constexpr int32_t INVALID_ECORE_WL2_WINDOW_ID = -1;
43 static bool IsIntersection(int px, int py, int tx, int ty, int tw, int th)
45 if(px > tx && py > ty && px < (tx + tw) && py < (ty + th))
52 static Eina_Bool EcoreEventDataSend(void* data, int type, void* event)
54 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
55 dndImpl->SendData(event);
57 return ECORE_CALLBACK_PASS_ON;
60 static Eina_Bool EcoreEventDataSourceEnd(void* data, int type, void* event)
62 Ecore_Wl2_Event_Data_Source_End* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_End*>(event);
63 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
66 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::CANCEL);
70 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::ACCEPT);
73 return ECORE_CALLBACK_PASS_ON;
76 static Eina_Bool EcoreEventDataSourceDrop(void* data, int type, void* event)
78 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
79 dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::FINISH);
80 return ECORE_CALLBACK_PASS_ON;
83 static Eina_Bool EcoreEventOfferDataReady(void* data, int type, void* event)
85 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
86 dndImpl->ReceiveData(event);
88 return ECORE_CALLBACK_PASS_ON;
91 static Eina_Bool EcoreEventDataMotion(void* data, int type, void* event)
93 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
94 dndImpl->CalculateDragEvent(event);
96 return ECORE_CALLBACK_PASS_ON;
99 static Eina_Bool EcoreEventDataDrop(void* data, int type, void* event)
101 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
102 dndImpl->CalculateViewRegion(event);
104 return ECORE_CALLBACK_PASS_ON;
107 static Eina_Bool EcoreEventDataEnter(void* data, int type, void* event)
109 Ecore_Wl2_Event_Dnd_Enter* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Enter*>(event);
111 // Set default offer is reject
112 ecore_wl2_offer_accept(ev->offer, NULL);
113 return ECORE_CALLBACK_PASS_ON;
116 static Eina_Bool EcoreEventDataLeave(void* data, int type, void* event)
118 DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
119 dndImpl->ResetDropTargets();
121 return ECORE_CALLBACK_PASS_ON;
124 Dali::DragAndDrop GetDragAndDrop()
126 Dali::DragAndDrop dnd;
128 Dali::SingletonService service(SingletonService::Get());
131 // Check whether the singleton is already created
132 Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::DragAndDrop));
135 // If so, downcast the handle
136 dnd = Dali::DragAndDrop(dynamic_cast<DragAndDrop*>(handle.GetObjectPtr()));
140 // Create a singleon instance
141 DragAndDropEcoreWl* dndImpl = new DragAndDropEcoreWl();
143 dnd = Dali::DragAndDrop(dndImpl);
144 service.Register(typeid(Dali::DragAndDrop), dnd);
151 DragAndDropEcoreWl::DragAndDropEcoreWl()
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);
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);
166 DragAndDropEcoreWl::~DragAndDropEcoreWl()
169 ecore_event_handler_del(mSendHandler);
170 ecore_event_handler_del(mSourceEndHandler);
171 ecore_event_handler_del(mSourceDropHandler);
174 ecore_event_handler_del(mReceiveHandler);
175 ecore_event_handler_del(mMotionHandler);
176 ecore_event_handler_del(mDropHandler);
177 ecore_event_handler_del(mEnterHandler);
180 bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback)
183 auto parent = Dali::DevelWindow::Get(source);
185 // Set Drag Source Data
186 mMimeType = data.GetMimeType();
187 mData = data.GetData();
190 mSourceCallback = callback;
193 mDragWindow = shadowWindow;
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);
201 // Set mime type for drag and drop
202 const char* mimeTypes[2];
203 mimeTypes[0] = mMimeType.c_str();
207 ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
209 // Start wayland drag and drop
210 mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
213 CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
218 bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
220 std::vector<DropTarget>::iterator itr;
221 for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
223 if((*itr).target == target)
229 auto window = Dali::DevelWindow::Get(target);
231 int parentWindowId = INVALID_ECORE_WL2_WINDOW_ID;
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);
241 Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
242 if(parentWindow == nullptr)
246 parentWindowId = ecore_wl2_window_id_get(parentWindow);
249 DropTarget targetData;
250 targetData.target = target;
251 targetData.callback = callback;
252 targetData.inside = false;
253 targetData.parentWindowId = parentWindowId;
255 mDropTargets.push_back(targetData);
260 bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
262 std::vector<DropTarget>::iterator itr;
263 for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
265 if((*itr).target == target)
267 mDropTargets.erase(itr);
275 void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
279 mSourceCallback(type);
283 void DragAndDropEcoreWl::ResetDropTargets()
285 for(std::size_t i = 0; i < mDropTargets.size(); i++)
287 if(mDropTargets[i].inside)
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);
295 mDropTargets[i].inside = false;
299 void DragAndDropEcoreWl::SendData(void* event)
301 Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
302 if(ev->serial != mSerial)
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))
316 char* buffer = new char[bufferSize];
322 memcpy(buffer, mData.c_str(), dataLength);
323 buffer[dataLength] = '\0';
325 auto ret = write(ev->fd, buffer, bufferSize);
326 if(DALI_UNLIKELY(ret != bufferSize))
328 DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
341 void DragAndDropEcoreWl::ReceiveData(void* event)
343 Ecore_Wl2_Event_Offer_Data_Ready* ev = reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
345 if(mTargetIndex != -1)
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);
355 bool DragAndDropEcoreWl::CalculateDragEvent(void* event)
357 Ecore_Wl2_Event_Dnd_Motion* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Motion*>(event);
359 Dali::DragAndDrop::DragEvent dragEvent;
360 Dali::Vector2 curPosition(ev->x, ev->y);
362 for(std::size_t i = 0; i < mDropTargets.size(); i++)
364 if(ev->win != mDropTargets[i].parentWindowId)
369 Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::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);
373 // Calculate Drag Enter, Leave, Move Event
374 if(currentInside && !mDropTargets[i].inside)
376 mDropTargets[i].inside = true;
378 dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
379 dragEvent.SetPosition(curPosition);
380 mDropTargets[i].callback(dragEvent);
382 ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
384 else if(!currentInside && mDropTargets[i].inside)
386 mDropTargets[i].inside = false;
388 dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
389 dragEvent.SetPosition(curPosition);
390 mDropTargets[i].callback(dragEvent);
392 ecore_wl2_offer_accept(ev->offer, NULL);
394 else if(currentInside && mDropTargets[i].inside)
397 dragEvent.SetAction(Dali::DragAndDrop::DragType::MOVE);
398 dragEvent.SetPosition(curPosition);
399 mDropTargets[i].callback(dragEvent);
406 bool DragAndDropEcoreWl::CalculateViewRegion(void* event)
408 Ecore_Wl2_Event_Dnd_Drop* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Drop*>(event);
410 // Check the target object region
413 for(std::size_t i = 0; i < mDropTargets.size(); i++)
415 if(ev->win != mDropTargets[i].parentWindowId)
420 Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::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))
426 mPosition = position;
427 Dali::Window window = Dali::DevelWindow::Get(mDropTargets[i].target);
429 char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
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));
444 void DragAndDropEcoreWl::DropTargetSceneOn(Dali::Actor target)
446 // Disconnect scene on signal
447 target.OnSceneSignal().Disconnect(this, &DragAndDropEcoreWl::DropTargetSceneOn);
449 for(auto iter = mDropTargets.begin(), iterEnd = mDropTargets.end(); iter != iterEnd; iter++)
451 if((*iter).target == target)
453 auto window = Dali::DevelWindow::Get(target);
455 Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(window.GetNativeHandle());
456 if(parentWindow == nullptr)
461 (*iter).parentWindowId = ecore_wl2_window_id_get(parentWindow);
467 } // namespace Adaptor
469 } // namespace Internal