mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-07-08 18:29:43 +00:00
wayland: Refactor the cursor event thread into a general purpose event thread
No functional changes, just tidies the mouse code, and allows for future generalized use of the event handling thread, if required.
This commit is contained in:
210
src/video/wayland/SDL_waylandeventthread.c
Normal file
210
src/video/wayland/SDL_waylandeventthread.c
Normal 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.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "SDL_waylandeventthread.h"
|
||||
#include "SDL_waylandvideo.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
struct Wayland_EventThreadContext
|
||||
{
|
||||
SDL_VideoData *wayland_data;
|
||||
SDL_Thread *thread;
|
||||
struct wl_event_queue *queue;
|
||||
SDL_Mutex *dispatch_lock;
|
||||
bool should_exit;
|
||||
};
|
||||
|
||||
static void handle_event_thread_exit(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
|
||||
{
|
||||
Wayland_EventThreadContext *context = (Wayland_EventThreadContext *)data;
|
||||
wl_callback_destroy(wl_callback);
|
||||
context->should_exit = true;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener event_thread_exit_listener = {
|
||||
handle_event_thread_exit
|
||||
};
|
||||
|
||||
static int SDLCALL Wayland_EventThreadFunc(void *data)
|
||||
{
|
||||
Wayland_EventThreadContext *context = (Wayland_EventThreadContext *)data;
|
||||
struct wl_display *display = context->wayland_data->display;
|
||||
const int display_fd = WAYLAND_wl_display_get_fd(display);
|
||||
int ret;
|
||||
|
||||
/* The lock must be held whenever dispatching to avoid a race condition when adding
|
||||
* or destroying objects using the queue.
|
||||
*
|
||||
* Any error other than EAGAIN is fatal and causes the thread to exit.
|
||||
*/
|
||||
while (!context->should_exit) {
|
||||
if (WAYLAND_wl_display_prepare_read_queue(display, context->queue) == 0) {
|
||||
Sint64 timeoutNS = -1;
|
||||
|
||||
ret = WAYLAND_wl_display_flush(display);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
// If the flush failed with EAGAIN, don't block as not to inhibit other threads from reading events.
|
||||
timeoutNS = SDL_MS_TO_NS(1);
|
||||
} else {
|
||||
WAYLAND_wl_display_cancel_read(display);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a read/write operation to become possible.
|
||||
ret = SDL_IOReady(display_fd, SDL_IOR_READ, timeoutNS);
|
||||
|
||||
if (ret <= 0) {
|
||||
WAYLAND_wl_display_cancel_read(display);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Nothing to read, and woke to flush; try again.
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = WAYLAND_wl_display_read_events(display);
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LockMutex(context->dispatch_lock);
|
||||
ret = WAYLAND_wl_display_dispatch_queue_pending(display, context->queue);
|
||||
SDL_UnlockMutex(context->dispatch_lock);
|
||||
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Wayland_EventThreadContext *Wayland_CreateEventThread(SDL_VideoData *data, const char *queue_name)
|
||||
{
|
||||
Wayland_EventThreadContext *context = SDL_calloc(1, sizeof(Wayland_EventThreadContext));
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->wayland_data = data;
|
||||
context->queue = Wayland_DisplayCreateQueue(data->display, queue_name);
|
||||
if (!context->queue) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
context->dispatch_lock = SDL_CreateMutex();
|
||||
if (!context->dispatch_lock) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
context->thread = SDL_CreateThread(Wayland_EventThreadFunc, "wl_event_thread", context);
|
||||
if (!context->thread) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return context;
|
||||
|
||||
cleanup:
|
||||
if (context->dispatch_lock) {
|
||||
SDL_DestroyMutex(context->dispatch_lock);
|
||||
}
|
||||
|
||||
if (context->queue) {
|
||||
WAYLAND_wl_event_queue_destroy(context->queue);
|
||||
}
|
||||
|
||||
SDL_free(context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Wayland_DestroyEventThread(Wayland_EventThreadContext *context)
|
||||
{
|
||||
if (context->thread) {
|
||||
// Dispatch the exit event to unblock the thread and signal it to exit.
|
||||
struct wl_display *display = context->wayland_data->display;
|
||||
struct wl_proxy *display_wrapper = Wayland_CreateEventThreadProxyWrapper(context, display);
|
||||
|
||||
SDL_LockMutex(context->dispatch_lock);
|
||||
struct wl_callback *cb = wl_display_sync((struct wl_display *)display_wrapper);
|
||||
wl_callback_add_listener(cb, &event_thread_exit_listener, context);
|
||||
SDL_UnlockMutex(context->dispatch_lock);
|
||||
|
||||
WAYLAND_wl_proxy_wrapper_destroy(display_wrapper);
|
||||
|
||||
int ret = WAYLAND_wl_display_flush(display);
|
||||
while (ret == -1 && errno == EAGAIN) {
|
||||
// Shutting down the thread requires a successful flush.
|
||||
ret = SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_IOR_WRITE, -1);
|
||||
if (ret >= 0) {
|
||||
ret = WAYLAND_wl_display_flush(display);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid a warning if the flush failed due to a broken connection.
|
||||
if (ret < 0) {
|
||||
wl_callback_destroy(cb);
|
||||
}
|
||||
|
||||
// Wait for the thread to return; it will exit automatically on a broken connection.
|
||||
SDL_WaitThread(context->thread, NULL);
|
||||
|
||||
WAYLAND_wl_event_queue_destroy(context->queue);
|
||||
SDL_DestroyMutex(context->dispatch_lock);
|
||||
SDL_free(context);
|
||||
}
|
||||
}
|
||||
|
||||
void *Wayland_CreateEventThreadProxyWrapper(Wayland_EventThreadContext *context, void *obj)
|
||||
{
|
||||
struct wl_proxy *wrapper = WAYLAND_wl_proxy_create_wrapper(obj);
|
||||
if (wrapper) {
|
||||
WAYLAND_wl_proxy_set_queue(wrapper, context->queue);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Wayland_LockEventThread(Wayland_EventThreadContext *context)
|
||||
{
|
||||
if (context) {
|
||||
SDL_LockMutex(context->dispatch_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void Wayland_UnlockEventThread(Wayland_EventThreadContext *context)
|
||||
{
|
||||
if (context) {
|
||||
SDL_UnlockMutex(context->dispatch_lock);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_WAYLAND
|
||||
36
src/video/wayland/SDL_waylandeventthread.h
Normal file
36
src/video/wayland/SDL_waylandeventthread.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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_waylandeventthread_h_
|
||||
#define SDL_waylandeventthread_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
typedef struct Wayland_EventThreadContext Wayland_EventThreadContext;
|
||||
|
||||
extern Wayland_EventThreadContext *Wayland_CreateEventThread(SDL_VideoData *data, const char *queue_name);
|
||||
extern void Wayland_DestroyEventThread(Wayland_EventThreadContext *context);
|
||||
extern void *Wayland_CreateEventThreadProxyWrapper(Wayland_EventThreadContext *context, void *obj);
|
||||
extern void Wayland_LockEventThread(Wayland_EventThreadContext *context);
|
||||
extern void Wayland_UnlockEventThread(Wayland_EventThreadContext *context);
|
||||
|
||||
#endif // SDL_waylandeventthread_h_
|
||||
@@ -324,168 +324,6 @@ static struct wl_buffer *Wayland_CursorStateGetFrame(SDL_WaylandCursorState *sta
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct CursorThreadContext
|
||||
{
|
||||
SDL_Thread *thread;
|
||||
struct wl_event_queue *queue;
|
||||
struct wl_proxy *compositor_wrapper;
|
||||
SDL_Mutex *lock;
|
||||
bool should_exit;
|
||||
} cursor_thread_context;
|
||||
|
||||
static void handle_cursor_thread_exit(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
|
||||
{
|
||||
wl_callback_destroy(wl_callback);
|
||||
cursor_thread_context.should_exit = true;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener cursor_thread_exit_listener = {
|
||||
handle_cursor_thread_exit
|
||||
};
|
||||
|
||||
static int SDLCALL Wayland_CursorThreadFunc(void *data)
|
||||
{
|
||||
struct wl_display *display = data;
|
||||
const int display_fd = WAYLAND_wl_display_get_fd(display);
|
||||
int ret;
|
||||
|
||||
/* The lock must be held whenever dispatching to avoid a race condition when setting
|
||||
* or destroying cursor frame callbacks, as adding the callback followed by setting
|
||||
* the listener is not an atomic operation, and the callback proxy must not be
|
||||
* destroyed while in the callback handler.
|
||||
*
|
||||
* Any error other than EAGAIN is fatal and causes the thread to exit.
|
||||
*/
|
||||
while (!cursor_thread_context.should_exit) {
|
||||
if (WAYLAND_wl_display_prepare_read_queue(display, cursor_thread_context.queue) == 0) {
|
||||
Sint64 timeoutNS = -1;
|
||||
|
||||
ret = WAYLAND_wl_display_flush(display);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
// If the flush failed with EAGAIN, don't block as not to inhibit other threads from reading events.
|
||||
timeoutNS = SDL_MS_TO_NS(1);
|
||||
} else {
|
||||
WAYLAND_wl_display_cancel_read(display);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a read/write operation to become possible.
|
||||
ret = SDL_IOReady(display_fd, SDL_IOR_READ, timeoutNS);
|
||||
|
||||
if (ret <= 0) {
|
||||
WAYLAND_wl_display_cancel_read(display);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Nothing to read, and woke to flush; try again.
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = WAYLAND_wl_display_read_events(display);
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LockMutex(cursor_thread_context.lock);
|
||||
ret = WAYLAND_wl_display_dispatch_queue_pending(display, cursor_thread_context.queue);
|
||||
SDL_UnlockMutex(cursor_thread_context.lock);
|
||||
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool Wayland_StartCursorThread(SDL_VideoData *data)
|
||||
{
|
||||
if (!cursor_thread_context.thread) {
|
||||
cursor_thread_context.queue = Wayland_DisplayCreateQueue(data->display, "SDL Cursor Surface Queue");
|
||||
if (!cursor_thread_context.queue) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cursor_thread_context.compositor_wrapper = WAYLAND_wl_proxy_create_wrapper(data->compositor);
|
||||
if (!cursor_thread_context.compositor_wrapper) {
|
||||
goto cleanup;
|
||||
}
|
||||
WAYLAND_wl_proxy_set_queue(cursor_thread_context.compositor_wrapper, cursor_thread_context.queue);
|
||||
|
||||
cursor_thread_context.lock = SDL_CreateMutex();
|
||||
if (!cursor_thread_context.lock) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cursor_thread_context.thread = SDL_CreateThread(Wayland_CursorThreadFunc, "wl_cursor_surface", data->display);
|
||||
if (!cursor_thread_context.thread) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (cursor_thread_context.lock) {
|
||||
SDL_DestroyMutex(cursor_thread_context.lock);
|
||||
}
|
||||
|
||||
if (cursor_thread_context.compositor_wrapper) {
|
||||
WAYLAND_wl_proxy_wrapper_destroy(cursor_thread_context.compositor_wrapper);
|
||||
}
|
||||
|
||||
if (cursor_thread_context.queue) {
|
||||
WAYLAND_wl_event_queue_destroy(cursor_thread_context.queue);
|
||||
}
|
||||
|
||||
SDL_zero(cursor_thread_context);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void Wayland_DestroyCursorThread(SDL_VideoData *data)
|
||||
{
|
||||
if (cursor_thread_context.thread) {
|
||||
// Dispatch the exit event to unblock the animation thread and signal it to exit.
|
||||
struct wl_proxy *display_wrapper = WAYLAND_wl_proxy_create_wrapper(data->display);
|
||||
WAYLAND_wl_proxy_set_queue(display_wrapper, cursor_thread_context.queue);
|
||||
|
||||
SDL_LockMutex(cursor_thread_context.lock);
|
||||
struct wl_callback *cb = wl_display_sync((struct wl_display *)display_wrapper);
|
||||
wl_callback_add_listener(cb, &cursor_thread_exit_listener, NULL);
|
||||
SDL_UnlockMutex(cursor_thread_context.lock);
|
||||
|
||||
WAYLAND_wl_proxy_wrapper_destroy(display_wrapper);
|
||||
|
||||
int ret = WAYLAND_wl_display_flush(data->display);
|
||||
while (ret == -1 && errno == EAGAIN) {
|
||||
// Shutting down the thread requires a successful flush.
|
||||
ret = SDL_IOReady(WAYLAND_wl_display_get_fd(data->display), SDL_IOR_WRITE, -1);
|
||||
if (ret >= 0) {
|
||||
ret = WAYLAND_wl_display_flush(data->display);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid a warning if the flush failed due to a broken connection.
|
||||
if (ret < 0) {
|
||||
wl_callback_destroy(cb);
|
||||
}
|
||||
|
||||
// Wait for the thread to return; it will exit automatically on a broken connection.
|
||||
SDL_WaitThread(cursor_thread_context.thread, NULL);
|
||||
|
||||
WAYLAND_wl_proxy_wrapper_destroy(cursor_thread_context.compositor_wrapper);
|
||||
WAYLAND_wl_event_queue_destroy(cursor_thread_context.queue);
|
||||
SDL_DestroyMutex(cursor_thread_context.lock);
|
||||
SDL_zero(cursor_thread_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
|
||||
static const struct wl_callback_listener cursor_frame_listener = {
|
||||
cursor_frame_done
|
||||
@@ -545,24 +383,26 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
|
||||
|
||||
void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata)
|
||||
{
|
||||
SDL_LockMutex(cursor_thread_context.lock);
|
||||
SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
|
||||
Wayland_LockEventThread(viddata->event_thread_context);
|
||||
|
||||
state->frame_callback = wl_surface_frame(state->surface);
|
||||
wl_callback_add_listener(state->frame_callback, &cursor_frame_listener, userdata);
|
||||
|
||||
SDL_UnlockMutex(cursor_thread_context.lock);
|
||||
Wayland_UnlockEventThread(viddata->event_thread_context);
|
||||
}
|
||||
|
||||
void Wayland_CursorStateDestroyFrameCallback(SDL_WaylandCursorState *state)
|
||||
{
|
||||
SDL_LockMutex(cursor_thread_context.lock);
|
||||
SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
|
||||
Wayland_LockEventThread(viddata->event_thread_context);
|
||||
|
||||
if (state->frame_callback) {
|
||||
wl_callback_destroy(state->frame_callback);
|
||||
state->frame_callback = NULL;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(cursor_thread_context.lock);
|
||||
Wayland_UnlockEventThread(viddata->event_thread_context);
|
||||
}
|
||||
|
||||
static void Wayland_CursorStateResetAnimationState(SDL_WaylandCursorState *state)
|
||||
@@ -575,9 +415,10 @@ static void Wayland_CursorStateResetAnimationState(SDL_WaylandCursorState *state
|
||||
static void Wayland_CursorStateResetAnimation(SDL_WaylandCursorState *state, bool lock)
|
||||
{
|
||||
if (lock) {
|
||||
SDL_LockMutex(cursor_thread_context.lock);
|
||||
SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
|
||||
Wayland_LockEventThread(viddata->event_thread_context);
|
||||
Wayland_CursorStateResetAnimationState(state);
|
||||
SDL_UnlockMutex(cursor_thread_context.lock);
|
||||
Wayland_UnlockEventThread(viddata->event_thread_context);
|
||||
} else {
|
||||
Wayland_CursorStateResetAnimationState(state);
|
||||
}
|
||||
@@ -1175,8 +1016,10 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
|
||||
state->current_cursor = cursor_data;
|
||||
|
||||
if (!state->surface) {
|
||||
if (cursor_thread_context.compositor_wrapper) {
|
||||
state->surface = wl_compositor_create_surface((struct wl_compositor *)cursor_thread_context.compositor_wrapper);
|
||||
if (viddata->event_thread_context) {
|
||||
struct wl_compositor *compositor_wrapper = Wayland_CreateEventThreadProxyWrapper(viddata->event_thread_context, viddata->compositor);
|
||||
state->surface = wl_compositor_create_surface(compositor_wrapper);
|
||||
WAYLAND_wl_proxy_wrapper_destroy(compositor_wrapper);
|
||||
} else {
|
||||
state->surface = wl_compositor_create_surface(viddata->compositor);
|
||||
}
|
||||
@@ -1541,10 +1384,6 @@ void Wayland_InitMouse(SDL_VideoData *data)
|
||||
mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
|
||||
mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
|
||||
|
||||
if (!Wayland_StartCursorThread(data)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Failed to start cursor animation event thread");
|
||||
}
|
||||
|
||||
SDL_HitTestResult r = SDL_HITTEST_NORMAL;
|
||||
while (r <= SDL_HITTEST_RESIZE_LEFT) {
|
||||
switch (r) {
|
||||
@@ -1604,7 +1443,6 @@ void Wayland_FiniMouse(SDL_VideoData *data)
|
||||
sys_cursors[i] = NULL;
|
||||
}
|
||||
|
||||
Wayland_DestroyCursorThread(data);
|
||||
Wayland_FreeCursorThemes(data);
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
|
||||
@@ -1684,6 +1684,11 @@ bool Wayland_VideoInit(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
|
||||
data->event_thread_context = Wayland_CreateEventThread(data, "SDL Event Thread Queue");
|
||||
if (!data->event_thread_context) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Failed to create event thread context");
|
||||
}
|
||||
|
||||
data->xkb_context = WAYLAND_xkb_context_new(0);
|
||||
if (!data->xkb_context) {
|
||||
return SDL_SetError("Failed to create XKB context");
|
||||
@@ -1787,6 +1792,10 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
|
||||
}
|
||||
|
||||
Wayland_FiniMouse(data);
|
||||
Wayland_QuitKeyboard(_this);
|
||||
|
||||
Wayland_DestroyEventThread(data->event_thread_context);
|
||||
data->event_thread_context = NULL;
|
||||
|
||||
if (data->pointer_constraints) {
|
||||
zwp_pointer_constraints_v1_destroy(data->pointer_constraints);
|
||||
@@ -1813,8 +1822,6 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
|
||||
data->key_inhibitor_manager = NULL;
|
||||
}
|
||||
|
||||
Wayland_QuitKeyboard(_this);
|
||||
|
||||
if (data->text_input_manager) {
|
||||
zwp_text_input_manager_v3_destroy(data->text_input_manager);
|
||||
data->text_input_manager = NULL;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../../core/linux/SDL_dbus.h"
|
||||
#include "../../core/linux/SDL_ime.h"
|
||||
#include "SDL_waylandeventthread.h"
|
||||
|
||||
struct xkb_context;
|
||||
struct SDL_WaylandSeat;
|
||||
@@ -97,6 +98,7 @@ struct SDL_VideoData
|
||||
struct SDL_WaylandSeat *last_implicit_grab_seat;
|
||||
struct SDL_WaylandSeat *last_incoming_data_offer_seat;
|
||||
struct SDL_WaylandSeat *last_incoming_primary_selection_seat;
|
||||
Wayland_EventThreadContext *event_thread_context;
|
||||
|
||||
SDL_DisplayData **output_list;
|
||||
int output_count;
|
||||
|
||||
Reference in New Issue
Block a user