SDL: wayland: Add support for SDL_DisplayOrientation

From 5cc23868ed335b581c5716e8fb8313495a678ba4 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Mon, 15 Nov 2021 11:52:43 -0500
Subject: [PATCH] wayland: Add support for SDL_DisplayOrientation

---
 src/video/wayland/SDL_waylandvideo.c | 79 ++++++++++++++++++++++++----
 src/video/wayland/SDL_waylandvideo.h |  2 +
 2 files changed, 71 insertions(+), 10 deletions(-)

diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 777a1fa014..01444a01b1 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -302,12 +302,50 @@ display_handle_geometry(void *data,
 {
     SDL_WaylandOutputData *driverdata = data;
 
+    if (driverdata->done) {
+        SDL_ResetDisplayModes(driverdata->index);
+
+        /* The display has officially started over. */
+        driverdata->done = SDL_FALSE;
+    }
+
     driverdata->x = x;
     driverdata->y = y;
     driverdata->physical_width = physical_width;
     driverdata->physical_height = physical_height;
-    driverdata->placeholder.name = SDL_strdup(model);
+    if (driverdata->index == -1) {
+        driverdata->placeholder.name = SDL_strdup(model);
+    }
+
     driverdata->transform = transform;
+    #define TF_CASE(in, out) \
+        case WL_OUTPUT_TRANSFORM_##in: \
+            driverdata->orientation = SDL_ORIENTATION_##out; \
+            break;
+    if (driverdata->physical_width >= driverdata->physical_height) {
+        switch (transform) {
+            TF_CASE(NORMAL, LANDSCAPE)
+            TF_CASE(90, PORTRAIT)
+            TF_CASE(180, LANDSCAPE_FLIPPED)
+            TF_CASE(270, PORTRAIT_FLIPPED)
+            TF_CASE(FLIPPED, LANDSCAPE_FLIPPED)
+            TF_CASE(FLIPPED_90, PORTRAIT_FLIPPED)
+            TF_CASE(FLIPPED_180, LANDSCAPE)
+            TF_CASE(FLIPPED_270, PORTRAIT)
+        }
+    } else {
+        switch (transform) {
+            TF_CASE(NORMAL, PORTRAIT)
+            TF_CASE(90, LANDSCAPE)
+            TF_CASE(180, PORTRAIT_FLIPPED)
+            TF_CASE(270, LANDSCAPE_FLIPPED)
+            TF_CASE(FLIPPED, PORTRAIT_FLIPPED)
+            TF_CASE(FLIPPED_90, LANDSCAPE_FLIPPED)
+            TF_CASE(FLIPPED_180, PORTRAIT)
+            TF_CASE(FLIPPED_270, LANDSCAPE)
+        }
+    }
+    #undef TF_CASE
 }
 
 static void
@@ -322,6 +360,7 @@ display_handle_mode(void *data,
     SDL_DisplayMode mode;
 
     if (flags & WL_OUTPUT_MODE_CURRENT) {
+        /* Don't rotate this yet, handle_done will do it later */
         driverdata->width = width;
         driverdata->height = height;
         driverdata->refresh = refresh;
@@ -344,7 +383,11 @@ display_handle_mode(void *data,
     }
     mode.refresh_rate = refresh / 1000; /* mHz to Hz */
     mode.driverdata = driverdata->output;
-    SDL_AddDisplayMode(&driverdata->placeholder, &mode);
+    if (driverdata->index > -1) {
+        SDL_AddDisplayMode(SDL_GetDisplay(driverdata->index), &mode);
+    } else {
+        SDL_AddDisplayMode(&driverdata->placeholder, &mode);
+    }
 }
 
 static void
@@ -353,6 +396,7 @@ display_handle_done(void *data,
 {
     SDL_WaylandOutputData* driverdata = data;
     SDL_DisplayMode mode;
+    SDL_VideoDisplay *dpy;
 
     if (driverdata->done)
         return;
@@ -392,14 +436,28 @@ display_handle_done(void *data,
     }
     mode.refresh_rate = driverdata->refresh / 1000; /* mHz to Hz */
     mode.driverdata = driverdata->output;
-    SDL_AddDisplayMode(&driverdata->placeholder, &mode);
-    driverdata->placeholder.current_mode = mode;
-    driverdata->placeholder.desktop_mode = mode;
-
-    driverdata->placeholder.driverdata = driverdata;
-    SDL_AddVideoDisplay(&driverdata->placeholder, SDL_FALSE);
-    SDL_free(driverdata->placeholder.name);
-    SDL_zero(driverdata->placeholder);
+
+    if (driverdata->index > -1) {
+        dpy = SDL_GetDisplay(driverdata->index);
+    } else {
+        dpy = &driverdata->placeholder;
+    }
+
+    SDL_AddDisplayMode(dpy, &mode);
+    SDL_SetCurrentDisplayMode(dpy, &mode);
+    SDL_SetDesktopDisplayMode(dpy, &mode);
+
+    if (driverdata->index == -1) {
+        /* First time getting display info, create the VideoDisplay */
+        driverdata->placeholder.driverdata = driverdata;
+        driverdata->index = SDL_AddVideoDisplay(&driverdata->placeholder, SDL_FALSE);
+        SDL_free(driverdata->placeholder.name);
+        SDL_zero(driverdata->placeholder);
+
+        dpy = SDL_GetDisplay(driverdata->index);
+    }
+
+    SDL_SendDisplayEvent(dpy, SDL_DISPLAYEVENT_ORIENTATION, driverdata->orientation);
 }
 
 static void
@@ -433,6 +491,7 @@ Wayland_add_display(SDL_VideoData *d, uint32_t id)
     SDL_zerop(data);
     data->output = output;
     data->scale_factor = 1.0;
+    data->index = -1;
 
     wl_output_add_listener(output, &output_listener, data);
     SDL_WAYLAND_register_output(output);
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 2d8b6323a1..1568122ef0 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -92,8 +92,10 @@ typedef struct {
     struct wl_output *output;
     float scale_factor;
     int x, y, width, height, refresh, transform;
+    SDL_DisplayOrientation orientation;
     int physical_width, physical_height;
     float ddpi, hdpi, vdpi;
+    int index;
     SDL_VideoDisplay placeholder;
     SDL_bool done;
 } SDL_WaylandOutputData;