xserver-multidpi/hw/xwayland/xwayland-shm.c

340 lines
9.2 KiB
C

/*
* Copyright © 2014 Intel Corporation
* Copyright © 2012 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of the
* copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <xwayland-config.h>
#include "os.h"
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "fb.h"
#include "pixmapstr.h"
#include "xwayland-pixmap.h"
#include "xwayland-screen.h"
#include "xwayland-shm.h"
struct xwl_pixmap {
struct wl_buffer *buffer;
void *data;
size_t size;
};
#ifndef HAVE_MKOSTEMP
static int
set_cloexec_or_close(int fd)
{
long flags;
if (fd == -1)
return -1;
flags = fcntl(fd, F_GETFD);
if (flags == -1)
goto err;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
goto err;
return fd;
err:
close(fd);
return -1;
}
#endif
static int
create_tmpfile_cloexec(char *tmpname)
{
int fd;
#ifdef HAVE_MKOSTEMP
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
#else
fd = mkstemp(tmpname);
if (fd >= 0) {
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return os_move_fd(fd);
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* If the C library implements posix_fallocate(), it is used to
* guarantee that disk space is available for the file at the
* given size. If disk space is insufficient, errno is set to ENOSPC.
* If posix_fallocate() is not supported, program may receive
* SIGBUS on accessing mmap()'ed file contents instead.
*
* If the C library implements memfd_create(), it is used to create the
* file purely in memory, without any backing file name on the file
* system, and then sealing off the possibility of shrinking it. This
* can then be checked before accessing mmap()'ed file contents, to
* make sure SIGBUS can't happen. It also avoids requiring
* XDG_RUNTIME_DIR.
*/
static int
os_create_anonymous_file(off_t size)
{
static const char template[] = "/xwayland-shared-XXXXXX";
const char *path;
char *name;
int fd;
int ret;
#ifdef HAVE_MEMFD_CREATE
fd = memfd_create("xwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd >= 0) {
/* We can add this seal before calling posix_fallocate(), as
* the file is currently zero-sized anyway.
*
* There is also no need to check for the return value, we
* couldn't do anything with it anyway.
*/
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
} else
#endif
{
path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
name = malloc(strlen(path) + sizeof(template));
if (!name)
return -1;
strcpy(name, path);
strcat(name, template);
fd = create_tmpfile_cloexec(name);
free(name);
if (fd < 0)
return -1;
}
#ifdef HAVE_POSIX_FALLOCATE
/*
* posix_fallocate does an explicit rollback if it gets EINTR.
* Temporarily block signals to allow the call to succeed on
* slow systems where the smart scheduler's SIGALRM prevents
* large allocation attempts from ever succeeding.
*/
OsBlockSignals();
do {
ret = posix_fallocate(fd, 0, size);
} while (ret == EINTR);
OsReleaseSignals();
if (ret != 0) {
close(fd);
errno = ret;
return -1;
}
#else
do {
ret = ftruncate(fd, size);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
#endif
return fd;
}
static uint32_t
shm_format_for_depth(int depth)
{
switch (depth) {
case 32:
return WL_SHM_FORMAT_ARGB8888;
case 24:
default:
return WL_SHM_FORMAT_XRGB8888;
#ifdef WL_SHM_FORMAT_RGB565
case 16:
/* XXX: Check run-time protocol version too */
return WL_SHM_FORMAT_RGB565;
#endif
}
}
static const struct wl_buffer_listener xwl_shm_buffer_listener = {
xwl_pixmap_buffer_release_cb,
};
PixmapPtr
xwl_shm_create_pixmap(ScreenPtr screen,
int width, int height, int depth, unsigned int hint)
{
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
struct xwl_pixmap *xwl_pixmap;
struct wl_shm_pool *pool;
PixmapPtr pixmap;
size_t size, stride;
uint32_t format;
int fd;
if (hint == CREATE_PIXMAP_USAGE_GLYPH_PICTURE ||
(!xwl_screen->rootless && hint != CREATE_PIXMAP_USAGE_BACKING_PIXMAP) ||
(width == 0 && height == 0) || depth < 15)
return fbCreatePixmap(screen, width, height, depth, hint);
pixmap = fbCreatePixmap(screen, 0, 0, depth, hint);
if (!pixmap)
return NULL;
xwl_pixmap = calloc(1, sizeof(*xwl_pixmap));
if (xwl_pixmap == NULL)
goto err_destroy_pixmap;
stride = PixmapBytePad(width, depth);
size = stride * height;
xwl_pixmap->buffer = NULL;
xwl_pixmap->size = size;
fd = os_create_anonymous_file(size);
if (fd < 0)
goto err_free_xwl_pixmap;
xwl_pixmap->data = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (xwl_pixmap->data == MAP_FAILED)
goto err_close_fd;
if (!(*screen->ModifyPixmapHeader) (pixmap, width, height, depth,
BitsPerPixel(depth),
stride, xwl_pixmap->data))
goto err_munmap;
format = shm_format_for_depth(pixmap->drawable.depth);
pool = wl_shm_create_pool(xwl_screen->shm, fd, xwl_pixmap->size);
xwl_pixmap->buffer = wl_shm_pool_create_buffer(pool, 0,
pixmap->drawable.width,
pixmap->drawable.height,
pixmap->devKind, format);
wl_shm_pool_destroy(pool);
close(fd);
wl_buffer_add_listener(xwl_pixmap->buffer,
&xwl_shm_buffer_listener, pixmap);
xwl_pixmap_set_private(pixmap, xwl_pixmap);
return pixmap;
err_munmap:
munmap(xwl_pixmap->data, size);
err_close_fd:
close(fd);
err_free_xwl_pixmap:
free(xwl_pixmap);
err_destroy_pixmap:
fbDestroyPixmap(pixmap);
return NULL;
}
Bool
xwl_shm_destroy_pixmap(PixmapPtr pixmap)
{
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
if (xwl_pixmap && pixmap->refcnt == 1) {
xwl_pixmap_del_buffer_release_cb(pixmap);
if (xwl_pixmap->buffer)
wl_buffer_destroy(xwl_pixmap->buffer);
munmap(xwl_pixmap->data, xwl_pixmap->size);
free(xwl_pixmap);
}
return fbDestroyPixmap(pixmap);
}
struct wl_buffer *
xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)
{
return xwl_pixmap_get(pixmap)->buffer;
}
Bool
xwl_shm_create_screen_resources(ScreenPtr screen)
{
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
int ret;
screen->CreateScreenResources = xwl_screen->CreateScreenResources;
ret = (*screen->CreateScreenResources) (screen);
xwl_screen->CreateScreenResources = screen->CreateScreenResources;
screen->CreateScreenResources = xwl_shm_create_screen_resources;
if (!ret)
return ret;
if (xwl_screen->rootless)
screen->devPrivate =
fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
else
screen->devPrivate =
xwl_shm_create_pixmap(screen, screen->width, screen->height,
screen->rootDepth,
CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
SetRootClip(screen, xwl_screen->root_clip_mode);
return screen->devPrivate != NULL;
}