Skip to content

Commit

Permalink
wayland: Implement basic window move events via wl_surface_listener.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
flibitijibibo authored and slouken committed Apr 17, 2021
1 parent 859230e commit ed24c34
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 30 deletions.
41 changes: 16 additions & 25 deletions src/video/wayland/SDL_waylandvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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)
Expand All @@ -317,21 +315,22 @@ 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
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 = {
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/video/wayland/SDL_waylandvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;

Expand Down
36 changes: 31 additions & 5 deletions src/video/wayland/SDL_waylandwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
}
Expand All @@ -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);
Expand Down

0 comments on commit ed24c34

Please sign in to comment.