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