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 |
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 | } |
40 | return X_handler(d, e); |
41 | } |
42 | |
43 | static 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 | |
51 | bool 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 | |
146 | bool 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 | |
229 | void 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 | |