Merge commit '5461f6700dac79e9e71e3966f8a1270706c385ba'

This commit is contained in:
2024-05-31 19:36:34 -05:00
7 changed files with 567 additions and 198 deletions

View File

@ -25,17 +25,49 @@ if(nfd_PLATFORM STREQUAL PLATFORM_UNIX)
endif()
if(nfd_PLATFORM STREQUAL PLATFORM_MACOS)
# For setting the filter list, macOS introduced allowedContentTypes in version 11.0 and deprecated allowedFileTypes in 12.0.
# By default (set to ON), NFDe will use allowedContentTypes when targeting macOS >= 11.0.
# Set this option to OFF to always use allowedFileTypes regardless of the target macOS version.
# This is mainly needed for applications that are built on macOS >= 11.0 but should be able to run on lower versions
# and should not be used otherwise.
option(NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE "Use allowedContentTypes for filter lists on macOS >= 11.0" ON)
find_library(APPKIT_LIBRARY AppKit)
if(NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
"
#include <Availability.h>
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
static_assert(false);
#endif
int main() { return 0; }
"
NFD_USE_ALLOWEDCONTENTTYPES
)
if(NFD_USE_ALLOWEDCONTENTTYPES)
find_library(UNIFORMTYPEIDENTIFIERS_LIBRARY UniformTypeIdentifiers)
if(NOT UNIFORMTYPEIDENTIFIERS_LIBRARY)
message(FATAL_ERROR "UniformTypeIdentifiers framework is not available even though we are targeting macOS >= 11.0")
endif()
endif()
endif()
list(APPEND SOURCE_FILES nfd_cocoa.m)
endif()
# Define the library
add_library(${TARGET_NAME}
${SOURCE_FILES})
add_library(${TARGET_NAME} ${SOURCE_FILES})
if (BUILD_SHARED_LIBS)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_EXPORT INTERFACE NFD_SHARED)
endif ()
# Allow includes from include/
target_include_directories(${TARGET_NAME}
PUBLIC include/)
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
if(nfd_PLATFORM STREQUAL PLATFORM_UNIX)
if(NOT NFD_PORTAL)
@ -45,15 +77,25 @@ if(nfd_PLATFORM STREQUAL PLATFORM_UNIX)
target_include_directories(${TARGET_NAME}
PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME}
PRIVATE ${DBUS_LIBRARIES})
PRIVATE ${DBUS_LINK_LIBRARIES})
target_compile_definitions(${TARGET_NAME}
PUBLIC NFD_PORTAL)
endif()
option(NFD_APPEND_EXTENSION "Automatically append file extension to an extensionless selection in SaveDialog()" OFF)
if(NFD_APPEND_EXTENSION)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_APPEND_EXTENSION)
endif()
endif()
if(nfd_PLATFORM STREQUAL PLATFORM_MACOS)
target_link_libraries(${TARGET_NAME}
PRIVATE ${APPKIT_LIBRARY})
if(NFD_USE_ALLOWEDCONTENTTYPES)
target_link_libraries(${TARGET_NAME} PRIVATE ${APPKIT_LIBRARY} ${UNIFORMTYPEIDENTIFIERS_LIBRARY})
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_MACOS_ALLOWEDCONTENTTYPES=1)
else()
target_link_libraries(${TARGET_NAME} PRIVATE ${APPKIT_LIBRARY})
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_MACOS_ALLOWEDCONTENTTYPES=0)
endif()
endif()
if(nfd_COMPILER STREQUAL COMPILER_MSVC)
@ -71,6 +113,20 @@ if(nfd_COMPILER STREQUAL COMPILER_GNU)
target_compile_options(${TARGET_NAME} PRIVATE -nostdlib -fno-exceptions -fno-rtti)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADER_FILES}")
set_target_properties(${TARGET_NAME} PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADER_FILES}"
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include)
if (NFD_INSTALL)
include(GNUInstallDirs)
install(TARGETS ${TARGET_NAME} EXPORT ${TARGET_NAME}-export
LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT ${TARGET_NAME}-export
DESTINATION lib/cmake/${TARGET_NAME}
NAMESPACE ${TARGET_NAME}::
FILE ${TARGET_NAME}-config.cmake
)
endif()

View File

