wayland: Implement basic window move events via wl_surface_listener. This unearthed an unspeakably large amount of bugs in the wl_output enumerator, notably the fact that the wl_output user pointer was to temporary memory! This was "fixed" in e862856, and was then pointed out as a leak in 4183211, which was undone in d9ba204. The busted fix was correct that the malloc was an issue, but wrong about _why_; SDL_AddVideoDisplay copies by value and does not reuse the pointer, so generally you want your VideoDisplay to be on the stack, but of course the callbacks don't allow that, so a malloc was a workaround. But we can do better and just host our temporary display inside WaylandOutputData because that will be persistent while also not leaking. Wait, wasn't I talking about move events? Right, that: wl_surface_listener does at least give us the ability to know what monitor we're on, even though we have no idea where we are on the monitor. All we need to do is check the wl_output against the display list and then push a move event that both indicates the correct display while also not being _too_ much of a lie (but enough of a lie to where our event doesn't get discarded as "undefined" or whatever). The index check for the video display is what spawned the great nightmare you see before you; aside from the bugfix this is actually a really basic patch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 5a32d8a..ea5e5b2 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -249,10 +249,10 @@ display_handle_geometry(void *data,
int transform)
{
- SDL_VideoDisplay *display = data;
+ SDL_WaylandOutputData *driverdata = data;
- display->name = SDL_strdup(model);
- ((SDL_WaylandOutputData*)display->driverdata)->transform = transform;
+ driverdata->placeholder.name = SDL_strdup(model);
+ driverdata->transform = transform;
}
static void
@@ -263,8 +263,7 @@ display_handle_mode(void *data,
int height,
int refresh)
{
- SDL_VideoDisplay *display = data;
- SDL_WaylandOutputData* driverdata = display->driverdata;
+ SDL_WaylandOutputData* driverdata = data;
SDL_DisplayMode mode;
if (flags & WL_OUTPUT_MODE_CURRENT) {
@@ -290,15 +289,14 @@ display_handle_mode(void *data,
}
mode.refresh_rate = refresh / 1000; /* mHz to Hz */
mode.driverdata = driverdata->output;
- SDL_AddDisplayMode(display, &mode);
+ SDL_AddDisplayMode(&driverdata->placeholder, &mode);
}
static void
display_handle_done(void *data,
struct wl_output *output)
{
- SDL_VideoDisplay *display = data;
- SDL_WaylandOutputData* driverdata = display->driverdata;
+ SDL_WaylandOutputData* driverdata = data;
SDL_DisplayMode mode;
if (driverdata->done)
@@ -317,12 +315,13 @@ display_handle_done(void *data,
}
mode.refresh_rate = driverdata->refresh / 1000; /* mHz to Hz */
mode.driverdata = driverdata->output;
- SDL_AddDisplayMode(display, &mode);
- display->current_mode = mode;
- display->desktop_mode = mode;
+ SDL_AddDisplayMode(&driverdata->placeholder, &mode);
+ driverdata->placeholder.current_mode = mode;
+ driverdata->placeholder.desktop_mode = mode;
- SDL_AddVideoDisplay(display, SDL_FALSE);
- SDL_free(display->name);
+ driverdata->placeholder.driverdata = driverdata;
+ SDL_AddVideoDisplay(&driverdata->placeholder, SDL_FALSE);
+ SDL_zero(driverdata->placeholder);
}
static void
@@ -330,8 +329,8 @@ display_handle_scale(void *data,
struct wl_output *output,
int32_t factor)
{
- SDL_VideoDisplay *display = data;
- ((SDL_WaylandOutputData*)display->driverdata)->scale_factor = factor;
+ SDL_WaylandOutputData *driverdata = data;
+ driverdata->scale_factor = factor;
}
static const struct wl_output_listener output_listener = {
@@ -346,26 +345,18 @@ Wayland_add_display(SDL_VideoData *d, uint32_t id)
{
struct wl_output *output;
SDL_WaylandOutputData *data;
- SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
- if (!display) {
- SDL_OutOfMemory();
- return;
- }
- SDL_zero(*display);
output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
if (!output) {
SDL_SetError("Failed to retrieve output.");
- SDL_free(display);
return;
}
data = SDL_malloc(sizeof *data);
+ SDL_zerop(data);
data->output = output;
data->scale_factor = 1.0;
- data->done = SDL_FALSE;
- display->driverdata = data;
- wl_output_add_listener(output, &output_listener, display);
+ wl_output_add_listener(output, &output_listener, data);
}
#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 7f5a60a..1c01dfd 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -39,6 +39,7 @@
#include <EGL/egl.h>
#include "wayland-util.h"
+#include "../SDL_sysvideo.h"
#include "../../core/linux/SDL_dbus.h"
#include "../../core/linux/SDL_ime.h"
@@ -93,6 +94,7 @@ typedef struct {
struct wl_output *output;
float scale_factor;
int width, height, refresh, transform;
+ SDL_VideoDisplay placeholder;
SDL_bool done;
} SDL_WaylandOutputData;
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 7f90310..a3029f0 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -467,8 +467,7 @@ update_scale_factor(SDL_WindowData *window) {
}
for (i = 0; i < window->num_outputs; i++) {
- SDL_VideoDisplay *display = wl_output_get_user_data(window->outputs[i]);
- SDL_WaylandOutputData* driverdata = display->driverdata;
+ SDL_WaylandOutputData* driverdata = wl_output_get_user_data(window->outputs[i]);
float factor = driverdata->scale_factor;
if (factor > new_factor) {
new_factor = factor;
@@ -487,26 +486,50 @@ update_scale_factor(SDL_WindowData *window) {
}
}
+/* While we can't get window position from the compositor, we do at least know
+ * what monitor we're on, so let's send move events that put the window at the
+ * center of the whatever display the wl_surface_listener events give us.
+ */
+static void
+Wayland_move_window(SDL_Window *window,
+ SDL_WaylandOutputData *driverdata)
+{
+ int i, numdisplays = SDL_GetNumVideoDisplays();
+ for (i = 0; i < numdisplays; i += 1) {
+ if (SDL_GetDisplay(i)->driverdata == driverdata) {
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED,
+ SDL_WINDOWPOS_CENTERED_DISPLAY(i),
+ SDL_WINDOWPOS_CENTERED_DISPLAY(i));
+ break;
+ }
+ }
+}
+
static void
handle_surface_enter(void *data, struct wl_surface *surface,
- struct wl_output *output) {
+ struct wl_output *output)
+{
SDL_WindowData *window = data;
window->outputs = SDL_realloc(window->outputs, (window->num_outputs + 1) * sizeof *window->outputs);
window->outputs[window->num_outputs++] = output;
update_scale_factor(window);
+
+ Wayland_move_window(window->sdlwindow, wl_output_get_user_data(output));
}
static void
handle_surface_leave(void *data, struct wl_surface *surface,
- struct wl_output *output) {
+ struct wl_output *output)
+{
SDL_WindowData *window = data;
- int i;
+ int i, send_move_event = 0;
for (i = 0; i < window->num_outputs; i++) {
if (window->outputs[i] == output) { /* remove this one */
if (i == (window->num_outputs-1)) {
window->outputs[i] = NULL;
+ send_move_event = 1;
} else {
SDL_memmove(&window->outputs[i], &window->outputs[i+1], sizeof (output) * ((window->num_outputs - i) - 1));
}
@@ -518,6 +541,9 @@ handle_surface_leave(void *data, struct wl_surface *surface,
if (window->num_outputs == 0) {
SDL_free(window->outputs);
window->outputs = NULL;
+ } else if (send_move_event) {
+ Wayland_move_window(window->sdlwindow,
+ wl_output_get_user_data(window->outputs[window->num_outputs - 1]));
}
update_scale_factor(window);