winewayland.drv: Make access to Wayland output information thread-safe.

Store all incoming Wayland output state as pending and make it current
in a thread-safe way when the "done" event arrives. This enables other
threads, with proper locking, to safely read consistent and complete
Wayland output information.
This commit is contained in:
Alexandros Frantzis 2023-04-26 10:38:04 +03:00 committed by Alexandre Julliard
parent 0031c3b46c
commit 1a336a259c
5 changed files with 113 additions and 39 deletions

View file

@ -1,7 +1,7 @@
MODULE = winewayland.drv MODULE = winewayland.drv
UNIXLIB = winewayland.so UNIXLIB = winewayland.so
UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS)
UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS)
SOURCES = \ SOURCES = \
display.c \ display.c \

View file

@ -50,7 +50,7 @@ void wayland_init_display_devices(BOOL force)
struct output_info struct output_info
{ {
int x, y; int x, y;
struct wayland_output *output; struct wayland_output_state *output;
}; };
static int output_info_cmp_primary_x_y(const void *va, const void *vb) static int output_info_cmp_primary_x_y(const void *va, const void *vb)
@ -298,11 +298,13 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_array_init(&output_info_array); wl_array_init(&output_info_array);
pthread_mutex_lock(&process_wayland.output_mutex);
wl_list_for_each(output, &process_wayland.output_list, link) wl_list_for_each(output, &process_wayland.output_list, link)
{ {
if (!output->current_mode) continue; if (!output->current.current_mode) continue;
output_info = wl_array_add(&output_info_array, sizeof(*output_info)); output_info = wl_array_add(&output_info_array, sizeof(*output_info));
if (output_info) output_info->output = output; if (output_info) output_info->output = &output->current;
else ERR("Failed to allocate space for output_info\n"); else ERR("Failed to allocate space for output_info\n");
} }
@ -321,5 +323,7 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_array_release(&output_info_array); wl_array_release(&output_info_array);
pthread_mutex_unlock(&process_wayland.output_mutex);
return TRUE; return TRUE;
} }

View file

