1 /*
2 * Copyright © 2014 Intel Corporation
3 * Copyright © 2012 Collabora, Ltd.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of the
10 * copyright holders not be used in advertising or publicity
11 * pertaining to distribution of the software without specific,
12 * written prior permission. The copyright holders make no
13 * representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied
15 * warranty.
16 *
17 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
22 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
23 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24 * SOFTWARE.
25 */
26
27 #ifdef HAVE_DIX_CONFIG_H
28 #include <dix-config.h>
29 #endif
30
31 #include "os.h"
32
33 #include "xwayland.h"
34
35 #include <sys/mman.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <stdlib.h>
42
43 struct xwl_pixmap {
44 struct wl_buffer *buffer;
45 void *data;
46 size_t size;
47 };
48
49 #ifndef HAVE_MKOSTEMP
50 static int
set_cloexec_or_close(int fd)51 set_cloexec_or_close(int fd)
52 {
53 long flags;
54
55 if (fd == -1)
56 return -1;
57
58 flags = fcntl(fd, F_GETFD);
59 if (flags == -1)
60 goto err;
61
62 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
63 goto err;
64
65 return fd;
66
67 err:
68 close(fd);
69 return -1;
70 }
71 #endif
72
73 static int
create_tmpfile_cloexec(char * tmpname)74 create_tmpfile_cloexec(char *tmpname)
75 {
76 int fd;
77
78 #ifdef HAVE_MKOSTEMP
79 fd = mkostemp(tmpname, O_CLOEXEC);
80 if (fd >= 0)
81 unlink(tmpname);
82 #else
83 fd = mkstemp(tmpname);
84 if (fd >= 0) {
85 fd = set_cloexec_or_close(fd);
86 unlink(tmpname);
87 }
88 #endif
89
90 return os_move_fd(fd);
91 }
92
93 /*
94 * Create a new, unique, anonymous file of the given size, and
95 * return the file descriptor for it. The file descriptor is set
96 * CLOEXEC. The file is immediately suitable for mmap()'ing
97 * the given size at offset zero.
98 *
99 * The file should not have a permanent backing store like a disk,
100 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
101 *
102 * The file name is deleted from the file system.
103 *
104 * The file is suitable for buffer sharing between processes by
105 * transmitting the file descriptor over Unix sockets using the
106 * SCM_RIGHTS methods.
107 *
108 * If the C library implements posix_fallocate(), it is used to
109 * guarantee that disk space is available for the file at the
110 * given size. If disk space is insufficent, errno is set to ENOSPC.
111 * If posix_fallocate() is not supported, program may receive
112 * SIGBUS on accessing mmap()'ed file contents instead.
113 */
114 static int
os_create_anonymous_file(off_t size)115 os_create_anonymous_file(off_t size)
116 {
117 static const char template[] = "/xwayland-shared-XXXXXX";
118 const char *path;
119 char *name;
120 int fd;
121 int ret;
122
123 path = getenv("XDG_RUNTIME_DIR");
124 if (!path) {
125 errno = ENOENT;
126 return -1;
127 }
128
129 name = malloc(strlen(path) + sizeof(template));
130 if (!name)
131 return -1;
132
133 strcpy(name, path);
134 strcat(name, template);
135
136 fd = create_tmpfile_cloexec(name);
137
138 free(name);
139
140 if (fd < 0)
141 return -1;
142
143 #ifdef HAVE_POSIX_FALLOCATE
144 /*
145 * posix_fallocate does an explicit rollback if it gets EINTR.
146 * Temporarily block signals to allow the call to succeed on
147 * slow systems where the smart scheduler's SIGALRM prevents
148 * large allocation attempts from ever succeeding.
149 */
150 OsBlockSignals();
151 do {
152 ret = posix_fallocate(fd, 0, size);
153 } while (ret == EINTR);
154 OsReleaseSignals();
155
156 if (ret != 0) {
157 close(fd);
158 errno = ret;
159 return -1;
160 }
161 #else
162 do {
163 ret = ftruncate(fd, size);
164 } while (ret == -1 && errno == EINTR);
165
166 if (ret < 0) {
167 close(fd);
168 return -1;
169 }
170 #endif
171
172 return fd;
173 }
174
175 static uint32_t
shm_format_for_depth(int depth)176 shm_format_for_depth(int depth)
177 {
178 switch (depth) {
179 case 32:
180 return WL_SHM_FORMAT_ARGB8888;
181 case 24:
182 default:
183 return WL_SHM_FORMAT_XRGB8888;
184 #ifdef WL_SHM_FORMAT_RGB565
185 case 16:
186 /* XXX: Check run-time protocol version too */
187 return WL_SHM_FORMAT_RGB565;
188 #endif
189 }
190 }
191
192 PixmapPtr
xwl_shm_create_pixmap(ScreenPtr screen,int width,int height,int depth,unsigned int hint)193 xwl_shm_create_pixmap(ScreenPtr screen,
194 int width, int height, int depth, unsigned int hint)
195 {
196 struct xwl_screen *xwl_screen = xwl_screen_get(screen);
197 struct xwl_pixmap *xwl_pixmap;
198 struct wl_shm_pool *pool;
199 PixmapPtr pixmap;
200 size_t size, stride;
201 uint32_t format;
202 int fd;
203
204 if (hint == CREATE_PIXMAP_USAGE_GLYPH_PICTURE ||
205 (width == 0 && height == 0) || depth < 15)
206 return fbCreatePixmap(screen, width, height, depth, hint);
207
208 pixmap = fbCreatePixmap(screen, 0, 0, depth, hint);
209 if (!pixmap)
210 return NULL;
211
212 xwl_pixmap = malloc(sizeof *xwl_pixmap);
213 if (xwl_pixmap == NULL)
214 goto err_destroy_pixmap;
215
216 stride = PixmapBytePad(width, depth);
217 size = stride * height;
218 xwl_pixmap->buffer = NULL;
219 xwl_pixmap->size = size;
220 fd = os_create_anonymous_file(size);
221 if (fd < 0)
222 goto err_free_xwl_pixmap;
223
224 xwl_pixmap->data = mmap(NULL, size, PROT_READ | PROT_WRITE,
225 MAP_SHARED, fd, 0);
226 if (xwl_pixmap->data == MAP_FAILED)
227 goto err_close_fd;
228
229 if (!(*screen->ModifyPixmapHeader) (pixmap, width, height, depth,
230 BitsPerPixel(depth),
231 stride, xwl_pixmap->data))
232 goto err_munmap;
233
234 format = shm_format_for_depth(pixmap->drawable.depth);
235 pool = wl_shm_create_pool(xwl_screen->shm, fd, xwl_pixmap->size);
236 xwl_pixmap->buffer = wl_shm_pool_create_buffer(pool, 0,
237 pixmap->drawable.width,
238 pixmap->drawable.height,
239 pixmap->devKind, format);
240 wl_shm_pool_destroy(pool);
241 close(fd);
242
243 xwl_pixmap_set_private(pixmap, xwl_pixmap);
244
245 return pixmap;
246
247 err_munmap:
248 munmap(xwl_pixmap->data, size);
249 err_close_fd:
250 close(fd);
251 err_free_xwl_pixmap:
252 free(xwl_pixmap);
253 err_destroy_pixmap:
254 fbDestroyPixmap(pixmap);
255
256 return NULL;
257 }
258
259 Bool
xwl_shm_destroy_pixmap(PixmapPtr pixmap)260 xwl_shm_destroy_pixmap(PixmapPtr pixmap)
261 {
262 struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
263
264 if (xwl_pixmap && pixmap->refcnt == 1) {
265 if (xwl_pixmap->buffer)
266 wl_buffer_destroy(xwl_pixmap->buffer);
267 munmap(xwl_pixmap->data, xwl_pixmap->size);
268 free(xwl_pixmap);
269 }
270
271 return fbDestroyPixmap(pixmap);
272 }
273
274 struct wl_buffer *
xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)275 xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)
276 {
277 return xwl_pixmap_get(pixmap)->buffer;
278 }
279
280 Bool
xwl_shm_create_screen_resources(ScreenPtr screen)281 xwl_shm_create_screen_resources(ScreenPtr screen)
282 {
283 struct xwl_screen *xwl_screen = xwl_screen_get(screen);
284 int ret;
285
286 screen->CreateScreenResources = xwl_screen->CreateScreenResources;
287 ret = (*screen->CreateScreenResources) (screen);
288 xwl_screen->CreateScreenResources = screen->CreateScreenResources;
289 screen->CreateScreenResources = xwl_shm_create_screen_resources;
290
291 if (!ret)
292 return ret;
293
294 if (xwl_screen->rootless)
295 screen->devPrivate =
296 fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
297 else
298 screen->devPrivate =
299 xwl_shm_create_pixmap(screen, screen->width, screen->height,
300 screen->rootDepth,
301 CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
302
303 SetRootClip(screen, xwl_screen->root_clip_mode);
304
305 return screen->devPrivate != NULL;
306 }
307