|
|
@@ -25,6 +25,7 @@
|
|
|
#include "throw_event.h"
|
|
|
#include "lightReMutexHolder.h"
|
|
|
#include "nativeWindowHandle.h"
|
|
|
+#include "virtualFileSystem.h"
|
|
|
|
|
|
#include <errno.h>
|
|
|
#include <fcntl.h>
|
|
|
@@ -53,6 +54,35 @@
|
|
|
#include <linux/input.h>
|
|
|
#endif
|
|
|
|
|
|
+#ifdef HAVE_XCURSOR
|
|
|
+static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
|
|
|
+ istream* str = (istream*) file->closure;
|
|
|
+ str->read((char*) buf, len);
|
|
|
+ return str->gcount();
|
|
|
+}
|
|
|
+
|
|
|
+static int xcursor_write(XcursorFile *file, unsigned char *buf, int len) {
|
|
|
+ // Not implemented, we don't need it.
|
|
|
+ nassertr_always(false, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static int xcursor_seek(XcursorFile *file, long offset, int whence) {
|
|
|
+ istream* str = (istream*) file->closure;
|
|
|
+ switch (whence) {
|
|
|
+ case SEEK_SET:
|
|
|
+ str->seekg(offset, istream::beg);
|
|
|
+ break;
|
|
|
+ case SEEK_CUR:
|
|
|
+ str->seekg(offset, istream::cur);
|
|
|
+ break;
|
|
|
+ case SEEK_END:
|
|
|
+ str->seekg(offset, istream::end);
|
|
|
+ }
|
|
|
+
|
|
|
+ return str->tellg();
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
TypeHandle x11GraphicsWindow::_type_handle;
|
|
|
|
|
|
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
|
|
|
@@ -1873,23 +1903,252 @@ get_cursor(const Filename &filename) {
|
|
|
return fi->second;
|
|
|
}
|
|
|
|
|
|
- Filename os = resolved.to_os_specific();
|
|
|
+ // Open the file through the virtual file system.
|
|
|
+ istream *str = VirtualFileSystem::get_global_ptr()->open_read_file(resolved, true);
|
|
|
+ if (str == NULL) {
|
|
|
+ x11display_cat.warning()
|
|
|
+ << "Could not open cursor file " << filename << "\n";
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check the first four bytes to see what kind of file it is.
|
|
|
+ char magic[4];
|
|
|
+ str->read(magic, 4);
|
|
|
+ if (!str->good()) {
|
|
|
+ x11display_cat.warning()
|
|
|
+ << "Could not read from cursor file " << filename << "\n";
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ str->seekg(0, istream::beg);
|
|
|
|
|
|
+ Cursor h = None;
|
|
|
+ if (memcmp(magic, "Xcur", 4) == 0) {
|
|
|
+ // X11 cursor.
|
|
|
#ifdef HAVE_XCURSOR
|
|
|
- Cursor h = XcursorFilenameLoadCursor(_display, os.c_str());
|
|
|
+ x11display_cat.debug()
|
|
|
+ << "Loading X11 cursor " << filename << "\n";
|
|
|
+ XcursorFile xcfile;
|
|
|
+ xcfile.closure = str;
|
|
|
+ xcfile.read = &xcursor_read;
|
|
|
+ xcfile.write = &xcursor_write;
|
|
|
+ xcfile.seek = &xcursor_seek;
|
|
|
+
|
|
|
+ XcursorImages *images = XcursorXcFileLoadImages(&xcfile, XcursorGetDefaultSize(_display));
|
|
|
+ if (images != NULL) {
|
|
|
+ h = XcursorImagesLoadCursor(_display, images);
|
|
|
+ XcursorImagesDestroy(images);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // Windows .ico or .cur file.
|
|
|
+ x11display_cat.debug()
|
|
|
+ << "Loading Windows cursor " << filename << "\n";
|
|
|
+ h = read_ico(*str);
|
|
|
+ }
|
|
|
+
|
|
|
+ delete str;
|
|
|
|
|
|
if (h == None) {
|
|
|
x11display_cat.warning()
|
|
|
- << "x11 cursor filename '" << os << "' could not be loaded!!\n";
|
|
|
+ << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
|
|
|
}
|
|
|
-#else
|
|
|
- // Can't support loading cursor from image, so fall back to default
|
|
|
- Cursor h = None;
|
|
|
-#endif
|
|
|
|
|
|
_cursor_filenames[filename] = h;
|
|
|
_cursor_filenames[resolved] = h;
|
|
|
return h;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: x11GraphicsWindow::load_ico
|
|
|
+// Access: Private
|
|
|
+// Description: Reads a Windows .ico or .cur file from the
|
|
|
+// indicated stream and returns it as an X11 Cursor.
|
|
|
+// If the file cannot be loaded, returns None.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+Cursor x11GraphicsWindow::
|
|
|
+read_ico(istream &ico) {
|
|
|
+ // Local structs, this is just POD, make input easier
|
|
|
+ typedef struct {
|
|
|
+ uint16_t reserved, type, count;
|
|
|
+ } IcoHeader;
|
|
|
+
|
|
|
+ typedef struct {
|
|
|
+ uint8_t width, height, colorCount, reserved;
|
|
|
+ uint16_t xhot, yhot;
|
|
|
+ uint32_t bitmapSize, offset;
|
|
|
+ } IcoEntry;
|
|
|
+
|
|
|
+ typedef struct {
|
|
|
+ uint32_t headerSize, width, height;
|
|
|
+ uint16_t planes, bitsPerPixel;
|
|
|
+ uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
|
|
|
+ } IcoInfoHeader;
|
|
|
+
|
|
|
+ typedef struct {
|
|
|
+ uint8_t blue, green, red, reserved;
|
|
|
+ } IcoColor;
|
|
|
+
|
|
|
+ int i, entry = 0;
|
|
|
+ unsigned int j, k, mask, shift;
|
|
|
+ size_t colorCount, bitsPerPixel;
|
|
|
+ IcoHeader header;
|
|
|
+ IcoInfoHeader infoHeader;
|
|
|
+ IcoEntry *entries = NULL;
|
|
|
+ IcoColor color, *palette = NULL;
|
|
|
+
|
|
|
+ size_t xorBmpSize, andBmpSize;
|
|
|
+ char *curXor, *curAnd;
|
|
|
+ char *xorBmp = NULL, *andBmp = NULL;
|
|
|
+ XcursorImage *image = NULL;
|
|
|
+ Cursor ret = None;
|
|
|
+
|
|
|
+#ifdef HAVE_XCURSOR
|
|
|
+ int def_size = XcursorGetDefaultSize(_display);
|
|
|
+#endif
|
|
|
+
|
|
|
+ // Get our header, note that ICO = type 1 and CUR = type 2.
|
|
|
+ ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+ cerr << header.type;
|
|
|
+ if (header.type != 1 && header.type != 2) goto cleanup;
|
|
|
+ if (header.count < 1) goto cleanup;
|
|
|
+ // Read the entry table into memory, select the largest entry.
|
|
|
+ entries = new IcoEntry[header.count];
|
|
|
+ ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+ for (i = 1; i < header.count; i++) {
|
|
|
+#ifdef HAVE_XCURSOR
|
|
|
+ if (entries[i].width == def_size && entries[i].height == def_size) {
|
|
|
+ // Wait, this is the default cursor size. This is perfect.
|
|
|
+ entry = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ if (entries[i].width > entries[entry].width ||
|
|
|
+ entries[i].height > entries[entry].height)
|
|
|
+ entry = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Seek to the image in the ICO.
|
|
|
+ ico.seekg(entries[entry].offset);
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+ ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+ bitsPerPixel = infoHeader.bitsPerPixel;
|
|
|
+
|
|
|
+ // TODO: Support PNG compressed ICOs.
|
|
|
+ if (infoHeader.compression != 0) goto cleanup;
|
|
|
+
|
|
|
+ // Load the color palette, if one exists.
|
|
|
+ colorCount = entries[entry].colorCount == 0 ? 256 : entries[entry].colorCount;
|
|
|
+ palette = new IcoColor[colorCount];
|
|
|
+ if (bitsPerPixel <= 8) ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+
|
|
|
+ // Read in the pixel data.
|
|
|
+ xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
|
|
|
+ andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
|
|
|
+ curXor = xorBmp = new char[xorBmpSize];
|
|
|
+ curAnd = andBmp = new char[andBmpSize];
|
|
|
+ ico.read(xorBmp, xorBmpSize);
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+ ico.read(andBmp, andBmpSize);
|
|
|
+ if (!ico.good()) goto cleanup;
|
|
|
+
|
|
|
+#ifdef HAVE_XCURSOR
|
|
|
+ // If this is an actual CUR not an ICO set up the hotspot properly.
|
|
|
+ image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
|
|
|
+ if (header.type == 2) { image->xhot = entries[entry].xhot; image->yhot = entries[entry].yhot; }
|
|
|
+
|
|
|
+ // Support all the formats that GIMP supports, minus PNG compressed ICOs.
|
|
|
+ // Would need to use libpng to decode the compressed ones.
|
|
|
+ switch (bitsPerPixel) {
|
|
|
+ case 1:
|
|
|
+ case 4:
|
|
|
+ case 8:
|
|
|
+ // For colors less that a byte wide, shift and mask the palette indices
|
|
|
+ // off each element of the xorBmp and append them to the image.
|
|
|
+ mask = ((1 << bitsPerPixel) - 1);
|
|
|
+ for (i = image->height - 1; i >= 0; i--) {
|
|
|
+ for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
|
|
|
+ for (k = 0; k < 8 / bitsPerPixel; k++) {
|
|
|
+ shift = 8 - ((k + 1) * bitsPerPixel);
|
|
|
+ color = palette[(*curXor & (mask << shift)) >> shift];
|
|
|
+ image->pixels[(i * image->width) + j + k] = (color.red << 16) +
|
|
|
+ (color.green << 8) +
|
|
|
+ (color.blue);
|
|
|
+ }
|
|
|
+
|
|
|
+ curXor++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the alpha byte properly according to the andBmp.
|
|
|
+ for (j = 0; j < image->width; j += 8) {
|
|
|
+ for (k = 0; k < 8; k++) {
|
|
|
+ shift = 7 - k;
|
|
|
+ image->pixels[(i * image->width) + j + k] |=
|
|
|
+ ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
|
|
|
+ }
|
|
|
+
|
|
|
+ curAnd++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 24:
|
|
|
+ // Pack each of the three bytes into a single color, BGR -> 0RGB
|
|
|
+ for (i = image->height - 1; i >= 0; i--) {
|
|
|
+ for (j = 0; j < image->width; j++) {
|
|
|
+ image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
|
|
|
+ (*(curXor + 1) << 8) + (*curXor);
|
|
|
+ curXor += 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the alpha byte properly according to the andBmp.
|
|
|
+ for (j = 0; j < image->width; j += 8) {
|
|
|
+ for (k = 0; k < 8; k++) {
|
|
|
+ shift = 7 - k;
|
|
|
+ image->pixels[(i * image->width) + j + k] |=
|
|
|
+ ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
|
|
|
+ }
|
|
|
+
|
|
|
+ curAnd++;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 32:
|
|
|
+ // Pack each of the four bytes into a single color, BGRA -> ARGB
|
|
|
+ for (i = image->height - 1; i >= 0; i--) {
|
|
|
+ for (j = 0; j < image->width; j++) {
|
|
|
+ image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
|
|
|
+ (*(curXor + 2) << 16) +
|
|
|
+ (*(curXor + 1) << 8) +
|
|
|
+ (*curXor);
|
|
|
+ curXor += 4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ goto cleanup;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = XcursorImageLoadCursor(_display, image);
|
|
|
+#endif
|
|
|
+
|
|
|
+cleanup:
|
|
|
+#ifdef HAVE_XCURSOR
|
|
|
+ XcursorImageDestroy(image);
|
|
|
+#endif
|
|
|
+ delete[] entries;
|
|
|
+ delete[] palette;
|
|
|
+ delete[] xorBmp;
|
|
|
+ delete[] andBmp;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|