Skip to content

Commit

Permalink
Improve clipboard without cbhm (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
JSUYA authored Apr 30, 2024
1 parent 3a43fb5 commit a03d21c
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 a03d21c

Please sign in to comment.