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.
This commit is contained in:
Sam Lantinga
2025-08-13 19:42:49 -07:00
parent eeae48464e
commit f934b3e066

View File

@@ -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;