import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; import 'dart:ui'; import 'package:fl_clash/plugins/app.dart'; import 'package:fl_clash/plugins/tile.dart'; import 'package:fl_clash/plugins/vpn.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'application.dart'; import 'clash/core.dart'; import 'clash/lib.dart'; import 'common/common.dart'; import 'models/models.dart'; Future main() async { globalState.isService = false; WidgetsFlutterBinding.ensureInitialized(); final version = await system.version; await clashCore.preload(); await globalState.initApp(version); await android?.init(); await window?.init(version); HttpOverrides.global = FlClashHttpOverrides(); runApp(ProviderScope( child: const Application(), )); } @pragma('vm:entry-point') Future _service(List flags) async { globalState.isService = true; WidgetsFlutterBinding.ensureInitialized(); final quickStart = flags.contains("quick"); final clashLibHandler = ClashLibHandler(); await globalState.init(); tile?.addListener( _TileListenerWithService( onStop: () async { await app?.tip(appLocalizations.stopVpn); clashLibHandler.stopListener(); await vpn?.stop(); exit(0); }, ), ); vpn?.handleGetStartForegroundParams = () { final traffic = clashLibHandler.getTraffic(); return json.encode({ "title": clashLibHandler.getCurrentProfileName(), "content": "$traffic" }); }; vpn?.addListener( _VpnListenerWithService( onDnsChanged: (String dns) { clashLibHandler.updateDns(dns); }, ), ); if (!quickStart) { _handleMainIpc(clashLibHandler); } else { commonPrint.log("quick start"); await ClashCore.initGeo(); app?.tip(appLocalizations.startVpn); final homeDirPath = await appPath.homeDirPath; final version = await system.version; final clashConfig = globalState.config.patchClashConfig.copyWith.tun( enable: false, ); Future(() async { final profileId = globalState.config.currentProfileId; if (profileId == null) { return; } final params = await globalState.getSetupParams( pathConfig: clashConfig, ); final res = await clashLibHandler.quickStart( InitParams( homeDir: homeDirPath, version: version, ), params, globalState.getCoreState(), ); debugPrint(res); if (res.isNotEmpty) { await vpn?.stop(); exit(0); } await vpn?.start( clashLibHandler.getAndroidVpnOptions(), ); clashLibHandler.startListener(); }); } } _handleMainIpc(ClashLibHandler clashLibHandler) { final sendPort = IsolateNameServer.lookupPortByName(mainIsolate); if (sendPort == null) { return; } final serviceReceiverPort = ReceivePort(); serviceReceiverPort.listen((message) async { final res = await clashLibHandler.invokeAction(message); sendPort.send(res); }); sendPort.send(serviceReceiverPort.sendPort); final messageReceiverPort = ReceivePort(); clashLibHandler.attachMessagePort( messageReceiverPort.sendPort.nativePort, ); messageReceiverPort.listen((message) { sendPort.send(message); }); } @immutable class _TileListenerWithService with TileListener { final Function() _onStop; const _TileListenerWithService({ required Function() onStop, }) : _onStop = onStop; @override void onStop() { _onStop(); } } @immutable class _VpnListenerWithService with VpnListener { final Function(String dns) _onDnsChanged; const _VpnListenerWithService({ required Function(String dns) onDnsChanged, }) : _onDnsChanged = onDnsChanged; @override void onDnsChanged(String dns) { super.onDnsChanged(dns); _onDnsChanged(dns); } }