diff --git a/config.def.h b/config.def.h index 6494b48a72..42ce20c1d0 100644 --- a/config.def.h +++ b/config.def.h @@ -97,7 +97,7 @@ #define DEFAULT_MOUSE_SCALE 1 #endif -#if defined(RARCH_MOBILE) || defined(HAVE_LIBNX) +#if defined(RARCH_MOBILE) || defined(HAVE_LIBNX) || defined(__WINRT__) #define DEFAULT_POINTER_ENABLE true #else #define DEFAULT_POINTER_ENABLE false diff --git a/frontend/drivers/platform_uwp.c b/frontend/drivers/platform_uwp.c index 4e61c17bd5..9bc96514f4 100644 --- a/frontend/drivers/platform_uwp.c +++ b/frontend/drivers/platform_uwp.c @@ -75,6 +75,9 @@ static void frontend_uwp_get_os(char *s, size_t len, int *major, int *minor) case PROCESSOR_ARCHITECTURE_ARM: arch = "ARM"; break; + case PROCESSOR_ARCHITECTURE_ARM64: + arch = "ARM64"; + break; default: break; } @@ -257,6 +260,9 @@ enum frontend_architecture frontend_uwp_get_architecture(void) case PROCESSOR_ARCHITECTURE_ARM: return FRONTEND_ARCH_ARM; break; + case PROCESSOR_ARCHITECTURE_ARM64: + return FRONTEND_ARCH_ARMV8; + break; default: break; } @@ -327,65 +333,73 @@ static void frontend_uwp_environment_get(int *argc, char *argv[], /* On UWP, we have to use the writable directory * instead of the install directory. */ fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_ASSETS], - "~\\assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); + "~\\assets\\", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER], - "~\\filters\\audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER])); + "~\\filters\\audio\\", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], - "~\\filters\\video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER])); + "~\\filters\\video\\", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CHEATS], - "~\\cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS])); + "~\\cheats\\", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_DATABASE], - "~\\database\\rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); + "~\\database\\rdb\\", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CURSOR], - "~\\database\\cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); + "~\\database\\cursors\\", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], - "~\\playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); + "~\\playlists\\", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG], - "~\\config\\record", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG])); + "~\\config\\record\\", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT], - "~\\recordings", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT])); + "~\\recordings\\", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], - "~\\config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); + "~\\config\\", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_REMAP], - "~\\config\\remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); + "~\\config\\remaps\\", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS], - "~\\assets\\wallpapers", sizeof(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS])); + "~\\assets\\wallpapers\\", sizeof(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS], - "~\\thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS])); + "~\\thumbnails\\", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_OVERLAY], - "~\\overlays", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY])); + "~\\overlays\\", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY])); #ifdef HAVE_VIDEO_LAYOUT fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT], - "~\\layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT])); + "~\\layouts\\", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT])); #endif /* This one is an exception: cores have to be loaded from * the install directory, * since this is the only place UWP apps can take .dlls from */ fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE], - ":\\cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); + ":\\cores\\", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], - "~\\info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); + "~\\info\\", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG], - "~\\autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG])); + "~\\autoconfig\\", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SHADER], - "~\\shaders", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER])); + "~\\shaders\\", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], - "~\\downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); + "~\\downloads\\", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT], - "~\\screenshots", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT])); + "~\\screenshots\\", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SRAM], - "~\\saves", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); + "~\\saves\\", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], - "~\\states", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); + "~\\states\\", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SYSTEM], - "~\\system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); + "~\\system\\", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_LOGS], - "~\\logs", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS])); + "~\\logs\\", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS])); #ifdef HAVE_MENU #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(HAVE_OPENGL_CORE) - snprintf(g_defaults.settings.menu, + if (string_is_equal(uwp_device_family, "Windows.Mobile")) + { + snprintf(g_defaults.settings.menu, + sizeof(g_defaults.settings.menu), "glui"); + } + else + { + snprintf(g_defaults.settings.menu, sizeof(g_defaults.settings.menu), "xmb"); + } #endif #endif } @@ -424,6 +438,16 @@ static uint64_t frontend_uwp_get_mem_used(void) #endif } +enum retro_language frontend_uwp_get_user_language(void) +{ + return uwp_get_language(); +} + +static const char* frontend_uwp_get_cpu_model_name(void) +{ + return uwp_get_cpu_model_name(); +} + frontend_ctx_driver_t frontend_ctx_uwp = { frontend_uwp_environment_get, frontend_uwp_init, @@ -451,7 +475,7 @@ frontend_ctx_driver_t frontend_ctx_uwp = { NULL, /* watch_path_for_changes */ NULL, /* check_for_path_changes */ NULL, /* set_sustained_performance_mode */ - NULL, /* get_cpu_model_name */ - NULL, /* get_user_language */ + frontend_uwp_get_cpu_model_name, + frontend_uwp_get_user_language, "uwp" }; diff --git a/libretro-common/vfs/vfs_implementation_uwp.cpp b/libretro-common/vfs/vfs_implementation_uwp.cpp index f0acba472e..f989000354 100644 --- a/libretro-common/vfs/vfs_implementation_uwp.cpp +++ b/libretro-common/vfs/vfs_implementation_uwp.cpp @@ -55,62 +55,10 @@ using namespace Windows::Storage::FileProperties; #include #include #include +#include namespace { - /* Dear Microsoft - * I really appreciate all the effort you took to not provide any - * synchronous file APIs and block all attempts to synchronously - * wait for the results of async tasks for "smooth user experience", - * but I'm not going to run and rewrite all RetroArch cores for - * async I/O. I hope you like this hack I made instead. - */ - template - T RunAsync(std::function()> func) - { - volatile bool finished = false; - Platform::Exception^ exception = nullptr; - T result; - - func().then([&finished, &exception, &result](concurrency::task t) { - try - { - result = t.get(); - } - catch (Platform::Exception^ exception_) - { - exception = exception_; - } - finished = true; - }); - - /* Don't stall the UI thread - prevents a deadlock */ - Windows::UI::Core::CoreWindow^ corewindow = Windows::UI::Core::CoreWindow::GetForCurrentThread(); - while (!finished) - { - if (corewindow) { - corewindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); - } - } - - if (exception != nullptr) - throw exception; - return result; - } - - template - T RunAsyncAndCatchErrors(std::function()> func, T valueOnError) - { - try - { - return RunAsync(func); - } - catch (Platform::Exception^ e) - { - return valueOnError; - } - } - void windowsize_path(wchar_t* path) { /* UWP deals with paths containing / instead of \ way worse than normal Windows */ diff --git a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj index 27d1f32ef3..b5e3bfe606 100644 --- a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj +++ b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj @@ -293,6 +293,7 @@ + diff --git a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj.filters b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj.filters index 64fc8eb8a8..7047509d75 100644 --- a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj.filters +++ b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/RetroArch-msvc2017-UWP.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -29,6 +29,9 @@ uwp + + uwp + diff --git a/retroarch.c b/retroarch.c index 633a4a4c9f..6025a061c2 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2259,6 +2259,10 @@ void dir_check_defaults(void) */ #if defined(ORBIS) || defined(ANDROID) if (path_is_valid("host0:app/custom.ini")) +#elif defined(__WINRT__) + char path[MAX_PATH]; + fill_pathname_expand_special(path, "~\\custom.ini", MAX_PATH); + if (path_is_valid(path)) #else if (path_is_valid("custom.ini")) #endif diff --git a/uwp/uwp_async.h b/uwp/uwp_async.h new file mode 100644 index 0000000000..f4c6b7fc0a --- /dev/null +++ b/uwp/uwp_async.h @@ -0,0 +1,86 @@ +/* Copyright (C) 2018-2019 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (uwp_async.h). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include "uwp_main.h" +#include "uwp_func.h" + +#ifdef __cplusplus +#ifdef __cplusplus_winrt +namespace +{ + /* Dear Microsoft + * I really appreciate all the effort you took to not provide any + * synchronous file APIs and block all attempts to synchronously + * wait for the results of async tasks for "smooth user experience", + * but I'm not going to run and rewrite all RetroArch cores for + * async I/O. I hope you like this hack I made instead. + */ + template + T RunAsync(std::function()> func) + { + volatile bool finished = false; + Platform::Exception^ exception = nullptr; + T result; + + func().then([&finished, &exception, &result](concurrency::task t) { + try + { + result = t.get(); + } + catch (Platform::Exception ^ exception_) + { + exception = exception_; + } + finished = true; + }); + + /* Don't stall the UI thread - prevents a deadlock */ + Windows::UI::Core::CoreWindow^ corewindow = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + while (!finished) + { + if (corewindow) { + corewindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); + } + } + + if (exception != nullptr) + throw exception; + return result; + } + + template + T RunAsyncAndCatchErrors(std::function()> func, T valueOnError) + { + try + { + return RunAsync(func); + } + catch (Platform::Exception ^ e) + { + return valueOnError; + } + } +} +#endif +#endif diff --git a/uwp/uwp_func.h b/uwp/uwp_func.h index 2e5c8ee6a4..eaeeedd6d5 100644 --- a/uwp/uwp_func.h +++ b/uwp/uwp_func.h @@ -36,6 +36,8 @@ void uwp_input_next_frame(void); bool uwp_keyboard_pressed(unsigned key); int16_t uwp_mouse_state(unsigned port, unsigned id, bool screen); int16_t uwp_pointer_state(unsigned idx, unsigned id, bool screen); +const char* uwp_get_cpu_model_name(); +enum retro_language uwp_get_language(); void uwp_fill_installed_core_packages(struct string_list *list); diff --git a/uwp/uwp_main.cpp b/uwp/uwp_main.cpp index d6e386a1af..ad84d650fe 100644 --- a/uwp/uwp_main.cpp +++ b/uwp/uwp_main.cpp @@ -23,9 +23,11 @@ #include "../libretro-common/include/encodings/utf.h" #include "../libretro-common/include/lists/string_list.h" #include "uwp_func.h" +#include "uwp_async.h" #include #include +#include using namespace RetroArchUWP; @@ -42,10 +44,12 @@ using namespace Windows::System::Profile; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::Graphics::Display; +using namespace Windows::Devices::Enumeration; char uwp_dir_install[PATH_MAX_LENGTH]; char uwp_dir_data[PATH_MAX_LENGTH]; char uwp_device_family[128]; +char win32_cpu_model_name[128] = { 0 }; // Some keys are unavailable in the VirtualKey enum (wtf) but the old-style constants work const struct rarch_key_map rarch_key_map_uwp[] = { @@ -162,6 +166,14 @@ const struct rarch_key_map rarch_key_map_uwp[] = { { 0, RETROK_UNKNOWN } }; +#define MAX_TOUCH 16 +struct input_pointer { + int id; + bool isInContact; + short x, y; + short full_x, full_y; +}; + struct uwp_input_state_t { short mouse_screen_x; short mouse_screen_y; @@ -174,15 +186,20 @@ struct uwp_input_state_t { bool mouse_button5; short mouse_wheel_left; short mouse_wheel_up; - short touch_screen_x; - short touch_screen_y; - short touch_rel_x; - short touch_rel_y; - bool touch_touched; + unsigned touch_count; + struct input_pointer touch[MAX_TOUCH]; }; struct uwp_input_state_t uwp_current_input, uwp_next_input; + +// Taken from DirectX UWP samples - on Xbox, everything is scaled 200% so getting the DPI calculation correct is crucial +static inline float ConvertDipsToPixels(float dips, float dpi) +{ + static const float dipsPerInch = 96.0f; + return floorf(dips * dpi / dipsPerInch + 0.5f); +} + // The main function is only used to initialize our IFrameworkView class. [Platform::MTAThread] int main(Platform::Array^) @@ -417,6 +434,9 @@ void App::OnKey(CoreWindow^ sender, KeyEventArgs^ args) void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args) { + + float dpi = DisplayInformation::GetForCurrentView()->LogicalDpi; + if (args->CurrentPoint->PointerDevice->PointerDeviceType == PointerDeviceType::Mouse) { uwp_next_input.mouse_left = args->CurrentPoint->Properties->IsLeftButtonPressed; @@ -424,8 +444,8 @@ void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args) uwp_next_input.mouse_right = args->CurrentPoint->Properties->IsRightButtonPressed; uwp_next_input.mouse_button4 = args->CurrentPoint->Properties->IsXButton1Pressed; uwp_next_input.mouse_button5 = args->CurrentPoint->Properties->IsXButton2Pressed; - uwp_next_input.mouse_screen_x = args->CurrentPoint->Position.X; - uwp_next_input.mouse_screen_y = args->CurrentPoint->Position.Y; + uwp_next_input.mouse_screen_x = ConvertDipsToPixels(args->CurrentPoint->Position.X, dpi); + uwp_next_input.mouse_screen_y = ConvertDipsToPixels(args->CurrentPoint->Position.Y, dpi); uwp_next_input.mouse_rel_x = uwp_next_input.mouse_screen_x - uwp_current_input.mouse_screen_x; uwp_next_input.mouse_rel_y = uwp_next_input.mouse_screen_y - uwp_current_input.mouse_screen_y; if (args->CurrentPoint->Properties->IsHorizontalMouseWheel) @@ -435,11 +455,53 @@ void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args) } else { - uwp_next_input.touch_touched = args->CurrentPoint->IsInContact; - uwp_next_input.touch_screen_x = args->CurrentPoint->Position.X; - uwp_next_input.touch_screen_y = args->CurrentPoint->Position.Y; - uwp_next_input.touch_rel_x = uwp_next_input.touch_screen_x - uwp_current_input.touch_screen_x; - uwp_next_input.touch_rel_y = uwp_next_input.touch_screen_y - uwp_current_input.touch_screen_y; + unsigned i, free_index = MAX_TOUCH; bool found = false; + int id = args->CurrentPoint->PointerId; + + for (i = 0; i < uwp_next_input.touch_count; i++) + { + if (!uwp_next_input.touch[i].isInContact && free_index == MAX_TOUCH) + free_index = i; + if (uwp_next_input.touch[i].id == id) + { + found = true; + break; + } + } + + if (!found) + { + if (free_index >= 0 && free_index < uwp_next_input.touch_count) + i = free_index; + else if (uwp_next_input.touch_count + 1 < MAX_TOUCH) + i = ++uwp_next_input.touch_count; + else + return; + } + + uwp_next_input.touch[i].id = id; + + struct video_viewport vp; + + /* convert from event coordinates to core and screen coordinates */ + vp.x = 0; + vp.y = 0; + vp.width = 0; + vp.height = 0; + vp.full_width = 0; + vp.full_height = 0; + + video_driver_translate_coord_viewport_wrap( + &vp, + ConvertDipsToPixels(args->CurrentPoint->Position.X, dpi), + ConvertDipsToPixels(args->CurrentPoint->Position.Y, dpi), + &uwp_next_input.touch[i].x, + &uwp_next_input.touch[i].y, + &uwp_next_input.touch[i].full_x, + &uwp_next_input.touch[i].full_y); + + uwp_next_input.touch[i].isInContact = args->CurrentPoint->IsInContact; + } } @@ -476,13 +538,6 @@ void App::OnPackageInstalling(PackageCatalog^ sender, PackageInstallingEventArgs } } -// Taken from DirectX UWP samples - on Xbox, everything is scaled 200% so getting the DPI calculation correct is crucial -static inline float ConvertDipsToPixels(float dips, float dpi) -{ - static const float dipsPerInch = 96.0f; - return floorf(dips * dpi / dipsPerInch + 0.5f); -} - // Implement UWP equivalents of various win32_* functions extern "C" { @@ -562,13 +617,12 @@ extern "C" { uwp_next_input.mouse_rel_y = 0; uwp_next_input.mouse_wheel_up %= WHEEL_DELTA; uwp_next_input.mouse_wheel_left %= WHEEL_DELTA; - uwp_next_input.touch_rel_x = 0; - uwp_next_input.touch_rel_y = 0; } bool uwp_keyboard_pressed(unsigned key) { - unsigned sym = rarch_keysym_lut[(enum retro_key)key]; + VirtualKey sym = (VirtualKey)rarch_keysym_lut[(enum retro_key)key]; + if (sym == VirtualKey::None) return false; CoreWindow^ window = CoreWindow::GetForCurrentThread(); if (!window) { @@ -576,7 +630,7 @@ extern "C" { // Dolphin core runs on its own CPU thread separate from the UI-thread and so we must do a check for this. return false; } - return (window->GetKeyState((VirtualKey)sym) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + return (window->GetKeyState(sym) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; } int16_t uwp_mouse_state(unsigned port, unsigned id, bool screen) @@ -612,17 +666,18 @@ extern "C" { return 0; } - // TODO: I don't have any touch-enabled Windows devices to test if this actually works int16_t uwp_pointer_state(unsigned idx, unsigned id, bool screen) { switch (id) { case RETRO_DEVICE_ID_POINTER_X: - return screen ? uwp_current_input.touch_screen_x : uwp_current_input.touch_rel_x; + return screen ? uwp_current_input.touch[idx].full_x : uwp_current_input.touch[idx].x; case RETRO_DEVICE_ID_POINTER_Y: - return screen ? uwp_current_input.touch_screen_y : uwp_current_input.touch_rel_y; + return screen ? uwp_current_input.touch[idx].full_y : uwp_current_input.touch[idx].y; case RETRO_DEVICE_ID_POINTER_PRESSED: - return uwp_current_input.touch_touched; + return uwp_current_input.touch[idx].isInContact; + case RETRO_DEVICE_ID_POINTER_COUNT: + return uwp_current_input.touch_count; default: break; } @@ -634,4 +689,72 @@ extern "C" { { Windows::System::Launcher::LaunchUriAsync(ref new Uri("ms-settings:privacy-broadfilesystemaccess")); } + + enum retro_language uwp_get_language() + { + auto lang = Windows::System::UserProfile::GlobalizationPreferences::Languages->GetAt(0); + char lang_bcp[16] = { 0 }; + char lang_iso[16] = { 0 }; + + wcstombs(lang_bcp, lang->Data(), 16); + + /* Trying to convert BCP 47 language codes to ISO 639 ones */ + string_list* split; + split = string_split(lang_bcp, "-"); + + strcat(lang_iso, split->elems[0].data); + + if (split->size >= 2) + { + strcat(lang_iso, "_"); + strcat(lang_iso, split->elems[split->size >= 3 ? 2 : 1].data); + } + free(split); + return rarch_get_language_from_iso(lang_iso); + } + + const char *uwp_get_cpu_model_name() + { + Platform::String^ cpu_id = nullptr; + Platform::String^ cpu_name = nullptr; + + /* GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0} */ + Platform::String^ if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\""; + + /* Enumerate all CPU DeviceInterfaces, and get DeviceInstanceID of the first one. */ + cpu_id = RunAsyncAndCatchErrors([&]() { + return create_task(DeviceInformation::FindAllAsync(if_filter)).then( + [&](DeviceInformationCollection^ collection) + { + return dynamic_cast( + collection->GetAt(0)->Properties->Lookup(L"System.Devices.DeviceInstanceID")); + }); + }, nullptr); + + if (cpu_id) + { + Platform::String^ dev_filter = L"System.Devices.DeviceInstanceID:=\"" + cpu_id + L"\""; + + /* Get the Device with the same ID as the DeviceInterface + * Then get the name (description) of that Device + * We have to do this because the DeviceInterface we get doesn't have a proper description. */ + cpu_name = RunAsyncAndCatchErrors([&]() { + return create_task( + DeviceInformation::FindAllAsync(dev_filter, {}, DeviceInformationKind::Device)).then( + [&](DeviceInformationCollection^ collection) + { + return cpu_name = collection->GetAt(0)->Name; + }); + }, nullptr); + } + + + if (cpu_name) + { + wcstombs(win32_cpu_model_name, cpu_name->Data(), 128); + return win32_cpu_model_name; + } + else + return "Unknown"; + } }