summaryrefslogtreecommitdiff
path: root/vendor/github.com/getlantern/systray/systray_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/getlantern/systray/systray_linux.c')
-rw-r--r--vendor/github.com/getlantern/systray/systray_linux.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/vendor/github.com/getlantern/systray/systray_linux.c b/vendor/github.com/getlantern/systray/systray_linux.c
new file mode 100644
index 0000000..72cd614
--- /dev/null
+++ b/vendor/github.com/getlantern/systray/systray_linux.c
@@ -0,0 +1,217 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <libappindicator/app-indicator.h>
+#include "systray.h"
+
+static AppIndicator *global_app_indicator;
+static GtkWidget *global_tray_menu = NULL;
+static GList *global_menu_items = NULL;
+static char temp_file_name[PATH_MAX] = "";
+
+typedef struct {
+ GtkWidget *menu_item;
+ int menu_id;
+} MenuItemNode;
+
+typedef struct {
+ int menu_id;
+ char* title;
+ char* tooltip;
+ short disabled;
+ short checked;
+} MenuItemInfo;
+
+int nativeLoop(void) {
+ gtk_init(0, NULL);
+ global_app_indicator = app_indicator_new("systray", "",
+ APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
+ app_indicator_set_status(global_app_indicator, APP_INDICATOR_STATUS_ACTIVE);
+ global_tray_menu = gtk_menu_new();
+ app_indicator_set_menu(global_app_indicator, GTK_MENU(global_tray_menu));
+ systray_ready();
+ gtk_main();
+ systray_on_exit();
+ return 0;
+}
+
+void _unlink_temp_file() {
+ if (strlen(temp_file_name) != 0) {
+ int ret = unlink(temp_file_name);
+ if (ret == -1) {
+ printf("failed to remove temp icon file %s: %s\n", temp_file_name, strerror(errno));
+ }
+ temp_file_name[0] = '\0';
+ }
+}
+
+// runs in main thread, should always return FALSE to prevent gtk to execute it again
+gboolean do_set_icon(gpointer data) {
+ _unlink_temp_file();
+ char *tmpdir = getenv("TMPDIR");
+ if (NULL == tmpdir) {
+ tmpdir = "/tmp";
+ }
+ strncpy(temp_file_name, tmpdir, PATH_MAX-1);
+ strncat(temp_file_name, "/systray_XXXXXX", PATH_MAX-1);
+ temp_file_name[PATH_MAX-1] = '\0';
+
+ GBytes* bytes = (GBytes*)data;
+ int fd = mkstemp(temp_file_name);
+ if (fd == -1) {
+ printf("failed to create temp icon file %s: %s\n", temp_file_name, strerror(errno));
+ return FALSE;
+ }
+ gsize size = 0;
+ gconstpointer icon_data = g_bytes_get_data(bytes, &size);
+ ssize_t written = write(fd, icon_data, size);
+ close(fd);
+ if(written != size) {
+ printf("failed to write temp icon file %s: %s\n", temp_file_name, strerror(errno));
+ return FALSE;
+ }
+ app_indicator_set_icon_full(global_app_indicator, temp_file_name, "");
+ app_indicator_set_attention_icon_full(global_app_indicator, temp_file_name, "");
+ g_bytes_unref(bytes);
+ return FALSE;
+}
+
+void _systray_menu_item_selected(int *id) {
+ systray_menu_item_selected(*id);
+}
+
+// runs in main thread, should always return FALSE to prevent gtk to execute it again
+gboolean do_add_or_update_menu_item(gpointer data) {
+ MenuItemInfo *mii = (MenuItemInfo*)data;
+ GList* it;
+ for(it = global_menu_items; it != NULL; it = it->next) {
+ MenuItemNode* item = (MenuItemNode*)(it->data);
+ if(item->menu_id == mii->menu_id){
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item->menu_item), mii->title);
+ break;
+ }
+ }
+
+ // menu id doesn't exist, add new item
+ if(it == NULL) {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(mii->title);
+ int *id = malloc(sizeof(int));
+ *id = mii->menu_id;
+ g_signal_connect_swapped(G_OBJECT(menu_item), "activate", G_CALLBACK(_systray_menu_item_selected), id);
+ gtk_menu_shell_append(GTK_MENU_SHELL(global_tray_menu), menu_item);
+
+ MenuItemNode* new_item = malloc(sizeof(MenuItemNode));
+ new_item->menu_id = mii->menu_id;
+ new_item->menu_item = menu_item;
+ GList* new_node = malloc(sizeof(GList));
+ new_node->data = new_item;
+ new_node->next = global_menu_items;
+ if(global_menu_items != NULL) {
+ global_menu_items->prev = new_node;
+ }
+ global_menu_items = new_node;
+ it = new_node;
+ }
+ GtkWidget * menu_item = GTK_WIDGET(((MenuItemNode*)(it->data))->menu_item);
+ gtk_widget_set_sensitive(menu_item, mii->disabled == 1 ? FALSE : TRUE);
+ gtk_widget_show(menu_item);
+
+ free(mii->title);
+ free(mii->tooltip);
+ free(mii);
+ return FALSE;
+}
+
+gboolean do_add_separator(gpointer data) {
+ GtkWidget *separator = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(global_tray_menu), separator);
+ gtk_widget_show(separator);
+ return FALSE;
+}
+
+// runs in main thread, should always return FALSE to prevent gtk to execute it again
+gboolean do_hide_menu_item(gpointer data) {
+ MenuItemInfo *mii = (MenuItemInfo*)data;
+ GList* it;
+ for(it = global_menu_items; it != NULL; it = it->next) {
+ MenuItemNode* item = (MenuItemNode*)(it->data);
+ if(item->menu_id == mii->menu_id){
+ gtk_widget_hide(GTK_WIDGET(item->menu_item));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+// runs in main thread, should always return FALSE to prevent gtk to execute it again
+gboolean do_show_menu_item(gpointer data) {
+ MenuItemInfo *mii = (MenuItemInfo*)data;
+ GList* it;
+ for(it = global_menu_items; it != NULL; it = it->next) {
+ MenuItemNode* item = (MenuItemNode*)(it->data);
+ if(item->menu_id == mii->menu_id){
+ gtk_widget_show(GTK_WIDGET(item->menu_item));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+// runs in main thread, should always return FALSE to prevent gtk to execute it again
+gboolean do_quit(gpointer data) {
+ _unlink_temp_file();
+ // app indicator doesn't provide a way to remove it, hide it as a workaround
+ app_indicator_set_status(global_app_indicator, APP_INDICATOR_STATUS_PASSIVE);
+ gtk_main_quit();
+ return FALSE;
+}
+
+void setIcon(const char* iconBytes, int length) {
+ GBytes* bytes = g_bytes_new_static(iconBytes, length);
+ g_idle_add(do_set_icon, bytes);
+}
+
+void setTitle(char* ctitle) {
+ app_indicator_set_label(global_app_indicator, ctitle, "");
+ free(ctitle);
+}
+
+void setTooltip(char* ctooltip) {
+ free(ctooltip);
+}
+
+void setMenuItemIcon(const char* iconBytes, int length, int menuId) {
+}
+
+void add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disabled, short checked) {
+ MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
+ mii->menu_id = menu_id;
+ mii->title = title;
+ mii->tooltip = tooltip;
+ mii->disabled = disabled;
+ mii->checked = checked;
+ g_idle_add(do_add_or_update_menu_item, mii);
+}
+
+void add_separator(int menu_id) {
+ MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
+ mii->menu_id = menu_id;
+ g_idle_add(do_add_separator, mii);
+}
+
+void hide_menu_item(int menu_id) {
+ MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
+ mii->menu_id = menu_id;
+ g_idle_add(do_hide_menu_item, mii);
+}
+
+void show_menu_item(int menu_id) {
+ MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
+ mii->menu_id = menu_id;
+ g_idle_add(do_show_menu_item, mii);
+}
+
+void quit() {
+ g_idle_add(do_quit, NULL);
+}