Hyprland/src/managers/XCursorManager.cpp
Tom Englund a399f98c68
cursormgr: avoid scanning ill formed inherit (#7211)
avoid adding ill formed Inherit lines to inherit vector and later
scanning them, it wont change anything in practice but makes the inherit
theme parsing more in line with what its supposed todo. also check for
return values of the various string functions so we dont end up erasing
the wrong thing.
2024-08-07 16:37:09 +02:00

544 lines
27 KiB
C++

#include <cstring>
#include <dirent.h>
#include <filesystem>
#include "helpers/CursorShapes.hpp"
#include "debug/Log.hpp"
#include "XCursorManager.hpp"
// clang-format off
static std::vector<uint32_t> HYPR_XCURSOR_PIXELS = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1b001816, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8e008173, 0x5f00564d, 0x16001412, 0x09000807, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2b002624, 0x05000404, 0x00000000, 0x35002f2b, 0xd400bead,
0xc300b09e, 0x90008275, 0x44003e37, 0x04000403, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x67005a56,
0x6f00615c, 0x00000000, 0x00000000, 0x8b007c72, 0xf200d7c6, 0xfa00e0cc, 0xe800d0bd, 0xa0009181, 0x44003e37, 0x1a001815, 0x06000505, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x8d007976, 0xd600b8b3, 0x2500201f, 0x00000000, 0x17001413, 0xbd00a79c, 0xf600dacb, 0xff00e3d1, 0xfc00e1ce, 0xe800d0bc, 0xbf00ac9b,
0x95008778, 0x51004a41, 0x0f000e0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x92007b7b, 0xf500d0cf, 0x9e008685, 0x00000000, 0x00000000, 0x23001f1d, 0x64005853,
0x9b008980, 0xd900bfb3, 0xfb00dfce, 0xff00e4d0, 0xfb00e1cd, 0xec00d5c0, 0xa7009788, 0x47004139, 0x1e001b18, 0x05000504, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xa200878a, 0xff00d6d9, 0xd600b4b5,
0x0e000c0c, 0x00000000, 0x00000000, 0x02000202, 0x0c000b0a, 0x30002a28, 0x8e007d75, 0xd600bdb0, 0xef00d4c4, 0xfb00e0ce, 0xff00e4d0, 0xe600cfbb, 0xb800a695, 0x5f00564d,
0x06000505, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02000202, 0xc600a3aa, 0xff00d3da, 0xea00c3c8, 0x08000707, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x2a002523, 0x61005550, 0x9500837b,
0xd800bfb1, 0xfd00e1cf, 0xff00e5d0, 0xf500dcc7, 0x7c007065, 0x2a002622, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000505, 0xd600aeb9, 0xff00d0dc, 0xcc00a7af, 0x04000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000101, 0x01000101, 0x2c002724, 0xa1008e85, 0xe600ccbd, 0xf800ddcb, 0xef00d6c3, 0xc300af9f, 0x2c002824, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708, 0xd800adbc, 0xff00cdde, 0xb90095a0, 0x02000202, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000e0d, 0x4b00423e, 0xa4009088, 0xfd00dfd0, 0xff00e3d1,
0xae009c8f, 0x42003b36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x14001012, 0xf400c0d6,
0xff00cadf, 0xb2008e9c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02000202, 0x1200100f, 0xa2008e86, 0xec00cfc3, 0xfc00ded0, 0xc300ada0, 0x15001311, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x2e002429, 0xfd00c4e0, 0xff00c7e2, 0x8f00707e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1e001a19, 0x75006662, 0xfb00dbd1, 0xf700d9cc, 0x9600847c, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e002f37, 0xfc00c1e1, 0xff00c5e3, 0x60004b55, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x15001212, 0xa6008f8b, 0xff00ddd5,
0xf800d8ce, 0x36002f2d, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x51003d49, 0xfe00bfe5, 0xfe00c1e4, 0x4c003a44,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x01000101, 0x1d001918, 0xf400d1cd, 0xfe00dad5, 0xb3009a96, 0x03000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x66004b5d, 0xff00bee7, 0xfd00bee5, 0x4500343f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000202, 0x82006e6e, 0xff00d8d7, 0xd800b9b6, 0x33002c2b, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x70005267, 0xff00bbe9, 0xfa00b8e3, 0x29001e25, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3f003536, 0xea00c3c7, 0xf800d1d3,
0x4a003e3f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5f004458, 0xff00b8eb, 0xf400b1e0, 0x29001e26, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x1b001617, 0xe100bac0, 0xff00d4da, 0x82006c6f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5a004054, 0xfe00b4eb,
0xfb00b3e8, 0x3b002a36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0a000809, 0xc900a4ad, 0xff00d1dc, 0x88007075, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x44002f3f, 0xf300aae3, 0xfc00b1ea, 0x48003343, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000404, 0xdf00b3c2, 0xff00cedd, 0x8f00747c, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x25001a23, 0xf200a6e4, 0xff00b1ef, 0x84005c7c, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708,
0xe400b5c8, 0xff00cbdf, 0x78006068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x12000c11, 0xc00082b6, 0xff00aef1, 0xaa0075a0,
0x11000c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x1e00171b, 0xf700c1db, 0xff00c8e1, 0x4b003b42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x05000305, 0x8d005f86, 0xfc00aaef, 0xed00a0e1, 0x26001a24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6f005463, 0xff00c4e3, 0xf500beda, 0x0c00090b, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e00293b, 0xed009de2, 0xff00aaf3, 0x8b005d84, 0x04000304, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000506, 0xeb00b1d4, 0xff00c1e6, 0xba008ea7,
0x02000202, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xb20075ab, 0xff00a7f4, 0xf300a1e9, 0x35002333,
0x06000406, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x8900647d, 0xff00bde8, 0xfb00bce2, 0x26001d22, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x2b001c29, 0xf1009ce8, 0xfe00a5f4, 0xc60082be, 0x3b002738, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000101, 0x54003c4d, 0xfd00b8e8, 0xff00baea, 0x96006f89, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x78004d74, 0xe20091da, 0xff00a5f6, 0xb60077af, 0x42002b3f, 0x0c00080c, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x9400688a, 0xff00b5ed, 0xff00b6ec, 0xcc0093bc, 0x02000102, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x8100527d, 0xeb0096e4, 0xf900a0f1, 0xdc008dd4,
0x9b006595, 0x32002130, 0x0a00070a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2100161f, 0x5300394e, 0xe2009cd4, 0xff00b1ef, 0xff00b2ee, 0xc8008cba, 0x17001015,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x19001018, 0x5e003b5b, 0xcb0081c5, 0xff00a2f8, 0xfc00a1f4, 0xde0090d6, 0xa9006da3, 0x8300567e, 0x6f00496a, 0x76004e71, 0xbb007db2, 0xeb009edf, 0xfb00a9ee, 0xff00adf1,
0xfd00adee, 0x9e006d95, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x34002133, 0xa60069a1, 0xd70089d1, 0xf2009bea, 0xff00a3f7, 0xfb00a1f2, 0xfb00a2f2, 0xff00a6f5,
0xff00a8f4, 0xfd00a7f2, 0xed009ee2, 0xcf008bc5, 0x14000e13, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000b11, 0x35002134, 0x5b003959,
0x8b005887, 0xb30072ae, 0xc90080c3, 0xd10086ca, 0xa6006ba0, 0x65004261, 0x3c002839, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x06000406, 0x0d00080d, 0x0f000a0f, 0x0a00060a, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000};
// clang-format on
CXCursorManager::CXCursorManager() {
hyprCursor = makeShared<SXCursors>();
SXCursorImage image;
image.size = {32, 32};
image.hotspot = {3, 2};
image.pixels = HYPR_XCURSOR_PIXELS;
image.delay = 0;
hyprCursor->images.push_back(image);
hyprCursor->shape = "left_ptr";
defaultCursor = hyprCursor;
}
void CXCursorManager::loadTheme(std::string const& name, int size) {
if (lastLoadSize == size && themeName == name)
return;
lastLoadSize = size;
themeName = name.empty() ? "default" : name;
defaultCursor.reset();
cursors.clear();
auto paths = themePaths(themeName);
if (paths.empty()) {
Debug::log(ERR, "XCursor librarypath is empty loading standard XCursors");
cursors = loadStandardCursors(themeName, lastLoadSize);
} else {
for (auto const& p : paths) {
auto dirCursors = loadAllFromDir(p, lastLoadSize);
std::copy_if(dirCursors.begin(), dirCursors.end(), std::back_inserter(cursors),
[this](auto const& p) { return std::none_of(cursors.begin(), cursors.end(), [&p](auto const& dp) { return dp->shape == p->shape; }); });
}
}
if (cursors.empty()) {
Debug::log(ERR, "XCursor failed finding any shapes in theme \"{}\".", themeName);
defaultCursor = hyprCursor;
return;
}
for (auto const& shape : CURSOR_SHAPE_NAMES) {
auto legacyName = getLegacyShapeName(shape);
if (legacyName.empty())
continue;
auto it = std::find_if(cursors.begin(), cursors.end(), [&legacyName](auto const& c) { return c->shape == legacyName; });
if (it == cursors.end()) {
Debug::log(LOG, "XCursor failed to find a legacy shape with name {}, skipping", legacyName);
continue;
}
if (std::any_of(cursors.begin(), cursors.end(), [&shape](auto const& dp) { return dp->shape == shape; })) {
Debug::log(LOG, "XCursor already has a shape {} loaded, skipping", shape);
continue;
}
auto cursor = makeShared<SXCursors>();
cursor->images = it->get()->images;
cursor->shape = shape;
cursors.emplace_back(cursor);
}
}
SP<SXCursors> CXCursorManager::getShape(std::string const& shape, int size) {
// monitor scaling changed etc, so reload theme with new size.
if (size != lastLoadSize)
loadTheme(themeName, size);
// try to get an icon we know if we have one
for (auto const& c : cursors) {
if (c->shape != shape)
continue;
return c;
}
Debug::log(WARN, "XCursor couldn't find shape {} , using default cursor instead", shape);
return defaultCursor;
}
SP<SXCursors> CXCursorManager::createCursor(std::string const& shape, XcursorImages* xImages) {
auto xcursor = makeShared<SXCursors>();
for (int i = 0; i < xImages->nimage; i++) {
auto xImage = xImages->images[i];
SXCursorImage image;
image.size = {(int)xImage->width, (int)xImage->height};
image.hotspot = {(int)xImage->xhot, (int)xImage->yhot};
image.pixels.resize(xImage->width * xImage->height);
std::memcpy(image.pixels.data(), xImage->pixels, xImage->width * xImage->height * sizeof(uint32_t));
image.delay = xImage->delay;
xcursor->images.emplace_back(image);
}
xcursor->shape = shape;
return xcursor;
}
std::unordered_set<std::string> CXCursorManager::themePaths(std::string const& theme) {
auto const* path = XcursorLibraryPath();
auto expandTilde = [](std::string const& path) {
if (!path.empty() && path[0] == '~') {
const char* home = std::getenv("HOME");
if (home)
return std::string(home) + path.substr(1);
}
return path;
};
auto getInheritThemes = [](std::string const& indexTheme) {
std::ifstream infile(indexTheme);
std::string line;
std::vector<std::string> themes;
Debug::log(LOG, "XCursor parsing index.theme {}", indexTheme);
while (std::getline(infile, line)) {
if (line.empty())
continue;
// Trim leading and trailing whitespace
auto pos = line.find_first_not_of(" \t\n\r");
if (pos != std::string::npos)
line.erase(0, pos);
pos = line.find_last_not_of(" \t\n\r");
if (pos != std::string::npos && pos < line.length()) {
line.erase(pos + 1);
}
if (line.rfind("Inherits", 8) != std::string::npos) { // Check if line starts with "Inherits"
std::string inheritThemes = line.substr(8); // Extract the part after "Inherits"
if (inheritThemes.empty())
continue;
// Remove leading whitespace from inheritThemes and =
pos = inheritThemes.find_first_not_of(" \t\n\r");
if (pos != std::string::npos)
inheritThemes.erase(0, pos);
if (inheritThemes.empty())
continue;
if (inheritThemes.at(0) == '=')
inheritThemes.erase(0, 1);
else
continue; // not correct formatted index.theme
pos = inheritThemes.find_first_not_of(" \t\n\r");
if (pos != std::string::npos)
inheritThemes.erase(0, pos);
std::stringstream inheritStream(inheritThemes);
std::string inheritTheme;
while (std::getline(inheritStream, inheritTheme, ',')) {
if (inheritTheme.empty())
continue;
// Trim leading and trailing whitespace from each theme
pos = inheritTheme.find_first_not_of(" \t\n\r");
if (pos != std::string::npos)
inheritTheme.erase(0, pos);
pos = inheritTheme.find_last_not_of(" \t\n\r");
if (pos != std::string::npos && pos < inheritTheme.length())
inheritTheme.erase(inheritTheme.find_last_not_of(" \t\n\r") + 1);
themes.push_back(inheritTheme);
}
}
}
infile.close();
return themes;
};
std::unordered_set<std::string> paths;
std::unordered_set<std::string> inherits;
auto scanTheme = [&path, &paths, &expandTilde, &inherits, &getInheritThemes](auto const& t) {
std::stringstream ss(path);
std::string line;
Debug::log(LOG, "XCursor scanning theme {}", t);
while (std::getline(ss, line, ':')) {
auto p = expandTilde(line + "/" + t + "/cursors");
if (std::filesystem::exists(p) && std::filesystem::is_directory(p)) {
Debug::log(LOG, "XCursor using theme path {}", p);
paths.insert(p);
}
auto inherit = expandTilde(line + "/" + t + "/index.theme");
if (std::filesystem::exists(inherit) && std::filesystem::is_regular_file(inherit)) {
auto inheritThemes = getInheritThemes(inherit);
for (auto const& i : inheritThemes) {
Debug::log(LOG, "XCursor theme {} inherits {}", t, i);
inherits.insert(i);
}
}
}
};
if (path) {
scanTheme(theme);
while (!inherits.empty()) {
auto oldInherits = inherits;
for (auto& i : oldInherits)
scanTheme(i);
if (oldInherits.size() == inherits.size())
break;
}
}
return paths;
}
std::string CXCursorManager::getLegacyShapeName(std::string const& shape) {
if (shape == "invalid")
return std::string();
else if (shape == "default")
return "left_ptr";
else if (shape == "context-menu")
return "left_ptr";
else if (shape == "help")
return "left_ptr";
else if (shape == "pointer")
return "hand2";
else if (shape == "progress")
return "watch";
else if (shape == "wait")
return "watch";
else if (shape == "cell")
return "plus";
else if (shape == "crosshair")
return "cross";
else if (shape == "text")
return "xterm";
else if (shape == "vertical-text")
return "xterm";
else if (shape == "alias")
return "dnd-link";
else if (shape == "copy")
return "dnd-copy";
else if (shape == "move")
return "dnd-move";
else if (shape == "no-drop")
return "dnd-none";
else if (shape == "not-allowed")
return "crossed_circle";
else if (shape == "grab")
return "hand1";
else if (shape == "grabbing")
return "hand1";
else if (shape == "e-resize")
return "right_side";
else if (shape == "n-resize")
return "top_side";
else if (shape == "ne-resize")
return "top_right_corner";
else if (shape == "nw-resize")
return "top_left_corner";
else if (shape == "s-resize")
return "bottom_side";
else if (shape == "se-resize")
return "bottom_right_corner";
else if (shape == "sw-resize")
return "bottom_left_corner";
else if (shape == "w-resize")
return "left_side";
else if (shape == "ew-resize")
return "sb_h_double_arrow";
else if (shape == "ns-resize")
return "sb_v_double_arrow";
else if (shape == "nesw-resize")
return "fd_double_arrow";
else if (shape == "nwse-resize")
return "bd_double_arrow";
else if (shape == "col-resize")
return "sb_h_double_arrow";
else if (shape == "row-resize")
return "sb_v_double_arrow";
else if (shape == "all-scroll")
return "fleur";
else if (shape == "zoom-in")
return "left_ptr";
else if (shape == "zoom-out")
return "left_ptr";
return std::string();
};
// Taken from https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/blob/master/src/library.c
// clang-format off
static std::array<const char*, 77> XCURSOR_STANDARD_NAMES = {
"X_cursor",
"arrow",
"based_arrow_down",
"based_arrow_up",
"boat",
"bogosity",
"bottom_left_corner",
"bottom_right_corner",
"bottom_side",
"bottom_tee",
"box_spiral",
"center_ptr",
"circle",
"clock",
"coffee_mug",
"cross",
"cross_reverse",
"crosshair",
"diamond_cross",
"dot",
"dotbox",
"double_arrow",
"draft_large",
"draft_small",
"draped_box",
"exchange",
"fleur",
"gobbler",
"gumby",
"hand1",
"hand2",
"heart",
"icon",
"iron_cross",
"left_ptr",
"left_side",
"left_tee",
"leftbutton",
"ll_angle",
"lr_angle",
"man",
"middlebutton",
"mouse",
"pencil",
"pirate",
"plus",
"question_arrow",
"right_ptr",
"right_side",
"right_tee",
"rightbutton",
"rtl_logo",
"sailboat",
"sb_down_arrow",
"sb_h_double_arrow",
"sb_left_arrow",
"sb_right_arrow",
"sb_up_arrow",
"sb_v_double_arrow",
"shuttle",
"sizing",
"spider",
"spraycan",
"star",
"target",
"tcross",
"top_left_arrow",
"top_left_corner",
"top_right_corner",
"top_side",
"top_tee",
"trek",
"ul_angle",
"umbrella",
"ur_angle",
"watch",
"xterm",
};
// clang-format on
std::vector<SP<SXCursors>> CXCursorManager::loadStandardCursors(std::string const& name, int size) {
std::vector<SP<SXCursors>> newCursors;
// load the default xcursor shapes that exist in the theme
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
std::string shape{XCURSOR_STANDARD_NAMES.at(i)};
auto xImages = XcursorShapeLoadImages(i << 1 /* wtf xcursor? */, name.c_str(), size);
if (!xImages) {
Debug::log(WARN, "XCursor failed to find a shape with name {}, trying size 24.", shape);
xImages = XcursorShapeLoadImages(i << 1 /* wtf xcursor? */, name.c_str(), 24);
if (!xImages) {
Debug::log(WARN, "XCursor failed to find a shape with name {}, skipping", shape);
continue;
}
}
auto cursor = createCursor(shape, xImages);
newCursors.emplace_back(cursor);
if (!defaultCursor && (shape == "left_ptr" || shape == "arrow"))
defaultCursor = cursor;
XcursorImagesDestroy(xImages);
}
// broken theme.. just set it.
if (!newCursors.empty() && !defaultCursor)
defaultCursor = newCursors.front();
return newCursors;
}
std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& path, int size) {
std::vector<SP<SXCursors>> newCursors;
if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) {
for (const auto& entry : std::filesystem::directory_iterator(path)) {
if (!entry.is_regular_file() && !entry.is_symlink())
continue;
auto const& full = entry.path().string();
using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> f(fopen(full.c_str(), "r"), static_cast<PcloseType>(fclose));
if (!f)
continue;
auto xImages = XcursorFileLoadImages(f.get(), size);
if (!xImages) {
Debug::log(WARN, "XCursor failed to load image {}, trying size 24.", full);
xImages = XcursorFileLoadImages(f.get(), 24);
if (!xImages) {
Debug::log(WARN, "XCursor failed to load image {}, skipping", full);
continue;
}
}
auto const& shape = entry.path().filename().string();
auto cursor = createCursor(shape, xImages);
newCursors.emplace_back(cursor);
if (!defaultCursor && (shape == "left_ptr" || shape == "arrow"))
defaultCursor = cursor;
XcursorImagesDestroy(xImages);
}
}
// broken theme.. just set it.
if (!newCursors.empty() && !defaultCursor)
defaultCursor = newCursors.front();
return newCursors;
}