mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-22 18:46:00 +01:00
cursormgr: implement inheriting themes for xcursor (#7197)
* cursormgr: reduce duplicated code add a few functions such as setCursorBuffer and setAnimationTimer to reduce duplicated code and also avoid future mishaps of forgetting to clear buffer or disarm timer. and generally reduce spaghetti even tho pasta can be delicious. * xcursormgr: implent inherited themes implent index.theme parsing and inherited themes. * cursormgr: ensure a fallback xcursor exist ensure a xcursor fallback exist otherwise it wont load the proper theme if we at launch have hyprcursor enabled and then set it to false in config and reload. also use the env var when using hyprctl setcursor incase its empty.
This commit is contained in:
parent
a05da63d85
commit
3d82d199f0
4 changed files with 285 additions and 223 deletions
|
@ -17,11 +17,61 @@ static void hcLogger(enum eHyprcursorLogLevel level, char* message) {
|
||||||
Debug::log(NONE, "[hc] {}", message);
|
Debug::log(NONE, "[hc] {}", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
CCursorManager::CCursorManager() {
|
CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
|
||||||
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.empty() ? nullptr : m_szTheme.c_str(), hcLogger);
|
surface = surf;
|
||||||
m_pXcursor = std::make_unique<CXCursorManager>();
|
size = size_;
|
||||||
|
stride = cairo_image_surface_get_stride(surf);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_pHyprcursor->valid()) {
|
CCursorBuffer::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
|
||||||
|
pixelData = pixelData_;
|
||||||
|
size = size_;
|
||||||
|
stride = 4 * size_.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::eBufferCapability CCursorBuffer::caps() {
|
||||||
|
return Aquamarine::eBufferCapability::BUFFER_CAPABILITY_DATAPTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::eBufferType CCursorBuffer::type() {
|
||||||
|
return Aquamarine::eBufferType::BUFFER_TYPE_SHM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCursorBuffer::update(const Hyprutils::Math::CRegion& damage) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCursorBuffer::isSynchronous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCursorBuffer::good() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::SSHMAttrs CCursorBuffer::shm() {
|
||||||
|
Aquamarine::SSHMAttrs attrs;
|
||||||
|
attrs.success = true;
|
||||||
|
attrs.format = DRM_FORMAT_ARGB8888;
|
||||||
|
attrs.size = size;
|
||||||
|
attrs.stride = stride;
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<uint8_t*, uint32_t, size_t> CCursorBuffer::beginDataPtr(uint32_t flags) {
|
||||||
|
return {pixelData ? pixelData : cairo_image_surface_get_data(surface), DRM_FORMAT_ARGB8888, stride};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCursorBuffer::endDataPtr() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCursorManager::CCursorManager() {
|
||||||
|
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.empty() ? nullptr : m_szTheme.c_str(), hcLogger);
|
||||||
|
m_pXcursor = std::make_unique<CXCursorManager>();
|
||||||
|
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
||||||
|
|
||||||
|
if (m_pHyprcursor->valid() && *PUSEHYPRCURSOR) {
|
||||||
// find default size. First, HYPRCURSOR_SIZE then default to 24
|
// find default size. First, HYPRCURSOR_SIZE then default to 24
|
||||||
auto const* SIZE = getenv("HYPRCURSOR_SIZE");
|
auto const* SIZE = getenv("HYPRCURSOR_SIZE");
|
||||||
if (SIZE) {
|
if (SIZE) {
|
||||||
|
@ -48,10 +98,12 @@ CCursorManager::CCursorManager() {
|
||||||
Debug::log(WARN, "XCURSOR_SIZE size not set, defaulting to size 24");
|
Debug::log(WARN, "XCURSOR_SIZE size not set, defaulting to size 24");
|
||||||
m_iSize = 24;
|
m_iSize = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pXcursor->loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default", m_iSize * std::ceil(m_fCursorScale));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// since we fallback to xcursor always load it on startup. otherwise we end up with a empty theme if hyprcursor is enabled in the config
|
||||||
|
// and then later is disabled.
|
||||||
|
m_pXcursor->loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default", m_iSize * std::ceil(m_fCursorScale));
|
||||||
|
|
||||||
m_pAnimationTimer = makeShared<CEventLoopTimer>(std::nullopt, cursorAnimTimer, this);
|
m_pAnimationTimer = makeShared<CEventLoopTimer>(std::nullopt, cursorAnimTimer, this);
|
||||||
g_pEventLoopManager->addTimer(m_pAnimationTimer);
|
g_pEventLoopManager->addTimer(m_pAnimationTimer);
|
||||||
|
|
||||||
|
@ -65,63 +117,9 @@ CCursorManager::~CCursorManager() {
|
||||||
g_pEventLoopManager->removeTimer(m_pAnimationTimer);
|
g_pEventLoopManager->removeTimer(m_pAnimationTimer);
|
||||||
m_pAnimationTimer.reset();
|
m_pAnimationTimer.reset();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void CCursorManager::dropBufferRef(CCursorManager::CCursorBuffer* ref) {
|
if (m_pHyprcursor->valid() && m_sCurrentStyleInfo.size > 0)
|
||||||
std::erase_if(m_vCursorBuffers, [ref](const auto& buf) { return buf.get() == ref; });
|
m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo);
|
||||||
}
|
|
||||||
|
|
||||||
CCursorManager::CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
|
|
||||||
surface = surf;
|
|
||||||
size = size_;
|
|
||||||
stride = cairo_image_surface_get_stride(surf);
|
|
||||||
}
|
|
||||||
|
|
||||||
CCursorManager::CCursorBuffer::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
|
|
||||||
pixelData = pixelData_;
|
|
||||||
size = size_;
|
|
||||||
stride = 4 * size_.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
CCursorManager::CCursorBuffer::~CCursorBuffer() {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
Aquamarine::eBufferCapability CCursorManager::CCursorBuffer::caps() {
|
|
||||||
return Aquamarine::eBufferCapability::BUFFER_CAPABILITY_DATAPTR;
|
|
||||||
}
|
|
||||||
|
|
||||||
Aquamarine::eBufferType CCursorManager::CCursorBuffer::type() {
|
|
||||||
return Aquamarine::eBufferType::BUFFER_TYPE_SHM;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CCursorManager::CCursorBuffer::update(const Hyprutils::Math::CRegion& damage) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CCursorManager::CCursorBuffer::isSynchronous() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CCursorManager::CCursorBuffer::good() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Aquamarine::SSHMAttrs CCursorManager::CCursorBuffer::shm() {
|
|
||||||
Aquamarine::SSHMAttrs attrs;
|
|
||||||
attrs.success = true;
|
|
||||||
attrs.format = DRM_FORMAT_ARGB8888;
|
|
||||||
attrs.size = size;
|
|
||||||
attrs.stride = stride;
|
|
||||||
return attrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<uint8_t*, uint32_t, size_t> CCursorManager::CCursorBuffer::beginDataPtr(uint32_t flags) {
|
|
||||||
return {pixelData ? pixelData : cairo_image_surface_get_data(surface), DRM_FORMAT_ARGB8888, stride};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CCursorManager::CCursorBuffer::endDataPtr() {
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SP<Aquamarine::IBuffer> CCursorManager::getCursorBuffer() {
|
SP<Aquamarine::IBuffer> CCursorManager::getCursorBuffer() {
|
||||||
|
@ -137,91 +135,101 @@ void CCursorManager::setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotsp
|
||||||
m_bOurBufferConnected = false;
|
m_bOurBufferConnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCursorManager::setXCursor(const std::string& name) {
|
void CCursorManager::setCursorBuffer(SP<CCursorBuffer> buf, const Vector2D& hotspot, const float& scale) {
|
||||||
float scale = std::ceil(m_fCursorScale);
|
m_vCursorBuffers.emplace_back(buf);
|
||||||
|
g_pPointerManager->setCursorBuffer(getCursorBuffer(), hotspot, scale);
|
||||||
auto xcursor = m_pXcursor->getShape(name, m_iSize * scale);
|
|
||||||
auto& icon = xcursor->images.front();
|
|
||||||
|
|
||||||
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot));
|
|
||||||
|
|
||||||
g_pPointerManager->setCursorBuffer(getCursorBuffer(), icon.hotspot / scale, scale);
|
|
||||||
if (m_vCursorBuffers.size() > 1)
|
if (m_vCursorBuffers.size() > 1)
|
||||||
dropBufferRef(m_vCursorBuffers.at(0).get());
|
std::erase_if(m_vCursorBuffers, [this](const auto& buf) { return buf.get() == m_vCursorBuffers.front().get(); });
|
||||||
|
|
||||||
m_currentXcursor = xcursor;
|
|
||||||
m_bOurBufferConnected = true;
|
m_bOurBufferConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_currentXcursor->images.size() > 1) {
|
void CCursorManager::setAnimationTimer(const int& frame, const int& delay) {
|
||||||
// animated
|
if (delay > 0) {
|
||||||
m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(m_currentXcursor->images[0].delay));
|
// arm
|
||||||
m_iCurrentAnimationFrame = 0;
|
m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(delay));
|
||||||
} else {
|
} else {
|
||||||
// disarm
|
// disarm
|
||||||
m_pAnimationTimer->updateTimeout(std::nullopt);
|
m_pAnimationTimer->updateTimeout(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_iCurrentAnimationFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCursorManager::setCursorFromName(const std::string& name) {
|
void CCursorManager::setCursorFromName(const std::string& name) {
|
||||||
|
|
||||||
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
||||||
|
|
||||||
if (!m_pHyprcursor->valid() || !*PUSEHYPRCURSOR) {
|
auto setXCursor = [this](auto const& name) {
|
||||||
setXCursor(name);
|
float scale = std::ceil(m_fCursorScale);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo);
|
auto xcursor = m_pXcursor->getShape(name, m_iSize * scale);
|
||||||
|
auto& icon = xcursor->images.front();
|
||||||
|
auto buf = makeShared<CCursorBuffer>((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot);
|
||||||
|
setCursorBuffer(buf, icon.hotspot / scale, scale);
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
m_currentXcursor = xcursor;
|
||||||
// try with '_' first (old hc, etc)
|
|
||||||
std::string newName = name;
|
|
||||||
std::replace(newName.begin(), newName.end(), '-', '_');
|
|
||||||
|
|
||||||
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(newName.c_str(), m_sCurrentStyleInfo);
|
int delay = 0;
|
||||||
}
|
int frame = 0;
|
||||||
|
if (m_currentXcursor->images.size() > 1)
|
||||||
|
delay = m_currentXcursor->images[frame].delay;
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
setAnimationTimer(frame, delay);
|
||||||
// fallback to a default if available
|
};
|
||||||
constexpr const std::array<const char*, 3> fallbackShapes = {"default", "left_ptr", "left-ptr"};
|
|
||||||
|
|
||||||
for (auto& s : fallbackShapes) {
|
auto setHyprCursor = [this](auto const& name) {
|
||||||
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(s, m_sCurrentStyleInfo);
|
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo);
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() > 0)
|
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
||||||
break;
|
// try with '_' first (old hc, etc)
|
||||||
|
std::string newName = name;
|
||||||
|
std::replace(newName.begin(), newName.end(), '-', '_');
|
||||||
|
|
||||||
|
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(newName.c_str(), m_sCurrentStyleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
||||||
Debug::log(ERR, "BUG THIS: No fallback found for a cursor in setCursorFromName");
|
// fallback to a default if available
|
||||||
setXCursor(name);
|
constexpr const std::array<const char*, 3> fallbackShapes = {"default", "left_ptr", "left-ptr"};
|
||||||
return;
|
|
||||||
|
for (auto& s : fallbackShapes) {
|
||||||
|
m_sCurrentCursorShapeData = m_pHyprcursor->getShape(s, m_sCurrentStyleInfo);
|
||||||
|
|
||||||
|
if (m_sCurrentCursorShapeData.images.size() > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_sCurrentCursorShapeData.images.size() < 1) {
|
||||||
|
Debug::log(ERR, "BUG THIS: No fallback found for a cursor in setCursorFromName");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>(m_sCurrentCursorShapeData.images[0].surface,
|
auto buf =
|
||||||
Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size},
|
makeShared<CCursorBuffer>(m_sCurrentCursorShapeData.images[0].surface, Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size},
|
||||||
Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY}));
|
Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY});
|
||||||
|
auto hotspot = Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY} / m_fCursorScale;
|
||||||
|
setCursorBuffer(buf, hotspot, m_fCursorScale);
|
||||||
|
|
||||||
g_pPointerManager->setCursorBuffer(getCursorBuffer(), Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY} / m_fCursorScale,
|
int delay = 0;
|
||||||
m_fCursorScale);
|
int frame = 0;
|
||||||
if (m_vCursorBuffers.size() > 1)
|
if (m_sCurrentCursorShapeData.images.size() > 1)
|
||||||
dropBufferRef(m_vCursorBuffers.at(0).get());
|
delay = m_sCurrentCursorShapeData.images[frame].delay;
|
||||||
|
|
||||||
m_bOurBufferConnected = true;
|
setAnimationTimer(frame, delay);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() > 1) {
|
if (!m_pHyprcursor->valid() || !*PUSEHYPRCURSOR || !setHyprCursor(name))
|
||||||
// animated
|
setXCursor(name);
|
||||||
m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(m_sCurrentCursorShapeData.images[0].delay));
|
|
||||||
m_iCurrentAnimationFrame = 0;
|
|
||||||
} else {
|
|
||||||
// disarm
|
|
||||||
m_pAnimationTimer->updateTimeout(std::nullopt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCursorManager::tickAnimatedCursor() {
|
void CCursorManager::tickAnimatedCursor() {
|
||||||
if (!m_pHyprcursor->valid() && m_currentXcursor->images.size() > 1 && m_bOurBufferConnected) {
|
if (!m_bOurBufferConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_pHyprcursor->valid() && m_currentXcursor->images.size() > 1) {
|
||||||
m_iCurrentAnimationFrame++;
|
m_iCurrentAnimationFrame++;
|
||||||
|
|
||||||
if ((size_t)m_iCurrentAnimationFrame >= m_currentXcursor->images.size())
|
if ((size_t)m_iCurrentAnimationFrame >= m_currentXcursor->images.size())
|
||||||
|
@ -229,39 +237,24 @@ void CCursorManager::tickAnimatedCursor() {
|
||||||
|
|
||||||
float scale = std::ceil(m_fCursorScale);
|
float scale = std::ceil(m_fCursorScale);
|
||||||
auto& icon = m_currentXcursor->images.at(m_iCurrentAnimationFrame);
|
auto& icon = m_currentXcursor->images.at(m_iCurrentAnimationFrame);
|
||||||
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot));
|
auto buf = makeShared<CCursorBuffer>((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot);
|
||||||
|
setCursorBuffer(buf, icon.hotspot / scale, scale);
|
||||||
|
setAnimationTimer(m_iCurrentAnimationFrame, m_currentXcursor->images[m_iCurrentAnimationFrame].delay);
|
||||||
|
} else if (m_sCurrentCursorShapeData.images.size() > 1) {
|
||||||
|
m_iCurrentAnimationFrame++;
|
||||||
|
|
||||||
g_pPointerManager->setCursorBuffer(getCursorBuffer(), icon.hotspot / scale, scale);
|
if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size())
|
||||||
|
m_iCurrentAnimationFrame = 0;
|
||||||
|
|
||||||
if (m_vCursorBuffers.size() > 1)
|
auto hotspot =
|
||||||
dropBufferRef(m_vCursorBuffers.at(0).get());
|
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY} / m_fCursorScale;
|
||||||
|
auto buf = makeShared<CCursorBuffer>(
|
||||||
m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(m_currentXcursor->images[m_iCurrentAnimationFrame].delay));
|
m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface,
|
||||||
|
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size},
|
||||||
return;
|
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY});
|
||||||
|
setCursorBuffer(buf, hotspot, m_fCursorScale);
|
||||||
|
setAnimationTimer(m_iCurrentAnimationFrame, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_sCurrentCursorShapeData.images.size() < 2 || !m_bOurBufferConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_iCurrentAnimationFrame++;
|
|
||||||
if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size())
|
|
||||||
m_iCurrentAnimationFrame = 0;
|
|
||||||
|
|
||||||
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>(
|
|
||||||
m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface,
|
|
||||||
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size},
|
|
||||||
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY}));
|
|
||||||
|
|
||||||
g_pPointerManager->setCursorBuffer(
|
|
||||||
getCursorBuffer(),
|
|
||||||
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY} / m_fCursorScale,
|
|
||||||
m_fCursorScale);
|
|
||||||
|
|
||||||
if (m_vCursorBuffers.size() > 1)
|
|
||||||
dropBufferRef(m_vCursorBuffers.at(0).get());
|
|
||||||
|
|
||||||
m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].delay));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SCursorImageData CCursorManager::dataFor(const std::string& name) {
|
SCursorImageData CCursorManager::dataFor(const std::string& name) {
|
||||||
|
@ -278,11 +271,12 @@ SCursorImageData CCursorManager::dataFor(const std::string& name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCursorManager::setXWaylandCursor() {
|
void CCursorManager::setXWaylandCursor() {
|
||||||
const auto CURSOR = dataFor("left_ptr");
|
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
||||||
if (CURSOR.surface) {
|
const auto CURSOR = dataFor("left_ptr");
|
||||||
|
if (CURSOR.surface && *PUSEHYPRCURSOR)
|
||||||
g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size},
|
g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size},
|
||||||
{CURSOR.hotspotX, CURSOR.hotspotY});
|
{CURSOR.hotspotX, CURSOR.hotspotY});
|
||||||
} else {
|
else {
|
||||||
auto xcursor = m_pXcursor->getShape("left_ptr", m_iSize * std::ceil(m_fCursorScale));
|
auto xcursor = m_pXcursor->getShape("left_ptr", m_iSize * std::ceil(m_fCursorScale));
|
||||||
auto& icon = xcursor->images.front();
|
auto& icon = xcursor->images.front();
|
||||||
|
|
||||||
|
@ -291,21 +285,25 @@ void CCursorManager::setXWaylandCursor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCursorManager::updateTheme() {
|
void CCursorManager::updateTheme() {
|
||||||
float highestScale = 1.0;
|
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
||||||
|
float highestScale = 1.0;
|
||||||
|
|
||||||
for (auto& m : g_pCompositor->m_vMonitors) {
|
for (auto& m : g_pCompositor->m_vMonitors) {
|
||||||
if (m->scale > highestScale)
|
if (m->scale > highestScale)
|
||||||
highestScale = m->scale;
|
highestScale = m->scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_sCurrentStyleInfo.size && m_pHyprcursor->valid())
|
m_fCursorScale = highestScale;
|
||||||
m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo);
|
|
||||||
|
|
||||||
m_sCurrentStyleInfo.size = std::round(m_iSize * highestScale);
|
if (*PUSEHYPRCURSOR) {
|
||||||
m_fCursorScale = highestScale;
|
if (m_sCurrentStyleInfo.size > 0 && m_pHyprcursor->valid())
|
||||||
|
m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo);
|
||||||
|
|
||||||
if (m_pHyprcursor->valid())
|
m_sCurrentStyleInfo.size = std::round(m_iSize * highestScale);
|
||||||
m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo);
|
|
||||||
|
if (m_pHyprcursor->valid())
|
||||||
|
m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo);
|
||||||
|
}
|
||||||
|
|
||||||
setCursorFromName("left_ptr");
|
setCursorFromName("left_ptr");
|
||||||
|
|
||||||
|
@ -316,24 +314,27 @@ void CCursorManager::updateTheme() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCursorManager::changeTheme(const std::string& name, const int size) {
|
bool CCursorManager::changeTheme(const std::string& name, const int size) {
|
||||||
auto options = Hyprcursor::SManagerOptions();
|
static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("cursor:enable_hyprcursor");
|
||||||
options.logFn = hcLogger;
|
m_szTheme = name.empty() ? "" : name;
|
||||||
options.allowDefaultFallback = false;
|
m_iSize = size <= 0 ? 24 : size;
|
||||||
|
auto xcursor_theme = getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default";
|
||||||
|
|
||||||
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(name.empty() ? "" : name.c_str(), options);
|
if (*PUSEHYPRCURSOR) {
|
||||||
if (m_pHyprcursor->valid()) {
|
auto options = Hyprcursor::SManagerOptions();
|
||||||
m_szTheme = name;
|
options.logFn = hcLogger;
|
||||||
m_iSize = size;
|
options.allowDefaultFallback = false;
|
||||||
updateTheme();
|
m_szTheme = name.empty() ? "" : name;
|
||||||
return true;
|
m_iSize = size;
|
||||||
}
|
|
||||||
|
|
||||||
Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to XCursor.", name);
|
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.empty() ? nullptr : m_szTheme.c_str(), options);
|
||||||
|
if (!m_pHyprcursor->valid()) {
|
||||||
|
Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to XCursor.", m_szTheme);
|
||||||
|
m_pXcursor->loadTheme(m_szTheme.empty() ? xcursor_theme : m_szTheme, m_iSize);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
m_pXcursor->loadTheme(m_szTheme.empty() ? xcursor_theme : m_szTheme, m_iSize);
|
||||||
|
|
||||||
m_pXcursor->loadTheme(name, size);
|
|
||||||
|
|
||||||
m_szTheme = name;
|
|
||||||
m_iSize = size;
|
|
||||||
updateTheme();
|
updateTheme();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
|
@ -15,6 +15,28 @@ class CWLSurface;
|
||||||
|
|
||||||
AQUAMARINE_FORWARD(IBuffer);
|
AQUAMARINE_FORWARD(IBuffer);
|
||||||
|
|
||||||
|
class CCursorBuffer : public Aquamarine::IBuffer {
|
||||||
|
public:
|
||||||
|
CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
|
||||||
|
CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
|
||||||
|
~CCursorBuffer() = default;
|
||||||
|
|
||||||
|
virtual Aquamarine::eBufferCapability caps();
|
||||||
|
virtual Aquamarine::eBufferType type();
|
||||||
|
virtual void update(const Hyprutils::Math::CRegion& damage);
|
||||||
|
virtual bool isSynchronous(); // whether the updates to this buffer are synchronous, aka happen over cpu
|
||||||
|
virtual bool good();
|
||||||
|
virtual Aquamarine::SSHMAttrs shm();
|
||||||
|
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||||
|
virtual void endDataPtr();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector2D hotspot;
|
||||||
|
cairo_surface_t* surface = nullptr;
|
||||||
|
uint8_t* pixelData = nullptr;
|
||||||
|
size_t stride = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class CCursorManager {
|
class CCursorManager {
|
||||||
public:
|
public:
|
||||||
CCursorManager();
|
CCursorManager();
|
||||||
|
@ -24,7 +46,8 @@ class CCursorManager {
|
||||||
|
|
||||||
void setCursorFromName(const std::string& name);
|
void setCursorFromName(const std::string& name);
|
||||||
void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot);
|
void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot);
|
||||||
void setXCursor(const std::string& name);
|
void setCursorBuffer(SP<CCursorBuffer> buf, const Vector2D& hotspot, const float& scale);
|
||||||
|
void setAnimationTimer(const int& frame, const int& delay);
|
||||||
|
|
||||||
bool changeTheme(const std::string& name, const int size);
|
bool changeTheme(const std::string& name, const int size);
|
||||||
void updateTheme();
|
void updateTheme();
|
||||||
|
@ -33,35 +56,8 @@ class CCursorManager {
|
||||||
|
|
||||||
void tickAnimatedCursor();
|
void tickAnimatedCursor();
|
||||||
|
|
||||||
class CCursorBuffer : public Aquamarine::IBuffer {
|
|
||||||
public:
|
|
||||||
CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
|
|
||||||
CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
|
|
||||||
~CCursorBuffer();
|
|
||||||
|
|
||||||
virtual Aquamarine::eBufferCapability caps();
|
|
||||||
virtual Aquamarine::eBufferType type();
|
|
||||||
virtual void update(const Hyprutils::Math::CRegion& damage);
|
|
||||||
virtual bool isSynchronous(); // whether the updates to this buffer are synchronous, aka happen over cpu
|
|
||||||
virtual bool good();
|
|
||||||
virtual Aquamarine::SSHMAttrs shm();
|
|
||||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
|
||||||
virtual void endDataPtr();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vector2D hotspot;
|
|
||||||
cairo_surface_t* surface = nullptr;
|
|
||||||
uint8_t* pixelData = nullptr;
|
|
||||||
size_t stride = 0;
|
|
||||||
|
|
||||||
friend class CCursorManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
void dropBufferRef(CCursorBuffer* ref);
|
|
||||||
|
|
||||||
bool m_bOurBufferConnected = false;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_bOurBufferConnected = false;
|
||||||
std::vector<SP<CCursorBuffer>> m_vCursorBuffers;
|
std::vector<SP<CCursorBuffer>> m_vCursorBuffers;
|
||||||
|
|
||||||
std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor;
|
std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor;
|
||||||
|
|
|
@ -135,6 +135,11 @@ void CXCursorManager::loadTheme(std::string const& name, int size) {
|
||||||
continue;
|
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>();
|
auto cursor = makeShared<SXCursors>();
|
||||||
cursor->images = it->get()->images;
|
cursor->images = it->get()->images;
|
||||||
cursor->shape = shape;
|
cursor->shape = shape;
|
||||||
|
@ -180,11 +185,10 @@ SP<SXCursors> CXCursorManager::createCursor(std::string const& shape, XcursorIma
|
||||||
return xcursor;
|
return xcursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> CXCursorManager::themePaths(std::string const& theme) {
|
std::unordered_set<std::string> CXCursorManager::themePaths(std::string const& theme) {
|
||||||
auto const* path = XcursorLibraryPath();
|
auto const* path = XcursorLibraryPath();
|
||||||
std::vector<std::string> paths;
|
|
||||||
|
|
||||||
auto expandTilde = [](std::string const& path) {
|
auto expandTilde = [](std::string const& path) {
|
||||||
if (!path.empty() && path[0] == '~') {
|
if (!path.empty() && path[0] == '~') {
|
||||||
const char* home = std::getenv("HOME");
|
const char* home = std::getenv("HOME");
|
||||||
if (home)
|
if (home)
|
||||||
|
@ -193,15 +197,76 @@ std::vector<std::string> CXCursorManager::themePaths(std::string const& theme) {
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (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)) {
|
||||||
|
// Trim leading and trailing whitespace
|
||||||
|
line.erase(0, line.find_first_not_of(" \t\n\r"));
|
||||||
|
line.erase(line.find_last_not_of(" \t\n\r") + 1);
|
||||||
|
|
||||||
|
if (line.rfind("Inherits", 0) == 0) { // Check if line starts with "Inherits"
|
||||||
|
std::string inheritThemes = line.substr(8); // Extract the part after "Inherits"
|
||||||
|
// Remove leading whitespace from inheritThemes and =
|
||||||
|
inheritThemes.erase(0, inheritThemes.find_first_not_of(" \t\n\r"));
|
||||||
|
inheritThemes.erase(0, 1);
|
||||||
|
inheritThemes.erase(0, inheritThemes.find_first_not_of(" \t\n\r"));
|
||||||
|
|
||||||
|
std::stringstream inheritStream(inheritThemes);
|
||||||
|
std::string inheritTheme;
|
||||||
|
while (std::getline(inheritStream, inheritTheme, ',')) {
|
||||||
|
// Trim leading and trailing whitespace from each theme
|
||||||
|
inheritTheme.erase(0, inheritTheme.find_first_not_of(" \t\n\r"));
|
||||||
|
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::stringstream ss(path);
|
||||||
std::string item;
|
std::string line;
|
||||||
|
|
||||||
while (std::getline(ss, item, ':')) {
|
Debug::log(LOG, "XCursor scanning theme {}", t);
|
||||||
auto p = expandTilde(item + "/" + theme + "/cursors");
|
|
||||||
|
|
||||||
if (std::filesystem::exists(p) && std::filesystem::is_directory(p))
|
while (std::getline(ss, line, ':')) {
|
||||||
paths.push_back(p);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,14 +467,13 @@ std::vector<SP<SXCursors>> CXCursorManager::loadStandardCursors(std::string cons
|
||||||
|
|
||||||
std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& path, int size) {
|
std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& path, int size) {
|
||||||
std::vector<SP<SXCursors>> newCursors;
|
std::vector<SP<SXCursors>> newCursors;
|
||||||
std::string full;
|
|
||||||
|
|
||||||
if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) {
|
if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) {
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(path)) {
|
for (const auto& entry : std::filesystem::directory_iterator(path)) {
|
||||||
if (!entry.is_regular_file() && !entry.is_symlink())
|
if (!entry.is_regular_file() && !entry.is_symlink())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string full = entry.path().string();
|
auto const& full = entry.path().string();
|
||||||
using PcloseType = int (*)(FILE*);
|
using PcloseType = int (*)(FILE*);
|
||||||
const std::unique_ptr<FILE, PcloseType> f(fopen(full.c_str(), "r"), static_cast<PcloseType>(fclose));
|
const std::unique_ptr<FILE, PcloseType> f(fopen(full.c_str(), "r"), static_cast<PcloseType>(fclose));
|
||||||
|
|
||||||
|
@ -428,7 +492,7 @@ std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string shape = entry.path().filename().string();
|
auto const& shape = entry.path().filename().string();
|
||||||
auto cursor = createCursor(shape, xImages);
|
auto cursor = createCursor(shape, xImages);
|
||||||
newCursors.emplace_back(cursor);
|
newCursors.emplace_back(cursor);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <hyprutils/math/Vector2D.hpp>
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
@ -32,15 +33,15 @@ class CXCursorManager {
|
||||||
SP<SXCursors> getShape(std::string const& shape, int size);
|
SP<SXCursors> getShape(std::string const& shape, int size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SP<SXCursors> createCursor(std::string const& shape, XcursorImages* xImages);
|
SP<SXCursors> createCursor(std::string const& shape, XcursorImages* xImages);
|
||||||
std::vector<std::string> themePaths(std::string const& theme);
|
std::unordered_set<std::string> themePaths(std::string const& theme);
|
||||||
std::string getLegacyShapeName(std::string const& shape);
|
std::string getLegacyShapeName(std::string const& shape);
|
||||||
std::vector<SP<SXCursors>> loadStandardCursors(std::string const& name, int size);
|
std::vector<SP<SXCursors>> loadStandardCursors(std::string const& name, int size);
|
||||||
std::vector<SP<SXCursors>> loadAllFromDir(std::string const& path, int size);
|
std::vector<SP<SXCursors>> loadAllFromDir(std::string const& path, int size);
|
||||||
|
|
||||||
int lastLoadSize = 0;
|
int lastLoadSize = 0;
|
||||||
std::string themeName = "";
|
std::string themeName = "";
|
||||||
SP<SXCursors> defaultCursor;
|
SP<SXCursors> defaultCursor;
|
||||||
SP<SXCursors> hyprCursor;
|
SP<SXCursors> hyprCursor;
|
||||||
std::vector<SP<SXCursors>> cursors;
|
std::vector<SP<SXCursors>> cursors;
|
||||||
};
|
};
|
Loading…
Reference in a new issue