@ -10,6 +10,23 @@
#ifndef _NFD_H
#define _NFD_H
#if defined(_WIN32)
#if defined(NFD_EXPORT)
#define NFD_API __declspec(dllexport)
#elif defined(NFD_SHARED)
#define NFD_API __declspec(dllimport)
#endif
#else
#if defined(NFD_EXPORT) || defined(NFD_SHARED)
#if defined(__GNUC__) || defined(__clang__)
#define NFD_API __attribute__((visibility("default")))
#endif
#endif
#endif
#ifndef NFD_API
#define NFD_API
#endif
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
@ -64,60 +81,60 @@ typedef struct {
/* free a file path that was returned by the dialogs */
/* Note: use NFD_PathSet_FreePath to free path from pathset instead of this function */
void NFD_FreePathN(nfdnchar_t* filePath);
NFD_API void NFD_FreePathN(nfdnchar_t* filePath);
/* initialize NFD - call this for every thread that might use NFD, before calling any other NFD
* functions on that thread */
nfdresult_t NFD_Init(void);
NFD_API nfdresult_t NFD_Init(void);
/* call this to de-initialize NFD, if NFD_Init returned NFD_OKAY */
void NFD_Quit(void);
NFD_API void NFD_Quit(void);
/* single file open dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY */
/* If filterCount is zero, filterList is ignored (you can use NULL) */
/* If defaultPath is NULL, the operating system will decide */
nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
NFD_API nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
/* multiple file open dialog */
/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function
* returns NFD_OKAY */
/* If filterCount is zero, filterList is ignored (you can use NULL) */
/* If defaultPath is NULL, the operating system will decide */
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
NFD_API nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
/* save dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY */
/* If filterCount is zero, filterList is ignored (you can use NULL) */
/* If defaultPath is NULL, the operating system will decide */
nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath,
const nfdnchar_t* defaultName);
NFD_API nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath,
const nfdnchar_t* defaultName);
/* select folder dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY */
/* If defaultPath is NULL, the operating system will decide */
nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath);
NFD_API nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath);
/* Get last error -- set when nfdresult_t returns NFD_ERROR */
/* Returns the last error that was set, or NULL if there is no error. */
/* The memory is owned by NFD and should not be freed by user code. */
/* This is *always* ASCII printable characters, so it can be interpreted as UTF-8 without any
* conversion. */
const char* NFD_GetError(void);
NFD_API const char* NFD_GetError(void);
/* clear the error */
void NFD_ClearError(void);
NFD_API void NFD_ClearError(void);
/* path set operations */
#ifdef _WIN32
@ -131,36 +148,37 @@ typedef unsigned int nfdpathsetsize_t;
/* Gets the number of entries stored in pathSet */
/* note that some paths might be invalid (NFD_ERROR will be returned by NFD_PathSet_GetPath), so we
* might not actually have this number of usable paths */
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count);
NFD_API nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count);
/* Gets the UTF-8 path at offset index */
/* It is the caller's responsibility to free `outPath` via NFD_PathSet_FreePathN() if this function
* returns NFD_OKAY */
nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t** outPath);
NFD_API nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t** outPath);
/* Free the path gotten by NFD_PathSet_GetPathN */
#ifdef _WIN32
#define NFD_PathSet_FreePathN NFD_FreePathN
#elif __APPLE__
#define NFD_PathSet_FreePathN NFD_FreePathN
#else
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath);
NFD_API void NFD_PathSet_FreePathN(const nfdnchar_t* filePath);
#endif // _WIN32, __APPLE__
/* Gets an enumerator of the path set. */
/* It is the caller's responsibility to free `enumerator` via NFD_PathSet_FreeEnum() if this
* function returns NFD_OKAY, and it should be freed before freeing the pathset. */
nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t* outEnumerator);
NFD_API nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet,
nfdpathsetenum_t* outEnumerator);
/* Frees an enumerator of the path set. */
void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator);
NFD_API void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator);
/* Gets the next item from the path set enumerator.
* If there are no more items, then *outPaths will be set to NULL. */
/* It is the caller's responsibility to free `*outPath` via NFD_PathSet_FreePath() if this
* function returns NFD_OKAY and `*outPath` is not null */
nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath);
NFD_API nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath);
/* Free the pathSet */
void NFD_PathSet_Free(const nfdpathset_t* pathSet);
NFD_API void NFD_PathSet_Free(const nfdpathset_t* pathSet);
#ifdef _WIN32
@ -177,50 +195,50 @@ typedef struct {
/* UTF-8 compatibility functions */
/* free a file path that was returned */
void NFD_FreePathU8(nfdu8char_t* outPath);
NFD_API void NFD_FreePathU8(nfdu8char_t* outPath);
/* single file open dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns
* NFD_OKAY */
nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t count,
const nfdu8char_t* defaultPath);
/* multiple file open dialog */
/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function
* returns NFD_OKAY */
nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths,
NFD_API nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t count,
const nfdu8char_t* defaultPath);
/* multiple file open dialog */
/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function
* returns NFD_OKAY */
NFD_API nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t count,
const nfdu8char_t* defaultPath);
/* save dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns
* NFD_OKAY */
nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t count,
const nfdu8char_t* defaultPath,
const nfdu8char_t* defaultName);
NFD_API nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t count,
const nfdu8char_t* defaultPath,
const nfdu8char_t* defaultName);
/* select folder dialog */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns
* NFD_OKAY */
nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath);
NFD_API nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath);
/* Get the UTF-8 path at offset index */
/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns
* NFD_OKAY */
nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t** outPath);
NFD_API nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t** outPath);
/* Gets the next item from the path set enumerator.
* If there are no more items, then *outPaths will be set to NULL. */
/* It is the caller's responsibility to free `*outPath` via NFD_PathSet_FreePathU8() if this
* function returns NFD_OKAY and `*outPath` is not null */
nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath);
NFD_API nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath);
#define NFD_PathSet_FreePathU8 NFD_FreePathU8

