Parcourir la source

x11: fixed creating a window when all displays are disconnected

The X server maintains the desktop, but XRandR sends disconnect notifications for all displays. In this case fall back to the generic X11 desktop as a display.
Sam Lantinga il y a 1 mois
Parent
commit
f934b3e066
1 fichiers modifiés avec 115 ajouts et 73 suppressions
  1. 115 73
      src/video/x11/SDL_x11modes.c

+ 115 - 73
src/video/x11/SDL_x11modes.c

@@ -243,7 +243,96 @@ SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *
     return SDL_PIXELFORMAT_UNKNOWN;
 }
 
+static SDL_DisplayID X11_AddGenericDisplay(SDL_VideoDevice *_this, bool send_event)
+{
+    // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function.
+    SDL_VideoData *data = _this->internal;
+    Display *dpy = data->display;
+    const int default_screen = DefaultScreen(dpy);
+    Screen *screen = ScreenOfDisplay(dpy, default_screen);
+    int scanline_pad, n, i;
+    SDL_DisplayModeData *modedata;
+    SDL_DisplayData *displaydata;
+    SDL_DisplayMode mode;
+    XPixmapFormatValues *pixmapformats;
+    Uint32 pixelformat;
+    XVisualInfo vinfo;
+    SDL_VideoDisplay display;
+
+    // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen.
+
+    if (!get_visualinfo(dpy, default_screen, &vinfo)) {
+        return SDL_SetError("Failed to find an X11 visual for the primary display");
+    }
+
+    pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
+    if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
+        return SDL_SetError("Palettized video modes are no longer supported");
+    }
+
+    SDL_zero(mode);
+    mode.w = WidthOfScreen(screen);
+    mode.h = HeightOfScreen(screen);
+    mode.format = pixelformat;
+
+    displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
+    if (!displaydata) {
+        return false;
+    }
+
+    modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
+    if (!modedata) {
+        SDL_free(displaydata);
+        return false;
+    }
+    mode.internal = modedata;
+
+    displaydata->screen = default_screen;
+    displaydata->visual = vinfo.visual;
+    displaydata->depth = vinfo.depth;
+
+    scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
+    pixmapformats = X11_XListPixmapFormats(dpy, &n);
+    if (pixmapformats) {
+        for (i = 0; i < n; ++i) {
+            if (pixmapformats[i].depth == vinfo.depth) {
+                scanline_pad = pixmapformats[i].scanline_pad;
+                break;
+            }
+        }
+        X11_XFree(pixmapformats);
+    }
+
+    displaydata->scanline_pad = scanline_pad;
+    displaydata->x = 0;
+    displaydata->y = 0;
+    displaydata->use_xrandr = false;
+
+    SDL_zero(display);
+    display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
+    display.desktop_mode = mode;
+    display.internal = displaydata;
+    display.content_scale = X11_GetGlobalContentScale(_this);
+    return SDL_AddVideoDisplay(&display, send_event);
+}
+
 #ifdef SDL_VIDEO_DRIVER_X11_XRANDR
+
+static void X11_RemoveGenericDisplay(SDL_VideoDevice *_this)
+{
+    SDL_DisplayID *displays = SDL_GetDisplays(NULL);
+    if (displays) {
+        for (int i = 0; displays[i]; ++i) {
+            SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
+            const SDL_DisplayData *displaydata = display->internal;
+            if (!displaydata->xrandr_output) {
+                SDL_DelVideoDisplay(displays[i], true);
+            }
+        }
+        SDL_free(displays);
+    }
+}
+
 static bool CheckXRandR(Display *display, int *major, int *minor)
 {
     // Default the extension not available
@@ -525,10 +614,17 @@ static bool X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int scree
         return true; // failed to query data, skip this display
     }
 
-    if (SDL_AddVideoDisplay(&display, send_event) == 0) {
+    SDL_DisplayID displayID = SDL_AddVideoDisplay(&display, false);
+    if (displayID == 0) {
         return false;
     }
 
+    // We added an XRandR display, remove the generic display, if any
+    X11_RemoveGenericDisplay(_this);
+
+    if (send_event) {
+        SDL_SendDisplayEvent(SDL_GetVideoDisplay(displayID), SDL_EVENT_DISPLAY_ADDED, 0, 0);
+    }
     return true;
 }
 
