Skip to content

Commit

Permalink
Improve clipboard without cbhm
Browse files Browse the repository at this point in the history
Starting from Tizen 8.0, the cbhm package is not distributed.
Therefore, support the platform's clipboard feature using the ecore_wl2 APIs.
ecore_wl2_offer_receive() and ecore_wl2_offer_mimes_get() can be found in Tizen 5.5 rootstrap,
but are not distributed in the actual ecore_wl2 header.
Therefore, this feature is supported starting from tizen 6.5.
This feature supports TV profiles of Tizen 6.5 or higher.
However, in Tizen 6.0 and below, copy&paste is supported only for data within the application.
  • Loading branch information
JSUYA committed Apr 30, 2024
1 parent 3a43fb5 commit c78aeb9
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 16 deletions.
6 changes: 5 additions & 1 deletion flutter/shell/platform/tizen/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ template("embedder") {
if (api_version == "6.5" && target_name != "flutter_tizen_wearable") {
sources += [
"flutter_tizen_nui.cc",
"tizen_clipboard.cc",
"tizen_view_nui.cc",
]

Expand All @@ -183,7 +184,10 @@ template("embedder") {
"dali2-core",
]

defines += [ "NUI_SUPPORT" ]
defines += [
"NUI_SUPPORT",
"CLIPBOARD_SUPPORT",
]
}

configs += [
Expand Down
49 changes: 39 additions & 10 deletions flutter/shell/platform/tizen/channels/platform_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ PlatformChannel::PlatformChannel(BinaryMessenger* messenger,
kChannelName,
&JsonMethodCodec::GetInstance())),
view_(view) {
#ifdef CLIPBOARD_SUPPORT
tizen_clipboard_ = std::make_unique<TizenClipboard>(view);
#endif

channel_->SetMethodCallHandler(
[this](const MethodCall<rapidjson::Document>& call,
std::unique_ptr<MethodResult<rapidjson::Document>> result) {
Expand Down Expand Up @@ -117,15 +121,27 @@ void PlatformChannel::HandleMethodCall(
"Clipboard API only supports text.");
return;
}
GetClipboardData([result = result.release()](const std::string& data) {
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
document.AddMember(rapidjson::Value(kTextKey, allocator),
rapidjson::Value(data, allocator), allocator);
result->Success(document);
delete result;
});
auto* result_ptr = result.release();
if (!GetClipboardData([result_ptr](std::optional<std::string> data) {
if (!data.has_value()) {
result_ptr->Error(kUnknownClipboardError, "Internal error.");
delete result_ptr;
return;
}

rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator =
document.GetAllocator();
document.AddMember(rapidjson::Value(kTextKey, allocator),
rapidjson::Value(data.value(), allocator),
allocator);
result_ptr->Success(document);
delete result_ptr;
})) {
result_ptr->Error(kUnknownClipboardError, "Internal error.");
delete result_ptr;
};
} else if (method == kSetClipboardDataMethod) {
const rapidjson::Value& document = *arguments;
auto iter = document.FindMember(kTextKey);
Expand Down Expand Up @@ -219,16 +235,29 @@ void PlatformChannel::HapticFeedbackVibrate(const std::string& feedback_type) {
FeedbackManager::GetInstance().Vibrate();
}

void PlatformChannel::GetClipboardData(ClipboardCallback on_data) {
bool PlatformChannel::GetClipboardData(ClipboardCallback on_data) {
#ifdef CLIPBOARD_SUPPORT
return tizen_clipboard_->GetData(std::move(on_data));
#else
on_data(clipboard_);
return true;
#endif
}

void PlatformChannel::SetClipboardData(const std::string& data) {
#ifdef CLIPBOARD_SUPPORT
tizen_clipboard_->SetData(data);
#else
clipboard_ = data;
#endif
}

bool PlatformChannel::ClipboardHasStrings() {
#ifdef CLIPBOARD_SUPPORT
return tizen_clipboard_->HasStrings();
#else
return !clipboard_.empty();
#endif
}

void PlatformChannel::RestoreSystemUiOverlays() {
Expand Down
16 changes: 11 additions & 5 deletions flutter/shell/platform/tizen/channels/platform_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@

#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h"
#ifdef CLIPBOARD_SUPPORT
#include "flutter/shell/platform/tizen/tizen_clipboard.h"
#endif
#include "flutter/shell/platform/tizen/tizen_view_base.h"
#include "rapidjson/document.h"

Expand All @@ -23,7 +27,8 @@ class PlatformChannel {
virtual ~PlatformChannel();

private:
using ClipboardCallback = std::function<void(const std::string& data)>;
using ClipboardCallback =
std::function<void(std::optional<std::string> data)>;

void HandleMethodCall(
const MethodCall<rapidjson::Document>& call,
Expand All @@ -32,7 +37,7 @@ class PlatformChannel {
void SystemNavigatorPop();
void PlaySystemSound(const std::string& sound_type);
void HapticFeedbackVibrate(const std::string& feedback_type);
void GetClipboardData(ClipboardCallback on_data);
bool GetClipboardData(ClipboardCallback on_data);
void SetClipboardData(const std::string& data);
bool ClipboardHasStrings();
void RestoreSystemUiOverlays();
Expand All @@ -48,11 +53,12 @@ class PlatformChannel {

// A reference to the native view managed by FlutterTizenView.
TizenViewBase* view_ = nullptr;

#ifdef CLIPBOARD_SUPPORT
std::unique_ptr<TizenClipboard> tizen_clipboard_;
#else
// A container that holds clipboard data during the engine lifetime.
//
// TODO(JSUYA): Remove after implementing the ecore_wl2 based clipboard.
std::string clipboard_;
#endif
};

} // namespace flutter
Expand Down
160 changes: 160 additions & 0 deletions flutter/shell/platform/tizen/tizen_clipboard.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "tizen_clipboard.h"

#include "flutter/shell/platform/tizen/logger.h"
#include "flutter/shell/platform/tizen/tizen_window.h"
#include "flutter/shell/platform/tizen/tizen_window_ecore_wl2.h"

namespace flutter {

namespace {

constexpr char kMimeTypeTextPlain[] = "text/plain;charset=utf-8";

} // namespace

TizenClipboard::TizenClipboard(TizenViewBase* view) {
if (auto* window = dynamic_cast<TizenWindowEcoreWl2*>(view)) {
auto* ecore_wl2_window =
static_cast<Ecore_Wl2_Window*>(window->GetNativeHandle());
display_ = ecore_wl2_window_display_get(ecore_wl2_window);
} else {
display_ = ecore_wl2_connected_display_get(NULL);
}

send_handler = ecore_event_handler_add(
ECORE_WL2_EVENT_DATA_SOURCE_SEND,
[](void* data, int type, void* event) -> Eina_Bool {
auto* self = reinterpret_cast<TizenClipboard*>(data);
self->SendData(event);
return ECORE_CALLBACK_PASS_ON;
},
this);
receive_handler = ecore_event_handler_add(
ECORE_WL2_EVENT_OFFER_DATA_READY,
[](void* data, int type, void* event) -> Eina_Bool {
auto* self = reinterpret_cast<TizenClipboard*>(data);
self->ReceiveData(event);
return ECORE_CALLBACK_PASS_ON;
},
this);
}

TizenClipboard::~TizenClipboard() {
ecore_event_handler_del(send_handler);
ecore_event_handler_del(receive_handler);
}

void TizenClipboard::SendData(void* event) {
auto* send_event = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
if (!send_event->type || strcmp(send_event->type, kMimeTypeTextPlain)) {
FT_LOG(Error) << "Invaild mime type.";
return;
}

if (send_event->serial != selection_serial_) {
FT_LOG(Error) << "The serial doesn't match.";
return;
}

write(send_event->fd, data_.c_str(), data_.length());
close(send_event->fd);
}

void TizenClipboard::ReceiveData(void* event) {
auto* ready_event =
reinterpret_cast<Ecore_Wl2_Event_Offer_Data_Ready*>(event);
if (ready_event->data == nullptr || ready_event->len < 1) {
FT_LOG(Info) << "No data available.";
if (on_data_callback_) {
on_data_callback_("");
on_data_callback_ = nullptr;
}
return;
}

if (ready_event->offer != selection_offer_) {
FT_LOG(Error) << "The offer doesn't match.";
if (on_data_callback_) {
on_data_callback_(std::nullopt);
on_data_callback_ = nullptr;
}
return;
}

size_t data_length = strlen(ready_event->data);
size_t buffer_size = ready_event->len;
std::string content;

if (data_length < buffer_size) {
content.append(ready_event->data, data_length);
} else {
content.append(ready_event->data, buffer_size);
}

if (on_data_callback_) {
on_data_callback_(content);
on_data_callback_ = nullptr;
}
}

void TizenClipboard::SetData(const std::string& data) {
data_ = data;

const char* mime_types[3];
mime_types[0] = kMimeTypeTextPlain;
// TODO(jsuya): There is an issue where ECORE_WL2_EVENT_DATA_SOURCE_SEND event
// does not work properly even if ecore_wl2_dnd_selection_set() is called in
// Tizen 6.5 or lower. Therefore, add empty mimetype for event call from the
// cbhm module. Since it works normally from Tizen 8.0, this part may be
// modified in the future.
mime_types[1] = "";
mime_types[2] = nullptr;

Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display_);
selection_serial_ = ecore_wl2_dnd_selection_set(input, mime_types);
ecore_wl2_display_flush(display_);
}

bool TizenClipboard::GetData(ClipboardCallback on_data_callback) {
on_data_callback_ = std::move(on_data_callback);

Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display_);
selection_offer_ = ecore_wl2_dnd_selection_get(input);

if (!selection_offer_) {
FT_LOG(Error) << "ecore_wl2_dnd_selection_get() failed.";
return false;
}

ecore_wl2_offer_receive(selection_offer_,
const_cast<char*>(kMimeTypeTextPlain));
return true;
}

bool TizenClipboard::HasStrings() {
Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display_);
selection_offer_ = ecore_wl2_dnd_selection_get(input);

if (!selection_offer_) {
FT_LOG(Error) << "ecore_wl2_dnd_selection_get() failed.";
return false;
}

Eina_Array* available_types = ecore_wl2_offer_mimes_get(selection_offer_);
unsigned int type_count = eina_array_count(available_types);

for (unsigned int i = 0; i < type_count; ++i) {
auto* available_type =
static_cast<char*>(eina_array_data_get(available_types, i));
if (!strcmp(kMimeTypeTextPlain, available_type)) {
return true;
}
}
return false;
}

} // namespace flutter
46 changes: 46 additions & 0 deletions flutter/shell/platform/tizen/tizen_clipboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EMBEDDER_TIZEN_CLIPBOARD_H_
#define EMBEDDER_TIZEN_CLIPBOARD_H_

#define EFL_BETA_API_SUPPORT
#include <Ecore_Wl2.h>

#include <functional>
#include <optional>
#include <string>

#include "flutter/shell/platform/tizen/tizen_view_base.h"

namespace flutter {

class TizenClipboard {
public:
using ClipboardCallback =
std::function<void(std::optional<std::string> data)>;

TizenClipboard(TizenViewBase* view);
virtual ~TizenClipboard();

void SetData(const std::string& data);
bool GetData(ClipboardCallback on_data_callback);
bool HasStrings();

private:
void SendData(void* event);
void ReceiveData(void* event);

std::string data_;
ClipboardCallback on_data_callback_;
uint32_t selection_serial_ = 0;
Ecore_Wl2_Offer* selection_offer_ = nullptr;
Ecore_Wl2_Display* display_ = nullptr;
Ecore_Event_Handler* send_handler = nullptr;
Ecore_Event_Handler* receive_handler = nullptr;
};

} // namespace flutter

#endif // EMBEDDER_TIZEN_CLIPBOARD_H_

0 comments on commit c78aeb9

Please sign in to comment.