File indexing completed on 2025-01-12 04:20:12

0001 /*
0002     SnoreNotify is a Notification Framework based on Qt
0003     Copyright (C) 2014-2015  Hannah von Reth <vonreth@kde.org>
0004 
0005     SnoreNotify is free software: you can redistribute it and/or modify
0006     it under the terms of the GNU Lesser General Public License as published by
0007     the Free Software Foundation, either version 3 of the License, or
0008     (at your option) any later version.
0009 
0010     SnoreNotify is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU Lesser General Public License for more details.
0014 
0015     You should have received a copy of the GNU Lesser General Public License
0016     along with SnoreNotify.  If not, see <http://www.gnu.org/licenses/>.
0017 */
0018 
0019 #include "notifywidget.h"
0020 #include "snorenotifier.h"
0021 #include "snorebackendconstants.h"
0022 #include "libsnore/utils.h"
0023 
0024 #include <QGuiApplication>
0025 #include <QQmlProperty>
0026 
0027 using namespace Snore;
0028 using namespace SnorePlugin;
0029 
0030 NotifyWidget::NotifyWidget(int id, const ::SnorePlugin::Snore *parent) :
0031     m_id(id),
0032     m_parent(parent),
0033     m_mem(QStringLiteral("SnoreNotifyWidget_rev%1_id%2").arg(QString::number(SHARED_MEM_TYPE_REV()), QString::number(m_id))),
0034     m_ready(true),
0035     m_fontFamily(qApp->font().family())
0036 {
0037 
0038 #ifdef Q_OS_WIN
0039     if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8) {
0040         m_fontFamily = QStringLiteral("Segoe UI Symbol");
0041         emit fontFamilyChanged();
0042     }
0043     // Qt 5.7+ is broken and can only display it in black and white for Segoe UI Emoji.
0044 #if QT_VERSION >= QT_VERSION_CHECK(5,5,0) && QT_VERSION < QT_VERSION_CHECK(5,7,0)
0045     if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10) {
0046         m_fontFamily = QStringLiteral("Segoe UI Emoji");
0047         emit fontFamilyChanged();
0048     }
0049 #endif
0050 #endif
0051     QQmlApplicationEngine *engine = new QQmlApplicationEngine(this);
0052     engine->rootContext()->setContextProperty(QStringLiteral("notifyWidget"), this);
0053     engine->load(QUrl::fromEncoded("qrc:/notification.qml"));
0054     m_window = qobject_cast<QQuickWindow *>(engine->rootObjects().value(0));
0055 
0056     // TODO: It looks like there is a Qt bug which make some Windows with this flag invisible in some cases...(Tested: Kubuntu willy)
0057     m_window->setFlags(Qt::WindowStaysOnTopHint | Qt::ToolTip
0058 #ifdef Q_OS_MAC
0059                        // TODO: is this needed or is ToolTip working?
0060                        //| Qt::SubWindow
0061 #endif
0062                       );
0063 
0064     if (m_mem.create(sizeof(SHARED_MEM_TYPE))) {
0065         m_mem.lock();
0066         SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data();
0067         data->free = true;
0068         data->date = QTime::currentTime().msecsSinceStartOfDay();
0069         m_mem.unlock();
0070     } else {
0071         if (!m_mem.attach()) {
0072             qCWarning(SNORE) << "Failed to atatche to shared mem";
0073         } else {
0074             m_mem.lock();
0075             SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data();
0076             m_mem.unlock();
0077             int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000;
0078             qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout;
0079         }
0080     }
0081 }
0082 
0083 NotifyWidget::~NotifyWidget()
0084 {
0085     release();
0086 }
0087 
0088 void NotifyWidget::display(const Notification &notification)
0089 {
0090     qCDebug(SNORE) << m_id << notification.id() << m_window->isVisible();
0091     m_notification = notification;
0092     QColor color;
0093     QVariant vcolor = notification.application().constHints().privateValue(parent(), "backgroundColor");
0094     if (vcolor.isValid()) {
0095         color = vcolor.value<QColor>();
0096     } else {
0097         color = computeBackgrondColor(notification.application().icon().pixmap(QSize(20, 20)).toImage());
0098         notification.application().hints().setPrivateValue(parent(), "backgroundColor", color);
0099     }
0100     m_appIcon = QUrl::fromLocalFile(notification.application().icon().localUrl(QSize(m_appIconSize, m_appIconSize)));
0101     emit appIconChanged();
0102 
0103     m_image = QUrl::fromLocalFile(notification.icon().localUrl(QSize(m_imageSize, m_imageSize)));
0104     emit imageChanged();
0105 
0106     m_title = notification.title(Utils::AllMarkup);
0107     emit titleChanged();
0108     m_body = notification.text(Utils::AllMarkup);
0109     emit bodyChanged();
0110 
0111     if (!notification.isUpdate()) {
0112         syncSettings();
0113         m_color = color;
0114         m_textColor = compueTextColor(color);
0115         emit colorChanged();
0116         emit textColorChanged();
0117         m_window->show();
0118         Utils::raiseWindowToFront(m_window);
0119     }
0120 }
0121 
0122 bool NotifyWidget::acquire(int timeout)
0123 {
0124     if (!m_mem.isAttached()) {
0125         return true;
0126     }
0127     bool out = false;
0128     if (m_ready) {
0129         m_mem.lock();
0130         SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data();
0131         int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000;
0132         qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout;
0133         bool isTimedOut = elapsed > data->timeout;
0134         if (data->free || isTimedOut) {
0135             if (isTimedOut) {
0136                 qCDebug(SNORE) << "Notification Lock timed out" << elapsed;
0137             }
0138             data->free = false;
0139             data->date = QTime::currentTime().msecsSinceStartOfDay();
0140             data->timeout = timeout;
0141             m_ready = false;
0142             out = true;
0143         }
0144         m_mem.unlock();
0145     }
0146     return out;
0147 }
0148 
0149 bool NotifyWidget::release()
0150 {
0151     if (!m_mem.isAttached()) {
0152         return true;
0153     }
0154     bool out = false;
0155     if (!m_ready) {
0156         m_mem.lock();
0157         SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data();
0158         int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000;
0159         qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout;
0160         if (!data->free) {
0161             data->free = true;
0162             m_ready = true;
0163             out = true;
0164         }
0165         m_mem.unlock();
0166         m_window->hide();
0167     }
0168     return out;
0169 }
0170 
0171 Notification &NotifyWidget::notification()
0172 {
0173     return m_notification;
0174 }
0175 
0176 int NotifyWidget::id() const
0177 {
0178     return m_id;
0179 }
0180 
0181 void NotifyWidget::syncSettings()
0182 {
0183     Qt::Corner c = static_cast<Qt::Corner>(m_parent->settingsValue(SnoreBackendConstants::Position).toInt());
0184     if (c != m_corner || !m_initialized) {
0185         m_initialized = true;
0186         m_corner = c;
0187         emit positionChanged();
0188     }
0189 }
0190 
0191 QColor NotifyWidget::computeBackgrondColor(const QImage &img)
0192 {
0193     if (img.isNull())
0194         return Qt::black;
0195 
0196     int stepSize = img.depth() / 8;
0197     qulonglong r = 0;
0198     qulonglong g = 0;
0199     qulonglong b = 0;
0200     const uchar *end = img.constBits() + img.byteCount();
0201     for (const uchar *bit = img.constBits(); bit != end; bit += stepSize) {
0202         const QRgb c = *reinterpret_cast<const QRgb *>(bit);
0203         r += qRed(c);
0204         g += qGreen(c);
0205         b += qBlue(c);
0206     }
0207     int size = img.byteCount() / stepSize;
0208     return QColor(r / size, g / size, b / size);
0209 }
0210 
0211 QColor NotifyWidget::compueTextColor(const QColor &backgroundColor)
0212 {
0213     // based on http://stackoverflow.com/a/946734
0214     QRgb compColor = qGray(backgroundColor.rgb()) > 186 ? 0 : 255;
0215     return QColor(compColor, compColor, compColor);
0216 }
0217