@@ -592,7 +688,7 @@ static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
         for (int i = 0; displays[i]; ++i) {
             SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
             const SDL_DisplayData *displaydata = display->internal;
-            if (displaydata->screen == screen) {
+            if (displaydata->xrandr_output && displaydata->screen == screen) {
                 X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display);
             }
         }
@@ -638,8 +734,12 @@ static void X11_CheckDisplaysRemoved(SDL_VideoDevice *_this, Display *dpy)
 
     for (int i = 0; i < num_displays; ++i) {
         if (displays[i]) {
-            // This display wasn't in the XRandR list
-            SDL_DelVideoDisplay(displays[i], true);
+            SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
+            const SDL_DisplayData *displaydata = display->internal;
+            if (displaydata->xrandr_output) {
+                // This display wasn't in the XRandR list
+                SDL_DelVideoDisplay(displays[i], true);
+            }
         }
     }
     SDL_free(displays);
@@ -673,7 +773,17 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
 
     if (ev->connection == RR_Disconnected) { // output is going away
         if (display) {
+            // Add the generic display if we're about to remove the last XRandR display
+            SDL_DisplayID generic_display = 0;
+            if (_this->num_displays == 1) {
+                generic_display = X11_AddGenericDisplay(_this, false);
+            }
+
             SDL_DelVideoDisplay(display->id, true);
+
+            if (generic_display) {
+                SDL_SendDisplayEvent(SDL_GetVideoDisplay(generic_display), SDL_EVENT_DISPLAY_ADDED, 0, 0);
+            }
         }
         X11_CheckDisplaysMoved(_this, ev->display);
 
@@ -813,75 +923,7 @@ static bool X11_InitModes_XRandR(SDL_VideoDevice *_this)
    enumerate the current displays and their current sizes. */
 static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this)
 {
-    // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function.
-    SDL_VideoData *data = _this->internal;
-    Display *dpy = data->display;
-    const int default_screen = DefaultScreen(dpy);
-    Screen *screen = ScreenOfDisplay(dpy, default_screen);
-    int scanline_pad, n, i;
-    SDL_DisplayModeData *modedata;
-    SDL_DisplayData *displaydata;
-    SDL_DisplayMode mode;
-    XPixmapFormatValues *pixmapformats;
-    Uint32 pixelformat;
-    XVisualInfo vinfo;
-    SDL_VideoDisplay display;
-
-    // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen.
-
-    if (!get_visualinfo(dpy, default_screen, &vinfo)) {
-        return SDL_SetError("Failed to find an X11 visual for the primary display");
-    }
-
-    pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
-    if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
-        return SDL_SetError("Palettized video modes are no longer supported");
-    }
-
-    SDL_zero(mode);
-    mode.w = WidthOfScreen(screen);
-    mode.h = HeightOfScreen(screen);
-    mode.format = pixelformat;
-
-    displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
-    if (!displaydata) {
-        return false;
-    }
-
-    modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
-    if (!modedata) {
-        SDL_free(displaydata);
-        return false;
-    }
-    mode.internal = modedata;
-
-    displaydata->screen = default_screen;
-    displaydata->visual = vinfo.visual;
-    displaydata->depth = vinfo.depth;
-
-    scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
-    pixmapformats = X11_XListPixmapFormats(dpy, &n);
-    if (pixmapformats) {
-        for (i = 0; i < n; ++i) {
-            if (pixmapformats[i].depth == vinfo.depth) {
-                scanline_pad = pixmapformats[i].scanline_pad;
-                break;
-            }
-        }
-        X11_XFree(pixmapformats);
-    }
-
-    displaydata->scanline_pad = scanline_pad;
-    displaydata->x = 0;
-    displaydata->y = 0;
-    displaydata->use_xrandr = false;
-
-    SDL_zero(display);
-    display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
-    display.desktop_mode = mode;
-    display.internal = displaydata;
-    display.content_scale = X11_GetGlobalContentScale(_this);
-    if (SDL_AddVideoDisplay(&display, true) == 0) {
+    if (X11_AddGenericDisplay(_this, true) == 0) {
         return false;
     }
     return true;