wlroots/xcursor/wlr_cursor.c
2017-08-14 17:09:56 +02:00

329 lines
7.5 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.
*/
#define _XOPEN_SOURCE 500
#include <wlr/render.h>
#include <wlr/xcursor.h>
#include <wlr/util/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "xcursor/xcursor.h"
static void wlr_cursor_destroy(struct wlr_cursor *cursor) {
for (size_t i = 0; i < cursor->image_count; i++) {
free(cursor->images[i]->buffer);
free(cursor->images[i]);
}
free(cursor->images);
free(cursor->name);
free(cursor);
}
#include "xcursor/cursor_data.h"
static struct wlr_cursor *wlr_cursor_create_from_data(
struct cursor_metadata *metadata, struct wlr_cursor_theme *theme) {
struct wlr_cursor *cursor;
struct wlr_cursor_image *image;
int size;
cursor = malloc(sizeof(*cursor));
if (!cursor) {
return NULL;
}
cursor->image_count = 1;
cursor->images = malloc(sizeof(*cursor->images));
if (!cursor->images) {
goto err_free_cursor;
}
cursor->name = strdup(metadata->name);
cursor->total_delay = 0;
image = malloc(sizeof(*image));
if (!image) {
goto err_free_images;
}
cursor->images[0] = image;
image->buffer = NULL;
image->width = metadata->width;
image->height = metadata->height;
image->hotspot_x = metadata->hotspot_x;
image->hotspot_y = metadata->hotspot_y;
image->delay = 0;
size = metadata->width * metadata->height * sizeof(uint32_t);
image->buffer = malloc(size);
if (!image->buffer) {
goto err_free_image;
}
memcpy(image->buffer, cursor_data + metadata->offset, size);
return cursor;
err_free_image:
free(image);
err_free_images:
free(cursor->name);
free(cursor->images);
err_free_cursor:
free(cursor);
return NULL;
}
static void load_default_theme(struct wlr_cursor_theme *theme) {
uint32_t i;
free(theme->name);
theme->name = strdup("default");
theme->cursor_count = sizeof(cursor_metadata) / sizeof(cursor_metadata[0]);
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] =
wlr_cursor_create_from_data(&cursor_metadata[i], theme);
if (theme->cursors[i] == NULL) {
break;
}
}
theme->cursor_count = i;
}
static struct wlr_cursor *wlr_cursor_create_from_xcursor_images(
XcursorImages *images, struct wlr_cursor_theme *theme) {
struct wlr_cursor *cursor;
struct wlr_cursor_image *image;
int i, size;
cursor = malloc(sizeof(*cursor));
if (!cursor) {
return NULL;
}
cursor->images = malloc(images->nimage * sizeof(cursor->images[0]));
if (!cursor->images) {
free(cursor);
return NULL;
}
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->buffer = NULL;
image->width = images->images[i]->width;
image->height = images->images[i]->height;
image->hotspot_x = images->images[i]->xhot;
image->hotspot_y = images->images[i]->yhot;
image->delay = images->images[i]->delay;
size = image->width * image->height * 4;
image->buffer = malloc(size);
if (!image->buffer) {
free(image);
break;
}
/* copy pixels to shm pool */
memcpy(image->buffer, images->images[i]->pixels, size);
cursor->total_delay += image->delay;
cursor->images[i] = image;
}
cursor->image_count = i;
if (cursor->image_count == 0) {
free(cursor->name);
free(cursor->images);
free(cursor);
return NULL;
}
return cursor;
}
static void load_callback(XcursorImages *images, void *data) {
struct wlr_cursor_theme *theme = data;
struct wlr_cursor *cursor;
if (wlr_cursor_theme_get_cursor(theme, images->name)) {
XcursorImagesDestroy(images);
return;
}
cursor = wlr_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);
}
struct wlr_cursor_theme *wlr_cursor_theme_load(const char *name, int size) {
struct wlr_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;
xcursor_load_theme(name, size, load_callback, theme);
if (theme->cursor_count == 0) {
load_default_theme(theme);
}
wlr_log(L_DEBUG, "Loaded cursor theme '%s', available cursors:",
theme->name);
for (size_t i = 0; i < theme->cursor_count; ++i) {
struct wlr_cursor *c = theme->cursors[i];
struct wlr_cursor_image *i = c->images[0];
wlr_log(L_DEBUG, "%s (%u images) %dx%d+%d,%d",
c->name, c->image_count,
i->width, i->height, i->hotspot_x, i->hotspot_y);
}
return theme;
out_error_name:
free(theme);
return NULL;
}
void wlr_cursor_theme_destroy(struct wlr_cursor_theme *theme) {
unsigned int i;
for (i = 0; i < theme->cursor_count; i++) {
wlr_cursor_destroy(theme->cursors[i]);
}
free(theme->name);
free(theme->cursors);
free(theme);
}
struct wlr_cursor *wlr_cursor_theme_get_cursor(struct wlr_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;
}
static int wlr_cursor_frame_and_duration(struct wlr_cursor *cursor,
uint32_t time, uint32_t *duration) {
uint32_t t;
int i;
if (cursor->image_count == 1) {
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->images[i]->delay < t) {
t -= 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->images[i]->delay) {
*duration = 1;
} else {
*duration = cursor->images[i]->delay - t;
}
return i;
}
int wlr_cursor_frame(struct wlr_cursor *_cursor, uint32_t time) {
return wlr_cursor_frame_and_duration(_cursor, time, NULL);
}