Ubuntu – How to develop a System Indicator for Unity

application-developmentcindicatorpythonunity

This is not a duplicate of How to create Unity indicators?. I'm looking for system indicator not application indicator.

Backgound:

From these two questions:

I learned that there are two types of indicators:

All application indicators are handled/showed by indicator-application (a system one). System indicators are showed directly by Unity panel.

Both questions are about adding/removing indicators from login & lock screen. The first was a straight forward setup (when dealing with system indicators). The second was a tough setup (when dealing with app indicators), which needs modifying the source of panel service (unity package) for lock screen and the source of unity-greeter for login screen.

As the case of sysmonitor, to me that was workaround. The best solution is to implement a system indicator instead of an application one.

Topic:

  • Is there a unified API for system indicators (Preferably: Python then C/C++)? Please, reference to the official documentations.

  • Most system indicators are written using Vala programming language. Could any one write small demo for a system indicator using Python or C?

Update:

I found few links that may give a push:

  • In Application Indicators project page, They listed links to AppIndicator-0.3 API (C & Python) used for application indicators.

    They listed also Indicate-0.7 API (C & Python). What is this? Well, it is a DBus messaging channel between Desktop Applications.

  • On other hand, in System Indicators project page, they mentioned:

    System Indicator APIs

    • Messaging Menu using libindicate.
    • Sound Menu using libunity.
    • Date/Time Indicator using Evolution-Data-Server

    They seem listing Data API not Indicator Development API like for Evolution-Data-Server. But not sure about libindicate & libunity. Did any one work with these two libraries?

    Try apt-cache rdepends libunity9 libindicator7 libindicator3-7 to see which indicator is relaying these libraries.

Update2: This to keep interested users updated.

From what I have collected till now, here is the order of possible solutions:

  1. libindicator3-7 (high, Many indicators depend on it)

    I found some test examples in the source, some dummy indicators that I have tried, could be installed in /usr/lib/indicators3/7/, they are shared lib .so. I could get them displayed in Login & Regular Session but Not in Lock Screen.

    However there are some test indicator services, which seems like Unity system ones. I haven't tried them yet.

  2. libindicator7

    From same source as libindicator3-7, from rdepends:

    mate-indicator-applet
    lxpanel-indicator-applet-plugin
    

    It seems to be used to make container for indicators in panels.

  3. libunity9 (low)

    No research yet

