1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_X11
24 
25 #include "SDL_x11video.h"
26 #include "SDL_x11framebuffer.h"
27 
28 
29 #ifndef NO_SHARED_MEMORY
30 
31 /* Shared memory error handler routine */
32 static int shm_error;
33 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
shm_errhandler(Display * d,XErrorEvent * e)34 static int shm_errhandler(Display *d, XErrorEvent *e)
35 {
36         if ( e->error_code == BadAccess ) {
37             shm_error = True;
38             return(0);
39         } else
40         return(X_handler(d,e));
41 }
42 
have_mitshm(Display * dpy)43 static SDL_bool have_mitshm(Display *dpy)
44 {
45     /* Only use shared memory on local X servers */
46     return X11_XShmQueryExtension(dpy) ? SDL_X11_HAVE_SHM : SDL_FALSE;
47 }
48 
49 #endif /* !NO_SHARED_MEMORY */
50 
51 int
X11_CreateWindowFramebuffer(_THIS,SDL_Window * window,Uint32 * format,void ** pixels,int * pitch)52 X11_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format,
53                             void ** pixels, int *pitch)
54 {
55     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
56     Display *display = data->videodata->display;
57     XGCValues gcv;
58     XVisualInfo vinfo;
59 
60     /* Free the old framebuffer surface */
61     X11_DestroyWindowFramebuffer(_this, window);
62 
63     /* Create the graphics context for drawing */
64     gcv.graphics_exposures = False;
65     data->gc = X11_XCreateGC(display, data->xwindow, GCGraphicsExposures, &gcv);
66     if (!data->gc) {
67         return SDL_SetError("Couldn't create graphics context");
68     }
69 
70     /* Find out the pixel format and depth */
71     if (X11_GetVisualInfoFromVisual(display, data->visual, &vinfo) < 0) {
72         return SDL_SetError("Couldn't get window visual information");
73     }
74 
75     *format = X11_GetPixelFormatFromVisualInfo(display, &vinfo);
76     if (*format == SDL_PIXELFORMAT_UNKNOWN) {
77         return SDL_SetError("Unknown window pixel format");
78     }
79 
80     /* Calculate pitch */
81     *pitch = (((window->w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
82 
83     /* Create the actual image */
84 #ifndef NO_SHARED_MEMORY
85     if (have_mitshm(display)) {
86         XShmSegmentInfo *shminfo = &data->shminfo;
87 
88         shminfo->shmid = shmget(IPC_PRIVATE, window->h*(*pitch), IPC_CREAT | 0777);
89         if ( shminfo->shmid >= 0 ) {
90             shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
91             shminfo->readOnly = False;
92             if ( shminfo->shmaddr != (char *)-1 ) {
93                 shm_error = False;
94                 X_handler = X11_XSetErrorHandler(shm_errhandler);
95                 X11_XShmAttach(display, shminfo);
96                 X11_XSync(display, False);
97                 X11_XSetErrorHandler(X_handler);
98                 if ( shm_error )
99                     shmdt(shminfo->shmaddr);
100             } else {
101                 shm_error = True;
102             }
103             shmctl(shminfo->shmid, IPC_RMID, NULL);
104         } else {
105             shm_error = True;
106         }
107         if (!shm_error) {
108             data->ximage = X11_XShmCreateImage(display, data->visual,
109                              vinfo.depth, ZPixmap,
110                              shminfo->shmaddr, shminfo,
111                              window->w, window->h);
112             if (!data->ximage) {
113                 X11_XShmDetach(display, shminfo);
114                 X11_XSync(display, False);
115                 shmdt(shminfo->shmaddr);
116             } else {
117                 /* Done! */
118                 data->use_mitshm = SDL_TRUE;
119                 *pixels = shminfo->shmaddr;
120                 return 0;
121             }
122         }
123     }
124 #endif /* not NO_SHARED_MEMORY */
125 
126     *pixels = SDL_malloc(window->h*(*pitch));
127     if (*pixels == NULL) {
128         return SDL_OutOfMemory();
129     }
130 
131     data->ximage = X11_XCreateImage(display, data->visual,
132                       vinfo.depth, ZPixmap, 0, (char *)(*pixels),
133                       window->w, window->h, 32, 0);
134     if (!data->ximage) {
135         SDL_free(*pixels);
136         return SDL_SetError("Couldn't create XImage");
137     }
138     return 0;
139 }
140 
141 int
X11_UpdateWindowFramebuffer(_THIS,SDL_Window * window,const SDL_Rect * rects,int numrects)142 X11_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects,
143                             int numrects)
144 {
145     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
146     Display *display = data->videodata->display;
147     int i;
148     int x, y, w ,h;
149 #ifndef NO_SHARED_MEMORY
150     if (data->use_mitshm) {
151         for (i = 0; i < numrects; ++i) {
152             x = rects[i].x;
153             y = rects[i].y;
154             w = rects[i].w;
155             h = rects[i].h;
156 
157             if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
158                 /* Clipped? */
159                 continue;
160             }
161             if (x < 0)
162             {
163                 x += w;
164                 w += rects[i].x;
165             }
166             if (y < 0)
167             {
168                 y += h;
169                 h += rects[i].y;
170             }
171             if (x + w > window->w)
172                 w = window->w - x;
173             if (y + h > window->h)
174                 h = window->h - y;
175 
176             X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage,
177                 x, y, x, y, w, h, False);
178         }
179     }
180     else
181 #endif /* !NO_SHARED_MEMORY */
182     {
183         for (i = 0; i < numrects; ++i) {
184             x = rects[i].x;
185             y = rects[i].y;
186             w = rects[i].w;
187             h = rects[i].h;
188 
189             if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
190                 /* Clipped? */
191                 continue;
192             }
193             if (x < 0)
194             {
195                 x += w;
196                 w += rects[i].x;
197             }
198             if (y < 0)
199             {
200                 y += h;
201                 h += rects[i].y;
202             }
203             if (x + w > window->w)
204                 w = window->w - x;
205             if (y + h > window->h)
206                 h = window->h - y;
207 
208             X11_XPutImage(display, data->xwindow, data->gc, data->ximage,
209                 x, y, x, y, w, h);
210         }
211     }
212 
213     X11_XSync(display, False);
214 
215     return 0;
216 }
217 
218 void
X11_DestroyWindowFramebuffer(_THIS,SDL_Window * window)219 X11_DestroyWindowFramebuffer(_THIS, SDL_Window * window)
220 {
221     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
222     Display *display;
223 
224     if (!data) {
225         /* The window wasn't fully initialized */
226         return;
227     }
228 
229     display = data->videodata->display;
230 
231     if (data->ximage) {
232         XDestroyImage(data->ximage);
233 
234 #ifndef NO_SHARED_MEMORY
235         if (data->use_mitshm) {
236             X11_XShmDetach(display, &data->shminfo);
237             X11_XSync(display, False);
238             shmdt(data->shminfo.shmaddr);
239             data->use_mitshm = SDL_FALSE;
240         }
241 #endif /* !NO_SHARED_MEMORY */
242 
243         data->ximage = NULL;
244     }
245     if (data->gc) {
246         X11_XFreeGC(display, data->gc);
247         data->gc = NULL;
248     }
249 }
250 
251 #endif /* SDL_VIDEO_DRIVER_X11 */
252 
253 /* vi: set ts=4 sw=4 expandtab: */
254