Merge commit '206dcdedf195fb320913584180edb12c7731e396' as 'external/SDL'

This commit is contained in:
2026-04-02 14:13:02 +02:00
2200 changed files with 961856 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "SDL_waylanddatamanager.h"
#include "SDL_waylandevents_c.h"
#include "SDL_waylandclipboard.h"
#include "../SDL_clipboard_c.h"
#include "../../events/SDL_events_c.h"
bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat;
bool result = false;
// If no implicit grab is available yet, just attach it to the first available seat.
if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) {
seat = wl_container_of(video_data->seat_list.next, seat, link);
}
video_data->last_incoming_data_offer_seat = seat;
if (seat && seat->data_device) {
SDL_WaylandDataDevice *data_device = seat->data_device;
if (_this->clipboard_callback && _this->clipboard_mime_types) {
SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
Wayland_data_source_set_callback(source, _this->clipboard_callback, _this->clipboard_userdata, _this->clipboard_sequence);
result = Wayland_data_device_set_selection(data_device, source, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types);
if (!result) {
Wayland_data_source_destroy(source);
}
} else {
result = Wayland_data_device_clear_selection(data_device);
}
}
return result;
}
void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
void *buffer = NULL;
if (seat && seat->data_device) {
SDL_WaylandDataDevice *data_device = seat->data_device;
if (data_device->selection_source) {
buffer = SDL_GetInternalClipboardData(_this, mime_type, length);
} else if (Wayland_data_offer_has_mime(data_device->selection_offer, mime_type)) {
buffer = Wayland_data_offer_receive(data_device->selection_offer, mime_type, length, true);
}
}
return buffer;
}
bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
bool result = false;
if (seat && seat->data_device) {
SDL_WaylandDataDevice *data_device = seat->data_device;
if (data_device->selection_source) {
result = SDL_HasInternalClipboardData(_this, mime_type);
} else {
result = Wayland_data_offer_has_mime(data_device->selection_offer, mime_type);
}
}
return result;
}
static const char *text_mime_types[] = {
TEXT_MIME,
"text/plain",
"TEXT",
"UTF8_STRING",
"STRING"
};
const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
{
*num_mime_types = SDL_arraysize(text_mime_types);
return text_mime_types;
}
bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat;
bool result;
// If no implicit grab is available yet, just attach it to the first available seat.
if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) {
seat = wl_container_of(video_data->seat_list.next, seat, link);
}
video_data->last_incoming_primary_selection_seat = seat;
if (seat && seat->primary_selection_device) {
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
if (text[0] != '\0') {
SDL_WaylandPrimarySelectionSource *source = Wayland_primary_selection_source_create(_this);
Wayland_primary_selection_source_set_callback(source, SDL_ClipboardTextCallback, SDL_strdup(text));
result = Wayland_primary_selection_device_set_selection(primary_selection_device,
source,
text_mime_types,
SDL_arraysize(text_mime_types));
if (!result) {
Wayland_primary_selection_source_destroy(source);
}
} else {
result = Wayland_primary_selection_device_clear_selection(primary_selection_device);
}
} else {
result = SDL_SetError("Primary selection not supported");
}
return result;
}
char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
char *text = NULL;
size_t length = 0;
if (seat && seat->primary_selection_device) {
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
if (primary_selection_device->selection_source) {
text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, TEXT_MIME, &length);
} else {
for (size_t i = 0; i < SDL_arraysize(text_mime_types); i++) {
if (Wayland_primary_selection_offer_has_mime(primary_selection_device->selection_offer, text_mime_types[i])) {
text = Wayland_primary_selection_offer_receive(primary_selection_device->selection_offer, text_mime_types[i], &length);
break;
}
}
}
}
if (!text) {
text = SDL_strdup("");
}
return text;
}
bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
{
SDL_VideoData *video_data = _this->internal;
SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
bool result = false;
if (seat && seat->primary_selection_device) {
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
if (primary_selection_device->selection_source) {
result = true;
} else {
size_t mime_count = 0;
const char **mime_types = Wayland_GetTextMimeTypes(_this, &mime_count);
for (size_t i = 0; i < mime_count; i++) {
if (Wayland_primary_selection_offer_has_mime(primary_selection_device->selection_offer, mime_types[i])) {
result = true;
break;
}
}
}
}
return result;
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+34
View File
@@ -0,0 +1,34 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandclipboard_h_
#define SDL_waylandclipboard_h_
extern const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types);
extern bool Wayland_SetClipboardData(SDL_VideoDevice *_this);
extern void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length);
extern bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
extern bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text);
extern char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this);
extern bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this);
#endif // SDL_waylandclipboard_h_
+348
View File
@@ -0,0 +1,348 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "SDL_waylandcolor.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#include "color-management-v1-client-protocol.h"
#include "../../events/SDL_windowevents_c.h"
typedef struct Wayland_ColorInfoState
{
struct wp_image_description_v1 *wp_image_description;
struct wp_image_description_info_v1 *wp_image_description_info;
struct wl_event_queue *queue;
union
{
SDL_WindowData *window_data;
SDL_DisplayData *display_data;
};
enum
{
WAYLAND_COLOR_OBJECT_TYPE_WINDOW,
WAYLAND_COLOR_OBJECT_TYPE_DISPLAY
} object_type;
SDL_HDROutputProperties HDR;
// The ICC fd is only valid if the size is non-zero.
int icc_fd;
Uint32 icc_size;
bool deferred_event_processing;
} Wayland_ColorInfoState;
static void Wayland_CancelColorInfoRequest(Wayland_ColorInfoState *state)
{
if (state) {
if (state->wp_image_description_info) {
wp_image_description_info_v1_destroy(state->wp_image_description_info);
state->wp_image_description_info = NULL;
}
if (state->wp_image_description) {
wp_image_description_v1_destroy(state->wp_image_description);
state->wp_image_description = NULL;
}
}
}
void Wayland_FreeColorInfoState(Wayland_ColorInfoState *state)
{
if (state) {
Wayland_CancelColorInfoRequest(state);
if (state->queue) {
WAYLAND_wl_event_queue_destroy(state->queue);
}
switch (state->object_type) {
case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
state->window_data->color_info_state = NULL;
break;
case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
state->display_data->color_info_state = NULL;
break;
}
SDL_free(state);
}
}
static void image_description_info_handle_done(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
Wayland_CancelColorInfoRequest(state);
switch (state->object_type) {
case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
{
SDL_SetWindowHDRProperties(state->window_data->sdlwindow, &state->HDR, true);
if (state->icc_size) {
state->window_data->icc_fd = state->icc_fd;
state->window_data->icc_size = state->icc_size;
SDL_SendWindowEvent(state->window_data->sdlwindow, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
}
} break;
case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
{
SDL_copyp(&state->display_data->HDR, &state->HDR);
if (state->display_data->display) {
SDL_VideoDisplay *disp = SDL_GetVideoDisplay(state->display_data->display);
if (disp) {
SDL_SetDisplayHDRProperties(disp, &state->HDR);
}
} else {
SDL_copyp(&state->display_data->placeholder.HDR, &state->HDR);
}
} break;
}
}
static void image_description_info_handle_icc_file(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t icc, uint32_t icc_size)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
state->icc_fd = icc;
state->icc_size = icc_size;
}
static void image_description_info_handle_primaries(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t r_x, int32_t r_y,
int32_t g_x, int32_t g_y,
int32_t b_x, int32_t b_y,
int32_t w_x, int32_t w_y)
{
// NOP
}
static void image_description_info_handle_primaries_named(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t primaries)
{
// NOP
}
static void image_description_info_handle_tf_power(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t eexp)
{
// NOP
}
static void image_description_info_handle_tf_named(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t tf)
{
// NOP
}
static void image_description_info_handle_luminances(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t min_lum,
uint32_t max_lum,
uint32_t reference_lum)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
state->HDR.HDR_headroom = (float)max_lum / (float)reference_lum;
}
static void image_description_info_handle_target_primaries(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t r_x, int32_t r_y,
int32_t g_x, int32_t g_y,
int32_t b_x, int32_t b_y,
int32_t w_x, int32_t w_y)
{
// NOP
}
static void image_description_info_handle_target_luminance(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t min_lum,
uint32_t max_lum)
{
// NOP
}
static void image_description_info_handle_target_max_cll(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t max_cll)
{
// NOP
}
static void image_description_info_handle_target_max_fall(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t max_fall)
{
// NOP
}
static const struct wp_image_description_info_v1_listener image_description_info_listener = {
image_description_info_handle_done,
image_description_info_handle_icc_file,
image_description_info_handle_primaries,
image_description_info_handle_primaries_named,
image_description_info_handle_tf_power,
image_description_info_handle_tf_named,
image_description_info_handle_luminances,
image_description_info_handle_target_primaries,
image_description_info_handle_target_luminance,
image_description_info_handle_target_max_cll,
image_description_info_handle_target_max_fall
};
static void PumpColorspaceEvents(Wayland_ColorInfoState *state)
{
SDL_VideoData *vid = SDL_GetVideoDevice()->internal;
// Run the image description sequence to completion in its own queue.
while (state->wp_image_description) {
WAYLAND_wl_display_dispatch_queue(vid->display, state->queue);
}
Wayland_FreeColorInfoState(state);
}
static void image_description_handle_failed(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t cause,
const char *msg)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
Wayland_CancelColorInfoRequest(state);
if (state->deferred_event_processing) {
Wayland_FreeColorInfoState(state);
}
}
static void image_description_handle_ready2(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t identity_hi, uint32_t identity_lo)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
/* If event processing was deferred, then the image description is on the default queue.
* Otherwise, it will inherit the queue from the image description object.
*/
if (state->deferred_event_processing) {
SDL_VideoData *vid = SDL_GetVideoDevice()->internal;
state->queue = Wayland_DisplayCreateQueue(vid->display, "SDL Color Management Queue");
struct wl_proxy *image_desc_wrapper = WAYLAND_wl_proxy_create_wrapper(state->wp_image_description);
WAYLAND_wl_proxy_set_queue(image_desc_wrapper, state->queue);
state->wp_image_description_info = wp_image_description_v1_get_information((struct wp_image_description_v1 *)image_desc_wrapper);
WAYLAND_wl_proxy_wrapper_destroy(image_desc_wrapper);
} else {
state->wp_image_description_info = wp_image_description_v1_get_information(state->wp_image_description);
}
wp_image_description_info_v1_add_listener(state->wp_image_description_info, &image_description_info_listener, data);
if (state->deferred_event_processing) {
PumpColorspaceEvents(state);
}
}
static void image_description_handle_ready(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t identity)
{
image_description_handle_ready2(data, wp_image_description_v1, 0, identity);
}
static const struct wp_image_description_v1_listener image_description_listener = {
image_description_handle_failed,
image_description_handle_ready,
image_description_handle_ready2
};
void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing)
{
// Cancel any pending request, as it is out-of-date.
Wayland_FreeColorInfoState(window_data->color_info_state);
Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
if (state) {
window_data->color_info_state = state;
state->window_data = window_data;
state->object_type = WAYLAND_COLOR_OBJECT_TYPE_WINDOW;
state->deferred_event_processing = defer_event_processing;
if (!defer_event_processing) {
state->queue = Wayland_DisplayCreateQueue(window_data->waylandData->display, "SDL Color Management Queue");
struct wl_proxy *surface_feedback_wrapper = WAYLAND_wl_proxy_create_wrapper(window_data->wp_color_management_surface_feedback);
WAYLAND_wl_proxy_set_queue(surface_feedback_wrapper, state->queue);
state->wp_image_description = wp_color_management_surface_feedback_v1_get_preferred((struct wp_color_management_surface_feedback_v1 *)surface_feedback_wrapper);
WAYLAND_wl_proxy_wrapper_destroy(surface_feedback_wrapper);
} else {
state->wp_image_description = wp_color_management_surface_feedback_v1_get_preferred(window_data->wp_color_management_surface_feedback);
}
wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
if (!defer_event_processing) {
PumpColorspaceEvents(state);
}
}
}
void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing)
{
// Cancel any pending request, as it is out-of-date.
Wayland_FreeColorInfoState(display_data->color_info_state);
Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
if (state) {
display_data->color_info_state = state;
state->display_data = display_data;
state->object_type = WAYLAND_COLOR_OBJECT_TYPE_DISPLAY;
state->deferred_event_processing = defer_event_processing;
if (!defer_event_processing) {
state->queue = Wayland_DisplayCreateQueue(display_data->videodata->display, "SDL Color Management Queue");
struct wl_proxy *output_feedback_wrapper = WAYLAND_wl_proxy_create_wrapper(display_data->wp_color_management_output);
WAYLAND_wl_proxy_set_queue(output_feedback_wrapper, state->queue);
state->wp_image_description = wp_color_management_output_v1_get_image_description((struct wp_color_management_output_v1 *)output_feedback_wrapper);
WAYLAND_wl_proxy_wrapper_destroy(output_feedback_wrapper);
} else {
state->wp_image_description = wp_color_management_output_v1_get_image_description(display_data->wp_color_management_output);
}
wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
if (!defer_event_processing) {
PumpColorspaceEvents(state);
}
}
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+35
View File
@@ -0,0 +1,35 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandcolor_h_
#define SDL_waylandcolor_h_
#include "../SDL_sysvideo.h"
struct Wayland_ColorInfoState;
extern void Wayland_FreeColorInfoState(struct Wayland_ColorInfoState *state);
extern void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing);
extern void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing);
#endif // SDL_waylandcolor_h_
+776
View File
@@ -0,0 +1,776 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include "../../core/unix/SDL_poll.h"
#include "../../events/SDL_events_c.h"
#include "../SDL_clipboard_c.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandevents_c.h"
#include "SDL_waylanddatamanager.h"
#include "primary-selection-unstable-v1-client-protocol.h"
/* This is arbitrary, but reading while polling should block for less than a frame, to
* prevent hanging while pumping events.
*
* When querying the clipboard data directly, a larger value is needed to avoid timing
* out if the source needs to process or transfer a large amount of data.
*/
#define DEFAULT_PIPE_TIMEOUT_NS SDL_MS_TO_NS(14)
#define EXTENDED_PIPE_TIMEOUT_NS SDL_MS_TO_NS(5000)
/* sigtimedwait() is an optional part of POSIX.1-2001, and OpenBSD doesn't implement it.
* Based on https://comp.unix.programmer.narkive.com/rEDH0sPT/sigtimedwait-implementation
*/
#ifndef HAVE_SIGTIMEDWAIT
#include <errno.h>
#include <time.h>
static int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout)
{
struct timespec elapsed = { 0 }, rem = { 0 };
sigset_t pending;
do {
// Check the pending signals, and call sigwait if there is at least one of interest in the set.
sigpending(&pending);
for (int signo = 1; signo < NSIG; ++signo) {
if (sigismember(set, signo) && sigismember(&pending, signo)) {
if (!sigwait(set, &signo)) {
if (info) {
SDL_zerop(info);
info->si_signo = signo;
}
return signo;
} else {
return -1;
}
}
}
if (timeout->tv_sec || timeout->tv_nsec) {
long ns = 20000000L; // 2/100ths of a second
nanosleep(&(struct timespec){ 0, ns }, &rem);
ns -= rem.tv_nsec;
elapsed.tv_sec += (elapsed.tv_nsec + ns) / 1000000000L;
elapsed.tv_nsec = (elapsed.tv_nsec + ns) % 1000000000L;
}
} while (elapsed.tv_sec < timeout->tv_sec || (elapsed.tv_sec == timeout->tv_sec && elapsed.tv_nsec < timeout->tv_nsec));
errno = EAGAIN;
return -1;
}
#endif
static ssize_t write_pipe(int fd, const void *buffer, size_t total_length, size_t *pos)
{
int ready = 0;
ssize_t bytes_written = 0;
ssize_t length = total_length - *pos;
sigset_t sig_set;
sigset_t old_sig_set;
struct timespec zerotime = { 0 };
ready = SDL_IOReady(fd, SDL_IOR_WRITE, DEFAULT_PIPE_TIMEOUT_NS);
sigemptyset(&sig_set);
sigaddset(&sig_set, SIGPIPE);
#ifdef SDL_THREADS_DISABLED
sigprocmask(SIG_BLOCK, &sig_set, &old_sig_set);
#else
pthread_sigmask(SIG_BLOCK, &sig_set, &old_sig_set);
#endif
if (ready == 0) {
bytes_written = SDL_SetError("Pipe timeout");
} else if (ready < 0) {
bytes_written = SDL_SetError("Pipe select error");
} else {
if (length > 0) {
bytes_written = write(fd, (Uint8 *)buffer + *pos, SDL_min(length, PIPE_BUF));
}
if (bytes_written > 0) {
*pos += bytes_written;
}
}
sigtimedwait(&sig_set, NULL, &zerotime);
#ifdef SDL_THREADS_DISABLED
sigprocmask(SIG_SETMASK, &old_sig_set, NULL);
#else
pthread_sigmask(SIG_SETMASK, &old_sig_set, NULL);
#endif
return bytes_written;
}
static ssize_t read_pipe(int fd, void **buffer, size_t *total_length, Sint64 timeout_ns)
{
int ready = 0;
void *output_buffer = NULL;
char temp[PIPE_BUF];
size_t new_buffer_length = 0;
ssize_t bytes_read = 0;
size_t pos = 0;
ready = SDL_IOReady(fd, SDL_IOR_READ, timeout_ns);
if (ready == 0) {
bytes_read = SDL_SetError("Pipe timeout");
} else if (ready < 0) {
bytes_read = SDL_SetError("Pipe select error");
} else {
bytes_read = read(fd, temp, sizeof(temp));
}
if (bytes_read > 0) {
pos = *total_length;
*total_length += bytes_read;
new_buffer_length = *total_length + sizeof(Uint32);
if (!*buffer) {
output_buffer = SDL_malloc(new_buffer_length);
} else {
output_buffer = SDL_realloc(*buffer, new_buffer_length);
}
if (!output_buffer) {
bytes_read = -1;
} else {
SDL_memcpy((Uint8 *)output_buffer + pos, temp, bytes_read);
SDL_memset((Uint8 *)output_buffer + (new_buffer_length - sizeof(Uint32)), 0, sizeof(Uint32));
*buffer = output_buffer;
}
}
return bytes_read;
}
static SDL_MimeDataList *mime_data_list_find(struct wl_list *list,
const char *mime_type)
{
SDL_MimeDataList *found = NULL;
SDL_MimeDataList *mime_list = NULL;
wl_list_for_each (mime_list, list, link) {
if (SDL_strcmp(mime_list->mime_type, mime_type) == 0) {
found = mime_list;
break;
}
}
return found;
}
static bool mime_data_list_add(struct wl_list *list,
const char *mime_type,
const void *buffer, size_t length)
{
bool result = true;
size_t mime_type_length = 0;
SDL_MimeDataList *mime_data = NULL;
void *internal_buffer = NULL;
if (buffer) {
internal_buffer = SDL_malloc(length);
if (!internal_buffer) {
return false;
}
SDL_memcpy(internal_buffer, buffer, length);
}
mime_data = mime_data_list_find(list, mime_type);
if (!mime_data) {
mime_data = SDL_calloc(1, sizeof(*mime_data));
if (!mime_data) {
result = false;
} else {
WAYLAND_wl_list_insert(list, &(mime_data->link));
mime_type_length = SDL_strlen(mime_type) + 1;
mime_data->mime_type = SDL_malloc(mime_type_length);
if (!mime_data->mime_type) {
result = false;
} else {
SDL_memcpy(mime_data->mime_type, mime_type, mime_type_length);
}
}
}
if (mime_data && buffer && length > 0) {
SDL_free(mime_data->data);
mime_data->data = internal_buffer;
mime_data->length = length;
} else {
SDL_free(internal_buffer);
}
return result;
}
static void mime_data_list_free(struct wl_list *list)
{
SDL_MimeDataList *mime_data = NULL;
SDL_MimeDataList *next = NULL;
wl_list_for_each_safe (mime_data, next, list, link) {
SDL_free(mime_data->data);
SDL_free(mime_data->mime_type);
SDL_free(mime_data);
}
}
static size_t Wayland_send_data(const void *data, size_t length, int fd)
{
size_t result = 0;
if (length > 0 && data) {
while (write_pipe(fd, data, length, &result) > 0) {
// Just keep spinning
}
}
close(fd);
return result;
}
ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source, const char *mime_type, int fd)
{
const void *data = NULL;
size_t length = 0;
if (SDL_strcmp(mime_type, SDL_DATA_ORIGIN_MIME) == 0) {
data = source->data_device->id_str;
length = SDL_strlen(source->data_device->id_str);
} else if (source->callback) {
data = source->callback(source->userdata.data, mime_type, &length);
}
return Wayland_send_data(data, length, fd);
}
ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source, const char *mime_type, int fd)
{
const void *data = NULL;
size_t length = 0;
if (source->callback) {
data = source->callback(source->userdata.data, mime_type, &length);
}
return Wayland_send_data(data, length, fd);
}
void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
SDL_ClipboardDataCallback callback,
void *userdata,
Uint32 sequence)
{
if (source) {
source->callback = callback;
source->userdata.sequence = sequence;
source->userdata.data = userdata;
}
}
void Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSource *source,
SDL_ClipboardDataCallback callback,
void *userdata)
{
if (source) {
source->callback = callback;
source->userdata.sequence = 0;
source->userdata.data = userdata;
}
}
static void *Wayland_clone_data_buffer(const void *buffer, const size_t *len)
{
void *clone = NULL;
if (*len > 0 && buffer) {
clone = SDL_malloc((*len)+sizeof(Uint32));
if (clone) {
SDL_memcpy(clone, buffer, *len);
SDL_memset((Uint8 *)clone + *len, 0, sizeof(Uint32));
}
}
return clone;
}
void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
const char *mime_type, size_t *length)
{
void *buffer = NULL;
const void *internal_buffer;
*length = 0;
if (!source) {
SDL_SetError("Invalid data source");
} else if (source->callback) {
internal_buffer = source->callback(source->userdata.data, mime_type, length);
buffer = Wayland_clone_data_buffer(internal_buffer, length);
}
return buffer;
}
void *Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type, size_t *length)
{
void *buffer = NULL;
const void *internal_buffer;
*length = 0;
if (!source) {
SDL_SetError("Invalid primary selection source");
} else if (source->callback) {
internal_buffer = source->callback(source->userdata.data, mime_type, length);
buffer = Wayland_clone_data_buffer(internal_buffer, length);
}
return buffer;
}
void Wayland_data_source_destroy(SDL_WaylandDataSource *source)
{
if (source) {
SDL_WaylandDataDevice *data_device = (SDL_WaylandDataDevice *)source->data_device;
if (data_device && (data_device->selection_source == source)) {
data_device->selection_source = NULL;
}
wl_data_source_destroy(source->source);
if (source->userdata.sequence) {
SDL_CancelClipboardData(source->userdata.sequence);
} else {
SDL_free(source->userdata.data);
}
SDL_free(source);
}
}
void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource *source)
{
if (source) {
SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *)source->primary_selection_device;
if (primary_selection_device && (primary_selection_device->selection_source == source)) {
primary_selection_device->selection_source = NULL;
}
zwp_primary_selection_source_v1_destroy(source->source);
if (source->userdata.sequence == 0) {
SDL_free(source->userdata.data);
}
SDL_free(source);
}
}
static void offer_source_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
if (!callback) {
return;
}
SDL_WaylandDataOffer *offer = data;
char *id = NULL;
size_t length = 0;
wl_callback_destroy(offer->callback);
offer->callback = NULL;
while (read_pipe(offer->read_fd, (void **)&id, &length, DEFAULT_PIPE_TIMEOUT_NS) > 0) {
}
close(offer->read_fd);
offer->read_fd = -1;
if (id) {
const bool source_is_external = SDL_strncmp(offer->data_device->id_str, id, length) != 0;
SDL_free(id);
if (source_is_external) {
Wayland_data_offer_notify_from_mimes(offer, false);
}
}
}
static struct wl_callback_listener offer_source_listener = {
offer_source_done_handler
};
static void Wayland_data_offer_check_source(SDL_WaylandDataOffer *offer, const char *mime_type)
{
SDL_WaylandDataDevice *data_device = NULL;
int pipefd[2];
if (!offer) {
SDL_SetError("Invalid data offer");
return;
}
data_device = offer->data_device;
if (!data_device) {
SDL_SetError("Data device not initialized");
} else if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) {
SDL_SetError("Could not read pipe");
} else {
if (offer->callback) {
wl_callback_destroy(offer->callback);
}
if (offer->read_fd >= 0) {
close(offer->read_fd);
}
offer->read_fd = pipefd[0];
wl_data_offer_receive(offer->offer, mime_type, pipefd[1]);
close(pipefd[1]);
offer->callback = wl_display_sync(offer->data_device->seat->display->display);
wl_callback_add_listener(offer->callback, &offer_source_listener, offer);
WAYLAND_wl_display_flush(data_device->seat->display->display);
}
}
void Wayland_data_offer_notify_from_mimes(SDL_WaylandDataOffer *offer, bool check_origin)
{
int nformats = 0;
char **new_mime_types = NULL;
if (offer) {
size_t alloc_size = 0;
// Do a first pass to compute allocation size.
SDL_MimeDataList *item = NULL;
wl_list_for_each(item, &offer->mimes, link) {
// If origin metadata is found, queue a check and wait for confirmation that this offer isn't recursive.
if (check_origin && SDL_strcmp(item->mime_type, SDL_DATA_ORIGIN_MIME) == 0) {
Wayland_data_offer_check_source(offer, item->mime_type);
return;
}
++nformats;
alloc_size += SDL_strlen(item->mime_type) + 1;
}
alloc_size += (nformats + 1) * sizeof(char *);
new_mime_types = SDL_AllocateTemporaryMemory(alloc_size);
if (!new_mime_types) {
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "unable to allocate new_mime_types");
return;
}
// Second pass to fill.
char *strPtr = (char *)(new_mime_types + nformats + 1);
item = NULL;
int i = 0;
wl_list_for_each(item, &offer->mimes, link) {
new_mime_types[i] = strPtr;
strPtr = stpcpy(strPtr, item->mime_type) + 1;
i++;
}
new_mime_types[nformats] = NULL;
}
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
}
void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout)
{
SDL_WaylandDataDevice *data_device = NULL;
const Sint64 timeout = extended_timeout ? EXTENDED_PIPE_TIMEOUT_NS : DEFAULT_PIPE_TIMEOUT_NS;
int pipefd[2];
void *buffer = NULL;
*length = 0;
if (!offer) {
SDL_SetError("Invalid data offer");
return NULL;
}
data_device = offer->data_device;
if (!data_device) {
SDL_SetError("Data device not initialized");
} else if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) {
SDL_SetError("Could not read pipe");
} else {
wl_data_offer_receive(offer->offer, mime_type, pipefd[1]);
close(pipefd[1]);
WAYLAND_wl_display_flush(data_device->seat->display->display);
while (read_pipe(pipefd[0], &buffer, length, timeout) > 0) {
}
close(pipefd[0]);
}
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In Wayland_data_offer_receive for '%s', buffer (%zu) at %p",
mime_type, *length, buffer);
return buffer;
}
void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type, size_t *length)
{
SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
int pipefd[2];
void *buffer = NULL;
*length = 0;
if (!offer) {
SDL_SetError("Invalid data offer");
return NULL;
}
primary_selection_device = offer->primary_selection_device;
if (!primary_selection_device) {
SDL_SetError("Primary selection device not initialized");
} else if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) {
SDL_SetError("Could not read pipe");
} else {
zwp_primary_selection_offer_v1_receive(offer->offer, mime_type, pipefd[1]);
close(pipefd[1]);
WAYLAND_wl_display_flush(primary_selection_device->seat->display->display);
while (read_pipe(pipefd[0], &buffer, length, EXTENDED_PIPE_TIMEOUT_NS) > 0) {
}
close(pipefd[0]);
}
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In Wayland_primary_selection_offer_receive for '%s', buffer (%zu) at %p",
mime_type, *length, buffer);
return buffer;
}
bool Wayland_data_offer_add_mime(SDL_WaylandDataOffer *offer,
const char *mime_type)
{
return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
}
bool Wayland_primary_selection_offer_add_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type)
{
return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
}
bool Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
const char *mime_type)
{
bool found = false;
if (offer) {
found = mime_data_list_find(&offer->mimes, mime_type) != NULL;
}
return found;
}
bool Wayland_primary_selection_offer_has_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type)
{
bool found = false;
if (offer) {
found = mime_data_list_find(&offer->mimes, mime_type) != NULL;
}
return found;
}
void Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer)
{
if (offer) {
if (offer->callback) {
wl_callback_destroy(offer->callback);
}
if (offer->read_fd >= 0) {
close(offer->read_fd);
}
wl_data_offer_destroy(offer->offer);
mime_data_list_free(&offer->mimes);
SDL_free(offer);
}
}
void Wayland_primary_selection_offer_destroy(SDL_WaylandPrimarySelectionOffer *offer)
{
if (offer) {
zwp_primary_selection_offer_v1_destroy(offer->offer);
mime_data_list_free(&offer->mimes);
SDL_free(offer);
}
}
bool Wayland_data_device_clear_selection(SDL_WaylandDataDevice *data_device)
{
bool result = true;
if (!data_device || !data_device->data_device) {
result = SDL_SetError("Invalid Data Device");
} else if (data_device->selection_source) {
wl_data_device_set_selection(data_device->data_device, NULL, data_device->seat->last_implicit_grab_serial);
Wayland_data_source_destroy(data_device->selection_source);
data_device->selection_source = NULL;
}
return result;
}
bool Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device)
{
bool result = true;
if (!primary_selection_device || !primary_selection_device->primary_selection_device) {
result = SDL_SetError("Invalid Primary Selection Device");
} else if (primary_selection_device->selection_source) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
NULL, primary_selection_device->seat->last_implicit_grab_serial);
Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
primary_selection_device->selection_source = NULL;
}
return result;
}
bool Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
SDL_WaylandDataSource *source,
const char **mime_types,
size_t mime_count)
{
bool result = true;
if (!data_device) {
result = SDL_SetError("Invalid Data Device");
} else if (!source) {
result = SDL_SetError("Invalid source");
} else {
size_t index = 0;
const char *mime_type;
for (index = 0; index < mime_count; ++index) {
mime_type = mime_types[index];
wl_data_source_offer(source->source,
mime_type);
}
// Advertise the data origin MIME
wl_data_source_offer(source->source, SDL_DATA_ORIGIN_MIME);
if (index == 0) {
Wayland_data_device_clear_selection(data_device);
result = SDL_SetError("No mime data");
} else {
// Only set if there is a valid serial if not set it later
if (data_device->selection_serial != 0) {
wl_data_device_set_selection(data_device->data_device,
source->source,
data_device->selection_serial);
}
if (data_device->selection_source) {
Wayland_data_source_destroy(data_device->selection_source);
}
data_device->selection_source = source;
source->data_device = data_device;
}
}
return result;
}
bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
SDL_WaylandPrimarySelectionSource *source,
const char **mime_types,
size_t mime_count)
{
bool result = true;
if (!primary_selection_device) {
result = SDL_SetError("Invalid Primary Selection Device");
} else if (!source) {
result = SDL_SetError("Invalid source");
} else {
size_t index = 0;
const char *mime_type = mime_types[index];
for (index = 0; index < mime_count; ++index) {
mime_type = mime_types[index];
zwp_primary_selection_source_v1_offer(source->source, mime_type);
}
if (index == 0) {
Wayland_primary_selection_device_clear_selection(primary_selection_device);
result = SDL_SetError("No mime data");
} else {
// Only set if there is a valid serial if not set it later
if (primary_selection_device->selection_serial != 0) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
source->source,
primary_selection_device->selection_serial);
}
if (primary_selection_device->selection_source) {
Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
}
primary_selection_device->selection_source = source;
source->primary_selection_device = primary_selection_device;
}
}
return result;
}
void Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device, uint32_t serial)
{
if (data_device) {
data_device->selection_serial = serial;
// If there was no serial and there is a pending selection set it now.
if (data_device->selection_serial == 0 && data_device->selection_source) {
wl_data_device_set_selection(data_device->data_device,
data_device->selection_source->source,
data_device->selection_serial);
}
}
}
void Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
uint32_t serial)
{
if (primary_selection_device) {
primary_selection_device->selection_serial = serial;
// If there was no serial and there is a pending selection set it now.
if (primary_selection_device->selection_serial == 0 && primary_selection_device->selection_source) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
primary_selection_device->selection_source->source,
primary_selection_device->selection_serial);
}
}
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+176
View File
@@ -0,0 +1,176 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_waylandevents_c.h"
#ifndef SDL_waylanddatamanager_h_
#define SDL_waylanddatamanager_h_
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#define TEXT_MIME "text/plain;charset=utf-8"
#define FILE_MIME "text/uri-list"
#define FILE_PORTAL_MIME "application/vnd.portal.filetransfer"
#define SDL_DATA_ORIGIN_MIME "application/x-sdl3-source-id"
typedef struct SDL_WaylandDataDevice SDL_WaylandDataDevice;
typedef struct SDL_WaylandPrimarySelectionDevice SDL_WaylandPrimarySelectionDevice;
typedef struct
{
char *mime_type;
void *data;
size_t length;
struct wl_list link;
} SDL_MimeDataList;
typedef struct SDL_WaylandUserdata
{
Uint32 sequence;
void *data;
} SDL_WaylandUserdata;
typedef struct
{
struct wl_data_source *source;
SDL_WaylandDataDevice *data_device;
SDL_ClipboardDataCallback callback;
SDL_WaylandUserdata userdata;
} SDL_WaylandDataSource;
typedef struct
{
struct zwp_primary_selection_source_v1 *source;
SDL_WaylandDataDevice *data_device;
SDL_WaylandPrimarySelectionDevice *primary_selection_device;
SDL_ClipboardDataCallback callback;
SDL_WaylandUserdata userdata;
} SDL_WaylandPrimarySelectionSource;
typedef struct
{
struct wl_data_offer *offer;
struct wl_list mimes;
SDL_WaylandDataDevice *data_device;
// Callback data for queued receive.
struct wl_callback *callback;
int read_fd;
} SDL_WaylandDataOffer;
typedef struct
{
struct zwp_primary_selection_offer_v1 *offer;
struct wl_list mimes;
SDL_WaylandPrimarySelectionDevice *primary_selection_device;
} SDL_WaylandPrimarySelectionOffer;
struct SDL_WaylandDataDevice
{
struct wl_data_device *data_device;
struct SDL_WaylandSeat *seat;
char *id_str;
// Drag and Drop
uint32_t drag_serial;
SDL_WaylandDataOffer *drag_offer;
SDL_WaylandDataOffer *selection_offer;
const char *mime_type;
bool has_mime_file, has_mime_text;
SDL_Window *dnd_window;
// Clipboard and Primary Selection
uint32_t selection_serial;
SDL_WaylandDataSource *selection_source;
};
struct SDL_WaylandPrimarySelectionDevice
{
struct zwp_primary_selection_device_v1 *primary_selection_device;
struct SDL_WaylandSeat *seat;
uint32_t selection_serial;
SDL_WaylandPrimarySelectionSource *selection_source;
SDL_WaylandPrimarySelectionOffer *selection_offer;
};
// Wayland Data Source / Primary Selection Source - (Sending)
extern SDL_WaylandDataSource *Wayland_data_source_create(SDL_VideoDevice *_this);
extern SDL_WaylandPrimarySelectionSource *Wayland_primary_selection_source_create(SDL_VideoDevice *_this);
extern ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source,
const char *mime_type, int fd);
extern ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type, int fd);
extern void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
SDL_ClipboardDataCallback callback,
void *userdata,
Uint32 sequence);
extern void Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSource *source,
SDL_ClipboardDataCallback callback,
void *userdata);
extern void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
const char *mime_type,
size_t *length);
extern void *Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type,
size_t *length);
extern void Wayland_data_source_destroy(SDL_WaylandDataSource *source);
extern void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource *source);
// Wayland Data / Primary Selection Offer - (Receiving)
extern void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
const char *mime_type,
size_t *length,
bool extended_timeout);
extern void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type,
size_t *length);
extern bool Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
const char *mime_type);
extern void Wayland_data_offer_notify_from_mimes(SDL_WaylandDataOffer *offer,
bool check_origin);
extern bool Wayland_primary_selection_offer_has_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type);
extern bool Wayland_data_offer_add_mime(SDL_WaylandDataOffer *offer,
const char *mime_type);
extern bool Wayland_primary_selection_offer_add_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type);
extern void Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer);
extern void Wayland_primary_selection_offer_destroy(SDL_WaylandPrimarySelectionOffer *offer);
// Clipboard / Primary Selection
extern bool Wayland_data_device_clear_selection(SDL_WaylandDataDevice *device);
extern bool Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelectionDevice *device);
extern bool Wayland_data_device_set_selection(SDL_WaylandDataDevice *device,
SDL_WaylandDataSource *source,
const char **mime_types,
size_t mime_count);
extern bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *device,
SDL_WaylandPrimarySelectionSource *source,
const char **mime_types,
size_t mime_count);
extern void Wayland_data_device_set_serial(SDL_WaylandDataDevice *device,
uint32_t serial);
extern void Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *device,
uint32_t serial);
#endif // SDL_waylanddatamanager_h_
+217
View File
@@ -0,0 +1,217 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#define DEBUG_DYNAMIC_WAYLAND 0
#include "SDL_waylanddyn.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
SDL_ELF_NOTE_DLOPEN(
"wayland",
"Support for Wayland video",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
)
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
SDL_ELF_NOTE_DLOPEN(
"wayland",
"Support for Wayland video",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
)
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR
SDL_ELF_NOTE_DLOPEN(
"wayland",
"Support for Wayland video",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR
)
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON
SDL_ELF_NOTE_DLOPEN(
"wayland",
"Support for Wayland video",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON
)
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
SDL_ELF_NOTE_DLOPEN(
"wayland",
"Support for Wayland video",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
)
#endif
typedef struct
{
SDL_SharedObject *lib;
const char *libname;
const char *hint;
bool hint_default;
} waylanddynlib;
static waylanddynlib waylandlibs[] = {
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC, NULL, false },
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL, NULL, false },
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR, NULL, false },
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON, NULL, false },
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR, SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, true },
#endif
{ NULL, NULL, NULL, false }
};
static void *WAYLAND_GetSym(const char *fnname, int *pHasModule, bool required)
{
void *fn = NULL;
waylanddynlib *dynlib;
for (dynlib = waylandlibs; dynlib->libname; dynlib++) {
if (dynlib->lib) {
fn = SDL_LoadFunction(dynlib->lib, fnname);
if (fn) {
break;
}
}
}
#if DEBUG_DYNAMIC_WAYLAND
if (fn) {
SDL_Log("WAYLAND: Found '%s' in %s (%p)", fnname, dynlib->libname, fn);
} else {
SDL_Log("WAYLAND: Symbol '%s' NOT FOUND!", fnname);
}
#endif
if (!fn && required) {
*pHasModule = 0; // kill this module.
}
return fn;
}
#else
#include <wayland-egl.h>
#endif // SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
// Define all the function pointers and wrappers...
#define SDL_WAYLAND_MODULE(modname) int SDL_WAYLAND_HAVE_##modname = 0;
#define SDL_WAYLAND_SYM(rc, fn, params) SDL_DYNWAYLANDFN_##fn WAYLAND_##fn = NULL;
#define SDL_WAYLAND_SYM_OPT(rc, fn, params) SDL_DYNWAYLANDFN_##fn WAYLAND_##fn = NULL;
#include "SDL_waylandsym.h"
static int wayland_load_refcount = 0;
void SDL_WAYLAND_UnloadSymbols(void)
{
// Don't actually unload if more than one module is using the libs...
if (wayland_load_refcount > 0) {
if (--wayland_load_refcount == 0) {
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
int i;
#endif
// set all the function pointers to NULL.
#define SDL_WAYLAND_MODULE(modname) SDL_WAYLAND_HAVE_##modname = 0;
#define SDL_WAYLAND_SYM(rc, fn, params) WAYLAND_##fn = NULL;
#define SDL_WAYLAND_SYM_OPT(rc, fn, params) WAYLAND_##fn = NULL;
#include "SDL_waylandsym.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
for (i = 0; i < SDL_arraysize(waylandlibs); i++) {
if (waylandlibs[i].lib) {
SDL_UnloadObject(waylandlibs[i].lib);
waylandlibs[i].lib = NULL;
}
}
#endif
}
}
}
// returns non-zero if all needed symbols were loaded.
bool SDL_WAYLAND_LoadSymbols(void)
{
bool result = true; // always succeed if not using Dynamic WAYLAND stuff.
// deal with multiple modules (dga, wayland, etc) needing these symbols...
if (wayland_load_refcount++ == 0) {
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
int i;
int *thismod = NULL;
for (i = 0; i < SDL_arraysize(waylandlibs); i++) {
if (waylandlibs[i].libname) {
if (waylandlibs[i].hint &&
!SDL_GetHintBoolean(waylandlibs[i].hint, waylandlibs[i].hint_default)) {
continue;
}
waylandlibs[i].lib = SDL_LoadObject(waylandlibs[i].libname);
}
}
#define SDL_WAYLAND_MODULE(modname) SDL_WAYLAND_HAVE_##modname = 1; // default yes
#include "SDL_waylandsym.h"
#define SDL_WAYLAND_MODULE(modname) thismod = &SDL_WAYLAND_HAVE_##modname;
#define SDL_WAYLAND_SYM(rc, fn, params) WAYLAND_##fn = (SDL_DYNWAYLANDFN_##fn)WAYLAND_GetSym(#fn, thismod, true);
#define SDL_WAYLAND_SYM_OPT(rc, fn, params) WAYLAND_##fn = (SDL_DYNWAYLANDFN_##fn)WAYLAND_GetSym(#fn, thismod, false);
#include "SDL_waylandsym.h"
if (SDL_WAYLAND_HAVE_WAYLAND_CLIENT &&
SDL_WAYLAND_HAVE_WAYLAND_CURSOR &&
SDL_WAYLAND_HAVE_WAYLAND_EGL &&
SDL_WAYLAND_HAVE_WAYLAND_XKB) {
// All required symbols loaded, only libdecor is optional.
SDL_ClearError();
} else {
// in case something got loaded...
SDL_WAYLAND_UnloadSymbols();
result = false;
}
#else // no dynamic WAYLAND
#define SDL_WAYLAND_MODULE(modname) SDL_WAYLAND_HAVE_##modname = 1; // default yes
#define SDL_WAYLAND_SYM(rc, fn, params) WAYLAND_##fn = fn;
#define SDL_WAYLAND_SYM_OPT(rc, fn, params) WAYLAND_##fn = fn;
#include "SDL_waylandsym.h"
#endif
}
return result;
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+184
View File
@@ -0,0 +1,184 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_waylanddyn_h_
#define SDL_waylanddyn_h_
#include "SDL_internal.h"
/* We can't include wayland-client.h here
* but we need some structs from it
*/
struct wl_interface;
struct wl_proxy;
struct wl_event_queue;
struct wl_display;
struct wl_surface;
struct wl_shm;
// We also need some for libdecor
struct wl_seat;
struct wl_output;
struct libdecor;
struct libdecor_frame;
struct libdecor_state;
struct libdecor_configuration;
struct libdecor_interface;
struct libdecor_frame_interface;
enum libdecor_resize_edge;
enum libdecor_capabilities;
enum libdecor_window_state;
#include "wayland-cursor.h"
#include "wayland-util.h"
#include "xkbcommon/xkbcommon.h"
#include "xkbcommon/xkbcommon-compose.h"
// Must be included before our #defines, see Bugzilla #4957
#include "wayland-client-core.h"
#define SDL_WAYLAND_CHECK_VERSION(x, y, z) \
(WAYLAND_VERSION_MAJOR > x || \
(WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR > y) || \
(WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR == y && WAYLAND_VERSION_MICRO >= z))
#define SDL_XKBCOMMON_CHECK_VERSION(x, y, z) \
(SDL_XKBCOMMON_VERSION_MAJOR > x || \
(SDL_XKBCOMMON_VERSION_MAJOR == x && SDL_XKBCOMMON_VERSION_MINOR > y) || \
(SDL_XKBCOMMON_VERSION_MAJOR == x && SDL_XKBCOMMON_VERSION_MINOR == y && SDL_XKBCOMMON_VERSION_PATCH >= z))
#ifdef HAVE_LIBDECOR_H
#define SDL_LIBDECOR_CHECK_VERSION(x, y, z) \
(SDL_LIBDECOR_VERSION_MAJOR > x || \
(SDL_LIBDECOR_VERSION_MAJOR == x && SDL_LIBDECOR_VERSION_MINOR > y) || \
(SDL_LIBDECOR_VERSION_MAJOR == x && SDL_LIBDECOR_VERSION_MINOR == y && SDL_LIBDECOR_VERSION_PATCH >= z))
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern bool SDL_WAYLAND_LoadSymbols(void);
extern void SDL_WAYLAND_UnloadSymbols(void);
#define SDL_WAYLAND_MODULE(modname) extern int SDL_WAYLAND_HAVE_##modname;
#define SDL_WAYLAND_SYM(rc, fn, params) \
typedef rc(*SDL_DYNWAYLANDFN_##fn) params; \
extern SDL_DYNWAYLANDFN_##fn WAYLAND_##fn;
#define SDL_WAYLAND_SYM_OPT(rc, fn, params) \
typedef rc(*SDL_DYNWAYLANDFN_##fn) params; \
extern SDL_DYNWAYLANDFN_##fn WAYLAND_##fn;
#define SDL_WAYLAND_INTERFACE(iface) extern const struct wl_interface *WAYLAND_##iface;
#include "SDL_waylandsym.h"
#ifdef __cplusplus
}
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
#if defined(_WAYLAND_CLIENT_H) || defined(WAYLAND_CLIENT_H)
#error Do not include wayland-client ahead of SDL_waylanddyn.h in dynamic loading mode
#endif
/* wayland-client-protocol.h included from wayland-client.h
* has inline functions that require these to be defined in dynamic loading mode
*/
#define wl_proxy_create (*WAYLAND_wl_proxy_create)
#define wl_proxy_destroy (*WAYLAND_wl_proxy_destroy)
#define wl_proxy_marshal (*WAYLAND_wl_proxy_marshal)
#define wl_proxy_set_user_data (*WAYLAND_wl_proxy_set_user_data)
#define wl_proxy_get_user_data (*WAYLAND_wl_proxy_get_user_data)
#define wl_proxy_get_version (*WAYLAND_wl_proxy_get_version)
#define wl_proxy_add_listener (*WAYLAND_wl_proxy_add_listener)
#define wl_proxy_marshal_constructor (*WAYLAND_wl_proxy_marshal_constructor)
#define wl_proxy_marshal_constructor_versioned (*WAYLAND_wl_proxy_marshal_constructor_versioned)
#define wl_proxy_set_tag (*WAYLAND_wl_proxy_set_tag)
#define wl_proxy_get_tag (*WAYLAND_wl_proxy_get_tag)
#define wl_proxy_marshal_flags (*WAYLAND_wl_proxy_marshal_flags)
#define wl_proxy_marshal_array_flags (*WAYLAND_wl_proxy_marshal_array_flags)
#define wl_display_reconnect (*WAYLAND_wl_display_reconnect)
/*
* These must be included before libdecor.h, otherwise the libdecor header
* pulls in the system Wayland protocol headers instead of ours.
*/
#include "wayland-client-protocol.h"
#include "wayland-egl.h"
#ifdef HAVE_LIBDECOR_H
// Must be included before our defines
#include <libdecor.h>
#define libdecor_unref (*WAYLAND_libdecor_unref)
#define libdecor_new (*WAYLAND_libdecor_new)
#define libdecor_decorate (*WAYLAND_libdecor_decorate)
#define libdecor_frame_unref (*WAYLAND_libdecor_frame_unref)
#define libdecor_frame_set_title (*WAYLAND_libdecor_frame_set_title)
#define libdecor_frame_set_app_id (*WAYLAND_libdecor_frame_set_app_id)
#define libdecor_frame_set_max_content_size (*WAYLAND_libdecor_frame_set_max_content_size)
#define libdecor_frame_get_max_content_size (*WAYLAND_libdecor_frame_get_max_content_size)
#define libdecor_frame_set_min_content_size (*WAYLAND_libdecor_frame_set_min_content_size)
#define libdecor_frame_get_min_content_size (*WAYLAND_libdecor_frame_get_min_content_size)
#define libdecor_frame_resize (*WAYLAND_libdecor_frame_resize)
#define libdecor_frame_move (*WAYLAND_libdecor_frame_move)
#define libdecor_frame_commit (*WAYLAND_libdecor_frame_commit)
#define libdecor_frame_set_minimized (*WAYLAND_libdecor_frame_set_minimized)
#define libdecor_frame_set_maximized (*WAYLAND_libdecor_frame_set_maximized)
#define libdecor_frame_unset_maximized (*WAYLAND_libdecor_frame_unset_maximized)
#define libdecor_frame_set_fullscreen (*WAYLAND_libdecor_frame_set_fullscreen)
#define libdecor_frame_unset_fullscreen (*WAYLAND_libdecor_frame_unset_fullscreen)
#define libdecor_frame_set_capabilities (*WAYLAND_libdecor_frame_set_capabilities)
#define libdecor_frame_unset_capabilities (*WAYLAND_libdecor_frame_unset_capabilities)
#define libdecor_frame_has_capability (*WAYLAND_libdecor_frame_has_capability)
#define libdecor_frame_set_visibility (*WAYLAND_libdecor_frame_set_visibility)
#define libdecor_frame_is_visible (*WAYLAND_libdecor_frame_is_visible)
#define libdecor_frame_is_floating (*WAYLAND_libdecor_frame_is_floating)
#define libdecor_frame_set_parent (*WAYLAND_libdecor_frame_set_parent)
#define libdecor_frame_show_window_menu (*WAYLAND_libdecor_frame_show_window_menu)
#define libdecor_frame_get_wm_capabilities (*WAYLAND_libdecor_frame_get_wm_capabilities)
#define libdecor_frame_get_xdg_surface (*WAYLAND_libdecor_frame_get_xdg_surface)
#define libdecor_frame_get_xdg_toplevel (*WAYLAND_libdecor_frame_get_xdg_toplevel)
#define libdecor_frame_translate_coordinate (*WAYLAND_libdecor_frame_translate_coordinate)
#define libdecor_frame_map (*WAYLAND_libdecor_frame_map)
#define libdecor_state_new (*WAYLAND_libdecor_state_new)
#define libdecor_state_free (*WAYLAND_libdecor_state_free)
#define libdecor_configuration_get_content_size (*WAYLAND_libdecor_configuration_get_content_size)
#define libdecor_configuration_get_window_state (*WAYLAND_libdecor_configuration_get_window_state)
#define libdecor_dispatch (*WAYLAND_libdecor_dispatch)
#endif
#else // SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
/*
* These must be included before libdecor.h, otherwise the libdecor header
* pulls in the system Wayland protocol headers instead of ours.
*/
#include "wayland-client-protocol.h"
#include "wayland-egl.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
#endif
#endif // SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC
#endif // SDL_waylanddyn_h_
File diff suppressed because it is too large Load Diff
+306
View File
@@ -0,0 +1,306 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandevents_h_
#define SDL_waylandevents_h_
#include "../../events/SDL_keymap_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_pen_c.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#include "SDL_waylanddatamanager.h"
enum SDL_WaylandAxisEvent
{
SDL_WAYLAND_AXIS_EVENT_CONTINUOUS = 0,
SDL_WAYLAND_AXIS_EVENT_DISCRETE,
SDL_WAYLAND_AXIS_EVENT_VALUE120
};
typedef struct
{
Sint32 repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
Sint32 repeat_delay_ms; // Time to first repeat event in milliseconds
Uint32 keyboard_id; // ID of the source keyboard.
bool is_initialized;
bool is_key_down;
Uint32 key;
Uint32 wl_press_time_ms; // Key press time as reported by the Wayland API in milliseconds
Uint64 base_time_ns; // Key press time as reported by the Wayland API in nanoseconds
Uint64 sdl_press_time_ns; // Key press time expressed in SDL ticks
Uint64 next_repeat_ns; // Next repeat event in nanoseconds
Uint32 scancode;
char text[8];
} SDL_WaylandKeyboardRepeat;
typedef struct SDL_WaylandCursorState
{
SDL_CursorData *current_cursor;
struct wp_cursor_shape_device_v1 *cursor_shape;
struct wl_surface *surface;
struct wp_viewport *viewport;
double scale;
// Pointer to the internal data for system cursors.
void *system_cursor_handle;
// The cursor animation thread lock must be held when modifying this.
struct wl_callback *frame_callback;
Uint64 last_frame_callback_time_ms;
Uint32 current_frame_time_ms;
// 0 or greater if a buffer is attached, -1 if in the reset state.
int current_frame;
SDL_HitTestResult hit_test_result;
} SDL_WaylandCursorState;
typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
{
SDL_PenID instance_id;
SDL_PenInfo info;
SDL_WindowData *focus;
struct zwp_tablet_tool_v2 *wltool;
Uint32 proximity_serial;
struct
{
float x;
float y;
float axes[SDL_PEN_AXIS_COUNT];
Uint32 axes_set;
enum
{
WAYLAND_TABLET_TOOL_BUTTON_NONE = 0,
WAYLAND_TABLET_TOOL_BUTTON_DOWN,
WAYLAND_TABLET_TOOL_BUTTON_UP
} buttons[3];
enum
{
WAYLAND_TABLET_TOOL_STATE_NONE = 0,
WAYLAND_TABLET_TOOL_STATE_DOWN,
WAYLAND_TABLET_TOOL_STATE_UP
} tool_state;
bool in_proximity;
bool have_motion;
bool have_proximity;
} frame;
SDL_WaylandCursorState cursor_state;
struct wl_list link;
} SDL_WaylandPenTool;
typedef struct SDL_WaylandSeat
{
SDL_VideoData *display;
struct wl_seat *wl_seat;
SDL_WaylandDataDevice *data_device;
SDL_WaylandPrimarySelectionDevice *primary_selection_device;
char *name;
struct wl_list link;
Uint32 last_implicit_grab_serial; // The serial of the last implicit grab event for window activation and selection data.
Uint32 registry_id; // The ID of the Wayland seat object,
struct
{
struct wl_keyboard *wl_keyboard;
struct zwp_input_timestamps_v1 *timestamps;
struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor;
SDL_WindowData *focus;
SDL_Keymap **sdl_keymap;
char *current_locale;
SDL_WaylandKeyboardRepeat repeat;
Uint64 highres_timestamp_ns;
// Current SDL modifier flags
SDL_Keymod pressed_modifiers;
SDL_Keymod locked_modifiers;
SDL_KeyboardID sdl_id;
bool is_virtual;
struct
{
struct xkb_keymap *keymap;
struct xkb_state *state;
struct xkb_compose_table *compose_table;
struct xkb_compose_state *compose_state;
// Current keyboard layout (aka 'group')
xkb_layout_index_t num_layouts;
xkb_layout_index_t current_layout;
// Modifier bitshift values
xkb_mod_mask_t shift_mask;
xkb_mod_mask_t ctrl_mask;
xkb_mod_mask_t alt_mask;
xkb_mod_mask_t gui_mask;
xkb_mod_mask_t level3_mask;
xkb_mod_mask_t level5_mask;
xkb_mod_mask_t num_mask;
xkb_mod_mask_t caps_mask;
// Current system modifier flags
xkb_mod_mask_t wl_pressed_modifiers;
xkb_mod_mask_t wl_latched_modifiers;
xkb_mod_mask_t wl_locked_modifiers;
} xkb;
} keyboard;
struct
{
struct wl_pointer *wl_pointer;
struct zwp_relative_pointer_v1 *relative_pointer;
struct zwp_input_timestamps_v1 *timestamps;
struct zwp_locked_pointer_v1 *locked_pointer;
struct zwp_confined_pointer_v1 *confined_pointer;
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
SDL_WindowData *focus;
// According to the spec, a seat can only have one active gesture of any type at a time.
SDL_WindowData *gesture_focus;
Uint64 highres_timestamp_ns;
Uint32 enter_serial;
SDL_MouseButtonFlags buttons_pressed;
SDL_Point last_motion;
bool is_confined;
SDL_MouseID sdl_id;
// Information about axis events on the current frame
struct
{
bool have_absolute;
bool have_relative;
bool have_axis;
Uint32 buttons_pressed;
Uint32 buttons_released;
struct
{
wl_fixed_t sx;
wl_fixed_t sy;
} absolute;
struct
{
wl_fixed_t dx;
wl_fixed_t dy;
wl_fixed_t dx_unaccel;
wl_fixed_t dy_unaccel;
} relative;
struct
{
enum SDL_WaylandAxisEvent x_axis_type;
float x;
enum SDL_WaylandAxisEvent y_axis_type;
float y;
SDL_MouseWheelDirection direction;
} axis;
struct wl_surface *enter_surface;
struct wl_surface *leave_surface;
// Event timestamp in nanoseconds
Uint64 timestamp_ns;
} pending_frame;
SDL_WaylandCursorState cursor_state;
} pointer;
struct
{
struct wl_touch *wl_touch;
struct zwp_input_timestamps_v1 *timestamps;
Uint64 highres_timestamp_ns;
struct wl_list points;
} touch;
struct
{
struct zwp_text_input_v3 *zwp_text_input;
SDL_Rect text_input_rect;
int text_input_cursor;
bool enabled;
bool has_preedit;
} text_input;
struct
{
struct zwp_tablet_seat_v2 *wl_tablet_seat;
struct wl_list tool_list;
} tablet;
} SDL_WaylandSeat;
extern Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms);
extern void Wayland_PumpEvents(SDL_VideoDevice *_this);
extern void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display);
extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display);
extern void Wayland_DisplayInitPointerGestureManager(SDL_VideoData *display);
extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display);
extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display);
extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display);
extern void Wayland_DisplayInitTextInputManager(SDL_VideoData *d, uint32_t id);
extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id);
extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool shutting_down);
extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat);
extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window);
extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window);
extern void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_WindowData *window);
/* The implicit grab serial needs to be updated on:
* - Keyboard key down/up
* - Mouse button down
* - Touch event down
* - Tablet tool down
* - Tablet tool button down/up
*/
extern void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandSeat *seat, Uint32 serial);
#endif // SDL_waylandevents_h_
+279
View File
@@ -0,0 +1,279 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if SDL_VIDEO_DRIVER_WAYLAND
#include "../SDL_sysvideo.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandevents_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "text-input-unstable-v3-client-protocol.h"
bool Wayland_InitKeyboard(SDL_VideoDevice *_this)
{
#ifdef SDL_USE_IME
SDL_VideoData *internal = _this->internal;
if (!internal->text_input_manager) {
SDL_IME_Init();
}
#endif
SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
return true;
}
void Wayland_QuitKeyboard(SDL_VideoDevice *_this)
{
#ifdef SDL_USE_IME
SDL_VideoData *internal = _this->internal;
if (!internal->text_input_manager) {
SDL_IME_Quit();
}
#endif
}
void Wayland_SeatUpdateTextInput(SDL_WaylandSeat *seat)
{
if (seat->text_input.zwp_text_input) {
SDL_WindowData *focus = seat->keyboard.focus;
if (focus && focus->text_input_props.active) {
SDL_Window *window = focus->sdlwindow;
// Enabling will reset all state, so don't do it redundantly.
if (!seat->text_input.enabled) {
seat->text_input.enabled = true;
zwp_text_input_v3_enable(seat->text_input.zwp_text_input);
// Now that it's enabled, set the input properties
zwp_text_input_v3_set_content_type(seat->text_input.zwp_text_input, focus->text_input_props.hint, focus->text_input_props.purpose);
if (!SDL_RectEmpty(&window->text_input_rect)) {
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / focus->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / focus->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / focus->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / focus->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / focus->pointer_scale.x);
SDL_copyp(&seat->text_input.text_input_rect, &scaled_rect);
seat->text_input.text_input_cursor = scaled_cursor;
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input,
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
}
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
}
}
} else {
if (seat->text_input.enabled) {
seat->text_input.enabled = false;
SDL_zero(seat->text_input.text_input_rect);
seat->text_input.text_input_cursor = 0;
zwp_text_input_v3_disable(seat->text_input.zwp_text_input);
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
}
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
}
}
}
}
bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
{
SDL_VideoData *display = _this->internal;
if (display->text_input_manager) {
SDL_WindowData *wind = window->internal;
wind->text_input_props.hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE;
switch (SDL_GetTextInputType(props)) {
default:
case SDL_TEXTINPUT_TYPE_TEXT:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL;
break;
case SDL_TEXTINPUT_TYPE_TEXT_NAME:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME;
break;
case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL;
break;
case SDL_TEXTINPUT_TYPE_TEXT_USERNAME:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL;
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
break;
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD;
wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA);
break;
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD;
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
break;
case SDL_TEXTINPUT_TYPE_NUMBER:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER;
break;
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN;
wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA);
break;
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN;
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
break;
}
switch (SDL_GetTextInputCapitalization(props)) {
default:
case SDL_CAPITALIZE_NONE:
break;
case SDL_CAPITALIZE_LETTERS:
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE;
break;
case SDL_CAPITALIZE_WORDS:
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE;
break;
case SDL_CAPITALIZE_SENTENCES:
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION;
break;
}
if (SDL_GetTextInputAutocorrect(props)) {
wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK);
}
if (SDL_GetTextInputMultiline(props)) {
wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE;
}
wind->text_input_props.active = true;
SDL_WaylandSeat *seat;
wl_list_for_each (seat, &display->seat_list, link) {
if (seat->keyboard.focus == wind) {
Wayland_SeatUpdateTextInput(seat);
}
}
return true;
}
return SDL_SetError("wayland: cannot enable text input; compositor lacks support for the required zwp_text_input_v3 protocol");
}
bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_VideoData *display = _this->internal;
if (display->text_input_manager) {
SDL_WaylandSeat *seat;
SDL_WindowData *wind = window->internal;
wind->text_input_props.active = false;
wl_list_for_each (seat, &display->seat_list, link) {
if (seat->keyboard.focus == wind) {
Wayland_SeatUpdateTextInput(seat);
}
}
}
#ifdef SDL_USE_IME
else {
SDL_IME_Reset();
}
#endif
return true;
}
bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_VideoData *internal = _this->internal;
if (internal->text_input_manager) {
SDL_WaylandSeat *seat;
SDL_WindowData *wind = window->internal;
wl_list_for_each (seat, &internal->seat_list, link) {
if (seat->text_input.zwp_text_input && seat->keyboard.focus == wind) {
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / wind->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / wind->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / wind->pointer_scale.x);
if (!SDL_RectsEqual(&scaled_rect, &seat->text_input.text_input_rect) || scaled_cursor != seat->text_input.text_input_cursor) {
SDL_copyp(&seat->text_input.text_input_rect, &scaled_rect);
seat->text_input.text_input_cursor = scaled_cursor;
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input,
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
}
}
}
}
#ifdef SDL_USE_IME
else {
SDL_IME_UpdateTextInputArea(window);
}
#endif
return true;
}
bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
{
/* In reality, we just want to return true when the screen keyboard is the
* _only_ way to get text input. So, in addition to checking for the text
* input protocol, make sure we don't have any physical keyboards either.
*/
SDL_VideoData *internal = _this->internal;
SDL_WaylandSeat *seat;
bool hastextmanager = (internal->text_input_manager != NULL);
bool haskeyboard = false;
// Check for at least one keyboard object on one seat.
wl_list_for_each (seat, &internal->seat_list, link) {
if (seat->keyboard.wl_keyboard) {
haskeyboard = true;
break;
}
}
return !haskeyboard && hastextmanager;
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+34
View File
@@ -0,0 +1,34 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandkeyboard_h_
#define SDL_waylandkeyboard_h_
extern bool Wayland_InitKeyboard(SDL_VideoDevice *_this);
extern void Wayland_QuitKeyboard(SDL_VideoDevice *_this);
extern bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SeatUpdateTextInput(SDL_WaylandSeat *seat);
extern bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
#endif // SDL_waylandkeyboard_h_
+42
View File
@@ -0,0 +1,42 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "SDL_waylandmessagebox.h"
#include "../../dialog/unix/SDL_zenitymessagebox.h"
bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
// Are we trying to connect to or are currently in a Wayland session?
if (!SDL_getenv("WAYLAND_DISPLAY")) {
const char *session = SDL_getenv("XDG_SESSION_TYPE");
if (session && SDL_strcasecmp(session, "wayland") != 0) {
return SDL_SetError("Not on a wayland display");
}
}
return SDL_Zenity_ShowMessageBox(messageboxdata, buttonID);
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
+31
View File
@@ -0,0 +1,31 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_waylandmessagebox_h_
#define SDL_waylandmessagebox_h_
#ifdef SDL_VIDEO_DRIVER_WAYLAND
extern bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
#endif // SDL_VIDEO_DRIVER_WAYLAND
#endif // SDL_waylandmessagebox_h_
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandmouse_h_
#define SDL_waylandmouse_h_
extern void Wayland_InitMouse(SDL_VideoData *data);
extern void Wayland_FiniMouse(SDL_VideoData *data);
extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatSetDefaultCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatResetCursor(SDL_WaylandSeat *seat);
extern void Wayland_DisplayUpdatePointerFocusedScale(SDL_WindowData *updated_window);
extern void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool);
extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y);
extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata);
extern void Wayland_CursorStateDestroyFrameCallback(SDL_WaylandCursorState *state);
extern void Wayland_CursorStateRelease(SDL_WaylandCursorState *state);
#if 0 // TODO RECONNECT: See waylandvideo.c for more information!
extern void Wayland_RecreateCursors(void);
#endif // 0
#endif
+228
View File
@@ -0,0 +1,228 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WAYLAND) && defined(SDL_VIDEO_OPENGL_EGL)
#include "../../core/unix/SDL_poll.h"
#include "../SDL_sysvideo.h"
#include "../../events/SDL_windowevents_c.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandopengles.h"
#include "SDL_waylandwindow.h"
#include "SDL_waylandevents_c.h"
#include "xdg-shell-client-protocol.h"
// EGL implementation of SDL OpenGL ES support
void Wayland_GLES_SetDefaultProfileConfig(SDL_VideoDevice *_this)
{
#if defined(SDL_PLATFORM_QNXNTO)
// QNX defaults to EGL_PLATFORM_SCREEN_QNX unless this is explicitly specified
_this->gl_config.egl_platform = EGL_PLATFORM_WAYLAND_EXT;
#endif
}
bool Wayland_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
bool result;
SDL_VideoData *data = _this->internal;
result = SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)data->display);
Wayland_PumpEvents(_this);
WAYLAND_wl_display_flush(data->display);
return result;
}
SDL_GLContext Wayland_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_GLContext context;
context = SDL_EGL_CreateContext(_this, window->internal->egl_surface);
WAYLAND_wl_display_flush(_this->internal->display);
return context;
}
/* Wayland wants to tell you when to provide new frames, and if you have a non-zero
swap interval, Mesa will block until a callback tells it to do so. On some
compositors, they might decide that a minimized window _never_ gets a callback,
which causes apps to hang during swapping forever. So we always set the official
eglSwapInterval to zero to avoid blocking inside EGL, and manage this ourselves.
If a swap blocks for too long waiting on a callback, we just go on, under the
assumption the frame will be wasted, but this is better than freezing the app.
I frown upon platforms that dictate this sort of control inversion (the callback
is intended for _rendering_, not stalling until vsync), but we can work around
this for now. --ryan. */
/* Addendum: several recent APIs demand this sort of control inversion: Emscripten,
libretro, Wayland, probably others...it feels like we're eventually going to have
to give in with a future SDL API revision, since we can bend the other APIs to
this style, but this style is much harder to bend the other way. :/ */
bool Wayland_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval)
{
if (!_this->egl_data) {
return SDL_SetError("EGL not initialized");
}
/* technically, this is _all_ adaptive vsync (-1), because we can't
actually wait for the _next_ vsync if you set 1, but things that
request 1 probably won't care _that_ much. I hope. No matter what
you do, though, you never see tearing on Wayland. */
if (interval > 1) {
interval = 1;
} else if (interval < -1) {
interval = -1;
}
// !!! FIXME: technically, this should be per-context, right?
_this->egl_data->egl_swapinterval = interval;
_this->egl_data->eglSwapInterval(_this->egl_data->egl_display, 0);
return true;
}
bool Wayland_GLES_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
{
if (!_this->egl_data) {
return SDL_SetError("EGL not initialized");
}
*interval =_this->egl_data->egl_swapinterval;
return true;
}
bool Wayland_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
const int swap_interval = _this->egl_data->egl_swapinterval;
/* For windows that we know are hidden, skip swaps entirely, if we don't do
* this compositors will intentionally stall us indefinitely and there's no
* way for an end user to show the window, unlike other situations (i.e.
* the window is minimized, behind another window, etc.).
*
* FIXME: Request EGL_WAYLAND_swap_buffers_with_timeout.
* -flibit
*/
if (data->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_SHOWN &&
data->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) {
return true;
}
/* By default, we wait for the Wayland frame callback and then issue the pageflip (eglSwapBuffers),
* but if we want low latency (double buffer scheme), we issue the pageflip and then wait
* immediately for the Wayland frame callback.
*/
if (data->double_buffer) {
// Feed the frame to Wayland. This will set it so the wl_surface_frame callback can fire again.
if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, data->egl_surface)) {
return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers");
}
WAYLAND_wl_display_flush(data->waylandData->display);
}
// Control swap interval ourselves. See comments on Wayland_GLES_SetSwapInterval
if (swap_interval != 0 && data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOWN) {
SDL_VideoData *videodata = _this->internal;
struct wl_display *display = videodata->display;
// 20hz, so we'll progress even if throttled to zero.
const Uint64 max_wait = SDL_GetTicksNS() + (SDL_NS_PER_SECOND / 20);
while (SDL_GetAtomicInt(&data->swap_interval_ready) == 0) {
Uint64 now;
WAYLAND_wl_display_flush(display);
/* wl_display_prepare_read_queue() will return false if the event queue is not empty.
* If the event queue is empty, it will prepare us for our SDL_IOReady() call. */
if (WAYLAND_wl_display_prepare_read_queue(display, data->gles_swap_frame_event_queue) != 0) {
// We have some pending events. Check if the frame callback happened.
WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue);
continue;
}
// Beyond this point, we must either call wl_display_cancel_read() or wl_display_read_events()
now = SDL_GetTicksNS();
if (now >= max_wait) {
// Timeout expired. Cancel the read.
WAYLAND_wl_display_cancel_read(display);
break;
}
if (SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_IOR_READ, max_wait - now) <= 0) {
// Error or timeout expired without any events for us. Cancel the read.
WAYLAND_wl_display_cancel_read(display);
break;
}
// We have events. Read and dispatch them.
WAYLAND_wl_display_read_events(display);
WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue);
}
SDL_SetAtomicInt(&data->swap_interval_ready, 0);
}
if (!data->double_buffer) {
// Feed the frame to Wayland. This will set it so the wl_surface_frame callback can fire again.
if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, data->egl_surface)) {
return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers");
}
WAYLAND_wl_display_flush(data->waylandData->display);
}
return true;
}
bool Wayland_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
{
bool result;
if (window && context) {
result = SDL_EGL_MakeCurrent(_this, window->internal->egl_surface, context);
} else {
result = SDL_EGL_MakeCurrent(_this, NULL, NULL);
}
WAYLAND_wl_display_flush(_this->internal->display);
_this->egl_data->eglSwapInterval(_this->egl_data->egl_display, 0); // see comments on Wayland_GLES_SetSwapInterval.
return result;
}
bool Wayland_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
{
bool result = SDL_EGL_DestroyContext(_this, context);
WAYLAND_wl_display_flush(_this->internal->display);
return result;
}
EGLSurface Wayland_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *windowdata = window->internal;
return windowdata->egl_surface;
}
#endif // SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL
+49
View File
@@ -0,0 +1,49 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandopengles_h_
#define SDL_waylandopengles_h_
#include "../SDL_sysvideo.h"
#include "../SDL_egl_c.h"
typedef struct SDL_PrivateGLESData
{
int dummy;
} SDL_PrivateGLESData;
// OpenGLES functions
#define Wayland_GLES_GetAttribute SDL_EGL_GetAttribute
#define Wayland_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define Wayland_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
extern bool Wayland_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_GLContext Wayland_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool Wayland_GLES_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
extern bool Wayland_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern bool Wayland_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
extern void Wayland_GLES_SetDefaultProfileConfig(SDL_VideoDevice *_this);
extern SDL_EGLSurface Wayland_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_waylandopengles_h_
+214
View File
@@ -0,0 +1,214 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <sys/mman.h>
#include <unistd.h>
#include "SDL_waylandshmbuffer.h"
#include "SDL_waylandvideo.h"
#include "single-pixel-buffer-v1-client-protocol.h"
static bool SetTempFileSize(int fd, off_t size)
{
#ifdef HAVE_POSIX_FALLOCATE
sigset_t set, old_set;
int ret;
/* SIGALRM can potentially block a large posix_fallocate() operation
* from succeeding, so block it.
*/
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_BLOCK, &set, &old_set);
do {
ret = posix_fallocate(fd, 0, size);
} while (ret == EINTR);
sigprocmask(SIG_SETMASK, &old_set, NULL);
if (ret == 0) {
return true;
} else if (ret != EINVAL && errno != EOPNOTSUPP) {
return false;
}
#endif
if (ftruncate(fd, size) < 0) {
return false;
}
return true;
}
static int CreateTempFD(off_t size)
{
int fd;
#ifdef HAVE_MEMFD_CREATE
fd = memfd_create("SDL", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd >= 0) {
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
} else
#endif
{
static const char template[] = "/sdl-shared-XXXXXX";
const char *xdg_path;
char tmp_path[PATH_MAX];
xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
if (!xdg_path) {
return -1;
}
SDL_strlcpy(tmp_path, xdg_path, PATH_MAX);
SDL_strlcat(tmp_path, template, PATH_MAX);
fd = mkostemp(tmp_path, O_CLOEXEC);
if (fd < 0) {
return -1;
}
// Need to manually unlink the temp files, or they can persist after close and fill up the temp storage.
unlink(tmp_path);
}
if (!SetTempFileSize(fd, size)) {
close(fd);
return -1;
}
return fd;
}
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
{
// NOP
}
static struct wl_buffer_listener buffer_listener = {
buffer_handle_release
};
struct Wayland_SHMPool
{
struct wl_shm_pool *shm_pool;
void *shm_pool_memory;
int shm_pool_size;
int offset;
};
Wayland_SHMPool *Wayland_AllocSHMPool(int size)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *data = vd->internal;
if (size <= 0) {
return NULL;
}
Wayland_SHMPool *shmPool = SDL_calloc(1, sizeof(Wayland_SHMPool));
if (!shmPool) {
return NULL;
}
shmPool->shm_pool_size = (size + 15) & (~15);
const int shm_fd = CreateTempFD(shmPool->shm_pool_size);
if (shm_fd < 0) {
SDL_free(shmPool);
SDL_SetError("Creating SHM buffer failed.");
return NULL;
}
shmPool->shm_pool_memory = mmap(NULL, shmPool->shm_pool_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shmPool->shm_pool_memory == MAP_FAILED) {
shmPool->shm_pool_memory = NULL;
close(shm_fd);
SDL_free(shmPool);
SDL_SetError("mmap() failed.");
return NULL;
}
shmPool->shm_pool = wl_shm_create_pool(data->shm, shm_fd, shmPool->shm_pool_size);
close(shm_fd);
return shmPool;
}
struct wl_buffer *Wayland_AllocBufferFromPool(Wayland_SHMPool *shmPool, int width, int height, void **data)
{
const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888;
if (!shmPool || !width || !height || !data) {
return NULL;
}
*data = (Uint8 *)shmPool->shm_pool_memory + shmPool->offset;
struct wl_buffer *buffer = wl_shm_pool_create_buffer(shmPool->shm_pool, shmPool->offset, width, height, width * 4, SHM_FMT);
wl_buffer_add_listener(buffer, &buffer_listener, shmPool);
shmPool->offset += width * height * 4;
return buffer;
}
void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool)
{
if (shmPool) {
wl_shm_pool_destroy(shmPool->shm_pool);
munmap(shmPool->shm_pool_memory, shmPool->shm_pool_size);
SDL_free(shmPool);
}
}
struct wl_buffer *Wayland_CreateSinglePixelBuffer(Uint32 r, Uint32 g, Uint32 b, Uint32 a)
{
SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
// The single-pixel buffer protocol is preferred, as the compositor can choose an optimal format.
if (viddata->single_pixel_buffer_manager) {
return wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(viddata->single_pixel_buffer_manager, r, g, b, a);
} else {
Wayland_SHMPool *pool = Wayland_AllocSHMPool(4);
if (!pool) {
return NULL;
}
void *mem;
struct wl_buffer *wl_buffer = Wayland_AllocBufferFromPool(pool, 1, 1, &mem);
const Uint8 pixel[4] = { r >> 24, g >> 24, b >> 24, a >> 24 };
SDL_memcpy(mem, pixel, sizeof(pixel));
Wayland_ReleaseSHMPool(pool);
return wl_buffer;
}
}
#endif
+35
View File
@@ -0,0 +1,35 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandshmbuffer_h_
#define SDL_waylandshmbuffer_h_
typedef struct Wayland_SHMPool Wayland_SHMPool;
extern Wayland_SHMPool *Wayland_AllocSHMPool(int size);
extern struct wl_buffer *Wayland_AllocBufferFromPool(Wayland_SHMPool *shmPool, int width, int height, void **data);
extern void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool);
extern struct wl_buffer *Wayland_CreateSinglePixelBuffer(Uint32 r, Uint32 g, Uint32 b, Uint32 a);
#endif
+243
View File
@@ -0,0 +1,243 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* *INDENT-OFF* */ // clang-format off
#ifndef SDL_WAYLAND_MODULE
#define SDL_WAYLAND_MODULE(modname)
#endif
#ifndef SDL_WAYLAND_SYM
#define SDL_WAYLAND_SYM(rc,fn,params)
#endif
#ifndef SDL_WAYLAND_SYM_OPT
#define SDL_WAYLAND_SYM_OPT(rc,fn,params)
#endif
SDL_WAYLAND_MODULE(WAYLAND_CLIENT)
SDL_WAYLAND_SYM(void, wl_proxy_marshal, (struct wl_proxy *, uint32_t, ...))
SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_create, (struct wl_proxy *, const struct wl_interface *))
SDL_WAYLAND_SYM(void, wl_proxy_destroy, (struct wl_proxy *))
SDL_WAYLAND_SYM(int, wl_proxy_add_listener, (struct wl_proxy *, void (**)(void), void *))
SDL_WAYLAND_SYM(void, wl_proxy_set_user_data, (struct wl_proxy *, void *))
SDL_WAYLAND_SYM(void *, wl_proxy_get_user_data, (struct wl_proxy *))
SDL_WAYLAND_SYM(uint32_t, wl_proxy_get_version, (struct wl_proxy *))
SDL_WAYLAND_SYM(uint32_t, wl_proxy_get_id, (struct wl_proxy *))
SDL_WAYLAND_SYM(const char *, wl_proxy_get_class, (struct wl_proxy *))
SDL_WAYLAND_SYM(void, wl_proxy_set_queue, (struct wl_proxy *, struct wl_event_queue *))
SDL_WAYLAND_SYM(void *, wl_proxy_create_wrapper, (void *))
SDL_WAYLAND_SYM(void, wl_proxy_wrapper_destroy, (void *))
SDL_WAYLAND_SYM(struct wl_display *, wl_display_connect, (const char *))
SDL_WAYLAND_SYM(struct wl_display *, wl_display_connect_to_fd, (int))
SDL_WAYLAND_SYM(void, wl_display_disconnect, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_get_fd, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_dispatch, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue, (struct wl_display *, struct wl_event_queue *))
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue_pending, (struct wl_display *, struct wl_event_queue *))
SDL_WAYLAND_SYM(int, wl_display_dispatch_pending, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_prepare_read, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_prepare_read_queue, (struct wl_display *, struct wl_event_queue *))
SDL_WAYLAND_SYM(int, wl_display_read_events, (struct wl_display *))
SDL_WAYLAND_SYM(void, wl_display_cancel_read, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_get_error, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_flush, (struct wl_display *))
SDL_WAYLAND_SYM(int, wl_display_roundtrip, (struct wl_display *))
SDL_WAYLAND_SYM(struct wl_event_queue *, wl_display_create_queue, (struct wl_display *))
SDL_WAYLAND_SYM(void, wl_event_queue_destroy, (struct wl_event_queue *))
SDL_WAYLAND_SYM(void, wl_log_set_handler_client, (wl_log_func_t))
SDL_WAYLAND_SYM(void, wl_list_init, (struct wl_list *))
SDL_WAYLAND_SYM(void, wl_list_insert, (struct wl_list *, struct wl_list *) )
SDL_WAYLAND_SYM(void, wl_list_remove, (struct wl_list *))
SDL_WAYLAND_SYM(int, wl_list_length, (const struct wl_list *))
SDL_WAYLAND_SYM(int, wl_list_empty, (const struct wl_list *))
SDL_WAYLAND_SYM(void, wl_list_insert_list, (struct wl_list *, struct wl_list *))
SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_constructor, (struct wl_proxy *, uint32_t opcode, const struct wl_interface *interface, ...))
SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_constructor_versioned, (struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...))
SDL_WAYLAND_SYM(void, wl_proxy_set_tag, (struct wl_proxy *, const char * const *))
SDL_WAYLAND_SYM(const char * const *, wl_proxy_get_tag, (struct wl_proxy *))
#if SDL_WAYLAND_CHECK_VERSION(1, 20, 0)
/* wayland-scanner 1.20 generates code that will call these, so these are
* non-optional when we are compiling against Wayland 1.20. We don't
* explicitly call them ourselves, though, so if we are only compiling
* against Wayland 1.18, they're unnecessary. */
SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_flags, (struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...))
SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_array_flags, (struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, union wl_argument *args))
#endif
#if SDL_WAYLAND_CHECK_VERSION(1, 23, 0) || defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC)
SDL_WAYLAND_SYM_OPT(struct wl_event_queue *, wl_display_create_queue_with_name, (struct wl_display *display, const char *name))
#endif
#if 0 // TODO RECONNECT: See waylandvideo.c for more information!
#if SDL_WAYLAND_CHECK_VERSION(broken, on, purpose)
SDL_WAYLAND_SYM(int, wl_display_reconnect, (struct wl_display *))
#endif
#endif // 0
SDL_WAYLAND_MODULE(WAYLAND_EGL)
SDL_WAYLAND_SYM(struct wl_egl_window *, wl_egl_window_create, (struct wl_surface *, int, int))
SDL_WAYLAND_SYM(void, wl_egl_window_destroy, (struct wl_egl_window *))
SDL_WAYLAND_SYM(void, wl_egl_window_resize, (struct wl_egl_window *, int, int, int, int))
SDL_WAYLAND_SYM(void, wl_egl_window_get_attached_size, (struct wl_egl_window *, int *, int *))
SDL_WAYLAND_MODULE(WAYLAND_CURSOR)
SDL_WAYLAND_SYM(struct wl_cursor_theme *, wl_cursor_theme_load, (const char *, int , struct wl_shm *))
SDL_WAYLAND_SYM(void, wl_cursor_theme_destroy, (struct wl_cursor_theme *))
SDL_WAYLAND_SYM(struct wl_cursor *, wl_cursor_theme_get_cursor, (struct wl_cursor_theme *, const char *))
SDL_WAYLAND_SYM(struct wl_buffer *, wl_cursor_image_get_buffer, (struct wl_cursor_image *))
SDL_WAYLAND_SYM(int, wl_cursor_frame, (struct wl_cursor *, uint32_t))
SDL_WAYLAND_MODULE(WAYLAND_XKB)
SDL_WAYLAND_SYM(int, xkb_state_key_get_syms, (struct xkb_state *, xkb_keycode_t, const xkb_keysym_t **))
SDL_WAYLAND_SYM(int, xkb_keysym_to_utf8, (xkb_keysym_t, char *, size_t) )
SDL_WAYLAND_SYM(struct xkb_keymap *, xkb_keymap_new_from_string, (struct xkb_context *, const char *, enum xkb_keymap_format, enum xkb_keymap_compile_flags))
SDL_WAYLAND_SYM(struct xkb_state *, xkb_state_new, (struct xkb_keymap *) )
SDL_WAYLAND_SYM(int, xkb_keymap_key_repeats, (struct xkb_keymap *keymap, xkb_keycode_t key) )
SDL_WAYLAND_SYM(void, xkb_keymap_unref, (struct xkb_keymap *) )
SDL_WAYLAND_SYM(void, xkb_state_unref, (struct xkb_state *) )
SDL_WAYLAND_SYM(void, xkb_context_unref, (struct xkb_context *) )
SDL_WAYLAND_SYM(struct xkb_context *, xkb_context_new, (enum xkb_context_flags flags) )
SDL_WAYLAND_SYM(enum xkb_state_component, xkb_state_update_mask, (struct xkb_state *state,\
xkb_mod_mask_t depressed_mods,\
xkb_mod_mask_t latched_mods,\
xkb_mod_mask_t locked_mods,\
xkb_layout_index_t depressed_layout,\
xkb_layout_index_t latched_layout,\
xkb_layout_index_t locked_layout) )
SDL_WAYLAND_SYM(struct xkb_compose_table *, xkb_compose_table_new_from_locale, (struct xkb_context *,\
const char *locale, enum xkb_compose_compile_flags) )
SDL_WAYLAND_SYM(void, xkb_compose_state_reset, (struct xkb_compose_state *) )
SDL_WAYLAND_SYM(void, xkb_compose_table_unref, (struct xkb_compose_table *) )
SDL_WAYLAND_SYM(struct xkb_compose_state *, xkb_compose_state_new, (struct xkb_compose_table *, enum xkb_compose_state_flags) )
SDL_WAYLAND_SYM(void, xkb_compose_state_unref, (struct xkb_compose_state *) )
SDL_WAYLAND_SYM(enum xkb_compose_feed_result, xkb_compose_state_feed, (struct xkb_compose_state *, xkb_keysym_t) )
SDL_WAYLAND_SYM(enum xkb_compose_status, xkb_compose_state_get_status, (struct xkb_compose_state *) )
SDL_WAYLAND_SYM(xkb_keysym_t, xkb_compose_state_get_one_sym, (struct xkb_compose_state *) )
SDL_WAYLAND_SYM(void, xkb_keymap_key_for_each, (struct xkb_keymap *, xkb_keymap_key_iter_t, void *) )
SDL_WAYLAND_SYM(xkb_layout_index_t, xkb_keymap_num_layouts, (struct xkb_keymap *) )
SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, const xkb_keysym_t **) )
SDL_WAYLAND_SYM(xkb_level_index_t, xkb_keymap_num_levels_for_key, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t) )
SDL_WAYLAND_SYM(uint32_t, xkb_keysym_to_utf32, (xkb_keysym_t) )
SDL_WAYLAND_SYM(uint32_t, xkb_keymap_mod_get_index, (struct xkb_keymap *, const char *) )
SDL_WAYLAND_SYM(const char *, xkb_keymap_layout_get_name, (struct xkb_keymap *, xkb_layout_index_t))
#if SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0)
SDL_WAYLAND_SYM(size_t, xkb_keymap_key_get_mods_for_level, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, xkb_mod_mask_t *, size_t) )
#else
// Only needed in the fallback replacement for xkb_keymap_key_get_mods_for_level().
SDL_WAYLAND_SYM(xkb_level_index_t, xkb_state_key_get_level, (struct xkb_state *, xkb_keycode_t, xkb_layout_index_t) )
#endif
#if SDL_XKBCOMMON_CHECK_VERSION(1, 10, 0)
SDL_WAYLAND_SYM(xkb_mod_mask_t, xkb_keymap_mod_get_mask, (struct xkb_keymap *, const char *))
#endif
#ifdef HAVE_LIBDECOR_H
SDL_WAYLAND_MODULE(WAYLAND_LIBDECOR)
#if defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR) || SDL_LIBDECOR_CHECK_VERSION(0, 3, 0)
#define SDL_libdecor_constsince03 const
#else
#define SDL_libdecor_constsince03 /* nothing */
#endif
SDL_WAYLAND_SYM(void, libdecor_unref, (struct libdecor *))
SDL_WAYLAND_SYM(struct libdecor *, libdecor_new, (struct wl_display *,\
SDL_libdecor_constsince03 struct libdecor_interface *))
SDL_WAYLAND_SYM(struct libdecor_frame *, libdecor_decorate, (struct libdecor *,\
struct wl_surface *,\
SDL_libdecor_constsince03 struct libdecor_frame_interface *,\
void *))
SDL_WAYLAND_SYM(void, libdecor_frame_unref, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_title, (struct libdecor_frame *, const char *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_app_id, (struct libdecor_frame *, const char *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_max_content_size, (struct libdecor_frame *frame,\
int content_width,\
int content_height))
SDL_WAYLAND_SYM(void, libdecor_frame_set_min_content_size, (struct libdecor_frame *frame,\
int content_width,\
int content_height))
SDL_WAYLAND_SYM(void, libdecor_frame_resize, (struct libdecor_frame *,\
struct wl_seat *,\
uint32_t,\
enum libdecor_resize_edge))
SDL_WAYLAND_SYM(void, libdecor_frame_move, (struct libdecor_frame *,\
struct wl_seat *,\
uint32_t))
SDL_WAYLAND_SYM(void, libdecor_frame_commit, (struct libdecor_frame *,\
struct libdecor_state *,\
struct libdecor_configuration *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_minimized, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_maximized, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_unset_maximized, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_fullscreen, (struct libdecor_frame *, struct wl_output *))
SDL_WAYLAND_SYM(void, libdecor_frame_unset_fullscreen, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_capabilities, (struct libdecor_frame *, \
enum libdecor_capabilities))
SDL_WAYLAND_SYM(void, libdecor_frame_unset_capabilities, (struct libdecor_frame *, \
enum libdecor_capabilities))
SDL_WAYLAND_SYM(bool, libdecor_frame_has_capability, (struct libdecor_frame *, \
enum libdecor_capabilities))
SDL_WAYLAND_SYM(void, libdecor_frame_set_visibility, (struct libdecor_frame *, bool))
SDL_WAYLAND_SYM(bool, libdecor_frame_is_visible, (struct libdecor_frame *))
SDL_WAYLAND_SYM(bool, libdecor_frame_is_floating, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_set_parent, (struct libdecor_frame *,\
struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_show_window_menu, (struct libdecor_frame *, struct wl_seat *, uint32_t, int, int))
SDL_WAYLAND_SYM(struct xdg_surface *, libdecor_frame_get_xdg_surface, (struct libdecor_frame *))
SDL_WAYLAND_SYM(struct xdg_toplevel *, libdecor_frame_get_xdg_toplevel, (struct libdecor_frame *))
SDL_WAYLAND_SYM(void, libdecor_frame_translate_coordinate, (struct libdecor_frame *, int, int, int *, int *))
SDL_WAYLAND_SYM(void, libdecor_frame_map, (struct libdecor_frame *))
SDL_WAYLAND_SYM(struct libdecor_state *, libdecor_state_new, (int, int))
SDL_WAYLAND_SYM(void, libdecor_state_free, (struct libdecor_state *))
SDL_WAYLAND_SYM(bool, libdecor_configuration_get_content_size, (struct libdecor_configuration *,\
struct libdecor_frame *,\
int *,\
int *))
SDL_WAYLAND_SYM(bool, libdecor_configuration_get_window_state, (struct libdecor_configuration *,\
enum libdecor_window_state *))
SDL_WAYLAND_SYM(int, libdecor_dispatch, (struct libdecor *, int))
#if defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR) || SDL_LIBDECOR_CHECK_VERSION(0, 2, 0)
// Only found in libdecor 0.1.1 or higher, so failure to load them is not fatal.
SDL_WAYLAND_SYM_OPT(void, libdecor_frame_get_min_content_size, (const struct libdecor_frame *,\
int *,\
int *))
SDL_WAYLAND_SYM_OPT(void, libdecor_frame_get_max_content_size, (const struct libdecor_frame *,\
int *,\
int *))
#endif
#if defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR) || SDL_LIBDECOR_CHECK_VERSION(0, 3, 0)
SDL_WAYLAND_SYM_OPT(enum libdecor_wm_capabilities, libdecor_frame_get_wm_capabilities, (struct libdecor_frame *))
#endif
#undef SDL_libdecor_constsince03
#endif
#undef SDL_WAYLAND_MODULE
#undef SDL_WAYLAND_SYM
#undef SDL_WAYLAND_SYM_OPT
/* *INDENT-ON* */ // clang-format on
File diff suppressed because it is too large Load Diff
+173
View File
@@ -0,0 +1,173 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandvideo_h_
#define SDL_waylandvideo_h_
#include <EGL/egl.h>
#include "wayland-util.h"
#include "../SDL_sysvideo.h"
#include "../../core/linux/SDL_dbus.h"
#include "../../core/linux/SDL_ime.h"
struct xkb_context;
struct SDL_WaylandSeat;
typedef struct
{
struct wl_cursor_theme *theme;
int size;
} SDL_WaylandCursorTheme;
typedef struct
{
struct wl_list link;
char wl_output_name[];
} SDL_WaylandConnectorName;
struct SDL_VideoData
{
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shm *shm;
SDL_WaylandCursorTheme *cursor_themes;
int num_cursor_themes;
struct
{
struct xdg_wm_base *xdg;
#ifdef HAVE_LIBDECOR_H
struct libdecor *libdecor;
#endif
} shell;
struct wl_subcompositor *subcompositor;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct wp_pointer_warp_v1 *wp_pointer_warp_v1;
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
struct wl_data_device_manager *data_device_manager;
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
struct zxdg_decoration_manager_v1 *decoration_manager;
struct zwp_keyboard_shortcuts_inhibit_manager_v1 *key_inhibitor_manager;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;
struct xdg_activation_v1 *activation_manager;
struct zwp_text_input_manager_v3 *text_input_manager;
struct zxdg_output_manager_v1 *xdg_output_manager;
struct wp_viewporter *viewporter;
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
struct zxdg_exporter_v2 *zxdg_exporter_v2;
struct xdg_wm_dialog_v1 *xdg_wm_dialog_v1;
struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1;
struct frog_color_management_factory_v1 *frog_color_management_factory_v1;
struct wp_color_manager_v1 *wp_color_manager_v1;
struct zwp_tablet_manager_v2 *tablet_manager;
struct wl_fixes *wl_fixes;
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures;
struct wp_single_pixel_buffer_manager_v1 *single_pixel_buffer_manager;
struct xkb_context *xkb_context;
struct wl_list seat_list;
struct SDL_WaylandSeat *last_implicit_grab_seat;
struct SDL_WaylandSeat *last_incoming_data_offer_seat;
struct SDL_WaylandSeat *last_incoming_primary_selection_seat;
SDL_DisplayData **output_list;
int output_count;
int output_max;
bool initializing;
bool display_disconnected;
bool display_externally_owned;
bool scale_to_display_enabled;
};
struct SDL_DisplayData
{
SDL_VideoData *videodata;
struct wl_output *output;
struct zxdg_output_v1 *xdg_output;
struct wp_color_management_output_v1 *wp_color_management_output;
struct Wayland_ColorInfoState *color_info_state;
char *wl_output_name;
double scale_factor;
Uint32 registry_id;
struct
{
int x;
int y;
int width;
int height;
} logical;
struct
{
int x;
int y;
int width;
int height;
} pixel;
struct
{
int width_mm; // Physical width in millimeters.
int height_mm; // Physical height in millimeters.
} physical;
int refresh; // Refresh in mHz
int transform; // wl_output_transform enum
SDL_DisplayOrientation orientation;
SDL_HDROutputProperties HDR;
SDL_DisplayID display;
SDL_VideoDisplay placeholder;
int wl_output_done_count;
bool has_logical_position;
bool has_logical_size;
bool geometry_changed;
};
// Needed here to get wl_surface declaration, fixes GitHub#4594
#include "SDL_waylanddyn.h"
extern void SDL_WAYLAND_register_surface(struct wl_surface *surface);
extern void SDL_WAYLAND_register_output(struct wl_output *output);
extern bool SDL_WAYLAND_own_surface(struct wl_surface *surface);
extern bool SDL_WAYLAND_own_output(struct wl_output *output);
extern SDL_WindowData *Wayland_GetWindowDataForOwnedSurface(struct wl_surface *surface);
void Wayland_AddWindowDataToExternalList(SDL_WindowData *data);
void Wayland_RemoveWindowDataFromExternalList(SDL_WindowData *data);
struct wl_event_queue *Wayland_DisplayCreateQueue(struct wl_display *display, const char *name);
extern bool Wayland_LoadLibdecor(SDL_VideoData *data, bool ignore_xdg);
extern bool Wayland_HandleDisplayDisconnected(SDL_VideoDevice *_this);
#endif // SDL_waylandvideo_h_
+210
View File
@@ -0,0 +1,210 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND)
#include "../SDL_vulkan_internal.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#include "SDL_waylandvulkan.h"
#ifdef SDL_PLATFORM_OPENBSD
#define DEFAULT_VULKAN "libvulkan.so"
#else
#define DEFAULT_VULKAN "libvulkan.so.1"
#endif
SDL_ELF_NOTE_DLOPEN(
"wayland-vulkan",
"Support for Vulkan on wayland backend",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
DEFAULT_VULKAN
)
bool Wayland_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 i, extensionCount = 0;
bool hasSurfaceExtension = false;
bool hasWaylandSurfaceExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan already loaded");
}
// Load the Vulkan loader library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
path = DEFAULT_VULKAN;
}
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
if (!_this->vulkan_config.loader_handle) {
return false;
}
SDL_strlcpy(_this->vulkan_config.loader_path, path,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr) {
goto fail;
}
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
goto fail;
}
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (!extensions) {
goto fail;
}
for (i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = true;
} else if (SDL_strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasWaylandSurfaceExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasWaylandSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME " extension");
goto fail;
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void Wayland_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
if (_this->vulkan_config.loader_handle) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
char const * const *Wayland_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count)
{
static const char *const extensionsForWayland[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
};
if (count) {
*count = SDL_arraysize(extensionsForWayland);
}
return extensionsForWayland;
}
bool Wayland_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
SDL_WindowData *windowData = window->internal;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR =
(PFN_vkCreateWaylandSurfaceKHR)vkGetInstanceProcAddr(
instance,
"vkCreateWaylandSurfaceKHR");
VkWaylandSurfaceCreateInfoKHR createInfo;
VkResult result;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkCreateWaylandSurfaceKHR) {
return SDL_SetError(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
" extension is not enabled in the Vulkan instance.");
}
SDL_zero(createInfo);
createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.display = windowData->waylandData->display;
createInfo.surface = windowData->surface;
result = vkCreateWaylandSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
return SDL_SetError("vkCreateWaylandSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
}
return true;
}
void Wayland_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator)
{
if (_this->vulkan_config.loader_handle) {
SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
}
}
bool Wayland_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)vkGetInstanceProcAddr(
instance,
"vkGetPhysicalDeviceWaylandPresentationSupportKHR");
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) {
return SDL_SetError(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
return vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice,
queueFamilyIndex,
_this->internal->display);
}
#endif
+55
View File
@@ -0,0 +1,55 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.h.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandvulkan_h_
#define SDL_waylandvulkan_h_
#include <SDL3/SDL_vulkan.h>
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND)
extern bool Wayland_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void Wayland_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const * const *Wayland_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool Wayland_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void Wayland_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
extern bool Wayland_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex);
#endif
#endif // SDL_waylandvulkan_h_
File diff suppressed because it is too large Load Diff
+283
View File
@@ -0,0 +1,283 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandwindow_h_
#define SDL_waylandwindow_h_
#include "../SDL_sysvideo.h"
#include "../../events/SDL_touch_c.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandshmbuffer.h"
struct SDL_WindowData
{
SDL_Window *sdlwindow;
SDL_VideoData *waylandData;
struct wl_surface *surface;
struct wl_callback *gles_swap_frame_callback;
struct wl_event_queue *gles_swap_frame_event_queue;
struct wl_surface *gles_swap_frame_surface_wrapper;
struct wl_callback *surface_frame_callback;
union
{
#ifdef HAVE_LIBDECOR_H
struct
{
struct libdecor_frame *frame;
} libdecor;
#endif
struct
{
struct xdg_surface *surface;
union
{
struct
{
struct xdg_toplevel *xdg_toplevel;
} toplevel;
struct
{
struct xdg_popup *xdg_popup;
struct xdg_positioner *xdg_positioner;
} popup;
};
Uint32 serial;
} xdg;
} shell_surface;
enum
{
WAYLAND_SHELL_SURFACE_TYPE_UNKNOWN = 0,
WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL,
WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP,
WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR,
WAYLAND_SHELL_SURFACE_TYPE_CUSTOM
} shell_surface_type;
enum
{
WAYLAND_SHELL_SURFACE_STATUS_HIDDEN = 0,
WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE,
WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME,
WAYLAND_SHELL_SURFACE_STATUS_SHOW_PENDING,
WAYLAND_SHELL_SURFACE_STATUS_SHOWN
} shell_surface_status;
enum
{
WAYLAND_WM_CAPS_WINDOW_MENU = 0x01,
WAYLAND_WM_CAPS_MAXIMIZE = 0x02,
WAYLAND_WM_CAPS_FULLSCREEN = 0x04,
WAYLAND_WM_CAPS_MINIMIZE = 0x08,
WAYLAND_WM_CAPS_ALL = WAYLAND_WM_CAPS_WINDOW_MENU |
WAYLAND_WM_CAPS_MAXIMIZE |
WAYLAND_WM_CAPS_FULLSCREEN |
WAYLAND_WM_CAPS_MINIMIZE
} wm_caps;
enum
{
WAYLAND_TOPLEVEL_CONSTRAINED_LEFT = 0x01,
WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT = 0x02,
WAYLAND_TOPLEVEL_CONSTRAINED_TOP = 0x04,
WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM = 0x08
} toplevel_constraints;
struct wl_egl_window *egl_window;
#ifdef SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
#endif
struct zxdg_toplevel_decoration_v1 *server_decoration;
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
struct xdg_activation_token_v1 *activation_token;
struct wp_viewport *viewport;
struct wp_fractional_scale_v1 *fractional_scale;
struct zxdg_exported_v2 *exported;
struct xdg_dialog_v1 *xdg_dialog_v1;
struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1;
struct xdg_toplevel_icon_v1 *xdg_toplevel_icon_v1;
struct frog_color_managed_surface *frog_color_managed_surface;
struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback;
struct Wayland_ColorInfoState *color_info_state;
SDL_AtomicInt swap_interval_ready;
SDL_DisplayData **outputs;
int num_outputs;
char *app_id;
double scale_factor;
struct wl_buffer **icon_buffers;
int icon_buffer_count;
// Keyboard, pointer, and touch focus refcount.
int keyboard_focus_count;
int pointer_focus_count;
int active_touch_count;
struct
{
double x;
double y;
} pointer_scale;
// The in-flight window size request.
struct
{
// The requested logical window size.
int logical_width;
int logical_height;
// The size of the window in pixels, when using screen space scaling.
int pixel_width;
int pixel_height;
} requested;
// The current size of the window and drawable backing store.
struct
{
// The size of the underlying window.
int logical_width;
int logical_height;
// The size of the window backbuffer in pixels.
int pixel_width;
int pixel_height;
// The dimensions of the active viewport, in logical units.
int viewport_width;
int viewport_height;
} current;
// The last compositor requested parameters; used for deduplication of window geometry configuration.
struct
{
int width;
int height;
} last_configure;
// System enforced window size limits.
struct
{
// Minimum allowed logical window size.
int min_width;
int min_height;
} system_limits;
struct
{
int width;
int height;
} toplevel_bounds;
struct
{
struct wl_surface *surface;
struct wl_subsurface *subsurface;
struct wl_buffer *buffer;
struct wp_viewport *viewport;
int offset_x;
int offset_y;
bool mapped;
bool opaque;
} mask;
struct
{
int hint;
int purpose;
bool active;
} text_input_props;
SDL_DisplayID last_displayID;
int pending_state_deadline_count;
Uint64 last_focus_event_time_ns;
int icc_fd;
Uint32 icc_size;
bool floating;
bool suspended;
bool resizing;
bool active;
bool pending_config_ack;
bool pending_state_commit;
bool limits_changed;
bool is_fullscreen;
bool fullscreen_exclusive;
bool drop_fullscreen_requests;
bool showing_window;
bool fullscreen_was_positioned;
bool show_hide_sync_required;
bool scale_to_display;
bool reparenting_required;
bool double_buffer;
SDL_HitTestResult hit_test_result;
struct wl_list external_window_list_link;
};
extern void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen);
extern void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern bool Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
extern void Wayland_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
extern bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern bool Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
extern SDL_DisplayID Wayland_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent_window);
extern bool Wayland_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
extern bool Wayland_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window);
extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled);
extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags);
extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data);
extern void Wayland_UpdateWindowPosition(SDL_Window *window);
#endif // SDL_waylandwindow_h_