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) {
254 if (view ==
nullptr) {
256 "Clipboard is not available in Windows headless mode");
260 std::unique_ptr<ScopedClipboardInterface> clipboard =
261 scoped_clipboard_provider_();
265 rapidjson::Document error_code;
266 error_code.SetInt(open_result);
267 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
270 if (!clipboard->HasString()) {
271 result->Success(rapidjson::Document());
274 std::variant<std::wstring, int> get_string_result = clipboard->GetString();
275 if (std::holds_alternative<int>(get_string_result)) {
276 rapidjson::Document error_code;
277 error_code.SetInt(std::get<int>(get_string_result));
278 result->Error(
kClipboardError,
"Unable to get clipboard data", error_code);
282 rapidjson::Document document;
283 document.SetObject();
284 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
286 rapidjson::Value(
key.data(), allocator),
288 fml::WideStringToUtf8(std::get<std::wstring>(get_string_result)),
291 result->Success(document);
299 if (view ==
nullptr) {
301 "Clipboard is not available in Windows headless mode");
305 std::unique_ptr<ScopedClipboardInterface> clipboard =
306 scoped_clipboard_provider_();
315 rapidjson::Document error_code;
316 error_code.SetInt(open_result);
317 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
322 hasStrings = clipboard->HasString();
325 rapidjson::Document document;
326 document.SetObject();
327 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
328 document.AddMember(rapidjson::Value(
kValueKey, allocator),
329 rapidjson::Value(hasStrings), allocator);
330 result->Success(document);
334 const std::string&
text,
339 if (view ==
nullptr) {
341 "Clipboard is not available in Windows headless mode");
345 std::unique_ptr<ScopedClipboardInterface> clipboard =
346 scoped_clipboard_provider_();
350 rapidjson::Document error_code;
351 error_code.SetInt(open_result);
352 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
355 int set_result = clipboard->SetString(fml::Utf8ToWideString(
text));
357 rapidjson::Document error_code;
358 error_code.SetInt(set_result);
359 result->Error(
kClipboardError,
"Unable to set clipboard data", error_code);
366 const std::string& sound_type,
372 result->NotImplemented();
380 rapidjson::Document result_doc;
381 result_doc.SetObject();
385 result_doc.GetAllocator());
386 result->Success(result_doc);
388 RequestAppExit(std::nullopt, std::nullopt, std::nullopt, exit_type,
391 result_doc.GetAllocator());
392 result->Success(result_doc);
402 std::optional<WPARAM> wparam,
403 std::optional<LPARAM> lparam,
406 auto callback = std::make_unique<MethodResultFunctions<rapidjson::Document>>(
407 [
this, exit_code, hwnd, wparam,
408 lparam](
const rapidjson::Document* response) {
412 auto args = std::make_unique<rapidjson::Document>();
414 args->GetObjectW().AddMember(
416 args->GetAllocator());
422 std::optional<WPARAM> wparam,
423 std::optional<LPARAM> lparam,
424 const rapidjson::Document* result,
426 rapidjson::Value::ConstMemberIterator itr =
428 if (itr == result->MemberEnd() || !itr->value.IsString()) {
429 FML_LOG(ERROR) <<
"Application request response did not contain a valid "
433 const std::string& exit_type = itr->value.GetString();
441 std::optional<WPARAM> wparam,
442 std::optional<LPARAM> lparam,
444 engine_->
OnQuit(hwnd, wparam, lparam, exit_code);
447 void PlatformHandler::HandleMethodCall(
450 const std::string& method = method_call.
method_name();
452 const rapidjson::Value& arguments = method_call.
arguments()[0];
454 rapidjson::Value::ConstMemberIterator itr =
456 if (itr == arguments.MemberEnd() || !itr->value.IsString()) {
460 const std::string& exit_type = itr->value.GetString();
463 if (itr == arguments.MemberEnd() || !itr->value.IsInt()) {
473 const rapidjson::Value& format = method_call.
arguments()[0];
482 const rapidjson::Value& format = method_call.
arguments()[0];
490 const rapidjson::Value& document = *method_call.
arguments();
491 rapidjson::Value::ConstMemberIterator itr = document.FindMember(
kTextKey);
492 if (itr == document.MemberEnd()) {
496 if (!itr->value.IsString()) {
500 SetPlainText(itr->value.GetString(), std::move(result));
503 const rapidjson::Value& sound_type = method_call.
arguments()[0];
510 result->NotImplemented();