plugin_framework, test install plugin
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
b06fad0e43
commit
4ee0fd9676
@ -11,9 +11,7 @@ import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
|||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:flutter_hbb/plugin/manager.dart';
|
import 'package:flutter_hbb/plugin/manager.dart';
|
||||||
import 'package:flutter_hbb/plugin/model.dart';
|
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
|
||||||
import 'package:flutter_hbb/plugin/common.dart';
|
|
||||||
import 'package:flutter_hbb/plugin/widget.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@ -1448,58 +1446,6 @@ class _CheckboxState extends State<_Checkbox> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PluginCard extends StatefulWidget {
|
|
||||||
final PluginInfo plugin;
|
|
||||||
const PluginCard({
|
|
||||||
Key? key,
|
|
||||||
required this.plugin,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<PluginCard> createState() => PluginCardState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PluginCardState extends State<PluginCard> {
|
|
||||||
PluginId get pluginId => widget.plugin.meta.id;
|
|
||||||
String get pluginName => widget.plugin.meta.name;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final children = [
|
|
||||||
_Button(
|
|
||||||
'Reload',
|
|
||||||
() async {
|
|
||||||
clearPlugin(pluginId);
|
|
||||||
await bind.pluginReload(id: pluginId);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_Checkbox(
|
|
||||||
label: 'Enable',
|
|
||||||
getValue: () => bind.pluginIsEnabled(id: pluginId),
|
|
||||||
setValue: (bool v) async {
|
|
||||||
if (!v) {
|
|
||||||
clearPlugin(pluginId);
|
|
||||||
}
|
|
||||||
await bind.pluginEnable(id: pluginId, v: v);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
final model = getPluginModel(kLocationHostMainPlugin, pluginId);
|
|
||||||
if (model != null) {
|
|
||||||
children.add(PluginItem(
|
|
||||||
pluginId: pluginId,
|
|
||||||
peerId: '',
|
|
||||||
location: kLocationHostMainPlugin,
|
|
||||||
pluginModel: model,
|
|
||||||
isMenu: false,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return _Card(title: pluginName, children: children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Plugin extends StatefulWidget {
|
class _Plugin extends StatefulWidget {
|
||||||
const _Plugin({Key? key}) : super(key: key);
|
const _Plugin({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -1508,18 +1454,9 @@ class _Plugin extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PluginState extends State<_Plugin> {
|
class _PluginState extends State<_Plugin> {
|
||||||
// temp checkbox widget
|
|
||||||
|
|
||||||
List<Widget> _buildCards(PluginManager model) => [
|
|
||||||
_Card(
|
|
||||||
title: 'Plugin',
|
|
||||||
children: [],
|
|
||||||
),
|
|
||||||
...model.plugins.map((entry) => PluginCard(plugin: entry)).toList(),
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
bind.pluginListReload();
|
||||||
final scrollController = ScrollController();
|
final scrollController = ScrollController();
|
||||||
return DesktopScrollWrapper(
|
return DesktopScrollWrapper(
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
@ -1529,7 +1466,9 @@ class _PluginState extends State<_Plugin> {
|
|||||||
return ListView(
|
return ListView(
|
||||||
physics: DraggableNeverScrollableScrollPhysics(),
|
physics: DraggableNeverScrollableScrollPhysics(),
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
children: _buildCards(model),
|
children: model.plugins
|
||||||
|
.map((entry) => DesktopSettingsCard(plugin: entry))
|
||||||
|
.toList(),
|
||||||
).marginOnly(bottom: _kListViewBottomMargin);
|
).marginOnly(bottom: _kListViewBottomMargin);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -8,7 +8,7 @@ import 'package:flutter_hbb/models/chat_model.dart';
|
|||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:flutter_hbb/plugin/widget.dart';
|
import 'package:flutter_hbb/plugin/widgets/desc_ui.dart';
|
||||||
import 'package:flutter_hbb/plugin/common.dart';
|
import 'package:flutter_hbb/plugin/common.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
@ -18,7 +18,7 @@ import 'package:flutter_hbb/models/user_model.dart';
|
|||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/plugin/event.dart';
|
import 'package:flutter_hbb/plugin/event.dart';
|
||||||
import 'package:flutter_hbb/plugin/manager.dart';
|
import 'package:flutter_hbb/plugin/manager.dart';
|
||||||
import 'package:flutter_hbb/plugin/widget.dart';
|
import 'package:flutter_hbb/plugin/widgets/desc_ui.dart';
|
||||||
import 'package:flutter_hbb/common/shared_state.dart';
|
import 'package:flutter_hbb/common/shared_state.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -22,16 +22,18 @@ class PluginModel with ChangeNotifier {
|
|||||||
final List<UiType> uiList = [];
|
final List<UiType> uiList = [];
|
||||||
final Map<String, String> opts = {};
|
final Map<String, String> opts = {};
|
||||||
|
|
||||||
void add(UiType ui) {
|
void add(List<UiType> uiList) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < uiList.length; i++) {
|
for (var ui in uiList) {
|
||||||
if (uiList[i].key == ui.key) {
|
for (int i = 0; i < this.uiList.length; i++) {
|
||||||
uiList[i] = ui;
|
if (this.uiList[i].key == ui.key) {
|
||||||
|
this.uiList[i] = ui;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
uiList.add(ui);
|
this.uiList.add(ui);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -44,12 +46,12 @@ class PluginModel with ChangeNotifier {
|
|||||||
class LocationModel with ChangeNotifier {
|
class LocationModel with ChangeNotifier {
|
||||||
final Map<PluginId, PluginModel> pluginModels = {};
|
final Map<PluginId, PluginModel> pluginModels = {};
|
||||||
|
|
||||||
void add(PluginId id, UiType ui) {
|
void add(PluginId id, List<UiType> uiList) {
|
||||||
if (pluginModels[id] != null) {
|
if (pluginModels[id] != null) {
|
||||||
pluginModels[id]!.add(ui);
|
pluginModels[id]!.add(uiList);
|
||||||
} else {
|
} else {
|
||||||
var model = PluginModel();
|
var model = PluginModel();
|
||||||
model.add(ui);
|
model.add(uiList);
|
||||||
pluginModels[id] = model;
|
pluginModels[id] = model;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -68,11 +70,11 @@ class LocationModel with ChangeNotifier {
|
|||||||
bool get isEmpty => pluginModels.isEmpty;
|
bool get isEmpty => pluginModels.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLocationUi(String location, PluginId id, UiType ui) {
|
void addLocationUi(String location, PluginId id, List<UiType> uiList) {
|
||||||
if (_locationModels[location] == null) {
|
if (_locationModels[location] == null) {
|
||||||
_locationModels[location] = LocationModel();
|
_locationModels[location] = LocationModel();
|
||||||
}
|
}
|
||||||
_locationModels[location]?.add(id, ui);
|
_locationModels[location]?.add(id, uiList);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationModel? getLocationModel(String location) => _locationModels[location];
|
LocationModel? getLocationModel(String location) => _locationModels[location];
|
||||||
|
@ -10,9 +10,9 @@ import 'package:get/get.dart';
|
|||||||
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
|
||||||
import './manager.dart';
|
import '../manager.dart';
|
||||||
import './model.dart';
|
import '../model.dart';
|
||||||
import './common.dart';
|
import '../common.dart';
|
||||||
|
|
||||||
// dup to flutter\lib\desktop\pages\desktop_setting_page.dart
|
// dup to flutter\lib\desktop\pages\desktop_setting_page.dart
|
||||||
const double _kCheckBoxLeftMargin = 10;
|
const double _kCheckBoxLeftMargin = 10;
|
||||||
@ -280,9 +280,15 @@ void handleReloading(Map<String, dynamic> evt, String peer) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final ui = UiType.create(json.decode(evt['ui'] as String));
|
final uiList = <UiType>[];
|
||||||
|
for (var e in json.decode(evt['ui'] as String)) {
|
||||||
|
final ui = UiType.create(e);
|
||||||
if (ui != null) {
|
if (ui != null) {
|
||||||
addLocationUi(evt['location']!, evt['id']!, ui);
|
uiList.add(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uiList.isNotEmpty) {
|
||||||
|
addLocationUi(evt['location']!, evt['id']!, uiList);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Failed handleReloading, json decode of ui, $e ');
|
debugPrint('Failed handleReloading, json decode of ui, $e ');
|
199
flutter/lib/plugin/widgets/desktop_settings.dart
Normal file
199
flutter/lib/plugin/widgets/desktop_settings.dart
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
import 'package:flutter_hbb/plugin/model.dart';
|
||||||
|
import 'package:flutter_hbb/plugin/common.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../manager.dart';
|
||||||
|
import './desc_ui.dart';
|
||||||
|
|
||||||
|
// to-do: use settings from desktop_setting_page.dart
|
||||||
|
const double _kCardFixedWidth = 540;
|
||||||
|
const double _kCardLeftMargin = 15;
|
||||||
|
const double _kContentHMargin = 15;
|
||||||
|
const double _kTitleFontSize = 20;
|
||||||
|
const double _kVersionFontSize = 12;
|
||||||
|
|
||||||
|
class DesktopSettingsCard extends StatefulWidget {
|
||||||
|
final PluginInfo plugin;
|
||||||
|
DesktopSettingsCard({
|
||||||
|
Key? key,
|
||||||
|
required this.plugin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DesktopSettingsCard> createState() => _DesktopSettingsCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
|
||||||
|
PluginInfo get plugin => widget.plugin;
|
||||||
|
bool get installed => plugin.installedVersion.isNotEmpty;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: SizedBox(
|
||||||
|
width: _kCardFixedWidth,
|
||||||
|
child: Card(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
header(),
|
||||||
|
body(),
|
||||||
|
],
|
||||||
|
).marginOnly(bottom: 10),
|
||||||
|
).marginOnly(left: _kCardLeftMargin, top: 15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget header() {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
headerNameVersion(),
|
||||||
|
headerInstallEnable(),
|
||||||
|
],
|
||||||
|
).marginOnly(
|
||||||
|
left: _kContentHMargin,
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
right: _kContentHMargin,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget headerNameVersion() {
|
||||||
|
return Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
translate(widget.plugin.meta.name),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: _kTitleFontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
plugin.meta.version,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: _kVersionFontSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget headerButton(String label, VoidCallback onPressed) {
|
||||||
|
return Container(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: Text(label),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget headerInstallEnable() {
|
||||||
|
final installButton = headerButton(installed ? 'uninstall' : 'install', () {
|
||||||
|
bind.pluginInstall(
|
||||||
|
id: plugin.meta.id,
|
||||||
|
b: !installed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (installed) {
|
||||||
|
final needUpdate =
|
||||||
|
plugin.installedVersion.compareTo(plugin.meta.version) < 0;
|
||||||
|
final updateButton = needUpdate
|
||||||
|
? headerButton('update', () {
|
||||||
|
bind.pluginInstall(
|
||||||
|
id: plugin.meta.id,
|
||||||
|
b: !installed,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
final isEnabled = bind.pluginIsEnabled(id: plugin.meta.id);
|
||||||
|
final enableButton = !installed
|
||||||
|
? Container()
|
||||||
|
: headerButton(isEnabled ? 'disable' : 'enable', () {
|
||||||
|
if (isEnabled) {
|
||||||
|
clearPlugin(plugin.meta.id);
|
||||||
|
}
|
||||||
|
bind.pluginEnable(id: plugin.meta.id, v: !isEnabled);
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
updateButton,
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
installButton,
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
enableButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return installButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget body() {
|
||||||
|
return Column(children: [
|
||||||
|
author(),
|
||||||
|
description(),
|
||||||
|
more(),
|
||||||
|
]).marginOnly(
|
||||||
|
left: _kCardLeftMargin,
|
||||||
|
top: 4,
|
||||||
|
right: _kContentHMargin,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget author() {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(plugin.meta.author),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget description() {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(plugin.meta.description),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget more() {
|
||||||
|
if (!installed) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Widget> children = [];
|
||||||
|
final model = getPluginModel(kLocationHostMainPlugin, plugin.meta.id);
|
||||||
|
if (model != null) {
|
||||||
|
children.add(PluginItem(
|
||||||
|
pluginId: plugin.meta.id,
|
||||||
|
peerId: '',
|
||||||
|
location: kLocationHostMainPlugin,
|
||||||
|
pluginModel: model,
|
||||||
|
isMenu: false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return ExpansionTile(
|
||||||
|
title: Text('Options'),
|
||||||
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1498,7 +1498,7 @@ pub fn plugin_reload(_id: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn plugin_enable(_id: String, _v: bool) {
|
pub fn plugin_enable(_id: String, _v: bool) -> SyncReturn<()> {
|
||||||
#[cfg(feature = "plugin_framework")]
|
#[cfg(feature = "plugin_framework")]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
{
|
{
|
||||||
@ -1512,6 +1512,7 @@ pub fn plugin_enable(_id: String, _v: bool) {
|
|||||||
} else {
|
} else {
|
||||||
crate::plugin::unload_plugin(&_id);
|
crate::plugin::unload_plugin(&_id);
|
||||||
}
|
}
|
||||||
|
SyncReturn(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ pub enum UiType {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub ui: HashMap<String, UiType>,
|
pub ui: HashMap<String, Vec<UiType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -152,7 +152,7 @@ async fn set_manager_plugin_config_async(id: &str, name: &str, value: String) ->
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_plugin_async(id: &str) -> ResultType<()> {
|
pub async fn load_plugin_async(id: &str) -> ResultType<()> {
|
||||||
let mut c = connect(1000, "").await?;
|
let mut c = connect(1000, "").await?;
|
||||||
c.send(&Data::Plugin(Plugin::Load(id.to_owned()))).await?;
|
c.send(&Data::Plugin(Plugin::Load(id.to_owned()))).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
use super::{desc::Meta as PluginMeta, ipc::InstallStatus, *};
|
use super::{desc::Meta as PluginMeta, ipc::InstallStatus, *};
|
||||||
use crate::flutter;
|
use crate::flutter;
|
||||||
#[cfg(not(debug_assertions))]
|
use hbb_common::{allow_err, bail, log, tokio, toml};
|
||||||
use hbb_common::toml;
|
|
||||||
use hbb_common::{allow_err, bail, config::load_path, log, tokio};
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::{
|
use std::{
|
||||||
@ -48,42 +46,15 @@ pub struct PluginInfo {
|
|||||||
|
|
||||||
static PLUGIN_SOURCE_LOCAL: &str = "local";
|
static PLUGIN_SOURCE_LOCAL: &str = "local";
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn get_plugin_source_list() -> Vec<PluginSource> {
|
fn get_plugin_source_list() -> Vec<PluginSource> {
|
||||||
// Only one source for now.
|
// Only one source for now.
|
||||||
vec![PluginSource {
|
vec![PluginSource {
|
||||||
name: "rustdesk".to_string(),
|
name: "rustdesk".to_string(),
|
||||||
url: "https://github.com/fufesou/rustdesk-plugins".to_string(),
|
url: "https://raw.githubusercontent.com/fufesou/rustdesk-plugins/main".to_string(),
|
||||||
description: "".to_string(),
|
description: "".to_string(),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
fn get_source_plugins() -> HashMap<String, PluginInfo> {
|
|
||||||
let meta_file = super::get_plugins_dir().unwrap().join("meta.toml");
|
|
||||||
let mut plugins = HashMap::new();
|
|
||||||
let manager_meta = load_path::<ManagerMeta>(meta_file);
|
|
||||||
let source = PluginSource {
|
|
||||||
name: "rustdesk".to_string(),
|
|
||||||
url: "https://github.com/fufesou/rustdesk-plugins".to_string(),
|
|
||||||
description: "".to_string(),
|
|
||||||
};
|
|
||||||
for meta in manager_meta.plugins.iter() {
|
|
||||||
plugins.insert(
|
|
||||||
meta.id.clone(),
|
|
||||||
PluginInfo {
|
|
||||||
source: source.clone(),
|
|
||||||
meta: meta.clone(),
|
|
||||||
installed_version: "".to_string(),
|
|
||||||
install_time: "".to_string(),
|
|
||||||
invalid_reason: "".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn get_source_plugins() -> HashMap<String, PluginInfo> {
|
fn get_source_plugins() -> HashMap<String, PluginInfo> {
|
||||||
let mut plugins = HashMap::new();
|
let mut plugins = HashMap::new();
|
||||||
for source in get_plugin_source_list().into_iter() {
|
for source in get_plugin_source_list().into_iter() {
|
||||||
@ -142,7 +113,8 @@ pub fn load_plugin_list() {
|
|||||||
for (id, info) in super::plugins::get_plugin_infos().read().unwrap().iter() {
|
for (id, info) in super::plugins::get_plugin_infos().read().unwrap().iter() {
|
||||||
if let Some(p) = plugins.get_mut(id) {
|
if let Some(p) = plugins.get_mut(id) {
|
||||||
p.install_time = info.install_time.clone();
|
p.install_time = info.install_time.clone();
|
||||||
p.invalid_reason = info.desc.meta().version.clone();
|
p.installed_version = info.desc.meta().version.clone();
|
||||||
|
p.invalid_reason = "".to_string();
|
||||||
} else {
|
} else {
|
||||||
plugins.insert(
|
plugins.insert(
|
||||||
id.to_string(),
|
id.to_string(),
|
||||||
@ -171,9 +143,10 @@ pub fn install_plugin(id: &str) -> ResultType<()> {
|
|||||||
"{}/plugins/{}/{}_{}.zip",
|
"{}/plugins/{}/{}_{}.zip",
|
||||||
plugin.source.url, plugin.meta.id, plugin.meta.id, plugin.meta.version
|
plugin.source.url, plugin.meta.id, plugin.meta.id, plugin.meta.version
|
||||||
);
|
);
|
||||||
|
// to-do: Support args with space in quotes. 'arg 1' and "arg 2"
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _res =
|
let _res =
|
||||||
crate::platform::elevate(&format!("--plugin-install '{}' '{}'", id, _plugin_url))?;
|
crate::platform::elevate(&format!("--plugin-install {} {}", id, _plugin_url))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -223,7 +196,8 @@ async fn handle_conn(mut stream: crate::ipc::Connection) {
|
|||||||
}
|
}
|
||||||
InstallStatus::Finished => {
|
InstallStatus::Finished => {
|
||||||
allow_err!(super::plugins::load_plugin(&id));
|
allow_err!(super::plugins::load_plugin(&id));
|
||||||
allow_err!(super::ipc::load_plugin(id));
|
allow_err!(super::ipc::load_plugin_async(id).await);
|
||||||
|
load_plugin_list();
|
||||||
push_install_event(&id, "finished");
|
push_install_event(&id, "finished");
|
||||||
}
|
}
|
||||||
InstallStatus::FailedCreating => {
|
InstallStatus::FailedCreating => {
|
||||||
@ -373,15 +347,31 @@ pub(super) mod install {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let filename = plugin_dir.join(format!("{}.zip", id));
|
let filename = plugin_dir.join(format!("{}.zip", id));
|
||||||
|
|
||||||
|
// download
|
||||||
if !download_file(id, url, &filename) {
|
if !download_file(id, url, &filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filename_to_remove = filename.clone();
|
||||||
|
let _call_on_ret = crate::common::SimpleCallOnReturn {
|
||||||
|
b: true,
|
||||||
|
f: Box::new(move || {
|
||||||
|
if let Err(e) = std::fs::remove_file(&filename_to_remove) {
|
||||||
|
log::error!("Failed to remove plugin file: {}", e);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// install
|
||||||
send_install_status(id, InstallStatus::Installing);
|
send_install_status(id, InstallStatus::Installing);
|
||||||
if let Err(e) = do_install_file(&filename, &plugin_dir) {
|
if let Err(e) = do_install_file(&filename, &plugin_dir) {
|
||||||
log::error!("Failed to install plugin: {}", e);
|
log::error!("Failed to install plugin: {}", e);
|
||||||
send_install_status(id, InstallStatus::FailedInstalling);
|
send_install_status(id, InstallStatus::FailedInstalling);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finished
|
||||||
send_install_status(id, InstallStatus::Finished);
|
send_install_status(id, InstallStatus::Finished);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ pub use plugins::{
|
|||||||
reload_plugin, sync_ui, unload_plugin, unload_plugins,
|
reload_plugin, sync_ui, unload_plugin, unload_plugins,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MSG_TO_UI_TYPE_PLUGIN_DESC: &str = "plugin_desc";
|
|
||||||
const MSG_TO_UI_TYPE_PLUGIN_EVENT: &str = "plugin_event";
|
const MSG_TO_UI_TYPE_PLUGIN_EVENT: &str = "plugin_event";
|
||||||
const MSG_TO_UI_TYPE_PLUGIN_RELOAD: &str = "plugin_reload";
|
const MSG_TO_UI_TYPE_PLUGIN_RELOAD: &str = "plugin_reload";
|
||||||
const MSG_TO_UI_TYPE_PLUGIN_OPTION: &str = "plugin_option";
|
const MSG_TO_UI_TYPE_PLUGIN_OPTION: &str = "plugin_option";
|
||||||
@ -90,12 +89,12 @@ impl PluginReturn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
|
if !is_server() {
|
||||||
std::thread::spawn(move || manager::start_ipc());
|
std::thread::spawn(move || manager::start_ipc());
|
||||||
if is_server() {
|
} else {
|
||||||
manager::remove_plugins();
|
manager::remove_plugins();
|
||||||
allow_err!(plugins::load_plugins());
|
|
||||||
}
|
}
|
||||||
load_plugin_list();
|
allow_err!(plugins::load_plugins());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -134,10 +133,10 @@ fn str_to_cstr_ret(s: &str) -> *const c_char {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn free_c_ptr(ret: *mut c_void) {
|
fn free_c_ptr(p: *mut c_void) {
|
||||||
if !ret.is_null() {
|
if !p.is_null() {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::free(ret);
|
libc::free(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,7 @@ struct InitData {
|
|||||||
impl Drop for InitData {
|
impl Drop for InitData {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
free_c_ptr(self.version as _);
|
free_c_ptr(self.version as _);
|
||||||
|
free_c_ptr(self.info as _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,18 +351,9 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
let init_info = serde_json::to_string(&InitInfo {
|
let init_info = serde_json::to_string(&InitInfo {
|
||||||
is_server: crate::common::is_server(),
|
is_server: crate::common::is_server(),
|
||||||
})?;
|
})?;
|
||||||
let ptr_info = str_to_cstr_ret(&init_info);
|
|
||||||
let ptr_version = str_to_cstr_ret(crate::VERSION);
|
|
||||||
let _call_on_ret = crate::common::SimpleCallOnReturn {
|
|
||||||
b: true,
|
|
||||||
f: Box::new(move || {
|
|
||||||
free_c_ptr(ptr_info as _);
|
|
||||||
free_c_ptr(ptr_version as _);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
let init_data = InitData {
|
let init_data = InitData {
|
||||||
version: ptr_version as _,
|
version: str_to_cstr_ret(crate::VERSION),
|
||||||
info: ptr_info as _,
|
info: str_to_cstr_ret(&init_info) as _,
|
||||||
cbs: Callbacks {
|
cbs: Callbacks {
|
||||||
msg: callback_msg::cb_msg,
|
msg: callback_msg::cb_msg,
|
||||||
get_conf: config::cb_get_conf,
|
get_conf: config::cb_get_conf,
|
||||||
@ -378,7 +370,6 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
|
|
||||||
// update ui
|
// update ui
|
||||||
// Ui may be not ready now, so we need to update again once ui is ready.
|
// Ui may be not ready now, so we need to update again once ui is ready.
|
||||||
update_ui_plugin_desc(&desc, None);
|
|
||||||
reload_ui(&desc, None);
|
reload_ui(&desc, None);
|
||||||
|
|
||||||
let install_time = PathBuf::from(path)
|
let install_time = PathBuf::from(path)
|
||||||
@ -404,7 +395,6 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
|
|
||||||
pub fn sync_ui(sync_to: String) {
|
pub fn sync_ui(sync_to: String) {
|
||||||
for plugin in PLUGIN_INFO.read().unwrap().values() {
|
for plugin in PLUGIN_INFO.read().unwrap().values() {
|
||||||
update_ui_plugin_desc(&plugin.desc, Some(&sync_to));
|
|
||||||
reload_ui(&plugin.desc, Some(&sync_to));
|
reload_ui(&plugin.desc, Some(&sync_to));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -640,27 +630,6 @@ fn reload_ui(desc: &Desc, sync_to: Option<&str>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_ui_plugin_desc(desc: &Desc, sync_to: Option<&str>) {
|
|
||||||
// This function is rarely used. There's no need to care about serialization efficiency here.
|
|
||||||
if let Ok(desc_str) = serde_json::to_string(desc) {
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
m.insert("name", MSG_TO_UI_TYPE_PLUGIN_DESC);
|
|
||||||
m.insert("desc", &desc_str);
|
|
||||||
let event = serde_json::to_string(&m).unwrap_or("".to_owned());
|
|
||||||
match sync_to {
|
|
||||||
Some(channel) => {
|
|
||||||
let _res = flutter::push_global_event(channel, event.clone());
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let _res = flutter::push_global_event(flutter::APP_TYPE_MAIN, event.clone());
|
|
||||||
let _res =
|
|
||||||
flutter::push_global_event(flutter::APP_TYPE_DESKTOP_REMOTE, event.clone());
|
|
||||||
let _res = flutter::push_global_event(flutter::APP_TYPE_CM, event.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get_plugin_infos() -> Arc<RwLock<HashMap<String, PluginInfo>>> {
|
pub(super) fn get_plugin_infos() -> Arc<RwLock<HashMap<String, PluginInfo>>> {
|
||||||
PLUGIN_INFO.clone()
|
PLUGIN_INFO.clone()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user