mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
[draft] Freedesktop Notifications implementation (Linux)
This commit is contained in:
parent
b132e19e2e
commit
9593bc230d
11 changed files with 589 additions and 87 deletions
|
|
@ -235,6 +235,7 @@ set(SOURCES
|
|||
src/components/other/colors/ColorListModel.cpp
|
||||
src/components/other/colors/ColorProxyModel.cpp
|
||||
src/components/other/colors/ImageColorsProxyModel.cpp
|
||||
src/components/other/desktop-tools/notifications/NotificationsDefault.cpp
|
||||
src/components/other/images/ImageModel.cpp
|
||||
src/components/other/images/ImageListModel.cpp
|
||||
src/components/other/images/ImageProxyModel.cpp
|
||||
|
|
@ -376,6 +377,7 @@ set(HEADERS
|
|||
src/components/other/images/ImageListModel.hpp
|
||||
src/components/other/images/ImageProxyModel.hpp
|
||||
src/components/other/desktop-tools/DesktopTools.hpp
|
||||
src/components/other/desktop-tools/notifications/NotificationsDefault.hpp
|
||||
src/components/other/text-to-speech/TextToSpeech.hpp
|
||||
src/components/other/timeZone/TimeZoneModel.hpp
|
||||
src/components/other/timeZone/TimeZoneListModel.hpp
|
||||
|
|
@ -460,6 +462,7 @@ else ()
|
|||
src/app/single-application/SingleApplicationDBus.cpp
|
||||
src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp
|
||||
src/components/other/desktop-tools/DesktopToolsLinux.cpp
|
||||
src/components/other/desktop-tools/notifications/NotificationsDBus.cpp
|
||||
src/components/other/desktop-tools/screen-saver/ScreenSaverDBus.cpp
|
||||
src/components/other/desktop-tools/screen-saver/ScreenSaverXdg.cpp
|
||||
)
|
||||
|
|
@ -467,6 +470,7 @@ else ()
|
|||
src/app/single-application/SingleApplicationDBusPrivate.hpp
|
||||
src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp
|
||||
src/components/other/desktop-tools/DesktopToolsLinux.hpp
|
||||
src/components/other/desktop-tools/notifications/NotificationsDBus.hpp
|
||||
src/components/other/desktop-tools/screen-saver/ScreenSaverDBus.hpp
|
||||
src/components/other/desktop-tools/screen-saver/ScreenSaverXdg.hpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1110,6 +1110,10 @@ void App::checkForUpdate() {
|
|||
checkForUpdates(false);
|
||||
}
|
||||
void App::checkForUpdates(bool force) {
|
||||
#ifdef DEBUG
|
||||
if(force)
|
||||
App::getInstance()->getNotifier()->notifyNewVersionAvailable("5.42.5","https://linphone.org");
|
||||
#endif
|
||||
if(force || CoreManager::getInstance()->getSettingsModel()->isCheckForUpdateEnabled())
|
||||
CoreManager::getInstance()->getCore()->checkForUpdate(
|
||||
Utils::appStringToCoreString(applicationVersion())
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/timeline/TimelineModel.hpp"
|
||||
#include "components/timeline/TimelineListModel.hpp"
|
||||
#include "components//other/desktop-tools/notifications/NotificationsDefault.hpp"
|
||||
#include "components//other/desktop-tools/notifications/NotificationsDBus.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include "Notifier.hpp"
|
||||
|
|
@ -48,33 +50,15 @@ namespace {
|
|||
|
||||
constexpr char NotificationShowMethodName[] = "open";
|
||||
|
||||
constexpr char NotificationPropertyData[] = "notificationData";
|
||||
|
||||
constexpr char NotificationPropertyX[] = "popupX";
|
||||
constexpr char NotificationPropertyY[] = "popupY";
|
||||
|
||||
constexpr char NotificationPropertyWindow[] = "__internalWindow";
|
||||
|
||||
constexpr char NotificationPropertyTimer[] = "__timer";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Arbitrary hardcoded values.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
constexpr int NotificationSpacing = 10;
|
||||
constexpr int MaxNotificationsNumber = 5;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
template<class T>
|
||||
void setProperty (QObject &object, const char *property, const T &value) {
|
||||
if (!object.setProperty(property, QVariant(value))) {
|
||||
qWarning() << QStringLiteral("Unable to set property: `%1`.").arg(property);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Available notifications.
|
||||
// =============================================================================
|
||||
|
|
@ -121,7 +105,7 @@ Notifier::~Notifier () {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
QObject *Notifier::createNotification (Notifier::NotificationType type, QVariantMap data) {
|
||||
QQuickItem *wrapperItem = nullptr;
|
||||
QObject *wrapperItem = nullptr;
|
||||
mMutex->lock();
|
||||
Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances.
|
||||
|
|
@ -129,74 +113,17 @@ QObject *Notifier::createNotification (Notifier::NotificationType type, QVariant
|
|||
mMutex->unlock();
|
||||
return nullptr;
|
||||
}
|
||||
QList<QScreen *> allScreens = QGuiApplication::screens();
|
||||
if(allScreens.size() > 0){ // Ensure to have a screen to avoid errors
|
||||
QQuickItem * previousWrapper = nullptr;
|
||||
++mInstancesNumber;
|
||||
bool showAsTool = false;
|
||||
#ifdef Q_OS_MACOS
|
||||
for(auto w : QGuiApplication::topLevelWindows()){
|
||||
if( (w->windowState()&Qt::WindowFullScreen)==Qt::WindowFullScreen){
|
||||
showAsTool = true;
|
||||
w->raise();// Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the only way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not available)
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
#elif defined(__APPLE__)
|
||||
#else
|
||||
wrapperItem = NotificationsDBus::create(type, data);
|
||||
#endif
|
||||
for(int i = 0 ; i < allScreens.size() ; ++i){
|
||||
QQuickView *view = new QQuickView(App::getInstance()->getEngine(), nullptr); // Use QQuickView to create a visual root object that is independant from current application Window
|
||||
QScreen *screen = allScreens[i];
|
||||
QObject::connect(view, &QQuickView::statusChanged, [allScreens](QQuickView::Status status){ // Debug handler : show screens descriptions on Error
|
||||
if( status == QQuickView::Error){
|
||||
QScreen * primaryScreen = QGuiApplication::primaryScreen();
|
||||
qInfo() << "Primary screen : " << primaryScreen->geometry() << primaryScreen->availableGeometry() << primaryScreen->virtualGeometry() << primaryScreen->availableVirtualGeometry();
|
||||
for(int i = 0 ; i < allScreens.size() ; ++i){
|
||||
QScreen *screen = allScreens[i];
|
||||
qInfo() << QString("Screen [")+QString::number(i)+"] (hdpi, Geometry, Available, Virtual, AvailableGeometry) :"
|
||||
<< screen->devicePixelRatio() << screen->geometry() << screen->availableGeometry() << screen->virtualGeometry() << screen->availableVirtualGeometry();
|
||||
}
|
||||
}
|
||||
});
|
||||
view->setScreen(screen); // Bind the visual root object to the screen
|
||||
view->setProperty("flags", QVariant(Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::X11BypassWindowManagerHint)); // Set the visual ghost window
|
||||
view->setSource(QString(NotificationsPath)+Notifier::Notifications[type].filename);
|
||||
|
||||
QQuickWindow *subWindow = view->findChild<QQuickWindow *>("__internalWindow");
|
||||
QObject::connect(subWindow, &QObject::destroyed, view, &QObject::deleteLater); // When destroying window, detroy visual root object too
|
||||
|
||||
int * screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
int heightOffset = availableGeometry.y() + (availableGeometry.height() - subWindow->height());//*screen->devicePixelRatio(); when using manual scaler
|
||||
if(showAsTool)
|
||||
subWindow->setProperty("showAsTool",true);
|
||||
subWindow->setX(availableGeometry.x()+ (availableGeometry.width()-subWindow->property("width").toInt()));//*screen->devicePixelRatio()); when using manual scaler
|
||||
subWindow->setY(heightOffset-(*screenHeightOffset % heightOffset));
|
||||
|
||||
*screenHeightOffset = (subWindow->height() + *screenHeightOffset) + NotificationSpacing;
|
||||
if (*screenHeightOffset - heightOffset + availableGeometry.y() >= 0)
|
||||
*screenHeightOffset = 0;
|
||||
|
||||
// if(primaryScreen != screen){ //Useful when doing manual scaling jobs. Need to implement scaler in GUI objects
|
||||
// //subwindow->setProperty("xScale", (double)screen->availableVirtualGeometry().width()/availableGeometry.width() );
|
||||
// //subwindow->setProperty("yScale", (double)screen->availableVirtualGeometry().height()/availableGeometry.height());
|
||||
// }
|
||||
wrapperItem = view->findChild<QQuickItem *>("__internalWrapper");
|
||||
::setProperty(*wrapperItem, NotificationPropertyData,data);
|
||||
view->setGeometry(subWindow->geometry()); // Ensure to have sufficient space to both let painter do job without error, and stay behind popup
|
||||
|
||||
if(previousWrapper!=nullptr){ // Link objects in order to propagate events without having to store them
|
||||
QObject::connect(previousWrapper, SIGNAL(deleteNotification(QVariant)), wrapperItem,SLOT(deleteNotificationSlot()));
|
||||
QObject::connect(wrapperItem, SIGNAL(isOpened()), previousWrapper,SLOT(open()));
|
||||
QObject::connect(wrapperItem, SIGNAL(isClosed()), previousWrapper,SLOT(close()));
|
||||
QObject::connect(wrapperItem, &QObject::destroyed, previousWrapper, &QObject::deleteLater);
|
||||
}
|
||||
previousWrapper = wrapperItem; // The last one is used as a point of start when deleting and openning
|
||||
|
||||
view->show();
|
||||
}
|
||||
qInfo() << QStringLiteral("Create notifications:") << wrapperItem;
|
||||
}
|
||||
if(!wrapperItem)
|
||||
wrapperItem = NotificationsDefault::create(type, data);
|
||||
|
||||
mMutex->unlock();
|
||||
if(wrapperItem)
|
||||
++mInstancesNumber;
|
||||
return wrapperItem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,10 @@ class ChatMessage;
|
|||
}
|
||||
|
||||
class Notifier : public QObject {
|
||||
Q_OBJECT;
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
friend class NotificationsDefault;
|
||||
Notifier (QObject *parent = Q_NULLPTR);
|
||||
~Notifier ();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,19 @@
|
|||
*/
|
||||
|
||||
#include "DesktopToolsLinux.hpp"
|
||||
#include "notifications/NotificationsDBus.hpp"
|
||||
|
||||
// =============================================================================
|
||||
DesktopTools gDesktopTools;
|
||||
|
||||
DesktopTools::~DesktopTools () {
|
||||
setScreenSaverStatus(true);
|
||||
}
|
||||
|
||||
void DesktopTools::init(){
|
||||
NotificationsDBus::init();
|
||||
}
|
||||
|
||||
bool DesktopTools::getScreenSaverStatus () const {
|
||||
return mScreenSaverStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,15 @@ class DesktopTools : public QObject {
|
|||
Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged);
|
||||
|
||||
public:
|
||||
DesktopTools (QObject *parent = Q_NULLPTR) : QObject(parent) {}
|
||||
DesktopTools (QObject *parent = Q_NULLPTR) : QObject(parent) {
|
||||
|
||||
}
|
||||
~DesktopTools ();
|
||||
|
||||
bool getScreenSaverStatus () const;
|
||||
void setScreenSaverStatus (bool status);
|
||||
|
||||
static void init(){}
|
||||
static void init();
|
||||
static void applicationStateChanged(Qt::ApplicationState){};
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDBusMetaType>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QImage>
|
||||
#include <QBuffer>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "NotificationsDBus.hpp"
|
||||
|
||||
#include "app/App.hpp"
|
||||
#include "app/providers/ImageProvider.hpp"
|
||||
#include "components/call/CallModel.hpp"
|
||||
#include "components/chat-room/ChatRoomModel.hpp"
|
||||
#include "components/settings/AccountSettingsModel.hpp"
|
||||
#include "components/timeline/TimelineModel.hpp"
|
||||
#include "qquickwindow.h"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
namespace {
|
||||
constexpr char ServiceName[] = "org.freedesktop.Notifications";
|
||||
constexpr char ServicePath[] = "/org/freedesktop/Notifications";
|
||||
}
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image) {
|
||||
QImage scaledImage;
|
||||
if (!image.isNull()) {
|
||||
scaledImage = image.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (scaledImage.format() != QImage::Format_ARGB32)
|
||||
scaledImage = scaledImage.convertToFormat(QImage::Format_ARGB32);
|
||||
scaledImage = scaledImage.rgbSwapped();
|
||||
}
|
||||
|
||||
const int channels = 4; // ARGB32 has 4 channels
|
||||
|
||||
arg.beginStructure();
|
||||
arg << scaledImage.width();
|
||||
arg << scaledImage.height();
|
||||
arg << scaledImage.bytesPerLine();
|
||||
arg << true; // ARGB32 has alpha
|
||||
arg << scaledImage.depth() / channels;
|
||||
arg << channels;
|
||||
arg << QByteArray::fromRawData((const char *)scaledImage.constBits(), scaledImage.height() * scaledImage.bytesPerLine());
|
||||
arg.endStructure();
|
||||
|
||||
return arg;
|
||||
}
|
||||
const QDBusArgument &operator >>(const QDBusArgument &arg, QImage &image) {
|
||||
Q_UNUSED(image)
|
||||
return arg;
|
||||
}
|
||||
|
||||
NotificationsDBus::NotificationsDBus (Notifier::NotificationType type, QVariantMap data, QDBusMessage message, QObject *parent) : QObject(parent), mType(type), mData(data), mMessage(message) {
|
||||
QDBusConnection::sessionBus().connect(QString(), QString(), "org.freedesktop.Notifications", "ActionInvoked", this, SLOT(onActionInvoked(quint32,QString)));
|
||||
QDBusConnection::sessionBus().connect(QString(), QString(), "org.freedesktop.Notifications", "NotificationClosed", this, SLOT(onNotificationClosed(quint32,quint32)));
|
||||
}
|
||||
|
||||
NotificationsDBus::~NotificationsDBus () {
|
||||
closeNotification();
|
||||
}
|
||||
|
||||
void NotificationsDBus::init(){
|
||||
qDBusRegisterMetaType<QImage>();
|
||||
}
|
||||
|
||||
static void openUrl(QFileInfo info){
|
||||
bool showDirectory = showDirectory || !info.exists();
|
||||
if(!QDesktopServices::openUrl(
|
||||
QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath()))
|
||||
) && !showDirectory){
|
||||
QDesktopServices::openUrl(QUrl(QStringLiteral("file:///%1").arg(info.absolutePath())));
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsDBus::onActionInvoked(quint32 code ,QString key){
|
||||
qWarning() << code << key;
|
||||
if(code == mId && !mProcessed){
|
||||
switch(mType){
|
||||
case Notifier::ReceivedCall :{
|
||||
if(key == "call-start"){
|
||||
mData["call"].value<CallModel*>()->accept();
|
||||
}else if(key == "call-stop"){
|
||||
mData["call"].value<CallModel*>()->terminate();
|
||||
}else
|
||||
emit deleteNotification(QVariant::fromValue(this));
|
||||
}
|
||||
break;
|
||||
case Notifier::ReceivedFileMessage:{
|
||||
if(key == "document-open"){
|
||||
openUrl(QFileInfo( mData["fileUri"].toString()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Notifier::ReceivedMessage: {
|
||||
if(key == "document-open"){
|
||||
CoreManager::getInstance()->getAccountSettingsModel()->setDefaultAccountFromSipAddress(mData["localAddress"].toString());
|
||||
auto timelineModel = mData["timelineModel"].value<TimelineModel*>();
|
||||
timelineModel->setSelected(true);
|
||||
App::getInstance()->smartShowWindow(App::getInstance()->getMainWindow());
|
||||
}
|
||||
/*
|
||||
notification.notificationData.window.setView('Conversation', {
|
||||
chatRoomModel:notification.timelineModel.getChatRoomModel()
|
||||
})
|
||||
*/
|
||||
|
||||
}break;
|
||||
case Notifier::NewVersionAvailable:{
|
||||
if(key == "software-update-available")
|
||||
QDesktopServices::openUrl(QUrl(mData["url"].toString()));
|
||||
}
|
||||
break;
|
||||
case Notifier::SnapshotWasTaken:{
|
||||
if(key == "document-open"){
|
||||
openUrl(QFileInfo( mData["filePath"].toString()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Notifier::RecordingCompleted:{
|
||||
if(key == "document-open"){
|
||||
openUrl(QFileInfo( mData["filePath"].toString()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:{
|
||||
}
|
||||
}
|
||||
mProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsDBus::onNotificationClosed(quint32 id,quint32 reason){
|
||||
if( !mProcessed ){// Is was closed from system.
|
||||
if(reason != 2)
|
||||
qWarning() << "Notification has been closed by system. If this is an issue, please deactivate native notifications [" << id << reason << "]";
|
||||
//open();// Not a workaround because of infinite openning loop.
|
||||
}
|
||||
}
|
||||
|
||||
QObject *NotificationsDBus::create(Notifier::NotificationType type, QVariantMap data) {
|
||||
QString title;
|
||||
QString message;
|
||||
QSize size, requestedSize(200,200);
|
||||
ImageProvider imageProvider;
|
||||
QVariantMap hints;
|
||||
QStringList actions;
|
||||
QString iconName = "linphone_logo";
|
||||
actions << "default" << "Close";
|
||||
//Check https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
|
||||
switch(type){
|
||||
case Notifier::ReceivedCall :
|
||||
//iconName = "call_sign_incoming";
|
||||
title = "Incoming call";
|
||||
message = Utils::getDisplayName(data["call"].value<CallModel*>()->getFullPeerAddress())+"\n"+data["call"].value<CallModel*>()->getPeerAddress();
|
||||
actions << "call-start" << "Accept";
|
||||
actions << "call-stop" << "Decline";
|
||||
break;
|
||||
case Notifier::ReceivedFileMessage :{
|
||||
auto timelineModel = data["timelineModel"].value<TimelineModel*>();
|
||||
title = "File received from " + timelineModel->getChatRoomModel()->getUsername();
|
||||
actions << "document-open" << "View";
|
||||
//message = timelineModel->getChatRoomModel()->getUsername();
|
||||
break;
|
||||
}
|
||||
case Notifier::ReceivedMessage: {
|
||||
auto timelineModel = data["timelineModel"].value<TimelineModel*>();
|
||||
title = "Message received from "+ timelineModel->getChatRoomModel()->getUsername();
|
||||
message = data["message"].toString();
|
||||
actions << "document-open" << "View";
|
||||
}
|
||||
break;
|
||||
case Notifier::NewVersionAvailable:{
|
||||
title = data["message"].toString();
|
||||
message = data["url"].toString();
|
||||
actions << "software-update-available" << "Download";
|
||||
}
|
||||
break;
|
||||
case Notifier::SnapshotWasTaken:{
|
||||
//iconName = "snapshot_sign";
|
||||
title = "Snapshot taken";
|
||||
actions << "document-open" << "View";
|
||||
}
|
||||
break;
|
||||
case Notifier::RecordingCompleted:{
|
||||
title = "Recording completed";
|
||||
actions << "document-open" << "View";
|
||||
}break;
|
||||
default:{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
hints["image_data"] = imageProvider.requestImage(iconName, &size, requestedSize);
|
||||
|
||||
return new NotificationsDBus(type, data, createMessage(title, message, hints, actions));
|
||||
}
|
||||
|
||||
QDBusMessage NotificationsDBus::createMessage(const QString& title, const QString& message, QVariantMap hints, QStringList actions){
|
||||
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify");
|
||||
hints["urgency"] = 2;// if not 2, it can be timeout without taking account of custom timeout
|
||||
hints["category"] = "im";
|
||||
//hints["resident"] = true;
|
||||
hints["transient"] = true;
|
||||
//hints["desktop-entry"] = "com.belledonnecommunications.linphone";
|
||||
hints["suppress-sound"] = true;
|
||||
|
||||
msg << APPLICATION_NAME; // Application name
|
||||
msg << quint32(0); // ID
|
||||
msg << ""; // Icon to display
|
||||
msg << APPLICATION_NAME +QString(": ") + title; // Summary / Header of the message to display
|
||||
msg << message; // Body of the message to display
|
||||
msg << actions; // Actions from which the user may choose
|
||||
msg << hints; // Hints to the server displaying the message
|
||||
msg << qint32(0); // Timeout in milliseconds
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void NotificationsDBus::open(){
|
||||
QDBusPendingReply<quint32> asyncReply(QDBusConnection::sessionBus().asyncCall(mMessage)); // Would return a message containing the id of this notification
|
||||
asyncReply.waitForFinished();
|
||||
if(asyncReply.isValid())
|
||||
mId = asyncReply.argumentAt(0).toInt();
|
||||
else
|
||||
qWarning() << asyncReply.error();
|
||||
}
|
||||
|
||||
void NotificationsDBus::closeNotification(){
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "CloseNotification");
|
||||
msg << quint32(mId);
|
||||
QDBusConnection::sessionBus().call(msg);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NOTIFICATIONS_DBUS_H_
|
||||
#define NOTIFICATIONS_DBUS_H_
|
||||
|
||||
#include <QDBusInterface>
|
||||
#include "components/notifier/Notifier.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
|
||||
class NotificationsDBus : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NotificationsDBus (Notifier::NotificationType type, QVariantMap data, QDBusMessage message, QObject *parent = Q_NULLPTR);
|
||||
~NotificationsDBus ();
|
||||
static void init();
|
||||
|
||||
static QObject *create(Notifier::NotificationType type, QVariantMap data);
|
||||
|
||||
//QObject *createNotification (NotificationType type, QVariantMap data);
|
||||
|
||||
|
||||
void closeNotification();
|
||||
static QDBusMessage createMessage(const QString& title, const QString& message, QVariantMap hints, QStringList actions);
|
||||
public slots:
|
||||
void open();
|
||||
void onActionInvoked(quint32 code ,QString key);
|
||||
void onNotificationClosed(quint32 id,quint32 reason);
|
||||
signals:
|
||||
void deleteNotification(QVariant notification);
|
||||
private:
|
||||
//bool mScreenSaverStatus = true;
|
||||
Notifier::NotificationType mType;
|
||||
QVariantMap mData;
|
||||
QDBusMessage mMessage;
|
||||
int mId = -1;
|
||||
bool mProcessed = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "NotificationsDefault.hpp"
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlComponent>
|
||||
#include <QGuiApplication>
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickView>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
#include "app/App.hpp"
|
||||
#include "components/call/CallModel.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/timeline/TimelineModel.hpp"
|
||||
#include "components/timeline/TimelineListModel.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
|
||||
namespace {
|
||||
constexpr char NotificationsPath[] = "qrc:/ui/modules/Linphone/Notifications/";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Notifications QML properties/methods.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
constexpr char NotificationShowMethodName[] = "open";
|
||||
|
||||
constexpr char NotificationPropertyData[] = "notificationData";
|
||||
|
||||
constexpr char NotificationPropertyX[] = "popupX";
|
||||
constexpr char NotificationPropertyY[] = "popupY";
|
||||
|
||||
constexpr char NotificationPropertyWindow[] = "__internalWindow";
|
||||
|
||||
constexpr char NotificationPropertyTimer[] = "__timer";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Arbitrary hardcoded values.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
constexpr int NotificationSpacing = 10;
|
||||
constexpr int MaxNotificationsNumber = 5;
|
||||
}
|
||||
|
||||
QHash<QString,int> NotificationsDefault::gScreenHeightOffset;
|
||||
|
||||
template<class T>
|
||||
void setProperty (QObject &object, const char *property, const T &value) {
|
||||
if (!object.setProperty(property, QVariant(value))) {
|
||||
qWarning() << QStringLiteral("Unable to set property: `%1`.").arg(property);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsDefault::NotificationsDefault (QObject *parent){
|
||||
|
||||
}
|
||||
|
||||
QObject *NotificationsDefault::create(Notifier::NotificationType type, QVariantMap data){
|
||||
QQuickItem *wrapperItem = nullptr;
|
||||
|
||||
QList<QScreen *> allScreens = QGuiApplication::screens();
|
||||
if(allScreens.size() > 0){ // Ensure to have a screen to avoid errors
|
||||
QQuickItem * previousWrapper = nullptr;
|
||||
|
||||
bool showAsTool = false;
|
||||
#ifdef Q_OS_MACOS
|
||||
for(auto w : QGuiApplication::topLevelWindows()){
|
||||
if( (w->windowState()&Qt::WindowFullScreen)==Qt::WindowFullScreen){
|
||||
showAsTool = true;
|
||||
w->raise();// Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the only way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not available)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for(int i = 0 ; i < allScreens.size() ; ++i){
|
||||
QQuickView *view = new QQuickView(App::getInstance()->getEngine(), nullptr); // Use QQuickView to create a visual root object that is independant from current application Window
|
||||
QScreen *screen = allScreens[i];
|
||||
QObject::connect(view, &QQuickView::statusChanged, [allScreens](QQuickView::Status status){ // Debug handler : show screens descriptions on Error
|
||||
if( status == QQuickView::Error){
|
||||
QScreen * primaryScreen = QGuiApplication::primaryScreen();
|
||||
qInfo() << "Primary screen : " << primaryScreen->geometry() << primaryScreen->availableGeometry() << primaryScreen->virtualGeometry() << primaryScreen->availableVirtualGeometry();
|
||||
for(int i = 0 ; i < allScreens.size() ; ++i){
|
||||
QScreen *screen = allScreens[i];
|
||||
qInfo() << QString("Screen [")+QString::number(i)+"] (hdpi, Geometry, Available, Virtual, AvailableGeometry) :"
|
||||
<< screen->devicePixelRatio() << screen->geometry() << screen->availableGeometry() << screen->virtualGeometry() << screen->availableVirtualGeometry();
|
||||
}
|
||||
}
|
||||
});
|
||||
view->setScreen(screen); // Bind the visual root object to the screen
|
||||
view->setProperty("flags", QVariant(Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::X11BypassWindowManagerHint)); // Set the visual ghost window
|
||||
view->setSource(QString(NotificationsPath)+Notifier::Notifications[type].filename);
|
||||
|
||||
QQuickWindow *subWindow = view->findChild<QQuickWindow *>("__internalWindow");
|
||||
QObject::connect(subWindow, &QObject::destroyed, view, &QObject::deleteLater); // When destroying window, detroy visual root object too
|
||||
|
||||
int * screenHeightOffset = &gScreenHeightOffset[screen->name()]; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
int heightOffset = availableGeometry.y() + (availableGeometry.height() - subWindow->height());//*screen->devicePixelRatio(); when using manual scaler
|
||||
if(showAsTool)
|
||||
subWindow->setProperty("showAsTool",true);
|
||||
subWindow->setX(availableGeometry.x()+ (availableGeometry.width()-subWindow->property("width").toInt()));//*screen->devicePixelRatio()); when using manual scaler
|
||||
subWindow->setY(heightOffset-(*screenHeightOffset % heightOffset));
|
||||
|
||||
*screenHeightOffset = (subWindow->height() + *screenHeightOffset) + NotificationSpacing;
|
||||
if (*screenHeightOffset - heightOffset + availableGeometry.y() >= 0)
|
||||
*screenHeightOffset = 0;
|
||||
|
||||
// if(primaryScreen != screen){ //Useful when doing manual scaling jobs. Need to implement scaler in GUI objects
|
||||
// //subwindow->setProperty("xScale", (double)screen->availableVirtualGeometry().width()/availableGeometry.width() );
|
||||
// //subwindow->setProperty("yScale", (double)screen->availableVirtualGeometry().height()/availableGeometry.height());
|
||||
// }
|
||||
wrapperItem = view->findChild<QQuickItem *>("__internalWrapper");
|
||||
::setProperty(*wrapperItem, NotificationPropertyData,data);
|
||||
view->setGeometry(subWindow->geometry()); // Ensure to have sufficient space to both let painter do job without error, and stay behind popup
|
||||
|
||||
if(previousWrapper!=nullptr){ // Link objects in order to propagate events without having to store them
|
||||
QObject::connect(previousWrapper, SIGNAL(deleteNotification(QVariant)), wrapperItem,SLOT(deleteNotificationSlot()));
|
||||
QObject::connect(wrapperItem, SIGNAL(isOpened()), previousWrapper,SLOT(open()));
|
||||
QObject::connect(wrapperItem, SIGNAL(isClosed()), previousWrapper,SLOT(close()));
|
||||
QObject::connect(wrapperItem, &QObject::destroyed, previousWrapper, &QObject::deleteLater);
|
||||
}
|
||||
previousWrapper = wrapperItem; // The last one is used as a point of start when deleting and openning
|
||||
|
||||
view->show();
|
||||
}
|
||||
qInfo() << QStringLiteral("Create notifications:") << wrapperItem;
|
||||
}
|
||||
return wrapperItem;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NOTIFICATIONS_DEFAULT_H_
|
||||
#define NOTIFICATIONS_DEFAULT_H_
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "components/notifier/Notifier.hpp"
|
||||
// =============================================================================
|
||||
|
||||
class NotificationsDefault : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NotificationsDefault (QObject *parent = Q_NULLPTR);
|
||||
|
||||
static QObject *create(Notifier::NotificationType type, QVariantMap data);
|
||||
|
||||
static QHash<QString,int> gScreenHeightOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* ScreenSaverMacOS.m
|
||||
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Created on: August 3, 2018
|
||||
* Author: Ronan Abhamon
|
||||
*/
|
||||
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static bool ScreenSaverEnabled = true;
|
||||
static IOPMAssertionID AssertionID;
|
||||
|
||||
bool enableScreenSaverMacOs () {
|
||||
if (ScreenSaverEnabled)
|
||||
return true;
|
||||
|
||||
ScreenSaverEnabled = IOPMAssertionRelease(AssertionID) == kIOReturnSuccess;
|
||||
return ScreenSaverEnabled;
|
||||
}
|
||||
|
||||
bool disableScreenSaverMacOs () {
|
||||
if (!ScreenSaverEnabled)
|
||||
return true;
|
||||
|
||||
ScreenSaverEnabled = IOPMAssertionCreateWithName(
|
||||
kIOPMAssertionTypeNoDisplaySleep,
|
||||
kIOPMAssertionLevelOn,
|
||||
CFSTR("Inhibit asked for video stream"),
|
||||
&AssertionID
|
||||
) != kIOReturnSuccess;
|
||||
return !ScreenSaverEnabled;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue