Squashed 'deps/nostalgia/' changes from dd5d1bfb..976550ef
976550ef [nostalgia/sample_project] Add missing type descriptors 1fb0a866 [nostalgia/core] Fix CompactPalette TypeName and TypeVersion eb46aeeb [nfde] Fix compiler warnings on Gtk build 9244b735 [nostalgia] Fix non-Linux non-Apple systems not to build GLFW for Wayland 3fba36b4 [nfde] Make only option BUILD_SHARED_LIBS if not already set 6ddb6b42 Merge commit '5461f6700dac79e9e71e3966f8a1270706c385ba' 5461f670 Squashed 'deps/nfde/' changes from 28ade5a5c..5786fabce 1ecc7aa7 Merge commit '26c8cc348eacea01237cd64e1a68d0df8141e848' 26c8cc34 Squashed 'deps/glfw/' changes from 7d5a16ce7..7b6aead9f fc25c12d Merge commit 'ae51a422787bc3b720ff1748c0219c8f33363427' ae51a422 Squashed 'deps/imgui/' changes from f33737806..2db79d086 6c71e1e2 [nostalgia/core/studio,studio] Give TileSheetEditor Ctrl-A and Ctrl-G for selection 2ede01e7 [nostalgia/core/studio] Fix build deacd4ab [nostalgia/core/studio] Fix PaletteEditor add/remove color commands git-subtree-dir: deps/nostalgia git-subtree-split: 976550ef6fa269cf1e367e880f148b0ad7b1b163
This commit is contained in:
347
deps/nfde/src/nfd_portal.cpp
vendored
347
deps/nfde/src/nfd_portal.cpp
vendored
@ -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(¤t_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(¤t_filter_iter, ¤t_filter_struct_iter);
|
||||
if (!dbus_message_iter_next(¤t_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(¤t_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(¤t_filter_struct_iter, ¤t_filter_array_iter);
|
||||
if (dbus_message_iter_get_arg_type(¤t_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(¤t_filter_array_iter, ¤t_filter_extn_iter);
|
||||
if (dbus_message_iter_get_arg_type(¤t_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(¤t_filter_extn_iter, &type);
|
||||
if (type != 0) {
|
||||
// NFDi_SetError("Wrong filter type.");
|
||||
return NFD_OKAY;
|
||||
}
|
||||
if (!dbus_message_iter_next(¤t_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(¤t_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(¤t_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;
|
||||
|
Reference in New Issue
Block a user