Skip to content

Commit f934b3e

Browse files
committed
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.
1 parent eeae484 commit f934b3e

File tree

1 file changed

+115
-73
lines changed

1 file changed

+115
-73
lines changed

src/video/x11/SDL_x11modes.c

Lines changed: 115 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,96 @@ SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *
243243
return SDL_PIXELFORMAT_UNKNOWN;
244244
}
245245

246+
static SDL_DisplayID X11_AddGenericDisplay(SDL_VideoDevice *_this, bool send_event)
247+
{
248+
// !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function.
249+
SDL_VideoData *data = _this->internal;
250+
Display *dpy = data->display;
251+
const int default_screen = DefaultScreen(dpy);
252+
Screen *screen = ScreenOfDisplay(dpy, default_screen);
253+
int scanline_pad, n, i;
254+
SDL_DisplayModeData *modedata;
255+
SDL_DisplayData *displaydata;
256+
SDL_DisplayMode mode;
257+
XPixmapFormatValues *pixmapformats;
258+
Uint32 pixelformat;
259+
XVisualInfo vinfo;
260+
SDL_VideoDisplay display;
261+
262+
// note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen.
263+
264+
if (!get_visualinfo(dpy, default_screen, &vinfo)) {
265+
return SDL_SetError("Failed to find an X11 visual for the primary display");
266+
}
267+
268+
pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
269+
if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
270+
return SDL_SetError("Palettized video modes are no longer supported");
271+
}
272+
273+
SDL_zero(mode);
274+
mode.w = WidthOfScreen(screen);
275+
mode.h = HeightOfScreen(screen);
276+
mode.format = pixelformat;
277+
278+
displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
279+
if (!displaydata) {
280+
return false;
281+
}
282+
283+
modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
284+
if (!modedata) {
285+
SDL_free(displaydata);
286+
return false;
287+
}
288+
mode.internal = modedata;
289+
290+
displaydata->screen = default_screen;
291+
displaydata->visual = vinfo.visual;
292+
displaydata->depth = vinfo.depth;
293+
294+
scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
295+
pixmapformats = X11_XListPixmapFormats(dpy, &n);
296+
if (pixmapformats) {
297+
for (i = 0; i < n; ++i) {
298+
if (pixmapformats[i].depth == vinfo.depth) {
299+
scanline_pad = pixmapformats[i].scanline_pad;
300+
break;
301+
}
302+
}
303+
X11_XFree(pixmapformats);
304+
}
305+
306+
displaydata->scanline_pad = scanline_pad;
307+
displaydata->x = 0;
308+
displaydata->y = 0;
309+
displaydata->use_xrandr = false;
310+
311+
SDL_zero(display);
312+
display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
313+
display.desktop_mode = mode;
314+
display.internal = displaydata;
315+
display.content_scale = X11_GetGlobalContentScale(_this);
316+
return SDL_AddVideoDisplay(&display, send_event);
317+
}
318+
246319
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
320+
321+
static void X11_RemoveGenericDisplay(SDL_VideoDevice *_this)
322+
{
323+
SDL_DisplayID *displays = SDL_GetDisplays(NULL);
324+
if (displays) {
325+
for (int i = 0; displays[i]; ++i) {
326+
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
327+
const SDL_DisplayData *displaydata = display->internal;
328+
if (!displaydata->xrandr_output) {
329+
SDL_DelVideoDisplay(displays[i], true);
330+
}
331+
}
332+
SDL_free(displays);
333+
}
334+
}
335+
247336
static bool CheckXRandR(Display *display, int *major, int *minor)
248337
{
249338
// Default the extension not available
@@ -525,10 +614,17 @@ static bool X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int scree
525614
return true; // failed to query data, skip this display
526615
}
527616

528-
if (SDL_AddVideoDisplay(&display, send_event) == 0) {
617+
SDL_DisplayID displayID = SDL_AddVideoDisplay(&display, false);
618+
if (displayID == 0) {
529619
return false;
530620
}
531621

622+
// We added an XRandR display, remove the generic display, if any
623+
X11_RemoveGenericDisplay(_this);
624+
625+
if (send_event) {
626+
SDL_SendDisplayEvent(SDL_GetVideoDisplay(displayID), SDL_EVENT_DISPLAY_ADDED, 0, 0);
627+
}
532628
return true;
533629
}
534630

