Fix QQ, remove static elf, downgrade hangover, add dxvk, update dependencies, update code to 1.0.23

This commit is contained in:
Caten
2025-06-12 18:15:30 +08:00
parent 009cd4ebe2
commit 6b67ddaf9a
13 changed files with 196 additions and 123 deletions

View File

@@ -485,7 +485,7 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
Text(AppLocalizations.of(context)!.hangoverDescription),
const SizedBox.square(dimension: 8),
Wrap(alignment: WrapAlignment.center, spacing: 4.0, runSpacing: 4.0, children: [
OutlinedButton(style: D.commandButtonStyle, child: Text("${AppLocalizations.of(context)!.installHangoverStable}10.9"), onPressed: () async {
OutlinedButton(style: D.commandButtonStyle, child: Text("${AppLocalizations.of(context)!.installHangoverStable}10.6.1"), onPressed: () async {
Util.termWrite("bash ~/.local/share/tiny/extra/install-hangover-stable");
G.pageIndex.value = 0;
}),
@@ -494,7 +494,7 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
G.pageIndex.value = 0;
}),
OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.uninstallHangover), onPressed: () async {
Util.termWrite("sudo apt autoremove --purge -y hangover-wine hangover-libarm64ecfex");
Util.termWrite("sudo apt autoremove --purge -y hangover*");
G.pageIndex.value = 0;
}),
OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.clearWineData), onPressed: () async {
@@ -1196,100 +1196,122 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
bool bannerAdsFailedToLoad = false;
//安装完成了吗?
//完成后从加载界面切换到主界面
bool isLoadingComplete = false;
@override
Widget build(BuildContext context) {
void initState() {
super.initState();
_initializeWorkflow();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: []);
}
G.homePageStateContext = context;
if (!isLoadingComplete) {
Workflow.workflow().then((value) {
setState(() {
isLoadingComplete = true;
});
Future<void> _initializeWorkflow() async {
await Workflow.workflow();
if (mounted) {
setState(() {
isLoadingComplete = true;
});
}
}
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,overlays: []);
@override
Widget build(BuildContext context) {
G.homePageStateContext = context;
return Scaffold(
appBar: AppBar(
title: Text(isLoadingComplete?Util.getCurrentProp("name"):widget.title),
title: Text(isLoadingComplete ? Util.getCurrentProp("name") : widget.title),
),
body: isLoadingComplete?
ValueListenableBuilder(valueListenable: G.pageIndex, builder: (context, value, child) {
return IndexedStack(index: G.pageIndex.value, children: const [TerminalPage(), Padding(
padding: EdgeInsets.all(8),
child: AspectRatioMax1To1(child: Scrollbar(child: SingleChildScrollView(restorationId: "control-scroll", child: Column(
children: [
Padding(
padding: EdgeInsets.all(16),
child: FractionallySizedBox(
widthFactor: 0.4,
child: Image(
image: AssetImage("images/icon.png")
)
body: isLoadingComplete
? ValueListenableBuilder(
valueListenable: G.pageIndex,
builder: (context, value, child) {
return IndexedStack(
index: G.pageIndex.value,
children: const [
TerminalPage(),
Padding(
padding: EdgeInsets.all(8),
child: AspectRatioMax1To1(
child: Scrollbar(
child: SingleChildScrollView(
restorationId: "control-scroll",
child: Column(
children: [
Padding(
padding: EdgeInsets.all(16),
child: FractionallySizedBox(
widthFactor: 0.4,
child: Image(image: AssetImage("images/icon.png")),
),
),
FastCommands(),
Padding(
padding: EdgeInsets.all(8),
child: Card(
child: Padding(
padding: EdgeInsets.all(8),
child: Column(
children: [
SettingPage(),
SizedBox.square(dimension: 8),
InfoPage(openFirstInfo: false),
],
),
),
),
),
],
),
),
),
),
),
),
FastCommands(),
Padding(padding: EdgeInsets.all(8), child: Card(child: Padding(padding: EdgeInsets.all(8), child:
Column(children: [
SettingPage(),
SizedBox.square(dimension: 8),
InfoPage(openFirstInfo: false)
])
)))
]
))))
)]);
}):const LoadingPage(),
bottomNavigationBar: ValueListenableBuilder(valueListenable: G.pageIndex, builder:(context, value, child) {
return Visibility(visible: isLoadingComplete,
// child: BottomNavigationBar(currentIndex: G.pageIndex.value,
// onTap: (index) {
// G.pageIndex.value = index;
// },
// items: const [
// BottomNavigationBarItem(icon: Icon(Icons.monitor), label: "终端"),
// BottomNavigationBarItem(icon: Icon(Icons.video_settings), label: "控制"),
// ],
// )
child: NavigationBar(
selectedIndex: G.pageIndex.value,
destinations: [
NavigationDestination(icon: Icon(Icons.monitor), label: AppLocalizations.of(context)!.terminal),
NavigationDestination(icon: Icon(Icons.video_settings), label: AppLocalizations.of(context)!.control)
],
onDestinationSelected: (index) {
G.pageIndex.value = index;
},
),
);}
],
);
},
)
: const LoadingPage(),
bottomNavigationBar: ValueListenableBuilder(
valueListenable: G.pageIndex,
builder: (context, value, child) {
return Visibility(
visible: isLoadingComplete,
child: NavigationBar(
selectedIndex: G.pageIndex.value,
destinations: [
NavigationDestination(icon: const Icon(Icons.monitor), label: AppLocalizations.of(context)!.terminal),
NavigationDestination(icon: const Icon(Icons.video_settings), label: AppLocalizations.of(context)!.control),
],
onDestinationSelected: (index) {
G.pageIndex.value = index;
},
),
);
},
),
floatingActionButton: ValueListenableBuilder(
valueListenable: G.pageIndex,
builder: (context, value, child) {
return Visibility(
visible: isLoadingComplete && (value == 0),
child: FloatingActionButton(
tooltip: AppLocalizations.of(context)!.enterGUI,
onPressed: () {
if (G.wasX11Enabled) {
Workflow.launchX11();
} else if (G.wasAvncEnabled) {
Workflow.launchAvnc();
} else {
Workflow.launchBrowser();
}
},
child: const Icon(Icons.play_arrow),
),
);
},
),
floatingActionButton: ValueListenableBuilder(valueListenable: G.pageIndex, builder:(context, value, child) {
return Visibility(visible: isLoadingComplete && (value == 0),
child: FloatingActionButton(
tooltip: AppLocalizations.of(context)!.enterGUI,
onPressed: () {
if (G.wasX11Enabled) {
Workflow.launchX11();
} else if (G.wasAvncEnabled) {
Workflow.launchAvnc();
} else {
Workflow.launchBrowser();
}
},
child: const Icon(Icons.play_arrow),
)
);
}), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

View File

@@ -121,7 +121,7 @@ class Util {
case "useAvnc" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true);
case "useX11" : 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 "defaultVirglCommand" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("--socket-path=\$CONTAINER_DIR/tmp/.virgl_test");
case "defaultVirglCommand" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("--use-egl-surfaceless --use-gles --socket-path=\$CONTAINER_DIR/tmp/.virgl_test");
case "defaultVirglOpt" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("GALLIUM_DRIVER=virpipe");
case "defaultTurnipOpt" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("MESA_LOADER_DRIVER_OVERRIDE=zink VK_ICD_FILENAMES=/home/tiny/.local/share/tiny/extra/freedreno_icd.aarch64.json TU_DEBUG=noconform");
case "defaultHidpiOpt" : return b ? G.prefs.getString(key)! : (value){G.prefs.setString(key, value); return value;}("GDK_SCALE=2 QT_FONT_DPI=192");
@@ -191,6 +191,11 @@ class Util {
return null;
}
//获取预制可执行文件路径
static String elf(String value) {
return "applib/libexec_$value.so";
}
}
//来自xterms关于操作ctrl, shift, alt键的示例
@@ -404,7 +409,7 @@ rm /tmp/wps.deb"""},
{"name":"卸载CAJViewer", "command":"sudo apt autoremove --purge -y net.cnki.cajviewer && bash /home/tiny/.local/share/tiny/caj/postrm"},
{"name":"安装亿图图示", "command":"wget https://cc-download.wondershare.cc/business/prd/edrawmax_13.1.0-1_arm64_binner.deb -O /tmp/edraw.deb && sudo apt update && sudo apt install -y /tmp/edraw.deb && bash /home/tiny/.local/share/tiny/edraw/postinst; rm /tmp/edraw.deb"},
{"name":"卸载亿图图示", "command":"sudo apt autoremove --purge -y edrawmax libldap-2.4-2"},
{"name":"安装QQ", "command":"""wget \$(curl -s https://im.qq.com/rainbow/linuxQQDownload | grep -o 'https://[^"]+arm64[^"]+deb') -O /tmp/qq.deb && sudo apt update && sudo apt install -y /tmp/qq.deb && sed -i 's#Exec=/opt/QQ/qq %U#Exec=/opt/QQ/qq --no-sandbox %U#g' /usr/share/applications/qq.desktop; rm /tmp/qq.deb"""},
{"name":"安装QQ", "command":"""wget \$(curl -s https://im.qq.com/rainbow/linuxQQDownload | grep -oP '"armDownloadUrl":{[^}]*"deb":"\\K[^"]+') -O /tmp/qq.deb && sudo apt update && sudo apt install -y /tmp/qq.deb && sed -i 's#Exec=/opt/QQ/qq %U#Exec=/opt/QQ/qq --no-sandbox %U#g' /usr/share/applications/qq.desktop; rm /tmp/qq.deb"""},
{"name":"卸载QQ", "command":"sudo apt autoremove --purge -y linuxqq"},
{"name":"安装微信", "command":"wget https://dldir1v6.qq.com/weixin/Universal/Linux/WeChatLinux_arm64.deb -O /tmp/wechat.deb && sudo apt update && sudo apt install -y /tmp/wechat.deb && echo '安装完成。如果你使用微信只是为了传输文件那么可以考虑使用支持SAF的文件管理器质感文件直接访问小小电脑所有文件。'; rm /tmp/wechat.deb"},
{"name":"卸载微信", "command":"sudo apt autoremove --purge -y wechat"},
@@ -571,6 +576,10 @@ class Workflow {
static Future<void> setupBootstrap() async {
//用来共享数据文件的文件夹
Util.createDirFromString("${G.dataPath}/share");
//用来存放可执行文件的文件夹
Util.createDirFromString("${G.dataPath}/bin");
//用来存放库的文件夹
Util.createDirFromString("${G.dataPath}/lib");
//挂载到/dev/shm的文件夹
Util.createDirFromString("${G.dataPath}/tmp");
//给proot的tmp文件夹虽然我不知道为什么proot要这个
@@ -590,24 +599,31 @@ class Workflow {
"assets/patch.tar.gz",
"${G.dataPath}/patch.tar.gz",
);
//dddd
await Util.copyAsset(
"assets/busybox",
"${G.dataPath}/busybox",
);
await Util.execute(
"""
export DATA_DIR=${G.dataPath}
export LD_LIBRARY_PATH=\$DATA_DIR/lib
cd \$DATA_DIR
chmod +x busybox
\$DATA_DIR/busybox unzip -o assets.zip
ln -sf ../applib/libexec_busybox.so \$DATA_DIR/bin/busybox
ln -sf ../applib/libexec_busybox.so \$DATA_DIR/bin/xz
ln -sf ../applib/libexec_busybox.so \$DATA_DIR/bin/gzip
ln -sf ../applib/libexec_proot.so \$DATA_DIR/bin/proot
ln -sf ../applib/libexec_tar.so \$DATA_DIR/bin/tar
ln -sf ../applib/libexec_virgl_test_server.so \$DATA_DIR/bin/virgl_test_server
ln -sf ../applib/libexec_getifaddrs_bridge_server.so \$DATA_DIR/bin/getifaddrs_bridge_server
ln -sf ../applib/libbusybox.so \$DATA_DIR/lib/libbusybox.so.1.36.1
ln -sf ../applib/libtalloc.so \$DATA_DIR/lib/libtalloc.so.2
ln -sf ../applib/libvirglrenderer.so \$DATA_DIR/lib/libvirglrenderer.so
ln -sf ../applib/libepoxy.so \$DATA_DIR/lib/libepoxy.so
ln -sf ../applib/libproot-loader32.so \$DATA_DIR/lib/loader32
ln -sf ../applib/libproot-loader.so \$DATA_DIR/lib/loader
\$DATA_DIR/bin/busybox unzip -o assets.zip
chmod -R +x bin/*
chmod -R +x libexec/proot/*
chmod 1777 tmp
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
\$DATA_DIR/bin/busybox rm -rf assets.zip patch.tar.gz
""");
}
@@ -630,13 +646,14 @@ ln -sf \$DATA_DIR/busybox \$DATA_DIR/bin/gzip
await Util.execute(
"""
export DATA_DIR=${G.dataPath}
export LD_LIBRARY_PATH=\$DATA_DIR/lib
export CONTAINER_DIR=\$DATA_DIR/containers/0
export EXTRA_OPT=""
cd \$DATA_DIR
export PATH=\$DATA_DIR/bin:\$PATH
export PROOT_TMP_DIR=\$DATA_DIR/proot_tmp
export PROOT_LOADER=\$DATA_DIR/libexec/proot/loader
export PROOT_LOADER_32=\$DATA_DIR/libexec/proot/loader32
export PROOT_LOADER=\$DATA_DIR/applib/libproot-loader.so
export PROOT_LOADER_32=\$DATA_DIR/applib/libproot-loader32.so
#export PROOT_L2S_DIR=\$CONTAINER_DIR/.l2s
\$DATA_DIR/bin/proot --link2symlink sh -c "cat xa* | \$DATA_DIR/bin/tar x -J --delay-directory-restore --preserve-permissions -v -C containers/0"
#Script from proot-distro
@@ -645,7 +662,7 @@ echo "aid_\$(id -un):x:\$(id -u):\$(id -g):Termux:/:/sbin/nologin" >> "\$CONTAIN
echo "aid_\$(id -un):*:18446:0:99999:7:::" >> "\$CONTAINER_DIR/etc/shadow"
id -Gn | tr ' ' '\\n' > tmp1
id -G | tr ' ' '\\n' > tmp2
\$DATA_DIR/busybox paste tmp1 tmp2 > tmp3
\$DATA_DIR/bin/busybox paste tmp1 tmp2 > tmp3
local group_name group_id
cat tmp3 | while read -r group_name group_id; do
echo "aid_\${group_name}:x:\${group_id}:root,aid_\$(id -un)" >> "\$CONTAINER_DIR/etc/group"
@@ -653,7 +670,7 @@ cat tmp3 | while read -r group_name group_id; do
echo "aid_\${group_name}:*::root,aid_\$(id -un)" >> "\$CONTAINER_DIR/etc/gshadow"
fi
done
\$DATA_DIR/busybox rm -rf xa* tmp1 tmp2 tmp3
\$DATA_DIR/bin/busybox rm -rf xa* tmp1 tmp2 tmp3
""");
//一些数据初始化
//$DATA_DIR是数据文件夹, $CONTAINER_DIR是容器根目录
@@ -678,12 +695,7 @@ done
G.prefs = await SharedPreferences.getInstance();
//限制一天内观看视频广告不超过5次
final String currentDate = DateFormat("yyyy-MM-dd").format(DateTime.now());
if (currentDate != (Util.getGlobal("lastDate") as String)) {
await G.prefs.setString("lastDate", currentDate);
//await G.prefs.setInt("adsWatchedToday", 0);
}
await Util.execute("ln -sf ${await D.androidChannel.invokeMethod("getNativeLibraryPath", {})} ${G.dataPath}/applib");
//如果没有这个key说明是初次启动
if (!G.prefs.containsKey("defaultContainer")) {
@@ -734,9 +746,10 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""";
);
G.audioPty!.write(const Utf8Encoder().convert("""
export DATA_DIR=${G.dataPath}
\$DATA_DIR/busybox sed "s/4713/${Util.getGlobal("defaultAudioPort") as int}/g" \$DATA_DIR/bin/pulseaudio.conf > \$DATA_DIR/bin/pulseaudio.conf.tmp
export LD_LIBRARY_PATH=\$DATA_DIR/lib
\$DATA_DIR/bin/busybox sed "s/4713/${Util.getGlobal("defaultAudioPort") as int}/g" \$DATA_DIR/bin/pulseaudio.conf > \$DATA_DIR/bin/pulseaudio.conf.tmp
rm -rf \$DATA_DIR/pulseaudio_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
TMPDIR=\$DATA_DIR/pulseaudio_tmp HOME=\$DATA_DIR/pulseaudio_tmp XDG_CONFIG_HOME=\$DATA_DIR/pulseaudio_tmp LD_LIBRARY_PATH=\$DATA_DIR/bin:\$LD_LIBRARY_PATH /system/bin/linker64 \$DATA_DIR/bin/pulseaudio -F \$DATA_DIR/bin/pulseaudio.conf.tmp
exit
"""));
await G.audioPty?.exitCode;
@@ -759,6 +772,7 @@ exit
if (Util.getGlobal("virgl")) {
Util.execute("""
export DATA_DIR=${G.dataPath}
export LD_LIBRARY_PATH=\$DATA_DIR/lib
export CONTAINER_DIR=\$DATA_DIR/containers/${G.currentContainer}
${G.dataPath}/bin/virgl_test_server ${Util.getGlobal("defaultVirglCommand")}""");
extraOpt += "${Util.getGlobal("defaultVirglOpt")} ";
@@ -777,14 +791,15 @@ ${G.dataPath}/bin/virgl_test_server ${Util.getGlobal("defaultVirglCommand")}""")
Util.termWrite(
"""
export DATA_DIR=${G.dataPath}
export LD_LIBRARY_PATH=\$DATA_DIR/lib
export CONTAINER_DIR=\$DATA_DIR/containers/${G.currentContainer}
export EXTRA_MOUNT="$extraMount"
export EXTRA_OPT="$extraOpt"
#export PROOT_L2S_DIR=\$DATA_DIR/containers/0/.l2s
cd \$DATA_DIR
export PROOT_TMP_DIR=\$DATA_DIR/proot_tmp
export PROOT_LOADER=\$DATA_DIR/libexec/proot/loader
export PROOT_LOADER_32=\$DATA_DIR/libexec/proot/loader32
export PROOT_LOADER=\$DATA_DIR/applib/libproot-loader.so
export PROOT_LOADER_32=\$DATA_DIR/applib/libproot-loader32.so
${Util.getCurrentProp("boot")}
${G.postCommand}
${(Util.getGlobal("autoLaunchVnc") as bool)?((Util.getGlobal("useX11") as bool)?"""mkdir -p "\$HOME/.vnc" && bash /etc/X11/xinit/Xsession &> "\$HOME/.vnc/x.log" &""":Util.getCurrentProp("vnc")):""}