/* This file is part of Clementine. Copyright 2010, David Sansome Clementine 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 3 of the License, or (at your option) any later version. Clementine 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 Clementine. If not, see . */ #include #include #include "config.h" #include "core/logging.h" #include "osd.h" #ifdef HAVE_DBUS #include #include #include "dbus/notification.h" QDBusArgument& operator<<(QDBusArgument& arg, const QImage& image) { if (image.isNull()) { // Sometimes this gets called with a null QImage for no obvious reason. arg.beginStructure(); arg << 0 << 0 << 0 << false << 0 << 0 << QByteArray(); arg.endStructure(); return arg; } QImage scaled = image.scaledToHeight(100, Qt::SmoothTransformation); scaled = scaled.convertToFormat(QImage::Format_ARGB32); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN // ABGR -> ARGB QImage i = scaled.rgbSwapped(); #else // ABGR -> GBAR QImage i(scaled.size(), scaled.format()); for (int y = 0; y < i.height(); ++y) { QRgb* p = (QRgb*)scaled.scanLine(y); QRgb* q = (QRgb*)i.scanLine(y); QRgb* end = p + scaled.width(); while (p < end) { *q = qRgba(qGreen(*p), qBlue(*p), qAlpha(*p), qRed(*p)); p++; q++; } } #endif arg.beginStructure(); arg << i.width(); arg << i.height(); arg << i.bytesPerLine(); arg << i.hasAlphaChannel(); int channels = i.isGrayscale() ? 1 : (i.hasAlphaChannel() ? 4 : 3); arg << i.depth() / channels; arg << channels; #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) arg << QByteArray(reinterpret_cast(i.bits()), i.sizeInBytes()); #else arg << QByteArray(reinterpret_cast(i.bits()), i.byteCount()); #endif arg.endStructure(); return arg; } const QDBusArgument& operator>>(const QDBusArgument& arg, QImage& image) { // This is needed to link but shouldn't be called. Q_ASSERT(0); return arg; } #endif // HAVE_DBUS void OSD::Init() { #ifdef HAVE_DBUS notification_id_ = 0; interface_.reset(new OrgFreedesktopNotificationsInterface( OrgFreedesktopNotificationsInterface::staticInterfaceName(), "/org/freedesktop/Notifications", QDBusConnection::sessionBus())); if (!interface_->isValid()) { qLog(Warning) << "Error connecting to notifications service."; } #endif // HAVE_DBUS } bool OSD::SupportsNativeNotifications() { #ifdef HAVE_DBUS return true; #else return false; #endif } bool OSD::SupportsTrayPopups() { return true; } void OSD::ShowMessageNative(const QString& summary, const QString& message, const QString& icon, const QImage& image) { #ifdef HAVE_DBUS if (!interface_) return; QVariantMap hints; if (!image.isNull()) { hints["image_data"] = QVariant(image); } hints["desktop-entry"] = QVariant("org.clementine_player.Clementine"); hints["transient"] = QVariant(true); int id = 0; if (last_notification_time_.secsTo(QDateTime::currentDateTime()) * 1000 < timeout_msec_) { // Reuse the existing popup if it's still open. The reason we don't always // reuse the popup is because the notification daemon on KDE4 won't re-show // the bubble if it's already gone to the tray. See issue #118 id = notification_id_; } QDBusPendingReply reply = interface_->Notify(QCoreApplication::applicationName(), id, icon, summary, message, QStringList(), hints, timeout_msec_); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(CallFinished(QDBusPendingCallWatcher*))); #else // HAVE_DBUS qLog(Warning) << "not implemented"; #endif // HAVE_DBUS } #ifdef HAVE_DBUS void OSD::CallFinished(QDBusPendingCallWatcher* watcher) { std::unique_ptr w(watcher); QDBusPendingReply reply = *watcher; if (reply.isError()) { qLog(Warning) << "Error sending notification" << reply.error().name(); return; } uint id = reply.value(); if (id != 0) { notification_id_ = id; last_notification_time_ = QDateTime::currentDateTime(); } } #endif