Merge pull request #9822 from driver1998/uwp-fixes

Multiple UWP Fixes
This commit is contained in:
Twinaphex 2019-12-05 23:38:47 +01:00 committed by GitHub
commit e61fc4e427
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 301 additions and 110 deletions

View File

@ -97,7 +97,7 @@
#define DEFAULT_MOUSE_SCALE 1 #define DEFAULT_MOUSE_SCALE 1
#endif #endif
#if defined(RARCH_MOBILE) || defined(HAVE_LIBNX) #if defined(RARCH_MOBILE) || defined(HAVE_LIBNX) || defined(__WINRT__)
#define DEFAULT_POINTER_ENABLE true #define DEFAULT_POINTER_ENABLE true
#else #else
#define DEFAULT_POINTER_ENABLE false #define DEFAULT_POINTER_ENABLE false

View File

@ -75,6 +75,9 @@ static void frontend_uwp_get_os(char *s, size_t len, int *major, int *minor)
case PROCESSOR_ARCHITECTURE_ARM: case PROCESSOR_ARCHITECTURE_ARM:
arch = "ARM"; arch = "ARM";
break; break;
case PROCESSOR_ARCHITECTURE_ARM64:
arch = "ARM64";
break;
default: default:
break; break;
} }
@ -257,6 +260,9 @@ enum frontend_architecture frontend_uwp_get_architecture(void)
case PROCESSOR_ARCHITECTURE_ARM: case PROCESSOR_ARCHITECTURE_ARM:
return FRONTEND_ARCH_ARM; return FRONTEND_ARCH_ARM;
break; break;
case PROCESSOR_ARCHITECTURE_ARM64:
return FRONTEND_ARCH_ARMV8;
break;
default: default:
break; break;
} }
@ -327,65 +333,73 @@ static void frontend_uwp_environment_get(int *argc, char *argv[],
/* On UWP, we have to use the writable directory /* On UWP, we have to use the writable directory
* instead of the install directory. */ * instead of the install directory. */
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_ASSETS], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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 #ifdef HAVE_VIDEO_LAYOUT
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_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 #endif
/* This one is an exception: cores have to be loaded from /* This one is an exception: cores have to be loaded from
* the install directory, * the install directory,
* since this is the only place UWP apps can take .dlls from */ * since this is the only place UWP apps can take .dlls from */
fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE], 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], 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], 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], 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], 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], 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], 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], 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], 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], 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 #ifdef HAVE_MENU
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(HAVE_OPENGL_CORE) #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"); sizeof(g_defaults.settings.menu), "xmb");
}
#endif #endif
#endif #endif
} }
@ -424,6 +438,16 @@ static uint64_t frontend_uwp_get_mem_used(void)
#endif #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_ctx_driver_t frontend_ctx_uwp = {
frontend_uwp_environment_get, frontend_uwp_environment_get,
frontend_uwp_init, frontend_uwp_init,
@ -451,7 +475,7 @@ frontend_ctx_driver_t frontend_ctx_uwp = {
NULL, /* watch_path_for_changes */ NULL, /* watch_path_for_changes */
NULL, /* check_for_path_changes */ NULL, /* check_for_path_changes */
NULL, /* set_sustained_performance_mode */ NULL, /* set_sustained_performance_mode */
NULL, /* get_cpu_model_name */ frontend_uwp_get_cpu_model_name,
NULL, /* get_user_language */ frontend_uwp_get_user_language,
"uwp" "uwp"
}; };

View File

