platforms/drm: Use a software cursor if the cursor image is too big
When dragging files on the desktop, the cursor image might be just too big for the cursor plane, in which case we need to abandon hardware cursors for a brief moment and use a software cursor. Once the files have been dropped and the cursor image is small enough, we can go back to using hw cursors. BUG: 424589
This commit is contained in:
parent
d5ee009ba5
commit
5442762371
9 changed files with 110 additions and 40 deletions
33
platform.cpp
33
platform.cpp
|
@ -37,7 +37,7 @@ Platform::Platform(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_eglDisplay(EGL_NO_DISPLAY)
|
, m_eglDisplay(EGL_NO_DISPLAY)
|
||||||
{
|
{
|
||||||
setSoftwareCursor(false);
|
setSoftwareCursorForced(false);
|
||||||
m_colorCorrect = new ColorCorrect::Manager(this);
|
m_colorCorrect = new ColorCorrect::Manager(this);
|
||||||
connect(Cursors::self(), &Cursors::currentCursorRendered, this, &Platform::cursorRendered);
|
connect(Cursors::self(), &Cursors::currentCursorRendered, this, &Platform::cursorRendered);
|
||||||
}
|
}
|
||||||
|
@ -188,13 +188,11 @@ bool Platform::usesSoftwareCursor() const
|
||||||
|
|
||||||
void Platform::setSoftwareCursor(bool set)
|
void Platform::setSoftwareCursor(bool set)
|
||||||
{
|
{
|
||||||
if (qEnvironmentVariableIsSet("KWIN_FORCE_SW_CURSOR")) {
|
|
||||||
set = true;
|
|
||||||
}
|
|
||||||
if (m_softwareCursor == set) {
|
if (m_softwareCursor == set) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_softwareCursor = set;
|
m_softwareCursor = set;
|
||||||
|
doSetSoftwareCursor();
|
||||||
if (m_softwareCursor) {
|
if (m_softwareCursor) {
|
||||||
connect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint);
|
connect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint);
|
||||||
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint);
|
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint);
|
||||||
|
@ -205,6 +203,33 @@ void Platform::setSoftwareCursor(bool set)
|
||||||
triggerCursorRepaint();
|
triggerCursorRepaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Platform::doSetSoftwareCursor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Platform::isSoftwareCursorForced() const
|
||||||
|
{
|
||||||
|
return m_softwareCursorForced;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Platform::setSoftwareCursorForced(bool forced)
|
||||||
|
{
|
||||||
|
if (qEnvironmentVariableIsSet("KWIN_FORCE_SW_CURSOR")) {
|
||||||
|
forced = true;
|
||||||
|
}
|
||||||
|
if (m_softwareCursorForced == forced) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_softwareCursorForced = forced;
|
||||||
|
if (m_softwareCursorForced) {
|
||||||
|
setSoftwareCursor(true);
|
||||||
|
} else {
|
||||||
|
// Do not unset the software cursor yet, the platform will choose the right
|
||||||
|
// moment when it can be done. There is still a chance that we must continue
|
||||||
|
// using the software cursor.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Platform::triggerCursorRepaint()
|
void Platform::triggerCursorRepaint()
|
||||||
{
|
{
|
||||||
if (!Compositor::self()) {
|
if (!Compositor::self()) {
|
||||||
|
|
18
platform.h
18
platform.h
|
@ -284,6 +284,21 @@ public:
|
||||||
*/
|
*/
|
||||||
bool usesSoftwareCursor() const;
|
bool usesSoftwareCursor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns @c true if the software cursor is being forced; otherwise returns @c false.
|
||||||
|
*
|
||||||
|
* Note that the value returned by this function not always matches usesSoftwareCursor().
|
||||||
|
* If this function returns @c true, then it is guaranteed that the compositor will
|
||||||
|
* use the software cursor. However, this doesn't apply vice versa.
|
||||||
|
*
|
||||||
|
* If the compositor uses a software cursor, this function may return @c false. This
|
||||||
|
* is typically the case if the current cursor image can't be displayed using hardware
|
||||||
|
* cursors, for example due to buffer size limitations, etc.
|
||||||
|
*
|
||||||
|
* @see usesSoftwareCursor()
|
||||||
|
*/
|
||||||
|
bool isSoftwareCursorForced() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a PlatformCursorImage. By default this is created by softwareCursor and
|
* Returns a PlatformCursorImage. By default this is created by softwareCursor and
|
||||||
* softwareCursorHotspot. An implementing subclass can use this to provide a better
|
* softwareCursorHotspot. An implementing subclass can use this to provide a better
|
||||||
|
@ -487,6 +502,7 @@ Q_SIGNALS:
|
||||||
protected:
|
protected:
|
||||||
explicit Platform(QObject *parent = nullptr);
|
explicit Platform(QObject *parent = nullptr);
|
||||||
void setSoftwareCursor(bool set);
|
void setSoftwareCursor(bool set);
|
||||||
|
void setSoftwareCursorForced(bool forced);
|
||||||
void repaint(const QRect &rect);
|
void repaint(const QRect &rect);
|
||||||
void setReady(bool ready);
|
void setReady(bool ready);
|
||||||
QSize initialWindowSize() const {
|
QSize initialWindowSize() const {
|
||||||
|
@ -532,10 +548,12 @@ protected:
|
||||||
* @see showCursor
|
* @see showCursor
|
||||||
*/
|
*/
|
||||||
virtual void doShowCursor();
|
virtual void doShowCursor();
|
||||||
|
virtual void doSetSoftwareCursor();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void triggerCursorRepaint();
|
void triggerCursorRepaint();
|
||||||
bool m_softwareCursor = false;
|
bool m_softwareCursor = false;
|
||||||
|
bool m_softwareCursorForced = false;
|
||||||
struct {
|
struct {
|
||||||
QRect lastRenderedGeometry;
|
QRect lastRenderedGeometry;
|
||||||
} m_cursor;
|
} m_cursor;
|
||||||
|
|
|
@ -506,7 +506,7 @@ void DrmBackend::initCursor()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSoftwareCursor(needsSoftwareCursor);
|
setSoftwareCursorForced(needsSoftwareCursor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (waylandServer()->seat()->hasPointer()) {
|
if (waylandServer()->seat()->hasPointer()) {
|
||||||
|
@ -529,41 +529,42 @@ void DrmBackend::initCursor()
|
||||||
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmBackend::moveCursor);
|
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmBackend::moveCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmBackend::setCursor()
|
|
||||||
{
|
|
||||||
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
|
|
||||||
if (!(*it)->showCursor()) {
|
|
||||||
setSoftwareCursor(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrmBackend::updateCursor()
|
void DrmBackend::updateCursor()
|
||||||
{
|
{
|
||||||
if (usesSoftwareCursor()) {
|
if (isSoftwareCursorForced() || isCursorHidden()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isCursorHidden()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cursor = Cursors::self()->currentCursor();
|
auto cursor = Cursors::self()->currentCursor();
|
||||||
const QImage &cursorImage = cursor->image();
|
if (cursor->image().isNull()) {
|
||||||
if (cursorImage.isNull()) {
|
|
||||||
doHideCursor();
|
doHideCursor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
|
|
||||||
(*it)->updateCursor();
|
bool success = true;
|
||||||
|
|
||||||
|
for (DrmOutput *output : qAsConst(m_outputs)) {
|
||||||
|
success = output->updateCursor();
|
||||||
|
if (!success) {
|
||||||
|
qCDebug(KWIN_DRM) << "Failed to update cursor on output" << output->name();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
success = output->showCursor();
|
||||||
|
if (!success) {
|
||||||
|
qCDebug(KWIN_DRM) << "Failed to show cursor on output" << output->name();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
output->moveCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursor();
|
setSoftwareCursor(!success);
|
||||||
|
|
||||||
moveCursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmBackend::doShowCursor()
|
void DrmBackend::doShowCursor()
|
||||||
{
|
{
|
||||||
|
if (usesSoftwareCursor()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
updateCursor();
|
updateCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,6 +578,16 @@ void DrmBackend::doHideCursor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrmBackend::doSetSoftwareCursor()
|
||||||
|
{
|
||||||
|
if (isCursorHidden() || !usesSoftwareCursor()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
|
||||||
|
(*it)->hideCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DrmBackend::moveCursor()
|
void DrmBackend::moveCursor()
|
||||||
{
|
{
|
||||||
if (isCursorHidden() || usesSoftwareCursor()) {
|
if (isCursorHidden() || usesSoftwareCursor()) {
|
||||||
|
|
|
@ -88,6 +88,8 @@ public Q_SLOTS:
|
||||||
protected:
|
protected:
|
||||||
void doHideCursor() override;
|
void doHideCursor() override;
|
||||||
void doShowCursor() override;
|
void doShowCursor() override;
|
||||||
|
void doSetSoftwareCursor() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmGpu;
|
friend class DrmGpu;
|
||||||
void addOutput(DrmOutput* output);
|
void addOutput(DrmOutput* output);
|
||||||
|
@ -98,7 +100,6 @@ private:
|
||||||
void reactivate();
|
void reactivate();
|
||||||
void deactivate();
|
void deactivate();
|
||||||
bool updateOutputs();
|
bool updateOutputs();
|
||||||
void setCursor();
|
|
||||||
void updateCursor();
|
void updateCursor();
|
||||||
void moveCursor();
|
void moveCursor();
|
||||||
void initCursor();
|
void initCursor();
|
||||||
|
|
|
@ -252,7 +252,7 @@ bool DrmGpu::updateOutputs()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!output->initCursor(m_cursorSize)) {
|
if (!output->initCursor(m_cursorSize)) {
|
||||||
m_backend->setSoftwareCursor(true);
|
m_backend->setSoftwareCursorForced(true);
|
||||||
}
|
}
|
||||||
qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid();
|
qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid();
|
||||||
|
|
||||||
|
|
|
@ -112,11 +112,6 @@ bool DrmOutput::showCursor()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Q_UNLIKELY(m_backend->usesSoftwareCursor())) {
|
|
||||||
qCCritical(KWIN_DRM) << "DrmOutput::showCursor should never be called when software cursor is enabled";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool ret = showCursor(m_cursor[m_cursorIndex].data());
|
const bool ret = showCursor(m_cursor[m_cursorIndex].data());
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
qCDebug(KWIN_DRM) << "DrmOutput::showCursor(DrmDumbBuffer) failed";
|
qCDebug(KWIN_DRM) << "DrmOutput::showCursor(DrmDumbBuffer) failed";
|
||||||
|
@ -131,25 +126,45 @@ bool DrmOutput::showCursor()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::updateCursor()
|
static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite)
|
||||||
|
{
|
||||||
|
// Note that we need compare the rects in the device independent pixels because the
|
||||||
|
// buffer and the cursor sprite image may have different scale factors.
|
||||||
|
const QRect bufferRect(QPoint(0, 0), buffer->size() / buffer->devicePixelRatio());
|
||||||
|
const QRect spriteRect(QPoint(0, 0), sprite->size() / sprite->devicePixelRatio());
|
||||||
|
|
||||||
|
return bufferRect.contains(spriteRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrmOutput::updateCursor()
|
||||||
{
|
{
|
||||||
if (m_deleted) {
|
if (m_deleted) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const Cursor *cursor = Cursors::self()->currentCursor();
|
const Cursor *cursor = Cursors::self()->currentCursor();
|
||||||
const QImage cursorImage = cursor->image();
|
const QImage cursorImage = cursor->image();
|
||||||
if (cursorImage.isNull()) {
|
if (cursorImage.isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
m_hasNewCursor = true;
|
|
||||||
QImage *c = m_cursor[m_cursorIndex]->image();
|
QImage *c = m_cursor[m_cursorIndex]->image();
|
||||||
|
c->setDevicePixelRatio(scale());
|
||||||
|
|
||||||
|
if (!isCursorSpriteCompatible(c, &cursorImage)) {
|
||||||
|
// If the cursor image is too big, fall back to rendering the software cursor.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hasNewCursor = true;
|
||||||
c->fill(Qt::transparent);
|
c->fill(Qt::transparent);
|
||||||
|
|
||||||
QPainter p;
|
QPainter p;
|
||||||
p.begin(c);
|
p.begin(c);
|
||||||
p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), scale(), transform()).toTransform());
|
p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform());
|
||||||
p.drawImage(QPoint(0, 0), cursorImage);
|
p.drawImage(QPoint(0, 0), cursorImage);
|
||||||
p.end();
|
p.end();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::moveCursor()
|
void DrmOutput::moveCursor()
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
bool showCursor(DrmDumbBuffer *buffer);
|
bool showCursor(DrmDumbBuffer *buffer);
|
||||||
bool showCursor();
|
bool showCursor();
|
||||||
bool hideCursor();
|
bool hideCursor();
|
||||||
void updateCursor();
|
bool updateCursor();
|
||||||
void moveCursor();
|
void moveCursor();
|
||||||
bool init(drmModeConnector *connector);
|
bool init(drmModeConnector *connector);
|
||||||
bool present(DrmBuffer *buffer);
|
bool present(DrmBuffer *buffer);
|
||||||
|
|
|
@ -67,7 +67,7 @@ QPainterBackend *FramebufferBackend::createQPainterBackend()
|
||||||
|
|
||||||
void FramebufferBackend::init()
|
void FramebufferBackend::init()
|
||||||
{
|
{
|
||||||
setSoftwareCursor(true);
|
setSoftwareCursorForced(true);
|
||||||
LogindIntegration *logind = LogindIntegration::self();
|
LogindIntegration *logind = LogindIntegration::self();
|
||||||
auto takeControl = [logind, this]() {
|
auto takeControl = [logind, this]() {
|
||||||
if (logind->hasSessionControl()) {
|
if (logind->hasSessionControl()) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ void VirtualBackend::init()
|
||||||
m_enabledOutputs << dummyOutput ;
|
m_enabledOutputs << dummyOutput ;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSoftwareCursor(true);
|
setSoftwareCursorForced(true);
|
||||||
setReady(true);
|
setReady(true);
|
||||||
waylandServer()->seat()->setHasPointer(true);
|
waylandServer()->seat()->setHasPointer(true);
|
||||||
waylandServer()->seat()->setHasKeyboard(true);
|
waylandServer()->seat()->setHasKeyboard(true);
|
||||||
|
|
Loading…
Reference in a new issue