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";
55 static const std::wstring kWindowClassName = L
"FlutterPlatformHandler";
58 class ScopedGlobalMemory {
61 ScopedGlobalMemory(
unsigned int flags,
size_t bytes) {
62 memory_ = ::GlobalAlloc(flags, bytes);
64 FML_LOG(ERROR) <<
"Unable to allocate global memory: "
65 <<
static_cast<int>(::GetLastError());
69 ~ScopedGlobalMemory() {
71 if (::GlobalFree(memory_) !=
nullptr) {
72 FML_LOG(ERROR) <<
"Failed to free global allocation: "
73 <<
static_cast<int>(::GetLastError());
79 void* get() {
return memory_; }
82 void* memory = memory_;
90 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalMemory);
94 class ScopedGlobalLock {
97 ScopedGlobalLock(HGLOBAL memory) {
100 locked_memory_ = ::GlobalLock(memory);
101 if (!locked_memory_) {
102 FML_LOG(ERROR) <<
"Unable to acquire global lock: " << ::GetLastError();
107 ~ScopedGlobalLock() {
108 if (locked_memory_) {
109 if (!::GlobalUnlock(source_)) {
110 DWORD error = ::GetLastError();
111 if (error != NO_ERROR) {
112 FML_LOG(ERROR) <<
"Unable to release global lock: "
121 void* get() {
return locked_memory_; }
125 void* locked_memory_;
127 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalLock);
132 class ScopedClipboard :
public ScopedClipboardInterface {
135 virtual ~ScopedClipboard();
137 int Open(HWND window)
override;
139 bool HasString()
override;
141 std::variant<std::wstring, int> GetString()
override;
143 int SetString(
const std::wstring
string)
override;
146 bool opened_ =
false;
148 FML_DISALLOW_COPY_AND_ASSIGN(ScopedClipboard);
151 ScopedClipboard::ScopedClipboard() {}
153 ScopedClipboard::~ScopedClipboard() {
159 int ScopedClipboard::Open(HWND window) {
160 opened_ = ::OpenClipboard(window);
163 return ::GetLastError();
169 bool ScopedClipboard::HasString() {
171 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) ||
172 ::IsClipboardFormatAvailable(CF_TEXT);
175 std::variant<std::wstring, int> ScopedClipboard::GetString() {
176 FML_DCHECK(opened_) <<
"Called GetString when clipboard is closed";
178 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
179 if (data ==
nullptr) {
180 return static_cast<int>(::GetLastError());
182 ScopedGlobalLock locked_data(data);
184 if (!locked_data.get()) {
185 return static_cast<int>(::GetLastError());
187 return static_cast<wchar_t*
>(locked_data.get());
190 int ScopedClipboard::SetString(
const std::wstring
string) {
191 FML_DCHECK(opened_) <<
"Called GetString when clipboard is closed";
192 if (!::EmptyClipboard()) {
193 return ::GetLastError();
195 size_t null_terminated_byte_count =
196 sizeof(decltype(
string)::traits_type::char_type) * (
string.size() + 1);
197 ScopedGlobalMemory destination_memory(GMEM_MOVEABLE,
198 null_terminated_byte_count);
199 ScopedGlobalLock locked_memory(destination_memory.get());
200 if (!locked_memory.get()) {
201 return ::GetLastError();
203 memcpy(locked_memory.get(),
string.c_str(), null_terminated_byte_count);
204 if (!::SetClipboardData(CF_UNICODETEXT, locked_memory.get())) {
205 return ::GetLastError();
208 destination_memory.release();
220 FML_LOG(ERROR) <<
string <<
" is not recognized as a valid exit type.";
227 std::optional<std::function<std::unique_ptr<ScopedClipboardInterface>()>>
228 scoped_clipboard_provider)
229 : channel_(std::make_unique<
MethodChannel<rapidjson::Document>>(
234 channel_->SetMethodCallHandler(
237 HandleMethodCall(call, std::move(result));
239 if (scoped_clipboard_provider.has_value()) {
240 scoped_clipboard_provider_ = scoped_clipboard_provider.value();
242 scoped_clipboard_provider_ = []() {
243 return std::make_unique<ScopedClipboard>();
247 WNDCLASS window_class = RegisterWindowClass();
249 CreateWindowEx(0, window_class.lpszClassName, L
"", 0, 0, 0, 0, 0,
250 HWND_MESSAGE,
nullptr, window_class.hInstance,
nullptr);
252 if (window_handle_) {
253 SetWindowLongPtr(window_handle_, GWLP_USERDATA,
254 reinterpret_cast<LONG_PTR
>(
this));
256 auto error = GetLastError();
258 size_t size = FormatMessageW(
259 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
260 FORMAT_MESSAGE_IGNORE_INSERTS,
261 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
262 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
269 if (window_handle_) {
270 DestroyWindow(window_handle_);
271 window_handle_ =
nullptr;
273 UnregisterClass(kWindowClassName.c_str(),
nullptr);
278 std::string_view
key) {
279 std::unique_ptr<ScopedClipboardInterface> clipboard =
280 scoped_clipboard_provider_();
282 int open_result = clipboard->Open(window_handle_);
284 rapidjson::Document error_code;
285 error_code.SetInt(open_result);
286 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
289 if (!clipboard->HasString()) {
290 result->Success(rapidjson::Document());
293 std::variant<std::wstring, int> get_string_result = clipboard->GetString();
294 if (std::holds_alternative<int>(get_string_result)) {
295 rapidjson::Document error_code;
296 error_code.SetInt(std::get<int>(get_string_result));
297 result->Error(
kClipboardError,
"Unable to get clipboard data", error_code);
301 rapidjson::Document document;
302 document.SetObject();
303 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
305 rapidjson::Value(
key.data(), allocator),
307 fml::WideStringToUtf8(std::get<std::wstring>(get_string_result)),
310 result->Success(document);
315 std::unique_ptr<ScopedClipboardInterface> clipboard =
316 scoped_clipboard_provider_();
319 int open_result = clipboard->Open(window_handle_);
325 rapidjson::Document error_code;
326 error_code.SetInt(open_result);
327 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
332 hasStrings = clipboard->HasString();
335 rapidjson::Document document;
336 document.SetObject();
337 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
338 document.AddMember(rapidjson::Value(
kValueKey, allocator),
339 rapidjson::Value(hasStrings), allocator);
340 result->Success(document);
344 const std::string&
text,
346 std::unique_ptr<ScopedClipboardInterface> clipboard =
347 scoped_clipboard_provider_();
349 int open_result = clipboard->Open(window_handle_);
351 rapidjson::Document error_code;
352 error_code.SetInt(open_result);
353 result->Error(
kClipboardError,
"Unable to open clipboard", error_code);
356 int set_result = clipboard->SetString(fml::Utf8ToWideString(
text));
358 rapidjson::Document error_code;
359 error_code.SetInt(set_result);
360 result->Error(
kClipboardError,
"Unable to set clipboard data", error_code);
367 const std::string& sound_type,
379 result->NotImplemented();
387 rapidjson::Document result_doc;
388 result_doc.SetObject();
392 result_doc.GetAllocator());
393 result->Success(result_doc);
395 RequestAppExit(std::nullopt, std::nullopt, std::nullopt, exit_type,
398 result_doc.GetAllocator());
399 result->Success(result_doc);
409 std::optional<WPARAM> wparam,
410 std::optional<LPARAM> lparam,
413 auto callback = std::make_unique<MethodResultFunctions<rapidjson::Document>>(
414 [
this, exit_code, hwnd, wparam,
415 lparam](
const rapidjson::Document* response) {
419 auto args = std::make_unique<rapidjson::Document>();
421 args->GetObjectW().AddMember(
423 args->GetAllocator());
429 std::optional<WPARAM> wparam,
430 std::optional<LPARAM> lparam,
431 const rapidjson::Document* result,
433 rapidjson::Value::ConstMemberIterator itr =
435 if (itr == result->MemberEnd() || !itr->value.IsString()) {
436 FML_LOG(ERROR) <<
"Application request response did not contain a valid "
440 const std::string& exit_type = itr->value.GetString();
448 std::optional<WPARAM> wparam,
449 std::optional<LPARAM> lparam,
451 engine_->
OnQuit(hwnd, wparam, lparam, exit_code);
454 void PlatformHandler::HandleMethodCall(
457 const std::string& method = method_call.
method_name();
459 const rapidjson::Value& arguments = method_call.
arguments()[0];
461 rapidjson::Value::ConstMemberIterator itr =
463 if (itr == arguments.MemberEnd() || !itr->value.IsString()) {
467 const std::string& exit_type = itr->value.GetString();
470 if (itr == arguments.MemberEnd() || !itr->value.IsInt()) {
480 const rapidjson::Value& format = method_call.
arguments()[0];
489 const rapidjson::Value& format = method_call.
arguments()[0];
497 const rapidjson::Value& document = *method_call.
arguments();
498 rapidjson::Value::ConstMemberIterator itr = document.FindMember(
kTextKey);
499 if (itr == document.MemberEnd()) {
503 if (!itr->value.IsString()) {
507 SetPlainText(itr->value.GetString(), std::move(result));
510 const rapidjson::Value& sound_type = method_call.
arguments()[0];
517 result->NotImplemented();
521 WNDCLASS PlatformHandler::RegisterWindowClass() {
522 WNDCLASS window_class{};
523 window_class.hCursor =
nullptr;
524 window_class.lpszClassName = kWindowClassName.c_str();
525 window_class.style = 0;
526 window_class.cbClsExtra = 0;
527 window_class.cbWndExtra = 0;
528 window_class.hInstance = GetModuleHandle(
nullptr);
529 window_class.hIcon =
nullptr;
530 window_class.hbrBackground = 0;
531 window_class.lpszMenuName =
nullptr;
532 window_class.lpfnWndProc = WndProc;
533 RegisterClass(&window_class);
537 LRESULT PlatformHandler::WndProc(HWND
const window,
540 LPARAM
const lparam) noexcept {
541 return DefWindowProc(window,
message, wparam, lparam);
void OnQuit(std::optional< HWND > hwnd, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
const std::string & method_name() const
const T * arguments() const
FlutterDesktopBinaryReply callback
static constexpr const char * kExitTypeNames[]
static AppExitType StringToAppExitType(const std::string &string)