Skip to content

Commit

Permalink
🐛 fix install codec
Browse files Browse the repository at this point in the history
  • Loading branch information
H2Sxxa committed Jul 24, 2024
1 parent 48fd099 commit 99f4d61
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 52 deletions.
6 changes: 6 additions & 0 deletions lib/models/distribution.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class LinuxDistribution {
late final String? amd64PackageUrl;
late final String? arm64PackageUrl;
late final String packageFamilyName;

static FutureLazyDynamicCan<List<LinuxDistribution>> distributions =
FutureLazyDynamicCan(
builder: fetch,
);

static Future<List<LinuxDistribution>> fetch() async {
var url = ArcheBus()
.of<AppConfigs>()
Expand Down
90 changes: 55 additions & 35 deletions lib/views/pages/install.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import 'package:wslconfigurer/models/distribution.dart';
import 'package:wslconfigurer/views/widgets/basic.dart';
import 'package:wslconfigurer/views/widgets/divider.dart';
import 'package:wslconfigurer/views/widgets/optfeat.dart';
import 'package:wslconfigurer/views/widgets/process.dart';
import 'package:wslconfigurer/windows/ms_open.dart';
import 'package:wslconfigurer/windows/utf16.dart';

class InstallPage extends StatefulWidget {
const InstallPage({super.key});
Expand All @@ -28,13 +30,18 @@ class _InstallPageState extends State<InstallPage> {
ListTile(
leading: const Icon(FontAwesomeIcons.section),
title: context.i18nText("install.install_linux_distro"),
trailing: IconButton(
onPressed: () => LinuxDistribution.distributions
.reload()
.then((_) => setState(() {})),
icon: const Icon(Icons.refresh)),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
FutureBuilder(
future: LinuxDistribution.fetch(),
future: LinuxDistribution.distributions.getValue(),
builder: (context, snapshot) {
var data = snapshot.data;
if (data == null) {
Expand All @@ -55,42 +62,55 @@ class _InstallPageState extends State<InstallPage> {
children: data
.map(
(distro) => ListTile(
title: Text(distro.friendlyName),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
var messager =
ScaffoldMessenger.of(context);
messager.clearSnackBars();
messager.showSnackBar(
const SnackBar(
content: Text("Copyied!"),
),
);
title: Text(distro.friendlyName),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
var messager =
ScaffoldMessenger.of(context);
messager.clearSnackBars();
messager.showSnackBar(
const SnackBar(
content: Text("Copyied!"),
),
);

Clipboard.setData(ClipboardData(
text:
"wsl.exe --install -d ${distro.name}"));
},
icon: const Icon(Icons.copy),
),
IconButton(
onPressed: () {
ComplexDialog.instance.text(
Clipboard.setData(ClipboardData(
text:
"wsl.exe --install -d ${distro.name} --no-launch"));
},
icon: const Icon(Icons.copy),
),
IconButton(
onPressed: () => ComplexDialog
.instance
.copy(barrierDismissible: false)
.text(
context: context,
);
},
icon: const Icon(Icons.terminal),
),
IconButton(
onPressed: () => openMSStoreProduct(
distro.storeAppId),
icon: const Icon(Icons.store),
),
],
)),
content:
ProcessCommandRunWidget(
executable: "wsl.exe",
arguments: [
"--install",
"-d",
distro.name,
"--no-launch"
],
codec: utf16,
),
),
icon: const Icon(Icons.terminal),
),
IconButton(
onPressed: () => openMSStoreProduct(
distro.storeAppId),
icon: const Icon(Icons.store),
),
],
),
),
)
.toList(),
),
Expand Down
1 change: 1 addition & 0 deletions lib/views/widgets/optfeat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class _CheckOptionalFeatureWidgetState
content: SingleChildScrollView(
child: ProcessText(
process: process,
latest: true,
),
),
)
Expand Down
162 changes: 150 additions & 12 deletions lib/views/widgets/process.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,162 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:wslconfigurer/windows/sh.dart';

