diff --git a/cursor.cpp b/cursor.cpp index 47e7b1b81..d0fe596e3 100644 --- a/cursor.cpp +++ b/cursor.cpp @@ -1,478 +1,478 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "cursor.h" // kwin #include #include "input.h" #include "keyboard_input.h" #include "main.h" #include "platform.h" #include "utils.h" #include "xcbutils.h" // KDE #include #include #include // Qt #include #include #include #include namespace KWin { Cursors *Cursors::s_self = nullptr; Cursors *Cursors::self() { if (!s_self) s_self = new Cursors; return s_self; } void Cursors::addCursor(Cursor* cursor) { Q_ASSERT(!m_cursors.contains(cursor)); m_cursors += cursor; connect(cursor, &Cursor::posChanged, this, [this, cursor] (const QPoint &pos) { setCurrentCursor(cursor); Q_EMIT positionChanged(cursor, pos); }); } void Cursors::removeCursor(Cursor* cursor) { m_cursors.removeOne(cursor); if (m_currentCursor == cursor) { if (m_cursors.isEmpty()) m_currentCursor = nullptr; else setCurrentCursor(m_cursors.constFirst()); } if (m_mouse == cursor) { m_mouse = nullptr; } } void Cursors::setCurrentCursor(Cursor* cursor) { if (m_currentCursor == cursor) return; Q_ASSERT(m_cursors.contains(cursor) || !cursor); if (m_currentCursor) { disconnect(m_currentCursor, &Cursor::rendered, this, &Cursors::currentCursorRendered); disconnect(m_currentCursor, &Cursor::cursorChanged, this, &Cursors::emitCurrentCursorChanged); } m_currentCursor = cursor; connect(m_currentCursor, &Cursor::rendered, this, &Cursors::currentCursorRendered); connect(m_currentCursor, &Cursor::cursorChanged, this, &Cursors::emitCurrentCursorChanged); Q_EMIT currentCursorChanged(m_currentCursor); } void Cursors::emitCurrentCursorChanged() { Q_EMIT currentCursorChanged(m_currentCursor); } Cursor::Cursor(QObject *parent) : QObject(parent) , m_mousePollingCounter(0) , m_cursorTrackingCounter(0) , m_themeName("default") , m_themeSize(24) { loadThemeSettings(); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange"), this, SLOT(slotKGlobalSettingsNotifyChange(int,int))); } Cursor::~Cursor() { Cursors::self()->removeCursor(this); } void Cursor::loadThemeSettings() { QString themeName = QString::fromUtf8(qgetenv("XCURSOR_THEME")); bool ok = false; // XCURSOR_SIZE might not be set (e.g. by startkde) const uint themeSize = qEnvironmentVariableIntValue("XCURSOR_SIZE", &ok); if (!themeName.isEmpty() && ok) { updateTheme(themeName, themeSize); return; } // didn't get from environment variables, read from config file loadThemeFromKConfig(); } void Cursor::loadThemeFromKConfig() { KConfigGroup mousecfg(kwinApp()->inputConfig(), "Mouse"); const QString themeName = mousecfg.readEntry("cursorTheme", "default"); - const uint themeSize = mousecfg.readEntry("cursorSize", 0); + const uint themeSize = mousecfg.readEntry("cursorSize", 24); updateTheme(themeName, themeSize); } void Cursor::updateTheme(const QString &name, int size) { if (m_themeName != name || m_themeSize != size) { m_themeName = name; m_themeSize = size; emit themeChanged(); } } void Cursor::slotKGlobalSettingsNotifyChange(int type, int arg) { Q_UNUSED(arg) if (type == 5 /*CursorChanged*/) { kwinApp()->inputConfig()->reparseConfiguration(); loadThemeFromKConfig(); // sync to environment qputenv("XCURSOR_THEME", m_themeName.toUtf8()); qputenv("XCURSOR_SIZE", QByteArray::number(m_themeSize)); } } QRect Cursor::geometry() const { return QRect(m_pos - hotspot(), image().size()); } QPoint Cursor::pos() { doGetPos(); return m_pos; } void Cursor::setPos(const QPoint &pos) { // first query the current pos to not warp to the already existing pos if (pos == m_pos) { return; } m_pos = pos; doSetPos(); } void Cursor::setPos(int x, int y) { setPos(QPoint(x, y)); } void Cursor::updateCursor(const QImage &image, const QPoint &hotspot) { m_image = image; m_hotspot = hotspot; Q_EMIT cursorChanged(); } xcb_cursor_t Cursor::getX11Cursor(CursorShape shape) { Q_UNUSED(shape) return XCB_CURSOR_NONE; } xcb_cursor_t Cursor::getX11Cursor(const QByteArray &name) { Q_UNUSED(name) return XCB_CURSOR_NONE; } xcb_cursor_t Cursor::x11Cursor(CursorShape shape) { return getX11Cursor(shape); } xcb_cursor_t Cursor::x11Cursor(const QByteArray &name) { return getX11Cursor(name); } void Cursor::doSetPos() { emit posChanged(m_pos); } void Cursor::doGetPos() { } void Cursor::updatePos(const QPoint &pos) { if (m_pos == pos) { return; } m_pos = pos; emit posChanged(m_pos); } void Cursor::startMousePolling() { ++m_mousePollingCounter; if (m_mousePollingCounter == 1) { doStartMousePolling(); } } void Cursor::stopMousePolling() { Q_ASSERT(m_mousePollingCounter > 0); --m_mousePollingCounter; if (m_mousePollingCounter == 0) { doStopMousePolling(); } } void Cursor::doStartMousePolling() { } void Cursor::doStopMousePolling() { } void Cursor::startCursorTracking() { ++m_cursorTrackingCounter; if (m_cursorTrackingCounter == 1) { doStartCursorTracking(); } } void Cursor::stopCursorTracking() { Q_ASSERT(m_cursorTrackingCounter > 0); --m_cursorTrackingCounter; if (m_cursorTrackingCounter == 0) { doStopCursorTracking(); } } void Cursor::doStartCursorTracking() { } void Cursor::doStopCursorTracking() { } QVector Cursor::cursorAlternativeNames(const QByteArray &name) const { static const QHash> alternatives = { {QByteArrayLiteral("left_ptr"), {QByteArrayLiteral("arrow"), QByteArrayLiteral("dnd-none"), QByteArrayLiteral("op_left_arrow")}}, {QByteArrayLiteral("cross"), {QByteArrayLiteral("crosshair"), QByteArrayLiteral("diamond-cross"), QByteArrayLiteral("cross-reverse")}}, {QByteArrayLiteral("up_arrow"), {QByteArrayLiteral("center_ptr"), QByteArrayLiteral("sb_up_arrow"), QByteArrayLiteral("centre_ptr")}}, {QByteArrayLiteral("wait"), {QByteArrayLiteral("watch"), QByteArrayLiteral("progress")}}, {QByteArrayLiteral("ibeam"), {QByteArrayLiteral("xterm"), QByteArrayLiteral("text")}}, {QByteArrayLiteral("size_all"), {QByteArrayLiteral("fleur")}}, {QByteArrayLiteral("pointing_hand"), {QByteArrayLiteral("hand2"), QByteArrayLiteral("hand"), QByteArrayLiteral("hand1"), QByteArrayLiteral("pointer"), QByteArrayLiteral("e29285e634086352946a0e7090d73106"), QByteArrayLiteral("9d800788f1b08800ae810202380a0822")}}, {QByteArrayLiteral("size_ver"), {QByteArrayLiteral("00008160000006810000408080010102"), QByteArrayLiteral("sb_v_double_arrow"), QByteArrayLiteral("v_double_arrow"), QByteArrayLiteral("n-resize"), QByteArrayLiteral("s-resize"), QByteArrayLiteral("col-resize"), QByteArrayLiteral("top_side"), QByteArrayLiteral("bottom_side"), QByteArrayLiteral("base_arrow_up"), QByteArrayLiteral("base_arrow_down"), QByteArrayLiteral("based_arrow_down"), QByteArrayLiteral("based_arrow_up")}}, {QByteArrayLiteral("size_hor"), {QByteArrayLiteral("028006030e0e7ebffc7f7070c0600140"), QByteArrayLiteral("sb_h_double_arrow"), QByteArrayLiteral("h_double_arrow"), QByteArrayLiteral("e-resize"), QByteArrayLiteral("w-resize"), QByteArrayLiteral("row-resize"), QByteArrayLiteral("right_side"), QByteArrayLiteral("left_side")}}, {QByteArrayLiteral("size_bdiag"), {QByteArrayLiteral("fcf1c3c7cd4491d801f1e1c78f100000"), QByteArrayLiteral("fd_double_arrow"), QByteArrayLiteral("bottom_left_corner"), QByteArrayLiteral("top_right_corner")}}, {QByteArrayLiteral("size_fdiag"), {QByteArrayLiteral("c7088f0f3e6c8088236ef8e1e3e70000"), QByteArrayLiteral("bd_double_arrow"), QByteArrayLiteral("bottom_right_corner"), QByteArrayLiteral("top_left_corner")}}, {QByteArrayLiteral("whats_this"), {QByteArrayLiteral("d9ce0ab605698f320427677b458ad60b"), QByteArrayLiteral("left_ptr_help"), QByteArrayLiteral("help"), QByteArrayLiteral("question_arrow"), QByteArrayLiteral("dnd-ask"), QByteArrayLiteral("5c6cd98b3f3ebcb1f9c7f1c204630408")}}, {QByteArrayLiteral("split_h"), {QByteArrayLiteral("14fef782d02440884392942c11205230"), QByteArrayLiteral("size_hor")}}, {QByteArrayLiteral("split_v"), {QByteArrayLiteral("2870a09082c103050810ffdffffe0204"), QByteArrayLiteral("size_ver")}}, {QByteArrayLiteral("forbidden"), {QByteArrayLiteral("03b6e0fcb3499374a867c041f52298f0"), QByteArrayLiteral("circle"), QByteArrayLiteral("dnd-no-drop"), QByteArrayLiteral("not-allowed")}}, {QByteArrayLiteral("left_ptr_watch"), {QByteArrayLiteral("3ecb610c1bf2410f44200f48c40d3599"), QByteArrayLiteral("00000000000000020006000e7e9ffc3f"), QByteArrayLiteral("08e8e1c95fe2fc01f976f1e063a24ccd")}}, {QByteArrayLiteral("openhand"), {QByteArrayLiteral("9141b49c8149039304290b508d208c40"), QByteArrayLiteral("all_scroll"), QByteArrayLiteral("all-scroll")}}, {QByteArrayLiteral("closedhand"), {QByteArrayLiteral("05e88622050804100c20044008402080"), QByteArrayLiteral("4498f0e0c1937ffe01fd06f973665830"), QByteArrayLiteral("9081237383d90e509aa00f00170e968f"), QByteArrayLiteral("fcf21c00b30f7e3f83fe0dfd12e71cff")}}, {QByteArrayLiteral("dnd-link"), {QByteArrayLiteral("link"), QByteArrayLiteral("alias"), QByteArrayLiteral("3085a0e285430894940527032f8b26df"), QByteArrayLiteral("640fb0e74195791501fd1ed57b41487f"), QByteArrayLiteral("a2a266d0498c3104214a47bd64ab0fc8")}}, {QByteArrayLiteral("dnd-copy"), {QByteArrayLiteral("copy"), QByteArrayLiteral("1081e37283d90000800003c07f3ef6bf"), QByteArrayLiteral("6407b0e94181790501fd1e167b474872"), QByteArrayLiteral("b66166c04f8c3109214a4fbd64a50fc8")}}, {QByteArrayLiteral("dnd-move"), {QByteArrayLiteral("move")}}, {QByteArrayLiteral("sw-resize"), {QByteArrayLiteral("size_bdiag"), QByteArrayLiteral("fcf1c3c7cd4491d801f1e1c78f100000"), QByteArrayLiteral("fd_double_arrow"), QByteArrayLiteral("bottom_left_corner")}}, {QByteArrayLiteral("se-resize"), {QByteArrayLiteral("size_fdiag"), QByteArrayLiteral("c7088f0f3e6c8088236ef8e1e3e70000"), QByteArrayLiteral("bd_double_arrow"), QByteArrayLiteral("bottom_right_corner")}}, {QByteArrayLiteral("ne-resize"), {QByteArrayLiteral("size_bdiag"), QByteArrayLiteral("fcf1c3c7cd4491d801f1e1c78f100000"), QByteArrayLiteral("fd_double_arrow"), QByteArrayLiteral("top_right_corner")}}, {QByteArrayLiteral("nw-resize"), {QByteArrayLiteral("size_fdiag"), QByteArrayLiteral("c7088f0f3e6c8088236ef8e1e3e70000"), QByteArrayLiteral("bd_double_arrow"), QByteArrayLiteral("top_left_corner")}}, {QByteArrayLiteral("n-resize"), {QByteArrayLiteral("size_ver"), QByteArrayLiteral("00008160000006810000408080010102"), QByteArrayLiteral("sb_v_double_arrow"), QByteArrayLiteral("v_double_arrow"), QByteArrayLiteral("col-resize"), QByteArrayLiteral("top_side")}}, {QByteArrayLiteral("e-resize"), {QByteArrayLiteral("size_hor"), QByteArrayLiteral("028006030e0e7ebffc7f7070c0600140"), QByteArrayLiteral("sb_h_double_arrow"), QByteArrayLiteral("h_double_arrow"), QByteArrayLiteral("row-resize"), QByteArrayLiteral("left_side")}}, {QByteArrayLiteral("s-resize"), {QByteArrayLiteral("size_ver"), QByteArrayLiteral("00008160000006810000408080010102"), QByteArrayLiteral("sb_v_double_arrow"), QByteArrayLiteral("v_double_arrow"), QByteArrayLiteral("col-resize"), QByteArrayLiteral("bottom_side")}}, {QByteArrayLiteral("w-resize"), {QByteArrayLiteral("size_hor"), QByteArrayLiteral("028006030e0e7ebffc7f7070c0600140"), QByteArrayLiteral("sb_h_double_arrow"), QByteArrayLiteral("h_double_arrow"), QByteArrayLiteral("right_side")}} }; auto it = alternatives.find(name); if (it != alternatives.end()) { return it.value(); } return QVector(); } QByteArray CursorShape::name() const { switch (m_shape) { case Qt::ArrowCursor: return QByteArrayLiteral("left_ptr"); case Qt::UpArrowCursor: return QByteArrayLiteral("up_arrow"); case Qt::CrossCursor: return QByteArrayLiteral("cross"); case Qt::WaitCursor: return QByteArrayLiteral("wait"); case Qt::IBeamCursor: return QByteArrayLiteral("ibeam"); case Qt::SizeVerCursor: return QByteArrayLiteral("size_ver"); case Qt::SizeHorCursor: return QByteArrayLiteral("size_hor"); case Qt::SizeBDiagCursor: return QByteArrayLiteral("size_bdiag"); case Qt::SizeFDiagCursor: return QByteArrayLiteral("size_fdiag"); case Qt::SizeAllCursor: return QByteArrayLiteral("size_all"); case Qt::SplitVCursor: return QByteArrayLiteral("split_v"); case Qt::SplitHCursor: return QByteArrayLiteral("split_h"); case Qt::PointingHandCursor: return QByteArrayLiteral("pointing_hand"); case Qt::ForbiddenCursor: return QByteArrayLiteral("forbidden"); case Qt::OpenHandCursor: return QByteArrayLiteral("openhand"); case Qt::ClosedHandCursor: return QByteArrayLiteral("closedhand"); case Qt::WhatsThisCursor: return QByteArrayLiteral("whats_this"); case Qt::BusyCursor: return QByteArrayLiteral("left_ptr_watch"); case Qt::DragMoveCursor: return QByteArrayLiteral("dnd-move"); case Qt::DragCopyCursor: return QByteArrayLiteral("dnd-copy"); case Qt::DragLinkCursor: return QByteArrayLiteral("dnd-link"); case KWin::ExtendedCursor::SizeNorthEast: return QByteArrayLiteral("ne-resize"); case KWin::ExtendedCursor::SizeNorth: return QByteArrayLiteral("n-resize"); case KWin::ExtendedCursor::SizeNorthWest: return QByteArrayLiteral("nw-resize"); case KWin::ExtendedCursor::SizeEast: return QByteArrayLiteral("e-resize"); case KWin::ExtendedCursor::SizeWest: return QByteArrayLiteral("w-resize"); case KWin::ExtendedCursor::SizeSouthEast: return QByteArrayLiteral("se-resize"); case KWin::ExtendedCursor::SizeSouth: return QByteArrayLiteral("s-resize"); case KWin::ExtendedCursor::SizeSouthWest: return QByteArrayLiteral("sw-resize"); default: return QByteArray(); } } } // namespace diff --git a/effects/startupfeedback/startupfeedback.cpp b/effects/startupfeedback/startupfeedback.cpp index 0b100bc4a..5f1264ea1 100644 --- a/effects/startupfeedback/startupfeedback.cpp +++ b/effects/startupfeedback/startupfeedback.cpp @@ -1,405 +1,399 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "startupfeedback.h" // Qt #include #include #include #include #include #include // KDE #include #include #include #include #include // KWin #include // based on StartupId in KRunner by Lubos Lunak // Copyright (C) 2001 Lubos Lunak namespace KWin { // number of key frames for bouncing animation static const int BOUNCE_FRAMES = 20; // duration between two key frames in msec static const int BOUNCE_FRAME_DURATION = 30; // duration of one bounce animation static const int BOUNCE_DURATION = BOUNCE_FRAME_DURATION * BOUNCE_FRAMES; // number of key frames for blinking animation static const int BLINKING_FRAMES = 5; // duration between two key frames in msec static const int BLINKING_FRAME_DURATION = 100; // duration of one blinking animation static const int BLINKING_DURATION = BLINKING_FRAME_DURATION * BLINKING_FRAMES; //const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 }; static const int FRAME_TO_BOUNCE_YOFFSET[] = { -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5 }; static const QSize BOUNCE_SIZES[] = { QSize(16, 16), QSize(14, 18), QSize(12, 20), QSize(18, 14), QSize(20, 12) }; static const int FRAME_TO_BOUNCE_TEXTURE[] = { 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0 }; static const int FRAME_TO_BLINKING_COLOR[] = { 0, 1, 2, 3, 2, 1 }; static const QColor BLINKING_COLORS[] = { Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white }; static const int s_startupDefaultTimeout = 5; StartupFeedbackEffect::StartupFeedbackEffect() : m_bounceSizesRatio(1.0) , m_startupInfo(new KStartupInfo(KStartupInfo::CleanOnCantDetect, this)) , m_selection(nullptr) , m_active(false) , m_frame(0) , m_progress(0) , m_texture(nullptr) , m_type(BouncingFeedback) , m_blinkingShader(nullptr) - , m_cursorSize(0) + , m_cursorSize(24) , m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("klaunchrc", KConfig::NoGlobals))) { for (int i = 0; i < 5; ++i) { m_bouncingTextures[i] = nullptr; } if (KWindowSystem::isPlatformX11()) { m_selection = new KSelectionOwner("_KDE_STARTUP_FEEDBACK", xcbConnection(), x11RootWindow(), this); m_selection->claim(true); } connect(m_startupInfo, &KStartupInfo::gotNewStartup, this, &StartupFeedbackEffect::gotNewStartup); connect(m_startupInfo, &KStartupInfo::gotRemoveStartup, this, &StartupFeedbackEffect::gotRemoveStartup); connect(m_startupInfo, &KStartupInfo::gotStartupChange, this, &StartupFeedbackEffect::gotStartupChange); connect(effects, &EffectsHandler::mouseChanged, this, &StartupFeedbackEffect::slotMouseChanged); connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this]() { reconfigure(ReconfigureAll); }); reconfigure(ReconfigureAll); } StartupFeedbackEffect::~StartupFeedbackEffect() { if (m_active) { effects->stopMousePolling(); } for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; } delete m_texture; delete m_blinkingShader; } bool StartupFeedbackEffect::supported() { return effects->isOpenGLCompositing(); } void StartupFeedbackEffect::reconfigure(Effect::ReconfigureFlags flags) { Q_UNUSED(flags) KConfigGroup c = m_configWatcher->config()->group("FeedbackStyle"); const bool busyCursor = c.readEntry("BusyCursor", true); c = m_configWatcher->config()->group("BusyCursorSettings"); m_startupInfo->setTimeout(c.readEntry("Timeout", s_startupDefaultTimeout)); const bool busyBlinking = c.readEntry("Blinking", false); const bool busyBouncing = c.readEntry("Bouncing", true); if (!busyCursor) m_type = NoFeedback; else if (busyBouncing) m_type = BouncingFeedback; else if (busyBlinking) { m_type = BlinkingFeedback; if (effects->compositingType() == OpenGL2Compositing) { delete m_blinkingShader; m_blinkingShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("blinking-startup-fragment.glsl")); if (m_blinkingShader->isValid()) { qCDebug(KWINEFFECTS) << "Blinking Shader is valid"; } else { qCDebug(KWINEFFECTS) << "Blinking Shader is not valid"; } } } else m_type = PassiveFeedback; if (m_active) { stop(); start(m_startups[ m_currentStartup ]); } } void StartupFeedbackEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { // need the unclipped version switch(m_type) { case BouncingFeedback: m_progress = (m_progress + time) % BOUNCE_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BOUNCE_FRAME_DURATION) % BOUNCE_FRAMES; m_currentGeometry = feedbackRect(); // bounce alters geometry with m_frame data.paint = data.paint.united(m_currentGeometry); break; case BlinkingFeedback: m_progress = (m_progress + time) % BLINKING_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BLINKING_FRAME_DURATION) % BLINKING_FRAMES; break; default: break; // nothing } } effects->prePaintScreen(data, time); } void StartupFeedbackEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (m_active) { GLTexture* texture; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: return; // safety } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->bind(); if (m_type == BlinkingFeedback && m_blinkingShader && m_blinkingShader->isValid()) { const QColor& blinkingColor = BLINKING_COLORS[ FRAME_TO_BLINKING_COLOR[ m_frame ]]; ShaderManager::instance()->pushShader(m_blinkingShader); m_blinkingShader->setUniform(GLShader::Color, blinkingColor); } else { ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); } QMatrix4x4 mvp = data.projectionMatrix(); mvp.translate(m_currentGeometry.x(), m_currentGeometry.y()); ShaderManager::instance()->getBoundShader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp); texture->render(m_currentGeometry, m_currentGeometry); ShaderManager::instance()->popShader(); texture->unbind(); glDisable(GL_BLEND); } } void StartupFeedbackEffect::postPaintScreen() { if (m_active) { m_dirtyRect = m_currentGeometry; // ensure the now dirty region is cleaned on the next pass if (m_type == BlinkingFeedback || m_type == BouncingFeedback) effects->addRepaint(m_dirtyRect); // we also have to trigger a repaint } effects->postPaintScreen(); } void StartupFeedbackEffect::slotMouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers) { Q_UNUSED(pos) Q_UNUSED(oldpos) Q_UNUSED(buttons) Q_UNUSED(oldbuttons) Q_UNUSED(modifiers) Q_UNUSED(oldmodifiers) if (m_active) { m_dirtyRect |= m_currentGeometry; m_currentGeometry = feedbackRect(); m_dirtyRect |= m_currentGeometry; effects->addRepaint(m_dirtyRect); } } void StartupFeedbackEffect::gotNewStartup(const KStartupInfoId& id, const KStartupInfoData& data) { const QString& icon = data.findIcon(); m_currentStartup = id; m_startups[ id ] = icon; start(icon); } void StartupFeedbackEffect::gotRemoveStartup(const KStartupInfoId& id, const KStartupInfoData& data) { Q_UNUSED( data ) m_startups.remove(id); if (m_startups.count() == 0) { m_currentStartup = KStartupInfoId(); // null stop(); return; } m_currentStartup = m_startups.begin().key(); start(m_startups[ m_currentStartup ]); } void StartupFeedbackEffect::gotStartupChange(const KStartupInfoId& id, const KStartupInfoData& data) { if (m_currentStartup == id) { const QString& icon = data.findIcon(); if (!icon.isEmpty() && icon != m_startups[ m_currentStartup ]) { m_startups[ id ] = icon; start(icon); } } } void StartupFeedbackEffect::start(const QString& icon) { if (m_type == NoFeedback) return; if (!m_active) effects->startMousePolling(); m_active = true; auto readCursorSize = []() -> int { // read details about the mouse-cursor theme define per default KConfigGroup mousecfg(effects->inputConfig(), "Mouse"); - QString size = mousecfg.readEntry("cursorSize", QString()); - - // fetch a reasonable size for the cursor-theme image - bool ok; - int cursorSize = size.toInt(&ok); - if (!ok) - cursorSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); + int cursorSize = mousecfg.readEntry("cursorSize", 24); return cursorSize; }; m_cursorSize = readCursorSize(); int iconSize = m_cursorSize / 1.5; if (!iconSize) { iconSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize); } // get ratio for bouncing cursor so we don't need to manually calculate the sizes for each icon size if (m_type == BouncingFeedback) m_bounceSizesRatio = iconSize / 16.0; const QPixmap iconPixmap = QIcon::fromTheme(icon, QIcon::fromTheme(QStringLiteral("system-run"))).pixmap(iconSize); prepareTextures(iconPixmap); m_dirtyRect = m_currentGeometry = feedbackRect(); effects->addRepaint(m_dirtyRect); } void StartupFeedbackEffect::stop() { if (m_active) effects->stopMousePolling(); m_active = false; effects->makeOpenGLContextCurrent(); switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = nullptr; } break; case BlinkingFeedback: case PassiveFeedback: delete m_texture; m_texture = nullptr; break; case NoFeedback: return; // don't want the full repaint default: break; // impossible } effects->addRepaintFull(); } void StartupFeedbackEffect::prepareTextures(const QPixmap& pix) { effects->makeOpenGLContextCurrent(); switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = new GLTexture(scalePixmap(pix, BOUNCE_SIZES[i])); } break; case BlinkingFeedback: case PassiveFeedback: m_texture = new GLTexture(pix); break; default: // for safety m_active = false; break; } } QImage StartupFeedbackEffect::scalePixmap(const QPixmap& pm, const QSize& size) const { const QSize& adjustedSize = size * m_bounceSizesRatio; QImage scaled = pm.toImage().scaled(adjustedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32) scaled = scaled.convertToFormat(QImage::Format_ARGB32); QImage result(20 * m_bounceSizesRatio, 20 * m_bounceSizesRatio, QImage::Format_ARGB32); QPainter p(&result); p.setCompositionMode(QPainter::CompositionMode_Source); p.fillRect(result.rect(), Qt::transparent); p.drawImage((20 * m_bounceSizesRatio - adjustedSize.width()) / 2, (20*m_bounceSizesRatio - adjustedSize.height()) / 2, scaled, 0, 0, adjustedSize.width(), adjustedSize.height() * m_bounceSizesRatio); return result; } QRect StartupFeedbackEffect::feedbackRect() const { int xDiff; if (m_cursorSize <= 16) xDiff = 8 + 7; else if (m_cursorSize <= 32) xDiff = 16 + 7; else if (m_cursorSize <= 48) xDiff = 24 + 7; else xDiff = 32 + 7; int yDiff = xDiff; GLTexture* texture = nullptr; int yOffset = 0; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; yOffset = FRAME_TO_BOUNCE_YOFFSET[ m_frame ] * m_bounceSizesRatio; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: // nothing break; } const QPoint cursorPos = effects->cursorPos() + QPoint(xDiff, yDiff + yOffset); QRect rect; if( texture ) rect = QRect(cursorPos, texture->size()); return rect; } bool StartupFeedbackEffect::isActive() const { return m_active; } } // namespace