View File

@ -6,8 +6,28 @@
*/
#include <AppKit/AppKit.h>
#include <Availability.h>
#include "nfd.h"
// MacOS is deprecating the allowedFileTypes property in favour of allowedContentTypes, so we have
// to introduce this breaking change. Define NFD_MACOS_ALLOWEDCONTENTTYPES to 1 to have it set the
// allowedContentTypes property of the SavePanel or OpenPanel. Define
// NFD_MACOS_ALLOWEDCONTENTTYPES to 0 to have it set the allowedFileTypes property of the SavePanel
// or OpenPanel. If NFD_MACOS_ALLOWEDCONTENTTYPES is undefined, then it will set it to 1 if
// __MAC_OS_X_VERSION_MIN_REQUIRED >= 11.0, and 0 otherwise.
#if !defined(NFD_MACOS_ALLOWEDCONTENTTYPES)
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || \
__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
#define NFD_MACOS_ALLOWEDCONTENTTYPES 0
#else
#define NFD_MACOS_ALLOWEDCONTENTTYPES 1
#endif
#endif
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#endif
static const char* g_errorstr = NULL;
static void NFDi_SetError(const char* msg) {
@ -26,10 +46,51 @@ static void NFDi_Free(void* ptr) {
free(ptr);
}
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
// Returns an NSArray of UTType representing the content types.
static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
NSMutableArray* buildFilterList = [[NSMutableArray alloc] init];
for (nfdfiltersize_t filterIndex = 0; filterIndex != filterCount; ++filterIndex) {
// this is the spec to parse (we don't use the friendly name on OS X)
const nfdnchar_t* filterSpec = filterList[filterIndex].spec;
const nfdnchar_t* p_currentFilterBegin = filterSpec;
for (const nfdnchar_t* p_filterSpec = filterSpec; *p_filterSpec; ++p_filterSpec) {
if (*p_filterSpec == ',') {
// add the extension to the array
NSString* filterStr = [[NSString alloc]
initWithBytes:(const void*)p_currentFilterBegin
length:(sizeof(nfdnchar_t) * (p_filterSpec - p_currentFilterBegin))
encoding:NSUTF8StringEncoding];
UTType* filterType = [UTType typeWithFilenameExtension:filterStr
conformingToType:UTTypeData];
[filterStr release];
if (filterType) [buildFilterList addObject:filterType];
p_currentFilterBegin = p_filterSpec + 1;
}
}
// add the extension to the array
NSString* filterStr = [[NSString alloc] initWithUTF8String:p_currentFilterBegin];
UTType* filterType = [UTType typeWithFilenameExtension:filterStr
conformingToType:UTTypeData];
[filterStr release];
if (filterType) [buildFilterList addObject:filterType];
}
NSArray* returnArray = [NSArray arrayWithArray:buildFilterList];
[buildFilterList release];
assert([returnArray count] != 0);
return returnArray;
}
#else
// Returns an NSArray of NSString representing the file types.
static NSArray* BuildAllowedFileTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
// Commas and semicolons are the same thing on this platform
NSMutableArray* buildFilterList = [[NSMutableArray alloc] init];
for (nfdfiltersize_t filterIndex = 0; filterIndex != filterCount; ++filterIndex) {
@ -61,6 +122,7 @@ static NSArray* BuildAllowedFileTypes(const nfdnfilteritem_t* filterList,
return returnArray;
}
#endif
static void AddFilterListToDialog(NSSavePanel* dialog,
const nfdnfilteritem_t* filterList,
@ -71,11 +133,15 @@ static void AddFilterListToDialog(NSSavePanel* dialog,
assert(filterList);
// make NSArray of file types
// Make NSArray of file types and set it on the dialog
// We use setAllowedFileTypes or setAllowedContentTypes depending on the deployment target
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
[dialog setAllowedContentTypes:allowedContentTypes];
#else
NSArray* allowedFileTypes = BuildAllowedFileTypes(filterList, filterCount);
// set it on the dialog
[dialog setAllowedFileTypes:allowedFileTypes];
#endif
}
static void SetDefaultPath(NSSavePanel* dialog, const nfdnchar_t* defaultPath) {
@ -114,6 +180,10 @@ const char* NFD_GetError(void) {
return g_errorstr;
}
void NFD_ClearError(void) {
NFDi_SetError(NULL);
}
void NFD_FreePathN(nfdnchar_t* filePath) {
NFDi_Free((void*)filePath);
}

View File

@ -14,19 +14,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/random.h> // for the random token string
#include <unistd.h> // for access()
#include <unistd.h> // for access()
#if !defined(__has_include) || !defined(__linux__)
#include <sys/random.h> // for getrandom() - the random token string
#elif __has_include(<sys/random.h>)
#include <sys/random.h>
#else // for GLIBC < 2.25
#include <sys/syscall.h>
#define getrandom(buf, sz, flags) syscall(SYS_getrandom, buf, sz, flags)
#endif
#include "nfd.h"
/*
Define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION to 0 if you don't want the file extension to be
appended when missing. Linux programs usually doesn't append the file extension, but for consistency
with other OSes we append it by default.
Define NFD_APPEND_EXTENSION if you want the file extension to be appended when missing. Linux
programs usually don't append the file extension, but for consistency with other OSes you might want
to append it. However, when using portals, the file overwrite prompt and the Flatpak sandbox won't
know that we appended an extension, so they will not check or whitelist the correct file. Enabling
NFD_APPEND_EXTENSION is not recommended for portals.
*/
#ifndef NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION
#define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION 1
#endif
namespace {
@ -70,7 +77,10 @@ struct DBusMessage_Guard {
DBusConnection* dbus_conn;
/* current D-Bus error */
DBusError dbus_err;
/* current error (may be a pointer to the D-Bus error message above, or a pointer to some string
/* current non D-Bus error */
constexpr size_t OWNED_ERR_LEN = 1024;
char owned_err[OWNED_ERR_LEN]{};
/* current error (may be a pointer to dbus_err.message, owned_err, or a pointer to some string
* literal) */
const char* err_ptr = nullptr;
/* the unique name of our connection, used for the Request handle; owned by D-Bus so we don't free
@ -81,6 +91,14 @@ void NFDi_SetError(const char* msg) {
err_ptr = msg;
}
void NFDi_SetFormattedError(const char* format, ...) {
va_list args;
va_start(args, format);
vsnprintf(owned_err, OWNED_ERR_LEN, format, args);
va_end(args);
err_ptr = owned_err;
}
template <typename T>
T* copy(const T* begin, const T* end, T* out) {
for (; begin != end; ++begin) {
@ -112,6 +130,10 @@ constexpr const char* STR_CURRENT_FOLDER = "current_folder";
constexpr const char* STR_CURRENT_FILE = "current_file";
constexpr const char* STR_ALL_FILES = "All files";
constexpr const char* STR_ASTERISK = "*";
constexpr const char* DBUS_DESTINATION = "org.freedesktop.portal.Desktop";
constexpr const char* DBUS_PATH = "/org/freedesktop/portal/desktop";
constexpr const char* DBUS_FILECHOOSER_IFACE = "org.freedesktop.portal.FileChooser";
constexpr const char* DBUS_REQUEST_IFACE = "org.freedesktop.portal.Request";
template <bool Multiple, bool Directory>
void AppendOpenFileQueryTitle(DBusMessageIter&);
@ -462,7 +484,7 @@ void AppendSaveFileQueryDictEntryCurrentName(DBusMessageIter& sub_iter, const ch
dbus_message_iter_close_container(&sub_iter, &sub_sub_iter);
}
void AppendSaveFileQueryDictEntryCurrentFolder(DBusMessageIter& sub_iter, const char* path) {
void AppendOpenFileQueryDictEntryCurrentFolder(DBusMessageIter& sub_iter, const char* path) {
if (!path) return;
DBusMessageIter sub_sub_iter;
DBusMessageIter variant_iter;
@ -529,7 +551,8 @@ template <bool Multiple, bool Directory>
void AppendOpenFileQueryParams(DBusMessage* query,
const char* handle_token,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
DBusMessageIter iter;
dbus_message_iter_init_append(query, &iter);
@ -543,6 +566,7 @@ void AppendOpenFileQueryParams(DBusMessage* query,
AppendOpenFileQueryDictEntryMultiple<Multiple>(sub_iter);
AppendOpenFileQueryDictEntryDirectory<Directory>(sub_iter);
AppendOpenFileQueryDictEntryFilters<!Directory>(sub_iter, filterList, filterCount);
AppendOpenFileQueryDictEntryCurrentFolder(sub_iter, defaultPath);
dbus_message_iter_close_container(&iter, &sub_iter);
}
@ -565,7 +589,7 @@ void AppendSaveFileQueryParams(DBusMessage* query,
AppendOpenFileQueryDictEntryHandleToken(sub_iter, handle_token);
AppendSaveFileQueryDictEntryFilters(sub_iter, filterList, filterCount, defaultName);
AppendSaveFileQueryDictEntryCurrentName(sub_iter, defaultName);
AppendSaveFileQueryDictEntryCurrentFolder(sub_iter, defaultPath);
AppendOpenFileQueryDictEntryCurrentFolder(sub_iter, defaultPath);
AppendSaveFileQueryDictEntryCurrentFile(sub_iter, defaultPath, defaultName);
dbus_message_iter_close_container(&iter, &sub_iter);
}
@ -649,7 +673,9 @@ nfdresult_t ReadResponseResults(DBusMessage* msg, DBusMessageIter& resultsIter)
return NFD_CANCEL;
} else {
// Some error occurred
NFDi_SetError("D-Bus file dialog interaction was ended abruptly.");
NFDi_SetFormattedError(
"D-Bus file dialog interaction was ended abruptly with response code %u.",
resp_code);
return NFD_ERROR;
}
}
@ -720,14 +746,14 @@ nfdresult_t ReadResponseUrisSingle(DBusMessage* msg, const char*& file) {
const nfdresult_t res = ReadResponseUris(msg, uri_iter);
if (res != NFD_OKAY) return res; // can be NFD_CANCEL or NFD_ERROR
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
dbus_message_iter_get_basic(&uri_iter, &file);
return NFD_OKAY;
}
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
#ifdef NFD_APPEND_EXTENSION
// Read the response URI and selected extension (in the form "*.abc" or "*") (if any). If response
// was okay, then returns NFD_OKAY and set file and extn to them (the pointer is set to some string
// owned by msg, so you should not manually free it). `file` is the user-entered file name, and
@ -763,47 +789,37 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
[&tmp_extn](DBusMessageIter& current_filter_iter) {
// current_filter is best_effort, so if we fail, we still return NFD_OKAY.
if (dbus_message_iter_get_arg_type(&current_filter_iter) != DBUS_TYPE_STRUCT) {
// NFDi_SetError("D-Bus response signal current_filter iter is not a struct.");
return NFD_OKAY;
}
DBusMessageIter current_filter_struct_iter;
dbus_message_iter_recurse(&current_filter_iter, &current_filter_struct_iter);
if (!dbus_message_iter_next(&current_filter_struct_iter)) {
// NFDi_SetError("D-Bus response signal current_filter struct iter ended
// prematurely.");
return NFD_OKAY;
}
if (dbus_message_iter_get_arg_type(&current_filter_struct_iter) !=
DBUS_TYPE_ARRAY) {
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
return NFD_OKAY;
}
DBusMessageIter current_filter_array_iter;
dbus_message_iter_recurse(&current_filter_struct_iter, &current_filter_array_iter);
if (dbus_message_iter_get_arg_type(&current_filter_array_iter) !=
DBUS_TYPE_STRUCT) {
// NFDi_SetError("D-Bus response signal current_filter iter is not a struct.");
return NFD_OKAY;
}
DBusMessageIter current_filter_extn_iter;
dbus_message_iter_recurse(&current_filter_array_iter, &current_filter_extn_iter);
if (dbus_message_iter_get_arg_type(&current_filter_extn_iter) != DBUS_TYPE_UINT32) {
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
return NFD_OKAY;
}
dbus_uint32_t type;
dbus_message_iter_get_basic(&current_filter_extn_iter, &type);
if (type != 0) {
// NFDi_SetError("Wrong filter type.");
return NFD_OKAY;
}
if (!dbus_message_iter_next(&current_filter_extn_iter)) {
// NFDi_SetError("D-Bus response signal current_filter struct iter ended
// prematurely.");
return NFD_OKAY;
}
if (dbus_message_iter_get_arg_type(&current_filter_extn_iter) != DBUS_TYPE_STRING) {
// NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
return NFD_OKAY;
}
dbus_message_iter_get_basic(&current_filter_extn_iter, &tmp_extn);
@ -942,29 +958,95 @@ class DBusSignalSubscriptionHandler {
}
};
// Returns true if ch is in [0-9A-Za-z], false otherwise.
bool IsHex(char ch) {
return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');
}
// Returns the hexadecimal value contained in the char. Precondition: IsHex(ch)
char ParseHexUnchecked(char ch) {
if ('0' <= ch && ch <= '9') return ch - '0';
if ('A' <= ch && ch <= 'F') return ch - ('A' - 10);
if ('a' <= ch && ch <= 'f') return ch - ('a' - 10);
#if defined(__GNUC__)
__builtin_unreachable();
#endif
}
// Returns true if the given file URI is decodable (i.e. not malformed), and false otherwise.
// If this function returns true, then `out` will be populated with the length of the decoded URI
// and `fileUriEnd` will point to the trailing null byte of `fileUri`. Otherwise, `out` and
// `fileUriEnd` will be unmodified.
bool TryUriDecodeLen(const char* fileUri, size_t& out, const char*& fileUriEnd) {
size_t len = 0;
while (*fileUri) {
if (*fileUri != '%') {
++fileUri;
} else {
if (*(fileUri + 1) == '\0' || *(fileUri + 2) == '\0') {
return false;
}
if (!IsHex(*(fileUri + 1)) || !IsHex(*(fileUri + 2))) {
return false;
}
fileUri += 3;
}
++len;
}
out = len;
fileUriEnd = fileUri;
return true;
}
// Decodes the given URI and writes it to `outPath`. The caller must ensure that the given URI is
// not malformed (typically with a prior call to `TryUriDecodeLen`). This function does not write
// any trailing null character.
char* UriDecodeUnchecked(const char* fileUri, const char* fileUriEnd, char* outPath) {
while (fileUri != fileUriEnd) {
if (*fileUri != '%') {
*outPath++ = *fileUri++;
} else {
++fileUri;
const char high_nibble = ParseHexUnchecked(*fileUri++);
const char low_nibble = ParseHexUnchecked(*fileUri++);
*outPath++ = (high_nibble << 4) | low_nibble;
}
}
return outPath;
}
constexpr const char FILE_URI_PREFIX[] = "file://";
constexpr size_t FILE_URI_PREFIX_LEN = sizeof(FILE_URI_PREFIX) - 1;
// If fileUri starts with "file://", strips that prefix and copies it to a new buffer, and make
// outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath and returns
// NFD_ERROR (with the correct error set)
// If fileUri starts with "file://", strips that prefix and URI-decodes the remaining part to a new
// buffer, and make outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath
// and returns NFD_ERROR (with the correct error set)
nfdresult_t AllocAndCopyFilePath(const char* fileUri, char*& outPath) {
const char* file_uri_iter = fileUri;
const char* prefix_begin = FILE_URI_PREFIX;
const char* const prefix_end = FILE_URI_PREFIX + FILE_URI_PREFIX_LEN;
for (; prefix_begin != prefix_end; ++prefix_begin, ++fileUri) {
if (*prefix_begin != *fileUri) {
NFDi_SetError("D-Bus freedesktop portal returned a URI that is not a file URI.");
for (; prefix_begin != prefix_end; ++prefix_begin, ++file_uri_iter) {
if (*prefix_begin != *file_uri_iter) {
NFDi_SetFormattedError(
"D-Bus freedesktop portal returned \"%s\", which is not a file URI.", fileUri);
return NFD_ERROR;
}
}
size_t len = strlen(fileUri);
char* path_without_prefix = NFDi_Malloc<char>(len + 1);
copy(fileUri, fileUri + (len + 1), path_without_prefix);
size_t decoded_len;
const char* file_uri_end;
if (!TryUriDecodeLen(file_uri_iter, decoded_len, file_uri_end)) {
NFDi_SetFormattedError("D-Bus freedesktop portal returned a malformed URI \"%s\".",
fileUri);
return NFD_ERROR;
}
char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
char* const out_end = UriDecodeUnchecked(file_uri_iter, file_uri_end, path_without_prefix);
*out_end = '\0';
outPath = path_without_prefix;
return NFD_OKAY;
}
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
#ifdef NFD_APPEND_EXTENSION
bool TryGetValidExtension(const char* extn,
const char*& trimmed_extn,
const char*& trimmed_extn_end) {
@ -985,19 +1067,30 @@ bool TryGetValidExtension(const char* extn,
// expected to be either in the form "*.abc" or "*", but this function will check for it, and ignore
// the extension if it is not in the correct form.
nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn, char*& outPath) {
const char* file_uri_iter = fileUri;
const char* prefix_begin = FILE_URI_PREFIX;
const char* const prefix_end = FILE_URI_PREFIX + FILE_URI_PREFIX_LEN;
for (; prefix_begin != prefix_end; ++prefix_begin, ++fileUri) {
if (*prefix_begin != *fileUri) {
NFDi_SetError("D-Bus freedesktop portal returned a URI that is not a file URI.");
for (; prefix_begin != prefix_end; ++prefix_begin, ++file_uri_iter) {
if (*prefix_begin != *file_uri_iter) {
NFDi_SetFormattedError(
"D-Bus freedesktop portal returned \"%s\", which is not a file URI.", fileUri);
return NFD_ERROR;
}
}
const char* file_end = fileUri;
for (; *file_end != '\0'; ++file_end)
;
const char* file_it = file_end;
size_t decoded_len;
const char* file_uri_end;
if (!TryUriDecodeLen(file_uri_iter, decoded_len, file_uri_end)) {
NFDi_SetFormattedError("D-Bus freedesktop portal returned a malformed URI \"%s\".",
fileUri);
return NFD_ERROR;
}
const char* file_it = file_uri_end;
// The following loop condition is safe because `FILE_URI_PREFIX` ends with '/',
// so we won't iterate past the beginning of the URI.
// Also in UTF-8 all non-ASCII code points are encoded using bytes 128-255 so every '.' or '/'
// is also '.' or '/' in UTF-8.
do {
--file_it;
} while (*file_it != '/' && *file_it != '.');
@ -1005,16 +1098,17 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
const char* trimmed_extn_end; // includes the '\0'
if (*file_it == '.' || !TryGetValidExtension(extn, trimmed_extn, trimmed_extn_end)) {
// has file extension already or no valid extension in `extn`
++file_end; // includes the '\0'
char* path_without_prefix = NFDi_Malloc<char>(file_end - fileUri);
copy(fileUri, file_end, path_without_prefix);
char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
char* const out_end = UriDecodeUnchecked(file_uri_iter, file_uri_end, path_without_prefix);
*out_end = '\0';
outPath = path_without_prefix;
} else {
// no file extension and we have a valid extension
char* path_without_prefix =
NFDi_Malloc<char>((file_end - fileUri) + (trimmed_extn_end - trimmed_extn));
char* out = copy(fileUri, file_end, path_without_prefix);
copy(trimmed_extn, trimmed_extn_end, out);
char* const path_without_prefix =
NFDi_Malloc<char>(decoded_len + (trimmed_extn_end - trimmed_extn));
char* const out_mid = UriDecodeUnchecked(file_uri_iter, file_uri_end, path_without_prefix);
char* const out_end = copy(trimmed_extn, trimmed_extn_end, out_mid);
*out_end = '\0';
outPath = path_without_prefix;
}
return NFD_OKAY;
@ -1028,7 +1122,8 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
template <bool Multiple, bool Directory>
nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
const char* handle_token_ptr;
char* handle_obj_path = MakeUniqueObjectPath(&handle_token_ptr);
Free_Guard<char> handle_obj_path_guard(handle_obj_path);
@ -1045,13 +1140,11 @@ nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg,
// TODO: use XOpenDisplay()/XGetInputFocus() to find xid of window... but what should one do on
// Wayland?
DBusMessage* query = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.FileChooser",
"OpenFile");
DBusMessage* query = dbus_message_new_method_call(
DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, "OpenFile");
DBusMessage_Guard query_guard(query);
AppendOpenFileQueryParams<Multiple, Directory>(
query, handle_token_ptr, filterList, filterCount);
query, handle_token_ptr, filterList, filterCount, defaultPath);
DBusMessage* reply =
dbus_connection_send_with_reply_and_block(dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err);
@ -1090,7 +1183,7 @@ nfdresult_t NFD_DBus_OpenFile(DBusMessage*& outMsg,
DBusMessage* msg = dbus_connection_pop_message(dbus_conn);
if (!msg) break;
if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
if (dbus_message_is_signal(msg, DBUS_REQUEST_IFACE, "Response")) {
// this is the response we're looking for
outMsg = msg;
return NFD_OKAY;
@ -1129,10 +1222,8 @@ nfdresult_t NFD_DBus_SaveFile(DBusMessage*& outMsg,
// TODO: use XOpenDisplay()/XGetInputFocus() to find xid of window... but what should one do on
// Wayland?
DBusMessage* query = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.FileChooser",
"SaveFile");
DBusMessage* query = dbus_message_new_method_call(
DBUS_DESTINATION, DBUS_PATH, DBUS_FILECHOOSER_IFACE, "SaveFile");
DBusMessage_Guard query_guard(query);
AppendSaveFileQueryParams(
query, handle_token_ptr, filterList, filterCount, defaultPath, defaultName);
@ -1174,7 +1265,7 @@ nfdresult_t NFD_DBus_SaveFile(DBusMessage*& outMsg,
DBusMessage* msg = dbus_connection_pop_message(dbus_conn);
if (!msg) break;
if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
if (dbus_message_is_signal(msg, DBUS_REQUEST_IFACE, "Response")) {
// this is the response we're looking for
outMsg = msg;
return NFD_OKAY;
@ -1188,6 +1279,57 @@ nfdresult_t NFD_DBus_SaveFile(DBusMessage*& outMsg,
return NFD_ERROR;
}
nfdresult_t NFD_DBus_GetVersion(dbus_uint32_t& outVersion) {
DBusError err; // need a separate error object because we don't want to mess with the old one
// if it's stil set
dbus_error_init(&err);
DBusMessage* query = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.DBus.Properties",
"Get");
DBusMessage_Guard query_guard(query);
{
DBusMessageIter iter;
dbus_message_iter_init_append(query, &iter);
constexpr const char* STR_INTERFACE = "org.freedesktop.portal.FileChooser";
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_INTERFACE);
constexpr const char* STR_VERSION = "version";
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &STR_VERSION);
}
DBusMessage* reply =
dbus_connection_send_with_reply_and_block(dbus_conn, query, DBUS_TIMEOUT_INFINITE, &err);
if (!reply) {
dbus_error_free(&dbus_err);
dbus_move_error(&err, &dbus_err);
NFDi_SetError(dbus_err.message);
return NFD_ERROR;
}
DBusMessage_Guard reply_guard(reply);
{
DBusMessageIter iter;
if (!dbus_message_iter_init(reply, &iter)) {
NFDi_SetError("D-Bus reply for version query is missing an argument.");
return NFD_ERROR;
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
NFDi_SetError("D-Bus reply for version query is not a variant.");
return NFD_ERROR;
}
DBusMessageIter variant_iter;
dbus_message_iter_recurse(&iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32) {
NFDi_SetError("D-Bus reply for version query is not a uint32.");
return NFD_ERROR;
}
dbus_message_iter_get_basic(&variant_iter, &outVersion);
}
return NFD_OKAY;
}
} // namespace
/* public */
@ -1202,7 +1344,7 @@ void NFD_ClearError(void) {
}
nfdresult_t NFD_Init(void) {
// Initialize dbus_error
// Initialize dbus_err
dbus_error_init(&dbus_err);
// Get DBus connection
dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_err);
@ -1233,37 +1375,35 @@ nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
(void)defaultPath; // Default path not supported for portal backend
DBusMessage* msg;
{
const nfdresult_t res = NFD_DBus_OpenFile<false, false>(msg, filterList, filterCount);
const nfdresult_t res =
NFD_DBus_OpenFile<false, false>(msg, filterList, filterCount, defaultPath);
if (res != NFD_OKAY) {
return res;
}
}
DBusMessage_Guard msg_guard(msg);
const char* file;
const char* uri;
{
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
return AllocAndCopyFilePath(file, *outPath);
return AllocAndCopyFilePath(uri, *outPath);
}
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
(void)defaultPath; // Default path not supported for portal backend
DBusMessage* msg;
{
const nfdresult_t res = NFD_DBus_OpenFile<true, false>(msg, filterList, filterCount);
const nfdresult_t res =
NFD_DBus_OpenFile<true, false>(msg, filterList, filterCount, defaultPath);
if (res != NFD_OKAY) {
return res;
}
@ -1295,51 +1435,67 @@ nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
}
DBusMessage_Guard msg_guard(msg);
#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
const char* file;
#ifdef NFD_APPEND_EXTENSION
const char* uri;
const char* extn;
{
const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, file, extn);
const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, uri, extn);
if (res != NFD_OKAY) {
return res;
}
}
return AllocAndCopyFilePathWithExtn(file, extn, *outPath);
return AllocAndCopyFilePathWithExtn(uri, extn, *outPath);
#else
const char* file;
const char* uri;
{
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
return AllocAndCopyFilePath(file, *outPath);
return AllocAndCopyFilePath(uri, *outPath);
#endif
}
nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) {
(void)defaultPath; // Default path not supported for portal backend
{
dbus_uint32_t version;
const nfdresult_t res = NFD_DBus_GetVersion(version);
if (res != NFD_OKAY) {
return res;
}
if (version < 3) {
NFDi_SetFormattedError(
"The xdg-desktop-portal installed on this system does not support a folder picker; "
"at least version 3 of the org.freedesktop.portal.FileChooser interface is "
"required but the installed interface version is %u.",
version);
return NFD_ERROR;
}
}
DBusMessage* msg;
{
const nfdresult_t res = NFD_DBus_OpenFile<false, true>(msg, nullptr, 0);
const nfdresult_t res = NFD_DBus_OpenFile<false, true>(msg, nullptr, 0, defaultPath);
if (res != NFD_OKAY) {
return res;
}
}
DBusMessage_Guard msg_guard(msg);
const char* file;
const char* uri;
{
const nfdresult_t res = ReadResponseUrisSingle(msg, file);
const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
return AllocAndCopyFilePath(file, *outPath);
return AllocAndCopyFilePath(uri, *outPath);
}
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) {
@ -1356,20 +1512,25 @@ nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
DBusMessage* msg = const_cast<DBusMessage*>(static_cast<const DBusMessage*>(pathSet));
DBusMessageIter uri_iter;
ReadResponseUrisUnchecked(msg, uri_iter);
while (index > 0) {
--index;
nfdpathsetsize_t rem_index = index;
while (rem_index > 0) {
--rem_index;
if (!dbus_message_iter_next(&uri_iter)) {
NFDi_SetError("Index out of bounds.");
NFDi_SetFormattedError(
"Index out of bounds; you asked for index %u but there are only %u file paths "
"available.",
index,
index - rem_index);
return NFD_ERROR;
}
}
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
const char* file;
dbus_message_iter_get_basic(&uri_iter, &file);
return AllocAndCopyFilePath(file, *outPath);
const char* uri;
dbus_message_iter_get_basic(&uri_iter, &uri);
return AllocAndCopyFilePath(uri, *outPath);
}
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) {
@ -1402,12 +1563,12 @@ nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** out
return NFD_OKAY;
}
if (arg_type != DBUS_TYPE_STRING) {
NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
const char* file;
dbus_message_iter_get_basic(&uri_iter, &file);
const nfdresult_t res = AllocAndCopyFilePath(file, *outPath);
const char* uri;
dbus_message_iter_get_basic(&uri_iter, &uri);
const nfdresult_t res = AllocAndCopyFilePath(uri, *outPath);
if (res != NFD_OKAY) return res;
dbus_message_iter_next(&uri_iter);
return NFD_OKAY;