mirror of
https://github.com/Cateners/tiny_computer.git
synced 2026-05-20 16:35:47 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
553e5862ca | ||
|
|
125791e44c | ||
|
|
587e93ca31 | ||
|
|
b2d45c95ac | ||
|
|
b6b9ac61c7 | ||
|
|
45da44d078 | ||
|
|
77fec49a75 | ||
|
|
8cde9b878a | ||
|
|
b15fe80e83 | ||
|
|
ae88dea9c5 | ||
|
|
6425e0443e | ||
|
|
010cf544ea | ||
|
|
a4a2898214 | ||
|
|
b2b642e7c0 | ||
|
|
23968eb1fc | ||
|
|
2aac0e57d7 | ||
|
|
ed1c4aa9b1 | ||
|
|
f2eb3e0491 | ||
|
|
d17e515981 | ||
|
|
10f481f976 | ||
|
|
c10d2b733f | ||
|
|
417cf7feef | ||
|
|
7b219facfe | ||
|
|
ed3ec63212 | ||
|
|
8f26ed77e7 | ||
|
|
db4431d4c7 | ||
|
|
0793f589f2 |
26
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
26
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -61,31 +61,17 @@ body:
|
||||
- type: textarea
|
||||
id: why-not-latest
|
||||
attributes:
|
||||
label: "若非最新版,必须说明原因"
|
||||
label: "若非最新版,必须说明不使用最新版的原因"
|
||||
placeholder: "详细解释原因..."
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: "出现问题的重现步骤"
|
||||
label: "重现问题的操作过程"
|
||||
description: |
|
||||
**为什么需要详细步骤?**
|
||||
Linux的自由度极高,任何操作都可能影响结果,例如:
|
||||
- 是否通过sudo运行?
|
||||
- 是否修改过环境变量?
|
||||
- 是否安装过第三方依赖?
|
||||
placeholder: "也许可以从启动软件开始逐步描述..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: "提供终端截图或其他相关信息"
|
||||
description: |
|
||||
**为什么需要这些?**
|
||||
文字描述可能遗漏关键信息
|
||||
placeholder: "粘贴日志文本或拖入图片..."
|
||||
**请注意:**
|
||||
请提供从启动软件开始到问题发生的**完整屏幕录制视频**,而不要使用文字描述或截图,因为根据以往的issue来看,用户的描述会遗漏可能的细节。
|
||||
由于Linux环境的复杂性,任何操作细节(如权限、环境配置等)都可能影响问题的重现。确保你的问题在新安装的小小电脑上可以复现。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -93,4 +79,4 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
**问题优先级说明:**
|
||||
本软件的初衷是安装PC级软件如WPS、CAJ Viewer、亿图图示。和这些软件相关的问题会得到重视。如果是其他问题得视情况(受限于精力和能力...),没能修复的issue会保留以让更多人看到,也许网友会有更好的解决办法!
|
||||
本软件的初衷是安装PC级软件如WPS、CAJ Viewer、亿图图示。小小电脑本身的问题,以及和这些软件相关的问题会得到重视。如果是其他问题,尤其是第三方软件或Linux本身的使用方法等,请在[discussion](https://github.com/Cateners/tiny_computer/discussions)区发布讨论
|
||||
24
.github/ISSUE_TEMPLATE/bug_report_eng.yml
vendored
24
.github/ISSUE_TEMPLATE/bug_report_eng.yml
vendored
@@ -60,7 +60,7 @@ body:
|
||||
- type: textarea
|
||||
id: why-not-latest
|
||||
attributes:
|
||||
label: "If not latest version, explanation is mandatory"
|
||||
label: "If not using the latest version, you must explain the reason for not using the latest version"
|
||||
placeholder: "Explain in detail..."
|
||||
|
||||
- type: textarea
|
||||
@@ -68,23 +68,9 @@ body:
|
||||
attributes:
|
||||
label: "Steps to reproduce the issue"
|
||||
description: |
|
||||
**Why detailed steps?**
|
||||
Linux offers extreme flexibility where any operation may affect results, such as:
|
||||
- Did you run with sudo?
|
||||
- Modified environment variables?
|
||||
- Installed third-party dependencies?
|
||||
placeholder: "Maybe start from launching the application..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: "Provide terminal screenshots or other relevant information"
|
||||
description: |
|
||||
**Why is this needed?**
|
||||
Text descriptions might omit critical details
|
||||
placeholder: "Paste log text or drag images here..."
|
||||
**Please note:**
|
||||
Provide a **complete screen recording video** starting from launching the software until the issue occurs, rather than using text descriptions or screenshots. Based on past issues, user descriptions often omit potential details.
|
||||
Due to the complexity of the Linux environment, any operational details (such as permissions, environment configurations, etc.) may affect issue reproduction. Ensure your issue can be reproduced on a freshly installed Tiny Computer.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -92,4 +78,4 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
**Issue Priority Explanation:**
|
||||
This software's primary purpose is running PC-level applications like WPS, CAJ Viewer, and Edraw Max. Issues related to these applications will receive priority. Other issues will be handled case-by-case (limited by time and capabilities...). Unresolved issues will remain open for community visibility - maybe someone has a better solution!
|
||||
The original purpose of this software is to install PC-level applications such as WPS, CAJ Viewer, and Edraw Max. Issues related to the Tiny Computer itself and problems associated with these software will be prioritized. For other issues, especially those involving third-party software or general Linux usage, please post in the [discussion](https://github.com/Cateners/tiny_computer/discussions) section.
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -14,7 +14,8 @@ body:
|
||||
attributes:
|
||||
label: "必须说明使用场景和价值"
|
||||
description: |
|
||||
**为什么需要这个?(附用户调研数据或统计截图更佳,避免主观描述)**
|
||||
**为什么需要这个?实现这个会对其他用户带来什么好处?开发者会花费许多时间在上面,这对开发者有什么好处?**
|
||||
(简单来说,就是不要许愿,用充分的理由来说服我或其他潜在(?)的开发者吧!如果你想要一个功能,要么有时间自己学自己开发,要么用钱雇人做,要么等待其他和你想法一致且有时间或有钱的人来做)
|
||||
因为精力有限,我会更高可能采纳有价值且易实现的功能。没能处理的issue会保留以让更多人看到,也许网友会有更好的解决办法!
|
||||
validations:
|
||||
required: true
|
||||
@@ -14,7 +14,8 @@ body:
|
||||
attributes:
|
||||
label: "Must specify usage scenarios and value"
|
||||
description: |
|
||||
**Why is this needed? (User research data or screenshot statistics are preferred. Avoid subjective descriptions.)**
|
||||
**Why is this needed? What benefits will implementing this bring to other users? Developers will spend a lot of time on this—what’s in it for them?**
|
||||
(In short, don’t just make a wish—convince me or other potential (?) developers with solid reasoning! If you want a feature, either take the time to learn and develop it yourself, pay someone to do it, or wait for someone who shares your idea and has the time or money to implement it.)
|
||||
Due to limited resources, I'm more likely to adopt valuable and easily implementable features. Unaddressed issues will remain visible for community solutions!
|
||||
validations:
|
||||
required: true
|
||||
24
README.md
24
README.md
@@ -4,34 +4,36 @@
|
||||
|
||||
给所有安卓 9 以上 arm64 设备的“PC 应用引擎”平替。你可以在小小电脑上安装 PC 级 WPS、CAJ Viewer、亿图图示等软件。
|
||||
|
||||
Run Debian Bookworm with XFCE/LXQt/... on Android with just one click - optimized for Chinese users. This package comes preinstalled with the Fcitx Pinyin input method and doesn't require Termux.
|
||||
|
||||
To change the language in the container, simply run the "tmoe" command (this root filesystem was created using [tmoe](https://github.com/2moe/tmoe)). You'll need to remove the LANG=zh_CN.UTF-8 environment variable from the startup command (Control -> Advanced Settings -> Startup Command) when switching languages.
|
||||
Run Debian Trixie with XFCE, LXQt, or other desktop environments on Android—just with one click. Originally developed for Chinese users to run applications like WPS Office, it comes preinstalled with tools such as the Fcitx Pinyin input method. Please note that this app does not require Termux.
|
||||
|
||||
Note: Since version 1.0.23, English UI is supported, though some hint texts remain in Chinese.
|
||||
To change the language inside the container, simply run the `tmoe` command, select “Manager” and navigate to the locale settings. The root filesystem was built using [tmoe](https://github.com/2moe/tmoe), so locale configuration is handled through it. You will also need to update the `LANG=zh_CN.UTF-8` environment variable in the startup command (go to Control → Advanced Settings → Startup Command) when switching to another language.
|
||||
|
||||
Note: English UI is supported since version 1.0.23, though some hint texts may still appear in Chinese.
|
||||
As of version 1.0.100, the container will automatically switch to English if it detects that your device is not using Chinese.
|
||||
|
||||
## 特点
|
||||
|
||||
- 一键安装,即开即用
|
||||
- 来自 kali-undercover 的 win10 主题(仅 xfce 版本),友好的界面
|
||||
|
||||
[](https://github.com/Cateners/tiny_computer/blob/master/readme/img1.png)
|
||||

|
||||
|
||||
- 提供常用软件的一键安装指令
|
||||
- 提供常用软件的一键安装指令(点击图片可查看更多说明)
|
||||
|
||||
[](https://github.com/Cateners/tiny_computer/blob/master/readme/img2.png)
|
||||
[](https://gitee.com/caten/tc-hints/blob/master/pool/solution.md)
|
||||
|
||||
- 可方便地改变屏幕缩放,不用担心屏幕过大或过小(仅 novnc)
|
||||
- 可方便地改变屏幕缩放,不用担心屏幕过大或过小 (点击图片可查看更多说明)
|
||||
|
||||
[](https://github.com/Cateners/tiny_computer/blob/master/readme/img3.gif)
|
||||
[](https://gitee.com/caten/tc-hints/blob/master/pool/scale.md)
|
||||
|
||||
- 便捷访问设备文件,或通过设备 SAF 访问软件文件
|
||||
- 便捷访问设备文件,或通过设备 SAF 访问软件文件(点击图片可查看更多说明)
|
||||
|
||||
[](https://github.com/Cateners/tiny_computer/blob/master/readme/img4.png)
|
||||
[](https://gitee.com/caten/tc-hints/blob/master/pool/fileaccess.md)
|
||||
|
||||
- 提供终端和众多可调节参数供高级用户使用
|
||||
|
||||
[](https://github.com/Cateners/tiny_computer/blob/master/readme/img5.png)
|
||||

|
||||
|
||||
## 下载
|
||||
|
||||
|
||||
@@ -59,6 +59,13 @@ android {
|
||||
|
||||
buildConfigField "String", "COMMIT", "\"" + ("git rev-parse HEAD\n".execute().getText().trim() ?: (System.getenv('CURRENT_COMMIT') ?: "NO_COMMIT")) + "\""
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags "-std=c++11"
|
||||
arguments "-DANDROID_STL=c++_shared"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
@@ -100,6 +107,12 @@ android {
|
||||
//checkReleaseBuilds false
|
||||
abortOnError false
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.22.1"
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst 'lib/arm64-v8a/libc++_shared.so'
|
||||
|
||||
3
android/app/proguard-rules.pro
vendored
3
android/app/proguard-rules.pro
vendored
@@ -12,3 +12,6 @@
|
||||
# 保持 Termux X11 所有内容
|
||||
-keep class com.termux.x11.** { *; }
|
||||
-keepclassmembers class com.termux.x11.** { *; }
|
||||
|
||||
|
||||
-dontwarn javax.annotation.Nullable
|
||||
@@ -4,7 +4,7 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application
|
||||
|
||||
11
android/app/src/main/cpp/CMakeLists.txt
Normal file
11
android/app/src/main/cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.18.1)
|
||||
project("native-socket")
|
||||
|
||||
# 添加库
|
||||
add_library(native-socket SHARED native-socket.cpp)
|
||||
|
||||
# 链接日志库
|
||||
find_library(log-lib log)
|
||||
|
||||
# 指定目标属性
|
||||
target_link_libraries(native-socket ${log-lib})
|
||||
78
android/app/src/main/cpp/native-socket.cpp
Normal file
78
android/app/src/main/cpp/native-socket.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <android/log.h>
|
||||
#include <cerrno>
|
||||
|
||||
#define LOG_TAG "NativeAudio"
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
int server_fd = -1;
|
||||
int client_fd = -1;
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_example_tiny_1computer_AudioStream_nativeInit(JNIEnv *env, jobject thiz, jstring path) {
|
||||
const char *socket_path = env->GetStringUTFChars(path, 0);
|
||||
|
||||
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (server_fd == -1) {
|
||||
LOGE("Socket creation failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||
unlink(socket_path); // Remove existing file if any
|
||||
|
||||
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
LOGE("Bind failed: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 1) == -1) {
|
||||
LOGE("Listen failed");
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(path, socket_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_example_tiny_1computer_AudioStream_nativeAccept(JNIEnv *env, jobject thiz) {
|
||||
if (server_fd == -1) return -1;
|
||||
// Blocks here until Linux connects
|
||||
client_fd = accept(server_fd, NULL, NULL);
|
||||
if (client_fd == -1) {
|
||||
LOGE("Accept failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_example_tiny_1computer_AudioStream_nativeSend(JNIEnv *env, jobject thiz, jbyteArray data, jint size) {
|
||||
if (client_fd == -1) return -1;
|
||||
|
||||
jbyte *buffer = env->GetByteArrayElements(data, NULL);
|
||||
ssize_t sent = write(client_fd, buffer, size);
|
||||
env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
|
||||
|
||||
if (sent == -1 && errno != EAGAIN) {
|
||||
LOGE("Write failed (Broken Pipe?)");
|
||||
return -1;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_example_tiny_1computer_AudioStream_nativeClose(JNIEnv *env, jobject thiz) {
|
||||
if (client_fd != -1) { close(client_fd); client_fd = -1; }
|
||||
if (server_fd != -1) { close(server_fd); server_fd = -1; }
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class TinyDocumentsProvider extends DocumentsProvider {
|
||||
@Override
|
||||
public Cursor queryRoots(String[] projection) {
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
|
||||
final String applicationName = "小小电脑";
|
||||
final String applicationName = getContext().getString(R.string.tc_app_name);
|
||||
final File BASE_DIR = new File(getContext().getFilesDir(), "containers");
|
||||
final MatrixCursor.RowBuilder row = result.newRow();
|
||||
row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR));
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.example.tiny_computer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.media.AudioFormat
|
||||
import android.media.AudioRecord
|
||||
import android.media.MediaRecorder
|
||||
import android.util.Log
|
||||
|
||||
object AudioStream {
|
||||
init {
|
||||
System.loadLibrary("native-socket")
|
||||
}
|
||||
|
||||
private var isStreaming = false
|
||||
private var recordingThread: Thread? = null
|
||||
|
||||
// Native functions
|
||||
private external fun nativeInit(path: String): Int
|
||||
private external fun nativeAccept(): Int
|
||||
private external fun nativeSend(data: ByteArray, size: Int): Int
|
||||
private external fun nativeClose()
|
||||
|
||||
@SuppressLint("MissingPermission") // Ensure RECORD_AUDIO is granted in Manifest
|
||||
fun startStreaming(path: String) {
|
||||
if (isStreaming) return
|
||||
isStreaming = true
|
||||
|
||||
recordingThread = Thread {
|
||||
// 1. Initialize Socket Server
|
||||
if (nativeInit(path) < 0) {
|
||||
Log.e("AudioStream", "Failed to bind socket")
|
||||
isStreaming = false
|
||||
return@Thread
|
||||
}
|
||||
|
||||
// 2. Wait for Linux client to connect (Blocking)
|
||||
Log.d("AudioStream", "Waiting for connection on $path...")
|
||||
if (nativeAccept() < 0) {
|
||||
Log.e("AudioStream", "Accept failed")
|
||||
isStreaming = false
|
||||
return@Thread
|
||||
}
|
||||
Log.d("AudioStream", "Client connected!")
|
||||
|
||||
// 3. Setup AudioRecord
|
||||
val sampleRate = 44100
|
||||
val bufferSize = AudioRecord.getMinBufferSize(
|
||||
sampleRate,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT
|
||||
)
|
||||
|
||||
val recorder = AudioRecord(
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
sampleRate,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize
|
||||
)
|
||||
|
||||
val data = ByteArray(bufferSize)
|
||||
recorder.startRecording()
|
||||
|
||||
val discardMillis = 5000 // 丢弃前5秒
|
||||
val discardBytes = (sampleRate * 2 * discardMillis / 1000).toInt() // 16bit = 2字节
|
||||
var bytesRead = 0
|
||||
|
||||
// 先读取并丢弃初始数据
|
||||
while (bytesRead < discardBytes && isStreaming) {
|
||||
val readBytes = recorder.read(data, 0, minOf(bufferSize, discardBytes - bytesRead))
|
||||
if (readBytes > 0) {
|
||||
bytesRead += readBytes
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Streaming Loop
|
||||
while (isStreaming) {
|
||||
val readBytes = recorder.read(data, 0, bufferSize)
|
||||
if (readBytes > 0) {
|
||||
val sent = nativeSend(data, readBytes)
|
||||
if (sent < 0) break // Socket broken
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
recorder.stop()
|
||||
recorder.release()
|
||||
nativeClose()
|
||||
}
|
||||
recordingThread?.start()
|
||||
}
|
||||
|
||||
fun stopStreaming() {
|
||||
isStreaming = false
|
||||
nativeClose() // Unblocks the native Accept/Send if hung
|
||||
recordingThread?.join()
|
||||
recordingThread = null
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,12 @@ class MainActivity: FlutterActivity() {
|
||||
"getNativeLibraryPath" -> {
|
||||
result.success(getApplicationInfo().nativeLibraryDir)
|
||||
}
|
||||
"startStreaming" -> {
|
||||
AudioStream.startStreaming(call.argument("path")!!)
|
||||
}
|
||||
"stopStreaming" -> {
|
||||
AudioStream.stopStreaming()
|
||||
}
|
||||
else -> {
|
||||
// 不支持的方法名
|
||||
result.notImplemented()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<resources>
|
||||
<string name="tc_app_name">Tiny Computer</string>
|
||||
|
||||
<string name="tc_s9a_error_message">Terminal terminated abnormally with error code 9. This error is caused by Android 12+ system limiting the number of child processes and requires manual removal of the restriction.</string>
|
||||
<string name="tc_s9a_error_message">Terminal terminated abnormally with signal 9. This error is caused by Android 12+ system limiting the number of child processes and requires manual removal of the restriction.</string>
|
||||
<string name="tc_s9a_kaomoji">:(</string>
|
||||
<string name="tc_s9a_solution_intro">You can use the following tool to fix:</string>
|
||||
<string name="tc_s9a_solution_alternative">If the above tool cannot fix the issue, or if the device is a HarmonyOS device, please follow the tutorial below (requires a computer and data cable):</string>
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
|
||||
@@ -19,7 +19,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.7.3" apply false
|
||||
id "com.android.application" version "8.9.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||
}
|
||||
|
||||
|
||||
147
build.ps1
Normal file
147
build.ps1
Normal file
@@ -0,0 +1,147 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
|
||||
[ValidateSet("xfce", "lxqt", "gxde")]
|
||||
[string[]]$DesktopEnvs,
|
||||
|
||||
[string]$NameSuffix
|
||||
)
|
||||
|
||||
# 设置路径
|
||||
$SourceDir = "C:\Users\29513\Downloads"
|
||||
$AssetsDir = "assets"
|
||||
|
||||
# 分割文件函数 - 使用xaa, xab, xac...命名
|
||||
function Split-File {
|
||||
param(
|
||||
[string]$Path,
|
||||
[long]$PartSizeBytes,
|
||||
[string]$DestinationPath
|
||||
)
|
||||
|
||||
$stream = [System.IO.File]::OpenRead($Path)
|
||||
$buffer = New-Object byte[] $PartSizeBytes
|
||||
$partNumber = 0
|
||||
|
||||
try {
|
||||
while (($bytesRead = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
|
||||
# 生成类似xaa, xab, xac...的文件名
|
||||
$partName = Get-SplitFileName $partNumber
|
||||
$partPath = Join-Path $DestinationPath $partName
|
||||
|
||||
$partStream = [System.IO.File]::OpenWrite($partPath)
|
||||
try {
|
||||
$partStream.Write($buffer, 0, $bytesRead)
|
||||
} finally {
|
||||
$partStream.Close()
|
||||
}
|
||||
|
||||
Write-Host "创建分片: $partName"
|
||||
$partNumber++
|
||||
}
|
||||
} finally {
|
||||
$stream.Close()
|
||||
}
|
||||
|
||||
return $partNumber
|
||||
}
|
||||
|
||||
# 生成split风格的文件名 (xaa, xab, xac, ..., xaz)
|
||||
function Get-SplitFileName {
|
||||
param([int]$index)
|
||||
|
||||
# 计算字母偏移量(0-25对应a-z)
|
||||
$charOffset = $index % 26
|
||||
# 将数字转换为对应的字母字符
|
||||
$char = [char]([byte][char]'a' + $charOffset)
|
||||
|
||||
return "xa$char"
|
||||
}
|
||||
|
||||
# 处理每个桌面环境
|
||||
foreach ($DesktopEnv in $DesktopEnvs) {
|
||||
Write-Host "`n开始处理 $DesktopEnv 桌面环境..." -ForegroundColor Green
|
||||
|
||||
# 设置文件路径
|
||||
$TarFile = "debian-$DesktopEnv.tar.xz"
|
||||
$SourcePath = Join-Path $SourceDir $TarFile
|
||||
|
||||
# 检查源文件是否存在
|
||||
if (-not (Test-Path $SourcePath)) {
|
||||
Write-Error "错误:找不到文件 $SourcePath"
|
||||
continue
|
||||
}
|
||||
|
||||
# 删除assets文件夹中已有的xa*文件
|
||||
if (Test-Path $AssetsDir) {
|
||||
Write-Host "正在清理assets文件夹中的旧文件..."
|
||||
Get-ChildItem -Path $AssetsDir -Filter "xa*" | Remove-Item -Force
|
||||
} else {
|
||||
Write-Host "创建assets文件夹..."
|
||||
New-Item -ItemType Directory -Path $AssetsDir | Out-Null
|
||||
}
|
||||
|
||||
# 分割文件 (98MB = 98 * 1024 * 1024 = 102760448 bytes)
|
||||
Write-Host "正在分割 $TarFile 文件..."
|
||||
$partCount = Split-File -Path $SourcePath -PartSizeBytes 102760448 -DestinationPath $AssetsDir
|
||||
|
||||
Write-Host "文件分割完成,共创建 $partCount 个分片文件"
|
||||
|
||||
# 运行Flutter构建
|
||||
Write-Host "正在运行Flutter构建..."
|
||||
flutter build apk --target-platform android-arm64 --split-per-abi --obfuscate --split-debug-info=tiny_computer/sdi
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "错误:Flutter构建失败"
|
||||
continue
|
||||
}
|
||||
|
||||
# 构建APK文件名
|
||||
$ApkBaseName = "tiny-computer-$DesktopEnv"
|
||||
if (-not [string]::IsNullOrEmpty($NameSuffix)) {
|
||||
$ApkBaseName += "-$NameSuffix"
|
||||
}
|
||||
|
||||
# 重命名APK和SHA1文件
|
||||
$ApkSource = "build\app\outputs\flutter-apk\app-arm64-v8a-release.apk"
|
||||
$Sha1Source = "build\app\outputs\flutter-apk\app-arm64-v8a-release.apk.sha1"
|
||||
|
||||
if (Test-Path $ApkSource) {
|
||||
Rename-Item -Path $ApkSource -NewName "$ApkBaseName.apk"
|
||||
Write-Host "已重命名APK文件: $ApkBaseName.apk"
|
||||
} else {
|
||||
Write-Error "错误:找不到APK文件 $ApkSource"
|
||||
continue
|
||||
}
|
||||
|
||||
if (Test-Path $Sha1Source) {
|
||||
Rename-Item -Path $Sha1Source -NewName "$ApkBaseName.apk.sha1"
|
||||
Write-Host "已重命名SHA1文件: $ApkBaseName.apk.sha1"
|
||||
} else {
|
||||
Write-Warning "警告:找不到SHA1文件 $Sha1Source"
|
||||
}
|
||||
|
||||
Write-Host "$DesktopEnv 处理完成!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "`n所有桌面环境处理完成!" -ForegroundColor Cyan
|
||||
|
||||
# 既然是开源,我认为应该把prompt开源出来才算,毕竟这个脚本更像编译后的产物,而不是源代码本身。
|
||||
|
||||
# 帮我写一个自动化脚本,做以下几件事:
|
||||
# 1. 脚本所在目录是项目的根目录,脚本应该运行在windows电脑上,接收一个参数,这个参数的值会是xfce, lxqt或gxde。
|
||||
# 2. 在C:\Users\29513\Downloads文件夹有debian-xfce.tar.xz,debian-lxqt.tar.xz和debian-gxde.tar.xz,需要根据之前的参数对应选择,然后分成98MB的小份,命名为xa*(就像linux上的split -b 98M debian.tar.xz),放到项目的assets文件夹。注意这个文件夹可能有之前残留的xa*文件,需要先彻底删除这些xa*文件。
|
||||
# 3. 然后在当前目录运行flutter build apk --target-platform android-arm64 --split-per-abi --obfuscate --split-debug-info=tiny_computer/sdi编译。
|
||||
# 4. 在build\app\outputs\flutter-apk文件夹会有app-arm64-v8a-release.apk和app-arm64-v8a-release.apk.sha1两个文件,需要重命名为tiny-computer-xfce.apk和tiny-computer-xfce.apk.sha1(以xfce为例,具体名称根据参数来定)
|
||||
|
||||
# 直接写成一个ps1脚本行吗
|
||||
|
||||
# 请再添加一些功能:首先可以传入多个选项,比如传入xfce lxqt就可以自动进行这两个构建;其实需要一个新参数允许在生成的apk名字加入后缀,比如添加targetSdk35后缀,就会生成tiny-computer-xfce-targetSdk35.apk和tiny-computer-xfce-targetSdk35.apk.sha1
|
||||
|
||||
# xa*文件的命名不对。要按照split命令默认的那样,命名为xaa,xab,xac... 另外我确定分割后的文件数量不多,不会超过xaz。
|
||||
|
||||
# Cannot convert value "97" to type "System.Char". Error: "Invalid cast from 'Decimal' to 'Char'."
|
||||
# At C:\Users\29513\FlutterProjects\tiny_computer\build.ps1:52 char:5
|
||||
# + $firstChar = [char](97 + [math]::Floor($index / 26)) # a-z
|
||||
# + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# + CategoryInfo : InvalidArgument: (:) [], RuntimeException
|
||||
# + FullyQualifiedErrorId : InvalidCastIConvertible
|
||||
@@ -181,6 +181,17 @@ tmoe还会安装gnome-keyring,由于之前我做xfce包时会造成VSCode反
|
||||
|
||||
### 额外步骤
|
||||
|
||||
- 升级到Debian 13(xfce, lxqt,v1.1.0):
|
||||
- 更换内存分配器([原因](https://github.com/termux/proot/issues/313)。不过截至目前这个bug可能已经被修了,虽然issue没关)安装libtcmalloc-minimal4包,并设置库/usr/lib/aarch64-linux-gnu/libtcmalloc_minimal.so.4到/etc/ld.so.preload
|
||||
- 把/etc/apt/source.list的bookworm改为trixie
|
||||
- sudo apt update, sudo apt full-upgrade, sudo apt autoremove
|
||||
- 取消内存分配器的更改
|
||||
- xfce版本重新修补了libtiff.so.5库
|
||||
- 升级到GXDE 25(gxde,v1.1.0):
|
||||
- 更换内存分配器
|
||||
- 使用AI重写的升级脚本(gxde-25-upgrade.sh),以便在不启动图形界面的情况下升级
|
||||
- 禁用电源管理(lxqt,v1.1.0):`mkdir -p ~/.config/autostart;cp /etc/xdg/autostart/lxqt-powermanagement.desktop ~/.config/autostart/;echo "Hidden=true" >> ~/.config/autostart/lxqt-powermanagement.desktop`
|
||||
- 禁用MIT-SHM扩展(v1.1.0):/usr/local/bin/startvnc第372行添加set "${a}" "-extension" "MIT-SHM"
|
||||
- 将桌面壁纸的配置从monitorBuiltinDisplay改为monitorbuiltin(xfce,v1.0.99),疑似Termux:X11显示器名称改变导致壁纸失效
|
||||
- 修复用vscode打开文件时只打开了vscode本身(v1.0.25):去掉/usr/share/applications/code-no-sandbox.desktop的Exec的--unity-launch
|
||||
- 修复选择文本时会把文本发送到剪切板(v1.0.25):在/usr/local/bin/startvnc文件的start_tmoe_xvnc()的start_win10_tigervnc行前面加入`vncconfig -set SendPrimary=0 SetPrimary=0`
|
||||
|
||||
@@ -19,9 +19,12 @@ fi
|
||||
|
||||
echo "最新版本: $latest_version"
|
||||
|
||||
hangover_url="https://github.com/AndreRH/hangover/releases/download/hangover-${latest_version}/hangover_${latest_version}_debian12_bookworm_arm64.tar"
|
||||
hangover_url="https://github.com/AndreRH/hangover/releases/download/hangover-${latest_version}/hangover_${latest_version}_debian13_trixie_arm64.tar"
|
||||
|
||||
mirror_sites=(
|
||||
"https://gh-proxy.org/"
|
||||
"https://cdn.gh-proxy.org/"
|
||||
"https://edgeone.gh-proxy.org/"
|
||||
"https://gh.llkk.cc/"
|
||||
"https://github.moeyy.xyz/"
|
||||
"https://mirror.ghproxy.com/"
|
||||
@@ -65,13 +68,6 @@ tar xvf dxvk-v*.tar.gz
|
||||
mv dxvk-v*/x32/* /home/tiny/.wine/drive_c/windows/syswow64
|
||||
mv dxvk-v*/arm64ec/* /home/tiny/.wine/drive_c/windows/system32
|
||||
|
||||
echo "自动配置 DLL 覆盖..."
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d8 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d9 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d10core /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d11 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v dxgi /d native /f >/dev/null 2>&1
|
||||
|
||||
echo "正在修复字体..."
|
||||
regedit "Z:\\home\\tiny\\.local\\share\\tiny\\extra\\chn_fonts.reg" && wine reg delete "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes" /va /f
|
||||
|
||||
|
||||
@@ -4,10 +4,13 @@ echo "正在更新软件包..."
|
||||
sudo apt update
|
||||
sudo apt upgrade -y
|
||||
|
||||
hangover_url="https://github.com/AndreRH/hangover/releases/download/hangover-10.11/hangover_10.11_debian12_bookworm_arm64.tar"
|
||||
latest_version="10.11"
|
||||
hangover_url="https://github.com/AndreRH/hangover/releases/download/hangover-11.0/hangover_11.0_debian13_trixie_arm64.tar"
|
||||
latest_version="11.0"
|
||||
|
||||
mirror_sites=(
|
||||
"https://gh-proxy.org/"
|
||||
"https://cdn.gh-proxy.org/"
|
||||
"https://edgeone.gh-proxy.org/"
|
||||
"https://gh.llkk.cc/"
|
||||
"https://github.moeyy.xyz/"
|
||||
"https://mirror.ghproxy.com/"
|
||||
@@ -51,13 +54,6 @@ tar xvf dxvk-v*.tar.gz
|
||||
mv dxvk-v*/x32/* /home/tiny/.wine/drive_c/windows/syswow64
|
||||
mv dxvk-v*/arm64ec/* /home/tiny/.wine/drive_c/windows/system32
|
||||
|
||||
echo "自动配置 DLL 覆盖..."
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d8 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d9 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d10core /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d11 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v dxgi /d native /f >/dev/null 2>&1
|
||||
|
||||
echo "正在修复字体..."
|
||||
regedit "Z:\\home\\tiny\\.local\\share\\tiny\\extra\\chn_fonts.reg" && wine reg delete "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes" /va /f
|
||||
|
||||
|
||||
146
extra/gxde-25-upgrade.sh
Normal file
146
extra/gxde-25-upgrade.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义颜色以便于阅读
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}=== GXDE OS 15 -> 25 命令行升级工具 ===${NC}"
|
||||
|
||||
# 1. 检查 root 权限
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e "${RED}错误: 请使用 root 权限运行此脚本 (例如: sudo $0)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. 风险确认 (替代 Zenity)
|
||||
echo -e "${YELLOW}警告:您即将执行 GXDE OS 15 到 25 的升级${NC}"
|
||||
echo "• 该操作不可逆且存在风险"
|
||||
echo "• 请确保系统已经更新到最新"
|
||||
echo "• 请确保已做好数据备份"
|
||||
echo "• 升级过程可能需要 1-3 小时,期间请勿关闭终端"
|
||||
echo ""
|
||||
read -p "您确定要继续吗?(输入 yes 继续,其他键取消): " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "操作已取消。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}再次确认:这是一个高风险操作!!!${NC}"
|
||||
read -p "请输入 'I AGREE' (大写) 以确认并开始升级: " confirm_final
|
||||
if [ "$confirm_final" != "I AGREE" ]; then
|
||||
echo "操作已取消。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}>>> 开始预处理...${NC}"
|
||||
|
||||
# 刷新缓存与修复依赖
|
||||
echo "正在刷新系统包缓存..."
|
||||
apt update
|
||||
aptss update
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "正在检查和修复系统依赖问题..."
|
||||
aptss install -f -yqq
|
||||
|
||||
# 删除冲突包
|
||||
echo "正在移除 qtbase5-dev..."
|
||||
apt autopurge qtbase5-dev -y
|
||||
|
||||
# 3. 替换软件源 (核心逻辑)
|
||||
echo "正在替换软件源..."
|
||||
# 备份并替换主源
|
||||
sed -i 's/bookworm/trixie/g' /etc/apt/sources.list
|
||||
|
||||
# 处理 PPA 源
|
||||
declare -A ppa_map=(
|
||||
["/etc/apt/sources.list.d/gxde.list"]='s/bixie/lizhi/g'
|
||||
["/etc/apt/sources.list.d/gxde-testing.list"]='s/tianlu/zhuangzhuang/g'
|
||||
)
|
||||
rm -vf /etc/apt/sources.list.d/gxde-bpo.list
|
||||
|
||||
for file in "${!ppa_map[@]}"; do
|
||||
if [[ -f "$file" ]]; then
|
||||
sed -i "${ppa_map[$file]}" "$file"
|
||||
echo "已更新源文件: $file"
|
||||
else
|
||||
[[ "$file" =~ testing ]] && continue
|
||||
echo -e "${RED}严重错误:关键源文件缺失 $file${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 屏蔽旧的更新器
|
||||
echo "正在屏蔽旧版更新器..."
|
||||
rm -fv /usr/bin/gxde-app-upgrader
|
||||
cat > /usr/bin/gxde-app-upgrader << EOF
|
||||
#!/bin/bash
|
||||
echo "警告:检测到您尚未完成系统大版本更新,请完成 CLI 更新流程!"
|
||||
EOF
|
||||
chmod +x /usr/bin/gxde-app-upgrader
|
||||
|
||||
# 刷新新源
|
||||
echo "正在刷新新源缓存..."
|
||||
apt update
|
||||
aptss update
|
||||
yes n | aptss install gxde-25-upgrader -yqq
|
||||
|
||||
echo -e "${GREEN}>>> 预处理完成,准备开始核心升级...${NC}"
|
||||
echo -e "${YELLOW}注意:接下来的过程请保持网络畅通,不要中断脚本运行。${NC}"
|
||||
sleep 3
|
||||
|
||||
# 4. 执行核心升级逻辑 (原 gxde-post-upgrade-fix 内容)
|
||||
|
||||
# 检查当前桌面环境状态
|
||||
ANDROID_INSTALLED=0
|
||||
DESKTOP_MISSING=0
|
||||
dpkg -s gxde-desktop-android &>/dev/null && ANDROID_INSTALLED=1
|
||||
dpkg -s gxde-desktop &>/dev/null || DESKTOP_MISSING=1
|
||||
|
||||
# 确定要安装的桌面包
|
||||
DESKTOP_PKG="gxde-desktop"
|
||||
if [ "$ANDROID_INSTALLED" -eq 1 ] && [ "$DESKTOP_MISSING" -eq 1 ]; then
|
||||
DESKTOP_PKG="gxde-desktop-android"
|
||||
echo "检测到 Android 环境,将安装: $DESKTOP_PKG"
|
||||
else
|
||||
echo "将在升级后安装: $DESKTOP_PKG"
|
||||
fi
|
||||
|
||||
# 执行 Full Upgrade
|
||||
echo -e "${GREEN}>>> 正在执行系统全面升级 (Full Upgrade)...这可能需要很长时间${NC}"
|
||||
yes n | env DEBIAN_FRONTEND=noninteractive aptss full-upgrade \
|
||||
-o DPkg::options::="--force-confdef" \
|
||||
-o DPkg::options::="--force-confold" \
|
||||
-o DPkg::options::="--force-overwrite" \
|
||||
-yqq --assume-yes
|
||||
|
||||
# 处理 grub 配置问题 (Hack)
|
||||
echo "正在处理 GRUB 配置..."
|
||||
if [ -f /var/lib/dpkg/info/grub-pc.postinst ]; then
|
||||
mv -v /var/lib/dpkg/info/grub-pc.postinst /var/lib/dpkg/info/grub-pc.postinst.bak
|
||||
dpkg --configure -a
|
||||
mv -v /var/lib/dpkg/info/grub-pc.postinst.bak /var/lib/dpkg/info/grub-pc.postinst
|
||||
else
|
||||
dpkg --configure -a
|
||||
fi
|
||||
|
||||
# 安装/更新核心软件包
|
||||
echo -e "${GREEN}>>> 正在安装/重装核心组件...${NC}"
|
||||
yes n | env DEBIAN_FRONTEND=noninteractive aptss install gxde-app-upgrader --reinstall -yqq
|
||||
|
||||
if yes n | env DEBIAN_FRONTEND=noninteractive aptss install $DESKTOP_PKG deepin-kwin-x11 libdtkcore-dev deepin-desktop-base spark-store gxde-control-center --reinstall -yqq; then
|
||||
|
||||
# 启用服务
|
||||
systemctl enable dde-filemanager-daemon.service || true
|
||||
|
||||
echo -e "${GREEN}-----------------------${NC}"
|
||||
echo -e "${GREEN}升级成功完成!${NC}"
|
||||
echo -e "${YELLOW}请按回车键重启您的计算机,或者按 Ctrl+C 稍后手动重启。${NC}"
|
||||
read
|
||||
reboot
|
||||
else
|
||||
echo -e "${RED}!!!!!! 升级过程中出现错误 !!!!!!${NC}"
|
||||
echo "请保留此终端输出,并反馈给 QQ 群 881201853"
|
||||
exit 1
|
||||
fi
|
||||
@@ -41,6 +41,10 @@ Turnip驱动。根据[这里](https://github.com/xDoge26/proot-setup/issues/26#i
|
||||
|
||||
快捷指令的彩蛋。原本放在容器里,但显然放这里更为合适
|
||||
|
||||
#### extra/tiny_virtual_mic
|
||||
|
||||
麦克风客户端。实现见tiny_virtual_mic.c
|
||||
|
||||
#### caj, edraw
|
||||
|
||||
这些分别是cajviewer,亿图图示的补丁
|
||||
@@ -48,12 +52,6 @@ Turnip驱动。根据[这里](https://github.com/xDoge26/proot-setup/issues/26#i
|
||||
- 亿图图示补丁的库文件是在小小电脑上下载了Qt对应版本源码后编译得到的;
|
||||
- 编译进行了两次,第一次直接编译,可以得到Gui和Widgets两个库。第二次编译带上XcbQpa,虽然会编译失败,但在这之前就可以得到XcbQpa的库。
|
||||
|
||||
#### wechat
|
||||
|
||||
微信的补丁。license, uos-lsb和uos-release来自星火的微信包或arch的wechat-uos打包(嗯,我忘记到底是哪的了。不过都差不多)。
|
||||
|
||||
libssl1.1来自debian官方源。deepin-elf-verifier是我打的空包。
|
||||
|
||||
#### font
|
||||
|
||||
[小赖字体](https://github.com/lxgw/kose-font)用于修复wine的方块字
|
||||
|
||||
74
extra/tiny_virtual_mic.c
Normal file
74
extra/tiny_virtual_mic.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: %s <socket_path> <target_device>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 1. Setup PulseAudio
|
||||
static const pa_sample_spec ss = {
|
||||
.format = PA_SAMPLE_S16LE,
|
||||
.rate = 44100,
|
||||
.channels = 1
|
||||
};
|
||||
|
||||
pa_buffer_attr attr;
|
||||
attr.maxlength = (uint32_t) -1;
|
||||
attr.tlength = pa_usec_to_bytes(60000, &ss); // 目标延迟设为 60ms
|
||||
attr.prebuf = (uint32_t) -1;
|
||||
attr.minreq = (uint32_t) -1;
|
||||
attr.fragsize = (uint32_t) -1;
|
||||
|
||||
int error;
|
||||
pa_simple *s = pa_simple_new(NULL, "AndroidStream", PA_STREAM_PLAYBACK, argv[2], "live_audio", &ss, NULL, &attr, &error);
|
||||
if (!s) {
|
||||
fprintf(stderr, "pa_simple_new() failed: %s\n", pa_strerror(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 2. Connect to Android Socket
|
||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);
|
||||
|
||||
printf("Connecting to %s...\n", argv[1]);
|
||||
while (connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
printf("Waiting for server...\n");
|
||||
sleep(1); // Retry logic since Android might start later
|
||||
}
|
||||
printf("Connected! Playing audio...\n");
|
||||
|
||||
// 3. Stream Loop
|
||||
uint8_t buf[BUFSIZE];
|
||||
while (1) {
|
||||
ssize_t r = read(sock_fd, buf, sizeof(buf));
|
||||
if (r <= 0) break;
|
||||
|
||||
if (pa_simple_write(s, buf, (size_t)r, &error) < 0) {
|
||||
fprintf(stderr, "pa_simple_write() failed: %s\n", pa_strerror(error));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
pa_simple_free(s);
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
363
extra/turnip.patch
Normal file
363
extra/turnip.patch
Normal file
@@ -0,0 +1,363 @@
|
||||
diff --git a/src/freedreno/vulkan/tu_shader.cc b/src/freedreno/vulkan/tu_shader.cc
|
||||
index edc0ce1f6fe..e5b8e9bc44d 100644
|
||||
--- a/src/freedreno/vulkan/tu_shader.cc
|
||||
+++ b/src/freedreno/vulkan/tu_shader.cc
|
||||
@@ -2556,10 +2556,10 @@ tu_upload_shader(struct tu_device *dev,
|
||||
size += vpc_size;
|
||||
}
|
||||
|
||||
- pthread_mutex_lock(&dev->pipeline_mutex);
|
||||
+ mtx_lock(&dev->pipeline_mutex);
|
||||
VkResult result = tu_suballoc_bo_alloc(&shader->bo, &dev->pipeline_suballoc,
|
||||
size * 4, 128);
|
||||
- pthread_mutex_unlock(&dev->pipeline_mutex);
|
||||
+ mtx_unlock(&dev->pipeline_mutex);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
@@ -2589,9 +2589,9 @@ tu_upload_shader(struct tu_device *dev,
|
||||
|
||||
result = tu_setup_pvtmem(dev, shader, &pvtmem_config, pvtmem_size, per_wave);
|
||||
if (result != VK_SUCCESS) {
|
||||
- pthread_mutex_lock(&dev->pipeline_mutex);
|
||||
+ mtx_lock(&dev->pipeline_mutex);
|
||||
tu_suballoc_bo_free(&dev->pipeline_suballoc, &shader->bo);
|
||||
- pthread_mutex_unlock(&dev->pipeline_mutex);
|
||||
+ mtx_unlock(&dev->pipeline_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3427,10 +3427,10 @@ tu_empty_shader_create(struct tu_device *dev,
|
||||
if (!shader)
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
|
||||
- pthread_mutex_lock(&dev->pipeline_mutex);
|
||||
+ mtx_lock(&dev->pipeline_mutex);
|
||||
VkResult result = tu_suballoc_bo_alloc(&shader->bo, &dev->pipeline_suballoc,
|
||||
32 * 4, 128);
|
||||
- pthread_mutex_unlock(&dev->pipeline_mutex);
|
||||
+ mtx_unlock(&dev->pipeline_mutex);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
vk_free(&dev->vk.alloc, shader);
|
||||
@@ -3541,9 +3541,9 @@ tu_shader_destroy(struct tu_device *dev,
|
||||
tu_cs_finish(&shader->cs);
|
||||
TU_RMV(resource_destroy, dev, &shader->bo);
|
||||
|
||||
- pthread_mutex_lock(&dev->pipeline_mutex);
|
||||
+ mtx_lock(&dev->pipeline_mutex);
|
||||
tu_suballoc_bo_free(&dev->pipeline_suballoc, &shader->bo);
|
||||
- pthread_mutex_unlock(&dev->pipeline_mutex);
|
||||
+ mtx_unlock(&dev->pipeline_mutex);
|
||||
|
||||
if (shader->pvtmem_bo)
|
||||
tu_bo_finish(dev, shader->pvtmem_bo);
|
||||
diff --git a/src/freedreno/vulkan/tu_wsi.cc b/src/freedreno/vulkan/tu_wsi.cc
|
||||
index 57cf9048b07..246a95dd894 100644
|
||||
--- a/src/freedreno/vulkan/tu_wsi.cc
|
||||
+++ b/src/freedreno/vulkan/tu_wsi.cc
|
||||
@@ -47,6 +47,10 @@ tu_wsi_init(struct tu_physical_device *physical_device)
|
||||
&options);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
+
|
||||
+ if (strcmp(physical_device->instance->knl->name, "kgsl") == 0) {
|
||||
+ physical_device->wsi_device.is_tu_kgsl = true;
|
||||
+ }
|
||||
|
||||
physical_device->wsi_device.supports_modifiers = true;
|
||||
physical_device->wsi_device.can_present_on_device =
|
||||
diff --git a/src/util/anon_file.c b/src/util/anon_file.c
|
||||
index a9ad2a2aad8..f5dcd5b3b48 100644
|
||||
--- a/src/util/anon_file.c
|
||||
+++ b/src/util/anon_file.c
|
||||
@@ -117,6 +117,11 @@ get_or_create_user_temp_dir(char* buf, size_t len) {
|
||||
int uid = getuid();
|
||||
|
||||
env = os_get_option("XDG_RUNTIME_DIR");
|
||||
+#ifdef __linux__
|
||||
+ if (!env) {
|
||||
+ env = "/tmp";
|
||||
+ }
|
||||
+#endif
|
||||
if (env && env[0] != '\0') {
|
||||
snprintf(buf, len, "%s", env);
|
||||
return buf;
|
||||
diff --git a/src/util/u_process.c b/src/util/u_process.c
|
||||
index 6846acd2e0b..8551c227b88 100644
|
||||
--- a/src/util/u_process.c
|
||||
+++ b/src/util/u_process.c
|
||||
@@ -101,7 +101,7 @@ __getProgramName()
|
||||
{
|
||||
return strdup(program_invocation_short_name);
|
||||
}
|
||||
-#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) || DETECT_OS_ANDROID || defined(__NetBSD__)
|
||||
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) || DETECT_OS_ANDROID || defined(__NetBSD__) || defined(__linux__)
|
||||
#if defined(__NetBSD__)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
|
||||
index 6783fbd6efb..3a81c200c2a 100644
|
||||
--- a/src/vulkan/wsi/wsi_common.c
|
||||
+++ b/src/vulkan/wsi/wsi_common.c
|
||||
@@ -2491,7 +2491,7 @@ wsi_common_queue_present(const struct wsi_device *wsi,
|
||||
#endif
|
||||
}
|
||||
|
||||
- if (wsi->sw) {
|
||||
+ if (wsi->sw || (wsi->is_tu_kgsl && (swapchain->dma_buf_semaphore == VK_NULL_HANDLE))) {
|
||||
wsi->WaitForFences(vk_device_to_handle(dev),
|
||||
1, &swapchain->fences[image_index], true, ~0ull);
|
||||
}
|
||||
@@ -3217,7 +3217,7 @@ wsi_configure_cpu_image(const struct wsi_swapchain *chain,
|
||||
const struct wsi_cpu_image_params *params,
|
||||
struct wsi_image_info *info)
|
||||
{
|
||||
- assert(params->base.image_type == WSI_IMAGE_TYPE_CPU);
|
||||
+ // assert(params->base.image_type == WSI_IMAGE_TYPE_CPU);
|
||||
assert(chain->blit.type == WSI_SWAPCHAIN_NO_BLIT ||
|
||||
chain->blit.type == WSI_SWAPCHAIN_BUFFER_BLIT);
|
||||
|
||||
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
|
||||
index c17a79c6b13..20c480babbc 100644
|
||||
--- a/src/vulkan/wsi/wsi_common.h
|
||||
+++ b/src/vulkan/wsi/wsi_common.h
|
||||
@@ -152,6 +152,7 @@ struct wsi_device {
|
||||
|
||||
|
||||
bool sw;
|
||||
+ bool is_tu_kgsl;
|
||||
|
||||
/* Set to true if the implementation is ok with linear WSI images. */
|
||||
bool wants_linear;
|
||||
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
|
||||
index f72e85c5e66..ac83c92236a 100644
|
||||
--- a/src/vulkan/wsi/wsi_common_display.c
|
||||
+++ b/src/vulkan/wsi/wsi_common_display.c
|
||||
@@ -516,6 +516,12 @@ struct wsi_display_sync {
|
||||
|
||||
static uint64_t fence_sequence;
|
||||
|
||||
+#ifdef __linux__
|
||||
+static void thread_signal_handler (int signum) {
|
||||
+ pthread_exit (0);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static void
|
||||
_wsi_display_cleanup_state(struct wsi_display_swapchain *chain);
|
||||
|
||||
@@ -2024,7 +2030,9 @@ wsi_display_wait_thread(void *data)
|
||||
.events = POLLIN
|
||||
};
|
||||
|
||||
+#ifndef __linux__
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
+#endif
|
||||
for (;;) {
|
||||
int ret = poll(&pollfd, 1, -1);
|
||||
if (ret > 0) {
|
||||
@@ -2052,9 +2060,21 @@ wsi_display_start_wait_thread(struct wsi_display *wsi)
|
||||
static void
|
||||
wsi_display_stop_wait_thread(struct wsi_display *wsi)
|
||||
{
|
||||
+#ifdef __linux__
|
||||
+ struct sigaction actions;
|
||||
+ memset (&actions, 0, sizeof (actions));
|
||||
+ sigemptyset (&actions.sa_mask);
|
||||
+ actions.sa_flags = 0;
|
||||
+ actions.sa_handler = thread_signal_handler;
|
||||
+ sigaction (SIGUSR2, &actions, NULL);
|
||||
+#endif
|
||||
mtx_lock(&wsi->wait_mutex);
|
||||
if (wsi->wait_thread) {
|
||||
+#ifndef __linux__
|
||||
pthread_cancel(wsi->wait_thread);
|
||||
+#else
|
||||
+ pthread_kill(wsi->wait_thread, SIGUSR2);
|
||||
+#endif
|
||||
pthread_join(wsi->wait_thread, NULL);
|
||||
wsi->wait_thread = 0;
|
||||
}
|
||||
@@ -3456,7 +3476,9 @@ udev_event_listener_thread(void *data)
|
||||
|
||||
int udev_fd = udev_monitor_get_fd(mon);
|
||||
|
||||
+#ifndef __linux__
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
+#endif
|
||||
|
||||
for (;;) {
|
||||
nfds_t nfds = 1;
|
||||
@@ -3603,6 +3625,15 @@ wsi_display_finish_wsi(struct wsi_device *wsi_device,
|
||||
struct wsi_display *wsi =
|
||||
(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
|
||||
|
||||
+#ifdef __linux__
|
||||
+ struct sigaction actions;
|
||||
+ memset (&actions, 0, sizeof (actions));
|
||||
+ sigemptyset (&actions.sa_mask);
|
||||
+ actions.sa_flags = 0;
|
||||
+ actions.sa_handler = thread_signal_handler;
|
||||
+ sigaction (SIGUSR2, &actions, NULL);
|
||||
+#endif
|
||||
+
|
||||
if (wsi) {
|
||||
wsi_for_each_connector(connector, wsi)
|
||||
wsi_display_free_connector(wsi, connector);
|
||||
@@ -3610,7 +3641,11 @@ wsi_display_finish_wsi(struct wsi_device *wsi_device,
|
||||
wsi_display_stop_wait_thread(wsi);
|
||||
|
||||
if (wsi->hotplug_thread) {
|
||||
+#ifndef __linux__
|
||||
pthread_cancel(wsi->hotplug_thread);
|
||||
+#else
|
||||
+ pthread_kill(wsi->hotplug_thread, SIGUSR2);
|
||||
+#endif
|
||||
pthread_join(wsi->hotplug_thread, NULL);
|
||||
}
|
||||
|
||||
diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c
|
||||
index fe297d3ca01..8c48b29d733 100644
|
||||
--- a/src/vulkan/wsi/wsi_common_drm.c
|
||||
+++ b/src/vulkan/wsi/wsi_common_drm.c
|
||||
@@ -47,6 +47,9 @@
|
||||
static VkResult
|
||||
wsi_dma_buf_export_sync_file(int dma_buf_fd, int *sync_file_fd)
|
||||
{
|
||||
+ #if defined (__linux__)
|
||||
+ return VK_ERROR_FEATURE_NOT_PRESENT;
|
||||
+ #else
|
||||
struct dma_buf_export_sync_file export = {
|
||||
.flags = DMA_BUF_SYNC_RW,
|
||||
.fd = -1,
|
||||
@@ -64,11 +67,15 @@ wsi_dma_buf_export_sync_file(int dma_buf_fd, int *sync_file_fd)
|
||||
*sync_file_fd = export.fd;
|
||||
|
||||
return VK_SUCCESS;
|
||||
+ #endif
|
||||
}
|
||||
|
||||
static VkResult
|
||||
wsi_dma_buf_import_sync_file(int dma_buf_fd, int sync_file_fd)
|
||||
{
|
||||
+ #if defined (__linux__)
|
||||
+ return VK_ERROR_FEATURE_NOT_PRESENT;
|
||||
+ #else
|
||||
struct dma_buf_import_sync_file import = {
|
||||
.flags = DMA_BUF_SYNC_RW,
|
||||
.fd = sync_file_fd,
|
||||
@@ -84,6 +91,7 @@ wsi_dma_buf_import_sync_file(int dma_buf_fd, int sync_file_fd)
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
+ #endif
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
|
||||
index 5e4b3a68d07..d19b8faa196 100644
|
||||
--- a/src/vulkan/wsi/wsi_common_x11.c
|
||||
+++ b/src/vulkan/wsi/wsi_common_x11.c
|
||||
@@ -149,22 +149,7 @@ static bool
|
||||
wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev,
|
||||
xcb_connection_t *conn)
|
||||
{
|
||||
- xcb_screen_iterator_t screen_iter =
|
||||
- xcb_setup_roots_iterator(xcb_get_setup(conn));
|
||||
- xcb_screen_t *screen = screen_iter.data;
|
||||
-
|
||||
- /* Open the DRI3 device from the X server. If we do not retrieve one we
|
||||
- * assume our local device is compatible.
|
||||
- */
|
||||
- int dri3_fd = wsi_dri3_open(conn, screen->root, None);
|
||||
- if (dri3_fd == -1)
|
||||
- return true;
|
||||
-
|
||||
- bool match = wsi_dev->can_present_on_device(wsi_dev->pdevice, dri3_fd);
|
||||
-
|
||||
- close(dri3_fd);
|
||||
-
|
||||
- return match;
|
||||
+ return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1453,8 +1438,6 @@ x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index,
|
||||
!wsi_device->x11.ignore_suboptimal)
|
||||
options |= XCB_PRESENT_OPTION_SUBOPTIMAL;
|
||||
|
||||
- xshmfence_reset(image->shm_fence);
|
||||
-
|
||||
if (!chain->base.image_info.explicit_sync) {
|
||||
++chain->sent_image_count;
|
||||
assert(chain->sent_image_count <= chain->base.image_count);
|
||||
@@ -1832,11 +1815,6 @@ x11_acquire_next_image(struct wsi_swapchain *wsi_chain,
|
||||
return result;
|
||||
|
||||
assert(*image_index < chain->base.image_count);
|
||||
-#ifdef HAVE_X11_DRM
|
||||
- if (chain->images[*image_index].shm_fence &&
|
||||
- !chain->base.image_info.explicit_sync)
|
||||
- xshmfence_await(chain->images[*image_index].shm_fence);
|
||||
-#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -2201,15 +2179,24 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
|
||||
if (fd == -1)
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
|
||||
- cookie =
|
||||
- xcb_dri3_pixmap_from_buffer_checked(chain->conn,
|
||||
- image->pixmap,
|
||||
- chain->window,
|
||||
- image->base.sizes[0],
|
||||
- pCreateInfo->imageExtent.width,
|
||||
- pCreateInfo->imageExtent.height,
|
||||
- image->base.row_pitches[0],
|
||||
- chain->depth, bpp, fd);
|
||||
+ cookie = xcb_dri3_pixmap_from_buffers_checked(chain->conn,
|
||||
+ image->pixmap,
|
||||
+ chain->window,
|
||||
+ image->base.num_planes,
|
||||
+ pCreateInfo->imageExtent.width,
|
||||
+ pCreateInfo->imageExtent.height,
|
||||
+ image->base.row_pitches[0],
|
||||
+ image->base.offsets[0],
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ chain->depth,
|
||||
+ bpp,
|
||||
+ 1274,
|
||||
+ &fd);
|
||||
}
|
||||
|
||||
error = xcb_request_check(chain->conn, cookie);
|
||||
@@ -2239,6 +2226,9 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
|
||||
}
|
||||
#endif
|
||||
|
||||
+ image->sync_fence = 0;
|
||||
+ return VK_SUCCESS;
|
||||
+
|
||||
out_fence:
|
||||
fence_fd = xshmfence_alloc_shm();
|
||||
if (fence_fd < 0)
|
||||
@@ -2281,12 +2271,6 @@ x11_image_finish(struct x11_swapchain *chain,
|
||||
{
|
||||
xcb_void_cookie_t cookie;
|
||||
if (!chain->base.wsi->sw || chain->has_mit_shm) {
|
||||
-#ifdef HAVE_X11_DRM
|
||||
- cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence);
|
||||
- xcb_discard_reply(chain->conn, cookie.sequence);
|
||||
- xshmfence_unmap_shm(image->shm_fence);
|
||||
-#endif
|
||||
-
|
||||
cookie = xcb_free_pixmap(chain->conn, image->pixmap);
|
||||
xcb_discard_reply(chain->conn, cookie.sequence);
|
||||
#ifdef HAVE_X11_DRM
|
||||
@@ -1,3 +1,2 @@
|
||||
synthetic-package: false
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: intl_en.arb
|
||||
@@ -31,9 +31,8 @@
|
||||
"reinstallBootPackage": "Reinstall Boot Package",
|
||||
"getifaddrsBridge": "getifaddrs Bridge",
|
||||
"fixGetifaddrsPermission": "Fix getifaddrs permission on Android 13",
|
||||
"fakeUOSSystem": "Pretend System as UOS",
|
||||
"displaySettings": "Display Settings",
|
||||
"avncAdvantages": "AVNC provides better control experience than noVNC:\nTouchpad controls, two-finger tap for keyboard, auto clipboard, picture-in-picture mode, etc. This is an experimental feature.",
|
||||
"avncAdvantages": "AVNC provides better control experience than noVNC:\nTouchpad controls, two-finger tap for keyboard, auto clipboard, picture-in-picture mode, etc.",
|
||||
"avncSettings": "AVNC Settings",
|
||||
"aboutAVNC": "About AVNC",
|
||||
"avncResolution": "AVNC Startup Resolution",
|
||||
@@ -44,11 +43,11 @@
|
||||
"save": "Save",
|
||||
"applyOnNextLaunch": "Apply on next launch",
|
||||
"useAVNCByDefault": "Use AVNC by default",
|
||||
"termuxX11Advantages": "Termux:X11 provides faster performance than VNC with better compatibility in some cases.\nSupports DRI3 (enable in Graphics Acceleration) for significant performance boost.\nNow supports features like bidirectional clipboard.\nExperimental feature! If black screen occurs, try fully closing and restarting the app.",
|
||||
"termuxX11Advantages": "Termux:X11 may provide faster speeds than VNC in certain scenarios.\n\nNote that Termux:X11 operates slightly differently from AVNC:\n- A two-finger tap acts as a right mouse click\n- Pressing the back button reveals the additional keyboard\n\nIf you encounter a black screen, try completely closing and restarting the application.",
|
||||
"termuxX11Preferences": "Termux:X11 Preferences",
|
||||
"useTermuxX11ByDefault": "Use Termux:X11 by default",
|
||||
"disableVNC": "Disable VNC. Requires restart",
|
||||
"hidpiAdvantages": "HiDPI support provides sharper display for high-resolution screens!\n\nNote:\nDisplay may appear too large - set appropriate resolution.\nSome apps may have display issues or slower rendering.",
|
||||
"hidpiAdvantages": "One-click to enable HiDPI mode for clearer display... at the cost of reduced speed.",
|
||||
"hidpiEnvVar": "HiDPI Environment Variables",
|
||||
"hidpiSupport": "HiDPI Support",
|
||||
"fileAccess": "File Access",
|
||||
@@ -70,17 +69,17 @@
|
||||
"dri3Requirement": "DRI3 requires Termux:X11 and Turnip",
|
||||
"windowsAppSupport": "Windows App Support",
|
||||
"hangoverDescription": "Run Windows apps using Hangover (running cross-arch apps on native Wine)!\n\nRunning Windows programs requires two layers of emulation (arch + system) - don't expect good performance!\n\nFor better speed, try enabling Graphics Acceleration. Crashes or failures are normal.\n\nRecommend moving Windows programs to desktop before running.\n\nBe patient. Even if GUI shows nothing. Check terminal - is it still running or stopped with error?\n\nOr check if the Windows app has official Linux arm64 version.",
|
||||
"installHangoverStable": "Install Hangover Stable",
|
||||
"installHangoverStable": "Install Hangover",
|
||||
"installHangoverLatest": "Install Hangover Latest (may fail)",
|
||||
"uninstallHangover": "Uninstall Hangover",
|
||||
"clearWineData": "Clear Wine Data",
|
||||
"wineCommandsHint": "Common Wine commands. Click to launch GUI and wait patiently.\n\nNote: DXVK is enabled by default. If the program crashes, try disabling it.\n\nTypical launch times:\nTiger T7510 6GB: over 1 minute\nSnapdragon 870 12GB: ~10 seconds",
|
||||
"wineCommandsHint": "Common Wine commands. Click to launch GUI and wait patiently.\n\nTypical launch times:\nTiger T7510 6GB: over 1 minute\nSnapdragon 870 12GB: ~10 seconds",
|
||||
"switchToJapanese": "Switch System to Japanese",
|
||||
"userManual": "User Manual",
|
||||
"openSourceLicenses": "Open Source Licenses",
|
||||
"permissionUsage": "Permission Usage",
|
||||
"privacyStatement": "\nThis app does not collect your private information.\n\nHowever, I cannot control behaviors of apps you install/use inside the container system (including via shortcut commands).\n\nRequested permissions are used for:\nFile permissions: accessing phone directories\nNotifications & accessibility: Required by Termux:X11",
|
||||
"supportAuthor": "Support Author",
|
||||
"privacyStatement": "\nThis app does not collect your private information.\n\nHowever, I cannot control behaviors of apps you install/use inside the container system (including via shortcut commands).\n\nRequested permissions are used for:\nFile permissions: accessing phone directories\nNotifications & accessibility: Required by Termux:X11\nMicrophone permission: for using the microphone",
|
||||
"supportAuthor": "Support Developers",
|
||||
"recommendApp": "If you find it useful, please recommend to others!",
|
||||
"projectUrl": "Project URL",
|
||||
"commandEdit": "Command Edit",
|
||||
@@ -98,14 +97,19 @@
|
||||
"enterNumber": "Please enter a number",
|
||||
"enterValidNumber": "Please enter a valid number",
|
||||
"installingBootPackage": "Installing Boot Package",
|
||||
"copyingContainerSystem": "Copying Container System",
|
||||
"installingContainerSystem": "Installing Container System",
|
||||
"copyingContainerSystem": "Copying System Files",
|
||||
"installingContainerSystem": "Installing System Files",
|
||||
"installationComplete": "Installation Complete",
|
||||
"reinstallingBootPackage": "Reinstalling Boot Package",
|
||||
"issueUrl": "Issue Report",
|
||||
"faqUrl": "FAQ",
|
||||
"solutionUrl": "Usage Guide",
|
||||
"discussionUrl": "Discussion",
|
||||
"firstLoadInstructions": "The first load may take about 5 to 10 minutes...\n\nNormally, the software will automatically redirect to the graphical interface after loading.\n\nIn the graphical interface:\n- Tap for left-click\n- Long press for right-click\n- Two-finger tap to open the keyboard\n- Two-finger swipe for mouse wheel\n\nPlease do not exit the software during installation.\n\nWhile waiting, you can click the button below to request permissions.\n\nMany folders in Tiny Computer (e.g., Downloads, Documents, Pictures) are bound to the corresponding device folders. Without these permissions, access to these folders will be denied.\n\nIf you don't need to access these folders, you can skip granting file permissions (but this may cause Firefox to fail when downloading files due to denied access to the Downloads folder).",
|
||||
"updateRequest": "Please try to use the latest version. Visit the project address to check for the latest version."
|
||||
"firstLoadInstructions": "The first load takes about 5 to 10 minutes... and does not require an internet connection.\n\nNormally, the software will automatically redirect to the graphical interface after loading.\n\nIn the graphical interface:\n- Tap for left-click\n- Long press for right-click\n- Two-finger tap to open the keyboard\n- Two-finger swipe for mouse wheel\n\nPlease do not exit the software during installation.\n\nWhile waiting, you can click the button below to request permissions.\n\nMany folders in Tiny Computer (e.g., Downloads, Documents, Pictures) are bound to the corresponding device folders. Without these permissions, access to these folders will be denied.\n\nIf you don't need to access these folders, you can skip granting file permissions (but this may cause Firefox to fail when downloading files due to denied access to the Downloads folder).",
|
||||
"updateRequest": "Please try to use the latest version. Visit the project address to check for the latest version.",
|
||||
"avncScreenResize": "Adaptive Screen Size",
|
||||
"avncResizeFactor": "Screen Scaling Ratio",
|
||||
"avncResizeFactorValue": "Current scaling is",
|
||||
"microphoneSupport": "Microphone Support",
|
||||
"startStreaming": "Start Microphone"
|
||||
}
|
||||
@@ -31,9 +31,8 @@
|
||||
"reinstallBootPackage": "重新安装引导包",
|
||||
"getifaddrsBridge": "getifaddrs桥接",
|
||||
"fixGetifaddrsPermission": "修复安卓13设备getifaddrs无权限",
|
||||
"fakeUOSSystem": "伪装系统为UOS",
|
||||
"displaySettings": "显示设置",
|
||||
"avncAdvantages": "AVNC可以带来相比noVNC更好的操控体验;\n如触摸板触控,双指单击弹出键盘,自动剪切板,画中画模式等等。这是一个实验性功能。",
|
||||
"avncAdvantages": "AVNC可以带来相比noVNC更好的操控体验;\n如触摸板触控,双指单击弹出键盘,自动剪切板,画中画模式等等。",
|
||||
"avncSettings": "AVNC设置",
|
||||
"aboutAVNC": "关于AVNC",
|
||||
"avncResolution": "AVNC启动时分辨率设置",
|
||||
@@ -44,11 +43,11 @@
|
||||
"applyOnNextLaunch": "下次启动时生效",
|
||||
"save": "保存",
|
||||
"useAVNCByDefault": "默认使用AVNC",
|
||||
"termuxX11Advantages": "Termux:X11可以带来比VNC更快的速度,某些情况下兼容性也会更好。\n支持使用DRI3(需在图形加速中开启),可以带来相当大的性能提升。\n随着版本的迭代,Termux:X11如今也支持了双向剪切板等功能。\n这是一个实验性功能!如果黑屏,请尝试彻底关闭本应用再重新启动。",
|
||||
"termuxX11Advantages": "Termux:X11某些情况下可以带来比VNC更快的速度。\n\n注意Termux:X11的操作与AVNC略有不同。\n- 双指点击为鼠标右键\n- 返回弹出小键盘\n\n如果黑屏,请尝试彻底关闭本应用再重新启动。",
|
||||
"termuxX11Preferences": "Termux:X11偏好设置",
|
||||
"useTermuxX11ByDefault": "默认使用Termux:X11",
|
||||
"disableVNC": "不使用VNC。重启生效",
|
||||
"hidpiAdvantages": "高分辨率支持可以为拥有高分辨率屏幕的设备带来更高清的体验!\n\n注意:\n选项开启后显示会变得很大,请设置一个合适的分辨率。\n\n一些软件可能会存在显示问题,或者显示速度变慢。",
|
||||
"hidpiAdvantages": "一键开启高清模式,显示更清晰的同时...速度会变慢。",
|
||||
"hidpiEnvVar": "HiDPI环境变量",
|
||||
"hidpiSupport": "高分辨率支持",
|
||||
"fileAccess": "文件访问",
|
||||
@@ -70,16 +69,16 @@
|
||||
"dri3Requirement": "DRI3必须配合Termux:X11和Turnip使用",
|
||||
"windowsAppSupport": "Windows应用支持",
|
||||
"hangoverDescription": "使用Hangover(在原生Wine运行跨架构应用)运行Windows应用!\n\n运行Windows程序需要经过架构和系统两层模拟,不要对运行速度抱有期待!\n\n需要速度可以尝试配合图形加速使用。当然程序崩溃甚至打不开也是正常的。\n\n建议将要运行的Windows程序连同程序文件夹移至桌面运行。\n\n你需要耐心。即使图形界面什么也没显示。看看终端,还在继续输出吗?还是停止在某个报错?\n\n或者寻找该Windows软件官方是否提供Linux arm64版本。",
|
||||
"installHangoverStable": "安装Hangover稳定版",
|
||||
"installHangoverStable": "安装Hangover",
|
||||
"installHangoverLatest": "安装Hangover最新版(可能出错)",
|
||||
"uninstallHangover": "卸载Hangover",
|
||||
"clearWineData": "清空Wine数据",
|
||||
"wineCommandsHint": "Wine的常用指令。点击后前往图形界面耐心等待。\n\n注:DXVK默认为开启状态,如程序崩溃可尝试关闭\n\n任意程序启动参考时间:\n虎贲T7510 6GB 超过一分钟\n骁龙870 12GB 约10秒\n",
|
||||
"wineCommandsHint": "Wine的常用指令。点击后前往图形界面耐心等待。\n\n任意程序启动参考时间:\n虎贲T7510 6GB 超过一分钟\n骁龙870 12GB 约10秒\n",
|
||||
"switchToJapanese": "切换系统到日语",
|
||||
"userManual": "使用说明",
|
||||
"openSourceLicenses": "开源许可",
|
||||
"permissionUsage": "权限使用说明",
|
||||
"privacyStatement": "\n本软件不会收集你的隐私信息。\n\n当然,你在容器系统内部安装或使用的软件行为(包括通过快捷指令)就不受我控制了,我不对其负责。\n\n本软件申请的权限用于以下目的:\n文件相关权限:用于系统访问手机目录;\n通知和无障碍:Termux:X11需要。",
|
||||
"privacyStatement": "\n本软件不会收集你的隐私信息。\n\n当然,你在容器系统内部安装或使用的软件行为(包括通过快捷指令)就不受我控制了,我不对其负责。\n\n本软件申请的权限用于以下目的:\n文件相关权限:用于系统访问手机目录;\n通知和无障碍:Termux:X11需要。\n麦克风权限:用于使用麦克风",
|
||||
"supportAuthor": "支持作者",
|
||||
"recommendApp": "如果认为好用的话,可以推荐给其他人用噢!",
|
||||
"projectUrl": "项目地址",
|
||||
@@ -106,6 +105,11 @@
|
||||
"faqUrl": "常见问题",
|
||||
"solutionUrl": "典型场景使用指南",
|
||||
"discussionUrl": "论坛与讨论",
|
||||
"firstLoadInstructions": "第一次加载大概需要5到10分钟...\n\n正常情况下,加载完成后软件会自动跳转到图形界面。\n\n在图形界面时:\n- 点击为鼠标左键\n- 长按为鼠标右键\n- 双指点击可弹出键盘\n- 双指划动为鼠标滚轮\n\n请不要在安装时退出软件。\n\n在等待时,可以点击下面的按钮申请一下权限。\n\n小小电脑的许多文件夹,比如下载、文档、图片等等都和设备的对应文件夹绑定,如果不授予这些权限会导致这些文件夹无权访问。\n\n但如果你不需要访问这些文件夹,也可以不授予文件权限(可能导致火狐浏览器下载文件失败,因为无权访问下载文件夹)。",
|
||||
"updateRequest": "请尽量使用最新版本。前往项目地址可查看最新版本。"
|
||||
"firstLoadInstructions": "第一次加载大概需要5到10分钟...并且不需要网络。\n\n正常情况下,加载完成后软件会自动跳转到图形界面。\n\n在图形界面时:\n- 点击为鼠标左键\n- 长按为鼠标右键\n- 双指点击可弹出键盘\n- 双指划动为鼠标滚轮\n\n请不要在安装时退出软件。\n\n在等待时,可以点击下面的按钮申请一下权限。\n\n小小电脑的许多文件夹,比如下载、文档、图片等等都和设备的对应文件夹绑定,如果不授予这些权限会导致这些文件夹无权访问。\n\n但如果你不需要访问这些文件夹,也可以不授予文件权限(可能导致火狐浏览器下载文件失败,因为无权访问下载文件夹)。",
|
||||
"updateRequest": "请尽量使用最新版本。前往项目地址可查看最新版本。",
|
||||
"avncScreenResize": "自适应屏幕尺寸",
|
||||
"avncResizeFactor": "屏幕缩放比",
|
||||
"avncResizeFactorValue": "当前缩放为",
|
||||
"microphoneSupport": "麦克风支持",
|
||||
"startStreaming": "开启麦克风"
|
||||
}
|
||||
@@ -31,9 +31,8 @@
|
||||
"reinstallBootPackage": "重新安裝啟動套件",
|
||||
"getifaddrsBridge": "getifaddrs 橋接",
|
||||
"fixGetifaddrsPermission": "修復 Android 13 裝置 getifaddrs 無權限",
|
||||
"fakeUOSSystem": "偽裝系統為 UOS",
|
||||
"displaySettings": "顯示設定",
|
||||
"avncAdvantages": "AVNC 可帶來比 noVNC 更好的操作體驗;如觸控板觸控、雙指單擊喚出鍵盤、自動剪貼簿、畫中畫模式等。這是一個實驗性功能。",
|
||||
"avncAdvantages": "AVNC 可帶來比 noVNC 更好的操作體驗;如觸控板觸控、雙指單擊喚出鍵盤、自動剪貼簿、畫中畫模式等。",
|
||||
"avncSettings": "AVNC 設定",
|
||||
"aboutAVNC": "關於 AVNC",
|
||||
"avncResolution": "AVNC 啟動時解析度設定",
|
||||
@@ -44,11 +43,11 @@
|
||||
"applyOnNextLaunch": "下次啟動時生效",
|
||||
"save": "儲存",
|
||||
"useAVNCByDefault": "預設使用 AVNC",
|
||||
"termuxX11Advantages": "Termux:X11 可帶來比 VNC 更快的速度,有時兼容性也更好。\n支援使用 DRI3(需在圖形加速中開啟),可大幅提升效能。",
|
||||
"termuxX11Advantages": "Termux:X11 在某些情況下可能比 VNC 提供更快的速度。\n\n請注意 Termux:X11 的操作與 AVNC 略有不同:\n- 雙指點擊相當於滑鼠右鍵\n- 返回鍵可彈出小鍵盤\n若出現黑屏,請嘗試完全關閉本應用後重新啟動。",
|
||||
"termuxX11Preferences": "Termux:X11 偏好設定",
|
||||
"useTermuxX11ByDefault": "預設使用 Termux:X11",
|
||||
"disableVNC": "停用 VNC。重啟生效",
|
||||
"hidpiAdvantages": "高解析度支援可讓有高解析度螢幕的裝置獲得更清晰的體驗!\n\n注意:\n啟用後顯示會變很大,請設定合適的解析度。",
|
||||
"hidpiAdvantages": "一鍵開啟高清模式,顯示更清晰的同時...速度會變慢。",
|
||||
"hidpiEnvVar": "HiDPI 環境變數",
|
||||
"hidpiSupport": "高解析度支援",
|
||||
"fileAccess": "檔案存取",
|
||||
@@ -70,16 +69,16 @@
|
||||
"dri3Requirement": "DRI3 必須配合 Termux:X11 與 Turnip 使用",
|
||||
"windowsAppSupport": "Windows 應用支援",
|
||||
"hangoverDescription": "使用 Hangover(於原生 Wine 執行跨架構應用)來執行 Windows 應用!\n\n執行 Windows 程式需經過架構與系統雙層模擬,請勿對速度抱太大期望。",
|
||||
"installHangoverStable": "安裝 Hangover 穩定版",
|
||||
"installHangoverStable": "安裝 Hangover",
|
||||
"installHangoverLatest": "安裝 Hangover 最新版(可能有錯誤)",
|
||||
"uninstallHangover": "解除安裝 Hangover",
|
||||
"clearWineData": "清除 Wine 資料",
|
||||
"wineCommandsHint": "Wine 常用指令。點擊後進入圖形介面,請耐心等候。\n\n註:DXVK預設為開啟狀態,如程式崩潰可嘗試關閉\n\n不同裝置啟動程式參考時間:\n虎賁 T7510 6GB 超過一分鐘\n驍龍 870 12GB 約 10 秒\n",
|
||||
"wineCommandsHint": "Wine 常用指令。點擊後進入圖形介面,請耐心等候。\n\n不同裝置啟動程式參考時間:\n虎賁 T7510 6GB 超過一分鐘\n驍龍 870 12GB 約 10 秒\n",
|
||||
"switchToJapanese": "切換系統為日語",
|
||||
"userManual": "使用說明",
|
||||
"openSourceLicenses": "開源授權",
|
||||
"permissionUsage": "權限使用說明",
|
||||
"privacyStatement": "\n本軟體不會收集你的隱私資訊。\n\n當然,你在容器系統內安裝或使用的軟體行為(包括快捷指令)不在本軟體控制範圍。",
|
||||
"privacyStatement": "\n本軟體不會收集你的隱私資訊。\n\n當然,你在容器系統內安裝或使用的軟體行為(包括快捷指令)不在本軟體控制範圍。\n\n本軟體申請的權限用於以下目的:\n檔案相關權限:用於系統存取手機目錄;\n通知和無障礙:Termux:X11 需要。\n麥克風權限:用於使用麥克風。",
|
||||
"supportAuthor": "支持作者",
|
||||
"recommendApp": "如果覺得好用,可以推薦給其他人哦!",
|
||||
"projectUrl": "專案網址",
|
||||
@@ -106,6 +105,11 @@
|
||||
"faqUrl": "常見問題",
|
||||
"solutionUrl": "操作指南",
|
||||
"discussionUrl": "討論",
|
||||
"firstLoadInstructions": "第一次載入大概需要5到10分鐘...\n\n正常情況下,載入完成後軟體會自動跳轉到圖形介面。\n\n在圖形介面時:\n- 點擊為滑鼠左鍵\n- 長按為滑鼠右鍵\n- 雙指點擊可彈出鍵盤\n- 雙指滑動為滑鼠滾輪\n\n請不要在安裝時退出軟體。\n\n在等待時,可以點擊下面的按鈕申請權限。\n\n小小電腦的許多資料夾,比如下載、文件、圖片等等都和裝置的這些資料夾綁定,如果不授予這些權限會導致這些資料夾無權存取。\n\n但如果你不需要存取這些資料夾,也可以不授予檔案權限(可能導致火狐瀏覽器下載檔案失敗,因為無權存取下載資料夾)。",
|
||||
"updateRequest": "請盡量使用最新版本。前往專案網址查看最新版本。"
|
||||
"firstLoadInstructions": "第一次加載大概需要5到10分鐘...並且不需要網絡。\n\n正常情況下,載入完成後軟體會自動跳轉到圖形介面。\n\n在圖形介面時:\n- 點擊為滑鼠左鍵\n- 長按為滑鼠右鍵\n- 雙指點擊可彈出鍵盤\n- 雙指滑動為滑鼠滾輪\n\n請不要在安裝時退出軟體。\n\n在等待時,可以點擊下面的按鈕申請權限。\n\n小小電腦的許多資料夾,比如下載、文件、圖片等等都和裝置的這些資料夾綁定,如果不授予這些權限會導致這些資料夾無權存取。\n\n但如果你不需要存取這些資料夾,也可以不授予檔案權限(可能導致火狐瀏覽器下載檔案失敗,因為無權存取下載資料夾)。",
|
||||
"updateRequest": "請盡量使用最新版本。前往專案網址查看最新版本。",
|
||||
"avncScreenResize": "自適應螢幕尺寸",
|
||||
"avncResizeFactor": "螢幕縮放比",
|
||||
"avncResizeFactorValue": "目前縮放為",
|
||||
"microphoneSupport": "麥克風支援",
|
||||
"startStreaming": "開啟麥克風"
|
||||
}
|
||||
|
||||
180
lib/main.dart
180
lib/main.dart
@@ -170,7 +170,9 @@ class SettingPage extends StatefulWidget {
|
||||
|
||||
class _SettingPageState extends State<SettingPage> {
|
||||
|
||||
final List<bool> _expandState = [false, false, false, false, false, false];
|
||||
final List<bool> _expandState = [false, false, false, false, false, false, false];
|
||||
|
||||
double _avncScaleFactor = Util.getGlobal("avncScaleFactor") as double;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -195,7 +197,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
Navigator.of(context).pop();
|
||||
}, child: Text(AppLocalizations.of(context)!.cancel)),
|
||||
TextButton(onPressed:() async {
|
||||
await Util.setCurrentProp("boot", D.boot);
|
||||
await Util.setCurrentProp("boot", Localizations.localeOf(context).languageCode == 'zh' ? D.boot : D.boot.replaceFirst('LANG=zh_CN.UTF-8', 'LANG=en_US.UTF-8').replaceFirst('公共', 'Public').replaceFirst('图片', 'Pictures').replaceFirst('音乐', 'Music').replaceFirst('视频', 'Videos').replaceFirst('下载', 'Downloads').replaceFirst('文档', 'Documents').replaceFirst('照片', 'Photos'));
|
||||
G.bootTextChange.value = !G.bootTextChange.value;
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
@@ -326,17 +328,33 @@ class _SettingPageState extends State<SettingPage> {
|
||||
G.prefs.setBool("getifaddrsBridge", value);
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 8),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.fakeUOSSystem), value: Util.getGlobal("uos") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("uos", value);
|
||||
setState(() {});
|
||||
},),
|
||||
],))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[2],
|
||||
headerBuilder: ((context, isExpanded) {
|
||||
return ListTile(title: Text(AppLocalizations.of(context)!.displaySettings));
|
||||
}), body: Padding(padding: const EdgeInsets.all(12), child: Column(children: [
|
||||
const SizedBox.square(dimension: 16),
|
||||
Text(AppLocalizations.of(context)!.hidpiAdvantages),
|
||||
const SizedBox.square(dimension: 16),
|
||||
TextFormField(maxLines: null, initialValue: Util.getGlobal("defaultHidpiOpt") as String, decoration: InputDecoration(border: OutlineInputBorder(), labelText: AppLocalizations.of(context)!.hidpiEnvVar),
|
||||
onChanged: (value) async {
|
||||
await G.prefs.setString("defaultHidpiOpt", value);
|
||||
},
|
||||
),
|
||||
const SizedBox.square(dimension: 8),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.hidpiSupport), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("isHidpiEnabled") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("isHidpiEnabled", value);
|
||||
// 开启高分辨率后把缩放比调为原来的两倍 +log4(2) = 0.5
|
||||
_avncScaleFactor += value ? 0.5 : -0.5;
|
||||
_avncScaleFactor = _avncScaleFactor.clamp(-1, 1);
|
||||
G.prefs.setDouble("avncScaleFactor", _avncScaleFactor);
|
||||
// Termux:X11 并不是设置缩放比例本身,而是倍率
|
||||
X11Flutter.setX11ScaleFactor(value ? 0.5 : 2.0);
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 16),
|
||||
const Divider(height: 2, indent: 8, endIndent: 8),
|
||||
const SizedBox.square(dimension: 16),
|
||||
Text(AppLocalizations.of(context)!.avncAdvantages),
|
||||
const SizedBox.square(dimension: 16),
|
||||
@@ -347,7 +365,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.aboutAVNC), onPressed: () async {
|
||||
await AvncFlutter.launchAboutPage();
|
||||
}),
|
||||
OutlinedButton(style: D.commandButtonStyle, child: Text(AppLocalizations.of(context)!.avncResolution), onPressed: () async {
|
||||
OutlinedButton(style: D.commandButtonStyle, onPressed: Util.getGlobal("avncResizeDesktop") as bool ? null : () async {
|
||||
final s = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize;
|
||||
final w0 = max(s.width, s.height);
|
||||
final h0 = min(s.width, s.height);
|
||||
@@ -388,13 +406,44 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
|
||||
}, child: Text(AppLocalizations.of(context)!.save)),
|
||||
]);
|
||||
});
|
||||
}),
|
||||
}, child: Text(AppLocalizations.of(context)!.avncResolution)),
|
||||
]),
|
||||
const SizedBox.square(dimension: 8),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.useAVNCByDefault), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("useAvnc") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("useAvnc", value);
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 8),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.avncScreenResize), value: Util.getGlobal("avncResizeDesktop") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("avncResizeDesktop", value);
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 8),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.avncResizeFactor),
|
||||
onTap: () {},
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 8),
|
||||
Text('${AppLocalizations.of(context)!.avncResizeFactorValue} ${pow(4, _avncScaleFactor).toStringAsFixed(2)}x'),
|
||||
SizedBox(height: 12),
|
||||
Slider(
|
||||
value: _avncScaleFactor,
|
||||
min: -1,
|
||||
max: 1,
|
||||
divisions: 96,
|
||||
onChangeEnd: (double value) {
|
||||
G.prefs.setDouble("avncScaleFactor", value);
|
||||
},
|
||||
onChanged: Util.getGlobal("avncResizeDesktop") as bool ? (double value) {
|
||||
_avncScaleFactor = value;
|
||||
setState(() {});
|
||||
} : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox.square(dimension: 16),
|
||||
const Divider(height: 2, indent: 8, endIndent: 8),
|
||||
const SizedBox.square(dimension: 16),
|
||||
@@ -414,21 +463,6 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 16),
|
||||
const Divider(height: 2, indent: 8, endIndent: 8),
|
||||
const SizedBox.square(dimension: 16),
|
||||
Text(AppLocalizations.of(context)!.hidpiAdvantages),
|
||||
const SizedBox.square(dimension: 16),
|
||||
TextFormField(maxLines: null, initialValue: Util.getGlobal("defaultHidpiOpt") as String, decoration: InputDecoration(border: OutlineInputBorder(), labelText: AppLocalizations.of(context)!.hidpiEnvVar),
|
||||
onChanged: (value) async {
|
||||
await G.prefs.setString("defaultHidpiOpt", value);
|
||||
},
|
||||
),
|
||||
const SizedBox.square(dimension: 8),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.hidpiSupport), subtitle: Text(AppLocalizations.of(context)!.applyOnNextLaunch), value: Util.getGlobal("isHidpiEnabled") as bool, onChanged:(value) {
|
||||
G.prefs.setBool("isHidpiEnabled", value);
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 16),
|
||||
],))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[3],
|
||||
@@ -514,7 +548,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.11)"), onPressed: () async {
|
||||
OutlinedButton(style: D.commandButtonStyle, child: Text("${AppLocalizations.of(context)!.installHangoverStable}(11.0)"), onPressed: () async {
|
||||
Util.termWrite("bash ~/.local/share/tiny/extra/install-hangover-stable");
|
||||
G.pageIndex.value = 0;
|
||||
}),
|
||||
@@ -536,14 +570,24 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
|
||||
const SizedBox.square(dimension: 16),
|
||||
Text(AppLocalizations.of(context)!.wineCommandsHint),
|
||||
const SizedBox.square(dimension: 8),
|
||||
Wrap(alignment: WrapAlignment.center, spacing: 4.0, runSpacing: 4.0, children: D.wineCommands.asMap().entries.map<Widget>(
|
||||
(e) {
|
||||
return OutlinedButton(style: D.commandButtonStyle, child: Text(e.value["name"]!), onPressed: () {
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: (Localizations.localeOf(context).languageCode == 'zh'
|
||||
? D.wineCommands
|
||||
: D.wineCommands4En
|
||||
).asMap().entries.map<Widget>((e) {
|
||||
return OutlinedButton(
|
||||
style: D.commandButtonStyle,
|
||||
child: Text(e.value["name"]!),
|
||||
onPressed: () {
|
||||
Util.termWrite("${e.value["command"]!} &");
|
||||
G.pageIndex.value = 0;
|
||||
});
|
||||
}
|
||||
).toList()),
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox.square(dimension: 16),
|
||||
const Divider(height: 2, indent: 8, endIndent: 8),
|
||||
const SizedBox.square(dimension: 16),
|
||||
@@ -558,6 +602,37 @@ sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""");
|
||||
setState(() {});
|
||||
},),
|
||||
],))),
|
||||
ExpansionPanel(
|
||||
isExpanded: _expandState[6],
|
||||
headerBuilder: ((context, isExpanded) {
|
||||
return ListTile(title: Text(AppLocalizations.of(context)!.microphoneSupport), subtitle: Text(AppLocalizations.of(context)!.experimentalFeature));
|
||||
}), body: Padding(padding: const EdgeInsets.all(12), child: Column(children: [
|
||||
const SizedBox.square(dimension: 16),
|
||||
SwitchListTile(title: Text(AppLocalizations.of(context)!.startStreaming), value: G.isStreaming, onChanged:(value) async {
|
||||
if (value) {
|
||||
await Permission.microphone.request();
|
||||
if (await Permission.microphone.isGranted) {
|
||||
String path = "/tmp/android_audio";
|
||||
D.androidChannel.invokeMethod("startStreaming", {"path": "${G.dataPath}/containers/${G.currentContainer}$path"});
|
||||
Util.termWrite("""
|
||||
pactl load-module module-null-sink sink_name=AndroidSink sink_properties=device.description="Android_Audio_Stream"
|
||||
pactl load-module module-remap-source master=AndroidSink.monitor source_name=AndroidMic source_properties=device.description="Android_Virtual_Mic"
|
||||
pkill -f tiny_virtual_mic
|
||||
tiny_virtual_mic $path AndroidSink &""");
|
||||
G.pageIndex.value = 0;
|
||||
}
|
||||
} else {
|
||||
Util.termWrite("""
|
||||
pactl list short modules | grep "Android" | cut -f1 | xargs -L1 pactl unload-module
|
||||
pkill -f tiny_virtual_mic""");
|
||||
G.pageIndex.value = 0;
|
||||
D.androidChannel.invokeMethod("stopStreaming", {});
|
||||
}
|
||||
G.isStreaming = value;
|
||||
setState(() {});
|
||||
},),
|
||||
const SizedBox.square(dimension: 16),
|
||||
],))),
|
||||
],);
|
||||
}
|
||||
}
|
||||
@@ -1130,7 +1205,7 @@ class TerminalPage extends StatelessWidget {
|
||||
}, child: Text(D.termCommands[index]["name"]! as String));
|
||||
}, separatorBuilder:(context, index) {
|
||||
return const SizedBox.square(dimension: 4);
|
||||
}, itemCount: D.termCommands.length))), SizedBox.fromSize(size: const Size(72, 0))])):const SizedBox.square(dimension: 0);
|
||||
}, itemCount: D.termCommands.length)))])):const SizedBox.square(dimension: 0);
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -1220,7 +1295,7 @@ class _FastCommandsState extends State<FastCommands> {
|
||||
Navigator.of(context).pop();
|
||||
}, child: Text(AppLocalizations.of(context)!.cancel)),
|
||||
TextButton(onPressed:() async {
|
||||
await Util.setCurrentProp("commands", D.commands);
|
||||
await Util.setCurrentProp("commands", Localizations.localeOf(context).languageCode == 'zh' ? D.commands : D.commands4En);
|
||||
setState(() {});
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
@@ -1247,7 +1322,9 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(Duration.zero,() {
|
||||
_initializeWorkflow();
|
||||
});
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: []);
|
||||
}
|
||||
|
||||
@@ -1267,6 +1344,22 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isLoadingComplete ? Util.getCurrentProp("name") : widget.title),
|
||||
actions: [
|
||||
Visibility(
|
||||
visible: isLoadingComplete,
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: () {
|
||||
if (G.wasX11Enabled) {
|
||||
Workflow.launchX11();
|
||||
} else if (G.wasAvncEnabled) {
|
||||
Workflow.launchAvnc();
|
||||
} else {
|
||||
Workflow.launchBrowser();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.play_arrow),
|
||||
tooltip: AppLocalizations.of(context)!.enterGUI))
|
||||
],
|
||||
),
|
||||
body: isLoadingComplete
|
||||
? ValueListenableBuilder(
|
||||
@@ -1336,27 +1429,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
);
|
||||
},
|
||||
),
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,13 @@ class Util {
|
||||
//String defaultVirglOpt 默认virgl环境变量
|
||||
//bool reinstallBootstrap = false 下次启动是否重装引导包
|
||||
//bool getifaddrsBridge = false 下次启动是否桥接getifaddrs
|
||||
//bool uos = false 下次启动是否伪装UOS
|
||||
//bool virgl = false 下次启动是否启用virgl
|
||||
//bool wakelock = false 屏幕常亮
|
||||
//bool isHidpiEnabled = false 是否开启高分辨率
|
||||
//bool isJpEnabled = false 是否切换系统到日语
|
||||
//bool useAvnc = false 是否默认使用AVNC
|
||||
//bool avncResizeDesktop = true 是否默认AVNC按当前屏幕大小调整分辨率
|
||||
//double avncScaleFactor = -0.5 AVNC:在当前屏幕大小的基础上调整缩放的比例。范围-1~1,对应比例4^-1~4^1
|
||||
//String defaultHidpiOpt 默认HiDPI环境变量
|
||||
//? int bootstrapVersion: 启动包版本
|
||||
//String[] containersInfo: 所有容器信息(json)
|
||||
@@ -112,7 +113,6 @@ class Util {
|
||||
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 "uos" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "virgl" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "turnip" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "dri3" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
@@ -120,8 +120,9 @@ class Util {
|
||||
case "isHidpiEnabled" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "isJpEnabled" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(false);
|
||||
case "useAvnc" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true);
|
||||
case "avncResizeDesktop" : return b ? G.prefs.getBool(key)! : (value){G.prefs.setBool(key, value); return value;}(true);
|
||||
case "avncScaleFactor" : return b ? G.prefs.getDouble(key)!.clamp(-1.0, 1.0) : (value){G.prefs.setDouble(key, value); return value;}(-0.5);
|
||||
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;}("--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");
|
||||
@@ -145,7 +146,7 @@ class Util {
|
||||
return m[key];
|
||||
}
|
||||
switch (key) {
|
||||
case "name" : return (value){addCurrentProp(key, value); return value;}("Debian Bookworm");
|
||||
case "name" : return (value){addCurrentProp(key, value); return value;}("Debian Trixie");
|
||||
case "boot" : return (value){addCurrentProp(key, value); return value;}(D.boot);
|
||||
case "vnc" : return (value){addCurrentProp(key, value); return value;}("startnovnc &");
|
||||
case "vncUrl" : return (value){addCurrentProp(key, value); return value;}("http://localhost:36082/vnc.html?host=localhost&port=36082&autoconnect=true&resize=remote&password=12345678");
|
||||
@@ -208,10 +209,10 @@ class Util {
|
||||
|
||||
while (true) {
|
||||
bool isReady = await isXServerReady(host, port);
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
if (isReady) {
|
||||
return;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +353,7 @@ class D {
|
||||
];
|
||||
|
||||
//默认快捷指令
|
||||
static const commands = [{"name":"检查更新并升级", "command":"sudo dpkg --configure -a && sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo localedef -c -i zh_CN -f UTF-8 zh_CN.UTF-8"},
|
||||
static const commands = [{"name":"检查更新并升级", "command":"sudo dpkg --configure -a && sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y"},
|
||||
{"name":"查看系统信息", "command":"neofetch -L && neofetch --off"},
|
||||
{"name":"清屏", "command":"clear"},
|
||||
{"name":"中断任务", "command":"\x03"},
|
||||
@@ -363,15 +364,15 @@ class D {
|
||||
{"name":"安装科学计算软件Octave", "command":"sudo apt update && sudo apt install -y octave"},
|
||||
{"name":"卸载Octave", "command":"sudo apt autoremove --purge -y octave"},
|
||||
{"name":"安装WPS", "command":r"""cat << 'EOF' | sh && sudo dpkg --configure -a && sudo apt update && sudo apt install -y /tmp/wps.deb
|
||||
wget https://github.akams.cn/https://github.com/tiny-computer/third-party-archives/releases/download/archives/wps-office_11.1.0.11720_arm64.deb -O /tmp/wps.deb
|
||||
wget https://mirrors.sdu.edu.cn/spark-store/arm64-store/office/wps-office/wps-office_11.1.0.11720-fix3_arm64.deb -O /tmp/wps.deb
|
||||
EOF
|
||||
rm /tmp/wps.deb"""},
|
||||
{"name":"卸载WPS", "command":"sudo apt autoremove --purge -y wps-office"},
|
||||
{"name":"安装CAJViewer", "command":"wget https://download.cnki.net/net.cnki.cajviewer_1.3.20-1_arm64.deb -O /tmp/caj.deb && sudo apt update && sudo apt install -y /tmp/caj.deb && bash /home/tiny/.local/share/tiny/caj/postinst; rm /tmp/caj.deb"},
|
||||
{"name":"卸载CAJViewer", "command":"sudo apt autoremove --purge -y net.cnki.cajviewer && bash /home/tiny/.local/share/tiny/caj/postrm"},
|
||||
{"name":"安装CAJViewer", "command":"wget https://download.cnki.net/cajPackage/tongxinUOS/signed_cajviewer_9.5.0-25268_arm64.deb -O /tmp/caj.deb && sudo apt update && sudo apt install -y /tmp/caj.deb; rm /tmp/caj.deb"},
|
||||
{"name":"卸载CAJViewer", "command":"sudo apt autoremove --purge -y cajviewer"},
|
||||
{"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 -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":"""wget \$(curl -s https://cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/linuxConfig.js | 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"},
|
||||
@@ -383,6 +384,30 @@ rm /tmp/wps.deb"""},
|
||||
{"name":"???", "command":"timeout 8 cmatrix"}
|
||||
];
|
||||
|
||||
//默认快捷指令,英文版本
|
||||
static const commands4En = [{"name":"Update Packages", "command":"sudo dpkg --configure -a && sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y"},
|
||||
{"name":"System Info", "command":"neofetch -L && neofetch --off"},
|
||||
{"name":"Clear", "command":"clear"},
|
||||
{"name":"Interrupt", "command":"\x03"},
|
||||
{"name":"Install Painting Program Krita", "command":"sudo apt update && sudo apt install -y krita krita-l10n"},
|
||||
{"name":"Uninstall Krita", "command":"sudo apt autoremove --purge -y krita krita-l10n"},
|
||||
{"name":"Install KDE Non-Linear Video Editor", "command":"sudo apt update && sudo apt install -y kdenlive"},
|
||||
{"name":"Uninstall Kdenlive", "command":"sudo apt autoremove --purge -y kdenlive"},
|
||||
{"name":"Install LibreOffice", "command":"sudo apt update && sudo apt install -y libreoffice"},
|
||||
{"name":"Uninstall LibreOffice", "command":"sudo apt autoremove --purge -y libreoffice"},
|
||||
{"name":"Install WPS", "command":r"""cat << 'EOF' | sh && sudo dpkg --configure -a && sudo apt update && sudo apt install -y /tmp/wps.deb
|
||||
wget https://github.com/tiny-computer/third-party-archives/releases/download/archives/wps-office_11.1.0.11720_arm64.deb -O /tmp/wps.deb
|
||||
EOF
|
||||
rm /tmp/wps.deb"""},
|
||||
{"name":"Uninstall WPS", "command":"sudo apt autoremove --purge -y wps-office"},
|
||||
{"name":"Install EdrawMax", "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 && sudo sed -i 's/<Language V="cn"\\/>/<Language V="en"\\/>/g' /opt/apps/edrawmax/config/settings.xml; rm /tmp/edraw.deb"""},
|
||||
{"name":"Uninstall EdrawMax", "command":"sudo apt autoremove --purge -y edrawmax libldap-2.4-2"},
|
||||
{"name":"Enable Recycle Bin", "command":"sudo apt update && sudo apt install -y gvfs && echo 'Restart the app to use Recycle Bin.'"},
|
||||
{"name":"Clean Package Cache", "command":"sudo apt clean"},
|
||||
{"name":"Power Off", "command":"stopvnc\nexit\nexit"},
|
||||
{"name":"???", "command":"timeout 8 cmatrix"}
|
||||
];
|
||||
|
||||
//默认wine快捷指令
|
||||
static const wineCommands = [{"name":"Wine配置", "command":"winecfg"},
|
||||
{"name":"修复方块字", "command":"regedit Z:\\\\home\\\\tiny\\\\.local\\\\share\\\\tiny\\\\extra\\\\chn_fonts.reg && wine reg delete \"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes\" /va /f"},
|
||||
@@ -408,6 +433,31 @@ WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_C
|
||||
{"name":"强制关闭Wine", "command":"wineserver -k"}
|
||||
];
|
||||
|
||||
//默认wine快捷指令,英文版本
|
||||
static const wineCommands4En = [{"name":"Wine Configuration", "command":"winecfg"},
|
||||
{"name":"Fix CJK Characters", "command":"regedit Z:\\\\home\\\\tiny\\\\.local\\\\share\\\\tiny\\\\extra\\\\chn_fonts.reg && wine reg delete \"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes\" /va /f"},
|
||||
{"name":"Start Menu Dir", "command":"wine explorer \"C:\\\\ProgramData\\\\Microsoft\\\\Windows\\\\Start Menu\\\\Programs\""},
|
||||
{"name":"Enable DXVK", "command":"""WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d8 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d9 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d10core /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d11 /d native /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=n,d3d9=n,d3d10core=n,d3d11=n,dxgi=n" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v dxgi /d native /f >/dev/null 2>&1"""},
|
||||
{"name":"Disable DXVK", "command":"""WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d8 /d builtin /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d9 /d builtin /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d10core /d builtin /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v d3d11 /d builtin /f >/dev/null 2>&1
|
||||
WINEDLLOVERRIDES="d3d8=b,d3d9=b,d3d10core=b,d3d11=b,dxgi=b" wine reg add 'HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides' /v dxgi /d builtin /f >/dev/null 2>&1"""},
|
||||
{"name":"Explorer", "command":"wine explorer"},
|
||||
{"name":"Notepad", "command":"notepad"},
|
||||
{"name":"Minesweeper", "command":"winemine"},
|
||||
{"name":"Regedit", "command":"regedit"},
|
||||
{"name":"Control Panel", "command":"wine control"},
|
||||
{"name":"File Manager", "command":"winefile"},
|
||||
{"name":"Task Manager", "command":"wine taskmgr"},
|
||||
{"name":"Internet Explorer", "command":"wine iexplore"},
|
||||
{"name":"Kill Wine Process", "command":"wineserver -k"}
|
||||
];
|
||||
|
||||
//默认小键盘
|
||||
static const termCommands = [
|
||||
{"name": "Esc", "key": TerminalKey.escape},
|
||||
@@ -467,11 +517,7 @@ class G {
|
||||
static late VirtualKeyboard keyboard; //存储ctrl, shift, alt状态
|
||||
static bool maybeCtrlJ = false; //为了区分按下的ctrl+J和enter而准备的变量
|
||||
static ValueNotifier<double> termFontScale = ValueNotifier(1); //终端字体大小,存储为G.prefs的termFontScale
|
||||
static bool isStreamServerStarted = false;
|
||||
static bool isStreaming = false;
|
||||
//static int? streamingPid;
|
||||
static String streamingOutput = "";
|
||||
static late Pty streamServerPty;
|
||||
//static int? virglPid;
|
||||
static ValueNotifier<int> pageIndex = ValueNotifier(0); //主界面索引
|
||||
static ValueNotifier<bool> terminalPageChange = ValueNotifier(true); //更改值,用于刷新小键盘
|
||||
@@ -482,7 +528,6 @@ class G {
|
||||
static bool wasAvncEnabled = false;
|
||||
static bool wasX11Enabled = false;
|
||||
|
||||
|
||||
static late SharedPreferences prefs;
|
||||
}
|
||||
|
||||
@@ -534,7 +579,12 @@ 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/libexec_pulseaudio.so \$DATA_DIR/bin/pulseaudio
|
||||
ln -sf ../applib/libbusybox.so \$DATA_DIR/lib/libbusybox.so.1.36.1
|
||||
ln -sf ../applib/libacl.so \$DATA_DIR/lib/libacl.so
|
||||
ln -sf ../applib/libandroid-selinux.so \$DATA_DIR/lib/libandroid-selinux.so
|
||||
ln -sf ../applib/libattr.so \$DATA_DIR/lib/libattr.so
|
||||
ln -sf ../applib/libbusybox.so \$DATA_DIR/lib/libbusybox.so.1.37.0
|
||||
ln -sf ../applib/libiconv.so \$DATA_DIR/lib/libiconv.so
|
||||
ln -sf ../applib/libpcre2-8.so \$DATA_DIR/lib/libpcre2-8.so
|
||||
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
|
||||
@@ -553,19 +603,26 @@ chmod 1777 tmp
|
||||
//初次启动要做的事情
|
||||
static Future<void> initForFirstTime() async {
|
||||
//首先设置bootstrap
|
||||
G.updateText.value = "正在安装引导包";
|
||||
G.updateText.value = AppLocalizations.of(G.homePageStateContext)!.installingBootPackage;
|
||||
await setupBootstrap();
|
||||
|
||||
G.updateText.value = "正在复制容器系统";
|
||||
G.updateText.value = AppLocalizations.of(G.homePageStateContext)!.copyingContainerSystem;
|
||||
//存放容器的文件夹0和存放硬链接的文件夹.l2s
|
||||
Util.createDirFromString("${G.dataPath}/containers/0/.l2s");
|
||||
//这个是容器rootfs,被split命令分成了xa*,放在assets里
|
||||
//首次启动,就用这个,别让用户另选了
|
||||
for (String name in jsonDecode(await rootBundle.loadString('AssetManifest.json')).keys.where((String e) => e.startsWith("assets/xa")).map((String e) => e.split("/").last).toList()) {
|
||||
//使用 AssetManifest API 获取 assets/xa* 文件列表
|
||||
final AssetManifest manifest = await AssetManifest.loadFromAssetBundle(rootBundle);
|
||||
final List<String> xaFiles = manifest
|
||||
.listAssets()
|
||||
.where((String key) => key.startsWith('assets/xa'))
|
||||
.map((String key) => key.split('/').last)
|
||||
.toList();
|
||||
for (String name in xaFiles) {
|
||||
await Util.copyAsset("assets/$name", "${G.dataPath}/$name");
|
||||
}
|
||||
//-J
|
||||
G.updateText.value = "正在安装容器系统";
|
||||
G.updateText.value = AppLocalizations.of(G.homePageStateContext)!.installingContainerSystem;
|
||||
await Util.execute(
|
||||
"""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
@@ -595,18 +652,19 @@ cat tmp3 | while read -r group_name group_id; do
|
||||
fi
|
||||
done
|
||||
\$DATA_DIR/bin/busybox rm -rf xa* tmp1 tmp2 tmp3
|
||||
${Localizations.localeOf(G.homePageStateContext).languageCode == 'zh' ? "" : "echo 'LANG=en_US.UTF-8' > \$CONTAINER_DIR/usr/local/etc/tmoe-linux/locale.txt"}
|
||||
""");
|
||||
//一些数据初始化
|
||||
//$DATA_DIR是数据文件夹, $CONTAINER_DIR是容器根目录
|
||||
//Termux:X11的启动命令并不在这里面,而是写死了。这下成💩山代码了:P
|
||||
await G.prefs.setStringList("containersInfo", ["""{
|
||||
"name":"Debian Bookworm",
|
||||
"boot":"${D.boot}",
|
||||
"name":"Debian Trixie",
|
||||
"boot":"${Localizations.localeOf(G.homePageStateContext).languageCode == 'zh' ? D.boot : D.boot.replaceFirst('LANG=zh_CN.UTF-8', 'LANG=en_US.UTF-8').replaceFirst('公共', 'Public').replaceFirst('图片', 'Pictures').replaceFirst('音乐', 'Music').replaceFirst('视频', 'Videos').replaceFirst('下载', 'Downloads').replaceFirst('文档', 'Documents').replaceFirst('照片', 'Photos')}",
|
||||
"vnc":"startnovnc &",
|
||||
"vncUrl":"http://localhost:36082/vnc.html?host=localhost&port=36082&autoconnect=true&resize=remote&password=12345678",
|
||||
"commands":${jsonEncode(D.commands)}
|
||||
"commands":${jsonEncode(Localizations.localeOf(G.homePageStateContext).languageCode == 'zh' ? D.commands : D.commands4En)}
|
||||
}"""]);
|
||||
G.updateText.value = "安装完成";
|
||||
G.updateText.value = AppLocalizations.of(G.homePageStateContext)!.installationComplete;
|
||||
}
|
||||
|
||||
static Future<void> initData() async {
|
||||
@@ -630,13 +688,21 @@ done
|
||||
final String h = (min(s.width, s.height) * 0.75).round().toString();
|
||||
G.postCommand = """sed -i -E "s@(geometry)=.*@\\1=${w}x${h}@" /etc/tigervnc/vncserver-config-tmoe
|
||||
sed -i -E "s@^(VNC_RESOLUTION)=.*@\\1=${w}x${h}@" \$(command -v startvnc)""";
|
||||
if (Localizations.localeOf(G.homePageStateContext).languageCode != 'zh') {
|
||||
G.postCommand += "\nlocaledef -c -i en_US -f UTF-8 en_US.UTF-8";
|
||||
// For English users, assume they need to enable terminal write
|
||||
await G.prefs.setBool("isTerminalWriteEnabled", true);
|
||||
await G.prefs.setBool("isTerminalCommandsEnabled", true);
|
||||
await G.prefs.setBool("isStickyKey", false);
|
||||
await G.prefs.setBool("wakelock", true);
|
||||
}
|
||||
await G.prefs.setBool("getifaddrsBridge", (await DeviceInfoPlugin().androidInfo).version.sdkInt >= 31);
|
||||
}
|
||||
G.currentContainer = Util.getGlobal("defaultContainer") as int;
|
||||
|
||||
//是否需要重新安装引导包?
|
||||
if (Util.getGlobal("reinstallBootstrap")) {
|
||||
G.updateText.value = "正在重新安装引导包";
|
||||
G.updateText.value = AppLocalizations.of(G.homePageStateContext)!.reinstallingBootPackage;
|
||||
await setupBootstrap();
|
||||
G.prefs.setBool("reinstallBootstrap", false);
|
||||
}
|
||||
@@ -690,10 +756,6 @@ exit
|
||||
if (Util.getGlobal("isHidpiEnabled")) {
|
||||
extraOpt += "${Util.getGlobal("defaultHidpiOpt")} ";
|
||||
}
|
||||
if (Util.getGlobal("uos")) {
|
||||
extraMount += "--mount=\$DATA_DIR/tiny/wechat/uos-lsb:/etc/lsb-release --mount=\$DATA_DIR/tiny/wechat/uos-release:/usr/lib/os-release ";
|
||||
extraMount += "--mount=\$DATA_DIR/tiny/wechat/license/var/uos:/var/uos --mount=\$DATA_DIR/tiny/wechat/license/var/lib/uos-license:/var/lib/uos-license ";
|
||||
}
|
||||
if (Util.getGlobal("virgl")) {
|
||||
Util.execute("""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
@@ -714,6 +776,7 @@ ${G.dataPath}/bin/virgl_test_server ${Util.getGlobal("defaultVirglCommand")}""")
|
||||
}
|
||||
extraMount += "--mount=\$DATA_DIR/tiny/font:/usr/share/fonts/tiny ";
|
||||
extraMount += "--mount=\$DATA_DIR/tiny/extra/cmatrix:/home/tiny/.local/bin/cmatrix ";
|
||||
extraMount += "--mount=\$DATA_DIR/tiny/extra/tiny_virtual_mic:/home/tiny/.local/bin/tiny_virtual_mic ";
|
||||
Util.termWrite(
|
||||
"""
|
||||
export DATA_DIR=${G.dataPath}
|
||||
@@ -729,10 +792,14 @@ 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")):""}
|
||||
clear""");
|
||||
}
|
||||
|
||||
static Future<void> launchGUIBackend() async {
|
||||
Util.termWrite((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")):"");
|
||||
Util.termWrite("clear");
|
||||
}
|
||||
|
||||
static Future<void> waitForConnection() async {
|
||||
await retry(
|
||||
// Make a GET request
|
||||
@@ -771,11 +838,11 @@ clear""");
|
||||
}
|
||||
|
||||
static Future<void> launchAvnc() async {
|
||||
await AvncFlutter.launchUsingUri(Util.getCurrentProp("vncUri") as String);
|
||||
await AvncFlutter.launchUsingUri(Util.getCurrentProp("vncUri") as String, resizeRemoteDesktop: Util.getGlobal("avncResizeDesktop") as bool, resizeRemoteDesktopScaleFactor: pow(4, Util.getGlobal("avncScaleFactor") as double).toDouble());
|
||||
}
|
||||
|
||||
static Future<void> launchXServer() async {
|
||||
await X11Flutter.launchXServer("${G.dataPath}/containers/${G.currentContainer}/tmp", "${G.dataPath}/containers/${G.currentContainer}/usr/share/X11/xkb", [":4"]);
|
||||
await X11Flutter.launchXServer("${G.dataPath}/containers/${G.currentContainer}/tmp", "${G.dataPath}/containers/${G.currentContainer}/usr/share/X11/xkb", [":4", "-extension", "MIT-SHM"]);
|
||||
}
|
||||
|
||||
static Future<void> launchX11() async {
|
||||
@@ -791,9 +858,11 @@ clear""");
|
||||
if (Util.getGlobal("autoLaunchVnc") as bool) {
|
||||
if (G.wasX11Enabled) {
|
||||
await Util.waitForXServer();
|
||||
launchGUIBackend();
|
||||
launchX11();
|
||||
return;
|
||||
}
|
||||
launchGUIBackend();
|
||||
waitForConnection().then((value) => G.wasAvncEnabled?launchAvnc():launchBrowser());
|
||||
}
|
||||
}
|
||||
|
||||
212
pubspec.lock
212
pubspec.lock
@@ -21,8 +21,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "73f6672f955ae89606336e0a018d4958af3570e6"
|
||||
resolved-ref: "73f6672f955ae89606336e0a018d4958af3570e6"
|
||||
ref: "44f24477b85b009cf84d6c7be25a929128eed728"
|
||||
resolved-ref: "44f24477b85b009cf84d6c7be25a929128eed728"
|
||||
url: "https://github.com/tiny-computer/avnc_flutter.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
@@ -46,10 +46,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: clipboard
|
||||
sha256: "1920c0337f8808be4166c5f1b236301ff381ef69633b0757c502d97f1f740102"
|
||||
sha256: c0ba5e7214035b824549473fa134870bc544fff0d7b89170d4756c0ac2c0239d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "3.0.14"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -58,6 +58,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -74,6 +82,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -86,10 +102,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.5.0"
|
||||
version: "12.3.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -110,10 +126,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -126,10 +142,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -174,14 +190,30 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -202,34 +234,42 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
version: "6.1.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -250,18 +290,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
network_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: network_info_plus
|
||||
sha256: f926b2ba86aa0086a0dfbb9e5072089bc213d854135c1712f1d29fc89ba3c877
|
||||
sha256: "2866dadcbee2709e20d67737a1556f5675b8b0cdcf2c1659ba74bc21bffede4f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
version: "7.0.0"
|
||||
network_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -278,14 +326,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "983c7fa1501f6dcc0cb7af4e42072e9993cb28d73604d25ebf4dab08165d997e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.5"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
|
||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.3.1"
|
||||
version: "9.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -314,18 +370,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db"
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.18"
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd"
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -422,6 +478,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -442,26 +506,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
|
||||
sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.12"
|
||||
version: "2.4.20"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -543,10 +607,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -567,34 +631,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7"
|
||||
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.18"
|
||||
version: "6.3.28"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7
|
||||
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.4"
|
||||
version: "6.3.6"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
version: "3.2.2"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f
|
||||
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.3"
|
||||
version: "3.2.5"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -607,50 +671,50 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.4.2"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
version: "3.1.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
version: "15.0.2"
|
||||
wakelock_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678
|
||||
sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.4.0"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus_platform_interface
|
||||
sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207
|
||||
sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
version: "1.3.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -663,18 +727,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
|
||||
sha256: a3da219916aba44947d3a5478b1927876a09781174b5a2b67fa5be0555154bf9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.13.0"
|
||||
version: "4.13.1"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3"
|
||||
sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
version: "4.10.11"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -687,18 +751,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f
|
||||
sha256: e49f378ed066efb13fc36186bbe0bd2425630d4ea0dbc71a18fdd0e4d8ed8ebc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.23.0"
|
||||
version: "3.23.5"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.14.0"
|
||||
version: "5.15.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -711,8 +775,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "778ae0873d9bf953d1fdbbd6bc4937ccf1020bb5"
|
||||
resolved-ref: "778ae0873d9bf953d1fdbbd6bc4937ccf1020bb5"
|
||||
ref: "3b92b515b1ebc32c93e18e8b3a2983656713e4dd"
|
||||
resolved-ref: "3b92b515b1ebc32c93e18e8b3a2983656713e4dd"
|
||||
url: "https://github.com/tiny-computer/x11_flutter.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
@@ -740,6 +804,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
zmodem:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -749,5 +821,5 @@ packages:
|
||||
source: hosted
|
||||
version: "0.0.6"
|
||||
sdks:
|
||||
dart: ">=3.8.1 <4.0.0"
|
||||
flutter: ">=3.29.0"
|
||||
dart: ">=3.10.3 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
|
||||
20
pubspec.yaml
20
pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.99+20250831
|
||||
version: 1.1.0+20260205
|
||||
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
@@ -33,28 +33,28 @@ dependencies:
|
||||
xterm: ^4.0.0
|
||||
flutter_pty: ^0.4.2
|
||||
path_provider: ^2.1.5
|
||||
webview_flutter: ^4.13.0
|
||||
webview_flutter: ^4.13.1
|
||||
permission_handler: ^12.0.1
|
||||
http: ^1.5.0
|
||||
http: ^1.6.0
|
||||
retry: ^3.1.2
|
||||
url_launcher: ^6.3.2
|
||||
shared_preferences: ^2.5.3
|
||||
clipboard: ^2.0.2
|
||||
wakelock_plus: ^1.3.2
|
||||
shared_preferences: ^2.5.4
|
||||
clipboard: ^3.0.14
|
||||
wakelock_plus: ^1.4.0
|
||||
dynamic_color: ^1.8.1
|
||||
network_info_plus: ^6.1.4
|
||||
device_info_plus: ^11.5.0
|
||||
network_info_plus: ^7.0.0
|
||||
device_info_plus: ^12.3.0
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
x11_flutter:
|
||||
git:
|
||||
url: https://github.com/tiny-computer/x11_flutter.git
|
||||
ref: 778ae0873d9bf953d1fdbbd6bc4937ccf1020bb5
|
||||
ref: 3b92b515b1ebc32c93e18e8b3a2983656713e4dd
|
||||
avnc_flutter:
|
||||
git:
|
||||
url: https://github.com/tiny-computer/avnc_flutter.git
|
||||
ref: 73f6672f955ae89606336e0a018d4958af3570e6
|
||||
ref: 44f24477b85b009cf84d6c7be25a929128eed728
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
||||
Reference in New Issue
Block a user