summaryrefslogtreecommitdiff
path: root/src/managers/XCursorManager.cpp
diff options
context:
space:
mode:
authorTom Englund <tomenglund26@gmail.com>2024-08-05 19:58:21 +0200
committerGitHub <noreply@github.com>2024-08-05 19:58:21 +0200
commit0e86808e5912823f1c6bea1b6d5fcae297fc9f57 (patch)
tree261ff4c9a108ef9d2de1c151685d52d85405497d /src/managers/XCursorManager.cpp
parent2b520571e897be2a0e88c8692da607b062000038 (diff)
cursor: Better xcursor implementation (#7178)
* xcursor: bootleg xcursors into its own manager implent XCursorManager and load themes based on librarypath and its dir, now we catch all supplied theme files. and also implent animated cursors. also refactor a bit of spaghetti regarding xcursors in CursorManager. * hyprcursor: fix buffer leak animated cursors are creating a new buffer for each image, ensure we drop the buffers so it continously doesnt build up in infinity. * cursormgr: use eventloopmgr for animation use EvenloopManager for timers instead of adding it directly to m_sWLEventLoop and using its related wl_* functions.
Diffstat (limited to 'src/managers/XCursorManager.cpp')
-rw-r--r--src/managers/XCursorManager.cpp447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/managers/XCursorManager.cpp b/src/managers/XCursorManager.cpp
new file mode 100644
index 00000000..b3f33086
--- /dev/null
+++ b/src/managers/XCursorManager.cpp
@@ -0,0 +1,447 @@
+#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;
+ }
+
+ 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::vector<std::string> CXCursorManager::themePaths(std::string const& theme) {
+ auto const* path = XcursorLibraryPath();
+ std::vector<std::string> paths;
+
+ 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;
+ };
+
+ if (path) {
+ std::stringstream ss(path);
+ std::string item;
+
+ while (std::getline(ss, item, ':')) {
+ auto p = expandTilde(item + "/" + theme + "/cursors");
+
+ if (std::filesystem::exists(p) && std::filesystem::is_directory(p))
+ paths.push_back(p);
+ }
+ }
+
+ 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;
+ std::string full;
+
+ 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;
+
+ std::string 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;
+ }
+ }
+
+ std::string 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;
+}