aboutsummaryrefslogtreecommitdiff
path: root/depends/libxcb-cursor/xcb
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2023-12-13 20:17:13 +0100
committerdec05eba <dec05eba@protonmail.com>2023-12-13 20:36:41 +0100
commitbc5a4cf5fcc70e30028ff8fa91dca075896b2764 (patch)
tree24b7fe9cc18a8deb3b30ee6f1265152ba8ad628f /depends/libxcb-cursor/xcb
parent4d639d934ec56b53e8f4776a7a0794f05b5451b6 (diff)
Include all libxcb utils (wm, util, image, errors, cursor)
Diffstat (limited to 'depends/libxcb-cursor/xcb')
-rw-r--r--depends/libxcb-cursor/xcb/cursor.c179
-rw-r--r--depends/libxcb-cursor/xcb/cursor.h169
-rw-r--r--depends/libxcb-cursor/xcb/load_cursor.c290
-rw-r--r--depends/libxcb-cursor/xcb/parse_cursor_file.c198
-rw-r--r--depends/libxcb-cursor/xcb/xcb_cursor.h124
5 files changed, 960 insertions, 0 deletions
diff --git a/depends/libxcb-cursor/xcb/cursor.c b/depends/libxcb-cursor/xcb/cursor.c
new file mode 100644
index 0000000..2f56f19
--- /dev/null
+++ b/depends/libxcb-cursor/xcb/cursor.c
@@ -0,0 +1,179 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * Copyright © 2013 Michael Stapelberg
+ *
+ * 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 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 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.
+ *
+ * Except as contained in this notice, the names of the authors or their
+ * institutions shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization from the authors.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_renderutil.h>
+
+#include "cursor.h"
+#include "xcb_cursor.h"
+
+/*
+ * Parses the root window’s RESOURCE_MANAGER atom contents and stores the
+ * attributes declared above in resource_manager_val.
+ *
+ */
+static void parse_resource_manager(xcb_cursor_context_t *c, const xcb_get_property_reply_t *rm_reply) {
+ int rm_length;
+ char *rm = NULL;
+ char *saveptr = NULL;
+ char *line = NULL;
+ char *sep = NULL;
+
+ if (rm_reply == NULL || (rm_length = xcb_get_property_value_length(rm_reply)) == 0)
+ return;
+
+ if (asprintf(&rm, "%.*s", rm_length, (char*)xcb_get_property_value(rm_reply)) == -1)
+ return;
+
+ for (char *str = rm; ; str = NULL) {
+ if ((line = strtok_r(str, "\n", &saveptr)) == NULL)
+ break;
+ /* Split the string at the delimiting : */
+ if ((sep = strchr(line, ':')) == NULL) {
+ /* Invalid line?! */
+ free(rm);
+ return;
+ }
+ *(sep++) = '\0';
+ while (isspace(*sep))
+ sep++;
+ /* strdup() may return NULL, which is interpreted later as the key not
+ * being available. */
+ if (strcmp(line, "Xcursor.theme") == 0) {
+ free(c->rm[RM_XCURSOR_THEME]);
+ c->rm[RM_XCURSOR_THEME] = strdup(sep);
+ } else if (strcmp(line, "Xcursor.size") == 0) {
+ free(c->rm[RM_XCURSOR_SIZE]);
+ c->rm[RM_XCURSOR_SIZE] = strdup(sep);
+ } else if (strcmp(line, "Xft.dpi") == 0) {
+ free(c->rm[RM_XFT_DPI]);
+ c->rm[RM_XFT_DPI] = strdup(sep);
+ }
+ }
+
+ free(rm);
+}
+
+/*
+ * Tries to figure out the cursor size by checking:
+ * 1. The environment variable XCURSOR_SIZE
+ * 2. The RESOURCE_MANAGER entry Xcursor.size
+ * 3. Guess with the RESOURCE_MANAGER entry Xft.dpi * 16 / 72
+ * 4. Guess with the display size.
+ *
+ */
+static uint32_t get_default_size(xcb_cursor_context_t *c, xcb_screen_t *screen) {
+ char *env;
+ uint16_t dim;
+
+ if ((env = getenv("XCURSOR_SIZE")) != NULL)
+ return atoi(env);
+
+ if (c->rm[RM_XCURSOR_SIZE] != NULL)
+ return atoi(c->rm[RM_XCURSOR_SIZE]);
+
+ if (c->rm[RM_XFT_DPI] != NULL) {
+ const int dpi = atoi(c->rm[RM_XFT_DPI]);
+ if (dpi > 0)
+ return dpi * 16 / 72;
+ }
+
+ if (screen->height_in_pixels < screen->width_in_pixels)
+ dim = screen->height_in_pixels;
+ else
+ dim = screen->width_in_pixels;
+
+ return dim / 48;
+}
+
+int xcb_cursor_context_new(xcb_connection_t *conn, xcb_screen_t *screen, xcb_cursor_context_t **ctx) {
+ xcb_cursor_context_t *c;
+ const xcb_query_extension_reply_t *ext;
+ xcb_get_property_cookie_t rm_cookie;
+ xcb_get_property_reply_t *rm_reply;
+ xcb_render_query_pict_formats_cookie_t pf_cookie;
+ xcb_render_query_version_cookie_t ver_cookie;
+
+ if ((*ctx = calloc(1, sizeof(struct xcb_cursor_context_t))) == NULL)
+ return -errno;
+
+ c = *ctx;
+ c->conn = conn;
+ c->root = screen->root;
+ c->render_version = RV_NONE;
+
+ ext = xcb_get_extension_data(conn, &xcb_render_id);
+
+ // XXX: Is it maybe necessary to ever use long_offset != 0?
+ // XXX: proper length? xlib seems to use 100 MB o_O
+ rm_cookie = xcb_get_property(conn, 0, c->root, XCB_ATOM_RESOURCE_MANAGER, XCB_ATOM_STRING, 0, 16 * 1024);
+ if (ext && ext->present) {
+ ver_cookie = xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
+ pf_cookie = xcb_render_query_pict_formats(conn);
+ }
+ c->cursor_font = xcb_generate_id(conn);
+ xcb_open_font(conn, c->cursor_font, strlen("cursor"), "cursor");
+
+ rm_reply = xcb_get_property_reply(conn, rm_cookie, NULL);
+ parse_resource_manager(c, rm_reply);
+ free(rm_reply);
+
+ if (ext && ext->present) {
+ xcb_render_query_version_reply_t *reply = xcb_render_query_version_reply(conn, ver_cookie, NULL);
+
+ if (reply && (reply->major_version >= 1 || reply->minor_version >= 8))
+ c->render_version = RV_ANIM_CURSOR;
+ else if (reply && (reply->major_version >= 1 || reply->minor_version >= 5))
+ c->render_version = RV_CURSOR;
+ free(reply);
+
+ c->pf_reply = xcb_render_query_pict_formats_reply(conn, pf_cookie, NULL);
+ c->pict_format = xcb_render_util_find_standard_format(c->pf_reply, XCB_PICT_STANDARD_ARGB_32);
+ }
+
+ c->size = get_default_size(c, screen);
+
+ return 0;
+}
+
+void xcb_cursor_context_free(xcb_cursor_context_t *c) {
+ free(c->rm[RM_XCURSOR_THEME]);
+ free(c->rm[RM_XCURSOR_SIZE]);
+ free(c->rm[RM_XFT_DPI]);
+ free(c->pf_reply);
+ free(c);
+}
diff --git a/depends/libxcb-cursor/xcb/cursor.h b/depends/libxcb-cursor/xcb/cursor.h
new file mode 100644
index 0000000..455dc34
--- /dev/null
+++ b/depends/libxcb-cursor/xcb/cursor.h
@@ -0,0 +1,169 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * Copyright © 2013 Michael Stapelberg
+ *
+ * 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 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 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.
+ *
+ * Except as contained in this notice, the names of the authors or their
+ * institutions shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization from the authors.
+ *
+ */
+#ifndef CURSOR_H
+#define CURSOR_H
+
+#include <xcb/render.h>
+
+#include "xcb_cursor.h"
+
+enum {
+ RM_XCURSOR_THEME = 0,
+ RM_XCURSOR_SIZE,
+ RM_XFT_DPI,
+ RM_MAX,
+};
+
+enum render_version {
+ RV_NONE = 0,
+ /* RENDER's CreateCursor was added in RENDER 0.5 */
+ RV_CURSOR,
+ /* RENDER's CreateAnimCursor was added in RENDER 0.8 */
+ RV_ANIM_CURSOR
+};
+
+struct xcb_cursor_context_t {
+ xcb_connection_t *conn;
+ xcb_window_t root;
+
+ xcb_font_t cursor_font;
+
+ xcb_render_query_pict_formats_reply_t *pf_reply;
+
+ /* This is a pointer into pf_reply. */
+ xcb_render_pictforminfo_t *pict_format;
+
+ /* Specific values of the root window’s RESOURCE_MANAGER atom contents. */
+ char *rm[RM_MAX];
+
+ /* Best cursor size. If a file contains multiple cursor images, the images
+ * which match the size best will be loaded. */
+ uint32_t size;
+
+ const char *home;
+ const char *path;
+
+ enum render_version render_version;
+};
+
+/*
+ * Cursor files start with a header. The header
+ * contains a magic number, a version number and a
+ * table of contents which has type and offset information
+ * for the remaining tables in the file.
+ *
+ * File minor versions increment for compatible changes
+ * File major versions increment for incompatible changes (never, we hope)
+ *
+ * Chunks of the same type are always upward compatible. Incompatible
+ * changes are made with new chunk types; the old data can remain under
+ * the old type. Upward compatible changes can add header data as the
+ * header lengths are specified in the file.
+ *
+ * File:
+ * FileHeader
+ * LISTofChunk
+ *
+ * FileHeader:
+ * CARD32 magic magic number
+ * CARD32 header bytes in file header
+ * CARD32 version file version
+ * CARD32 ntoc number of toc entries
+ * LISTofFileToc toc table of contents
+ *
+ * FileToc:
+ * CARD32 type entry type
+ * CARD32 subtype entry subtype (size for images)
+ * CARD32 position absolute file position
+ */
+
+/* little-endian */
+#define XCURSOR_MAGIC 0x72756358
+
+typedef struct xcint_file_header_t {
+ uint32_t magic;
+ uint32_t header;
+ uint32_t version;
+ uint32_t ntoc;
+} __attribute__((packed)) xcint_file_header_t;
+
+typedef struct xcint_file_toc_t {
+ uint32_t type;
+ uint32_t subtype;
+ uint32_t position;
+} __attribute__((packed)) xcint_file_toc_t;
+
+typedef struct xcint_cursor_file_t {
+ xcint_file_header_t header;
+ xcint_file_toc_t *tocs;
+} xcint_cursor_file_t;
+
+/*
+ * The rest of the file is a list of chunks, each tagged by type
+ * and version.
+ *
+ * Chunk:
+ * ChunkHeader
+ * <extra type-specific header fields>
+ * <type-specific data>
+ *
+ * ChunkHeader:
+ * CARD32 header bytes in chunk header + type header
+ * CARD32 type chunk type
+ * CARD32 subtype chunk subtype
+ * CARD32 version chunk type version
+ */
+
+typedef struct xcint_chunk_header_t {
+ uint32_t header;
+ uint32_t type;
+ uint32_t subtype;
+ uint32_t version;
+} __attribute__((packed)) xcint_chunk_header_t;
+
+#define XCURSOR_IMAGE_TYPE 0xfffd0002
+#define XCURSOR_IMAGE_VERSION 1
+#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
+
+typedef struct xcint_image_t {
+ uint32_t width;
+ uint32_t height;
+ uint32_t xhot;
+ uint32_t yhot;
+ uint32_t delay;
+ uint32_t *pixels;
+} __attribute__((packed)) xcint_image_t;
+
+/* shape_to_id.c */
+const int cursor_shape_to_id(const char *name);
+
+/* parse_cursor_file.c */
+int parse_cursor_file(xcb_cursor_context_t *c, const int fd, xcint_image_t **images, int *nimg);
+
+#endif
diff --git a/depends/libxcb-cursor/xcb/load_cursor.c b/depends/libxcb-cursor/xcb/load_cursor.c
new file mode 100644
index 0000000..0e96f8a
--- /dev/null
+++ b/depends/libxcb-cursor/xcb/load_cursor.c
@@ -0,0 +1,290 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * Copyright © 2013 Michael Stapelberg
+ * Copyright © 2002 Keith Packard
+ *
+ * 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 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 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.
+ *
+ * Except as contained in this notice, the names of the authors or their
+ * institutions shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization from the authors.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/xcb_image.h>
+
+#include "cursor.h"
+#include "xcb_cursor.h"
+
+#ifdef O_CLOEXEC
+#define FOPEN_CLOEXEC "e"
+#else
+#define FOPEN_CLOEXEC ""
+#define O_CLOEXEC 0
+#endif
+
+#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"
+
+static const char *cursor_path(struct xcb_cursor_context_t *c) {
+ if (c->path == NULL) {
+ c->path = getenv("XCURSOR_PATH");
+ if (c->path == NULL)
+ c->path = XCURSORPATH;
+ }
+ return c->path;
+}
+
+static const char *next_path(const char *path) {
+ const char *colon = strchr(path, ':');
+ return (colon ? colon + 1 : NULL);
+}
+
+/*
+ * _XcursorThemeInherits was directly copied from libxcursor so as to not break
+ * compatibility.
+ *
+ */
+#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define XcursorSep(c) ((c) == ';' || (c) == ',')
+
+static char *
+_XcursorThemeInherits (const char *full)
+{
+ char line[8192];
+ char *result = NULL;
+ FILE *f;
+
+ if (!full)
+ return NULL;
+
+ f = fopen (full, "r" FOPEN_CLOEXEC);
+ if (f)
+ {
+ while (fgets (line, sizeof (line), f))
+ {
+ if (!strncmp (line, "Inherits", 8))
+ {
+ char *l = line + 8;
+ while (*l == ' ') l++;
+ if (*l != '=') continue;
+ l++;
+ while (*l == ' ') l++;
+ result = malloc (strlen (l) + 1);
+ if (result)
+ {
+ char *r = result;
+ while (*l)
+ {
+ while (XcursorSep(*l) || XcursorWhite (*l)) l++;
+ if (!*l)
+ break;
+ if (r != result)
+ *r++ = ':';
+ while (*l && !XcursorWhite(*l) &&
+ !XcursorSep(*l))
+ *r++ = *l++;
+ }
+ *r++ = '\0';
+ }
+ break;
+ }
+ }
+ fclose (f);
+ }
+ return result;
+}
+
+/*
+ * Tries to open the cursor file “name” in the “theme”/cursors subfolder of
+ * each component of cursor_path(). When the file cannot be found, but a file
+ * “index.theme” in the component is present, the Inherits= key will be
+ * extracted and open_cursor_file calls itself recursively to search the
+ * specified inherited themes, too.
+ *
+ */
+static int open_cursor_file(xcb_cursor_context_t *c, const char *theme, const char *name, int *scan_core) {
+ int fd = -1;
+ char *inherits = NULL;
+
+ *scan_core = -1;
+
+ if (strcmp(theme, "core") == 0 &&
+ (*scan_core = cursor_shape_to_id(name)) >= 0) {
+ return -1;
+ }
+
+ if (c->home == NULL)
+ if ((c->home = getenv("HOME")) == NULL)
+ return -1;
+
+ for (const char *path = cursor_path(c);
+ (path != NULL && fd == -1);
+ ) {
+ const char *sep = strchr(path, ':');
+ const int pathlen = (sep ? (sep - path) : strlen(path));
+ char *themedir = NULL;
+ char *full = NULL;
+ if (*path == '~') {
+ if (asprintf(&themedir, "%s%.*s/%s", c->home, pathlen - 1, path + 1, theme) == -1)
+ return -1;
+ } else {
+ if (asprintf(&themedir, "%.*s/%s", pathlen, path, theme) == -1)
+ return -1;
+ }
+ if (asprintf(&full, "%s/%s/%s", themedir, "cursors", name) == -1) {
+ free(themedir);
+ return -1;
+ }
+ fd = open(full, O_RDONLY | O_CLOEXEC);
+ free(full);
+ if (fd == -1 && inherits == NULL) {
+ if (asprintf(&full, "%s/index.theme", themedir) == -1) {
+ free(themedir);
+ return -1;
+ }
+ inherits = _XcursorThemeInherits(full);
+ free(full);
+ }
+ free(themedir);
+ path = (sep ? sep + 1 : NULL);
+ }
+
+ for (const char *path = inherits;
+ (path != NULL && fd == -1);
+ (path = next_path(path))) {
+ fd = open_cursor_file(c, path, name, scan_core);
+ }
+
+ if (inherits != NULL)
+ free(inherits);
+
+ return fd;
+}
+
+xcb_cursor_t xcb_cursor_load_cursor(xcb_cursor_context_t *c, const char *name) {
+ /* The character id of the X11 "cursor" font when falling back to un-themed
+ * cursors. */
+ int core_char = -1;
+ int fd = -1;
+ xcint_image_t *images;
+ int nimg = 0;
+ xcb_pixmap_t pixmap = XCB_NONE;
+ xcb_gcontext_t gc = XCB_NONE;
+ uint32_t last_width = 0;
+ uint32_t last_height = 0;
+ xcb_cursor_t cid = XCB_NONE;
+
+ // NB: if !render_present, fd will be -1 and thus the next if statement
+ // will trigger the fallback.
+ if (c->render_version != RV_NONE) {
+ if (c->rm[RM_XCURSOR_THEME])
+ fd = open_cursor_file(c, c->rm[RM_XCURSOR_THEME], name, &core_char);
+
+ if (fd == -1 && core_char == -1)
+ fd = open_cursor_file(c, "default", name, &core_char);
+ }
+
+ if (fd == -1 || core_char > -1) {
+ if (core_char == -1)
+ core_char = cursor_shape_to_id(name);
+ if (core_char == -1)
+ return XCB_NONE;
+
+ cid = xcb_generate_id(c->conn);
+ xcb_create_glyph_cursor(c->conn, cid, c->cursor_font, c->cursor_font, core_char, core_char + 1, 0, 0, 0, 65535, 65535, 65535);
+ return cid;
+ }
+
+ if (parse_cursor_file(c, fd, &images, &nimg) < 0) {
+ close(fd);
+ return XCB_NONE;
+ }
+
+ close(fd);
+
+ /* create a cursor from it */
+ xcb_render_animcursorelt_t elements[nimg];
+ xcb_render_picture_t pic = xcb_generate_id(c->conn);
+
+ for (int n = 0; n < nimg; n++) {
+ xcint_image_t *i = &(images[n]);
+ xcb_image_t *img = xcb_image_create_native(c->conn, i->width, i->height, XCB_IMAGE_FORMAT_Z_PIXMAP, 32, NULL, (i->width * i->height * sizeof(uint32_t)), (uint8_t*)i->pixels);
+
+ if (pixmap == XCB_NONE ||
+ (i->width != last_width) ||
+ (i->height != last_height)) {
+ if (pixmap == XCB_NONE) {
+ pixmap = xcb_generate_id(c->conn);
+ gc = xcb_generate_id(c->conn);
+ } else {
+ xcb_free_pixmap(c->conn, pixmap);
+ xcb_free_gc(c->conn, gc);
+ }
+
+ xcb_create_pixmap(c->conn, 32, pixmap, c->root, i->width, i->height);
+ xcb_create_gc(c->conn, gc, pixmap, 0, NULL);
+
+ last_width = i->width;
+ last_height = i->height;
+ }
+
+ xcb_image_put(c->conn, pixmap, gc, img, 0, 0, 0);
+
+ xcb_render_create_picture(c->conn, pic, pixmap, c->pict_format->id, 0, NULL);
+
+ elements[n].cursor = xcb_generate_id(c->conn);
+ elements[n].delay = i->delay;
+
+ xcb_render_create_cursor(c->conn, elements[n].cursor, pic, i->xhot, i->yhot);
+
+ xcb_render_free_picture(c->conn, pic);
+ xcb_image_destroy(img);
+ free(i->pixels);
+ }
+
+ xcb_free_pixmap(c->conn, pixmap);
+ xcb_free_gc(c->conn, gc);
+ free(images);
+
+ if (nimg == 1 || c->render_version == RV_CURSOR) {
+ /* non-animated cursor or no support for animated cursors */
+ return elements[0].cursor;
+ } else {
+ cid = xcb_generate_id(c->conn);
+ xcb_render_create_anim_cursor (c->conn, cid, nimg, elements);
+
+ for (int n = 0; n < nimg; n++) {
+ xcb_free_cursor(c->conn, elements[n].cursor);
+ }
+
+ return cid;
+ }
+}
diff --git a/depends/libxcb-cursor/xcb/parse_cursor_file.c b/depends/libxcb-cursor/xcb/parse_cursor_file.c
new file mode 100644
index 0000000..21f72bf
--- /dev/null
+++ b/depends/libxcb-cursor/xcb/parse_cursor_file.c
@@ -0,0 +1,198 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * Copyright © 2013 Michael Stapelberg
+ *
+ * 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 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 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.
+ *
+ * Except as contained in this notice, the names of the authors or their
+ * institutions shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization from the authors.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+#ifdef HAVE_SYS_BYTEORDER_H
+#include <sys/byteorder.h>
+# ifndef HAVE_LE32TOH
+# define le32toh(x) LE_32(x)
+# endif
+#elif defined(HAVE_LIBKERN_OSBYTEORDER_H)
+#include <libkern/OSByteOrder.h>
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+#endif
+
+#include <xcb/xcb.h>
+
+#include "cursor.h"
+#include "xcb_cursor.h"
+
+static uint32_t dist(const uint32_t a, const uint32_t b) {
+ return (a > b ? (a - b) : (b - a));
+}
+
+static uint32_t find_best_size(xcint_cursor_file_t *cf, const uint32_t target, uint32_t *nsizesp) {
+ uint32_t best = 0;
+ /* Amount of cursors with the best size */
+ uint32_t nsizes = 0;
+ for (int n = 0; n < cf->header.ntoc; n++) {
+ const uint32_t size = cf->tocs[n].subtype;
+
+ if (cf->tocs[n].type != XCURSOR_IMAGE_TYPE)
+ continue;
+
+ /* If the distance is less to the target size, this is a better fit. */
+ if (best == 0 || dist(size, target) < dist(best, target)) {
+ best = size;
+ nsizes = 0;
+ }
+
+ if (size == best)
+ nsizes++;
+ }
+
+ *nsizesp = nsizes;
+ return best;
+}
+
+/* Returns true if and only if the read() call read the entirety of the data it
+ * was supposed to read. */
+static bool read_entirely(int fd, void *buf, size_t count) {
+ return read(fd, buf, count) == count;
+}
+
+int parse_cursor_file(xcb_cursor_context_t *c, const int fd, xcint_image_t **images, int *nimg) {
+ /* Read the header, verify the magic value. */
+ xcint_cursor_file_t cf;
+ uint32_t nsizes = 0;
+ uint32_t best = 0;
+ uint32_t skip = 0;
+ /* The amount of images stored in 'images', used when cleaning up. */
+ int cnt = 0;
+
+ if (!read_entirely(fd, &(cf.header), sizeof(xcint_file_header_t)))
+ return -EINVAL;
+
+ cf.header.magic = le32toh(cf.header.magic);
+ cf.header.header = le32toh(cf.header.header);
+ cf.header.version = le32toh(cf.header.version);
+ cf.header.ntoc = le32toh(cf.header.ntoc);
+
+ if (cf.header.magic != XCURSOR_MAGIC)
+ return -EINVAL;
+
+ if ((skip = (cf.header.header - sizeof(xcint_file_header_t))) > 0)
+ if (lseek(fd, skip, SEEK_CUR) == EOF)
+ return -EINVAL;
+
+ if (cf.header.ntoc > 0x10000)
+ return -EINVAL;
+
+ /* Read the table of contents */
+ cf.tocs = malloc(cf.header.ntoc * sizeof(xcint_file_toc_t));
+ if (!read_entirely(fd, cf.tocs, cf.header.ntoc * sizeof(xcint_file_toc_t)))
+ goto error;
+
+ for (int n = 0; n < cf.header.ntoc; n++) {
+ cf.tocs[n].type = le32toh(cf.tocs[n].type);
+ cf.tocs[n].subtype = le32toh(cf.tocs[n].subtype);
+ cf.tocs[n].position = le32toh(cf.tocs[n].position);
+ }
+
+ /* No images? Invalid file. */
+ if ((best = find_best_size(&cf, c->size, &nsizes)) == 0 || nsizes == 0)
+ goto error;
+
+ *nimg = nsizes;
+ if ((*images = calloc(nsizes, sizeof(xcint_image_t))) == NULL)
+ goto error;
+
+ for (int n = 0; n < cf.header.ntoc; n++) {
+ xcint_chunk_header_t chunk;
+ /* for convenience */
+ xcint_image_t *i = &((*images)[cnt]);
+ uint32_t numpixels = 0;
+ uint32_t *p = NULL;
+
+ if (cf.tocs[n].type != XCURSOR_IMAGE_TYPE ||
+ cf.tocs[n].subtype != best)
+ continue;
+
+ lseek(fd, cf.tocs[n].position, SEEK_SET);
+ if (!read_entirely(fd, &chunk, sizeof(xcint_chunk_header_t)))
+ goto error2;
+ chunk.header = le32toh(chunk.header);
+ chunk.type = le32toh(chunk.type);
+ chunk.subtype = le32toh(chunk.subtype);
+ chunk.version = le32toh(chunk.version);
+ /* Sanity check, as libxcursor does it. */
+ if (chunk.type != cf.tocs[n].type ||
+ chunk.subtype != cf.tocs[n].subtype)
+ goto error2;
+ if (!read_entirely(fd, i, sizeof(xcint_image_t) - sizeof(uint32_t*))) // TODO: better type
+ goto error2;
+ i->width = le32toh(i->width);
+ i->height = le32toh(i->height);
+ i->xhot = le32toh(i->xhot);
+ i->yhot = le32toh(i->yhot);
+ i->delay = le32toh(i->delay);
+
+ /* Read the actual image data and convert it to host byte order */
+ /* Catch integer overflows */
+ if (((uint64_t)i->width) * i->height > UINT32_MAX)
+ goto error2;
+ numpixels = i->width * i->height;
+ i->pixels = malloc(numpixels * sizeof(uint32_t));
+ /* With the malloc, one more image is eligible for cleanup later. */
+ cnt++;
+ if (!read_entirely(fd, i->pixels, numpixels * sizeof(uint32_t)))
+ goto error2;
+ p = i->pixels;
+ for (int j = 0; j < numpixels; j++, p++)
+ *p = le32toh(*p);
+ }
+
+ free(cf.tocs);
+ return 0;
+
+error2:
+ /* Free the memory for all images that were read so far. */
+ for (int n = 0; n < cnt; n++)
+ free((*images)[n].pixels);
+ free(*images);
+error:
+ *images = NULL;
+ free(cf.tocs);
+ return -EINVAL;
+}
diff --git a/depends/libxcb-cursor/xcb/xcb_cursor.h b/depends/libxcb-cursor/xcb/xcb_cursor.h
new file mode 100644
index 0000000..8752a9c
--- /dev/null
+++ b/depends/libxcb-cursor/xcb/xcb_cursor.h
@@ -0,0 +1,124 @@
+#ifndef XCB_CURSOR_H
+#define XCB_CURSOR_H
+
+/* Copyright © 2013 Michael Stapelberg
+ *
+ * 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 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 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.
+ *
+ * Except as contained in this notice, the names of the authors or their
+ * institutions shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization from the authors.
+ */
+
+#include <xcb/xcb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup xcb__cursor_context_t XCB Cursor Functions
+ *
+ * These functions are the equivalent of libXcursor, but re-implemented for
+ * XCB. They respect the user’s configured cursor theme when loading cursors,
+ * specified by the X resources setting "Xcursor.theme".
+ *
+ * Here is how you would use these functions to change the X11 root window
+ * cursor to "watch":
+ * @code
+ * int screennr;
+ * xcb_connection_t *conn = xcb_connect(NULL, &screennr);
+ * if (conn == NULL || xcb_connection_has_error(conn))
+ * err(EXIT_FAILURE, "Could not connect to X11");
+ *
+ * xcb_screen_t *screen = xcb_aux_get_screen(conn, screennr);
+ * xcb_cursor_context_t *ctx;
+ * if (xcb_cursor_context_new(conn, screen, &ctx) < 0)
+ * err(EXIT_FAILURE, "Could not initialize xcb-cursor");
+ *
+ * xcb_cursor_t cid = xcb_cursor_load_cursor(ctx, "watch");
+ *
+ * xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
+ * xcb_change_window_attributes(conn, screen->root, XCB_CW_CURSOR, (uint32_t[]){ cid });
+ * xcb_free_cursor(conn, cid);
+ * xcb_flush(conn);
+ *
+ * xcb_cursor_context_free(ctx);
+ * xcb_disconnect(conn);
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * @struct xcb_cursor_context_t
+ * Describes a context for using this library.
+ *
+ * Create a context with @ref xcb_cursor_context_new (), then load one or more
+ * cursors with @ref xcb_cursor_load_cursor () and destroy the context with @ref
+ * xcb_cursor_context_free ().
+ */
+typedef struct xcb_cursor_context_t xcb_cursor_context_t;
+
+/**
+ * Create a new @ref xcb_cursor_context_t.
+ *
+ * @param conn A working XCB connection, which will be used until you destroy
+ * the context with @ref xcb_cursor_context_free ().
+ * @param screen The xcb_screen_t to use (e.g. for getting the RESOURCE_MANAGER
+ * contents, for creating cursors on, for using the size as fallback when
+ * calculating the best cursor size).
+ * @param ctx A pointer to an xcb_cursor_context_t* which will be modified to
+ * refer to the newly created context.
+ * @return 0 on success, a negative error code otherwise.
+ *
+ * @ingroup xcb_cursor_context_t
+ */
+int xcb_cursor_context_new(xcb_connection_t *conn, xcb_screen_t *screen, xcb_cursor_context_t **ctx);
+
+/**
+ * Loads the specified cursor, either from the cursor theme or by falling back
+ * to the X11 "cursor" font.
+ *
+ * @param ctx A cursor context, created with @ref xcb_cursor_context_new ()
+ * @param name The name of the cursor to load, e.g. "watch".
+ * @returns The ID of the created cursor. When you are done using it, use
+ * xcb_free_cursor. Calling @ref xcb_cursor_context_free () will NOT free the
+ * created cursor.
+ *
+ */
+xcb_cursor_t xcb_cursor_load_cursor(xcb_cursor_context_t *ctx, const char *name);
+
+/**
+ * Frees the @ref xcb_cursor_context_t.
+ *
+ * @param ctx The context to free.
+ *
+ */
+void xcb_cursor_context_free(xcb_cursor_context_t *ctx);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif