1a66cad3fb
Tested by bru@, jsg@ and others
307 lines
7.9 KiB
C
307 lines
7.9 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include "os.h"
|
|
|
|
#include "xwayland.h"
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.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 insufficent, errno is set to ENOSPC.
|
|
* If posix_fallocate() is not supported, program may receive
|
|
* SIGBUS on accessing mmap()'ed file contents instead.
|
|
*/
|
|
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;
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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 ||
|
|
(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 = malloc(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);
|
|
|
|
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) {
|
|
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;
|
|
}
|