如何为Unity开发一个系统指示器?

这不是如何创建Unity指示器?的重复。我正在寻找系统指示器而不是应用程序指示器。
背景:
从以下两个问题中得知:
- 如何在登录界面中添加或操作应用程序/系统指示器? - 如何将indicator-sysmonitor设置为登录界面上的默认指示器? 我了解到有两种类型的指示器:
- 系统指示器:声音、键盘、应用程序等(尝试:`ps ax | grep indicator`) - 应用程序指示器:nm-applet、sysmonitor等,几乎所有列在可用的应用程序指示器中的指示器都属于此类
所有应用程序指示器都由一个系统指示器indicator-application处理/显示。系统指示器直接显示在Unity面板上。
两个问题都涉及在登录和锁屏界面上添加/删除指示器。第一个问题是一个简单的设置(处理系统指示器时)。第二个问题是一个棘手的设置(处理应用程序指示器时),需要修改面板服务(unity包)的源代码以适应锁屏界面,以及修改unity-greeter的源代码以适应登录界面。
对于“sysmonitor”这种情况,对我来说那只是一个权宜之计。最好的解决方案是实现一个系统指示器,而不是一个应用程序指示器。
主题:
- 是否有统一的系统指示器API(首选:Python然后C/C++)?请参考官方文档。 - 大多数系统指示器都是使用Vala编程语言编写的。是否有人可以用Python或C编写一个小型的系统指示器演示?
更新:
我找到了几个可能会有所帮助的链接。
应用指示器项目页面中,他们列出了用于应用指示器的AppIndicator-0.3 API(CPython)的链接。 他们还列出了Indicate-0.7 API(CPython)。这是什么?嗯,这是桌面应用程序之间的DBus消息通道。
另一方面,在系统指示器项目页面中,他们提到:

系统指示器API

  • 使用libindicate的消息菜单。
  • 使用libunity的声音菜单。
  • 使用Evolution-Data-Server的日期/时间指示器。
他们似乎列出了数据API,而不是像Evolution-Data-Server那样的指示器开发API。但对于libindicate和 libunity不太确定。有人使用过这两个库吗?
尝试使用apt-cache rdepends libunity9 libindicator7 libindicator3-7来查看哪个指示器正在使用这些库。
更新2:这是为了让感兴趣的用户保持最新的信息。
根据我目前收集到的情况,以下是可能解决方案的顺序:
  1. libindicator3-7(高,许多指示器依赖它)

    我在源代码中找到了一些测试示例,一些我尝试过的虚拟指示器,可以安装在/usr/lib/indicators3/7/目录下,它们是共享库文件.so。我可以在登录和常规会话中看到它们,但无法在锁屏界面中显示。

    然而,还有一些测试指示器服务,看起来像是Unity系统自带的。我还没有尝试过它们。

  2. libindicator7

    与libindicator3-7来自同一源代码,从rdepends中得知:

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

    它似乎用于在面板中创建指示器容器。

  3. libunity9(低)

    尚未进行研究

2个回答

系统指示器服务

嗯,这比我预期的要简单。它没有特定的API。因为它只是一个GSimpleActionGroup和相应的GMenu通过DBus导出,然后使用同名的声明文件放在/usr/share/unity/indicators/中告诉Unity它们的存在。不需要任何其他库。

