186 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2015 GitHub, Inc.
 | |
| // Use of this source code is governed by the MIT license that can be
 | |
| // found in the LICENSE file.
 | |
| 
 | |
| #include "shell/browser/notifications/linux/libnotify_notification.h"
 | |
| 
 | |
| #include <set>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include "base/files/file_enumerator.h"
 | |
| #include "base/logging.h"
 | |
| #include "base/strings/string_util.h"
 | |
| #include "base/strings/utf_string_conversions.h"
 | |
| #include "chrome/browser/ui/libgtkui/gtk_util.h"
 | |
| #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
 | |
| #include "shell/browser/notifications/notification_delegate.h"
 | |
| #include "shell/common/application_info.h"
 | |
| #include "shell/common/platform_util.h"
 | |
| #include "third_party/skia/include/core/SkBitmap.h"
 | |
| 
 | |
| namespace electron {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| LibNotifyLoader libnotify_loader_;
 | |
| 
 | |
| const std::set<std::string>& GetServerCapabilities() {
 | |
|   static std::set<std::string> caps;
 | |
|   if (caps.empty()) {
 | |
|     auto* capabilities = libnotify_loader_.notify_get_server_caps();
 | |
|     for (auto* l = capabilities; l != nullptr; l = l->next)
 | |
|       caps.insert(static_cast<const char*>(l->data));
 | |
|     g_list_free_full(capabilities, g_free);
 | |
|   }
 | |
|   return caps;
 | |
| }
 | |
| 
 | |
| bool HasCapability(const std::string& capability) {
 | |
|   return GetServerCapabilities().count(capability) != 0;
 | |
| }
 | |
| 
 | |
| bool NotifierSupportsActions() {
 | |
|   if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER"))
 | |
|     return false;
 | |
| 
 | |
|   return HasCapability("actions");
 | |
| }
 | |
| 
 | |
| void log_and_clear_error(GError* error, const char* context) {
 | |
|   LOG(ERROR) << context << ": domain=" << error->domain
 | |
|              << " code=" << error->code << " message=\"" << error->message
 | |
|              << '"';
 | |
|   g_error_free(error);
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| // static
 | |
| bool LibnotifyNotification::Initialize() {
 | |
|   if (!libnotify_loader_.Load("libnotify.so.4") &&  // most common one
 | |
|       !libnotify_loader_.Load("libnotify.so.5") &&
 | |
|       !libnotify_loader_.Load("libnotify.so.1") &&
 | |
|       !libnotify_loader_.Load("libnotify.so")) {
 | |
|     LOG(WARNING) << "Unable to find libnotify; notifications disabled";
 | |
|     return false;
 | |
|   }
 | |
|   if (!libnotify_loader_.notify_is_initted() &&
 | |
|       !libnotify_loader_.notify_init(GetApplicationName().c_str())) {
 | |
|     LOG(WARNING) << "Unable to initialize libnotify; notifications disabled";
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate,
 | |
|                                              NotificationPresenter* presenter)
 | |
|     : Notification(delegate, presenter), notification_(nullptr) {}
 | |
| 
 | |
| LibnotifyNotification::~LibnotifyNotification() {
 | |
|   if (notification_) {
 | |
|     g_signal_handlers_disconnect_by_data(notification_, this);
 | |
|     g_object_unref(notification_);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void LibnotifyNotification::Show(const NotificationOptions& options) {
 | |
|   notification_ = libnotify_loader_.notify_notification_new(
 | |
|       base::UTF16ToUTF8(options.title).c_str(),
 | |
|       base::UTF16ToUTF8(options.msg).c_str(), nullptr);
 | |
| 
 | |
|   g_signal_connect(notification_, "closed",
 | |
|                    G_CALLBACK(OnNotificationClosedThunk), this);
 | |
| 
 | |
|   // NB: On Unity and on any other DE using Notify-OSD, adding a notification
 | |
|   // action will cause the notification to display as a modal dialog box.
 | |
|   if (NotifierSupportsActions()) {
 | |
|     libnotify_loader_.notify_notification_add_action(
 | |
|         notification_, "default", "View", OnNotificationViewThunk, this,
 | |
|         nullptr);
 | |
|   }
 | |
| 
 | |
|   NotifyUrgency urgency = NOTIFY_URGENCY_NORMAL;
 | |
|   if (options.urgency == base::ASCIIToUTF16("critical")) {
 | |
|     urgency = NOTIFY_URGENCY_CRITICAL;
 | |
|   } else if (options.urgency == base::ASCIIToUTF16("low")) {
 | |
|     urgency = NOTIFY_URGENCY_LOW;
 | |
|   }
 | |
| 
 | |
|   // Set the urgency level of the notification.
 | |
|   libnotify_loader_.notify_notification_set_urgency(notification_, urgency);
 | |
| 
 | |
|   if (!options.icon.drawsNothing()) {
 | |
|     GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(options.icon);
 | |
|     libnotify_loader_.notify_notification_set_image_from_pixbuf(notification_,
 | |
|                                                                 pixbuf);
 | |
|     libnotify_loader_.notify_notification_set_timeout(notification_,
 | |
|                                                       NOTIFY_EXPIRES_DEFAULT);
 | |
|     g_object_unref(pixbuf);
 | |
|   }
 | |
| 
 | |
|   if (!options.tag.empty()) {
 | |
|     GQuark id = g_quark_from_string(options.tag.c_str());
 | |
|     g_object_set(G_OBJECT(notification_), "id", id, NULL);
 | |
|   }
 | |
| 
 | |
|   // Always try to append notifications.
 | |
|   // Unique tags can be used to prevent this.
 | |
|   if (HasCapability("append")) {
 | |
|     libnotify_loader_.notify_notification_set_hint_string(notification_,
 | |
|                                                           "append", "true");
 | |
|   } else if (HasCapability("x-canonical-append")) {
 | |
|     libnotify_loader_.notify_notification_set_hint_string(
 | |
|         notification_, "x-canonical-append", "true");
 | |
|   }
 | |
| 
 | |
|   // Send the desktop name to identify the application
 | |
|   // The desktop-entry is the part before the .desktop
 | |
|   std::string desktop_id;
 | |
|   if (platform_util::GetDesktopName(&desktop_id)) {
 | |
|     const std::string suffix{".desktop"};
 | |
|     if (base::EndsWith(desktop_id, suffix,
 | |
|                        base::CompareCase::INSENSITIVE_ASCII)) {
 | |
|       desktop_id.resize(desktop_id.size() - suffix.size());
 | |
|     }
 | |
|     libnotify_loader_.notify_notification_set_hint_string(
 | |
|         notification_, "desktop-entry", desktop_id.c_str());
 | |
|   }
 | |
| 
 | |
|   GError* error = nullptr;
 | |
|   libnotify_loader_.notify_notification_show(notification_, &error);
 | |
|   if (error) {
 | |
|     log_and_clear_error(error, "notify_notification_show");
 | |
|     NotificationFailed();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (delegate())
 | |
|     delegate()->NotificationDisplayed();
 | |
| }
 | |
| 
 | |
| void LibnotifyNotification::Dismiss() {
 | |
|   if (!notification_) {
 | |
|     Destroy();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   GError* error = nullptr;
 | |
|   libnotify_loader_.notify_notification_close(notification_, &error);
 | |
|   if (error) {
 | |
|     log_and_clear_error(error, "notify_notification_close");
 | |
|     Destroy();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void LibnotifyNotification::OnNotificationClosed(
 | |
|     NotifyNotification* notification) {
 | |
|   NotificationDismissed();
 | |
| }
 | |
| 
 | |
| void LibnotifyNotification::OnNotificationView(NotifyNotification* notification,
 | |
|                                                char* action) {
 | |
|   NotificationClicked();
 | |
| }
 | |
| 
 | |
| }  // namespace electron
 | 
