github.com/shurcool/trayhost@v0.0.0-20181020202213-114974ef9e16/platform/linux/tray.c (about)

     1  #ifndef NATIVE_C
     2  #define NATIVE_C
     3  
     4  #include <stdio.h>
     5  #include <dlfcn.h>
     6  #include <unistd.h>
     7  #include <gtk/gtk.h>
     8  #include <gio/gio.h>
     9  #include <gdk-pixbuf/gdk-pixbuf.h>
    10  #include <string.h>
    11  #include <libappindicator/app-indicator.h>
    12  
    13  static const char *icon = NULL;
    14  static size_t iconSize = 0;
    15  static const char *menu_title = NULL;
    16  static const char *url = NULL;
    17  static GtkWidget *menu = NULL;
    18  char tmpIconNameBuf[32];
    19  
    20  // implemented in go
    21  extern void tray_callback(int itemId);
    22  
    23  // internal wrapper for go callback
    24  void _tray_callback(GtkMenuItem *item, gpointer user_data)
    25  {
    26    tray_callback(GPOINTER_TO_INT(user_data));
    27  }
    28  
    29  // TODO: Implement.
    30  void display_notification(int id, const char* title, const char* body, struct image imageData, double duration)
    31  {
    32  }
    33  
    34  // TODO: Implement.
    35  void clear_menu_items() {}
    36  
    37  // TODO: Implement.
    38  struct clipboard_content get_clipboard_content()
    39  {
    40    struct clipboard_content cc;
    41    return cc;
    42  }
    43  
    44  // TODO: Implement.
    45  void set_clipboard_string(const char* cp)
    46  {
    47  }
    48  
    49  void add_menu_item(int id, const char* title, int disabled) {
    50    GtkWidget *item = gtk_menu_item_new_with_label(title);
    51    if (disabled == TRUE) {
    52       gtk_widget_set_sensitive(item, FALSE);
    53    }
    54    g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(_tray_callback), GINT_TO_POINTER(id));
    55    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    56  }
    57  
    58  void add_separator_item() {
    59    gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
    60  }
    61  
    62  typedef void* (*app_indicator_new_fun)(const gchar*, const gchar*, AppIndicatorCategory);
    63  typedef void* (*app_indicator_set_status_fun)(AppIndicator*, AppIndicatorStatus);
    64  typedef void* (*app_indicator_set_attention_icon_full_fun) (AppIndicator*,  const gchar* ,const gchar*);
    65  typedef void* (*app_indicator_set_menu_fun)(AppIndicator*,GtkMenu*);
    66  
    67  void create_indicator(void *handle)
    68  {
    69    app_indicator_new_fun                       app_indicator_new;
    70    app_indicator_set_status_fun                app_indicator_set_status;
    71    app_indicator_set_menu_fun                  app_indicator_set_menu;
    72  
    73    app_indicator_new = dlsym(handle, "app_indicator_new");
    74    app_indicator_set_status = dlsym(handle, "app_indicator_set_status");
    75    app_indicator_set_menu = dlsym(handle, "app_indicator_set_menu");
    76  
    77    // write icon to temp file, otherwise imposible to set in libappindicator
    78    int fd = -1;
    79    memset(tmpIconNameBuf, 0, sizeof(tmpIconNameBuf));
    80    strncpy(tmpIconNameBuf,"/tmp/storageguiicon-XXXXXX",26);
    81    fd = mkstemp(tmpIconNameBuf);
    82  
    83    if (fd > 0) {
    84      if(write(fd, icon, iconSize) == -1) {
    85        fprintf(stderr, "Failed to write icon data into temp file\n");
    86      }
    87    } else {
    88      fprintf(stderr, "Failed to create temp file for icon\n");
    89    }
    90  
    91    AppIndicator *indicator = app_indicator_new (menu_title,
    92                                   tmpIconNameBuf,
    93                                   APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
    94  
    95    app_indicator_set_status (indicator, APP_INDICATOR_STATUS_ACTIVE);
    96    app_indicator_set_menu (indicator, GTK_MENU (menu));
    97  }
    98  
    99  static void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
   100  {
   101      gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
   102  }
   103  
   104  void create_status_icon()
   105  {
   106      GError *error = NULL;
   107      GInputStream *stream = g_memory_input_stream_new_from_data(icon, iconSize, NULL);
   108      GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
   109      if (error)
   110          fprintf(stderr, "Unable to create PixBuf: %s\n", error->message);
   111  
   112  
   113      GtkStatusIcon *tray_icon = gtk_status_icon_new_from_pixbuf(pixbuf);
   114      g_signal_connect(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(tray_icon_on_menu), NULL);
   115      gtk_status_icon_set_tooltip_text(tray_icon, menu_title);
   116      gtk_status_icon_set_visible(tray_icon, TRUE);
   117  }
   118  
   119  void init(const char* title, struct image imageData)
   120  {
   121      int argc = 0;
   122      char *argv[] = { "" };
   123      gtk_init(&argc, (char***)&argv);
   124  
   125      menu_title = title;
   126      icon = imageData.bytes;
   127      iconSize = imageData.length;
   128      menu = gtk_menu_new();
   129      void *handle;
   130  
   131      // check if system has libappindicator1 package
   132      handle = dlopen("libappindicator.so.1", RTLD_LAZY);
   133      if(!handle) {
   134        create_status_icon();
   135      } else {
   136        create_indicator(handle);
   137      }
   138  }
   139  
   140  void native_loop()
   141  {
   142    gtk_widget_show_all(menu);
   143    gtk_main ();
   144  }
   145  
   146  void exit_loop()
   147  {
   148    gtk_main_quit();
   149  }
   150  
   151  
   152  #endif // NATIVE_C