Browse Source

X11TK: Implement SHM transport for fractional scaling (#13919)

eafton 4 ngày trước cách đây
mục cha
commit
d3a6be9607
3 tập tin đã thay đổi với 251 bổ sung22 xóa
  1. 4 0
      src/video/x11/SDL_x11sym.h
  2. 238 22
      src/video/x11/SDL_x11toolkit.c
  3. 9 0
      src/video/x11/SDL_x11toolkit.h

+ 4 - 0
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
 
 /*

+ 238 - 22
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;
-
-        /* 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);
-
-        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);
+		SDL_Surface *scale_surface;
+
+		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(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) {

+ 9 - 0
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;