@@ -592,7 +688,7 @@ static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
592688
for (int i = 0; displays[i]; ++i) {
593689
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
594690
const SDL_DisplayData *displaydata = display->internal;
595-
if (displaydata->screen == screen) {
691+
if (displaydata->xrandr_output && displaydata->screen == screen) {
596692
X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display);
597693
}
598694
}
@@ -638,8 +734,12 @@ static void X11_CheckDisplaysRemoved(SDL_VideoDevice *_this, Display *dpy)
638734

639735
for (int i = 0; i < num_displays; ++i) {
640736
if (displays[i]) {
641-
// This display wasn't in the XRandR list
642-
SDL_DelVideoDisplay(displays[i], true);
737+
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
738+
const SDL_DisplayData *displaydata = display->internal;
739+
if (displaydata->xrandr_output) {
740+
// This display wasn't in the XRandR list
741+
SDL_DelVideoDisplay(displays[i], true);
742+
}
643743
}
644744
}
645745
SDL_free(displays);
@@ -673,7 +773,17 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
673773

674774
if (ev->connection == RR_Disconnected) { // output is going away
675775
if (display) {
776+
// Add the generic display if we're about to remove the last XRandR display
777+
SDL_DisplayID generic_display = 0;
778+
if (_this->num_displays == 1) {
779+
generic_display = X11_AddGenericDisplay(_this, false);
780+
}
781+
676782
SDL_DelVideoDisplay(display->id, true);
783+
784+
if (generic_display) {
785+
SDL_SendDisplayEvent(SDL_GetVideoDisplay(generic_display), SDL_EVENT_DISPLAY_ADDED, 0, 0);
786+
}
677787
}
678788
X11_CheckDisplaysMoved(_this, ev->display);
679789

@@ -813,75 +923,7 @@ static bool X11_InitModes_XRandR(SDL_VideoDevice *_this)
813923
enumerate the current displays and their current sizes. */
814924
static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this)
815925
{
816-
// !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function.
817-
SDL_VideoData *data = _this->internal;
818-
Display *dpy = data->display;
819-
const int default_screen = DefaultScreen(dpy);
820-
Screen *screen = ScreenOfDisplay(dpy, default_screen);
821-
int scanline_pad, n, i;
822-
SDL_DisplayModeData *modedata;
823-
SDL_DisplayData *displaydata;
824-
SDL_DisplayMode mode;
825-
XPixmapFormatValues *pixmapformats;
826-
Uint32 pixelformat;
827-
XVisualInfo vinfo;
828-
SDL_VideoDisplay display;
829-
830-
// note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen.
831-
832-
if (!get_visualinfo(dpy, default_screen, &vinfo)) {
833-
return SDL_SetError("Failed to find an X11 visual for the primary display");
834-
}
835-
836-
pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
837-
if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
838-
return SDL_SetError("Palettized video modes are no longer supported");
839-
}
840-
841-
SDL_zero(mode);
842-
mode.w = WidthOfScreen(screen);
843-
mode.h = HeightOfScreen(screen);
844-
mode.format = pixelformat;
845-
846-
displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
847-
if (!displaydata) {
848-
return false;
849-
}
850-
851-
modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
852-
if (!modedata) {
853-
SDL_free(displaydata);
854-
return false;
855-
}
856-
mode.internal = modedata;
857-
858-
displaydata->screen = default_screen;
859-
displaydata->visual = vinfo.visual;
860-
displaydata->depth = vinfo.depth;
861-
862-
scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
863-
pixmapformats = X11_XListPixmapFormats(dpy, &n);
864-
if (pixmapformats) {
865-
for (i = 0; i < n; ++i) {
866-
if (pixmapformats[i].depth == vinfo.depth) {
867-
scanline_pad = pixmapformats[i].scanline_pad;
868-
break;
869-
}
870-
}
871-
X11_XFree(pixmapformats);
872-
}
873-
874-
displaydata->scanline_pad = scanline_pad;
875-
displaydata->x = 0;
876-
displaydata->y = 0;
877-
displaydata->use_xrandr = false;
878-
879-
SDL_zero(display);
880-
display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
881-
display.desktop_mode = mode;
882-
display.internal = displaydata;
883-
display.content_scale = X11_GetGlobalContentScale(_this);
884-
if (SDL_AddVideoDisplay(&display, true) == 0) {
926+
if (X11_AddGenericDisplay(_this, true) == 0) {
885927
return false;
886928
}
887929
return true;

0 commit comments

Comments
 (0)