@ -55,62 +55,10 @@ using namespace Windows::Storage::FileProperties;
#include <string/stdstring.h> #include <string/stdstring.h>
#include <retro_environment.h> #include <retro_environment.h>
#include <uwp/uwp_func.h> #include <uwp/uwp_func.h>
#include <uwp/uwp_async.h>
namespace 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<typename T>
T RunAsync(std::function<concurrency::task<T>()> func)
{
volatile bool finished = false;
Platform::Exception^ exception = nullptr;
T result;
func().then([&finished, &exception, &result](concurrency::task<T> 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<typename T>
T RunAsyncAndCatchErrors(std::function<concurrency::task<T>()> func, T valueOnError)
{
try
{
return RunAsync<T>(func);
}
catch (Platform::Exception^ e)
{
return valueOnError;
}
}
void windowsize_path(wchar_t* path) void windowsize_path(wchar_t* path)
{ {
/* UWP deals with paths containing / instead of \ way worse than normal Windows */ /* UWP deals with paths containing / instead of \ way worse than normal Windows */

View File

@ -293,6 +293,7 @@
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\uwp\uwp_async.h" />
<ClInclude Include="..\..\..\uwp\uwp_func.h" /> <ClInclude Include="..\..\..\uwp\uwp_func.h" />
<ClInclude Include="..\..\..\uwp\uwp_main.h" /> <ClInclude Include="..\..\..\uwp\uwp_main.h" />
</ItemGroup> </ItemGroup>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="uwp"> <Filter Include="uwp">
@ -29,6 +29,9 @@
<ClInclude Include="..\..\..\uwp\uwp_main.h"> <ClInclude Include="..\..\..\uwp\uwp_main.h">
<Filter>uwp</Filter> <Filter>uwp</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\..\uwp\uwp_async.h">
<Filter>uwp</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\griffin\griffin.c"> <ClCompile Include="..\..\..\griffin\griffin.c">

View File

@ -2259,6 +2259,10 @@ void dir_check_defaults(void)
*/ */
#if defined(ORBIS) || defined(ANDROID) #if defined(ORBIS) || defined(ANDROID)
if (path_is_valid("host0:app/custom.ini")) 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 #else
if (path_is_valid("custom.ini")) if (path_is_valid("custom.ini"))
#endif #endif

86
uwp/uwp_async.h Normal file
View File

@ -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 <ppl.h>
#include <ppltasks.h>
#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<typename T>
T RunAsync(std::function<concurrency::task<T>()> func)
{
volatile bool finished = false;
Platform::Exception^ exception = nullptr;
T result;
func().then([&finished, &exception, &result](concurrency::task<T> 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<typename T>
T RunAsyncAndCatchErrors(std::function<concurrency::task<T>()> func, T valueOnError)
{
try
{
return RunAsync<T>(func);
}
catch (Platform::Exception ^ e)
{
return valueOnError;
}
}
}
#endif
#endif

View File

@ -36,6 +36,8 @@ void uwp_input_next_frame(void);
bool uwp_keyboard_pressed(unsigned key); bool uwp_keyboard_pressed(unsigned key);
int16_t uwp_mouse_state(unsigned port, unsigned id, bool screen); int16_t uwp_mouse_state(unsigned port, unsigned id, bool screen);
int16_t uwp_pointer_state(unsigned idx, 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); void uwp_fill_installed_core_packages(struct string_list *list);

View File

@ -23,9 +23,11 @@
#include "../libretro-common/include/encodings/utf.h" #include "../libretro-common/include/encodings/utf.h"
#include "../libretro-common/include/lists/string_list.h" #include "../libretro-common/include/lists/string_list.h"
#include "uwp_func.h" #include "uwp_func.h"
#include "uwp_async.h"
#include <ppltasks.h> #include <ppltasks.h>
#include <collection.h> #include <collection.h>
#include <windows.devices.enumeration.h>
using namespace RetroArchUWP; using namespace RetroArchUWP;
@ -42,10 +44,12 @@ using namespace Windows::System::Profile;
using namespace Windows::Foundation; using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections; using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display;
using namespace Windows::Devices::Enumeration;
char uwp_dir_install[PATH_MAX_LENGTH]; char uwp_dir_install[PATH_MAX_LENGTH];
char uwp_dir_data[PATH_MAX_LENGTH]; char uwp_dir_data[PATH_MAX_LENGTH];
char uwp_device_family[128]; 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 // Some keys are unavailable in the VirtualKey enum (wtf) but the old-style constants work
const struct rarch_key_map rarch_key_map_uwp[] = { 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 } { 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 { struct uwp_input_state_t {
short mouse_screen_x; short mouse_screen_x;
short mouse_screen_y; short mouse_screen_y;
@ -174,15 +186,20 @@ struct uwp_input_state_t {
bool mouse_button5; bool mouse_button5;
short mouse_wheel_left; short mouse_wheel_left;
short mouse_wheel_up; short mouse_wheel_up;
short touch_screen_x; unsigned touch_count;
short touch_screen_y; struct input_pointer touch[MAX_TOUCH];
short touch_rel_x;
short touch_rel_y;
bool touch_touched;
}; };
struct uwp_input_state_t uwp_current_input, uwp_next_input; 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. // The main function is only used to initialize our IFrameworkView class.
[Platform::MTAThread] [Platform::MTAThread]
int main(Platform::Array<Platform::String^>^) int main(Platform::Array<Platform::String^>^)
@ -417,6 +434,9 @@ void App::OnKey(CoreWindow^ sender, KeyEventArgs^ args)
void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args) void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args)
{ {
float dpi = DisplayInformation::GetForCurrentView()->LogicalDpi;
if (args->CurrentPoint->PointerDevice->PointerDeviceType == PointerDeviceType::Mouse) if (args->CurrentPoint->PointerDevice->PointerDeviceType == PointerDeviceType::Mouse)
{ {
uwp_next_input.mouse_left = args->CurrentPoint->Properties->IsLeftButtonPressed; 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_right = args->CurrentPoint->Properties->IsRightButtonPressed;
uwp_next_input.mouse_button4 = args->CurrentPoint->Properties->IsXButton1Pressed; uwp_next_input.mouse_button4 = args->CurrentPoint->Properties->IsXButton1Pressed;
uwp_next_input.mouse_button5 = args->CurrentPoint->Properties->IsXButton2Pressed; uwp_next_input.mouse_button5 = args->CurrentPoint->Properties->IsXButton2Pressed;
uwp_next_input.mouse_screen_x = args->CurrentPoint->Position.X; uwp_next_input.mouse_screen_x = ConvertDipsToPixels(args->CurrentPoint->Position.X, dpi);
uwp_next_input.mouse_screen_y = args->CurrentPoint->Position.Y; 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_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; uwp_next_input.mouse_rel_y = uwp_next_input.mouse_screen_y - uwp_current_input.mouse_screen_y;
if (args->CurrentPoint->Properties->IsHorizontalMouseWheel) if (args->CurrentPoint->Properties->IsHorizontalMouseWheel)
@ -435,11 +455,53 @@ void App::OnPointer(CoreWindow^ sender, PointerEventArgs^ args)
} }
else else
{ {
uwp_next_input.touch_touched = args->CurrentPoint->IsInContact; unsigned i, free_index = MAX_TOUCH; bool found = false;
uwp_next_input.touch_screen_x = args->CurrentPoint->Position.X; int id = args->CurrentPoint->PointerId;
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; for (i = 0; i < uwp_next_input.touch_count; i++)
uwp_next_input.touch_rel_y = uwp_next_input.touch_screen_y - uwp_current_input.touch_screen_y; {
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 // Implement UWP equivalents of various win32_* functions
extern "C" { extern "C" {
@ -562,13 +617,12 @@ extern "C" {
uwp_next_input.mouse_rel_y = 0; uwp_next_input.mouse_rel_y = 0;
uwp_next_input.mouse_wheel_up %= WHEEL_DELTA; uwp_next_input.mouse_wheel_up %= WHEEL_DELTA;
uwp_next_input.mouse_wheel_left %= 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) 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(); CoreWindow^ window = CoreWindow::GetForCurrentThread();
if (!window) 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. // 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 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) int16_t uwp_mouse_state(unsigned port, unsigned id, bool screen)
@ -612,17 +666,18 @@ extern "C" {
return 0; 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) int16_t uwp_pointer_state(unsigned idx, unsigned id, bool screen)
{ {
switch (id) switch (id)
{ {
case RETRO_DEVICE_ID_POINTER_X: 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: 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: 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: default:
break; break;
} }
@ -634,4 +689,72 @@ extern "C" {
{ {
Windows::System::Launcher::LaunchUriAsync(ref new Uri("ms-settings:privacy-broadfilesystemaccess")); 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<Platform::String^>([&]() {
return create_task(DeviceInformation::FindAllAsync(if_filter)).then(
[&](DeviceInformationCollection^ collection)
{
return dynamic_cast<Platform::String^>(
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<Platform::String^>([&]() {
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";
}
} }