Best Answer

  • System Indicator Service

    Well, it is really simpler then I expected. There is no specific API for it. Because it is just a GSimpleActionGroup & with corresponding GMenu's exported through DBus then Unity is told about their presence using declaration file with same name put in /usr/share/unity/indicators/. No need for any other library.

    Here a very small C language example:

    1. Get a copy of tests/indicator-test-service.c from libindicator source

      apt-get source libindicator
      cp libindicator-*/tests/indicator-test-service.c .
      cp libindicator-*/tests/com.canonical.indicator.test* .
      
      • indicator-test-service.c no changes

        #include <gio/gio.h>
        
        typedef struct
        {
          GSimpleActionGroup *actions;
          GMenu *menu;
        
          guint actions_export_id;
          guint menu_export_id;
        } IndicatorTestService;
        
        static void
        bus_acquired (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
        {
          IndicatorTestService *indicator = user_data;
          GError *error = NULL;
        
          indicator->actions_export_id = g_dbus_connection_export_action_group (connection,
                                                                                "/com/canonical/indicator/test",
                                                                                G_ACTION_GROUP (indicator->actions),
                                                                                &error);
          if (indicator->actions_export_id == 0)
            {
              g_warning ("cannot export action group: %s", error->message);
              g_error_free (error);
              return;
            }
        
          indicator->menu_export_id = g_dbus_connection_export_menu_model (connection,
                                                                           "/com/canonical/indicator/test/desktop",
                                                                           G_MENU_MODEL (indicator->menu),
                                                                           &error);
          if (indicator->menu_export_id == 0)
            {
              g_warning ("cannot export menu: %s", error->message);
              g_error_free (error);
              return;
            }
        }
        
        static void
        name_lost (GDBusConnection *connection,
                   const gchar     *name,
                   gpointer         user_data)
        {
          IndicatorTestService *indicator = user_data;
        
          if (indicator->actions_export_id)
            g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id);
        
          if (indicator->menu_export_id)
            g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id);
        }
        
        static void
        activate_show (GSimpleAction *action,
                       GVariant      *parameter,
                       gpointer       user_data)
        {
          g_message ("showing");
        }
        
        int
        main (int argc, char **argv)
        {
          IndicatorTestService indicator = { 0 };
          GMenuItem *item;
          GMenu *submenu;
          GActionEntry entries[] = {
            { "_header", NULL, NULL, "{'label': <'Test'>,"
                                     " 'icon': <'indicator-test'>,"
                                     " 'accessible-desc': <'Test indicator'> }", NULL },
            { "show", activate_show, NULL, NULL, NULL }
          };
          GMainLoop *loop;
        
          indicator.actions = g_simple_action_group_new ();
          g_simple_action_group_add_entries (indicator.actions, entries, G_N_ELEMENTS (entries), NULL);
        
          submenu = g_menu_new ();
          g_menu_append (submenu, "Show", "indicator.show");
          item = g_menu_item_new (NULL, "indicator._header");
          g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root");
          g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
          indicator.menu = g_menu_new ();
          g_menu_append_item (indicator.menu, item);
        
          g_bus_own_name (G_BUS_TYPE_SESSION,
                          "com.canonical.indicator.test",
                          G_BUS_NAME_OWNER_FLAGS_NONE,
                          bus_acquired,
                          NULL,
                          name_lost,
                          &indicator,
                          NULL);
        
          loop = g_main_loop_new (NULL, FALSE);
          g_main_loop_run (loop);
        
          g_object_unref (submenu);
          g_object_unref (item);
          g_object_unref (indicator.actions);
          g_object_unref (indicator.menu);
          g_object_unref (loop);
        
          return 0;
        }
        
      • com.canonical.indicator.test modified to add lock & greeter mode

        [Indicator Service]
        Name=indicator-test
        ObjectPath=/com/canonical/indicator/test
        
        [desktop]
        ObjectPath=/com/canonical/indicator/test/desktop
        
        [desktop_greeter]
        ObjectPath=/com/canonical/indicator/test/desktop
        
        [desktop_lockscreen]
        ObjectPath=/com/canonical/indicator/test/desktop
        
      • com.canonical.indicator.test.service remove .in postfix from filename and change the executable path

        [D-BUS Service]
        Name=com.canonical.indicator.test
        Exec=/usr/lib/x86_64-linux-gnu/indicator-test/indicator-test-service
        
    2. Compile it

      gcc -o indicator-test-service indicator-test-service.c `pkg-config --cflags --libs gtk+-3.0`
      
    3. Manual Installation

      sudo su
      mkdir /usr/lib/x86_64-linux-gnu/indicator-test/
      cp indicator-test-service /usr/lib/x86_64-linux-gnu/indicator-test/
      cp com.canonical.indicator.test /usr/share/unity/indicators/
      cp com.canonical.indicator.test.service /usr/share/dbus-1/services/
      
    4. Configuration for Greeter, override the default indicators list

      • 90_unity-greeter.gschema.override

        [com.canonical.unity-greeter]
        indicators=['ug-accessibility', 'com.canonical.indicator.keyboard', 'com.canonical.indicator.session', 'com.canonical.indicator.datetime', 'com.canonical.indicator.power', 'com.canonical.indicator.sound', 'com.canonical.indicator.test', 'application']
        
      • Install

        cp 90_unity-greeter.gschema.override /usr/share/glib-2.0/schemas/
        glib-compile-schemas /usr/share/glib-2.0/schemas/
        
    5. Test

      sudo service lightdm restart
      

    Notes

    • DBus service is troublesome, if you want user to be able to close application anytime. It is better to use autostart instead, like default indicators do.

    • I have uploaded ready files here:

      https://github.com/sneetsher/mysystemindicator_minimum

      and a modified copy here:

      https://github.com/sneetsher/mysystemindicator

      Where I have tried different menu for different mode. It could be installed and tested quickly.

    • This seems too simple and can be easily ported to any other language that have support for GIO Gnome lib (including DBus). As I'm looking for python, I may add it later.

    References:


    System Indicator Plugin

    This is not full standalone indicator as the one above, it is just a share lib plugin, similar to libappmenu.so & libprintersmenu.so (application menu & printer indicator). It could be shown only in regular user session & greeter (Not on lock screen).

    I couldn't make it work in my current machine, but I did before. Here the steps, may be I'm missing something.

    1. Using same source above of libindicator

      test/libdummy-indicator-*.c are examples (simple & visible the ones show up on the panel)

    2. Compile

      ./autogen.sh
      make
      
    3. Install

      sudo cp tests/.libs/libdummy-indicator-visible.so /usr/lib/indicators3/7/libdummy.so
      
    4. Configure to show in greeter screen

      • 90_unity-greeter.gschema.override use same name without lib prefix and .so extension.

        [com.canonical.unity-greeter]
        indicators=['ug-accessibility', 'com.canonical.indicator.keyboard', 'com.canonical.indicator.session', 'com.canonical.indicator.datetime', 'com.canonical.indicator.power', 'com.canonical.indicator.sound', 'application', 'dummy']
        
      • Install

        cp 90_unity-greeter.gschema.override /usr/share/glib-2.0/schemas/
        glib-compile-schemas /usr/share/glib-2.0/schemas/