12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/macros.h"
14 #include "flutter/fml/platform/win/wstring_conversion.h"
27 "System.initializationComplete";
41 "Unknown clipboard format";
49 "Invalid application exit request";
56 class ScopedGlobalMemory {
59 ScopedGlobalMemory(
unsigned int flags,
size_t bytes) {
60 memory_ = ::GlobalAlloc(flags, bytes);
62 FML_LOG(ERROR) <<
"Unable to allocate global memory: "
67 ~ScopedGlobalMemory() {
69 if (::GlobalFree(memory_) !=
nullptr) {
70 FML_LOG(ERROR) <<
"Failed to free global allocation: "
77 void* get() {
return memory_; }
80 void* memory = memory_;
88 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalMemory);
92 class ScopedGlobalLock {
95 ScopedGlobalLock(HGLOBAL memory) {
98 locked_memory_ = ::GlobalLock(memory);
99 if (!locked_memory_) {
100 FML_LOG(ERROR) <<
"Unable to acquire global lock: " << ::GetLastError();
105 ~ScopedGlobalLock() {
106 if (locked_memory_) {
107 if (!::GlobalUnlock(source_)) {
108 DWORD error = ::GetLastError();
109 if (error != NO_ERROR) {
110 FML_LOG(ERROR) <<
"Unable to release global lock: "
119 void* get() {
return locked_memory_; }
123 void* locked_memory_;
125 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalLock);
130 class ScopedClipboard :
public ScopedClipboardInterface {
133 virtual ~ScopedClipboard();
135 int Open(HWND window)
override;
137 bool HasString()
override;
139 std::variant<std::wstring, int> GetString()
override;
141 int SetString(
const std::wstring
string)
override;
144 bool opened_ =
false;
146 FML_DISALLOW_COPY_AND_ASSIGN(ScopedClipboard);
149 ScopedClipboard::ScopedClipboard() {}
151 ScopedClipboard::~ScopedClipboard() {
157 int ScopedClipboard::Open(HWND window) {
158 opened_ = ::OpenClipboard(window);
161 return ::GetLastError();
167 bool ScopedClipboard::HasString() {
169 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) ||
170 ::IsClipboardFormatAvailable(CF_TEXT);
173 std::variant<std::wstring, int> ScopedClipboard::GetString() {
174 FML_DCHECK(opened_) <<
"Called GetString when clipboard is closed";
176 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
177 if (data ==
nullptr) {
178 return ::GetLastError();
180 ScopedGlobalLock locked_data(data);
182 if (!locked_data.get()) {
183 return ::GetLastError();
185 return static_cast<wchar_t*
>(locked_data.get());
188 int ScopedClipboard::SetString(
const std::wstring
string) {
189 FML_DCHECK(opened_) <<
"Called GetString when clipboard is closed";
190 if (!::EmptyClipboard()) {
191 return ::GetLastError();
193 size_t null_terminated_byte_count =
194 sizeof(decltype(
string)::traits_type::char_type) * (
string.size() + 1);
195 ScopedGlobalMemory destination_memory(GMEM_MOVEABLE,
196 null_terminated_byte_count);
197 ScopedGlobalLock locked_memory(destination_memory.get());
198 if (!locked_memory.get()) {
199 return ::GetLastError();
201 memcpy(locked_memory.get(),
string.c_str(), null_terminated_byte_count);
202 if (!::SetClipboardData(CF_UNICODETEXT, locked_memory.get())) {
203 return ::GetLastError();
206 destination_memory.release();
218 FML_LOG(ERROR) <<
string <<
" is not recognized as a valid exit type.";
225 std::optional<std::function<std::unique_ptr<ScopedClipboardInterface>()>>
226 scoped_clipboard_provider)
227 : channel_(std::make_unique<
MethodChannel<rapidjson::Document>>(
232 channel_->SetMethodCallHandler(
235 HandleMethodCall(call, std::move(result));
237 if (scoped_clipboard_provider.has_value()) {
238 scoped_clipboard_provider_ = scoped_clipboard_provider.value();
240 scoped_clipboard_provider_ = []() {
241 return std::make_unique<ScopedClipboard>();
250 std::string_view
key) {
252 if (view ==
nullptr) {
254 "Clipboard is not available in Windows headless mode");
258 std::unique_ptr<ScopedClipboardInterface> clipboard =
259 scoped_clipboard_provider_();
261 int open_result = clipboard->Open(std::get<HWND>(*view->
GetRenderTarget()));
263 rapidjson::Document error_code;
264 error_code.SetInt(open_result);
265 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
268 if (!clipboard->HasString()) {
269 result->Success(rapidjson::Document());
272 std::variant<std::wstring, int> get_string_result = clipboard->GetString();
273 if (std::holds_alternative<int>(get_string_result)) {
274 rapidjson::Document error_code;
275 error_code.SetInt(std::get<int>(get_string_result));
276 result->Error(
kClipboardError,
"Unable to get clipboard data", error_code);
280 rapidjson::Document document;
281 document.SetObject();
282 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
284 rapidjson::Value(
key.data(), allocator),
286 fml::WideStringToUtf8(std::get<std::wstring>(get_string_result)),
289 result->Success(document);
295 if (view ==
nullptr) {
297 "Clipboard is not available in Windows headless mode");
301 std::unique_ptr<ScopedClipboardInterface> clipboard =
302 scoped_clipboard_provider_();
305 int open_result = clipboard->Open(std::get<HWND>(*view->
GetRenderTarget()));
311 rapidjson::Document error_code;
312 error_code.SetInt(open_result);
313 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
318 hasStrings = clipboard->HasString();
321 rapidjson::Document document;
322 document.SetObject();
323 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
324 document.AddMember(rapidjson::Value(
kValueKey, allocator),
325 rapidjson::Value(hasStrings), allocator);
326 result->Success(document);
330 const std::string&
text,
333 if (view ==
nullptr) {
335 "Clipboard is not available in Windows headless mode");
339 std::unique_ptr<ScopedClipboardInterface> clipboard =
340 scoped_clipboard_provider_();
342 int open_result = clipboard->Open(std::get<HWND>(*view->
GetRenderTarget()));
344 rapidjson::Document error_code;
345 error_code.SetInt(open_result);
346 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
349 int set_result = clipboard->SetString(fml::Utf8ToWideString(
text));
351 rapidjson::Document error_code;
352 error_code.SetInt(set_result);
353 result->Error(
kClipboardError,
"Unable to set clipboard data", error_code);
360 const std::string& sound_type,
366 result->NotImplemented();
374 rapidjson::Document result_doc;
375 result_doc.SetObject();
379 result_doc.GetAllocator());
380 result->Success(result_doc);
382 RequestAppExit(std::nullopt, std::nullopt, std::nullopt, exit_type,
385 result_doc.GetAllocator());
386 result->Success(result_doc);
396 std::optional<WPARAM> wparam,
397 std::optional<LPARAM> lparam,
400 auto callback = std::make_unique<MethodResultFunctions<rapidjson::Document>>(
401 [
this, exit_code, hwnd, wparam,
402 lparam](
const rapidjson::Document* response) {
406 auto args = std::make_unique<rapidjson::Document>();
408 args->GetObjectW().AddMember(
410 args->GetAllocator());
416 std::optional<WPARAM> wparam,
417 std::optional<LPARAM> lparam,
418 const rapidjson::Document* result,
420 rapidjson::Value::ConstMemberIterator itr =
422 if (itr == result->MemberEnd() || !itr->value.IsString()) {
423 FML_LOG(ERROR) <<
"Application request response did not contain a valid "
427 const std::string& exit_type = itr->value.GetString();
435 std::optional<WPARAM> wparam,
436 std::optional<LPARAM> lparam,
438 engine_->
OnQuit(hwnd, wparam, lparam, exit_code);
441 void PlatformHandler::HandleMethodCall(
444 const std::string& method = method_call.
method_name();
446 const rapidjson::Value& arguments = method_call.
arguments()[0];
448 rapidjson::Value::ConstMemberIterator itr =
450 if (itr == arguments.MemberEnd() || !itr->value.IsString()) {
454 const std::string& exit_type = itr->value.GetString();
457 if (itr == arguments.MemberEnd() || !itr->value.IsInt()) {
467 const rapidjson::Value& format = method_call.
arguments()[0];
476 const rapidjson::Value& format = method_call.
arguments()[0];
484 const rapidjson::Value& document = *method_call.
arguments();
485 rapidjson::Value::ConstMemberIterator itr = document.FindMember(
kTextKey);
486 if (itr == document.MemberEnd()) {
490 if (!itr->value.IsString()) {
494 SetPlainText(itr->value.GetString(), std::move(result));
497 const rapidjson::Value& sound_type = method_call.
arguments()[0];
504 result->NotImplemented();