Документация
ОС Аврора 3.32.7

Реализации плагина с использованием FFI

Пример платформо-зависимого плагина, работающего с операционной системой Аврора с использованием механизма Foreign function interface (FFI). Задача плагина — получить название устройства с использованием FFI.

Реализация плагина через FFI позволяет работать с операционной системой без использования механизма Platform Channel. Это удобный способ для реализации простых платформо-зависимых плагинов.

Примечание:

Этот демонстрационный плагин доступен в репозитории Flutter в разделе examples/ffi.

Содержание:

Создание приложения

Плагин FFI является платформо-зависимым, то есть зависит от операционной системы. Подробнее ознакомиться с типами плагинов можно в разделе Пакеты Flutter. FFI позволяет использовать библиотеки с интерфесом, написанным на C. Flutter CLI позволяет создать шаблон плагина. Для генерации шаблона плагина можно выполнить следующую команду в терминале:

flutter-aurora create --template=plugin_ffi --platforms aurora --org=ru.aurora ffi_demo
  • ru.aurora — имя организации (Organization name), участвует в формировании названия пакета;
  • ffi_demo — название плагина (Application Name), участвует в формировании названия пакета.

Данная команда генерирует базовый пример плагина Flutter с настроенным окружением для сборки под ОС Аврора. Структура файлов и каталогов проекта имеет следующий вид:

.
├── aurora
│   └── CMakeLists.txt
├── example
│   ├── aurora
│   │   ├── CMakeLists.txt
│   │   ├── desktop
│   │   │   └── ru.aurora.ffi_demo_example.desktop
│   │   ├── icons
│   │   │   ├── 108x108.png
│   │   │   ├── 128x128.png
│   │   │   ├── 172x172.png
│   │   │   └── 86x86.png
│   │   ├── main.cpp
│   │   └── rpm
│   │       └── ru.aurora.ffi_demo_example.spec
│   ├── lib
│   │   └── main.dart
│   ├── analysis_options.yaml
│   ├── ffi_demo_example.iml
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── README.md
├── lib
│   ├── ffi_demo_bindings_generated.dart
│   └── ffi_demo.dart
├── src
│ ├── CMakeLists.txt
│ ├── ffi_demo.c
│ └── ffi_demo.h
├── analysis_options.yaml
├── CHANGELOG.md
├── ffi_demo.iml
├── ffigen.yaml
├── LICENSE
├── pubspec.lock
├── pubspec.yaml
└── README.md


10 directories, 28 files

