| Flutter Windows Embedder |

platform_handler.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/windows/platform_handler.h"
6
7 #include <windows.h>
8
9 #include <cstring>
10 #include <optional>
11
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/macros.h"
14 #include "flutter/fml/platform/win/wstring_conversion.h"
15 #include "flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h"
16 #include "flutter/shell/platform/common/json_method_codec.h"
17 #include "flutter/shell/platform/windows/flutter_windows_view.h"
18
19 static constexpr char kChannelName[] = "flutter/platform";
20
21 static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
22 static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings";
23 static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
24 static constexpr char kExitApplicationMethod[] = "System.exitApplication";
25 static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
26 static constexpr char kInitializationCompleteMethod[] =
27 "System.initializationComplete";
28 static constexpr char kPlaySoundMethod[] = "SystemSound.play";
29
30 static constexpr char kExitCodeKey[] = "exitCode";
31
32 static constexpr char kExitTypeKey[] = "type";
33
34 static constexpr char kExitResponseKey[] = "response";
35 static constexpr char kExitResponseCancel[] = "cancel";
36 static constexpr char kExitResponseExit[] = "exit";
37
38 static constexpr char kTextPlainFormat[] = "text/plain";
39 static constexpr char kTextKey[] = "text";
40 static constexpr char kUnknownClipboardFormatMessage[] =
41 "Unknown clipboard format";
42
43 static constexpr char kValueKey[] = "value";
44 static constexpr int kAccessDeniedErrorCode = 5;
45 static constexpr int kErrorSuccess = 0;
46
47 static constexpr char kExitRequestError[] = "ExitApplication error";
48 static constexpr char kInvalidExitRequestMessage[] =
49 "Invalid application exit request";
50
51 namespace flutter {
52
53 namespace {
54
55 // A scoped wrapper for GlobalAlloc/GlobalFree.
56 class ScopedGlobalMemory {
57 public:
58 // Allocates |bytes| bytes of global memory with the given flags.
59 ScopedGlobalMemory(unsigned int flags, size_t bytes) {
60 memory_ = ::GlobalAlloc(flags, bytes);
61 if (!memory_) {
62 FML_LOG(ERROR) << "Unable to allocate global memory: "
63 << static_cast<int>(::GetLastError());
64 }
65 }
66
67 ~ScopedGlobalMemory() {
68 if (memory_) {
69 if (::GlobalFree(memory_) != nullptr) {
70 FML_LOG(ERROR) << "Failed to free global allocation: "
71 << static_cast<int>(::GetLastError());
72 }
73 }
74 }
75
76 // Returns the memory pointer, which will be nullptr if allocation failed.
77 void* get() { return memory_; }
78
79 void* release() {
80 void* memory = memory_;
81 memory_ = nullptr;
82 return memory;
83 }
84
85 private:
86 HGLOBAL memory_;
87
88 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalMemory);
89 };
90
91 // A scoped wrapper for GlobalLock/GlobalUnlock.
92 class ScopedGlobalLock {
93 public:
94 // Attempts to acquire a global lock on |memory| for the life of this object.
95 ScopedGlobalLock(HGLOBAL memory) {
96 source_ = memory;
97 if (memory) {
98 locked_memory_ = ::GlobalLock(memory);
99 if (!locked_memory_) {
100 FML_LOG(ERROR) << "Unable to acquire global lock: " << ::GetLastError();
101 }
102 }
103 }
104
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: "
111 << ::GetLastError();
112 }
113 }
114 }
115 }
116
117 // Returns the locked memory pointer, which will be nullptr if acquiring the
118 // lock failed.
119 void* get() { return locked_memory_; }
120
121 private:
122 HGLOBAL source_;
123 void* locked_memory_;
124
125 FML_DISALLOW_COPY_AND_ASSIGN(ScopedGlobalLock);
126 };
127
128 // A Clipboard wrapper that automatically closes the clipboard when it goes out
129 // of scope.
130 class ScopedClipboard : public ScopedClipboardInterface {
131 public:
132 ScopedClipboard();
133 virtual ~ScopedClipboard();
134
135 int Open(HWND window) override;
136
137 bool HasString() override;
138
139 std::variant<std::wstring, int> GetString() override;
140
141 int SetString(const std::wstring string) override;
142
143 private:
144 bool opened_ = false;
145
146 FML_DISALLOW_COPY_AND_ASSIGN(ScopedClipboard);
147 };
148
149 ScopedClipboard::ScopedClipboard() {}
150
151 ScopedClipboard::~ScopedClipboard() {
152 if (opened_) {
153 ::CloseClipboard();
154 }
155 }
156
157 int ScopedClipboard::Open(HWND window) {
158 opened_ = ::OpenClipboard(window);
159
160 if (!opened_) {
161 return ::GetLastError();
162 }
163
164 return kErrorSuccess;
165 }
166
167 bool ScopedClipboard::HasString() {
168 // Allow either plain text format, since getting data will auto-interpolate.
169 return ::IsClipboardFormatAvailable(CF_UNICODETEXT) ||
170 ::IsClipboardFormatAvailable(CF_TEXT);
171 }
172
173 std::variant<std::wstring, int> ScopedClipboard::GetString() {
174 FML_DCHECK(opened_) << "Called GetString when clipboard is closed";
175
176 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
177 if (data == nullptr) {
178 return static_cast<int>(::GetLastError());
179 }
180 ScopedGlobalLock locked_data(data);
181
182 if (!locked_data.get()) {
183 return static_cast<int>(::GetLastError());
184 }
185 return static_cast<wchar_t*>(locked_data.get());
186 }
187
188 int ScopedClipboard::SetString(const std::wstring string) {
189 FML_DCHECK(opened_) << "Called GetString when clipboard is closed";
190 if (!::EmptyClipboard()) {
191 return ::GetLastError();
192 }
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();
200 }
201 memcpy(locked_memory.get(), string.c_str(), null_terminated_byte_count);
202 if (!::SetClipboardData(CF_UNICODETEXT, locked_memory.get())) {
203 return ::GetLastError();
204 }
205 // The clipboard now owns the global memory.
206 destination_memory.release();
207 return kErrorSuccess;
208 }
209
210 } // namespace
211
212 static AppExitType StringToAppExitType(const std::string& string) {
213 if (string.compare(PlatformHandler::kExitTypeRequired) == 0) {
214 return AppExitType::required;
215 } else if (string.compare(PlatformHandler::kExitTypeCancelable) == 0) {
216 return AppExitType::cancelable;
217 }
218 FML_LOG(ERROR) << string << " is not recognized as a valid exit type.";
219 return AppExitType::required;
220 }
221
222 PlatformHandler::PlatformHandler(
223 BinaryMessenger* messenger,
224 FlutterWindowsEngine* engine,
225 std::optional<std::function<std::unique_ptr<ScopedClipboardInterface>()>>
226 scoped_clipboard_provider)
227 : channel_(std::make_unique<MethodChannel<rapidjson::Document>>(
228 messenger,
229 kChannelName,
230 &JsonMethodCodec::GetInstance())),
231 engine_(engine) {
232 channel_->SetMethodCallHandler(
233 [this](const MethodCall<rapidjson::Document>& call,
234 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
235 HandleMethodCall(call, std::move(result));
236 });
237 if (scoped_clipboard_provider.has_value()) {
238 scoped_clipboard_provider_ = scoped_clipboard_provider.value();
239 } else {
240 scoped_clipboard_provider_ = []() {
241 return std::make_unique<ScopedClipboard>();
242 };
243 }
244 }
245
246 PlatformHandler::~PlatformHandler() = default;
247
248 void PlatformHandler::GetPlainText(
249 std::unique_ptr<MethodResult<rapidjson::Document>> result,
250 std::string_view key) {
251 // TODO(loicsharma): Remove implicit view assumption.
252 // https://github.com/flutter/flutter/issues/142845
253 const FlutterWindowsView* view = engine_->view(kImplicitViewId);
254 if (view == nullptr) {
255 result->Error(kClipboardError,
256 "Clipboard is not available in Windows headless mode");
257 return;
258 }
259
260 std::unique_ptr<ScopedClipboardInterface> clipboard =
261 scoped_clipboard_provider_();
262
263 int open_result = clipboard->Open(view->GetWindowHandle());
264 if (open_result != kErrorSuccess) {
265 rapidjson::Document error_code;
266 error_code.SetInt(open_result);
267 result->Error(kClipboardError, "Unable to open clipboard", error_code);
268 return;
269 }
270 if (!clipboard->HasString()) {
271 result->Success(rapidjson::Document());
272 return;
273 }
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);
279 return;
280 }
281
282 rapidjson::Document document;
283 document.SetObject();
284 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
285 document.AddMember(
286 rapidjson::Value(key.data(), allocator),
287 rapidjson::Value(
288 fml::WideStringToUtf8(std::get<std::wstring>(get_string_result)),
289 allocator),
290 allocator);
291 result->Success(document);
292 }
293
294 void PlatformHandler::GetHasStrings(
295 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
296 // TODO(loicsharma): Remove implicit view assumption.
297 // https://github.com/flutter/flutter/issues/142845
298 const FlutterWindowsView* view = engine_->view(kImplicitViewId);
299 if (view == nullptr) {
300 result->Error(kClipboardError,
301 "Clipboard is not available in Windows headless mode");
302 return;
303 }
304
305 std::unique_ptr<ScopedClipboardInterface> clipboard =
306 scoped_clipboard_provider_();
307
308 bool hasStrings;
309 int open_result = clipboard->Open(view->GetWindowHandle());
310 if (open_result != kErrorSuccess) {
311 // Swallow errors of type ERROR_ACCESS_DENIED. These happen when the app is
312 // not in the foreground and GetHasStrings is irrelevant.
313 // See https://github.com/flutter/flutter/issues/95817.
314 if (open_result != kAccessDeniedErrorCode) {
315 rapidjson::Document error_code;
316 error_code.SetInt(open_result);
317 result->Error(kClipboardError, "Unable to open clipboard", error_code);
318 return;
319 }
320 hasStrings = false;
321 } else {
322 hasStrings = clipboard->HasString();
323 }
324
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);
331 }
332
333 void PlatformHandler::SetPlainText(
334 const std::string& text,
335 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
336 // TODO(loicsharma): Remove implicit view assumption.
337 // https://github.com/flutter/flutter/issues/142845
338 const FlutterWindowsView* view = engine_->view(kImplicitViewId);
339 if (view == nullptr) {
340 result->Error(kClipboardError,
341 "Clipboard is not available in Windows headless mode");
342 return;
343 }
344
345 std::unique_ptr<ScopedClipboardInterface> clipboard =
346 scoped_clipboard_provider_();
347
348 int open_result = clipboard->Open(view->GetWindowHandle());
349 if (open_result != kErrorSuccess) {
350 rapidjson::Document error_code;
351 error_code.SetInt(open_result);
352 result->Error(kClipboardError, "Unable to open clipboard", error_code);
353 return;
354 }
355 int set_result = clipboard->SetString(fml::Utf8ToWideString(text));
356 if (set_result != kErrorSuccess) {
357 rapidjson::Document error_code;
358 error_code.SetInt(set_result);
359 result->Error(kClipboardError, "Unable to set clipboard data", error_code);
360 return;
361 }
362 result->Success();
363 }
364
365 void PlatformHandler::SystemSoundPlay(
366 const std::string& sound_type,
367 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
368 if (sound_type.compare(kSoundTypeAlert) == 0) {
369 MessageBeep(MB_OK);
370 result->Success();
371 } else {
372 result->NotImplemented();
373 }
374 }
375
376 void PlatformHandler::SystemExitApplication(
377 AppExitType exit_type,
378 UINT exit_code,
379 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
380 rapidjson::Document result_doc;
381 result_doc.SetObject();
382 if (exit_type == AppExitType::required) {
383 QuitApplication(std::nullopt, std::nullopt, std::nullopt, exit_code);
384 result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseExit,
385 result_doc.GetAllocator());
386 result->Success(result_doc);
387 } else {
388 RequestAppExit(std::nullopt, std::nullopt, std::nullopt, exit_type,
389 exit_code);
390 result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseCancel,
391 result_doc.GetAllocator());
392 result->Success(result_doc);
393 }
394 }
395
396 // Indicates whether an exit request may be canceled by the framework.
397 // These values must be kept in sync with ExitType in platform_handler.h
398 static constexpr const char* kExitTypeNames[] = {
399 PlatformHandler::kExitTypeRequired, PlatformHandler::kExitTypeCancelable};
400
401 void PlatformHandler::RequestAppExit(std::optional<HWND> hwnd,
402 std::optional<WPARAM> wparam,
403 std::optional<LPARAM> lparam,
404 AppExitType exit_type,
405 UINT exit_code) {
406 auto callback = std::make_unique<MethodResultFunctions<rapidjson::Document>>(
407 [this, exit_code, hwnd, wparam,
408 lparam](const rapidjson::Document* response) {
409 RequestAppExitSuccess(hwnd, wparam, lparam, response, exit_code);
410 },
411 nullptr, nullptr);
412 auto args = std::make_unique<rapidjson::Document>();
413 args->SetObject();
414 args->GetObjectW().AddMember(
415 kExitTypeKey, std::string(kExitTypeNames[static_cast<int>(exit_type)]),
416 args->GetAllocator());
417 channel_->InvokeMethod(kRequestAppExitMethod, std::move(args),
418 std::move(callback));
419 }
420
421 void PlatformHandler::RequestAppExitSuccess(std::optional<HWND> hwnd,
422 std::optional<WPARAM> wparam,
423 std::optional<LPARAM> lparam,
424 const rapidjson::Document* result,
425 UINT exit_code) {
426 rapidjson::Value::ConstMemberIterator itr =
427 result->FindMember(kExitResponseKey);
428 if (itr == result->MemberEnd() || !itr->value.IsString()) {
429 FML_LOG(ERROR) << "Application request response did not contain a valid "
430 "response value";
431 return;
432 }
433 const std::string& exit_type = itr->value.GetString();
434
435 if (exit_type.compare(kExitResponseExit) == 0) {
436 QuitApplication(hwnd, wparam, lparam, exit_code);
437 }
438 }
439
440 void PlatformHandler::QuitApplication(std::optional<HWND> hwnd,
441 std::optional<WPARAM> wparam,
442 std::optional<LPARAM> lparam,
443 UINT exit_code) {
444 engine_->OnQuit(hwnd, wparam, lparam, exit_code);
445 }
446
447 void PlatformHandler::HandleMethodCall(
448 const MethodCall<rapidjson::Document>& method_call,
449 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
450 const std::string& method = method_call.method_name();
451 if (method.compare(kExitApplicationMethod) == 0) {
452 const rapidjson::Value& arguments = method_call.arguments()[0];
453
454 rapidjson::Value::ConstMemberIterator itr =
455 arguments.FindMember(kExitTypeKey);
456 if (itr == arguments.MemberEnd() || !itr->value.IsString()) {
457 result->Error(kExitRequestError, kInvalidExitRequestMessage);
458 return;
459 }
460 const std::string& exit_type = itr->value.GetString();
461
462 itr = arguments.FindMember(kExitCodeKey);
463 if (itr == arguments.MemberEnd() || !itr->value.IsInt()) {
464 result->Error(kExitRequestError, kInvalidExitRequestMessage);
465 return;
466 }
467 UINT exit_code = arguments[kExitCodeKey].GetInt();
468
469 SystemExitApplication(StringToAppExitType(exit_type), exit_code,
470 std::move(result));
471 } else if (method.compare(kGetClipboardDataMethod) == 0) {
472 // Only one string argument is expected.
473 const rapidjson::Value& format = method_call.arguments()[0];
474
475 if (strcmp(format.GetString(), kTextPlainFormat) != 0) {
476 result->Error(kClipboardError, kUnknownClipboardFormatMessage);
477 return;
478 }
479 GetPlainText(std::move(result), kTextKey);
480 } else if (method.compare(kHasStringsClipboardMethod) == 0) {
481 // Only one string argument is expected.
482 const rapidjson::Value& format = method_call.arguments()[0];
483
484 if (strcmp(format.GetString(), kTextPlainFormat) != 0) {
485 result->Error(kClipboardError, kUnknownClipboardFormatMessage);
486 return;
487 }
488 GetHasStrings(std::move(result));
489 } else if (method.compare(kSetClipboardDataMethod) == 0) {
490 const rapidjson::Value& document = *method_call.arguments();
491 rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey);
492 if (itr == document.MemberEnd()) {
493 result->Error(kClipboardError, kUnknownClipboardFormatMessage);
494 return;
495 }
496 if (!itr->value.IsString()) {
497 result->Error(kClipboardError, kUnknownClipboardFormatMessage);
498 return;
499 }
500 SetPlainText(itr->value.GetString(), std::move(result));
501 } else if (method.compare(kPlaySoundMethod) == 0) {
502 // Only one string argument is expected.
503 const rapidjson::Value& sound_type = method_call.arguments()[0];
504
505 SystemSoundPlay(sound_type.GetString(), std::move(result));
506 } else if (method.compare(kInitializationCompleteMethod) == 0) {
507 // Deprecated but should not cause an error.
508 result->Success();
509 } else {
510 result->NotImplemented();
511 }
512 }
513
514 } // namespace flutter
flutter::BinaryMessenger
Definition: binary_messenger.h:28
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
flutter::FlutterWindowsEngine::view
FlutterWindowsView * view(FlutterViewId view_id) const
Definition: flutter_windows_engine.cc:674
flutter::FlutterWindowsEngine::OnQuit
void OnQuit(std::optional< HWND > hwnd, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
Definition: flutter_windows_engine.cc:1010
flutter::FlutterWindowsView
Definition: flutter_windows_view.h:34
flutter::FlutterWindowsView::GetWindowHandle
virtual HWND GetWindowHandle() const
Definition: flutter_windows_view.cc:778
flutter::JsonMethodCodec
Definition: json_method_codec.h:16
flutter::MethodCall
Definition: method_call.h:18
flutter::MethodCall::method_name
const std::string & method_name() const
Definition: method_call.h:31
flutter::MethodCall::arguments
const T * arguments() const
Definition: method_call.h:34
flutter::MethodChannel
Definition: method_channel.h:34
flutter::MethodResult
Definition: method_result.h:17
flutter::PlatformHandler::QuitApplication
virtual void QuitApplication(std::optional< HWND > hwnd, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
Definition: platform_handler.cc:440
flutter::PlatformHandler::GetHasStrings
virtual void GetHasStrings(std::unique_ptr< MethodResult< rapidjson::Document >> result)
Definition: platform_handler.cc:294
flutter::PlatformHandler::RequestAppExitSuccess
virtual void RequestAppExitSuccess(std::optional< HWND > hwnd, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, const rapidjson::Document *result, UINT exit_code)
Definition: platform_handler.cc:421
flutter::PlatformHandler::kClipboardError
static constexpr char kClipboardError[]
Definition: platform_handler.h:104
flutter::PlatformHandler::PlatformHandler
PlatformHandler(BinaryMessenger *messenger, FlutterWindowsEngine *engine, std::optional< std::function< std::unique_ptr< ScopedClipboardInterface >()>> scoped_clipboard_provider=std::nullopt)
Definition: platform_handler.cc:222
flutter::PlatformHandler::SystemSoundPlay
virtual void SystemSoundPlay(const std::string &sound_type, std::unique_ptr< MethodResult< rapidjson::Document >> result)
Definition: platform_handler.cc:365
flutter::PlatformHandler::kExitTypeCancelable
static constexpr char kExitTypeCancelable[]
Definition: platform_handler.h:44
flutter::PlatformHandler::SystemExitApplication
virtual void SystemExitApplication(AppExitType exit_type, UINT exit_code, std::unique_ptr< MethodResult< rapidjson::Document >> result)
Definition: platform_handler.cc:376
flutter::PlatformHandler::GetPlainText
virtual void GetPlainText(std::unique_ptr< MethodResult< rapidjson::Document >> result, std::string_view key)
Definition: platform_handler.cc:248
flutter::PlatformHandler::~PlatformHandler
virtual ~PlatformHandler()
flutter::PlatformHandler::RequestAppExit
virtual void RequestAppExit(std::optional< HWND > hwnd, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, AppExitType exit_type, UINT exit_code)
Definition: platform_handler.cc:401
flutter::PlatformHandler::kExitTypeRequired
static constexpr char kExitTypeRequired[]
Definition: platform_handler.h:45
flutter::PlatformHandler::kSoundTypeAlert
static constexpr char kSoundTypeAlert[]
Definition: platform_handler.h:106
flutter::PlatformHandler::SetPlainText
virtual void SetPlainText(const std::string &text, std::unique_ptr< MethodResult< rapidjson::Document >> result)
Definition: platform_handler.cc:333
flutter_windows_view.h
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:52
json_method_codec.h
key
int key
Definition: keyboard_key_handler_unittests.cc:114
text
std::u16string text
Definition: keyboard_unittests.cc:332
method_result_functions.h
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::AppExitType
AppExitType
Definition: platform_handler.h:27
flutter::AppExitType::required
@ required
flutter::AppExitType::cancelable
@ cancelable
flutter::kExitTypeNames
static constexpr const char * kExitTypeNames[]
Definition: platform_handler.cc:398
flutter::StringToAppExitType
static AppExitType StringToAppExitType(const std::string &string)
Definition: platform_handler.cc:212
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
kValueKey
static constexpr char kValueKey[]
Definition: platform_handler.cc:43
kInitializationCompleteMethod
static constexpr char kInitializationCompleteMethod[]
Definition: platform_handler.cc:26
kInvalidExitRequestMessage
static constexpr char kInvalidExitRequestMessage[]
Definition: platform_handler.cc:48
kGetClipboardDataMethod
static constexpr char kGetClipboardDataMethod[]
Definition: platform_handler.cc:21
kChannelName
static constexpr char kChannelName[]
Definition: platform_handler.cc:19
kHasStringsClipboardMethod
static constexpr char kHasStringsClipboardMethod[]
Definition: platform_handler.cc:22
kExitApplicationMethod
static constexpr char kExitApplicationMethod[]
Definition: platform_handler.cc:24
kExitResponseExit
static constexpr char kExitResponseExit[]
Definition: platform_handler.cc:36
kExitCodeKey
static constexpr char kExitCodeKey[]
Definition: platform_handler.cc:30
kErrorSuccess
static constexpr int kErrorSuccess
Definition: platform_handler.cc:45
kRequestAppExitMethod
static constexpr char kRequestAppExitMethod[]
Definition: platform_handler.cc:25
kUnknownClipboardFormatMessage
static constexpr char kUnknownClipboardFormatMessage[]
Definition: platform_handler.cc:40
kExitResponseKey
static constexpr char kExitResponseKey[]
Definition: platform_handler.cc:34
kPlaySoundMethod
static constexpr char kPlaySoundMethod[]
Definition: platform_handler.cc:28
kAccessDeniedErrorCode
static constexpr int kAccessDeniedErrorCode
Definition: platform_handler.cc:44
kExitResponseCancel
static constexpr char kExitResponseCancel[]
Definition: platform_handler.cc:35
kExitTypeKey
static constexpr char kExitTypeKey[]
Definition: platform_handler.cc:32
kTextPlainFormat
static constexpr char kTextPlainFormat[]
Definition: platform_handler.cc:38
kTextKey
static constexpr char kTextKey[]
Definition: platform_handler.cc:39
kSetClipboardDataMethod
static constexpr char kSetClipboardDataMethod[]
Definition: platform_handler.cc:23
kExitRequestError
static constexpr char kExitRequestError[]
Definition: platform_handler.cc:47
platform_handler.h