class ProcessText extends StatelessWidget {
class ProcessText extends StatefulWidget {
final Process process;
final bool latest;
final Encoding codec;
const ProcessText({
super.key,
required this.process,
this.latest = false,
this.codec = systemEncoding,
});

const ProcessText({super.key, required this.process});
@override
State<StatefulWidget> createState() => _ProcessTextState();
}

class _ProcessTextState extends State<ProcessText> {
List<(bool, String)> span = [];

@override
void initState() {
super.initState();

var process = widget.process;

process.stderr.listen(
(data) => setState(() {
span.add((true, widget.codec.decode(data).trim()));
}),
);
process.stdout.listen(
(data) => setState(() {
span.add((false, widget.codec.decode(data).trim()));
}),
);
}

@override
Widget build(BuildContext context) {
if (span.isEmpty) {
return const SizedBox.shrink();
}

if (widget.latest) {
return span.lastOrNull == null
? const Text("")
: SelectableText(
span.last.$2,
style: span.last.$1 ? const TextStyle(color: Colors.red) : null,
);
}

return Column(
children: span
.map((data) => SelectableText(data.$2,
style: data.$1 ? const TextStyle(color: Colors.red) : null))
.toList(),
);
}
}

class ProcessCommandRunWidget extends StatefulWidget {
final String executable;
final Iterable<String> arguments;
final bool su;
final bool runInShell;
final Encoding codec;

const ProcessCommandRunWidget({
super.key,
required this.executable,
this.arguments = const [],
this.su = false,
this.runInShell = false,
this.codec = systemEncoding,
});

@override
State<StatefulWidget> createState() => _ProcessCommandRunWidgetState();
}

class _ProcessCommandRunWidgetState extends State<ProcessCommandRunWidget> {
bool confirm = false;
Process? process;

@override
void dispose() {
super.dispose();
process?.kill();
}

@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: process.stdout,
builder: (context, snapshot) {
var data = snapshot.data;
if (data == null) {
return const Text("");
}

return SelectableText(const SystemEncoding().decode(data));
},
if (confirm) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
[widget.executable, ...widget.arguments].join(" "),
),
const IconButton(
onPressed: null,
icon: Icon(Icons.keyboard_arrow_right_rounded),
),
],
),
FutureBuilder(
future: Process.start(
widget.executable,
widget.arguments.toList(),
runInShell: widget.runInShell,
),
builder: (context, snapshot) {
var proc = snapshot.data;
if (proc == null) {
return const CircularProgressIndicator();
}
process = proc;

return ProcessText(
process: proc,
codec: widget.codec,
);
},
)
],
);
}
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
[widget.executable, ...widget.arguments].join(" "),
),
IconButton(
onPressed: () {
if (widget.su) {
su(
context,
() => setState(() {
confirm = true;
}));
} else {
setState(() {
confirm = true;
});
}
},
icon: const Icon(Icons.keyboard_arrow_right_rounded),
),
],
);
}
}
1 change: 0 additions & 1 deletion lib/windows/sh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,3 @@ Future<Process> enableFeature(String featurename) async {
"/norestart"
]);
}

33 changes: 33 additions & 0 deletions lib/windows/utf16.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/services.dart';

const utf16 = Utf16Codec();

final class Utf16Codec extends Encoding {
const Utf16Codec();
@override
Converter<List<int>, String> get decoder => Utf16Decoder();

@override
Converter<String, List<int>> get encoder => Utf16Encoder();

@override
String get name => "utf-16";
}

class Utf16Decoder extends Converter<List<int>, String> {
@override
String convert(List<int> input) {
return String.fromCharCodes(
Uint16List.sublistView(Uint8List.fromList(input)));
}
}

class Utf16Encoder extends Converter<String, List<int>> {
@override
List<int> convert(String input) {
return input.codeUnits;
}
}
6 changes: 3 additions & 3 deletions lib/windows/wsl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:io';

class WindowsSubSystemLinux {
static void shutdown(String target, {String? distro}) async {
await Process.run("wsl", [
await Process.run("wsl.exe", [
...distro != null ? ["-d", distro] : [],
"--shutdown"
]);
Expand Down Expand Up @@ -40,7 +40,7 @@ class WindowsSubSystemLinux {
}

static Future<List<String>> getAvailableDistro() async {
return (await Process.run("wsl", ["-l", "-q"]))
return (await Process.run("wsl.exe", ["-l", "-q"]))
.stdout
.toString()
.split("\n")
Expand All @@ -50,7 +50,7 @@ class WindowsSubSystemLinux {
}

static List<String> getAvailableDistroSync() {
return Process.runSync("wsl", ["-l", "-q"])
return Process.runSync("wsl.exe", ["-l", "-q"])
.stdout
.toString()
.split("\n")
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ flutter:
- assets/i18n/
- assets/i18n/en_US/
- assets/i18n/zh_CN/

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

Expand Down

0 comments on commit 99f4d61

Please sign in to comment.