Данная структура является типичной для приложений на Flutter. В директории src находится код библиотеки C++, которая будет подключена к плагину. В директориях <project>/aurora и <project>/example/aurora находятся файлы, обеспечивающие работу плагина и приложения на платформе ОС Аврора:

  • CMakeLists.txt — приложение и плагины Flutter для ОС Аврора имеют платформенную часть на С++, а сборка реализована через CMake;
  • desktop/ru.aurora.app_demo.desktop — файл с конфигурацией ярлыка приложения. В нём можно указать название приложения, нужные права для приложения и другие настройки;
  • icons/*.png — иконки приложения;
  • main.cpp — точка входа в приложение для ОС Аврора. Это зачастую шаблонный код для запуска всех необходимых компонентов Flutter и приложения;
  • rpm/ru.aurora.app_demo.specфайл с конфигурацией установочного пакета, который утилита rpmbuild использует для сборки RPM-пакета.

Примечание:

Для предварительного знакомства с Flutter можно обратиться к документации Flutter и создать своё первое приложение, используя информацию из статьи "Write your first Flutter app".

Настройка FFI

В файле pubspec.yaml плагина необходимо указать зависимость от плагина ffi:

dependencies:
  ffi: ^2.0.2

Проверить наличие активации FFI в файле плагина pubspec.yaml:

flutter:
  plugin:
    platforms:
      aurora:
        ffiPlugin: true

Эта конфигурация вызывает встроенную сборку для различных целевых платформ и объединяет двоичные файлы в приложение Flutter.

Генерация привязок

Для генерации привязок в корне шаблона плагина находится файл ffigen.yaml. Здесь описывается информация, с помощью которой можно создать привязки на языке Dart.

# Название класса в Dart после генерации
name: FfiDemoBindings
# Описание привязок
description: |
  Bindings for `src/ffi_demo.h`.
# Путь к файлу который должен быть создан
output: 'lib/ffi_demo_bindings_generated.dart'
# Заголовочные файлы библиотеки, на данных которых создаются привязки
headers:
  entry-points:
    - 'src/ffi_demo.h'

На основе файла конфигурации можно выполнить генерацию привязок в файл <poject>/lib/ffi_demo_bindings_generated.dart из корня плагина:

flutter-aurora pub run ffigen --config ffigen.yaml

Команда создаст файл <project>/lib/ffi_demo_bindings_generated.dart с классом FfiDemoBindings, который можно использовать для доступа к библиотеке из кода Dart.

Доработка плагина

Демонстрационный плагин выполняет задачу по получению названия устройства. Это можно осуществить через доступную службу интерфейса D-Bus ru.omp.deviceinfo. Подробнее со службой можно ознакомиться в документации ОС Аврора "Device Info API". Для реализации задачи можно использовать QtDBus.

В директории src находится код на C или C++, созданный Flutter CLI при генерации шаблона плагина. В файл CMakeLists.txt следует добавить зависимости, которые позволят получить доступ к QtDBus:

cmake_minimum_required(VERSION 3.10)

project(ffi_demo_library VERSION 0.0.1)

find_package(PkgConfig REQUIRED)
find_package(Qt5 COMPONENTS Core DBus REQUIRED)

add_library(ffi_demo SHARED
  "ffi_demo.cpp"
)

target_link_libraries(ffi_demo PUBLIC Qt5::Core Qt5::DBus)

set_target_properties(ffi_demo PROPERTIES
  PUBLIC_HEADER ffi_demo.h
  OUTPUT_NAME "ffi_demo"
)

target_compile_definitions(ffi_demo PUBLIC DART_SHARED_LIB)

Примечание:

Следует обратить внимание, что файл реализации шаблона ffi_demo.c был переименован в ffi_demo.cpp.

В файле <project>/src/ffi_demo.h необходимо указать функцию для получения названия устройства следующим образом:

#ifdef __cplusplus
extern "C" {
#endif

const char *getDeviceName();

#ifdef __cplusplus
}
#endif

В файле ffi_demo.cpp выполнить реализацию метода, который через DBus обратится к службе ru.omp.deviceinfo для получения названия устройства:

#include <cstring>

#include <QtDBus/QtDBus>

#include "ffi_demo.h"

namespace {

const char* copyString(const QString& str) {
    auto* tmp = new char[str.length() + 1];
    std::strcpy(tmp, str.toUtf8().data());
    return tmp;
}

}  // пространство имён

const char* getDeviceName() {
    if (QDBusConnection::systemBus().isConnected()) {
        QDBusInterface iface(
            "ru.omp.deviceinfo",
            "/ru/omp/deviceinfo/Features",
            "ru.omp.deviceinfo.Features",
            QDBusConnection::systemBus()
        );
        if (iface.isValid()) {
            QDBusReply<QString> reply = iface.call("getDeviceModel");
            if (reply.isValid()) {
                return copyString(reply.value());
            }
        }
    }
    return "";
}

После модификаций плагина следует обновить привязки библиотеки к языку Dart:

flutter-aurora pub run ffigen --config ffigen.yaml

Код Dart-плагина можно доработать следующим образом:

library ffi_demo;

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'ffi_demo_bindings_generated.dart';

class FFIDemo {

  final FfiDemoBindings _bindings = FfiDemoBindings(
    DynamicLibrary.open('libffi_demo.so'),
  );

  /// Реализация метода для получения имени устройства
  String? getDeviceName() => using((Arena arena) {
    // Выполнение метода
    final Pointer<Utf8> pointer = _bindings.getDeviceName().cast<Utf8>();
    // Освобождение памяти
    arena.using(pointer, calloc.free);
    // Возвращение результата
    final deviceName = pointer.toDartString();
    if (deviceName == '') {
      return null;
    }
    return deviceName;
  });
}

Для обновления зависимостей плагина следует выполнить команду в корне проекта:

flutter-aurora pub get

Доработка примера

В проекте плагина FFI, сгенерированного по шаблону plugin_ffi, в директории example имеется приложение-пример для работы с плагином. Его необходимо доработать для вывода информации о названии устройства.

Для получения доступа к "Device Info API" требуется добавить разрешение в файл <plugin>/example/aurora/desktop/ru.aurora.ffi_demo_example.desktop:

Permissions=DeviceInfo

Для подключения библиотеки QtDBus в файл <plugin>/example/aurora/rpm/ru.aurora.ffi_demo_example.spec следует добавить зависимости:

BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5DBus)

Для повышения читаемости кода, систематизации и упрощения написания приложений-примеров был разработан плагин internal_aurora. Добавить в зависимости плагин internal_aurora в pubspec.yaml можно следующим образом:

dependencies:
  internal_aurora:
    git:
      url: https://developer.auroraos.ru/git/flutter/flutter-community-plugins/internal_aurora.git

Обновить зависимости в директории example можно следующим образом:

cd example
flutter-aurora pub get

Далее следует доработать приложение, которое будет использовать плагин ffi_demo и демонстрировать название устройства:

import 'package:ffi_demo/ffi_demo.dart';
import 'package:flutter/material.dart';
import 'package:internal_aurora/list_item_data.dart';
import 'package:internal_aurora/list_item_info.dart';
import 'package:internal_aurora/list_separated.dart';
import 'package:internal_aurora/theme/colors.dart';
import 'package:internal_aurora/theme/theme.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final FFIDemo _plugin = FFIDemo();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: internalTheme,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('FFI Demo'),
        ),
        body: ListSeparated(
          children: [
            const ListItemInfo("""
            An example of a platform-dependent plugin that works with the
            Aurora OS via the FFI.
            """),
            ListItemData(
              'Get Device Name',
              InternalColors.purple,
              description:
              'Getting the device name through a platform-dependent plugin FFI via D-Bus',
              value: _plugin.getDeviceName(),
            ),
          ],
        ),
      ),
    );
  }
}

Теперь можно запустить приложение. В корне проекта выполнить команду Flutter CLI для сборки и запуска приложения:

flutter-aurora run

Примечание:

Для сборки доступны 3 архитектуры, более детально с этим вопросом можно ознакомиться в разделе "Целевая архитектура".

После успешно выполненной задачи по сборке будет выведен путь к файлу RPM c собранным приложением:

┌─ Result ───────────────────────────────────────────────────────────────────────────────────────────┐
│ ./build/aurora/psdk_5.0.0.60/aurora-x64/release/RPMS/ru.aurora.ffi_demo_example-0.1.0-1.x86_64.rpm │
└────────────────────────────────────────────────────────────────────────────────────────────────────┘

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования cookies.