diff options
Diffstat (limited to 'vendor/github.com/getlantern/systray/systray_linux.c')
-rw-r--r-- | vendor/github.com/getlantern/systray/systray_linux.c | 217 |
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); +} |