@ -34,7 +34,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
struct wayland process_wayland = struct wayland process_wayland =
{ {
.output_list = {&process_wayland.output_list, &process_wayland.output_list} .output_list = {&process_wayland.output_list, &process_wayland.output_list},
.output_mutex = PTHREAD_MUTEX_INITIALIZER
}; };
/********************************************************************** /**********************************************************************
@ -77,7 +78,7 @@ static void registry_handle_global_remove(void *data, struct wl_registry *regist
{ {
if (output->global_id == id) if (output->global_id == id)
{ {
TRACE("removing output->name=%s\n", output->name); TRACE("removing output->name=%s\n", output->current.name);
wayland_output_destroy(output); wayland_output_destroy(output);
return; return;
} }

View file

@ -35,6 +35,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
static const int32_t default_refresh = 60000; static const int32_t default_refresh = 60000;
static uint32_t next_output_id = 0; static uint32_t next_output_id = 0;
#define WAYLAND_OUTPUT_CHANGED_MODES 0x01
#define WAYLAND_OUTPUT_CHANGED_NAME 0x02
#define WAYLAND_OUTPUT_CHANGED_LOGICAL_XY 0x04
#define WAYLAND_OUTPUT_CHANGED_LOGICAL_WH 0x08
/********************************************************************** /**********************************************************************
* Output handling * Output handling
*/ */
@ -62,9 +67,9 @@ static int wayland_output_mode_cmp_rb(const void *key,
return 0; return 0;
} }
static void wayland_output_add_mode(struct wayland_output *output, static void wayland_output_state_add_mode(struct wayland_output_state *state,
int32_t width, int32_t height, int32_t width, int32_t height,
int32_t refresh, BOOL current) int32_t refresh, BOOL current)
{ {
struct rb_entry *mode_entry; struct rb_entry *mode_entry;
struct wayland_output_mode *mode; struct wayland_output_mode *mode;
@ -75,7 +80,7 @@ static void wayland_output_add_mode(struct wayland_output *output,
.refresh = refresh, .refresh = refresh,
}; };
mode_entry = rb_get(&output->modes, &key); mode_entry = rb_get(&state->modes, &key);
if (mode_entry) if (mode_entry)
{ {
mode = RB_ENTRY_VALUE(mode_entry, struct wayland_output_mode, entry); mode = RB_ENTRY_VALUE(mode_entry, struct wayland_output_mode, entry);
@ -91,10 +96,10 @@ static void wayland_output_add_mode(struct wayland_output *output,
mode->width = width; mode->width = width;
mode->height = height; mode->height = height;
mode->refresh = refresh; mode->refresh = refresh;
rb_put(&output->modes, mode, &mode->entry); rb_put(&state->modes, mode, &mode->entry);
} }
if (current) output->current_mode = mode; if (current) state->current_mode = mode;
} }
static void maybe_init_display_devices(void) static void maybe_init_display_devices(void)
@ -117,19 +122,65 @@ static void maybe_init_display_devices(void)
wayland_init_display_devices(TRUE); wayland_init_display_devices(TRUE);
} }
static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx)
{
free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry));
}
static void wayland_output_done(struct wayland_output *output) static void wayland_output_done(struct wayland_output *output)
{ {
struct wayland_output_mode *mode; struct wayland_output_mode *mode;
TRACE("name=%s logical=%d,%d+%dx%d\n", /* Update current state from pending state. */
output->name, output->logical_x, output->logical_y, pthread_mutex_lock(&process_wayland.output_mutex);
output->logical_w, output->logical_h);
RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_MODES)
{
RB_FOR_EACH_ENTRY(mode, &output->pending.modes, struct wayland_output_mode, entry)
{
wayland_output_state_add_mode(&output->current,
mode->width, mode->height, mode->refresh,
mode == output->pending.current_mode);
}
rb_destroy(&output->pending.modes, wayland_output_mode_free_rb, NULL);
rb_init(&output->pending.modes, wayland_output_mode_cmp_rb);
}
if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_NAME)
{
free(output->current.name);
output->current.name = output->pending.name;
output->pending.name = NULL;
}
if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_LOGICAL_XY)
{
output->current.logical_x = output->pending.logical_x;
output->current.logical_y = output->pending.logical_y;
}
if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_LOGICAL_WH)
{
output->current.logical_w = output->pending.logical_w;
output->current.logical_h = output->pending.logical_h;
}
if (wl_list_empty(&output->link))
wl_list_insert(process_wayland.output_list.prev, &output->link);
output->pending_flags = 0;
pthread_mutex_unlock(&process_wayland.output_mutex);
TRACE("name=%s logical=%d,%d+%dx%d\n",
output->current.name, output->current.logical_x, output->current.logical_y,
output->current.logical_w, output->current.logical_h);
RB_FOR_EACH_ENTRY(mode, &output->current.modes, struct wayland_output_mode, entry)
{ {
TRACE("mode %dx%d @ %d %s\n", TRACE("mode %dx%d @ %d %s\n",
mode->width, mode->height, mode->refresh, mode->width, mode->height, mode->refresh,
output->current_mode == mode ? "*" : ""); output->current.current_mode == mode ? "*" : "");
} }
maybe_init_display_devices(); maybe_init_display_devices();
@ -153,8 +204,10 @@ static void output_handle_mode(void *data, struct wl_output *wl_output,
/* Windows apps don't expect a zero refresh rate, so use a default value. */ /* Windows apps don't expect a zero refresh rate, so use a default value. */
if (refresh == 0) refresh = default_refresh; if (refresh == 0) refresh = default_refresh;
wayland_output_add_mode(output, width, height, refresh, wayland_output_state_add_mode(&output->pending, width, height, refresh,
(flags & WL_OUTPUT_MODE_CURRENT)); (flags & WL_OUTPUT_MODE_CURRENT));
output->pending_flags |= WAYLAND_OUTPUT_CHANGED_MODES;
} }
static void output_handle_done(void *data, struct wl_output *wl_output) static void output_handle_done(void *data, struct wl_output *wl_output)
@ -187,8 +240,9 @@ static void zxdg_output_v1_handle_logical_position(void *data,
{ {
struct wayland_output *output = data; struct wayland_output *output = data;
TRACE("logical_x=%d logical_y=%d\n", x, y); TRACE("logical_x=%d logical_y=%d\n", x, y);
output->logical_x = x; output->pending.logical_x = x;
output->logical_y = y; output->pending.logical_y = y;
output->pending_flags |= WAYLAND_OUTPUT_CHANGED_LOGICAL_XY;
} }
static void zxdg_output_v1_handle_logical_size(void *data, static void zxdg_output_v1_handle_logical_size(void *data,
@ -198,8 +252,9 @@ static void zxdg_output_v1_handle_logical_size(void *data,
{ {
struct wayland_output *output = data; struct wayland_output *output = data;
TRACE("logical_w=%d logical_h=%d\n", width, height); TRACE("logical_w=%d logical_h=%d\n", width, height);
output->logical_w = width; output->pending.logical_w = width;
output->logical_h = height; output->pending.logical_h = height;
output->pending_flags |= WAYLAND_OUTPUT_CHANGED_LOGICAL_WH;
} }
static void zxdg_output_v1_handle_done(void *data, static void zxdg_output_v1_handle_done(void *data,
@ -218,8 +273,9 @@ static void zxdg_output_v1_handle_name(void *data,
{ {
struct wayland_output *output = data; struct wayland_output *output = data;
free(output->name); free(output->pending.name);
output->name = strdup(name); output->pending.name = strdup(name);
output->pending_flags |= WAYLAND_OUTPUT_CHANGED_NAME;
} }
static void zxdg_output_v1_handle_description(void *data, static void zxdg_output_v1_handle_description(void *data,
@ -259,14 +315,15 @@ BOOL wayland_output_create(uint32_t id, uint32_t version)
wl_output_add_listener(output->wl_output, &output_listener, output); wl_output_add_listener(output->wl_output, &output_listener, output);
wl_list_init(&output->link); wl_list_init(&output->link);
rb_init(&output->modes, wayland_output_mode_cmp_rb); rb_init(&output->pending.modes, wayland_output_mode_cmp_rb);
rb_init(&output->current.modes, wayland_output_mode_cmp_rb);
/* Have a fallback while we don't have compositor given name. */ /* Have a fallback while we don't have compositor given name. */
name_len = snprintf(NULL, 0, "WaylandOutput%d", next_output_id); name_len = snprintf(NULL, 0, "WaylandOutput%d", next_output_id);
output->name = malloc(name_len + 1); output->current.name = malloc(name_len + 1);
if (output->name) if (output->current.name)
{ {
snprintf(output->name, name_len + 1, "WaylandOutput%d", next_output_id++); snprintf(output->current.name, name_len + 1, "WaylandOutput%d", next_output_id++);
} }
else else
{ {
@ -277,8 +334,6 @@ BOOL wayland_output_create(uint32_t id, uint32_t version)
if (process_wayland.zxdg_output_manager_v1) if (process_wayland.zxdg_output_manager_v1)
wayland_output_use_xdg_extension(output); wayland_output_use_xdg_extension(output);
wl_list_insert(process_wayland.output_list.prev, &output->link);
return TRUE; return TRUE;
err: err:
@ -286,9 +341,10 @@ err:
return FALSE; return FALSE;
} }
static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) static void wayland_output_state_deinit(struct wayland_output_state *state)
{ {
free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry)); rb_destroy(&state->modes, wayland_output_mode_free_rb, NULL);
free(state->name);
} }
/********************************************************************** /**********************************************************************
@ -298,12 +354,15 @@ static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx)
*/ */
void wayland_output_destroy(struct wayland_output *output) void wayland_output_destroy(struct wayland_output *output)
{ {
rb_destroy(&output->modes, wayland_output_mode_free_rb, NULL); pthread_mutex_lock(&process_wayland.output_mutex);
wl_list_remove(&output->link); wl_list_remove(&output->link);
pthread_mutex_unlock(&process_wayland.output_mutex);
wayland_output_state_deinit(&output->pending);
wayland_output_state_deinit(&output->current);
if (output->zxdg_output_v1) if (output->zxdg_output_v1)
zxdg_output_v1_destroy(output->zxdg_output_v1); zxdg_output_v1_destroy(output->zxdg_output_v1);
wl_output_destroy(output->wl_output); wl_output_destroy(output->wl_output);
free(output->name);
free(output); free(output);
maybe_init_display_devices(); maybe_init_display_devices();

View file

@ -25,6 +25,7 @@
# error You must include config.h to use this header # error You must include config.h to use this header
#endif #endif
#include <pthread.h>
#include <wayland-client.h> #include <wayland-client.h>
#include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h"
@ -53,6 +54,8 @@ struct wayland
struct wl_registry *wl_registry; struct wl_registry *wl_registry;
struct zxdg_output_manager_v1 *zxdg_output_manager_v1; struct zxdg_output_manager_v1 *zxdg_output_manager_v1;
struct wl_list output_list; struct wl_list output_list;
/* Protects the output_list and the wayland_output.current states. */
pthread_mutex_t output_mutex;
}; };
struct wayland_output_mode struct wayland_output_mode
@ -63,17 +66,24 @@ struct wayland_output_mode
int32_t refresh; int32_t refresh;
}; };
struct wayland_output struct wayland_output_state
{ {
struct wl_list link;
struct wl_output *wl_output;
struct zxdg_output_v1 *zxdg_output_v1;
struct rb_tree modes; struct rb_tree modes;
struct wayland_output_mode *current_mode; struct wayland_output_mode *current_mode;
char *name; char *name;
int logical_x, logical_y; int logical_x, logical_y;
int logical_w, logical_h; int logical_w, logical_h;
};
struct wayland_output
{
struct wl_list link;
struct wl_output *wl_output;
struct zxdg_output_v1 *zxdg_output_v1;
uint32_t global_id; uint32_t global_id;
unsigned int pending_flags;
struct wayland_output_state pending;
struct wayland_output_state current;
}; };
/********************************************************************** /**********************************************************************