diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fff7db76..d09ee318c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,19 +30,12 @@ if(WIN32) else(WIN32) pkg_check_modules(TAGLIB taglib) pkg_check_modules(XINE libxine) - pkg_check_modules(LIBNOTIFY libnotify) endif(WIN32) if (NOT Boost_FOUND) message(FATAL_ERROR "Boost not found") endif (NOT Boost_FOUND) -if (LIBNOTIFY_FOUND) - add_definitions(-DHAVE_LIBNOTIFY) - link_directories(${LIBNOTIFY_LIBRARY_DIRS}) - include_directories(${LIBNOTIFY_INCLUDE_DIRS}) -endif (LIBNOTIFY_FOUND) - if (TAGLIB_VERSION VERSION_LESS 1.6) message(FATAL_ERROR "Taglib version 1.6 or greater is required") endif (TAGLIB_VERSION VERSION_LESS 1.6) diff --git a/data/org.freedesktop.Notifications.xml b/data/org.freedesktop.Notifications.xml new file mode 100644 index 000000000..41f733009 --- /dev/null +++ b/data/org.freedesktop.Notifications.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd075398f..2e79396a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -184,6 +184,7 @@ else(APPLE) set(CLEMENTINE-SOURCES ${CLEMENTINE-SOURCES} osd_win.cpp) else(WIN32) set(CLEMENTINE-SOURCES ${CLEMENTINE-SOURCES} osd_x11.cpp) + # MPRIS qt4_add_dbus_adaptor(MPRIS-PLAYER-SOURCES ../data/org.freedesktop.MediaPlayer.player.xml player.h Player mpris_player MprisPlayer) @@ -193,10 +194,16 @@ else(APPLE) qt4_add_dbus_adaptor(MPRIS-TRACKLIST-SOURCES ../data/org.freedesktop.MediaPlayer.tracklist.xml player.h Player mpris_tracklist MprisTrackList) + + # org.freedesktop.Notifications + qt4_add_dbus_interface(NOTIFICATION-SOURCES + ../data/org.freedesktop.Notifications.xml + notification) set(CLEMENTINE-SOURCES ${CLEMENTINE-SOURCES} ${MPRIS-PLAYER-SOURCES} ${MPRIS-ROOT-SOURCES} ${MPRIS-TRACKLIST-SOURCES} + ${NOTIFICATION-SOURCES} mpris.cpp ) set(CLEMENTINE-MOC-HEADERS ${CLEMENTINE-MOC-HEADERS} mpris.h) @@ -231,7 +238,6 @@ target_link_libraries(clementine_lib qxt lastfm ${XINE_LIBRARIES} - ${LIBNOTIFY_LIBRARIES} ${TAGLIB_LIBRARIES} ${QT_LIBRARIES} ) diff --git a/src/main.cpp b/src/main.cpp index a0acb64f1..c8b6511a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,7 @@ # include # include # include "mpris.h" +# include "osd.h" #endif // Load sqlite plugin on windows @@ -100,6 +101,7 @@ int main(int argc, char *argv[]) { #ifdef Q_WS_X11 qDBusRegisterMetaType(); qDBusRegisterMetaType(); + qDBusRegisterMetaType(); QDBusConnection::sessionBus().registerService("org.mpris.clementine"); MPRIS mpris; #endif diff --git a/src/osd.h b/src/osd.h index f16a339ec..f6b2decd0 100644 --- a/src/osd.h +++ b/src/osd.h @@ -25,10 +25,13 @@ #include "song.h" #ifdef Q_WS_X11 -# ifndef _NOTIFY_NOTIFICATION_H_ - struct GdkPixbuf; - struct NotifyNotification; -# endif +#include +#include +#include "notification.h" + +QDBusArgument& operator<< (QDBusArgument& arg, const QImage& image); +const QDBusArgument& operator>> (const QDBusArgument& arg, QImage& image); + #endif class OSD : public QObject { @@ -79,15 +82,16 @@ class OSD : public QObject { bool show_on_volume_change_; bool show_art_; -#ifdef Q_WS_X11 - NotifyNotification* notification_; - GdkPixbuf* pixbuf_; -#endif - #ifdef Q_OS_DARWIN class GrowlNotificationWrapper; GrowlNotificationWrapper* wrapper_; #endif // Q_OS_DARWIN + +#ifdef Q_WS_X11 + boost::scoped_ptr interface_; + private slots: + void CallFinished(QDBusPendingCallWatcher* watcher); +#endif }; #endif // OSD_H diff --git a/src/osd_x11.cpp b/src/osd_x11.cpp index d84123e55..19e1b7cb8 100644 --- a/src/osd_x11.cpp +++ b/src/osd_x11.cpp @@ -14,34 +14,55 @@ along with Clementine. If not, see . */ -// Libnotify headers need to go before Qt ones because they use "signals" as -// a variable name -#ifdef HAVE_LIBNOTIFY -# include -# include -# include -#endif // HAVE_LIBNOTIFY - #include "osd.h" #include #include #include +using boost::scoped_ptr; + +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); + QImage i = scaled.convertToFormat(QImage::Format_ARGB32).rgbSwapped(); + 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; + arg << QByteArray(reinterpret_cast(i.bits()), i.numBytes()); + 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; +} + void OSD::Init() { - notification_ = NULL; - pixbuf_ = NULL; -#ifdef HAVE_LIBNOTIFY - notify_init(QCoreApplication::applicationName().toUtf8().constData()); -#endif + interface_.reset(new org::freedesktop::Notifications( + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + QDBusConnection::sessionBus())); + if (!interface_->isValid()) { + qWarning() << "Error connecting to notifications service."; + } } bool OSD::SupportsNativeNotifications() { -#ifdef HAVE_LIBNOTIFY return true; -#else - return false; -#endif } bool OSD::SupportsTrayPopups() { @@ -50,50 +71,45 @@ bool OSD::SupportsTrayPopups() { void OSD::ShowMessageNative(const QString& summary, const QString& message, const QString& icon) { -#ifdef HAVE_LIBNOTIFY - if (summary.isNull()) - return; - - #define STR(x) (x.isNull() ? NULL : x.toUtf8().constData()) - - notification_ = notify_notification_new( - STR(summary), STR(Qt::escape(message)), STR(icon), NULL); - - #undef STR - - notify_notification_set_urgency(notification_, NOTIFY_URGENCY_LOW); - notify_notification_set_timeout(notification_, timeout_); - - if (pixbuf_) { - notify_notification_set_icon_from_pixbuf(notification_, pixbuf_); - } - - GError* error = NULL; - notify_notification_show(notification_, &error); - if (error) { - qDebug() << "Error from notify_notification_show:" << error->message; - g_error_free(error); - } - - pixbuf_ = NULL; -#endif // HAVE_LIBNOTIFY + QDBusPendingReply reply = interface_->Notify( + QCoreApplication::applicationName(), + 0, + icon, + summary, + message, + QStringList(), + QVariantMap(), + timeout_); + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(CallFinished(QDBusPendingCallWatcher*))); } void OSD::ShowMessageNative(const QString& summary, const QString& message, const QImage& image) { -#ifdef HAVE_LIBNOTIFY - QImage happy_gdk_image = image.scaledToHeight(100, Qt::SmoothTransformation) - .convertToFormat(QImage::Format_RGB888); - pixbuf_ = gdk_pixbuf_new_from_data( - happy_gdk_image.bits(), - GDK_COLORSPACE_RGB, - false, // has_alpha - 8, // bits_per_sample - happy_gdk_image.width(), - happy_gdk_image.height(), - happy_gdk_image.bytesPerLine(), - NULL, NULL); - - ShowMessageNative(summary, message, QString()); -#endif // HAVE_LIBNOTIFY + QVariantMap hints; + if (!image.isNull()) { + hints["image_data"] = QVariant(image); + } + QDBusPendingReply reply = interface_->Notify( + QCoreApplication::applicationName(), + 0, + QString(), + summary, + message, + QStringList(), + hints, + timeout_); + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(CallFinished(QDBusPendingCallWatcher*))); +} + +void OSD::CallFinished(QDBusPendingCallWatcher* watcher) { + scoped_ptr w(watcher); + + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning() << "Error sending notification" << reply.error(); + } }