a4886169fd8df219138cb0aef456becbc5504324
[platform/upstream/SDL.git] / src / video / x11 / SDL_x11framebuffer.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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;
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
43 static SDL_bool have_mitshm(void)
44 {
45     /* Only use shared memory on local X servers */
46     if ( (SDL_strncmp(X11_XDisplayName(NULL), ":", 1) == 0) ||
47          (SDL_strncmp(X11_XDisplayName(NULL), "unix:", 5) == 0) ) {
48         return SDL_X11_HAVE_SHM;
49     }
50     return SDL_FALSE;
51 }
52
53 #endif /* !NO_SHARED_MEMORY */
54
55 int
56 X11_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format,
57                             void ** pixels, int *pitch)
58 {
59     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
60     Display *display = data->videodata->display;
61     XGCValues gcv;
62     XVisualInfo vinfo;
63
64     /* Free the old framebuffer surface */
65     X11_DestroyWindowFramebuffer(_this, window);
66
67     /* Create the graphics context for drawing */
68     gcv.graphics_exposures = False;
69     data->gc = X11_XCreateGC(display, data->xwindow, GCGraphicsExposures, &gcv);
70     if (!data->gc) {
71         return SDL_SetError("Couldn't create graphics context");
72     }
73
74     /* Find out the pixel format and depth */
75     if (X11_GetVisualInfoFromVisual(display, data->visual, &vinfo) < 0) {
76         return SDL_SetError("Couldn't get window visual information");
77     }
78
79     *format = X11_GetPixelFormatFromVisualInfo(display, &vinfo);
80     if (*format == SDL_PIXELFORMAT_UNKNOWN) {
81         return SDL_SetError("Unknown window pixel format");
82     }
83
84     /* Calculate pitch */
85     *pitch = (((window->w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
86
87     /* Create the actual image */
88 #ifndef NO_SHARED_MEMORY
89     if (have_mitshm()) {
90         XShmSegmentInfo *shminfo = &data->shminfo;
91
92         shminfo->shmid = shmget(IPC_PRIVATE, window->h*(*pitch), IPC_CREAT | 0777);
93         if ( shminfo->shmid >= 0 ) {
94             shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
95             shminfo->readOnly = False;
96             if ( shminfo->shmaddr != (char *)-1 ) {
97                 shm_error = False;
98                 X_handler = X11_XSetErrorHandler(shm_errhandler);
99                 X11_XShmAttach(display, shminfo);
100                 X11_XSync(display, True);
101                 X11_XSetErrorHandler(X_handler);
102                 if ( shm_error )
103                     shmdt(shminfo->shmaddr);
104             } else {
105                 shm_error = True;
106             }
107             shmctl(shminfo->shmid, IPC_RMID, NULL);
108         } else {
109             shm_error = True;
110         }
111         if (!shm_error) {
112             data->ximage = X11_XShmCreateImage(display, data->visual,
113                              vinfo.depth, ZPixmap,
114                              shminfo->shmaddr, shminfo,
115                              window->w, window->h);
116             if (!data->ximage) {
117                 X11_XShmDetach(display, shminfo);
118                 X11_XSync(display, False);
119                 shmdt(shminfo->shmaddr);
120             } else {
121                 /* Done! */
122                 data->use_mitshm = SDL_TRUE;
123                 *pixels = shminfo->shmaddr;
124                 return 0;
125             }
126         }
127     }
128 #endif /* not NO_SHARED_MEMORY */
129
130     *pixels = SDL_malloc(window->h*(*pitch));
131     if (*pixels == NULL) {
132         return SDL_OutOfMemory();
133     }
134
135     data->ximage = X11_XCreateImage(display, data->visual,
136                       vinfo.depth, ZPixmap, 0, (char *)(*pixels),
137                       window->w, window->h, 32, 0);
138     if (!data->ximage) {
139         SDL_free(*pixels);
140         return SDL_SetError("Couldn't create XImage");
141     }
142     return 0;
143 }
144
145 int
146 X11_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects,
147                             int numrects)
148 {
149     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
150     Display *display = data->videodata->display;
151     int i;
152     int x, y, w ,h;
153 #ifndef NO_SHARED_MEMORY
154     if (data->use_mitshm) {
155         for (i = 0; i < numrects; ++i) {
156             x = rects[i].x;
157             y = rects[i].y;
158             w = rects[i].w;
159             h = rects[i].h;
160
161             if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
162                 /* Clipped? */
163                 continue;
164             }
165             if (x < 0)
166             {
167                 x += w;
168                 w += rects[i].x;
169             }
170             if (y < 0)
171             {
172                 y += h;
173                 h += rects[i].y;
174             }
175             if (x + w > window->w)
176                 w = window->w - x;
177             if (y + h > window->h)
178                 h = window->h - y;
179
180             X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage,
181                 x, y, x, y, w, h, False);
182         }
183     }
184     else
185 #endif /* !NO_SHARED_MEMORY */
186     {
187         for (i = 0; i < numrects; ++i) {
188             x = rects[i].x;
189             y = rects[i].y;
190             w = rects[i].w;
191             h = rects[i].h;
192
193             if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
194                 /* Clipped? */
195                 continue;
196             }
197             if (x < 0)
198             {
199                 x += w;
200                 w += rects[i].x;
201             }
202             if (y < 0)
203             {
204                 y += h;
205                 h += rects[i].y;
206             }
207             if (x + w > window->w)
208                 w = window->w - x;
209             if (y + h > window->h)
210                 h = window->h - y;
211
212             X11_XPutImage(display, data->xwindow, data->gc, data->ximage,
213                 x, y, x, y, w, h);
214         }
215     }
216
217     X11_XSync(display, False);
218
219     return 0;
220 }
221
222 void
223 X11_DestroyWindowFramebuffer(_THIS, SDL_Window * window)
224 {
225     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
226     Display *display;
227
228     if (!data) {
229         /* The window wasn't fully initialized */
230         return;
231     }
232
233     display = data->videodata->display;
234
235     if (data->ximage) {
236         XDestroyImage(data->ximage);
237
238 #ifndef NO_SHARED_MEMORY
239         if (data->use_mitshm) {
240             X11_XShmDetach(display, &data->shminfo);
241             X11_XSync(display, False);
242             shmdt(data->shminfo.shmaddr);
243             data->use_mitshm = SDL_FALSE;
244         }
245 #endif /* !NO_SHARED_MEMORY */
246
247         data->ximage = NULL;
248     }
249     if (data->gc) {
250         X11_XFreeGC(display, data->gc);
251         data->gc = NULL;
252     }
253 }
254
255 #endif /* SDL_VIDEO_DRIVER_X11 */
256
257 /* vi: set ts=4 sw=4 expandtab: */