This commit is contained in:
Tau Gärtli 2025-09-04 11:12:06 +00:00 committed by GitHub
commit 42488649eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 314 additions and 3 deletions

View File

@ -21,7 +21,7 @@ jobs:
- name: Install dependencies
run: |
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
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
- [Wayland] Bugfix: The cursor position was not updated when clicking through
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)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`

View File

@ -161,6 +161,8 @@ if (GLFW_BUILD_WAYLAND)
wayland-egl>=0.2.7
xkbcommon>=0.5.0)
set(DBUS_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
target_include_directories(glfw PRIVATE ${Wayland_INCLUDE_DIRS})
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")

View File

@ -829,6 +829,73 @@ int _glfwInitWayland(void)
}
}
_glfw.wl.dbus.handle = _glfwPlatformLoadModule("libdbus-1.so.3");
if (_glfw.wl.dbus.handle)
{
_glfw.wl.dbus.dbus_error_init_ = (PFN_dbus_error_init)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_error_init");
_glfw.wl.dbus.dbus_error_free_ = (PFN_dbus_error_free)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_error_free");
_glfw.wl.dbus.dbus_error_is_set_ = (PFN_dbus_error_is_set)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_error_is_set");
_glfw.wl.dbus.dbus_bus_get_ = (PFN_dbus_bus_get)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_bus_get");
_glfw.wl.dbus.dbus_connection_set_exit_on_disconnect_ = (PFN_dbus_connection_set_exit_on_disconnect)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_connection_set_exit_on_disconnect");
_glfw.wl.dbus.dbus_connection_send_with_reply_and_block_ = (PFN_dbus_connection_send_with_reply_and_block)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_connection_send_with_reply_and_block");
_glfw.wl.dbus.dbus_message_new_method_call_ = (PFN_dbus_message_new_method_call)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_new_method_call");
_glfw.wl.dbus.dbus_message_unref_ = (PFN_dbus_message_unref)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_unref");
_glfw.wl.dbus.dbus_message_iter_init_ = (PFN_dbus_message_iter_init)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_init");
_glfw.wl.dbus.dbus_message_iter_init_append_ = (PFN_dbus_message_iter_init_append)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_init_append");
_glfw.wl.dbus.dbus_message_iter_append_basic_ = (PFN_dbus_message_iter_append_basic)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_append_basic");
_glfw.wl.dbus.dbus_message_iter_open_container_ = (PFN_dbus_message_iter_open_container)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_open_container");
_glfw.wl.dbus.dbus_message_iter_close_container_ = (PFN_dbus_message_iter_close_container)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_close_container");
_glfw.wl.dbus.dbus_message_iter_get_arg_type_ = (PFN_dbus_message_iter_get_arg_type)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_get_arg_type");
_glfw.wl.dbus.dbus_message_iter_get_element_type_ = (PFN_dbus_message_iter_get_element_type)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_get_element_type");
_glfw.wl.dbus.dbus_message_iter_recurse_ = (PFN_dbus_message_iter_recurse)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_recurse");
_glfw.wl.dbus.dbus_message_iter_get_element_count_ = (PFN_dbus_message_iter_get_element_count)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_get_element_count");
_glfw.wl.dbus.dbus_message_iter_get_basic_ = (PFN_dbus_message_iter_get_basic)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_get_basic");
_glfw.wl.dbus.dbus_message_iter_next_ = (PFN_dbus_message_iter_next)
_glfwPlatformGetModuleSymbol(_glfw.wl.dbus.handle, "dbus_message_iter_next");
if (!_glfw.wl.dbus.dbus_error_init_ ||
!_glfw.wl.dbus.dbus_error_free_ ||
!_glfw.wl.dbus.dbus_error_is_set_ ||
!_glfw.wl.dbus.dbus_bus_get_ ||
!_glfw.wl.dbus.dbus_connection_set_exit_on_disconnect_ ||
!_glfw.wl.dbus.dbus_connection_send_with_reply_and_block_ ||
!_glfw.wl.dbus.dbus_message_new_method_call_ ||
!_glfw.wl.dbus.dbus_message_unref_ ||
!_glfw.wl.dbus.dbus_message_iter_init_ ||
!_glfw.wl.dbus.dbus_message_iter_init_append_ ||
!_glfw.wl.dbus.dbus_message_iter_append_basic_ ||
!_glfw.wl.dbus.dbus_message_iter_open_container_ ||
!_glfw.wl.dbus.dbus_message_iter_close_container_ ||
!_glfw.wl.dbus.dbus_message_iter_get_arg_type_ ||
!_glfw.wl.dbus.dbus_message_iter_get_element_type_ ||
!_glfw.wl.dbus.dbus_message_iter_recurse_ ||
!_glfw.wl.dbus.dbus_message_iter_get_element_count_ ||
!_glfw.wl.dbus.dbus_message_iter_get_basic_ ||
!_glfw.wl.dbus.dbus_message_iter_next_)
{
_glfwPlatformFreeModule(_glfw.wl.dbus.handle);
memset(&_glfw.wl.dbus, 0, sizeof(_glfw.wl.dbus));
}
}
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);

View File

@ -326,6 +326,97 @@ typedef void (* PFN_libdecor_state_free)(struct libdecor_state*);
#define libdecor_state_new _glfw.wl.libdecor.libdecor_state_new_
#define libdecor_state_free _glfw.wl.libdecor.libdecor_state_free_
#define DBUS_FALSE 0
#define DBUS_TYPE_STRING ((int) 's')
#define DBUS_TYPE_ARRAY ((int) 'a')
#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
typedef uint32_t dbus_bool_t;
typedef struct DBusError
{
const char *name;
const char *message;
unsigned int dummy1 : 1;
unsigned int dummy2 : 1;
unsigned int dummy3 : 1;
unsigned int dummy4 : 1;
unsigned int dummy5 : 1;
void *padding1;
} DBusError;
typedef struct DBusConnection DBusConnection;
typedef enum DBusBusType
{
DBUS_BUS_SESSION,
DBUS_BUS_SYSTEM,
DBUS_BUS_STARTER,
} DBusBusType;
typedef struct DBusMessage DBusMessage;
typedef struct DBusMessageIter
{
#if DBUS_SIZEOF_VOID_P > 8
void *dummy[16];
#else
void *dummy1;
void *dummy2;
uint32_t dummy3;
int dummy4;
int dummy5;
int dummy6;
int dummy7;
int dummy8;
int dummy9;
int dummy10;
int dummy11;
int pad1;
void *pad2;
void *pad3;
#endif
} DBusMessageIter;
typedef void (* PFN_dbus_error_init) (DBusError*);
typedef void (* PFN_dbus_error_free) (DBusError*);
typedef dbus_bool_t (* PFN_dbus_error_is_set) (const DBusError*);
typedef DBusConnection* (* PFN_dbus_bus_get) (enum DBusBusType, DBusError*);
typedef void (* PFN_dbus_connection_set_exit_on_disconnect) (DBusConnection*, dbus_bool_t);
typedef DBusMessage* (* PFN_dbus_connection_send_with_reply_and_block) (DBusConnection*, DBusMessage*, int, DBusError*);
typedef DBusMessage* (* PFN_dbus_message_new_method_call) (const char*, const char*, const char*, const char*);
typedef void (* PFN_dbus_message_unref) (DBusMessage*);
typedef dbus_bool_t (* PFN_dbus_message_iter_init) (DBusMessage*, DBusMessageIter*);
typedef void (* PFN_dbus_message_iter_init_append) (DBusMessage*, DBusMessageIter*);
typedef dbus_bool_t (* PFN_dbus_message_iter_append_basic) (DBusMessageIter*, int, const void*);
typedef dbus_bool_t (* PFN_dbus_message_iter_open_container) (DBusMessageIter*, int, const char*, DBusMessageIter*);
typedef dbus_bool_t (* PFN_dbus_message_iter_close_container) (DBusMessageIter*, DBusMessageIter*);
typedef int (* PFN_dbus_message_iter_get_arg_type) (DBusMessageIter*);
typedef int (* PFN_dbus_message_iter_get_element_type) (DBusMessageIter*);
typedef void (* PFN_dbus_message_iter_recurse) (DBusMessageIter*, DBusMessageIter*);
typedef int (* PFN_dbus_message_iter_get_element_count) (DBusMessageIter*);
typedef void (* PFN_dbus_message_iter_get_basic) (DBusMessageIter*, void*);
typedef dbus_bool_t (* PFN_dbus_message_iter_next) (DBusMessageIter*);
#define dbus_error_init _glfw.wl.dbus.dbus_error_init_
#define dbus_error_free _glfw.wl.dbus.dbus_error_free_
#define dbus_error_is_set _glfw.wl.dbus.dbus_error_is_set_
#define dbus_bus_get _glfw.wl.dbus.dbus_bus_get_
#define dbus_connection_set_exit_on_disconnect _glfw.wl.dbus.dbus_connection_set_exit_on_disconnect_
#define dbus_connection_send_with_reply_and_block _glfw.wl.dbus.dbus_connection_send_with_reply_and_block_
#define dbus_message_new_method_call _glfw.wl.dbus.dbus_message_new_method_call_
#define dbus_message_unref _glfw.wl.dbus.dbus_message_unref_
#define dbus_message_iter_init _glfw.wl.dbus.dbus_message_iter_init_
#define dbus_message_iter_init_append _glfw.wl.dbus.dbus_message_iter_init_append_
#define dbus_message_iter_append_basic _glfw.wl.dbus.dbus_message_iter_append_basic_
#define dbus_message_iter_open_container _glfw.wl.dbus.dbus_message_iter_open_container_
#define dbus_message_iter_close_container _glfw.wl.dbus.dbus_message_iter_close_container_
#define dbus_message_iter_get_arg_type _glfw.wl.dbus.dbus_message_iter_get_arg_type_
#define dbus_message_iter_get_element_type _glfw.wl.dbus.dbus_message_iter_get_element_type_
#define dbus_message_iter_recurse _glfw.wl.dbus.dbus_message_iter_recurse_
#define dbus_message_iter_get_element_count _glfw.wl.dbus.dbus_message_iter_get_element_count_
#define dbus_message_iter_get_basic _glfw.wl.dbus.dbus_message_iter_get_basic_
#define dbus_message_iter_next _glfw.wl.dbus.dbus_message_iter_next_
typedef struct _GLFWfallbackEdgeWayland
{
struct wl_surface* surface;
@ -338,6 +429,7 @@ typedef struct _GLFWofferWayland
struct wl_data_offer* offer;
GLFWbool text_plain_utf8;
GLFWbool text_uri_list;
GLFWbool portal_file_transfer;
} _GLFWofferWayland;
typedef struct _GLFWscaleWayland
@ -450,6 +542,7 @@ typedef struct _GLFWlibraryWayland
struct wl_data_offer* dragOffer;
_GLFWwindow* dragFocus;
uint32_t dragSerial;
GLFWbool dragUsePortal;
const char* tag;
@ -585,6 +678,29 @@ typedef struct _GLFWlibraryWayland
PFN_libdecor_state_new libdecor_state_new_;
PFN_libdecor_state_free libdecor_state_free_;
} libdecor;
struct {
void* handle;
PFN_dbus_error_init dbus_error_init_;
PFN_dbus_error_free dbus_error_free_;
PFN_dbus_error_is_set dbus_error_is_set_;
PFN_dbus_bus_get dbus_bus_get_;
PFN_dbus_connection_set_exit_on_disconnect dbus_connection_set_exit_on_disconnect_;
PFN_dbus_connection_send_with_reply_and_block dbus_connection_send_with_reply_and_block_;
PFN_dbus_message_new_method_call dbus_message_new_method_call_;
PFN_dbus_message_unref dbus_message_unref_;
PFN_dbus_message_iter_init dbus_message_iter_init_;
PFN_dbus_message_iter_init_append dbus_message_iter_init_append_;
PFN_dbus_message_iter_append_basic dbus_message_iter_append_basic_;
PFN_dbus_message_iter_open_container dbus_message_iter_open_container_;
PFN_dbus_message_iter_close_container dbus_message_iter_close_container_;
PFN_dbus_message_iter_get_arg_type dbus_message_iter_get_arg_type_;
PFN_dbus_message_iter_get_element_type dbus_message_iter_get_element_type_;
PFN_dbus_message_iter_recurse dbus_message_iter_recurse_;
PFN_dbus_message_iter_get_element_count dbus_message_iter_get_element_count_;
PFN_dbus_message_iter_get_basic dbus_message_iter_get_basic_;
PFN_dbus_message_iter_next dbus_message_iter_next_;
} dbus;
} _GLFWlibraryWayland;
// Wayland-specific per-monitor data

View File

@ -54,6 +54,7 @@
#define GLFW_BORDER_SIZE 4
#define GLFW_CAPTION_HEIGHT 24
#define FILE_TRANSFER_PORTAL_MIME_TYPE "application/vnd.portal.filetransfer"
static int createTmpfileCloexec(char* tmpname)
{
@ -1969,6 +1970,8 @@ static void dataOfferHandleOffer(void* userData,
_glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
else if (strcmp(mimeType, "text/uri-list") == 0)
_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;
}
@ -2031,16 +2034,22 @@ static void dataDeviceHandleEnter(void* userData,
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (window->wl.surface == surface)
{
if (_glfw.wl.offers[i].text_uri_list)
GLFWbool portal = _glfw.wl.offers[i].portal_file_transfer && _glfw.wl.dbus.handle;
if (_glfw.wl.offers[i].text_uri_list || portal)
{
_glfw.wl.dragOffer = offer;
_glfw.wl.dragFocus = window;
_glfw.wl.dragSerial = serial;
_glfw.wl.dragUsePortal = portal;
if (portal) {
wl_data_offer_accept(offer, serial, FILE_TRANSFER_PORTAL_MIME_TYPE);
} else {
wl_data_offer_accept(offer, serial, "text/uri-list");
}
}
}
}
if (!_glfw.wl.dragOffer)
{
@ -2071,12 +2080,128 @@ 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)
{
assert(_glfw.wl.dbus.handle != NULL);
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, DBUS_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,
struct wl_data_device* device)
{
if (!_glfw.wl.dragOffer)
return;
if (_glfw.wl.dragUsePortal)
{
dataDeviceHandleFileTransferPortalDrop(userData, device);
return;
}
char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list");
if (string)
{