diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,13 +25,27 @@ include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) -find_package(Phonon4Qt5 4.6.60 NO_MODULE) find_package(KF5Completion ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5I18n ${KF5_DEP_VERSION} REQUIRED) find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED) +find_package(Canberra) +set_package_properties(Canberra PROPERTIES + PURPOSE "Needed to preview notification sounds" + TYPE OPTIONAL) +if (Canberra_FOUND) + add_definitions(-DHAVE_CANBERRA) +else() + # This is REQUIRED since you cannot tell CMake "either one of those two optional ones are required" + find_package(Phonon4Qt5 4.6.60 NO_MODULE REQUIRED) + set_package_properties(Phonon4Qt5 PROPERTIES + DESCRIPTION "Qt-based audio library" + PURPOSE "Needed to preview notification sounds when Canberra isn't available") + add_definitions(-DHAVE_PHONON4QT5) +endif() + # Includes include(GenerateExportHeader) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,11 +29,10 @@ KF5::KIOWidgets # KUrlRequester Qt5::DBus) -if(HAVE_PHONON) - target_link_libraries(KF5NotifyConfig - PRIVATE - Phonon::phonon4qt5 - ) +if (Canberra_FOUND) + target_link_libraries(KF5NotifyConfig PRIVATE Canberra::Canberra) +elseif (Phonon4Qt5_FOUND) + target_link_libraries(KF5NotifyConfig PRIVATE Phonon::phonon4qt5) endif() set_target_properties(KF5NotifyConfig PROPERTIES diff --git a/src/knotifyconfigactionswidget.h b/src/knotifyconfigactionswidget.h --- a/src/knotifyconfigactionswidget.h +++ b/src/knotifyconfigactionswidget.h @@ -23,6 +23,10 @@ #include #include "ui_knotifyconfigactionswidgetbase.h" +#ifdef HAVE_CANBERRA +struct ca_context; +#endif + class KNotifyConfigElement; /** @@ -35,7 +39,7 @@ Q_OBJECT public: explicit KNotifyConfigActionsWidget(QWidget *parent); - ~KNotifyConfigActionsWidget() {} + ~KNotifyConfigActionsWidget() override; void setConfigElement(KNotifyConfigElement *config); void save(KNotifyConfigElement *config); @@ -46,6 +50,9 @@ void slotTTSComboChanged(); private: Ui::KNotifyConfigActionsWidgetBase m_ui; +#ifdef HAVE_CANBERRA + ca_context *m_context = nullptr; +#endif }; #endif // KNOTIFYCONFIGACTIONSWIDGET_H diff --git a/src/knotifyconfigactionswidget.cpp b/src/knotifyconfigactionswidget.cpp --- a/src/knotifyconfigactionswidget.cpp +++ b/src/knotifyconfigactionswidget.cpp @@ -18,10 +18,14 @@ #include "knotifyconfigactionswidget.h" #include "knotifyconfigelement.h" +#include #include #include "knotify-config.h" -#if HAVE_PHONON + +#if defined(HAVE_CANBERRA) +#include +#elif defined(HAVE_PHONON) #include #endif @@ -63,7 +67,16 @@ m_ui.TTS_select->setVisible(false); m_ui.TTS_combo->setVisible(false); } +} +KNotifyConfigActionsWidget::~KNotifyConfigActionsWidget() +{ +#ifdef HAVE_CANBERRA + if (m_context) { + ca_context_destroy(m_context); + } + m_context = nullptr; +#endif } void KNotifyConfigActionsWidget::setConfigElement(KNotifyConfigElement *config) @@ -149,7 +162,49 @@ } soundURL.clear(); } -#if HAVE_PHONON + +#if defined(HAVE_CANBERRA) + if (!m_context) { + int ret = ca_context_create(&m_context); + if (ret != CA_SUCCESS) { + qWarning() << "Failed to initialize canberra context for audio notification:" << ca_strerror(ret); + m_context = nullptr; + return; + } + + QString desktopFileName = QGuiApplication::desktopFileName(); + // handle apps which set the desktopFileName property with filename suffix, + // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521) + if (desktopFileName.endsWith(QLatin1String(".desktop"))) { + desktopFileName.chop(8); + } + ret = ca_context_change_props(m_context, + CA_PROP_APPLICATION_NAME, qUtf8Printable(qApp->applicationDisplayName()), + CA_PROP_APPLICATION_ID, qUtf8Printable(desktopFileName), + CA_PROP_APPLICATION_ICON_NAME, qUtf8Printable(qApp->windowIcon().name()), + nullptr); + if (ret != CA_SUCCESS) { + qWarning() << "Failed to set application properties on canberra context for audio notification:" << ca_strerror(ret); + } + } + + ca_proplist *props = nullptr; + ca_proplist_create(&props); + + // We'll also want this cached for a time. volatile makes sure the cache is + // dropped after some time or when the cache is under pressure. + ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, QFile::encodeName(soundURL.toLocalFile()).constData()); + ca_proplist_sets(props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile"); + + int ret = ca_context_play_full(m_context, 0, props, nullptr, nullptr); + + ca_proplist_destroy(props); + + if (ret != CA_SUCCESS) { + qWarning() << "Failed to play sound with canberra:" << ca_strerror(ret); + return; + } +#elif defined(HAVE_PHONON) Phonon::MediaObject *media = Phonon::createPlayer(Phonon::NotificationCategory, soundURL); media->play(); connect(media, SIGNAL(finished()), media, SLOT(deleteLater()));