This commit is contained in:
Tau Gärtli 2025-09-01 16:51:02 +02:00 committed by GitHub
commit 49cb099acf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 134 additions and 3 deletions

View File

@ -21,7 +21,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt update sudo apt update
sudo apt install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libwayland-dev libxkbcommon-dev sudo apt install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libwayland-dev libxkbcommon-dev libdbus-1-dev
- name: Configure Null shared library - name: Configure Null shared library
run: cmake -B build-null-shared -D GLFW_BUILD_WAYLAND=OFF -D GLFW_BUILD_X11=OFF -D BUILD_SHARED_LIBS=ON run: cmake -B build-null-shared -D GLFW_BUILD_WAYLAND=OFF -D GLFW_BUILD_X11=OFF -D BUILD_SHARED_LIBS=ON

View File

@ -144,6 +144,7 @@ information on what to include when reporting a bug.
a modal to a fallback decoration a modal to a fallback decoration
- [Wayland] Bugfix: The cursor position was not updated when clicking through - [Wayland] Bugfix: The cursor position was not updated when clicking through
from a modal to the content area from a modal to the content area
- [Wayland]: Add support for dropping files when sandboxed
- [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface` - [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless` - [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`

View File

@ -161,6 +161,10 @@ if (GLFW_BUILD_WAYLAND)
wayland-egl>=0.2.7 wayland-egl>=0.2.7
xkbcommon>=0.5.0) xkbcommon>=0.5.0)
pkg_check_modules(DBUS REQUIRED dbus-1)
target_include_directories(glfw PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(glfw PRIVATE ${DBUS_LIBRARIES})
target_include_directories(glfw PRIVATE ${Wayland_INCLUDE_DIRS}) target_include_directories(glfw PRIVATE ${Wayland_INCLUDE_DIRS})
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")

View File

@ -338,6 +338,7 @@ typedef struct _GLFWofferWayland
struct wl_data_offer* offer; struct wl_data_offer* offer;
GLFWbool text_plain_utf8; GLFWbool text_plain_utf8;
GLFWbool text_uri_list; GLFWbool text_uri_list;
GLFWbool portal_file_transfer;
} _GLFWofferWayland; } _GLFWofferWayland;
typedef struct _GLFWscaleWayland typedef struct _GLFWscaleWayland
@ -450,6 +451,7 @@ typedef struct _GLFWlibraryWayland
struct wl_data_offer* dragOffer; struct wl_data_offer* dragOffer;
_GLFWwindow* dragFocus; _GLFWwindow* dragFocus;
uint32_t dragSerial; uint32_t dragSerial;
GLFWbool dragUsePortal;
const char* tag; const char* tag;

View File

@ -41,6 +41,7 @@
#include <sys/timerfd.h> #include <sys/timerfd.h>
#include <poll.h> #include <poll.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <dbus/dbus.h>
#include "wayland-client-protocol.h" #include "wayland-client-protocol.h"
#include "xdg-shell-client-protocol.h" #include "xdg-shell-client-protocol.h"
@ -54,6 +55,7 @@
#define GLFW_BORDER_SIZE 4 #define GLFW_BORDER_SIZE 4
#define GLFW_CAPTION_HEIGHT 24 #define GLFW_CAPTION_HEIGHT 24
#define FILE_TRANSFER_PORTAL_MIME_TYPE "application/vnd.portal.filetransfer"
static int createTmpfileCloexec(char* tmpname) static int createTmpfileCloexec(char* tmpname)
{ {
@ -1969,6 +1971,8 @@ static void dataOfferHandleOffer(void* userData,
_glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
else if (strcmp(mimeType, "text/uri-list") == 0) else if (strcmp(mimeType, "text/uri-list") == 0)
_glfw.wl.offers[i].text_uri_list = GLFW_TRUE; _glfw.wl.offers[i].text_uri_list = GLFW_TRUE;
else if (strcmp(mimeType, FILE_TRANSFER_PORTAL_MIME_TYPE) == 0)
_glfw.wl.offers[i].portal_file_transfer = GLFW_TRUE;
break; break;
} }
@ -2031,13 +2035,19 @@ static void dataDeviceHandleEnter(void* userData,
_GLFWwindow* window = wl_surface_get_user_data(surface); _GLFWwindow* window = wl_surface_get_user_data(surface);
if (window->wl.surface == surface) if (window->wl.surface == surface)
{ {
if (_glfw.wl.offers[i].text_uri_list) GLFWbool portal = _glfw.wl.offers[i].portal_file_transfer;
if (_glfw.wl.offers[i].text_uri_list || portal)
{ {
_glfw.wl.dragOffer = offer; _glfw.wl.dragOffer = offer;
_glfw.wl.dragFocus = window; _glfw.wl.dragFocus = window;
_glfw.wl.dragSerial = serial; _glfw.wl.dragSerial = serial;
_glfw.wl.dragUsePortal = portal;
wl_data_offer_accept(offer, serial, "text/uri-list"); if (portal) {
wl_data_offer_accept(offer, serial, FILE_TRANSFER_PORTAL_MIME_TYPE);
} else {
wl_data_offer_accept(offer, serial, "text/uri-list");
}
} }
} }
} }
@ -2071,12 +2081,126 @@ static void dataDeviceHandleMotion(void* userData,
{ {
} }
// Receives a dropped file that was sent using the
// [File Transfer](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html) portal.
// This enables us to receive files when running as a Flatpak or Snap.
static void dataDeviceHandleFileTransferPortalDrop(void* userData,
struct wl_data_device* device)
{
char* key = readDataOfferAsString(_glfw.wl.dragOffer, FILE_TRANSFER_PORTAL_MIME_TYPE);
if (!key)
return;
DBusError error;
dbus_error_init(&error);
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error))
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus: %s", error.message);
dbus_error_free(&error);
_glfw_free(key);
return;
}
dbus_connection_set_exit_on_disconnect(connection, FALSE);
DBusMessage* message = dbus_message_new_method_call(
"org.freedesktop.portal.Documents",
"/org/freedesktop/portal/documents",
"org.freedesktop.portal.FileTransfer",
"RetrieveFiles"
);
if (!message)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
_glfw_free(key);
return;
}
DBusMessageIter args, options;
dbus_message_iter_init_append(message, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &key))
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
dbus_message_unref(message);
_glfw_free(key);
return;
}
if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &options))
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
dbus_message_unref(message);
_glfw_free(key);
return;
}
if (!dbus_message_iter_close_container(&args, &options))
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
dbus_message_unref(message);
_glfw_free(key);
return;
}
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_INFINITE, &error);
if (dbus_error_is_set(&error))
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus: %s", error.message);
dbus_error_free(&error);
dbus_message_unref(message);
_glfw_free(key);
return;
}
DBusMessageIter out, array;
if (!dbus_message_iter_init(reply, &out))
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
dbus_message_unref(reply);
dbus_message_unref(message);
_glfw_free(key);
return;
}
if (dbus_message_iter_get_arg_type(&out) != DBUS_TYPE_ARRAY
|| dbus_message_iter_get_element_type(&out) != DBUS_TYPE_STRING) {
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus: Reply is not an array of strings");
dbus_message_unref(reply);
dbus_message_unref(message);
_glfw_free(key);
return;
}
dbus_message_iter_recurse(&out, &array);
int elements = dbus_message_iter_get_element_count(&out);
char** paths = _glfw_calloc(elements, sizeof(char*));
if (!paths) {
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
dbus_message_unref(reply);
dbus_message_unref(message);
_glfw_free(key);
return;
}
int i = 0;
do {
dbus_message_iter_get_basic(&array, &paths[i++]);
} while (dbus_message_iter_next(&array));
_glfwInputDrop(_glfw.wl.dragFocus, elements, (const char**) paths);
_glfw_free(paths);
dbus_message_unref(reply);
dbus_message_unref(message);
_glfw_free(key);
}
static void dataDeviceHandleDrop(void* userData, static void dataDeviceHandleDrop(void* userData,
struct wl_data_device* device) struct wl_data_device* device)
{ {
if (!_glfw.wl.dragOffer) if (!_glfw.wl.dragOffer)
return; return;
if (_glfw.wl.dragUsePortal)
{
dataDeviceHandleFileTransferPortalDrop(userData, device);
return;
}
char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list"); char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
if (string) if (string)
{ {