abcf1048e2
If a cursor file contains multiple images for the same size, this typically indicates an animation. The compositor weston uses wl_cursor_frame_and_duration to figure out at which time a specific image should be shown. The total delay is the sum of all image delays. But if all images have a delay of 0, the total delay is 0 as well. The code does not check for this special condition and triggers a floating point exception by eventually performing a modulo operation with 0. This, of course, could also happen if the sum of all image delays triggers an unsigned int overflow. But since a comment in the code already indicates that it does not try to "fix" handling of weird files, I would argue that it's "okay" if that happens. At least the program won't crash. Proof of Concept: install -D ~/.icons/poc/cursors base64 -d > ~/.icons/poc/cursors/left_ptr << EOF WGN1chAAAAAAAAEAAgAAAAIA/f8BAAAAKAAAAAIA/f8BAAAAKAAAACQAAAACAP3/AQAAAAEAAAAB AAAAAQAAAAEAAAABAAAAAAAAAAAAAAA= EOF cat > /tmp/weston.ini << EOF [shell] cursor-theme=poc EOF weston -c /tmp/weston.ini Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
524 lines
12 KiB
C
524 lines
12 KiB
C
/*
|
|
* Copyright © 2012 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "xcursor.h"
|
|
#include "wayland-cursor.h"
|
|
#include "wayland-client.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include "os-compatibility.h"
|
|
|
|
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
|
|
|
|
struct shm_pool {
|
|
struct wl_shm_pool *pool;
|
|
int fd;
|
|
unsigned int size;
|
|
unsigned int used;
|
|
char *data;
|
|
};
|
|
|
|
static struct shm_pool *
|
|
shm_pool_create(struct wl_shm *shm, int size)
|
|
{
|
|
struct shm_pool *pool;
|
|
|
|
pool = malloc(sizeof *pool);
|
|
if (!pool)
|
|
return NULL;
|
|
|
|
pool->fd = os_create_anonymous_file(size);
|
|
if (pool->fd < 0)
|
|
goto err_free;
|
|
|
|
pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
pool->fd, 0);
|
|
|
|
if (pool->data == MAP_FAILED)
|
|
goto err_close;
|
|
|
|
pool->pool = wl_shm_create_pool(shm, pool->fd, size);
|
|
pool->size = size;
|
|
pool->used = 0;
|
|
|
|
return pool;
|
|
|
|
err_close:
|
|
close(pool->fd);
|
|
err_free:
|
|
free(pool);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
shm_pool_resize(struct shm_pool *pool, int size)
|
|
{
|
|
if (os_resize_anonymous_file(pool->fd, size) < 0)
|
|
return 0;
|
|
|
|
wl_shm_pool_resize(pool->pool, size);
|
|
|
|
munmap(pool->data, pool->size);
|
|
|
|
pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
pool->fd, 0);
|
|
if (pool->data == (void *)-1)
|
|
return 0;
|
|
pool->size = size;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
shm_pool_allocate(struct shm_pool *pool, int size)
|
|
{
|
|
int offset;
|
|
|
|
if (pool->used + size > pool->size)
|
|
if (!shm_pool_resize(pool, 2 * pool->size + size))
|
|
return -1;
|
|
|
|
offset = pool->used;
|
|
pool->used += size;
|
|
|
|
return offset;
|
|
}
|
|
|
|
static void
|
|
shm_pool_destroy(struct shm_pool *pool)
|
|
{
|
|
munmap(pool->data, pool->size);
|
|
wl_shm_pool_destroy(pool->pool);
|
|
close(pool->fd);
|
|
free(pool);
|
|
}
|
|
|
|
|
|
struct wl_cursor_theme {
|
|
unsigned int cursor_count;
|
|
struct wl_cursor **cursors;
|
|
struct wl_shm *shm;
|
|
struct shm_pool *pool;
|
|
char *name;
|
|
int size;
|
|
};
|
|
|
|
struct cursor_image {
|
|
struct wl_cursor_image image;
|
|
struct wl_cursor_theme *theme;
|
|
struct wl_buffer *buffer;
|
|
int offset; /* data offset of this image in the shm pool */
|
|
};
|
|
|
|
struct cursor {
|
|
struct wl_cursor cursor;
|
|
uint32_t total_delay; /* length of the animation in ms */
|
|
};
|
|
|
|
/** Get an shm buffer for a cursor image
|
|
*
|
|
* \param image The cursor image
|
|
* \return An shm buffer for the cursor image. The user should not destroy
|
|
* the returned buffer.
|
|
*/
|
|
WL_EXPORT struct wl_buffer *
|
|
wl_cursor_image_get_buffer(struct wl_cursor_image *_img)
|
|
{
|
|
struct cursor_image *image = (struct cursor_image *) _img;
|
|
struct wl_cursor_theme *theme = image->theme;
|
|
|
|
if (!image->buffer) {
|
|
image->buffer =
|
|
wl_shm_pool_create_buffer(theme->pool->pool,
|
|
image->offset,
|
|
_img->width, _img->height,
|
|
_img->width * 4,
|
|
WL_SHM_FORMAT_ARGB8888);
|
|
};
|
|
|
|
return image->buffer;
|
|
}
|
|
|
|
static void
|
|
wl_cursor_image_destroy(struct wl_cursor_image *_img)
|
|
{
|
|
struct cursor_image *image = (struct cursor_image *) _img;
|
|
|
|
if (image->buffer)
|
|
wl_buffer_destroy(image->buffer);
|
|
|
|
free(image);
|
|
}
|
|
|
|
static void
|
|
wl_cursor_destroy(struct wl_cursor *cursor)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < cursor->image_count; i++)
|
|
wl_cursor_image_destroy(cursor->images[i]);
|
|
|
|
free(cursor->images);
|
|
free(cursor->name);
|
|
free(cursor);
|
|
}
|
|
|
|
#include "cursor-data.h"
|
|
|
|
static struct wl_cursor *
|
|
wl_cursor_create_from_data(struct cursor_metadata *metadata,
|
|
struct wl_cursor_theme *theme)
|
|
{
|
|
struct cursor *cursor;
|
|
struct cursor_image *image;
|
|
int size;
|
|
|
|
cursor = malloc(sizeof *cursor);
|
|
if (!cursor)
|
|
return NULL;
|
|
|
|
cursor->cursor.image_count = 1;
|
|
cursor->cursor.images = malloc(sizeof *cursor->cursor.images);
|
|
if (!cursor->cursor.images)
|
|
goto err_free_cursor;
|
|
|
|
cursor->cursor.name = strdup(metadata->name);
|
|
cursor->total_delay = 0;
|
|
|
|
image = malloc(sizeof *image);
|
|
if (!image)
|
|
goto err_free_images;
|
|
|
|
cursor->cursor.images[0] = (struct wl_cursor_image *) image;
|
|
image->theme = theme;
|
|
image->buffer = NULL;
|
|
image->image.width = metadata->width;
|
|
image->image.height = metadata->height;
|
|
image->image.hotspot_x = metadata->hotspot_x;
|
|
image->image.hotspot_y = metadata->hotspot_y;
|
|
image->image.delay = 0;
|
|
|
|
size = metadata->width * metadata->height * sizeof(uint32_t);
|
|
image->offset = shm_pool_allocate(theme->pool, size);
|
|
|
|
if (image->offset < 0)
|
|
goto err_free_image;
|
|
|
|
memcpy(theme->pool->data + image->offset,
|
|
cursor_data + metadata->offset, size);
|
|
|
|
return &cursor->cursor;
|
|
|
|
err_free_image:
|
|
free(image);
|
|
|
|
err_free_images:
|
|
free(cursor->cursor.name);
|
|
free(cursor->cursor.images);
|
|
|
|
err_free_cursor:
|
|
free(cursor);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
load_default_theme(struct wl_cursor_theme *theme)
|
|
{
|
|
uint32_t i;
|
|
|
|
free(theme->name);
|
|
theme->name = strdup("default");
|
|
|
|
theme->cursor_count = ARRAY_LENGTH(cursor_metadata);
|
|
theme->cursors = malloc(theme->cursor_count * sizeof(*theme->cursors));
|
|
|
|
if (theme->cursors == NULL) {
|
|
theme->cursor_count = 0;
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < theme->cursor_count; ++i) {
|
|
theme->cursors[i] =
|
|
wl_cursor_create_from_data(&cursor_metadata[i], theme);
|
|
|
|
if (theme->cursors[i] == NULL)
|
|
break;
|
|
}
|
|
theme->cursor_count = i;
|
|
}
|
|
|
|
static struct wl_cursor *
|
|
wl_cursor_create_from_xcursor_images(XcursorImages *images,
|
|
struct wl_cursor_theme *theme)
|
|
{
|
|
struct cursor *cursor;
|
|
struct cursor_image *image;
|
|
int i, size;
|
|
|
|
cursor = malloc(sizeof *cursor);
|
|
if (!cursor)
|
|
return NULL;
|
|
|
|
cursor->cursor.images =
|
|
malloc(images->nimage * sizeof cursor->cursor.images[0]);
|
|
if (!cursor->cursor.images) {
|
|
free(cursor);
|
|
return NULL;
|
|
}
|
|
|
|
cursor->cursor.name = strdup(images->name);
|
|
cursor->total_delay = 0;
|
|
|
|
for (i = 0; i < images->nimage; i++) {
|
|
image = malloc(sizeof *image);
|
|
if (image == NULL)
|
|
break;
|
|
|
|
image->theme = theme;
|
|
image->buffer = NULL;
|
|
|
|
image->image.width = images->images[i]->width;
|
|
image->image.height = images->images[i]->height;
|
|
image->image.hotspot_x = images->images[i]->xhot;
|
|
image->image.hotspot_y = images->images[i]->yhot;
|
|
image->image.delay = images->images[i]->delay;
|
|
|
|
size = image->image.width * image->image.height * 4;
|
|
image->offset = shm_pool_allocate(theme->pool, size);
|
|
if (image->offset < 0) {
|
|
free(image);
|
|
break;
|
|
}
|
|
|
|
/* copy pixels to shm pool */
|
|
memcpy(theme->pool->data + image->offset,
|
|
images->images[i]->pixels, size);
|
|
cursor->total_delay += image->image.delay;
|
|
cursor->cursor.images[i] = (struct wl_cursor_image *) image;
|
|
}
|
|
cursor->cursor.image_count = i;
|
|
|
|
if (cursor->cursor.image_count == 0) {
|
|
free(cursor->cursor.name);
|
|
free(cursor->cursor.images);
|
|
free(cursor);
|
|
return NULL;
|
|
}
|
|
|
|
return &cursor->cursor;
|
|
}
|
|
|
|
static void
|
|
load_callback(XcursorImages *images, void *data)
|
|
{
|
|
struct wl_cursor_theme *theme = data;
|
|
struct wl_cursor *cursor;
|
|
|
|
if (wl_cursor_theme_get_cursor(theme, images->name)) {
|
|
XcursorImagesDestroy(images);
|
|
return;
|
|
}
|
|
|
|
cursor = wl_cursor_create_from_xcursor_images(images, theme);
|
|
|
|
if (cursor) {
|
|
theme->cursor_count++;
|
|
theme->cursors =
|
|
realloc(theme->cursors,
|
|
theme->cursor_count * sizeof theme->cursors[0]);
|
|
|
|
if (theme->cursors == NULL) {
|
|
theme->cursor_count--;
|
|
free(cursor);
|
|
} else {
|
|
theme->cursors[theme->cursor_count - 1] = cursor;
|
|
}
|
|
}
|
|
|
|
XcursorImagesDestroy(images);
|
|
}
|
|
|
|
/** Load a cursor theme to memory shared with the compositor
|
|
*
|
|
* \param name The name of the cursor theme to load. If %NULL, the default
|
|
* theme will be loaded.
|
|
* \param size Desired size of the cursor images.
|
|
* \param shm The compositor's shm interface.
|
|
*
|
|
* \return An object representing the theme that should be destroyed with
|
|
* wl_cursor_theme_destroy() or %NULL on error. If no theme with the given
|
|
* name exists, a default theme will be loaded.
|
|
*/
|
|
WL_EXPORT struct wl_cursor_theme *
|
|
wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
|
|
{
|
|
struct wl_cursor_theme *theme;
|
|
|
|
theme = malloc(sizeof *theme);
|
|
if (!theme)
|
|
return NULL;
|
|
|
|
if (!name)
|
|
name = "default";
|
|
|
|
theme->name = strdup(name);
|
|
if (!theme->name)
|
|
goto out_error_name;
|
|
theme->size = size;
|
|
theme->cursor_count = 0;
|
|
theme->cursors = NULL;
|
|
|
|
theme->pool = shm_pool_create(shm, size * size * 4);
|
|
if (!theme->pool)
|
|
goto out_error_pool;
|
|
|
|
xcursor_load_theme(name, size, load_callback, theme);
|
|
|
|
if (theme->cursor_count == 0)
|
|
load_default_theme(theme);
|
|
|
|
return theme;
|
|
|
|
out_error_pool:
|
|
free(theme->name);
|
|
out_error_name:
|
|
free(theme);
|
|
return NULL;
|
|
}
|
|
|
|
/** Destroys a cursor theme object
|
|
*
|
|
* \param theme The cursor theme to be destroyed
|
|
*/
|
|
WL_EXPORT void
|
|
wl_cursor_theme_destroy(struct wl_cursor_theme *theme)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < theme->cursor_count; i++)
|
|
wl_cursor_destroy(theme->cursors[i]);
|
|
|
|
shm_pool_destroy(theme->pool);
|
|
|
|
free(theme->name);
|
|
free(theme->cursors);
|
|
free(theme);
|
|
}
|
|
|
|
/** Get the cursor for a given name from a cursor theme
|
|
*
|
|
* \param theme The cursor theme
|
|
* \param name Name of the desired cursor
|
|
* \return The theme's cursor of the given name or %NULL if there is no
|
|
* such cursor
|
|
*/
|
|
WL_EXPORT struct wl_cursor *
|
|
wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme,
|
|
const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < theme->cursor_count; i++) {
|
|
if (strcmp(name, theme->cursors[i]->name) == 0)
|
|
return theme->cursors[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** Find the frame for a given elapsed time in a cursor animation
|
|
* as well as the time left until next cursor change.
|
|
*
|
|
* \param cursor The cursor
|
|
* \param time Elapsed time in ms since the beginning of the animation
|
|
* \param duration pointer to uint32_t to store time left for this image or
|
|
* zero if the cursor won't change.
|
|
*
|
|
* \return The index of the image that should be displayed for the
|
|
* given time in the cursor animation.
|
|
*/
|
|
WL_EXPORT int
|
|
wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time,
|
|
uint32_t *duration)
|
|
{
|
|
struct cursor *cursor = (struct cursor *) _cursor;
|
|
uint32_t t;
|
|
int i;
|
|
|
|
if (cursor->cursor.image_count == 1 || cursor->total_delay == 0) {
|
|
if (duration)
|
|
*duration = 0;
|
|
return 0;
|
|
}
|
|
|
|
i = 0;
|
|
t = time % cursor->total_delay;
|
|
|
|
/* If there is a 0 delay in the image set then this
|
|
* loop breaks on it and we display that cursor until
|
|
* time % cursor->total_delay wraps again.
|
|
* Since a 0 delay is silly, and we've never actually
|
|
* seen one in a cursor file, we haven't bothered to
|
|
* "fix" this.
|
|
*/
|
|
while (t - cursor->cursor.images[i]->delay < t)
|
|
t -= cursor->cursor.images[i++]->delay;
|
|
|
|
if (!duration)
|
|
return i;
|
|
|
|
/* Make sure we don't accidentally tell the caller this is
|
|
* a static cursor image.
|
|
*/
|
|
if (t >= cursor->cursor.images[i]->delay)
|
|
*duration = 1;
|
|
else
|
|
*duration = cursor->cursor.images[i]->delay - t;
|
|
|
|
return i;
|
|
}
|
|
|
|
/** Find the frame for a given elapsed time in a cursor animation
|
|
*
|
|
* \param cursor The cursor
|
|
* \param time Elapsed time in ms since the beginning of the animation
|
|
*
|
|
* \return The index of the image that should be displayed for the
|
|
* given time in the cursor animation.
|
|
*/
|
|
WL_EXPORT int
|
|
wl_cursor_frame(struct wl_cursor *_cursor, uint32_t time)
|
|
{
|
|
return wl_cursor_frame_and_duration(_cursor, time, NULL);
|
|
}
|