mirror of
https://github.com/libretro/RetroArch
synced 2025-02-19 03:40:59 +00:00
(UWP) Support access through the file picker
This commit is contained in:
parent
c8510378dc
commit
a116bb908e
@ -279,7 +279,7 @@ static int frontend_uwp_parse_drive_list(void *data, bool load_content)
|
|||||||
|
|
||||||
for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
|
for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
|
||||||
{
|
{
|
||||||
if (path_is_valid(drive))
|
if (uwp_drive_exists(drive))
|
||||||
{
|
{
|
||||||
menu_entries_append_enum(list,
|
menu_entries_append_enum(list,
|
||||||
drive,
|
drive,
|
||||||
@ -296,13 +296,22 @@ static int frontend_uwp_parse_drive_list(void *data, bool load_content)
|
|||||||
enum_idx,
|
enum_idx,
|
||||||
FILE_TYPE_DIRECTORY, 0, 0);
|
FILE_TYPE_DIRECTORY, 0, 0);
|
||||||
|
|
||||||
if (!have_any_drives && string_is_equal(uwp_device_family, "Windows.Desktop"))
|
if (!have_any_drives)
|
||||||
{
|
{
|
||||||
menu_entries_append_enum(list,
|
menu_entries_append_enum(list,
|
||||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILE_BROWSER_OPEN_PICKER),
|
||||||
msg_hash_to_str(MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
msg_hash_to_str(MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER),
|
||||||
MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS,
|
MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER,
|
||||||
MENU_SETTING_ACTION, 0, 0);
|
MENU_SETTING_ACTION, 0, 0);
|
||||||
|
|
||||||
|
if (string_is_equal(uwp_device_family, "Windows.Desktop"))
|
||||||
|
{
|
||||||
|
menu_entries_append_enum(list,
|
||||||
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
||||||
|
msg_hash_to_str(MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
||||||
|
MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS,
|
||||||
|
MENU_SETTING_ACTION, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(home_dir);
|
free(home_dir);
|
||||||
|
@ -395,6 +395,11 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
|
|||||||
"Open Windows permission settings to enable \n"
|
"Open Windows permission settings to enable \n"
|
||||||
"the broadFileSystemAccess capability.");
|
"the broadFileSystemAccess capability.");
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER:
|
||||||
|
snprintf(s, len,
|
||||||
|
"Open the system file picker to access \n"
|
||||||
|
"additional directories.");
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_FILE_BROWSER_SHADER_PRESET:
|
case MENU_ENUM_LABEL_FILE_BROWSER_SHADER_PRESET:
|
||||||
snprintf(s, len,
|
snprintf(s, len,
|
||||||
"Shader preset file.");
|
"Shader preset file.");
|
||||||
|
@ -2052,6 +2052,14 @@ MSG_HASH(
|
|||||||
MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS,
|
MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS,
|
||||||
"Open Windows file access permissions settings"
|
"Open Windows file access permissions settings"
|
||||||
)
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_LABEL_VALUE_FILE_BROWSER_OPEN_PICKER,
|
||||||
|
"Open..."
|
||||||
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_PICKER,
|
||||||
|
"Open another directory using the system file picker"
|
||||||
|
)
|
||||||
MSG_HASH(
|
MSG_HASH(
|
||||||
MENU_ENUM_LABEL_VALUE_PAUSE_LIBRETRO,
|
MENU_ENUM_LABEL_VALUE_PAUSE_LIBRETRO,
|
||||||
"Pause when menu activated"
|
"Pause when menu activated"
|
||||||
|
@ -27,8 +27,10 @@
|
|||||||
#include <wrl/implements.h>
|
#include <wrl/implements.h>
|
||||||
#include <windows.storage.streams.h>
|
#include <windows.storage.streams.h>
|
||||||
#include <robuffer.h>
|
#include <robuffer.h>
|
||||||
|
#include <collection.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
using namespace Windows::Foundation;
|
||||||
using namespace Windows::Foundation::Collections;
|
using namespace Windows::Foundation::Collections;
|
||||||
using namespace Windows::Storage;
|
using namespace Windows::Storage;
|
||||||
using namespace Windows::Storage::Streams;
|
using namespace Windows::Storage::Streams;
|
||||||
@ -78,8 +80,13 @@ namespace
|
|||||||
finished = true;
|
finished = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Don't stall the UI thread - prevents a deadlock */
|
||||||
|
Windows::UI::Core::CoreWindow^ corewindow = Windows::UI::Core::CoreWindow::GetForCurrentThread();
|
||||||
while (!finished)
|
while (!finished)
|
||||||
Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
|
{
|
||||||
|
if (corewindow)
|
||||||
|
corewindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
|
||||||
|
}
|
||||||
|
|
||||||
if (exception != nullptr)
|
if (exception != nullptr)
|
||||||
throw exception;
|
throw exception;
|
||||||
@ -115,6 +122,146 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/* Damn you, UWP, why no functions for that either */
|
||||||
|
template<typename T>
|
||||||
|
concurrency::task<T^> GetItemFromPathAsync(Platform::String^ path)
|
||||||
|
{
|
||||||
|
static_assert(false, "StorageFile and StorageFolder only");
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
concurrency::task<StorageFile^> GetItemFromPathAsync(Platform::String^ path)
|
||||||
|
{
|
||||||
|
return concurrency::create_task(StorageFile::GetFileFromPathAsync(path));
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
concurrency::task<StorageFolder^> GetItemFromPathAsync(Platform::String^ path)
|
||||||
|
{
|
||||||
|
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concurrency::task<T^> GetItemInFolderFromPathAsync(StorageFolder^ folder, Platform::String^ path)
|
||||||
|
{
|
||||||
|
static_assert(false, "StorageFile and StorageFolder only");
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
concurrency::task<StorageFile^> GetItemInFolderFromPathAsync(StorageFolder^ folder, Platform::String^ path)
|
||||||
|
{
|
||||||
|
if (path->IsEmpty())
|
||||||
|
retro_assert(false); /* Attempt to read a folder as a file - this really should have been caught earlier */
|
||||||
|
return concurrency::create_task(folder->GetFileAsync(path));
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
concurrency::task<StorageFolder^> GetItemInFolderFromPathAsync(StorageFolder^ folder, Platform::String^ path)
|
||||||
|
{
|
||||||
|
if (path->IsEmpty())
|
||||||
|
return concurrency::create_task(concurrency::create_async([folder]() { return folder; }));
|
||||||
|
return concurrency::create_task(folder->GetFolderAsync(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/* A list of all StorageFolder objects returned using from the file picker */
|
||||||
|
Platform::Collections::Vector<StorageFolder^> accessible_directories;
|
||||||
|
|
||||||
|
concurrency::task<Platform::String^> TriggerPickerAddDialog()
|
||||||
|
{
|
||||||
|
auto folderPicker = ref new Windows::Storage::Pickers::FolderPicker();
|
||||||
|
folderPicker->SuggestedStartLocation = Windows::Storage::Pickers::PickerLocationId::Desktop;
|
||||||
|
folderPicker->FileTypeFilter->Append("*");
|
||||||
|
|
||||||
|
return concurrency::create_task(folderPicker->PickSingleFolderAsync()).then([](StorageFolder^ folder) {
|
||||||
|
if (folder == nullptr)
|
||||||
|
throw ref new Platform::Exception(E_ABORT, L"Operation cancelled by user");
|
||||||
|
|
||||||
|
/* TODO: check for duplicates */
|
||||||
|
accessible_directories.Append(folder);
|
||||||
|
return folder->Path;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concurrency::task<T^> LocateStorageItem(Platform::String^ path)
|
||||||
|
{
|
||||||
|
/* Look for a matching directory we can use */
|
||||||
|
for each (StorageFolder^ folder in accessible_directories)
|
||||||
|
{
|
||||||
|
std::wstring folder_path = folder->Path->Data();
|
||||||
|
/* Could be C:\ or C:\Users\somebody - remove the trailing slash to unify them */
|
||||||
|
if (folder_path[folder_path.size() - 1] == '\\')
|
||||||
|
folder_path.erase(folder_path.size() - 1);
|
||||||
|
std::wstring file_path = path->Data();
|
||||||
|
if (file_path.find(folder_path) == 0)
|
||||||
|
{
|
||||||
|
/* Found a match */
|
||||||
|
file_path = file_path.length() > folder_path.length() ? file_path.substr(folder_path.length() + 1) : L"";
|
||||||
|
return concurrency::create_task(GetItemInFolderFromPathAsync<T>(folder, ref new Platform::String(file_path.data())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No matches - try accessing directly, and fallback to user prompt */
|
||||||
|
return concurrency::create_task(GetItemFromPathAsync<T>(path)).then([path](concurrency::task<T^> item) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
T^ storageItem = item.get();
|
||||||
|
return concurrency::create_task(concurrency::create_async([storageItem]() { return storageItem; }));
|
||||||
|
}
|
||||||
|
catch (Platform::AccessDeniedException^ e)
|
||||||
|
{
|
||||||
|
Windows::UI::Popups::MessageDialog^ dialog =
|
||||||
|
ref new Windows::UI::Popups::MessageDialog("Path \"" + path + "\" is not currently accessible. Please open any containing directory to access it.");
|
||||||
|
dialog->Commands->Append(ref new Windows::UI::Popups::UICommand("Open file picker"));
|
||||||
|
dialog->Commands->Append(ref new Windows::UI::Popups::UICommand("Cancel"));
|
||||||
|
return concurrency::create_task(dialog->ShowAsync()).then([path](Windows::UI::Popups::IUICommand^ cmd) {
|
||||||
|
if (cmd->Label == "Open file picker")
|
||||||
|
{
|
||||||
|
return TriggerPickerAddDialog().then([path](Platform::String^ added_path) {
|
||||||
|
/* Retry */
|
||||||
|
return LocateStorageItem<T>(path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw ref new Platform::Exception(E_ABORT, L"Operation cancelled by user");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
IStorageItem^ LocateStorageFileOrFolder(Platform::String^ path)
|
||||||
|
{
|
||||||
|
if (!path || path->IsEmpty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (*(path->End() - 1) == '\\')
|
||||||
|
{
|
||||||
|
/* Ends with a slash, so it's definitely a directory */
|
||||||
|
return RunAsyncAndCatchErrors<StorageFolder^>([&]() {
|
||||||
|
return concurrency::create_task(LocateStorageItem<StorageFolder>(path));
|
||||||
|
}, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No final slash - probably a file (since RetroArch usually slash-terminates dirs), but there is still a chance it's a directory */
|
||||||
|
IStorageItem^ item;
|
||||||
|
item = RunAsyncAndCatchErrors<StorageFile^>([&]() {
|
||||||
|
return concurrency::create_task(LocateStorageItem<StorageFile>(path));
|
||||||
|
}, nullptr);
|
||||||
|
if (!item)
|
||||||
|
{
|
||||||
|
item = RunAsyncAndCatchErrors<StorageFolder^>([&]() {
|
||||||
|
return concurrency::create_task(LocateStorageItem<StorageFolder>(path));
|
||||||
|
}, nullptr);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef VFS_FRONTEND
|
#ifdef VFS_FRONTEND
|
||||||
struct retro_vfs_file_handle
|
struct retro_vfs_file_handle
|
||||||
#else
|
#else
|
||||||
@ -160,7 +307,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns
|
|||||||
retro_assert(!dirpath_str->IsEmpty() && !filename_str->IsEmpty());
|
retro_assert(!dirpath_str->IsEmpty() && !filename_str->IsEmpty());
|
||||||
|
|
||||||
return RunAsyncAndCatchErrors<libretro_vfs_implementation_file*>([&]() {
|
return RunAsyncAndCatchErrors<libretro_vfs_implementation_file*>([&]() {
|
||||||
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(dirpath_str)).then([&](StorageFolder^ dir) {
|
return concurrency::create_task(LocateStorageItem<StorageFolder>(dirpath_str)).then([&](StorageFolder^ dir) {
|
||||||
if (mode == RETRO_VFS_FILE_ACCESS_READ)
|
if (mode == RETRO_VFS_FILE_ACCESS_READ)
|
||||||
return dir->GetFileAsync(filename_str);
|
return dir->GetFileAsync(filename_str);
|
||||||
else
|
else
|
||||||
@ -361,7 +508,7 @@ int retro_vfs_file_remove_impl(const char *path)
|
|||||||
free(path_wide);
|
free(path_wide);
|
||||||
|
|
||||||
return RunAsyncAndCatchErrors<int>([&]() {
|
return RunAsyncAndCatchErrors<int>([&]() {
|
||||||
return concurrency::create_task(StorageFile::GetFileFromPathAsync(path_str)).then([&](StorageFile^ file) {
|
return concurrency::create_task(LocateStorageItem<StorageFile>(path_str)).then([&](StorageFile^ file) {
|
||||||
return file->DeleteAsync(StorageDeleteOption::PermanentDelete);
|
return file->DeleteAsync(StorageDeleteOption::PermanentDelete);
|
||||||
}).then([&]() {
|
}).then([&]() {
|
||||||
return 0;
|
return 0;
|
||||||
@ -397,8 +544,8 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
|
|||||||
retro_assert(!old_path_str->IsEmpty() && !new_dir_path_str->IsEmpty() && !new_file_name_str->IsEmpty());
|
retro_assert(!old_path_str->IsEmpty() && !new_dir_path_str->IsEmpty() && !new_file_name_str->IsEmpty());
|
||||||
|
|
||||||
return RunAsyncAndCatchErrors<int>([&]() {
|
return RunAsyncAndCatchErrors<int>([&]() {
|
||||||
concurrency::task<StorageFile^> old_file_task = concurrency::create_task(StorageFile::GetFileFromPathAsync(old_path_str));
|
concurrency::task<StorageFile^> old_file_task = concurrency::create_task(LocateStorageItem<StorageFile>(old_path_str));
|
||||||
concurrency::task<StorageFolder^> new_dir_task = concurrency::create_task(StorageFolder::GetFolderFromPathAsync(new_dir_path_str));
|
concurrency::task<StorageFolder^> new_dir_task = concurrency::create_task(LocateStorageItem<StorageFolder>(new_dir_path_str));
|
||||||
return concurrency::create_task([&] {
|
return concurrency::create_task([&] {
|
||||||
/* Run these two tasks in parallel */
|
/* Run these two tasks in parallel */
|
||||||
/* TODO: There may be some cleaner way to express this */
|
/* TODO: There may be some cleaner way to express this */
|
||||||
@ -422,37 +569,6 @@ const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *strea
|
|||||||
return stream->orig_path;
|
return stream->orig_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This is ugly, but I can't figure out a better way and there may be no better way... */
|
|
||||||
static IStorageItem^ GetFileOrFolderFromPath(Platform::String^ path)
|
|
||||||
{
|
|
||||||
if (!path || path->IsEmpty())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (*(path->End() - 1) == '\\')
|
|
||||||
{
|
|
||||||
/* Ends with a slash, so it's definitely a directory */
|
|
||||||
return RunAsyncAndCatchErrors<StorageFolder^>([&]() {
|
|
||||||
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(path));
|
|
||||||
}, nullptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No final slash - probably a file (since RetroArch usually slash-terminates dirs), but there is a chance it's a directory */
|
|
||||||
IStorageItem^ item;
|
|
||||||
item = RunAsyncAndCatchErrors<StorageFile^>([&]() {
|
|
||||||
return concurrency::create_task(StorageFile::GetFileFromPathAsync(path));
|
|
||||||
}, nullptr);
|
|
||||||
if (!item)
|
|
||||||
{
|
|
||||||
item = RunAsyncAndCatchErrors<StorageFolder^>([&]() {
|
|
||||||
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(path));
|
|
||||||
}, nullptr);
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int retro_vfs_stat_impl(const char *path, int32_t *size)
|
int retro_vfs_stat_impl(const char *path, int32_t *size)
|
||||||
{
|
{
|
||||||
if (!path || !*path)
|
if (!path || !*path)
|
||||||
@ -463,7 +579,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||||||
Platform::String^ path_str = ref new Platform::String(path_wide);
|
Platform::String^ path_str = ref new Platform::String(path_wide);
|
||||||
free(path_wide);
|
free(path_wide);
|
||||||
|
|
||||||
IStorageItem^ item = GetFileOrFolderFromPath(path_str);
|
IStorageItem^ item = LocateStorageFileOrFolder(path_str);
|
||||||
if (!item)
|
if (!item)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -507,7 +623,7 @@ int retro_vfs_mkdir_impl(const char *dir)
|
|||||||
free(dir_local);
|
free(dir_local);
|
||||||
|
|
||||||
return RunAsyncAndCatchErrors<int>([&]() {
|
return RunAsyncAndCatchErrors<int>([&]() {
|
||||||
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(parent_path_str)).then([&](StorageFolder^ parent) {
|
return concurrency::create_task(LocateStorageItem<StorageFolder>(parent_path_str)).then([&](StorageFolder^ parent) {
|
||||||
return parent->CreateFolderAsync(dir_name_str);
|
return parent->CreateFolderAsync(dir_name_str);
|
||||||
}).then([&](concurrency::task<StorageFolder^> new_dir) {
|
}).then([&](concurrency::task<StorageFolder^> new_dir) {
|
||||||
try
|
try
|
||||||
@ -553,7 +669,7 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *name, bool i
|
|||||||
free(name_wide);
|
free(name_wide);
|
||||||
|
|
||||||
rdir->directory = RunAsyncAndCatchErrors<IVectorView<IStorageItem^>^>([&]() {
|
rdir->directory = RunAsyncAndCatchErrors<IVectorView<IStorageItem^>^>([&]() {
|
||||||
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(name_str)).then([&](StorageFolder^ folder) {
|
return concurrency::create_task(LocateStorageItem<StorageFolder>(name_str)).then([&](StorageFolder^ folder) {
|
||||||
return folder->GetItemsAsync();
|
return folder->GetItemsAsync();
|
||||||
});
|
});
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
@ -615,3 +731,28 @@ bool uwp_is_path_accessible_using_standard_io(char *path)
|
|||||||
free(relative_path_abbrev);
|
free(relative_path_abbrev);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool uwp_drive_exists(const char *path)
|
||||||
|
{
|
||||||
|
if (!path || !*path)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wchar_t *path_wide = utf8_to_utf16_string_alloc(path);
|
||||||
|
Platform::String^ path_str = ref new Platform::String(path_wide);
|
||||||
|
free(path_wide);
|
||||||
|
|
||||||
|
return RunAsyncAndCatchErrors<bool>([&]() {
|
||||||
|
return concurrency::create_task(StorageFolder::GetFolderFromPathAsync(path_str)).then([](StorageFolder^ properties) {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* uwp_trigger_picker(void)
|
||||||
|
{
|
||||||
|
return RunAsyncAndCatchErrors<char*>([&]() {
|
||||||
|
return TriggerPickerAddDialog().then([](Platform::String^ path) {
|
||||||
|
return utf16_to_utf8_string_alloc(path->Data());
|
||||||
|
});
|
||||||
|
}, NULL);
|
||||||
|
}
|
||||||
|
@ -3980,6 +3980,28 @@ static int action_ok_open_uwp_permission_settings(const char *path,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int action_ok_open_picker(const char *path,
|
||||||
|
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
||||||
|
{
|
||||||
|
char* new_path;
|
||||||
|
int ret;
|
||||||
|
#ifdef __WINRT__
|
||||||
|
new_path = uwp_trigger_picker();
|
||||||
|
if (!new_path)
|
||||||
|
return 0; /* User aborted */
|
||||||
|
#else
|
||||||
|
retro_assert(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = generic_action_ok_displaylist_push(path, new_path,
|
||||||
|
msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES),
|
||||||
|
type, idx,
|
||||||
|
entry_idx, ACTION_OK_DL_CONTENT_LIST);
|
||||||
|
|
||||||
|
free(new_path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int action_ok_shader_pass(const char *path,
|
static int action_ok_shader_pass(const char *path,
|
||||||
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
||||||
{
|
{
|
||||||
@ -5483,6 +5505,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
|
|||||||
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS:
|
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS:
|
||||||
BIND_ACTION_OK(cbs, action_ok_open_uwp_permission_settings);
|
BIND_ACTION_OK(cbs, action_ok_open_uwp_permission_settings);
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER:
|
||||||
|
BIND_ACTION_OK(cbs, action_ok_open_picker);
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS:
|
case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS:
|
||||||
BIND_ACTION_OK(cbs, action_ok_retro_achievements_list);
|
BIND_ACTION_OK(cbs, action_ok_retro_achievements_list);
|
||||||
break;
|
break;
|
||||||
|
@ -335,6 +335,7 @@ default_sublabel_macro(action_bind_sublabel_goto_music,
|
|||||||
default_sublabel_macro(action_bind_sublabel_goto_video, MENU_ENUM_SUBLABEL_GOTO_VIDEO)
|
default_sublabel_macro(action_bind_sublabel_goto_video, MENU_ENUM_SUBLABEL_GOTO_VIDEO)
|
||||||
default_sublabel_macro(action_bind_sublabel_menu_filebrowser_settings, MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS)
|
default_sublabel_macro(action_bind_sublabel_menu_filebrowser_settings, MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS)
|
||||||
default_sublabel_macro(action_bind_sublabel_menu_filebrowser_open_uwp_permissions, MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS)
|
default_sublabel_macro(action_bind_sublabel_menu_filebrowser_open_uwp_permissions, MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS)
|
||||||
|
default_sublabel_macro(action_bind_sublabel_menu_filebrowser_open_picker, MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_PICKER)
|
||||||
default_sublabel_macro(action_bind_sublabel_auto_remaps_enable, MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE)
|
default_sublabel_macro(action_bind_sublabel_auto_remaps_enable, MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE)
|
||||||
default_sublabel_macro(action_bind_sublabel_auto_overrides_enable, MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE)
|
default_sublabel_macro(action_bind_sublabel_auto_overrides_enable, MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE)
|
||||||
default_sublabel_macro(action_bind_sublabel_game_specific_options, MENU_ENUM_SUBLABEL_GAME_SPECIFIC_OPTIONS)
|
default_sublabel_macro(action_bind_sublabel_game_specific_options, MENU_ENUM_SUBLABEL_GAME_SPECIFIC_OPTIONS)
|
||||||
@ -1383,6 +1384,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
|||||||
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS:
|
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_filebrowser_open_uwp_permissions);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_filebrowser_open_uwp_permissions);
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER:
|
||||||
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_filebrowser_open_picker);
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_ADD_TO_FAVORITES:
|
case MENU_ENUM_LABEL_ADD_TO_FAVORITES:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_add_to_favorites);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_add_to_favorites);
|
||||||
break;
|
break;
|
||||||
|
@ -825,6 +825,7 @@ enum msg_hash_enums
|
|||||||
MENU_LABEL(PARENT_DIRECTORY),
|
MENU_LABEL(PARENT_DIRECTORY),
|
||||||
|
|
||||||
MENU_LABEL(FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
MENU_LABEL(FILE_BROWSER_OPEN_UWP_PERMISSIONS),
|
||||||
|
MENU_LABEL(FILE_BROWSER_OPEN_PICKER),
|
||||||
|
|
||||||
MENU_ENUM_LABEL_CONTENT_ACTIONS,
|
MENU_ENUM_LABEL_CONTENT_ACTIONS,
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ extern char uwp_device_family[128];
|
|||||||
|
|
||||||
void uwp_open_broadfilesystemaccess_settings(void);
|
void uwp_open_broadfilesystemaccess_settings(void);
|
||||||
bool uwp_is_path_accessible_using_standard_io(char *path);
|
bool uwp_is_path_accessible_using_standard_io(char *path);
|
||||||
|
bool uwp_drive_exists(const char *path);
|
||||||
|
char* uwp_trigger_picker(void);
|
||||||
|
|
||||||
void* uwp_get_corewindow(void);
|
void* uwp_get_corewindow(void);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user