From d3a6be9607a5e3998bc4887f05b86b278d2239a0 Mon Sep 17 00:00:00 2001 From: eafton Date: Wed, 10 Sep 2025 18:05:23 +0300 Subject: [PATCH] X11TK: Implement SHM transport for fractional scaling (#13919) --- src/video/x11/SDL_x11sym.h | 4 + src/video/x11/SDL_x11toolkit.c | 256 ++++++++++++++++++++++++++++++--- src/video/x11/SDL_x11toolkit.h | 9 ++ 3 files changed, 249 insertions(+), 20 deletions(-) diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 07f2b4539f..9ee239c3f1 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -50,6 +50,7 @@ SDL_X11_SYM(void,XSetFont,(Display* a,GC b,Font c)) SDL_X11_SYM(Status,XAllocColor,(Display *a, Colormap b, XColor *c)) SDL_X11_SYM(XImage*,XCreateImage,(Display* a,Visual* b,unsigned int c,int d,int e,char* f,unsigned int g,unsigned int h,int i,int j)) SDL_X11_SYM(Window,XCreateWindow,(Display* a,Window b,int c,int d,unsigned int e,unsigned int f,unsigned int g,int h,unsigned int i,Visual* j,unsigned long k,XSetWindowAttributes* l)) +SDL_X11_SYM(void,XCopyArea,(Display *a, Drawable b, Drawable c, GC d, int e, int f, unsigned int g, unsigned int h, int i, int j)) SDL_X11_SYM(int,XDefineCursor,(Display* a,Window b,Cursor c)) SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c)) SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b)) @@ -240,9 +241,12 @@ SDL_X11_MODULE(SHM) SDL_X11_SYM(Status,XShmAttach,(Display* a,XShmSegmentInfo* b)) SDL_X11_SYM(Status,XShmDetach,(Display* a,XShmSegmentInfo* b)) SDL_X11_SYM(Status,XShmPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j,Bool k)) +SDL_X11_SYM(Status,XShmGetImage,(Display* a,Drawable b,XImage *c,int d, int e, unsigned long f)) SDL_X11_SYM(XImage*,XShmCreateImage,(Display* a,Visual* b,unsigned int c,int d,char* e,XShmSegmentInfo* f,unsigned int g,unsigned int h)) SDL_X11_SYM(Pixmap,XShmCreatePixmap,(Display *a,Drawable b,char* c,XShmSegmentInfo* d, unsigned int e, unsigned int f, unsigned int g)) SDL_X11_SYM(Bool,XShmQueryExtension,(Display* a)) +SDL_X11_SYM(Status,XShmQueryVersion,(Display* a, int *b, int *c, Bool *d)) +SDL_X11_SYM(int,XShmPixmapFormat,(Display* a)) #endif /* diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c index 2674f4cf98..138677e03b 100644 --- a/src/video/x11/SDL_x11toolkit.c +++ b/src/video/x11/SDL_x11toolkit.c @@ -131,6 +131,18 @@ static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { 235, 235, 235 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, }; +static int g_shm_error; +static int (*g_old_error_handler)(Display *, XErrorEvent *) = NULL; + +static int X11Toolkit_SharedMemoryErrorHandler(Display *d, XErrorEvent *e) +{ + if (e->error_code == BadAccess || e->error_code == BadRequest) { + g_shm_error = True; + return 0; + } + return g_old_error_handler(d, e); +} + int X11Toolkit_SettingsGetInt(XSettingsClient *client, const char *key, int fallback_value) { XSettingsSetting *setting = NULL; int res = fallback_value; @@ -297,7 +309,62 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, } X11_XFreePixmap(window->display, window->drawable); - window->drawable = X11_XCreatePixmap(window->display, window->window, window->pixmap_width, window->pixmap_height, window->depth); +#ifndef NO_SHARED_MEMORY + if (window->shm) { + X11_XShmDetach(window->display, &window->shm_info); + XDestroyImage(window->image); + shmdt(window->shm_info.shmaddr); + + window->image = X11_XShmCreateImage(window->display, window->visual, window->depth, ZPixmap, NULL, &window->shm_info, window->pixmap_width, window->pixmap_height); + if (window->image) { + window->shm_bytes_per_line = window->image->bytes_per_line; + window->shm_info.shmid = shmget(IPC_PRIVATE, window->image->bytes_per_line * window->image->height, IPC_CREAT | 0777); + if (window->shm_info.shmid < 0) { + XDestroyImage(window->image); + window->shm = false; + } + + window->shm_info.readOnly = False; + window->shm_info.shmaddr = window->image->data = (char *)shmat(window->shm_info.shmid, 0, 0); + if (((signed char *)window->shm_info.shmaddr) == (signed char *)-1) { + XDestroyImage(window->image); + window->shm = false; + } + + g_shm_error = False; + g_old_error_handler = X11_XSetErrorHandler(X11Toolkit_SharedMemoryErrorHandler); + X11_XShmAttach(window->display, &window->shm_info); + X11_XSync(window->display, False); + X11_XSetErrorHandler(g_old_error_handler); + if (g_shm_error) { + XDestroyImage(window->image); + shmdt(window->shm_info.shmaddr); + shmctl(window->shm_info.shmid, IPC_RMID, 0); + window->shm = false; + } + + if (window->shm_pixmap) { + window->drawable = X11_XShmCreatePixmap(window->display, window->window, window->shm_info.shmaddr, &window->shm_info, window->pixmap_width, window->pixmap_height, window->depth); + if (window->drawable == None) { + window->shm_pixmap = False; + } else { + XDestroyImage(window->image); + } + } + + shmctl(window->shm_info.shmid, IPC_RMID, 0); + } else { + window->shm = false; + } + } +#endif +#ifndef NO_SHARED_MEMORY + if (!window->shm_pixmap) { + window->drawable = X11_XCreatePixmap(window->display, window->window, window->pixmap_width, window->pixmap_height, window->depth); + } +#else + window->drawable = X11_XCreatePixmap(window->display, window->window, window->pixmap_width, window->pixmap_height, window->depth); +#endif } else { if (!dbe_already_setup) { X11_XFreePixmap(window->display, window->drawable); @@ -485,6 +552,22 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->xrandr = X11_XRRQueryExtension(window->display, &xrandr_event_base, &xrandr_error_base); #endif +#ifndef NO_SHARED_MEMORY + window->shm_pixmap = False; + window->shm = X11_XShmQueryExtension(window->display) ? SDL_X11_HAVE_SHM : false; + if (window->shm) { + int major; + int minor; + + X11_XShmQueryVersion(window->display, &major, &minor, &window->shm_pixmap); + if (window->shm_pixmap) { + if (X11_XShmPixmapFormat(window->display) != ZPixmap) { + window->shm_pixmap = False; + } + } + } +#endif + /* Scale/Xsettings */ window->pixmap = false; window->xsettings_first_time = true; @@ -906,7 +989,59 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx #endif if (data->pixmap) { - data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); +#ifndef NO_SHARED_MEMORY + if (!data->shm_pixmap) { + data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } +#else + data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); +#endif +#ifndef NO_SHARED_MEMORY + if (data->shm) { + data->image = X11_XShmCreateImage(display, data->visual, data->depth, ZPixmap, NULL, &data->shm_info, data->pixmap_width, data->pixmap_height); + if (data->image) { + data->shm_bytes_per_line = data->image->bytes_per_line; + + data->shm_info.shmid = shmget(IPC_PRIVATE, data->image->bytes_per_line * data->image->height, IPC_CREAT | 0777); + if (data->shm_info.shmid < 0) { + XDestroyImage(data->image); + data->shm = false; + } + + data->shm_info.readOnly = False; + data->shm_info.shmaddr = data->image->data = (char *)shmat(data->shm_info.shmid, 0, 0); + if (((signed char *)data->shm_info.shmaddr) == (signed char *)-1) { + XDestroyImage(data->image); + data->shm = false; + } + + g_shm_error = False; + g_old_error_handler = X11_XSetErrorHandler(X11Toolkit_SharedMemoryErrorHandler); + X11_XShmAttach(display, &data->shm_info); + X11_XSync(display, False); + X11_XSetErrorHandler(g_old_error_handler); + if (g_shm_error) { + XDestroyImage(data->image); + shmdt(data->shm_info.shmaddr); + shmctl(data->shm_info.shmid, IPC_RMID, 0); + data->shm = false; + } + + if (data->shm_pixmap) { + data->drawable = X11_XShmCreatePixmap(display, data->window, data->shm_info.shmaddr, &data->shm_info, data->pixmap_width, data->pixmap_height, data->depth); + if (data->drawable == None) { + data->shm_pixmap = False; + } else { + XDestroyImage(data->image); + } + } + + shmctl(data->shm_info.shmid, IPC_RMID, 0); + } else { + data->shm = false; + } + } +#endif } SDL_zero(ctx_vals); @@ -941,7 +1076,8 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx } static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { - int i; + SDL_Rect rect; + int i; #ifdef SDL_VIDEO_DRIVER_X11_XDBE if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { @@ -978,24 +1114,39 @@ static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { #endif if (data->pixmap) { - XImage *pixmap_image; - XImage *put_image; - SDL_Surface *pixmap_surface; - SDL_Surface *put_surface; + SDL_Surface *scale_surface; - /* FIXME: Implement SHM transport? */ - pixmap_image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); - pixmap_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), pixmap_image->data, pixmap_image->bytes_per_line); - put_surface = SDL_ScaleSurface(pixmap_surface, data->window_width, data->window_height, SDL_SCALEMODE_LINEAR); - put_image = X11_XCreateImage(data->display, data->visual, data->vi.depth, ZPixmap, 0, put_surface->pixels, data->window_width, data->window_height, 32, put_surface->pitch); - X11_XPutImage(data->display, data->window, data->ctx, put_image, 0, 0, 0, 0, data->window_width, data->window_height); + rect.x = rect.y = 0; + rect.w = data->window_width; + rect.h = data->window_height; +#ifndef NO_SHARED_MEMORY + if (data->shm) { + if (data->shm_pixmap) { + X11_XFlush(data->display); + X11_XSync(data->display, false); + scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), data->shm_info.shmaddr, data->shm_bytes_per_line); + SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); + SDL_DestroySurface(scale_surface); + X11_XCopyArea(data->display, data->drawable, data->window, data->ctx, 0, 0, data->window_width, data->window_height, 0, 0); + } else { + X11_XShmGetImage(data->display, data->drawable, data->image, 0, 0, AllPlanes); + scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), data->image->data, data->image->bytes_per_line); + SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); + X11_XShmPutImage(data->display, data->window, data->ctx, data->image, 0, 0, 0, 0, data->window_width, data->window_height, False); + } + } else +#endif + { + XImage *image; + + image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); + scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), image->data, image->bytes_per_line); + SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); + X11_XPutImage(data->display, data->window, data->ctx, image, 0, 0, 0, 0, data->window_width, data->window_height); - XDestroyImage(pixmap_image); - /* Needed because XDestroyImage results in a double-free otherwise */ - put_image->data = NULL; - XDestroyImage(put_image); - SDL_DestroySurface(pixmap_surface); - SDL_DestroySurface(put_surface); + XDestroyImage(image); + SDL_DestroySurface(scale_surface); + } } X11_XFlush(data->display); @@ -1248,7 +1399,62 @@ void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { data->pixmap_width = w; data->pixmap_height = h; X11_XFreePixmap(data->display, data->drawable); - data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); +#ifndef NO_SHARED_MEMORY + if (!data->shm_pixmap) { + data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } +#else + data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); +#endif +#ifndef NO_SHARED_MEMORY + if (data->shm) { + X11_XShmDetach(data->display, &data->shm_info); + XDestroyImage(data->image); + shmdt(data->shm_info.shmaddr); + + data->image = X11_XShmCreateImage(data->display, data->visual, data->depth, ZPixmap, NULL, &data->shm_info, data->pixmap_width, data->pixmap_height); + if (data->image) { + data->shm_bytes_per_line = data->image->bytes_per_line; + data->shm_info.shmid = shmget(IPC_PRIVATE, data->image->bytes_per_line * data->image->height, IPC_CREAT | 0777); + if (data->shm_info.shmid < 0) { + XDestroyImage(data->image); + data->shm = false; + } + + data->shm_info.readOnly = False; + data->shm_info.shmaddr = data->image->data = (char *)shmat(data->shm_info.shmid, 0, 0); + if (((signed char *)data->shm_info.shmaddr) == (signed char *)-1) { + XDestroyImage(data->image); + data->shm = false; + } + + g_shm_error = False; + g_old_error_handler = X11_XSetErrorHandler(X11Toolkit_SharedMemoryErrorHandler); + X11_XShmAttach(data->display, &data->shm_info); + X11_XSync(data->display, False); + X11_XSetErrorHandler(g_old_error_handler); + if (g_shm_error) { + XDestroyImage(data->image); + shmdt(data->shm_info.shmaddr); + shmctl(data->shm_info.shmid, IPC_RMID, 0); + data->shm = false; + } + + if (data->shm_pixmap) { + data->drawable = X11_XShmCreatePixmap(data->display, data->window, data->shm_info.shmaddr, &data->shm_info, data->pixmap_width, data->pixmap_height, data->depth); + if (data->drawable == None) { + data->shm_pixmap = False; + } else { + XDestroyImage(data->image); + } + } + + shmctl(data->shm_info.shmid, IPC_RMID, 0); + } else { + data->shm = false; + } + } +#endif } X11_XResizeWindow(data->display, data->window, data->window_width, data->window_height); @@ -1677,6 +1883,16 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { if (data->pixmap) { X11_XFreePixmap(data->display, data->drawable); } + +#ifndef NO_SHARED_MEMORY + if (data->pixmap && data->shm) { + X11_XShmDetach(data->display, &data->shm_info); + if (!data->shm_pixmap) { + XDestroyImage(data->image); + } + shmdt(data->shm_info.shmaddr); + } +#endif #ifdef X_HAVE_UTF8_STRING if (data->font_set) { diff --git a/src/video/x11/SDL_x11toolkit.h b/src/video/x11/SDL_x11toolkit.h index 29b1d9a23d..14f79ce753 100644 --- a/src/video/x11/SDL_x11toolkit.h +++ b/src/video/x11/SDL_x11toolkit.h @@ -68,6 +68,11 @@ typedef struct SDL_ToolkitWindowX11 /* Window */ Window window; Drawable drawable; +#ifndef NO_SHARED_MEMORY + XImage *image; + XShmSegmentInfo shm_info; + int shm_bytes_per_line; +#endif /* Visuals and drawing */ Visual *visual; @@ -84,6 +89,10 @@ typedef struct SDL_ToolkitWindowX11 #endif #ifdef SDL_VIDEO_DRIVER_X11_XRANDR bool xrandr; // Whether Xrandr is present or not +#endif +#ifndef NO_SHARED_MEMORY + bool shm; + Bool shm_pixmap; #endif bool utf8;