mirror of https://github.com/libsdl-org/SDL
linux: Add portal drag and drop
This commit is contained in:
parent
952c5059b5
commit
99421b64d0
|
@ -565,4 +565,70 @@ char *SDL_DBus_GetLocalMachineId(void)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths
|
||||
* Result must be freed with dbus->free_string_array().
|
||||
* https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles
|
||||
*/
|
||||
char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
|
||||
{
|
||||
DBusError err;
|
||||
DBusMessageIter iter, iterDict;
|
||||
char **paths = NULL;
|
||||
DBusMessage *reply = NULL;
|
||||
DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", /* Node */
|
||||
"/org/freedesktop/portal/documents", /* Path */
|
||||
"org.freedesktop.portal.FileTransfer", /* Interface */
|
||||
"RetrieveFiles"); /* Method */
|
||||
|
||||
/* Make sure we have a connection to the dbus session bus */
|
||||
if (!SDL_DBus_GetContext() || !dbus.session_conn) {
|
||||
/* We either cannot connect to the session bus or were unable to
|
||||
* load the D-Bus library at all. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus.error_init(&err);
|
||||
|
||||
/* First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event */
|
||||
if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
|
||||
SDL_OutOfMemory();
|
||||
dbus.message_unref(msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Second argument is a variant dictionary for options.
|
||||
* The spec doesn't define any entries yet so it's empty. */
|
||||
dbus.message_iter_init_append(msg, &iter);
|
||||
if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) ||
|
||||
!dbus.message_iter_close_container(&iter, &iterDict)) {
|
||||
SDL_OutOfMemory();
|
||||
dbus.message_unref(msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err);
|
||||
dbus.message_unref(msg);
|
||||
|
||||
if (reply) {
|
||||
dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID);
|
||||
dbus.message_unref(reply);
|
||||
}
|
||||
|
||||
if (paths) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
failed:
|
||||
if (dbus.error_is_set(&err)) {
|
||||
SDL_SetError("%s: %s", err.name, err.message);
|
||||
dbus.error_free(&err);
|
||||
} else {
|
||||
SDL_SetError("Error retrieving paths for documents portal \"%s\"", key);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -102,6 +102,8 @@ extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
|
|||
extern void SDL_DBus_PumpEvents(void);
|
||||
extern char *SDL_DBus_GetLocalMachineId(void);
|
||||
|
||||
extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);
|
||||
|
||||
#endif /* HAVE_DBUS_DBUS_H */
|
||||
|
||||
#endif /* SDL_dbus_h_ */
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#define TEXT_MIME "text/plain;charset=utf-8"
|
||||
#define FILE_MIME "text/uri-list"
|
||||
#define FILE_PORTAL_MIME "application/vnd.portal.filetransfer"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
|
@ -1848,18 +1848,26 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
|
|||
data_device->drag_offer = wl_data_offer_get_user_data(id);
|
||||
|
||||
/* TODO: SDL Support more mime types */
|
||||
has_mime = Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_MIME);
|
||||
|
||||
/* If drag_mime is NULL this will decline the offer */
|
||||
wl_data_offer_accept(id, serial,
|
||||
(has_mime == SDL_TRUE) ? FILE_MIME : NULL);
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
|
||||
has_mime = SDL_TRUE;
|
||||
wl_data_offer_accept(id, serial, FILE_PORTAL_MIME);
|
||||
}
|
||||
#endif
|
||||
if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) {
|
||||
has_mime = SDL_TRUE;
|
||||
wl_data_offer_accept(id, serial, FILE_MIME);
|
||||
}
|
||||
|
||||
/* SDL only supports "copy" style drag and drop */
|
||||
if (has_mime == SDL_TRUE) {
|
||||
if (has_mime) {
|
||||
dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||
} else {
|
||||
/* drag_mime is NULL this will decline the offer */
|
||||
wl_data_offer_accept(id, serial, NULL);
|
||||
}
|
||||
if (wl_data_offer_get_version(data_device->drag_offer->offer) >= 3) {
|
||||
if (wl_data_offer_get_version(data_device->drag_offer->offer) >=
|
||||
WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
|
||||
wl_data_offer_set_actions(data_device->drag_offer->offer,
|
||||
dnd_action, dnd_action);
|
||||
}
|
||||
|
@ -2041,6 +2049,37 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
|
|||
if (data_device->drag_offer != NULL) {
|
||||
/* TODO: SDL Support more mime types */
|
||||
size_t length;
|
||||
SDL_bool drop_handled = SDL_FALSE;
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
if (Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_PORTAL_MIME)) {
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_PORTAL_MIME, &length);
|
||||
if (buffer) {
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
if (dbus) {
|
||||
int path_count = 0;
|
||||
char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count);
|
||||
/* If dropped files contain a directory the list is empty */
|
||||
if (paths && path_count > 0) {
|
||||
for (int i = 0; i < path_count; i++) {
|
||||
SDL_SendDropFile(data_device->dnd_window, paths[i]);
|
||||
}
|
||||
dbus->free_string_array(paths);
|
||||
SDL_SendDropComplete(data_device->dnd_window);
|
||||
drop_handled = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
SDL_free(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* If XDG document portal fails fallback.
|
||||
* When running a flatpak sandbox this will most likely be a list of
|
||||
* non paths that are not visible to the application
|
||||
*/
|
||||
if (!drop_handled && Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_MIME)) {
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_MIME, &length);
|
||||
if (buffer) {
|
||||
|
@ -2055,8 +2094,17 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
|
|||
}
|
||||
SDL_SendDropComplete(data_device->dnd_window);
|
||||
SDL_free(buffer);
|
||||
drop_handled = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >=
|
||||
WL_DATA_OFFER_FINISH_SINCE_VERSION) {
|
||||
wl_data_offer_finish(data_device->drag_offer->offer);
|
||||
}
|
||||
Wayland_data_offer_destroy(data_device->drag_offer);
|
||||
data_device->drag_offer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
|
||||
|
|
Loading…
Reference in New Issue