Linuxでシステムトレイにアイコン表示(gtk/app-indicator/C++)

Windowsなら簡単に出来そうだけど、LinuxではGUIを使う人が少ないのかそれほど多くの資料がありませんでした。LinuxのGUIは初めてなので、どんな方法があるのかもまだ分かりませんが、やってみたこと/調べたことを書き記そうかと思います。

システムトレイにアイコン表示する方法

前提1) デスクトップ環境の違い

LinuxはWindowsと違って、デスクトップ環境が1つではありません。GNOME/KDE/Xfce/LXDE/etc…と複数のデスクトップ環境をユーザーが選んで使う形になっていて、それぞれ見た目や機能が違います。これらはX Window System上で動作するという共通点があります。

前提2) ツールキットの違い

X Window System上では様々なツールキットを使用してアプリケーションなどを構築できます。現在 X Window System で双璧をなす2つの主要ツールキットはGtkQtです。上で例示された各デスクトップ環境はそれぞれ、どちらかのツールキットを使って構築されています。ただし、(アプリケーションが使用するツールキットは自由なので、)どのデスクトップ環境を使ったとしてもどちらのツールキットも入っているケースが多いと思います。

前提3) システムトレイにアイコン表示するということ

システムトレイはデスクトップ環境が提供する一機能です。このシステムトレイにアイコンを表示するということは、別のアプリケーションを実装してそこからアイコンを表示してもらい、アイコンに対する各操作の制御を行うこということになります。

するとシステムトレイが使用しているツールキットと、アイコンを制御するアプリのツールキットが同じにならない可能性が出てきます。しかしX Window Systemでのシステムトレイには標準仕様というものが決められており、各デスクトップ環境がシステムトレイ機能を提供する場合、この標準仕様に基づいて実装するのが慣例になっています。なのでたとえツールキットが同じでない場合でも、この標準仕様に基づいて実装されていれば提供される機能の範囲内で動作させることができます。

デスクトップ環境の選択

愛用しているのはLubuntuなのでデスクトップ環境はLXQtになります。これはGtk2.0ベースのLXDEをQtに置き換えた環境で、Gtk寄りのQt環境と言えば分かりやすいかもしれません。今回はついでにガチQt環境であるNeonでも同じプログラムを試しています。

アイコン表示アプリのツールキットの選択

ツールキットはGtk3を使用しました。 Gtk3にはGtkStatusIconというシステムトレイで使用するアイコンを表すものがあるらしいのですが、3.14から非推奨となり、4からは削除されています。最近のGNOMEはX Window Systemに依存しているわけではなく、通知だけのためにその影響を色濃く残す標準を取り込むと X Window System でないシステムで都合が悪いという理由のようです。

というわけで今回はかなり古いライブラリであるlibappindicatorのGtk3バージョンであるlibappindicator3というライブラリを使用してみました。最近では同じインターフェースを持つayatanaというのもあるようです。

実装

システムトレイにvirtualbox用のアイコン(流用しただけ)を表示し、右クリックするとExitメニューを表示し、それを選択することで終了するだけのプログラムです。

#include <stdexcept>
#include <libappindicator/app-indicator.h>

class MyIndicator
{
public:
    MyIndicator(const char *identifier, const char *icon)
    {
        if (!gtk_init_check(nullptr, nullptr))
        {
            throw std::runtime_error("gtk_init_check() failed.");
            return;
        }
        indicator = app_indicator_new(identifier, icon, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
        app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);

        exitButton = gtk_menu_item_new_with_label("Exit");
        gtk_widget_set_sensitive(exitButton, TRUE);
        g_signal_connect(
            exitButton,
            "activate",
            reinterpret_cast<GCallback>(
                static_cast<void(*)(GtkWidget*, MyIndicator*)>(
                    [](GtkWidget *widget, MyIndicator *target)->void {
                        target->exit();
                    }
                )
            ),
            this);
        gtk_widget_show(exitButton);
        auto *menu = reinterpret_cast<GtkMenuShell*>(gtk_menu_new());
        gtk_menu_shell_append(menu, exitButton);
        app_indicator_set_menu(indicator, reinterpret_cast<GtkMenu*>(menu));
    }
    void run()
    {
        while (indicator)
        {
            gtk_main_iteration_do(true);
        }
    }
    void exit()
    {
        g_idle_add(
            [](gpointer data) -> gboolean
            {
                auto myindicator = reinterpret_cast<MyIndicator *>(data);
                g_object_unref(myindicator->indicator);
                myindicator->indicator = nullptr;
                return FALSE;
            },
            this);
    }
private:
    AppIndicator *indicator;
    GtkWidget *exitButton;
};

int main()
{
    MyIndicator indicator("test", "virtualbox");
    indicator.run();
    return 0;
}

ビルドなどにはGtk3用に

pkg-config --libs --cflags gtk+-3.0

で出力されるオプションと、appindicator3用の -I/usr/include/libappindicator3-0.1-lappindicator3 というオプションを加えています。また、ubuntuのパッケージとしてはlibappindicator3-devのインストールが必要です。

参考にさせてもらったのは↓のコードです。

https://github.com/Soundux/traypp

結果

見た目

こんな感じのアイコンとメニューが表示されます。

Lubuntu-LXQt
Neon-KDE

実行しているバイナリは同じモノです。

メモリ

Lubuntu-LXQtでは…

$ ps -eo uid,pid,rss,vsz,args | grep example
 1000  179978 21908 341144 ./example
...
$ 

Neon-KDEでは…

$ ps -eo uid,pid,rss,vsz,args | grep example
 1000   43454 44488 361156 ./example
...
$

rssだけに着目すると、Lubuntuで22MB、Neonで44MBも消費していました。

まとめ

  • Gtk3ではシステムトレイにアイコンを置くための推奨クラスがない
  • Gtk3を使ってQtを使ったDEのシステムトレイにアイコンを置くことが出来た
  • C++で書いてみたが、Exitボタンのみのアプリでも実メモリを22MB~44MB消費していた

次はPythonを使った場合のメモリ使用量を調べてみました。

未分類C++,gtk,linux

Posted by first_user