diff --git a/qtox.pro b/qtox.pro index b37a05c6a..aa759ec46 100644 --- a/qtox.pro +++ b/qtox.pro @@ -237,6 +237,30 @@ contains(ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND, NO) { } } +# The systray GTK backend implements a system tray icon compatible with many systems +unix:!macx:!android { +contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) { +} else { + DEFINES += ENABLE_SYSTRAY_GTK_BACKEND + + INCLUDEPATH += "/usr/include/gtk-2.0" + INCLUDEPATH += "/usr/include/glib-2.0" + INCLUDEPATH += "/usr/lib/glib-2.0/include" + INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include" + INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include" + INCLUDEPATH += "/usr/lib/gtk-2.0/include" + INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include" + INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include" + INCLUDEPATH += "/usr/include/atk-1.0" + INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0" + INCLUDEPATH += "/usr/include/cairo" + INCLUDEPATH += "/usr/include/pango-1.0" + + + LIBS += -lglib-2.0 -lgdk_pixbuf-2.0 -lgio-2.0 -lcairo -lgtk-x11-2.0 -lgdk-x11-2.0 -lgobject-2.0 +} +} + !android { HEADERS += \ src/friend.h \ diff --git a/src/widget/systemtrayicon.cpp b/src/widget/systemtrayicon.cpp index 96d21e82b..0ae4e957c 100644 --- a/src/widget/systemtrayicon.cpp +++ b/src/widget/systemtrayicon.cpp @@ -37,6 +37,42 @@ SystemTrayIcon::SystemTrayIcon() app_indicator_set_menu(unityIndicator, GTK_MENU(unityMenu)); } #endif + #ifdef ENABLE_SYSTRAY_GTK_BACKEND + else if (desktop == "xfce" || (desktop == "kde" + && getenv("KDE_SESSION_VERSION") == QString("4"))) + { + qDebug() << "SystemTrayIcon: Using GTK backend"; + backendType = SystrayBackendType::GTK; + gtk_init(nullptr, nullptr); + void (*callbackFreeImage)(guchar*, gpointer) = + [](guchar*, gpointer image) + { + delete reinterpret_cast(image); + }; + QImage* image = new QImage(":/img/icon.png"); + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(image->bits(), GDK_COLORSPACE_RGB, image->hasAlphaChannel(), + 8, image->width(), image->height(), + image->bytesPerLine(), callbackFreeImage, image); + gtkIcon = gtk_status_icon_new_from_pixbuf(pixbuf); + gtkMenu = gtk_menu_new(); + + void (*callbackTrigger)(GtkStatusIcon*, gpointer) = + [](GtkStatusIcon*, gpointer data) + { + ((SystemTrayIcon*)data)->activated(QSystemTrayIcon::Trigger); + }; + g_signal_connect(gtkIcon, "activate", G_CALLBACK(callbackTrigger), this); + void (*callbackButtonClick)(GtkStatusIcon*, GdkEvent*, gpointer) = + [](GtkStatusIcon*, GdkEvent* event, gpointer data) + { + if (event->button.button == 2) + ((SystemTrayIcon*)data)->activated(QSystemTrayIcon::MiddleClick); + }; + g_signal_connect(gtkIcon, "button-release-event", G_CALLBACK(callbackButtonClick), this); + } + #endif #ifdef ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND else if (desktop == "kde" && getenv("KDE_SESSION_VERSION") == QString("5")) @@ -156,6 +192,51 @@ void SystemTrayIcon::setContextMenu(QMenu* menu) g_signal_connect(statusNotifier, "context-menu", G_CALLBACK(callbackMenu), this); } #endif + #ifdef ENABLE_SYSTRAY_GTK_BACKEND + else if (backendType == SystrayBackendType::GTK) + { + for (QAction* a : menu->actions()) + { + QString aText = a->text().replace('&',""); + GtkWidget* item; + if (a->isSeparator()) + item = gtk_menu_item_new(); + else if (a->icon().isNull()) + item = gtk_menu_item_new_with_label(aText.toStdString().c_str()); + else + { + void (*callbackFreeImage)(guchar*, gpointer) = + [](guchar*, gpointer image) + { + delete reinterpret_cast(image); + }; + QImage* image = new QImage(a->icon().pixmap(64, 64).toImage()); + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(image->bits(), GDK_COLORSPACE_RGB, image->hasAlphaChannel(), + 8, image->width(), image->height(), + image->bytesPerLine(), callbackFreeImage, image); + item = gtk_image_menu_item_new_with_label(aText.toStdString().c_str()); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_pixbuf(pixbuf)); + gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item),TRUE); + } + gtk_menu_shell_append(GTK_MENU_SHELL(gtkMenu), item); + void (*callback)(GtkMenu*, gpointer data) = [](GtkMenu*, gpointer a) + { + ((QAction*)a)->activate(QAction::Trigger); + }; + g_signal_connect(item, "activate", G_CALLBACK(callback), a); + gtk_widget_show(item); + } + void (*callbackMenu)(StatusNotifier*, gint, gint, gpointer) = + [](StatusNotifier*, gint, gint, gpointer data) + { + gtk_widget_show_all(((SystemTrayIcon*)data)->gtkMenu); + gtk_menu_popup(GTK_MENU(((SystemTrayIcon*)data)->gtkMenu), 0, 0, 0, 0, 3, gtk_get_current_event_time()); + }; + g_signal_connect(gtkIcon, "popup-menu", G_CALLBACK(callbackMenu), this); + } + #endif #ifdef ENABLE_SYSTRAY_UNITY_BACKEND else if (backendType == SystrayBackendType::Unity) { @@ -224,6 +305,15 @@ void SystemTrayIcon::setVisible(bool newState) status_notifier_set_status(statusNotifier, STATUS_NOTIFIER_STATUS_PASSIVE); } #endif + #ifdef ENABLE_SYSTRAY_GTK_BACKEND + else if (backendType == SystrayBackendType::GTK) + { + if (newState) + gtk_status_icon_set_visible(gtkIcon, true); + else + gtk_status_icon_set_visible(gtkIcon, false); + } + #endif #ifdef ENABLE_SYSTRAY_UNITY_BACKEND else if (backendType == SystrayBackendType::Unity) { @@ -262,6 +352,23 @@ void SystemTrayIcon::setIcon(QIcon &&icon) status_notifier_set_from_pixbuf(statusNotifier, STATUS_NOTIFIER_ICON, pixbuf); } #endif + #ifdef ENABLE_SYSTRAY_GTK_BACKEND + else if (backendType == SystrayBackendType::GTK) + { + void (*callbackFreeImage)(guchar*, gpointer) = + [](guchar*, gpointer image) + { + delete reinterpret_cast(image); + }; + QImage* image = new QImage(icon.pixmap(64, 64).toImage()); + if (image->format() != QImage::Format_RGBA8888_Premultiplied) + *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(image->bits(), GDK_COLORSPACE_RGB, image->hasAlphaChannel(), + 8, image->width(), image->height(), + image->bytesPerLine(), callbackFreeImage, image); + gtk_status_icon_set_from_pixbuf(gtkIcon, pixbuf); + } + #endif #ifdef ENABLE_SYSTRAY_UNITY_BACKEND else if (backendType == SystrayBackendType::Unity) { diff --git a/src/widget/systemtrayicon.h b/src/widget/systemtrayicon.h index 66186fa26..1d062acfa 100644 --- a/src/widget/systemtrayicon.h +++ b/src/widget/systemtrayicon.h @@ -36,6 +36,10 @@ private: StatusNotifier* statusNotifier; GtkWidget* snMenu; #endif +#ifdef ENABLE_SYSTRAY_GTK_BACKEND + GtkStatusIcon* gtkIcon; + GtkWidget* gtkMenu; +#endif }; #endif // SYSTEMTRAYICON_H diff --git a/src/widget/systemtrayicon_private.h b/src/widget/systemtrayicon_private.h index e84389f84..ca803113b 100644 --- a/src/widget/systemtrayicon_private.h +++ b/src/widget/systemtrayicon_private.h @@ -30,6 +30,17 @@ extern "C" { #define signals public #endif +#ifdef ENABLE_SYSTRAY_GTK_BACKEND +#ifdef signals +#undef signals +#endif +extern "C" { + #include + #include +} +#define signals public +#endif + enum class SystrayBackendType { Qt, @@ -40,15 +51,9 @@ enum class SystrayBackendType #ifdef ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND StatusNotifier, #endif -}; - -union SystrayBackend -{ - QSystemTrayIcon *qt; -#ifdef ENABLE_SYSTRAY_UNITY_BACKEND - AppIndicator *unity; +#ifdef ENABLE_SYSTRAY_GTK_BACKEND + GTK, #endif }; - #endif // SYSTEMTRAYICON_PRIVATE_H