这里是一个非常简单的C语言示例:

  1. libindicator 源代码中获取 tests/indicator-test-service.c 的副本

    apt-get source libindicator
    cp libindicator-*/tests/indicator-test-service.c .
    cp libindicator-*/tests/com.canonical.indicator.test* .
    
  2. indicator-test-service.c 无更改

    #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;
    }
    
  3. com.canonical.indicator.test 修改以添加锁定和欢迎界面模式

    [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
    注意事项
    • 如果你希望用户随时能够关闭应用程序,DBus服务会很麻烦。最好使用自动启动,就像默认的指示器一样。

    • 我已经在这里上传了准备好的文件:

      https://github.com/sneetsher/mysystemindicator_minimum

      还有一个修改过的副本:

      https://github.com/sneetsher/mysystemindicator

      我在其中尝试了不同的菜单模式。可以快速安装和测试。

    • 这个项目看起来很简单,可以轻松移植到任何支持GIO Gnome库(包括DBus)的其他语言上。因为我正在寻找Python,所以我可能以后会添加它。

    参考资料:


    系统指示插件

    这不是完整的独立指示器,与上面那个类似,它只是一个共享库插件,类似于 libappmenu.solibprintersmenu.so(应用程序菜单和打印机指示器)。它只能在常规用户会话和登录屏幕中显示(不能在锁定屏幕中显示)。

    我无法在我的当前机器上使它工作,但以前我可以。以下是步骤,也许我遗漏了某些东西。

    使用与上述的libindicator相同的源代码 test/libdummy-indicator-*.c是示例(简单且在面板上可见的)
    编译
    ./autogen.sh make 安装
    sudo cp tests/.libs/libdummy-indicator-visible.so /usr/lib/indicators3/7/libdummy.so
    配置以在登录界面显示
    90_unity-greeter.gschema.override使用相同的名称,不带有lib前缀和.so扩展名。
    [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']
    安装
    cp 90_unity-greeter.gschema.override /usr/share/glib-2.0/schemas/ glib-compile-schemas /usr/share/glib-2.0/schemas/

2我在想这个能不能用Python来做... C看起来有点吓人。 - Seth
@Seth 我相信是的,它可以用Python来实现。因为我刚刚检查了所有必要的函数(export_action_groupexport_menu_model)和对象(SimpleActionGroupMenu),它们都在gi.repository.Gio中。我会在接下来的几天尝试写一个出来。 - user.dz
1我刚刚上传了一个从@user.dz的C示例移植过来的原始“工作”Python示例: https://github.com/sneetsher/mysystemindicator 我会在进行中不断更新它,但欢迎任何贡献。感谢提供有用的信息! - Marto
欢迎来到Ask Ubuntu。谢谢Marto。我一直在寻找Python系统指示器,可能还有些用户在使用Unity。这个问题是关于C语言的。当你拿到可用的副本时,请让我知道你是否可以在问题下面发表评论。我将重新打开另一个Python问题,链接在这里:https://askubuntu.com/q/786831/26246。如果你能提供仓库链接,我会很乐意测试它。 - user.dz
@Marto 我已经测试过了,它确实显示得很好,非常感谢。我请求重新打开我的另一个Python问题,这样你就可以在那里提交你的答案。 - user.dz
@Marto 请你在这里回答一下问题 https://askubuntu.com/q/786831/26246 ,它已经重新开放了。 - user.dz
@Seth 我很感激Marto,他把它移植到了Python。 - user.dz

注意:请查看本帖底部以获取对此答案的最终解释。

我不知道我是否真的能提供任何帮助,但我希望这个想法可能会有用。

根据我的搜索结果,系统指示器和应用程序指示器之间的区别是明显的。基于这一点,我现在介绍一个值得怀疑的概念:

在系统指示器中使用应用程序指示器API(而不是为相同目的创建一个新的统一API)

这个想法是我在查看以下帖子时想到的:

https://askubuntu.com/a/234204/408654

https://askubuntu.com/a/42213/408654

Unity API似乎主要用于与应用程序指示器一起使用,但系统和应用程序指示器可能使用类似的编程(C语言)。然而,您之前提到这两种类型的指示器由两个不同的系统处理。因此,我随后阅读了您提供的其中一个来源: 如何在登录屏幕中添加或操作应用程序/系统指示器? 主要答案涉及覆盖已存在的用户以获得所需的访问权限。它还提供了一种添加和删除所有现有指示器的解决方案。这是一种统一的指示器管理解决方案。是否有可能覆盖默认(预先存在的)用户来运行/引入系统指示器?
系统指示器能否使用Unity应用程序指示器API(API能否在Unity面板上正常使用和显示)?如果答案是肯定的,那就可以满足这种情况 - 前提是不会引起其他问题。我知道这个回答可能不够明确,所以我将解释一下我的尝试 - 我正在尝试将任务分解为更小的目标。主要目标是找出是否可以使用应用程序指示器API来编写系统指示器(作为现有的统一API)。
针对您查询的这部分内容的回答:
“是否有一个统一的系统指示器API”
不幸的是,无法使用应用程序指示器API来实现系统指示器。因此,我的解决方案无效 :(

很遗憾,应用程序指示器 API 无法用于创建系统指示器。它与 indicator-sysmonitor 一样,需要修改 Unity 和 Unity-greeter 的构建版本。 - user.dz
在这种情况下,似乎需要一个新的API - 一个专门用于系统指示器的API。目前,系统指示器与Ubuntu有多个独立的API。我认为我们只能选择使用第三方库,正如问题帖子末尾所述。 - user408654