Приложение на GTK#

Пример собирает GTK4-приложение из исходников через meson и доводит его до установленного Flatpak. Программа пишется здесь же, поэтому пример повторяется целиком.

GTK-приложения работают на среде org.astra.Gtk. В ней GTK3 и GTK4 (libgtk-3.so.0 версии 3.24 и libgtk-4.so.1 версии 4.8), GLib 2.74, Pango, Cairo, GdkPixbuf, а также то, что обычно нужно рядом: libsecret, libnotify, libappindicator3, libnss3, libatk, libcups.

Подготовка#

sudo flatpak install astra-main org.astra.Gtk//1.8.6
sudo flatpak install astra-extended org.astra.gtkSdk//1.8.6

Дерево примера#

gtk-hello/
|-- gtk-hello.yaml
`-- src/
    |-- meson.build
    |-- main.c
    |-- ru.example.GtkHello.desktop
    |-- ru.example.GtkHello.metainfo.xml
    `-- icons/
        `-- 128x128/
            `-- app.png

В каталог icons/128x128 кладётся любой PNG размером 128 на 128.

Исходный код#

src/main.c:

#include <gtk/gtk.h>

static void on_activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "GTK Hello");
    gtk_window_set_default_size(GTK_WINDOW(window), 360, 120);

    GtkWidget *label = gtk_label_new("GTK4 внутри Flatpak");
    gtk_window_set_child(GTK_WINDOW(window), label);

    gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char **argv) {
    GtkApplication *app = gtk_application_new("ru.example.GtkHello",
                                              G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
    int status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);
    return status;
}

src/meson.build:

project('gtk-hello', 'c', version: '1.0')

gtk_dep = dependency('gtk4')

executable('gtk-hello',
           'main.c',
           dependencies: gtk_dep,
           install: true)

Устанавливается только бинарник. Desktop-файл, описание AppStream и иконки подкладывает aft-app из полей секции files, поэтому в meson их дублировать не нужно.

src/ru.example.GtkHello.desktop:

[Desktop Entry]
Type=Application
Name=GTK Hello
Name[ru]=Пример на GTK
Comment=Минимальное приложение на GTK4
Exec=gtk-hello
Icon=gtk-hello
Terminal=false
Categories=Utility;

src/ru.example.GtkHello.metainfo.xml:

<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
  <id>ru.example.GtkHello</id>
  <metadata_license>CC0-1.0</metadata_license>
  <project_license>MIT</project_license>
  <name>GTK Hello</name>
  <summary>Минимальное приложение на GTK4</summary>
  <description>
    <p>Пример приложения на GTK4 для проверки сборки во Flatpak.</p>
  </description>
  <launchable type="desktop-id">ru.example.GtkHello.desktop</launchable>
</component>

Манифест#

gtk-hello.yaml:

version: "1.0"

metadata:
  id: ru.example.GtkHello
  name: "GTK Hello"
  version: "1.0"
  arch: amd64
  branch: stable

runtime:
  id: org.astra.Gtk
  version: "1.8.6"
  sdk: org.astra.gtkSdk

source:
  type: sourcedir
  sourcedir:
    path: ./src
    buildDeps:
      packages:
        - libgtk-4-dev
        - meson
        - ninja-build
    buildCommands:
      - meson setup build --prefix=/app --buildtype=release
      - meson compile -C build
      - meson install -C build

files:
  command: gtk-hello
  desktop: ./src/ru.example.GtkHello.desktop
  metainfo: ./src/ru.example.GtkHello.metainfo.xml
  icons-dir: ./src/icons

permissions:
  sockets: [wayland, fallback-x11]
  devices: [dri]
  shares: [ipc]
  filesystems: [xdg-pictures]
  persist: [.config, .local/share]

export:
  repository: ./repo

Сборка и установка#

cd gtk-hello
aft-app validate gtk-hello.yaml
sudo aft-app build gtk-hello.yaml
sudo flatpak remote-add --if-not-exists --no-gpg-verify local-repo ./repo
sudo flatpak install -y local-repo ru.example.GtkHello
flatpak run ru.example.GtkHello

Проверка готовой сборки:

aft-analyzer ru.example.GtkHello

Если программа уже установлена в системе#

Источник system переносит во Flatpak то, что уже стоит на машине. Он пригождается, когда исходников нет, пакет собрать неоткуда, а работающая установка есть:

version: "1.0"

metadata:
  id: ru.example.LegacyTool
  name: "Устаревшая утилита"
  version: "3.1"
  arch: amd64
  branch: stable

runtime:
  id: org.astra.Gtk
  version: "1.8.6"
  sdk: org.astra.gtkSdk

source:
  type: system
  system:
    binary: /usr/bin/legacy-tool
    patterns:
      - /usr/share/legacy-tool
      - /etc/legacy-tool
    includeOwningPackages: true

files:
  command: legacy-tool

permissions:
  sockets: [wayland, fallback-x11]
  devices: [dri]
  shares: [ipc]
  persist: [.config]

export:
  repository: ./repo

Поле binary принимает путь к программе или её имя в PATH. Список patterns добавляет файлы и каталоги, которые сама программа по зависимостям не тянет. Флаг includeOwningPackages включает в сборку пакеты-владельцы целиком: так надёжнее, но сборка выходит крупнее.

Для источников system и tar deb-зависимости не разбираются: состав задаёт сам источник. Что программа требует на самом деле, покажет analyze по разделяемым библиотекам:

aft-app analyze --system /usr/bin/legacy-tool \
    --runtime org.astra.Gtk --runtime-version 1.8.6

Тонкости GTK#

Схемы GSettings нужно скомпилировать, иначе приложение падает при первом обращении к настройкам. При сборке из исходников это делает meson. Если схема приезжает готовым файлом, её кладут в share/glib-2.0/schemas и компилируют командой перед экспортом:

commands:
  pre-export:
    - glib-compile-schemas files/share/glib-2.0/schemas

Команды секции pre-export выполняются на сборочной машине, а не в песочнице SDK, и рабочим каталогом им служит каталог сборки, где лежит дерево files. Поэтому путь пишется относительно него, а сама утилита должна быть установлена на машине: glib-compile-schemas приезжает с пакетом libglib2.0-bin.

Тема оформления и иконки берутся из среды, а не с хоста. Приложение в песочнице может выглядеть иначе, чем вне её, и это не признак поломки.

Работа с файлами идёт через портал: диалог открытия возвращает приложению уже открытый файл. Доступ ко всему домашнему каталогу почти никогда не нужен, достаточно нужных каталогов XDG.

GTK3 и GTK4 стоят в среде рядом, поэтому приложения на обеих версиях собираются на одну среду. Библиотеку разработки для GTK3 в сборочных зависимостях зовут libgtk-3-dev.