diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index e8cd6d619..cab71150c 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/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;