Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UWP/PPSSPP_UWPMain.cpp
3185 views
1
#include "pch.h"
2
#include "PPSSPP_UWPMain.h"
3
4
#include <mutex>
5
#include <list>
6
#include <memory>
7
8
#include "Common/File/FileUtil.h"
9
#include "Common/Net/HTTPClient.h"
10
#include "Common/Net/Resolve.h"
11
#include "Common/GPU/thin3d_create.h"
12
13
#include "Common/Common.h"
14
#include "Common/Audio/AudioBackend.h"
15
#include "Common/Input/InputState.h"
16
#include "Common/File/VFS/VFS.h"
17
#include "Common/Thread/ThreadUtil.h"
18
#include "Common/Data/Encoding/Utf8.h"
19
#include "Common/DirectXHelper.h"
20
#include "Common/Log.h"
21
#include "Common/Log/LogManager.h"
22
#include "Common/TimeUtil.h"
23
#include "Common/StringUtils.h"
24
#include "Common/System/Display.h"
25
#include "Common/System/NativeApp.h"
26
#include "Common/System/Request.h"
27
28
#include "Core/System.h"
29
#include "Core/Loaders.h"
30
#include "Core/Config.h"
31
32
#include "Windows/InputDevice.h"
33
#include "Windows/XinputDevice.h"
34
#include "NKCodeFromWindowsSystem.h"
35
#include "XAudioSoundStream.h"
36
#include "UWPUtil.h"
37
#include "App.h"
38
39
// UWP Helpers includes
40
#include "UWPHelpers/StorageManager.h"
41
#include "UWPHelpers/StorageAsync.h"
42
#include "UWPHelpers/LaunchItem.h"
43
#include "UWPHelpers/InputHelpers.h"
44
#include "Windows/InputDevice.h"
45
46
using namespace UWP;
47
using namespace Windows::Foundation;
48
using namespace Windows::Storage;
49
using namespace Windows::Storage::Streams;
50
using namespace Windows::System::Threading;
51
using namespace Windows::ApplicationModel::DataTransfer;
52
using namespace Windows::Devices::Enumeration;
53
using namespace Concurrency;
54
55
// TODO: Use Microsoft::WRL::ComPtr<> for D3D11 objects?
56
// TODO: See https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/WindowsAudioSession for WASAPI with UWP
57
// TODO: Low latency input: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/LowLatencyInput/cpp
58
59
// Loads and initializes application assets when the application is loaded.
60
PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptr<DX::DeviceResources>& deviceResources) :
61
app_(app),
62
m_deviceResources(deviceResources)
63
{
64
TimeInit();
65
66
// Register to be notified if the Device is lost or recreated
67
m_deviceResources->RegisterDeviceNotify(this);
68
69
ctx_.reset(new UWPGraphicsContext(deviceResources));
70
71
#if _DEBUG
72
g_logManager.SetAllLogLevels(LogLevel::LDEBUG);
73
74
if (g_Config.bEnableLogging) {
75
g_logManager.SetFileLogPath(Path(GetLogFile()));
76
}
77
#endif
78
79
// At this point we have main requirements initialized (Log, Config, NativeInit, Device)
80
NativeInitGraphics(ctx_.get());
81
NativeResized();
82
83
int width = m_deviceResources->GetScreenViewport().Width;
84
int height = m_deviceResources->GetScreenViewport().Height;
85
86
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
87
88
// add first XInput device to respond
89
g_InputManager.AddDevice(new XinputDevice());
90
g_InputManager.BeginPolling();
91
92
// Prepare input pane (for Xbox & touch devices)
93
PrepareInputPane();
94
}
95
96
PPSSPP_UWPMain::~PPSSPP_UWPMain() {
97
g_InputManager.StopPolling();
98
g_InputManager.Shutdown();
99
100
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
101
NativeShutdownGraphics();
102
NativeShutdown();
103
g_VFS.Clear();
104
105
// Deregister device notification
106
m_deviceResources->RegisterDeviceNotify(nullptr);
107
net::Shutdown();
108
}
109
110
// Updates application state when the window size changes (e.g. device orientation change)
111
void PPSSPP_UWPMain::CreateWindowSizeDependentResources() {
112
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
113
114
NativeResized();
115
116
int width = m_deviceResources->GetScreenViewport().Width;
117
int height = m_deviceResources->GetScreenViewport().Height;
118
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
119
}
120
121
void PPSSPP_UWPMain::UpdateScreenState() {
122
// This code was included into the render loop directly
123
// based on my test I don't understand why it should be called each loop
124
// is it better to call it on demand only, like when screen state changed?
125
auto context = m_deviceResources->GetD3DDeviceContext();
126
127
switch (m_deviceResources->ComputeDisplayRotation()) {
128
case DXGI_MODE_ROTATION_IDENTITY: g_display.rotation = DisplayRotation::ROTATE_0; break;
129
case DXGI_MODE_ROTATION_ROTATE90: g_display.rotation = DisplayRotation::ROTATE_90; break;
130
case DXGI_MODE_ROTATION_ROTATE180: g_display.rotation = DisplayRotation::ROTATE_180; break;
131
case DXGI_MODE_ROTATION_ROTATE270: g_display.rotation = DisplayRotation::ROTATE_270; break;
132
}
133
// Not super elegant but hey.
134
memcpy(&g_display.rot_matrix, &m_deviceResources->GetOrientationTransform3D(), sizeof(float) * 16);
135
136
// Reset the viewport to target the whole screen.
137
auto viewport = m_deviceResources->GetScreenViewport();
138
139
g_display.pixel_xres = viewport.Width;
140
g_display.pixel_yres = viewport.Height;
141
142
if (g_display.rotation == DisplayRotation::ROTATE_90 || g_display.rotation == DisplayRotation::ROTATE_270) {
143
// We need to swap our width/height.
144
// TODO: This is most likely dead code, since we no longer support Windows Phone.
145
std::swap(g_display.pixel_xres, g_display.pixel_yres);
146
}
147
148
// TODO: The below stuff is probably completely redundant since the UWP app elsewhere calls Native_UpdateScreenScale.
149
150
float dpi = m_deviceResources->GetActualDpi();
151
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
152
// Boost DPI a bit to look better.
153
dpi *= 96.0f / 136.0f;
154
}
155
156
g_display.dpi_scale_real_x = 96.0f / dpi;
157
g_display.dpi_scale_real_y = 96.0f / dpi;
158
159
g_display.dpi_scale_x = g_display.dpi_scale_real_x;
160
g_display.dpi_scale_y = g_display.dpi_scale_real_y;
161
g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;
162
g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;
163
164
g_display.dp_xres = g_display.pixel_xres * g_display.dpi_scale_x;
165
g_display.dp_yres = g_display.pixel_yres * g_display.dpi_scale_y;
166
167
context->RSSetViewports(1, &viewport);
168
}
169
170
// Renders the current frame according to the current application state.
171
// Returns true if the frame was rendered and is ready to be displayed.
172
bool PPSSPP_UWPMain::Render() {
173
static bool hasSetThreadName = false;
174
if (!hasSetThreadName) {
175
SetCurrentThreadName("EmuThread");
176
hasSetThreadName = true;
177
}
178
179
UpdateScreenState();
180
181
NativeFrame(ctx_.get());
182
return true;
183
}
184
185
// Notifies renderers that device resources need to be released.
186
void PPSSPP_UWPMain::OnDeviceLost() {
187
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_DEVICE, 0, 0, nullptr);
188
}
189
190
// Notifies renderers that device resources may now be recreated.
191
void PPSSPP_UWPMain::OnDeviceRestored() {
192
CreateWindowSizeDependentResources();
193
194
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_DEVICE, 0, 0, nullptr);
195
}
196
197
void PPSSPP_UWPMain::OnKeyDown(int scanCode, Windows::System::VirtualKey virtualKey, int repeatCount) {
198
// TODO: Look like (Ctrl, Alt, Shift) don't trigger this event
199
bool isDPad = (int)virtualKey >= 195 && (int)virtualKey <= 218; // DPad buttons range
200
DPadInputState(isDPad);
201
202
auto iter = virtualKeyCodeToNKCode.find(virtualKey);
203
if (iter != virtualKeyCodeToNKCode.end()) {
204
KeyInput key{};
205
key.deviceId = DEVICE_ID_KEYBOARD;
206
key.keyCode = iter->second;
207
key.flags = KEY_DOWN | (repeatCount > 1 ? KEY_IS_REPEAT : 0);
208
NativeKey(key);
209
}
210
}
211
212
void PPSSPP_UWPMain::OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKey) {
213
auto iter = virtualKeyCodeToNKCode.find(virtualKey);
214
if (iter != virtualKeyCodeToNKCode.end()) {
215
KeyInput key{};
216
key.deviceId = DEVICE_ID_KEYBOARD;
217
key.keyCode = iter->second;
218
key.flags = KEY_UP;
219
NativeKey(key);
220
}
221
}
222
223
void PPSSPP_UWPMain::OnCharacterReceived(int scanCode, unsigned int keyCode) {
224
// This event triggered only in chars case, (Arrows, Delete..etc don't call it)
225
// TODO: Add ` && !IsCtrlOnHold()` once it's ready and implemented
226
if (isTextEditActive()) {
227
KeyInput key{};
228
key.deviceId = DEVICE_ID_KEYBOARD;
229
key.keyCode = (InputKeyCode)keyCode;
230
// After many tests turns out for char just add `KEY_CHAR` for the flags
231
// any other flag like `KEY_DOWN` will cause conflict and trigger something else
232
key.flags = KEY_CHAR;
233
NativeKey(key);
234
}
235
}
236
237
void PPSSPP_UWPMain::OnMouseWheel(float delta) {
238
InputKeyCode key = NKCODE_EXT_MOUSEWHEEL_UP;
239
if (delta < 0) {
240
key = NKCODE_EXT_MOUSEWHEEL_DOWN;
241
} else if (delta == 0) {
242
return;
243
}
244
245
KeyInput keyInput{};
246
keyInput.keyCode = key;
247
keyInput.deviceId = DEVICE_ID_MOUSE;
248
keyInput.flags = KEY_DOWN;
249
NativeKey(keyInput);
250
251
// KEY_UP is now sent automatically afterwards for mouse wheel events, see NativeKey.
252
}
253
254
bool PPSSPP_UWPMain::OnHardwareButton(HardwareButton button) {
255
KeyInput keyInput{};
256
keyInput.deviceId = DEVICE_ID_KEYBOARD;
257
keyInput.flags = KEY_DOWN | KEY_UP;
258
switch (button) {
259
case HardwareButton::BACK:
260
keyInput.keyCode = NKCODE_BACK;
261
return NativeKey(keyInput);
262
default:
263
return false;
264
}
265
}
266
267
void PPSSPP_UWPMain::OnTouchEvent(int touchEvent, int touchId, float x, float y, double timestamp) {
268
// We get the coordinate in Windows' device independent pixels already. So let's undo that,
269
// and then apply our own "dpi".
270
float dpiFactor_x = m_deviceResources->GetActualDpi() / 96.0f;
271
float dpiFactor_y = dpiFactor_x;
272
dpiFactor_x /= g_display.pixel_in_dps_x;
273
dpiFactor_y /= g_display.pixel_in_dps_y;
274
275
TouchInput input{};
276
input.id = touchId;
277
input.x = x * dpiFactor_x;
278
input.y = y * dpiFactor_y;
279
input.flags = touchEvent;
280
input.timestamp = timestamp;
281
NativeTouch(input);
282
283
KeyInput key{};
284
key.deviceId = DEVICE_ID_MOUSE;
285
if (touchEvent & TOUCH_DOWN) {
286
key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
287
key.flags = KEY_DOWN;
288
NativeKey(key);
289
}
290
if (touchEvent & TOUCH_UP) {
291
key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
292
key.flags = KEY_UP;
293
NativeKey(key);
294
}
295
}
296
297
void PPSSPP_UWPMain::OnSuspend() {
298
// TODO
299
}
300
301
302
UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr<DX::DeviceResources> resources) {
303
std::vector<std::string> adapterNames = resources->GetAdapters();
304
305
draw_ = Draw::T3DCreateD3D11Context(
306
resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetSwapChain(), resources->GetDeviceFeatureLevel(), 0, adapterNames, g_Config.iInflightFrames);
307
bool success = draw_->CreatePresets();
308
_assert_(success);
309
}
310
311
void UWPGraphicsContext::Shutdown() {
312
delete draw_;
313
}
314
315
std::string System_GetProperty(SystemProperty prop) {
316
static bool hasCheckedGPUDriverVersion = false;
317
switch (prop) {
318
case SYSPROP_NAME:
319
return GetSystemName();
320
case SYSPROP_SYSTEMBUILD:
321
return GetWindowsBuild();
322
case SYSPROP_LANGREGION:
323
return GetLangRegion();
324
case SYSPROP_CLIPBOARD_TEXT:
325
/* TODO: Need to either change this API or do this on a thread in an ugly fashion.
326
DataPackageView ^view = Clipboard::GetContent();
327
if (view) {
328
string text = await view->GetTextAsync();
329
}
330
*/
331
return "";
332
case SYSPROP_GPUDRIVER_VERSION:
333
return "";
334
case SYSPROP_BUILD_VERSION:
335
return PPSSPP_GIT_VERSION;
336
default:
337
return "";
338
}
339
}
340
341
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
342
std::vector<std::string> result;
343
switch (prop) {
344
case SYSPROP_TEMP_DIRS:
345
{
346
std::wstring tempPath(MAX_PATH, '\0');
347
size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
348
if (sz >= tempPath.size()) {
349
tempPath.resize(sz);
350
sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
351
}
352
// Need to resize off the null terminator either way.
353
tempPath.resize(sz);
354
result.push_back(ConvertWStringToUTF8(tempPath));
355
return result;
356
}
357
358
default:
359
return result;
360
}
361
}
362
363
extern AudioBackend *g_audioBackend;
364
365
int64_t System_GetPropertyInt(SystemProperty prop) {
366
switch (prop) {
367
case SYSPROP_AUDIO_SAMPLE_RATE:
368
return g_audioBackend ? g_audioBackend->SampleRate() : -1;
369
370
case SYSPROP_DEVICE_TYPE:
371
{
372
if (IsMobile()) {
373
return DEVICE_TYPE_MOBILE;
374
} else if (IsXBox()) {
375
return DEVICE_TYPE_TV;
376
} else {
377
return DEVICE_TYPE_DESKTOP;
378
}
379
}
380
case SYSPROP_DISPLAY_XRES:
381
{
382
CoreWindow^ corewindow = CoreWindow::GetForCurrentThread();
383
if (corewindow) {
384
return (int)corewindow->Bounds.Width;
385
}
386
}
387
case SYSPROP_DISPLAY_YRES:
388
{
389
CoreWindow^ corewindow = CoreWindow::GetForCurrentThread();
390
if (corewindow) {
391
return (int)corewindow->Bounds.Height;
392
}
393
}
394
default:
395
return -1;
396
}
397
}
398
399
float System_GetPropertyFloat(SystemProperty prop) {
400
switch (prop) {
401
case SYSPROP_DISPLAY_REFRESH_RATE:
402
return 60.f;
403
case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
404
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
405
case SYSPROP_DISPLAY_SAFE_INSET_TOP:
406
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
407
return 0.0f;
408
default:
409
return -1;
410
}
411
}
412
413
void System_Toast(std::string_view str) {}
414
415
bool System_GetPropertyBool(SystemProperty prop) {
416
switch (prop) {
417
case SYSPROP_HAS_TEXT_CLIPBOARD:
418
case SYSPROP_HAS_OPEN_DIRECTORY:
419
{
420
return !IsXBox();
421
}
422
case SYSPROP_HAS_FILE_BROWSER:
423
return true;
424
case SYSPROP_HAS_FOLDER_BROWSER:
425
return true;
426
case SYSPROP_HAS_IMAGE_BROWSER:
427
return true; // we just use the file browser
428
case SYSPROP_HAS_BACK_BUTTON:
429
return true;
430
case SYSPROP_HAS_ACCELEROMETER:
431
return IsMobile();
432
case SYSPROP_APP_GOLD:
433
#ifdef GOLD
434
return true;
435
#else
436
return false;
437
#endif
438
case SYSPROP_CAN_JIT:
439
return true;
440
case SYSPROP_HAS_KEYBOARD:
441
{
442
// Do actual check
443
// touch devices has input pane, we need to depend on it
444
// I don't know any possible way to display input dialog in non-xaml apps
445
return isKeyboardAvailable() || isTouchAvailable();
446
}
447
case SYSPROP_DEBUGGER_PRESENT:
448
return IsDebuggerPresent();
449
case SYSPROP_OK_BUTTON_LEFT:
450
return true;
451
default:
452
return false;
453
}
454
}
455
456
void System_Notify(SystemNotification notification) {}
457
458
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) {
459
switch (type) {
460
461
case SystemRequestType::EXIT_APP:
462
{
463
bool state = false;
464
ExecuteTask(state, Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->TryConsolidateAsync());
465
if (!state) {
466
// Notify the user?
467
}
468
return true;
469
}
470
case SystemRequestType::RESTART_APP:
471
{
472
Windows::ApplicationModel::Core::AppRestartFailureReason error;
473
ExecuteTask(error, Windows::ApplicationModel::Core::CoreApplication::RequestRestartAsync(nullptr));
474
if (error != Windows::ApplicationModel::Core::AppRestartFailureReason::RestartPending) {
475
// Shutdown
476
System_MakeRequest(SystemRequestType::EXIT_APP, requestId, param1, param2, param3, param4);
477
}
478
return true;
479
}
480
case SystemRequestType::BROWSE_FOR_IMAGE:
481
{
482
std::vector<std::string> supportedExtensions = { ".jpg", ".png" };
483
484
//Call file picker
485
ChooseFile(supportedExtensions).then([requestId](std::string filePath) {
486
if (filePath.size() > 1) {
487
g_requestManager.PostSystemSuccess(requestId, filePath.c_str());
488
}
489
else {
490
g_requestManager.PostSystemFailure(requestId);
491
}
492
});
493
return true;
494
}
495
case SystemRequestType::BROWSE_FOR_FILE:
496
{
497
std::vector<std::string> supportedExtensions = {};
498
switch ((BrowseFileType)param3) {
499
case BrowseFileType::BOOTABLE:
500
supportedExtensions = { ".cso", ".iso", ".chd", ".elf", ".pbp", ".zip", ".prx", ".bin" }; // should .bin even be here?
501
break;
502
case BrowseFileType::INI:
503
supportedExtensions = { ".ini" };
504
break;
505
case BrowseFileType::ZIP:
506
supportedExtensions = { ".zip" };
507
break;
508
case BrowseFileType::SYMBOL_MAP:
509
supportedExtensions = { ".ppmap" };
510
break;
511
case BrowseFileType::SYMBOL_MAP_NOCASH:
512
supportedExtensions = { ".sym" };
513
break;
514
case BrowseFileType::DB:
515
supportedExtensions = { ".db" };
516
break;
517
case BrowseFileType::SOUND_EFFECT:
518
supportedExtensions = { ".wav", ".mp3" };
519
break;
520
case BrowseFileType::ATRAC3:
521
supportedExtensions = { ".at3" };
522
break;
523
case BrowseFileType::ANY:
524
// 'ChooseFile' will added '*' by default when there are no extensions assigned
525
break;
526
default:
527
ERROR_LOG(Log::FileSystem, "Unexpected BrowseFileType: %d", param3);
528
return false;
529
}
530
531
//Call file picker
532
ChooseFile(supportedExtensions).then([requestId](std::string filePath) {
533
if (filePath.size() > 1) {
534
g_requestManager.PostSystemSuccess(requestId, filePath.c_str());
535
}
536
else {
537
g_requestManager.PostSystemFailure(requestId);
538
}
539
});
540
541
return true;
542
}
543
case SystemRequestType::BROWSE_FOR_FOLDER:
544
{
545
ChooseFolder().then([requestId](std::string folderPath) {
546
if (folderPath.size() > 1) {
547
g_requestManager.PostSystemSuccess(requestId, folderPath.c_str());
548
}
549
else {
550
g_requestManager.PostSystemFailure(requestId);
551
}
552
});
553
return true;
554
}
555
case SystemRequestType::NOTIFY_UI_EVENT:
556
{
557
switch ((UIEventNotification)param3) {
558
case UIEventNotification::MENU_RETURN:
559
CloseLaunchItem();
560
break;
561
case UIEventNotification::POPUP_CLOSED:
562
DeactivateTextEditInput();
563
break;
564
case UIEventNotification::TEXT_GOTFOCUS:
565
ActivateTextEditInput(true);
566
break;
567
case UIEventNotification::TEXT_LOSTFOCUS:
568
DeactivateTextEditInput(true);
569
break;
570
default:
571
break;
572
}
573
return true;
574
}
575
case SystemRequestType::COPY_TO_CLIPBOARD:
576
{
577
auto dataPackage = ref new DataPackage();
578
dataPackage->RequestedOperation = DataPackageOperation::Copy;
579
dataPackage->SetText(ToPlatformString(param1));
580
Clipboard::SetContent(dataPackage);
581
return true;
582
}
583
case SystemRequestType::TOGGLE_FULLSCREEN_STATE:
584
{
585
auto view = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
586
bool flag = !view->IsFullScreenMode;
587
if (param1 == "0") {
588
flag = false;
589
} else if (param1 == "1"){
590
flag = true;
591
}
592
if (flag) {
593
view->TryEnterFullScreenMode();
594
} else {
595
view->ExitFullScreenMode();
596
}
597
return true;
598
}
599
case SystemRequestType::SHOW_FILE_IN_FOLDER:
600
OpenFolder(param1);
601
return true;
602
default:
603
return false;
604
}
605
}
606
607
void System_LaunchUrl(LaunchUrlType urlType, const char *url) {
608
auto uri = ref new Windows::Foundation::Uri(ToPlatformString(url));
609
610
create_task(Windows::System::Launcher::LaunchUriAsync(uri)).then([](bool b) {});
611
}
612
613
void System_Vibrate(int length_ms) {
614
#if _M_ARM
615
if (length_ms == -1 || length_ms == -3)
616
length_ms = 50;
617
else if (length_ms == -2)
618
length_ms = 25;
619
else
620
return;
621
622
auto timeSpan = Windows::Foundation::TimeSpan();
623
timeSpan.Duration = length_ms * 10000;
624
// TODO: Can't use this?
625
// Windows::Phone::Devices::Notification::VibrationDevice::GetDefault()->Vibrate(timeSpan);
626
#endif
627
}
628
629
void System_AskForPermission(SystemPermission permission) {
630
// Do nothing
631
}
632
633
PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
634
return PERMISSION_STATUS_GRANTED;
635
}
636
637
std::string GetCPUBrandString() {
638
Platform::String^ cpu_id = nullptr;
639
Platform::String^ cpu_name = nullptr;
640
641
// GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0}
642
Platform::String^ if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\"";
643
644
// Enumerate all CPU DeviceInterfaces, and get DeviceInstanceID of the first one.
645
auto if_task = create_task(
646
DeviceInformation::FindAllAsync(if_filter)).then([&](DeviceInformationCollection ^ collection) {
647
if (collection->Size > 0) {
648
auto cpu = collection->GetAt(0);
649
auto id = cpu->Properties->Lookup(L"System.Devices.DeviceInstanceID");
650
cpu_id = dynamic_cast<Platform::String^>(id);
651
}
652
});
653
654
try {
655
if_task.wait();
656
}
657
catch (const std::exception & e) {
658
const char* what = e.what();
659
INFO_LOG(Log::System, "%s", what);
660
}
661
662
if (cpu_id != nullptr) {
663
// Get the Device with the same ID as the DeviceInterface
664
// Then get the name (description) of that Device
665
// We have to do this because the DeviceInterface we get doesn't have a proper description.
666
Platform::String^ dev_filter = L"System.Devices.DeviceInstanceID:=\"" + cpu_id + L"\"";
667
668
auto dev_task = create_task(
669
DeviceInformation::FindAllAsync(dev_filter, {}, DeviceInformationKind::Device)).then(
670
[&](DeviceInformationCollection ^ collection) {
671
if (collection->Size > 0) {
672
cpu_name = collection->GetAt(0)->Name;
673
}
674
});
675
676
try {
677
dev_task.wait();
678
}
679
catch (const std::exception & e) {
680
const char* what = e.what();
681
INFO_LOG(Log::System, "%s", what);
682
}
683
}
684
685
if (cpu_name != nullptr) {
686
return FromPlatformString(cpu_name);
687
} else {
688
return "Unknown";
689
}
690
}
691
692