Этот репозиторий содержит Flutter плагины для платформы ОС Аврора.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
7.5 KiB

# FFI Plugin package
Специализированный пакет Dart, содержащий API, написанный на Dart, с одной или несколькими реализациями для конкретной платформы, использующими [Dart FFI](https://dart.dev/guides/libraries/c-interop).
> Для демонстрации создания платформо-зависимого плагина для ОС Аврора типа "FFI Plugin package" был написан и опубликован проект "[Demo Dart Packages](https://gitlab.com/omprussia/flutter/demo-dart-packages)" которые подробно описан в статье "[Flutter на ОС Аврора](https://habr.com/ru/articles/761176/)".
Проект "[Demo Dart Packages](https://gitlab.com/omprussia/flutter/demo-dart-packages)" содержит в себе пакет реализующий платформо-зависимый плагин для ОС Аврора типа "FFI Plugin package". Данный пакет использует API ОС Аврора - "[Device Info API](https://developer.auroraos.ru/doc/software_development/reference/device_info)". Плагин использует [QtDBus](https://doc.qt.io/qt-5/qtdbus-index.html) но не является типом плагина "Qt plugin package" так как не использует сигналы и слоты.
Создать пакет "FFI Plugin package" можно командой:
```shell
$ flutter-aurora create --template=plugin_ffi <package-name>
```
Рассмотрим плагин "[ffi_plugin_device](https://gitlab.com/omprussia/flutter/demo-dart-packages/-/tree/master/packages/aurora/ffi_plugin_device?ref_type=heads)" из проекта [Demo Dart Packages](https://gitlab.com/omprussia/flutter/demo-dart-packages). **ffi_plugin_device** - платформо-зависимая реализация плагина "Flutter Device" для ОС Аврора типа "FFI Plugin package".
Структура пакета **ffi_plugin_device**
```shell
└── aurora
└── ffi_plugin_device
   ├── aurora
     └── CMakeLists.txt
   ├── ffigen.yaml
   ├── lib
     ├── ffi_plugin_device_bindings_generated.dart
     └── ffi_plugin_device.dart
   ├── pubspec.yaml
   └── src
   ├── CMakeLists.txt
   ├── ffi_plugin_device.cpp
   └── ffi_plugin_device.h
```
Для генерации привязок FFI можно использовать плагин [`ffigen`](https://pub.dev/packages/ffigen). Пакет, предоставляющий утилиты для работы с кодом интерфейса внешних функций, называется [`ffi`](https://pub.dev/packages/ffi). В `pubspec.yaml` плагина нужно добавить зависимости и активировать `ffiPlugin` для платформы ОС Аврора.
```yaml
dependencies:
ffi: ^2.0.2
dev_dependencies:
ffigen: ^7.2.7
flutter:
plugin:
platforms:
aurora:
ffiPlugin: true
dartPluginClass: FFIPluginDevice
```
Для генерации привязок нужно добавить файл `ffigen.yaml` в корень плагина.
Файл `packages/aurora/ffi_plugin_device/ffigen.yaml`
```yaml
name: PluginDeviceBindings
llvm-path:
- '/usr/lib/llvm-14/lib/libclang.so' # Ubuntu 22.04
- '/usr/lib/llvm-15/lib/libclang.so' # Ubuntu 23.04
description: |
Bindings for `src/ffi_plugin_device.h`.
output: 'lib/ffi_plugin_device_bindings_generated.dart'
headers:
entry-points:
- 'src/ffi_plugin_device.h'
include-directives:
- 'src/ffi_plugin_device.h'
comments:
style: any
length: full
```
В файле `ffigen.yaml` указываются хедеры нужных библиотек. В данном случае библиотека лежит в папке `src`, которая получает название модели устройства с помощью [`Qt D-Bus`](https://doc.qt.io/qt-5/qtdbus-index.html).
Файл `packages/aurora/ffi_plugin_device/src/ffi_plugin_device.h`
```cpp
#ifdef __cplusplus
extern "C" {
#endif
char *getDeviceName();
#ifdef __cplusplus
}
#endif
```
Файл `packages/aurora/ffi_plugin_device/src/ffi_plugin_device.cpp`
```cpp
#include <QStandardPaths>
#include <QtDBus/QtDBus>
#include "ffi_plugin_device.h"
char *getDeviceName()
{
QString deviceName = "";
if (QDBusConnection::sessionBus().isConnected()) {
QDBusInterface iface("ru.omp.deviceinfo",
"/ru/omp/deviceinfo/Features",
"",
QDBusConnection::sessionBus()
);
if (iface.isValid()) {
QDBusReply<QString> reply = iface.call("getDeviceModel");
if (reply.isValid()) {
deviceName = reply.value();
}
}
}
return deviceName.toUtf8().data();
}
```
Выполнить генерацию FFI-привязок можно следующей командой, выполнив её из корня плагина (например, `packages/aurora/ffi_plugin_device`):
```shell
flutter-aurora pub run ffigen --config ffigen.yaml
```
В папке `lib` будет сгенерирован файл `ffi_plugin_device_bindings_generated.dart`.
Файл `packages/aurora/ffi_plugin_device/lib/ffi_plugin_device_bindings_generated.dart`
```dart
import 'dart:ffi' as ffi;
/// Bindings for `src/ffi_plugin_device.h`.
class PluginDeviceBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
PluginDeviceBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
PluginDeviceBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
ffi.Pointer<ffi.Char> getDeviceName() {
return _getDeviceName();
}
late final _getDeviceNamePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'getDeviceName');
late final _getDeviceName =
_getDeviceNamePtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
}
```
Теперь можно создать класс `FFIPluginDevice`, реализующий интерфейс **[device_platform_interface](https://gitlab.com/omprussia/flutter/demo-dart-packages/-/tree/master/device_platform_interface?ref_type=heads)**.
Файл `packages/aurora/ffi_plugin_device/lib/ffi_plugin_device.dart`
```dart
class FFIPluginDevice extends DevicePlatform {
/// Метод, который выполнится при старте приложения
/// В этом методе можно установить платформо-зависимый плагин
static void registerWith() {
DevicePlatform.instance = FFIPluginDevice();
}
/// Привязки к нативным функциям в [_dylib].
final PluginDeviceBindings _bindings = PluginDeviceBindings(
DynamicLibrary.open('libffi_plugin_device.so'),
);
/// Реализация метода интерфейса [DevicePlatform]
@override
Future<String?> get deviceName async {
// Получение deviceName
final deviceName = _bindings.getDeviceName().cast<Utf8>().toDartString();
// Возврат результата
return deviceName == '' ? null : deviceName;
}
}
```