mirror of
https://github.com/Cateners/tiny_computer.git
synced 2026-05-20 16:35:47 +08:00
getifaddrs fix
This commit is contained in:
200
lib/main.dart
200
lib/main.dart
@@ -125,8 +125,10 @@ class _SettingPageState extends State<SettingPage> {
|
||||
//setState(() {});
|
||||
}),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
TextFormField(maxLines: null, initialValue: Util.getCurrentProp("boot"), decoration: const InputDecoration(border: OutlineInputBorder(), labelText: "启动命令"), onChanged: (value) async {
|
||||
await Util.setCurrentProp("boot", value);
|
||||
ValueListenableBuilder(valueListenable: G.bootTextChange, builder:(context, v, child) {
|
||||
return TextFormField(maxLines: null, initialValue: Util.getCurrentProp("boot"), decoration: const InputDecoration(border: OutlineInputBorder(), labelText: "启动命令"), onChanged: (value) async {
|
||||
await Util.setCurrentProp("boot", value);
|
||||
});
|
||||
}),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
TextFormField(maxLines: null, initialValue: Util.getCurrentProp("vnc"), decoration: const InputDecoration(border: OutlineInputBorder(), labelText: "vnc启动命令"), onChanged: (value) async {
|
||||
@@ -136,6 +138,22 @@ class _SettingPageState extends State<SettingPage> {
|
||||
TextFormField(maxLines: null, initialValue: Util.getCurrentProp("vncUrl"), decoration: const InputDecoration(border: OutlineInputBorder(), labelText: "网页跳转地址"), onChanged: (value) async {
|
||||
await Util.setCurrentProp("vncUrl", value);
|
||||
}),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
OutlinedButton(style: D.commandButtonStyle, child: const Text("重置启动命令"), onPressed: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(title: const Text("注意"), content: const Text("是否重置启动命令?"), actions: [
|
||||
TextButton(onPressed:() {
|
||||
Navigator.of(context).pop();
|
||||
}, child: const Text("取消")),
|
||||
TextButton(onPressed:() async {
|
||||
await Util.setCurrentProp("boot", D.boot);
|
||||
G.bootTextChange.value = !G.bootTextChange.value;
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
}, child: const Text("是")),
|
||||
]);
|
||||
});
|
||||
}),
|
||||
],))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[1],
|
||||
@@ -176,7 +194,9 @@ class _SettingPageState extends State<SettingPage> {
|
||||
return;
|
||||
}
|
||||
G.prefs.setBool("isBannerAdsClosed", value);
|
||||
setState(() {});
|
||||
setState(() {
|
||||
G.bannerAdsChange.value = !G.bannerAdsChange.value;
|
||||
});
|
||||
},),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
SwitchListTile(title: const Text("启用终端"), value: Util.getGlobal("isTerminalWriteEnabled") as bool, onChanged:(value) {
|
||||
@@ -203,7 +223,9 @@ class _SettingPageState extends State<SettingPage> {
|
||||
return;
|
||||
}
|
||||
G.prefs.setBool("isTerminalCommandsEnabled", value);
|
||||
setState(() {});
|
||||
setState(() {
|
||||
G.terminalPageChange.value = !G.terminalPageChange.value;
|
||||
});
|
||||
},),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
SwitchListTile(title: const Text("终端粘滞键"), value: Util.getGlobal("isStickyKey") as bool, onChanged:(value) {
|
||||
@@ -215,6 +237,16 @@ class _SettingPageState extends State<SettingPage> {
|
||||
G.prefs.setBool("autoLaunchVnc", value);
|
||||
setState(() {});
|
||||
},),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
SwitchListTile(title: const Text("重新安装引导包"), subtitle: const Text("下次启动时生效"), value: Util.getGlobal("reinstallBootstrap") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("reinstallBootstrap", value);
|
||||
setState(() {});
|
||||
},),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
SwitchListTile(title: const Text("getifaddrs桥接"), subtitle: const Text("下次启动时生效"), value: Util.getGlobal("getifaddrsBridge") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("getifaddrsBridge", value);
|
||||
setState(() {});
|
||||
},),
|
||||
],))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[2],
|
||||
@@ -365,7 +397,9 @@ class _SettingPageState extends State<SettingPage> {
|
||||
}
|
||||
|
||||
class InfoPage extends StatefulWidget {
|
||||
const InfoPage({super.key});
|
||||
final bool openFirstInfo;
|
||||
|
||||
const InfoPage({super.key, this.openFirstInfo=false});
|
||||
|
||||
@override
|
||||
State<InfoPage> createState() => _InfoPageState();
|
||||
@@ -373,6 +407,13 @@ class InfoPage extends StatefulWidget {
|
||||
|
||||
class _InfoPageState extends State<InfoPage> {
|
||||
final List<bool> _expandState = [false, false, false, false, false];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_expandState[0] = widget.openFirstInfo;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ExpansionPanelList(
|
||||
@@ -390,7 +431,11 @@ class _InfoPageState extends State<InfoPage> {
|
||||
},
|
||||
body: const Padding(padding: EdgeInsets.all(8), child: Text("""
|
||||
第一次加载大概需要5到10分钟...
|
||||
加载完成后,软件会自动跳转到图形界面
|
||||
正常情况下,加载完成后软件会自动跳转到图形界面
|
||||
|
||||
在图形界面时,点击即鼠标左键
|
||||
双指点击为鼠标右键
|
||||
双指划动为鼠标滚轮
|
||||
|
||||
在图形界面返回,可以回到终端界面和控制界面
|
||||
你可以在控制界面安装更多软件或者阅读帮助信息
|
||||
@@ -418,8 +463,8 @@ class _InfoPageState extends State<InfoPage> {
|
||||
可能会在使用过程中异常退出(返回错误码9)
|
||||
届时本软件会提供方案指引你修复
|
||||
并不难
|
||||
此软件因为没有权限
|
||||
所以不能帮你修复
|
||||
但是软件没有权限
|
||||
不能帮你修复
|
||||
|
||||
如果你给了存储权限
|
||||
那么通过主目录下的文件夹
|
||||
@@ -477,11 +522,11 @@ Vivo Pad,安卓13,看不见鼠标移动(可以去左栏设置开启显示
|
||||
也不方便定位原因)
|
||||
如果你遇到了类似问题
|
||||
不管解没解决
|
||||
都可以去https://github.com/Cateners/tiny_computer/issues/1留个言
|
||||
都可以去https://github.com/Cateners/tiny_computer/issues/留个言
|
||||
|
||||
如果软件里有程序正在正常运行
|
||||
请不要强行关闭本软件
|
||||
否则可能会损坏本容器
|
||||
否则可能会损坏容器
|
||||
特别是在安装某些比较大的软件的时候
|
||||
|
||||
感谢使用!
|
||||
@@ -902,7 +947,9 @@ SOFTWARE.
|
||||
}), body: const Padding(padding: EdgeInsets.all(8), child: Text("""
|
||||
除由Unity提供的广告功能外, 本软件不会收集你的隐私信息。
|
||||
|
||||
申请的权限用于以下目的:
|
||||
当然,你在容器系统内部安装或使用的软件行为就不受我控制了,所以我不对其负责。
|
||||
|
||||
本软件申请的权限用于以下目的:
|
||||
文件相关权限:用于系统访问手机目录;
|
||||
相机和麦克风:用于推流,默认不会开启。
|
||||
|
||||
@@ -921,17 +968,6 @@ SOFTWARE.
|
||||
本程序是自由软件:你可以再分发之和/或依照由自由软件基金会发布的 GNU 通用公共许可证修改之,无论是版本 3 许可证,还是任何以后版都可以。
|
||||
发布该程序是希望它能有用,但是并无保障;甚至连可销售和符合某个特定的目的都不保证。请参看 GNU 通用公共许可证,了解详情。
|
||||
你应该随程序获得一份 GNU 通用公共许可证的复本。如果没有,请看 <https://www.gnu.org/licenses/>。
|
||||
|
||||
你可能注意到本软件使用了Unity广告服务,
|
||||
那么它是否与本项目有冲突?
|
||||
|
||||
事实上,这个项目不依赖广告,
|
||||
你完全可以自行编译一份不包含广告的版本;
|
||||
|
||||
不管怎么说,我希望这不是一个问题...
|
||||
|
||||
...当然!
|
||||
我还是不想看到你们去编译一个不含广告的版本><
|
||||
"""))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[4],
|
||||
@@ -997,11 +1033,11 @@ class LoadingPage extends StatelessWidget {
|
||||
const LoadingPage({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 16, 16, 16),
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: 0.4,
|
||||
@@ -1011,14 +1047,16 @@ class LoadingPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 8),
|
||||
child: Text("小小电脑", textScaleFactor: 2),
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 8),
|
||||
child: ValueListenableBuilder(valueListenable: G.updateText, builder:(context, value, child) {
|
||||
return Text(value, textScaleFactor: 2);
|
||||
}),
|
||||
),
|
||||
FakeLoadingStatus(),
|
||||
Expanded(child: Padding(padding: EdgeInsets.all(8), child: Card(child: Padding(padding: EdgeInsets.all(8), child:
|
||||
const FakeLoadingStatus(),
|
||||
const Expanded(child: Padding(padding: EdgeInsets.all(8), child: Card(child: Padding(padding: EdgeInsets.all(8), child:
|
||||
Scrollbar(child:
|
||||
SingleChildScrollView(
|
||||
child: InfoPage()
|
||||
child: InfoPage(openFirstInfo: true)
|
||||
)
|
||||
)
|
||||
))
|
||||
@@ -1060,43 +1098,45 @@ class TerminalPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(children: [Expanded(child: forceScaleGestureDetector(onScaleUpdate: (details) {
|
||||
G.termFontScale.value = (details.scale * (Util.getGlobal("termFontScale") as double)).clamp(0.2, 5);
|
||||
}, onScaleEnd: (details) async {
|
||||
await G.prefs.setDouble("termFontScale", G.termFontScale.value);
|
||||
}, child: ValueListenableBuilder(valueListenable: G.termFontScale, builder:(context, value, child) {
|
||||
return TerminalView(G.termPtys[G.currentContainer]!.terminal, textScaleFactor: G.termFontScale.value, keyboardType: TextInputType.multiline);
|
||||
},) )),
|
||||
(Util.getGlobal("isTerminalCommandsEnabled") as bool)?Padding(padding: const EdgeInsets.all(8), child: Row(children: [AnimatedBuilder(
|
||||
animation: G.keyboard,
|
||||
builder: (context, child) => ToggleButtons(
|
||||
constraints: const BoxConstraints(minWidth: 32, minHeight: 24),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
isSelected: [G.keyboard.ctrl, G.keyboard.alt, G.keyboard.shift],
|
||||
onPressed: (index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
G.keyboard.ctrl = !G.keyboard.ctrl;
|
||||
break;
|
||||
case 1:
|
||||
G.keyboard.alt = !G.keyboard.alt;
|
||||
break;
|
||||
case 2:
|
||||
G.keyboard.shift = !G.keyboard.shift;
|
||||
break;
|
||||
}
|
||||
},
|
||||
children: const [Text('Ctrl'), Text('Alt'), Text('Shift')],
|
||||
G.termFontScale.value = (details.scale * (Util.getGlobal("termFontScale") as double)).clamp(0.2, 5);
|
||||
}, onScaleEnd: (details) async {
|
||||
await G.prefs.setDouble("termFontScale", G.termFontScale.value);
|
||||
}, child: ValueListenableBuilder(valueListenable: G.termFontScale, builder:(context, value, child) {
|
||||
return TerminalView(G.termPtys[G.currentContainer]!.terminal, textScaleFactor: G.termFontScale.value, keyboardType: TextInputType.multiline);
|
||||
},) )),
|
||||
ValueListenableBuilder(valueListenable: G.terminalPageChange, builder:(context, value, child) {
|
||||
return (Util.getGlobal("isTerminalCommandsEnabled") as bool)?Padding(padding: const EdgeInsets.all(8), child: Row(children: [AnimatedBuilder(
|
||||
animation: G.keyboard,
|
||||
builder: (context, child) => ToggleButtons(
|
||||
constraints: const BoxConstraints(minWidth: 32, minHeight: 24),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
isSelected: [G.keyboard.ctrl, G.keyboard.alt, G.keyboard.shift],
|
||||
onPressed: (index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
G.keyboard.ctrl = !G.keyboard.ctrl;
|
||||
break;
|
||||
case 1:
|
||||
G.keyboard.alt = !G.keyboard.alt;
|
||||
break;
|
||||
case 2:
|
||||
G.keyboard.shift = !G.keyboard.shift;
|
||||
break;
|
||||
}
|
||||
},
|
||||
children: const [Text('Ctrl'), Text('Alt'), Text('Shift')],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
Expanded(child: SizedBox(height: 24, child: ListView.separated(scrollDirection: Axis.horizontal, itemBuilder:(context, index) {
|
||||
return OutlinedButton(style: D.controlButtonStyle, onPressed: () {
|
||||
G.termPtys[G.currentContainer]!.terminal.keyInput(D.termCommands[index]["key"]! as TerminalKey);
|
||||
}, child: Text(D.termCommands[index]["name"]! as String));
|
||||
}, separatorBuilder:(context, index) {
|
||||
return SizedBox.fromSize(size: const Size.square(4));
|
||||
}, itemCount: D.termCommands.length))), SizedBox.fromSize(size: const Size(72, 0))])):SizedBox.fromSize(size: const Size.square(0))
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
Expanded(child: SizedBox(height: 24, child: ListView.separated(scrollDirection: Axis.horizontal, itemBuilder:(context, index) {
|
||||
return OutlinedButton(style: D.controlButtonStyle, onPressed: () {
|
||||
G.termPtys[G.currentContainer]!.terminal.keyInput(D.termCommands[index]["key"]! as TerminalKey);
|
||||
}, child: Text(D.termCommands[index]["name"]! as String));
|
||||
}, separatorBuilder:(context, index) {
|
||||
return SizedBox.fromSize(size: const Size.square(4));
|
||||
}, itemCount: D.termCommands.length))), SizedBox.fromSize(size: const Size(72, 0))])):SizedBox.fromSize(size: const Size.square(0));
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1177,7 +1217,7 @@ class _FastCommandsState extends State<FastCommands> {
|
||||
},);
|
||||
}, onLongPress: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(content: const Text("是否重置所有快捷指令?"), actions: [
|
||||
return AlertDialog(title: const Text("重置指令"), content: const Text("是否重置所有快捷指令?"), actions: [
|
||||
TextButton(onPressed:() {
|
||||
Navigator.of(context).pop();
|
||||
}, child: const Text("取消")),
|
||||
@@ -1230,17 +1270,19 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
title: Text(isLoadingComplete?Util.getCurrentProp("name"):widget.title),
|
||||
),
|
||||
body: isLoadingComplete?Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
(Util.getGlobal("isBannerAdsClosed") as bool)||bannerAdsFailedToLoad?SizedBox.fromSize(size: const Size.square(0)):UnityBannerAd(
|
||||
placementId: AdManager.bannerAdPlacementId,
|
||||
onLoad: (placementId) => debugPrint('Banner loaded: $placementId'),
|
||||
onClick: (placementId) => debugPrint('Banner clicked: $placementId'),
|
||||
onFailed: (placementId, error, message) {
|
||||
debugPrint('Banner Ad $placementId failed: $error $message');
|
||||
setState(() {
|
||||
bannerAdsFailedToLoad = true;
|
||||
});
|
||||
},
|
||||
), Expanded(child: ValueListenableBuilder(valueListenable: G.pageIndex, builder: (context, value, child) {
|
||||
ValueListenableBuilder(valueListenable: G.bannerAdsChange, builder:(context, value, child) {
|
||||
return (Util.getGlobal("isBannerAdsClosed") as bool)||bannerAdsFailedToLoad?SizedBox.fromSize(size: const Size.square(0)):UnityBannerAd(
|
||||
placementId: AdManager.bannerAdPlacementId,
|
||||
onLoad: (placementId) => debugPrint('Banner loaded: $placementId'),
|
||||
onClick: (placementId) => debugPrint('Banner clicked: $placementId'),
|
||||
onFailed: (placementId, error, message) {
|
||||
debugPrint('Banner Ad $placementId failed: $error $message');
|
||||
setState(() {
|
||||
bannerAdsFailedToLoad = true;
|
||||
});
|
||||
},
|
||||
);
|
||||
}), Expanded(child: ValueListenableBuilder(valueListenable: G.pageIndex, builder: (context, value, child) {
|
||||
return IndexedStack(index: G.pageIndex.value, children: [const TerminalPage(), Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Scrollbar(child: SingleChildScrollView(restorationId: "control-scroll", child: Column(
|
||||
@@ -1263,7 +1305,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
Column(children: [
|
||||
const SettingPage(),
|
||||
SizedBox.fromSize(size: const Size.square(8)),
|
||||
const InfoPage()
|
||||
const InfoPage(openFirstInfo: false)
|
||||
])
|
||||
)))
|
||||
]
|
||||
|
||||
@@ -85,6 +85,7 @@ class Util {
|
||||
//int vip = 0 用户等级,vip免广告,你要改吗?(ToT)
|
||||
//bool isStickyKey = true 终端ctrl, shift, alt键是否粘滞
|
||||
//String defaultFFmpegCommand 默认推流命令
|
||||
//bool reinstallBootstrap = false 下次启动是否重装引导包
|
||||
//? int bootstrapVersion: 启动包版本
|
||||
//String[] containersInfo: 所有容器信息(json)
|
||||
//{name, boot:"\$DATA_DIR/bin/proot ...", vnc:"startnovnc", vncUrl:"...", commands:[{name:"更新和升级", command:"apt update -y && apt upgrade -y"},
|
||||
@@ -108,6 +109,8 @@ class Util {
|
||||
case "termFontScale" : return b ? G.prefs.getDouble(key)! : (value){G.prefs.setDouble(key, value); return value;}(1.0);
|
||||
case "vip" : return b ? G.prefs.getInt(key)! : (value){G.prefs.setInt(key, value); return value;}(0);
|
||||
case "isStickyKey" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true);
|
||||
case "reinstallBootstrap" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "getifaddrsBridge" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "defaultFFmpegCommand" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("-hide_banner -an -max_delay 1000000 -r 30 -f android_camera -camera_index 0 -i 0:0 -vf scale=iw/2:-1 -rtsp_transport udp -f rtsp rtsp://127.0.0.1:8554/stream");
|
||||
case "containersInfo" : return G.prefs.getStringList(key)!;
|
||||
case "adsBonus" : return b ? G.prefs.getStringList(key)! : (value){G.prefs.setStringList(key, value); return value;}([].cast<String>());
|
||||
@@ -377,6 +380,7 @@ class D {
|
||||
{"name": "F10", "key": TerminalKey.f10},
|
||||
{"name": "F11", "key": TerminalKey.f11},
|
||||
{"name": "F12", "key": TerminalKey.f12},
|
||||
|
||||
];
|
||||
|
||||
//看广告可以获得的奖励。
|
||||
@@ -405,6 +409,8 @@ class D {
|
||||
|
||||
};
|
||||
|
||||
static const String boot = "\$DATA_DIR/bin/proot -H --change-id=1000:1000 --pwd=/home/tiny --rootfs=\$CONTAINER_DIR --mount=/system --mount=/apex --kill-on-exit --mount=/storage:/storage --sysvipc -L --link2symlink --mount=/proc:/proc --mount=/dev:/dev --mount=\$CONTAINER_DIR/tmp:/dev/shm --mount=/dev/urandom:/dev/random --mount=/proc/self/fd:/dev/fd --mount=/proc/self/fd/0:/dev/stdin --mount=/proc/self/fd/1:/dev/stdout --mount=/proc/self/fd/2:/dev/stderr --mount=/dev/null:/dev/tty0 --mount=/dev/null:/proc/sys/kernel/cap_last_cap --mount=/storage/self/primary:/media/sd --mount=\$DATA_DIR/share:/home/tiny/公共 --mount=\$DATA_DIR/tiny:/home/tiny/.local/share/tiny --mount=/storage/self/primary/Fonts:/usr/share/fonts/wpsm --mount=/storage/self/primary/AppFiles/Fonts:/usr/share/fonts/yozom --mount=/system/fonts:/usr/share/fonts/androidm --mount=/storage/self/primary/Pictures:/home/tiny/图片 --mount=/storage/self/primary/Music:/home/tiny/音乐 --mount=/storage/self/primary/Movies:/home/tiny/视频 --mount=/storage/self/primary/Download:/home/tiny/下载 --mount=/storage/self/primary/DCIM:/home/tiny/照片 --mount=/storage/self/primary/Documents:/home/tiny/文档 --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/.tmoe-container.stat:/proc/stat --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/.tmoe-container.version:/proc/version --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/bus:/proc/bus --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/buddyinfo:/proc/buddyinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/cgroups:/proc/cgroups --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/consoles:/proc/consoles --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/crypto:/proc/crypto --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/devices:/proc/devices --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/diskstats:/proc/diskstats --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/execdomains:/proc/execdomains --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/fb:/proc/fb --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/filesystems:/proc/filesystems --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/interrupts:/proc/interrupts --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/iomem:/proc/iomem --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/ioports:/proc/ioports --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/kallsyms:/proc/kallsyms --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/keys:/proc/keys --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/key-users:/proc/key-users --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/kpageflags:/proc/kpageflags --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/loadavg:/proc/loadavg --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/locks:/proc/locks --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/misc:/proc/misc --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/modules:/proc/modules --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/pagetypeinfo:/proc/pagetypeinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/partitions:/proc/partitions --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/sched_debug:/proc/sched_debug --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/softirqs:/proc/softirqs --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/timer_list:/proc/timer_list --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/uptime:/proc/uptime --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/vmallocinfo:/proc/vmallocinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/vmstat:/proc/vmstat --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/zoneinfo:/proc/zoneinfo /usr/bin/env -i HOSTNAME=TINY HOME=/home/tiny USER=tiny TERM=xterm-256color SDL_IM_MODULE=fcitx XMODIFIERS=@im=fcitx QT_IM_MODULE=fcitx GTK_IM_MODULE=fcitx TMOE_CHROOT=false TMOE_PROOT=true TMPDIR=/tmp MOZ_FAKE_NO_SANDBOX=1 DISPLAY=:4 PULSE_SERVER=tcp:127.0.0.1:4718 LANG=zh_CN.UTF-8 SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/games:/usr/local/games \$EXTRA_OPT /bin/bash -l";
|
||||
|
||||
static final ButtonStyle commandButtonStyle = OutlinedButton.styleFrom(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
minimumSize: const Size(0, 0),
|
||||
@@ -441,6 +447,10 @@ class G {
|
||||
static String streamingOutput = "";
|
||||
static late Pty streamServerPty;
|
||||
static ValueNotifier<int> pageIndex = ValueNotifier(0); //主界面索引
|
||||
static ValueNotifier<bool> terminalPageChange = ValueNotifier(true); //更改值,用于刷新小键盘
|
||||
static ValueNotifier<bool> bannerAdsChange = ValueNotifier(true); //更改值,用于刷新banner广告
|
||||
static ValueNotifier<bool> bootTextChange = ValueNotifier(true); //更改值,用于刷新启动命令
|
||||
static ValueNotifier<String> updateText = ValueNotifier("小小电脑"); //加载界面的说明文字
|
||||
|
||||
|
||||
static late SharedPreferences prefs;
|
||||
@@ -532,6 +542,8 @@ class Workflow {
|
||||
Util.createDirFromString("${G.dataPath}/tmp");
|
||||
//给proot的tmp文件夹,虽然我不知道为什么proot要这个
|
||||
Util.createDirFromString("${G.dataPath}/proot_tmp");
|
||||
//给pulseaudio的tmp文件夹
|
||||
Util.createDirFromString("${G.dataPath}/pulseaudio_tmp");
|
||||
//解压后得到bin文件夹和libexec文件夹
|
||||
//bin存放了proot, pulseaudio, tar等
|
||||
//libexec存放了proot loader
|
||||
@@ -539,6 +551,12 @@ class Workflow {
|
||||
"assets/assets.zip",
|
||||
"${G.dataPath}/assets.zip",
|
||||
);
|
||||
//patch.tar.gz存放了tiny文件夹
|
||||
//里面是一些补丁,会被挂载到~/.local/share/tiny
|
||||
await Util.copyAsset(
|
||||
"assets/patch.tar.gz",
|
||||
"${G.dataPath}/patch.tar.gz",
|
||||
);
|
||||
//dddd
|
||||
await Util.copyAsset(
|
||||
"assets/busybox",
|
||||
@@ -549,19 +567,24 @@ class Workflow {
|
||||
export DATA_DIR=${G.dataPath}
|
||||
cd \$DATA_DIR
|
||||
chmod +x busybox
|
||||
\$DATA_DIR/busybox unzip assets.zip
|
||||
\$DATA_DIR/busybox unzip -o assets.zip
|
||||
chmod -R +x bin/*
|
||||
chmod -R +x libexec/proot/*
|
||||
chmod 1777 tmp
|
||||
ln -s \$DATA_DIR/busybox \$DATA_DIR/bin/xz
|
||||
\$DATA_DIR/busybox rm -rf assets.zip
|
||||
ln -sf \$DATA_DIR/busybox \$DATA_DIR/bin/xz
|
||||
ln -sf \$DATA_DIR/busybox \$DATA_DIR/bin/gzip
|
||||
\$DATA_DIR/bin/tar zxf patch.tar.gz
|
||||
\$DATA_DIR/busybox rm -rf assets.zip patch.tar.gz
|
||||
""");
|
||||
}
|
||||
|
||||
//初次启动要做的事情
|
||||
static Future<void> initForFirstTime() async {
|
||||
//首先设置bootstrap
|
||||
G.updateText.value = "正在安装引导包";
|
||||
await setupBootstrap();
|
||||
|
||||
G.updateText.value = "正在复制容器系统";
|
||||
//存放容器的文件夹0和存放硬链接的文件夹.l2s
|
||||
Util.createDirFromString("${G.dataPath}/containers/0/.l2s");
|
||||
//这个是容器rootfs,被split命令分成了xa*
|
||||
@@ -572,6 +595,7 @@ ln -s \$DATA_DIR/busybox \$DATA_DIR/bin/xz
|
||||
await Util.copyAsset("assets/$name", "${G.dataPath}/$name");
|
||||
}
|
||||
//-J
|
||||
G.updateText.value = "正在安装容器系统";
|
||||
await Util.execute(
|
||||
"""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
@@ -604,11 +628,12 @@ done
|
||||
//$DATA_DIR是数据文件夹, $CONTAINER_DIR是容器根目录
|
||||
await G.prefs.setStringList("containersInfo", ["""{
|
||||
"name":"Debian Bookworm",
|
||||
"boot":"\$DATA_DIR/bin/proot -H --change-id=1000:1000 --pwd=/home/tiny --rootfs=\$CONTAINER_DIR --mount=/system --mount=/apex --kill-on-exit --mount=/storage:/storage --sysvipc -L --link2symlink --mount=/proc:/proc --mount=/dev:/dev --mount=\$CONTAINER_DIR/tmp:/dev/shm --mount=/dev/urandom:/dev/random --mount=/proc/self/fd:/dev/fd --mount=/proc/self/fd/0:/dev/stdin --mount=/proc/self/fd/1:/dev/stdout --mount=/proc/self/fd/2:/dev/stderr --mount=/dev/null:/dev/tty0 --mount=/dev/null:/proc/sys/kernel/cap_last_cap --mount=/storage/self/primary:/media/sd --mount=\$DATA_DIR/share:/home/tiny/公共 --mount=/storage/self/primary/Fonts:/usr/share/fonts/wpsm --mount=/storage/self/primary/AppFiles/Fonts:/usr/share/fonts/yozom --mount=/system/fonts:/usr/share/fonts/androidm --mount=/storage/self/primary/Pictures:/home/tiny/图片 --mount=/storage/self/primary/Music:/home/tiny/音乐 --mount=/storage/self/primary/Movies:/home/tiny/视频 --mount=/storage/self/primary/Download:/home/tiny/下载 --mount=/storage/self/primary/DCIM:/home/tiny/照片 --mount=/storage/self/primary/Documents:/home/tiny/文档 --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/.tmoe-container.stat:/proc/stat --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/.tmoe-container.version:/proc/version --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/bus:/proc/bus --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/buddyinfo:/proc/buddyinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/cgroups:/proc/cgroups --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/consoles:/proc/consoles --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/crypto:/proc/crypto --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/devices:/proc/devices --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/diskstats:/proc/diskstats --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/execdomains:/proc/execdomains --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/fb:/proc/fb --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/filesystems:/proc/filesystems --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/interrupts:/proc/interrupts --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/iomem:/proc/iomem --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/ioports:/proc/ioports --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/kallsyms:/proc/kallsyms --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/keys:/proc/keys --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/key-users:/proc/key-users --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/kpageflags:/proc/kpageflags --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/loadavg:/proc/loadavg --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/locks:/proc/locks --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/misc:/proc/misc --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/modules:/proc/modules --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/pagetypeinfo:/proc/pagetypeinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/partitions:/proc/partitions --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/sched_debug:/proc/sched_debug --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/softirqs:/proc/softirqs --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/timer_list:/proc/timer_list --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/uptime:/proc/uptime --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/vmallocinfo:/proc/vmallocinfo --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/vmstat:/proc/vmstat --mount=\$CONTAINER_DIR/usr/local/etc/tmoe-linux/proot_proc/zoneinfo:/proc/zoneinfo /usr/bin/env -i HOSTNAME=TINY HOME=/home/tiny USER=tiny TERM=xterm-256color SDL_IM_MODULE=fcitx XMODIFIERS=@im=fcitx QT_IM_MODULE=fcitx GTK_IM_MODULE=fcitx TMOE_CHROOT=false TMOE_PROOT=true TMPDIR=/tmp MOZ_FAKE_NO_SANDBOX=1 DISPLAY=:4 PULSE_SERVER=tcp:127.0.0.1:4718 LANG=zh_CN.UTF-8 SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/games:/usr/local/games \$EXTRA_OPT /bin/bash -l",
|
||||
"boot":"${D.boot}",
|
||||
"vnc":"startnovnc &",
|
||||
"vncUrl":"http://localhost:36082/vnc.html?host=localhost&port=36082&autoconnect=true&resize=remote&password=12345678",
|
||||
"commands":${jsonEncode(D.commands)}
|
||||
}"""]);
|
||||
G.updateText.value = "安装完成";
|
||||
}
|
||||
|
||||
static Future<void> initData() async {
|
||||
@@ -634,6 +659,13 @@ done
|
||||
}
|
||||
G.currentContainer = Util.getGlobal("defaultContainer") as int;
|
||||
|
||||
//是否需要重新安装引导包?
|
||||
if (Util.getGlobal("reinstallBootstrap")) {
|
||||
G.updateText.value = "正在重新安装引导包";
|
||||
await setupBootstrap();
|
||||
G.prefs.setBool("reinstallBootstrap", false);
|
||||
}
|
||||
|
||||
G.termFontScale.value = Util.getGlobal("termFontScale") as double;
|
||||
|
||||
G.controller = WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted);
|
||||
@@ -684,23 +716,24 @@ done
|
||||
//pulseaudio还需要一个文件夹放配置,这里用share
|
||||
G.audioPty!.write(const Utf8Encoder().convert("""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
cd \$DATA_DIR/..
|
||||
export TMPDIR=\$PWD/cache
|
||||
cd \$DATA_DIR
|
||||
export HOME=\$DATA_DIR/share
|
||||
export LD_LIBRARY_PATH=\$DATA_DIR/bin
|
||||
\$DATA_DIR/busybox sed "s/4713/${Util.getGlobal("defaultAudioPort") as int}/g" \$DATA_DIR/bin/pulseaudio.conf > \$DATA_DIR/bin/pulseaudio.conf.tmp
|
||||
\$DATA_DIR/bin/pulseaudio -F \$DATA_DIR/bin/pulseaudio.conf.tmp
|
||||
TMPDIR=\$DATA_DIR/pulseaudio_tmp HOME=\$DATA_DIR/pulseaudio_tmp XDG_CONFIG_HOME=\$DATA_DIR/pulseaudio_tmp LD_LIBRARY_PATH=\$DATA_DIR/bin \$DATA_DIR/bin/pulseaudio -F \$DATA_DIR/bin/pulseaudio.conf.tmp
|
||||
exit
|
||||
"""));
|
||||
await G.audioPty?.exitCode;
|
||||
}
|
||||
|
||||
static Future<void> launchCurrentContainer() async {
|
||||
String extraOpt = "";
|
||||
if (Util.getGlobal("getifaddrsBridge")) {
|
||||
Util.execute("${G.dataPath}/bin/getifaddrs_bridge_server ${G.dataPath}/containers/${G.currentContainer}/tmp/.getifaddrs-bridge");
|
||||
extraOpt += "LD_PRELOAD=/home/tiny/.local/share/tiny/extra/getifaddrs_bridge_client_lib.so ";
|
||||
}
|
||||
Util.termWrite(
|
||||
"""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
export CONTAINER_DIR=\$DATA_DIR/containers/${G.currentContainer}
|
||||
export EXTRA_OPT="$extraOpt"
|
||||
#export PROOT_L2S_DIR=\$DATA_DIR/containers/0/.l2s
|
||||
cd \$DATA_DIR
|
||||
export PROOT_TMP_DIR=\$DATA_DIR/proot_tmp
|
||||
|
||||
Reference in New Issue
Block a user