Squashed 'deps/nfde/' changes from 28ade5a5c..5786fabce

5786fabce Release: v1.1.1
cdbc9e9df Build: Update minimum CMake version to 3.5 (#113)
a6d93cb12 CI: Downgrade from C++23 to C++20 on Ubuntu (#116)
f4bf38915 CI: Remove CircleCI config file (#114)
75cbdf819 Build: Add support for building shared libraries on Windows (#109)
800f58283 Release: v1.1.0
210ae0e76 Portal: Support defaultPath for OpenDialog, OpenDialogMultiple, and PickFolder (#108)
dbd7139b4 Build: Generate and install CMake config (#100)
1fde8a5aa CI: Update MacOS 10.15 to MacOS 11 (#101)
06a5c1f0a Release: v1.0.3
ae6718b68 Portal: Make `PickFolder()` check that portal interface version is >=3 (#94)
08216013f Portal: Support formatted error messages using sprintf() (#97)
da81bb077 README: Remove untested portal warning and link current_folder PR (#96)
2b55a1f83 CI: Upgrade Ubuntu 18.04 to Ubuntu 20.04 (#95)
7909f55d9 Release: v1.0.2
d1b80e3a6 MacOS: Add NFD_ClearError() definition (#88)
44e63d5e5 Portal: Add `getrandom` fallback for old versions of GLIBC (#86)
43fe9cf95 Build: Use XXX_LINK_LIBRARIES for linking to support *BSD (#85)
dd46d2a05 ClangFormat: Force ClangFormat 13 for now (#84)
699bb6f82 Build: Set target_include_directories correctly when NFDe is added as a subdirectory (#83)
6efc82407 Release: v1.0.1
74923e7c0 README: Add missing Windows shell32.lib dependency
31df8e30c README: Update NFD_BUILD_TESTS and add NFD_INSTALL flags (#78)
dee61e555 Add NFD_INSTALL option + disable install when in subproject (#77)
e018ec82b Option to generate shared library & use GNUInstallDirs (#76)
d4df2b6ad README: Add NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE flag
6967d28b0 MacOS: Perform CMake check if allowedContentTypes will be used
f397884eb MacOS: Rename flag to NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE
89a67f8a5 MacOS: Add CMake flag to avoid using and linking the UniformTypeIdentifiers framework (#72)
331159281 Portal: Don't automatically append file extension in SaveDialog()
008da08d0 Portal: Decode returned URIs
c886650bd README: Update MacOS quirk
eb465a366 Build: Choose latest C++ version based on CMake version
957cf8b0a CI: Build MacOS 10.15 and name things more properly
ff7c3e7cb MacOS:  Remove runtime version check and use deployment target version instead
800060ddb Portal: Fix typo in error messages
aa1debf5e README: Add dark mode images
b0e3db8b1 README: Mention the wiki
2f5732c12 GitHub CI: Fix Linux artefact naming
31f8a5c80 CI: Add stricter C compiler flags
6aba31f38 Circle CI: Initial config
70c11d4d0 MacOS: Use allowedContentTypes on >=12.0 instead of allowedFileTypes

git-subtree-dir: deps/nfde
git-subtree-split: 5786fabceeaee4d892f3c7a16b243796244cdddc
This commit is contained in:
2024-05-31 19:29:33 -05:00
parent 004534c8e7
commit 5461f6700d
7 changed files with 568 additions and 199 deletions
+254 -93
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;