mirror of
https://gitee.com/gfdgd-xi/deep-wine-runner
synced 2025-12-18 04:51:37 +08:00
Compare commits
32 Commits
4.0.0
...
6ca3cea617
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ca3cea617 | |||
| 9fed1005dd | |||
| 24131b0050 | |||
| 83fd8fffd4 | |||
| b6f8145fa5 | |||
| 1d59f6b4f1 | |||
| ab31fc6d65 | |||
| c88521a3e7 | |||
| 385348a7b9 | |||
| 3bcff8dd87 | |||
| 0623b95fca | |||
| fbd14710a5 | |||
| 9a212ca212 | |||
| ee6b166dd7 | |||
| fcd4e5e610 | |||
| dd4785d8e2 | |||
| e6df6addd7 | |||
| 9e1e898da4 | |||
| ff60f69565 | |||
| c5577568c9 | |||
| eb6fe87485 | |||
| e9f6847e57 | |||
| 47f1b2a843 | |||
| d95a4cd9f4 | |||
| e5ffd06b26 | |||
| f327216855 | |||
| d335a72aee | |||
| ee8be07185 | |||
| 4f08e44ca3 | |||
| caa08cfa8d | |||
| a4ab3b27d1 | |||
| 4caf1a3be5 |
33
.github/workflows/auto-building-pkg.yml
vendored
Normal file
33
.github/workflows/auto-building-pkg.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Auto Building Wine Runner(pkg)
|
||||||
|
run-name: ${{ github.actor }} Auto Building Wine Runner(pkg) 🚀
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
Explore-GitHub-Actions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: archlinux
|
||||||
|
steps:
|
||||||
|
- name: Building PKG
|
||||||
|
env:
|
||||||
|
GUSER: ${{ secrets.GUSER }}
|
||||||
|
PASSWORD: ${{ secrets.PASSWORD }}
|
||||||
|
UPLOADURL: ${{ secrets.UPLOADURL }}
|
||||||
|
run: |
|
||||||
|
# 配置环境
|
||||||
|
pacman -Sy
|
||||||
|
pacman -S yay git sudo
|
||||||
|
pacman -S dpkg qt5-base -y
|
||||||
|
yay install
|
||||||
|
git clone https://github.com/gfdgd-xi/deep-wine-runner
|
||||||
|
cd deep-wine-runner
|
||||||
|
make package-deb -j4
|
||||||
|
make package-pkg -j4
|
||||||
|
cd ..
|
||||||
|
mv spark-deepin-wine-runner*.pkg.tar.zst ../spark-deepin-wine-runner.pkg.tar.zst
|
||||||
|
- name: upload result
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: spark-deepin-wine-runner.pkg.tar.zst
|
||||||
|
path: spark-deepin-wine-runner.pkg.tar.zst
|
||||||
|
|
||||||
|
|
||||||
18
.github/workflows/auto-building.yml
vendored
18
.github/workflows/auto-building.yml
vendored
@@ -8,10 +8,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Building DEB
|
- name: Building DEB
|
||||||
env:
|
|
||||||
GUSER: ${{ secrets.GUSER }}
|
|
||||||
PASSWORD: ${{ secrets.PASSWORD }}
|
|
||||||
UPLOADURL: ${{ secrets.UPLOADURL }}
|
|
||||||
run: |
|
run: |
|
||||||
# 配置环境
|
# 配置环境
|
||||||
sudo apt update
|
sudo apt update
|
||||||
@@ -22,14 +18,24 @@ jobs:
|
|||||||
mv spark-deepin-wine-runner.deb ~
|
mv spark-deepin-wine-runner.deb ~
|
||||||
mv spark-deepin-wine-runner-ace.deb ~
|
mv spark-deepin-wine-runner-ace.deb ~
|
||||||
- name: upload result
|
- name: upload result
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: spark-deepin-wine-runner.deb
|
name: spark-deepin-wine-runner.deb
|
||||||
path: /home/runner/spark-deepin-wine-runner.deb
|
path: /home/runner/spark-deepin-wine-runner.deb
|
||||||
- name: upload result
|
- name: upload result
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: spark-deepin-wine-runner-ace.deb
|
name: spark-deepin-wine-runner-ace.deb
|
||||||
path: /home/runner/spark-deepin-wine-runner-ace.deb
|
path: /home/runner/spark-deepin-wine-runner-ace.deb
|
||||||
|
- name: Building DEB (termux)
|
||||||
|
run: |
|
||||||
|
cd deep-wine-runner
|
||||||
|
make package-deb-termux -j4
|
||||||
|
mv spark-deepin-wine-runner-termux.deb ~
|
||||||
|
- name: upload result
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: spark-deepin-wine-runner-termux.deb
|
||||||
|
path: /home/runner/spark-deepin-wine-runner-termux.deb
|
||||||
|
|
||||||
|
|
||||||
76
.github/workflows/building-off-line-package-arm.yml
vendored
Normal file
76
.github/workflows/building-off-line-package-arm.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
name: Building Wine Runner Off-line Pages(arm64)
|
||||||
|
run-name: ${{ github.actor }} Building Wine Runner Off-line Pages(arm64) 🚀
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
Explore-GitHub-Actions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Building DEB
|
||||||
|
run: |
|
||||||
|
# 获取所需数据
|
||||||
|
cpu=$(cat /proc/cpuinfo | grep processor | wc -l)
|
||||||
|
# 配置环境
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3-requests debootstrap xz-utils qemu-user-static -y
|
||||||
|
sudo apt install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools psmisc -y
|
||||||
|
cd ~
|
||||||
|
mkdir package
|
||||||
|
# 部署 chroot 环境
|
||||||
|
sudo debootstrap --arch=arm64 bookworm debian
|
||||||
|
wget https://github.com/gfdgd-xi/deep-wine-runner/raw/main/pardus-chroot
|
||||||
|
sudo cp pardus-chroot /usr/bin
|
||||||
|
sudo chmod 777 /usr/bin/pardus-chroot
|
||||||
|
sudo pardus-chroot debian
|
||||||
|
### 配置容器
|
||||||
|
## 加入 wine 源
|
||||||
|
sudo chroot debian apt update
|
||||||
|
sudo chroot debian apt install sudo gpg wget -y
|
||||||
|
sudo chroot debian wget https://ryanfortner.github.io/box64-debs/box64.list -O /etc/apt/sources.list.d/box64.list
|
||||||
|
sudo chroot debian bash -c "wget -qO- https://ryanfortner.github.io/box64-debs/KEY.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/box64-debs-archive-keyring.gpg"
|
||||||
|
sudo chroot debian apt update
|
||||||
|
sudo chroot debian apt install box64 wine -y
|
||||||
|
## 获取 Wine 运行器安装包
|
||||||
|
git clone https://github.com/gfdgd-xi/deep-wine-runner --depth=1
|
||||||
|
cd deep-wine-runner
|
||||||
|
make package-deb
|
||||||
|
cd ..
|
||||||
|
url=`python3 deep-wine-runner/off-line-shell/GetNewestDebUrl.py`
|
||||||
|
#wget $url
|
||||||
|
mv deep-wine-runner/spark-deepin-wine-runner.deb debian/tmp/wine-runner.deb
|
||||||
|
## 安装
|
||||||
|
sudo chroot debian apt install locales /tmp/wine-runner.deb fcitx xfce4-terminal -y
|
||||||
|
sudo chroot debian apt install libxenmisc4.17 libxenstore4 libxenforeignmemory1 -y
|
||||||
|
# 构建软件包
|
||||||
|
mkdir package/runner -p
|
||||||
|
sudo cp debian/usr/local/bin package -rv
|
||||||
|
sudo cp debian/usr/bin package -rv
|
||||||
|
sudo cp debian/usr/lib package -rv
|
||||||
|
sudo cp debian/usr/share package -rv
|
||||||
|
#sudo cp debian/usr/lib64 package -rv
|
||||||
|
sudo cp debian/opt/apps/deepin-wine-runner/* package/runner -rv
|
||||||
|
# 精简运行器体积
|
||||||
|
sudo rm -rf package/runner/2048
|
||||||
|
sudo rm -rf package/runner/geek.exe
|
||||||
|
sudo rm -rf package/runner/BeCyIconGrabber.exe
|
||||||
|
sudo rm -rf package/runner/Icon
|
||||||
|
sudo rm -rf package/runner/RegShot.exe
|
||||||
|
sudo rm -rf package/runner/novnc
|
||||||
|
sudo rm -rf package/bin/wine*
|
||||||
|
cp deep-wine-runner/off-line-shell/run.sh package -rv
|
||||||
|
cp deep-wine-runner/off-line-shell/bwrap_arm64 package/bwrap -rv
|
||||||
|
sudo chmod 777 -Rv package ; true
|
||||||
|
cd package
|
||||||
|
# 添加 Wine 运行器离线模式标识
|
||||||
|
touch runner/off-line.lock
|
||||||
|
tar -cvf ../spark-deepin-wine-runner-off-line.tar *
|
||||||
|
cd ..
|
||||||
|
xz -T $cpu spark-deepin-wine-runner-off-line.tar
|
||||||
|
- name: upload result
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: spark-deepin-wine-runner-off-line.tar.xz
|
||||||
|
path: /home/runner/spark-deepin-wine-runner-off-line.tar.xz
|
||||||
|
|
||||||
|
|
||||||
27
.github/workflows/building-off-line-package.yml
vendored
27
.github/workflows/building-off-line-package.yml
vendored
@@ -1,16 +1,13 @@
|
|||||||
name: Building Wine Runner Off-line Pages(amd64)
|
name: Building Wine Runner Off-line Pages(amd64)
|
||||||
run-name: ${{ github.actor }} Building Wine Runner Off-line Pages(amd64) 🚀
|
run-name: ${{ github.actor }} Building Wine Runner Off-line Pages(amd64) 🚀
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
Explore-GitHub-Actions:
|
Explore-GitHub-Actions:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Building DEB
|
- name: Building DEB
|
||||||
env:
|
|
||||||
GUSER: ${{ secrets.GUSER }}
|
|
||||||
PASSWORD: ${{ secrets.PASSWORD }}
|
|
||||||
UPLOADURL: ${{ secrets.UPLOADURL }}
|
|
||||||
run: |
|
run: |
|
||||||
# 获取所需数据
|
# 获取所需数据
|
||||||
cpu=$(cat /proc/cpuinfo | grep processor | wc -l)
|
cpu=$(cat /proc/cpuinfo | grep processor | wc -l)
|
||||||
@@ -30,7 +27,7 @@ jobs:
|
|||||||
## 加入 wine 源
|
## 加入 wine 源
|
||||||
sudo chroot debian dpkg --add-architecture i386
|
sudo chroot debian dpkg --add-architecture i386
|
||||||
sudo chroot debian apt update
|
sudo chroot debian apt update
|
||||||
sudo chroot debian apt install wget -y
|
sudo chroot debian apt install sudo gpg wget -y
|
||||||
sudo chroot debian wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
|
sudo chroot debian wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
|
||||||
sudo chroot debian wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/debian/dists/bookworm/winehq-bookworm.sources
|
sudo chroot debian wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/debian/dists/bookworm/winehq-bookworm.sources
|
||||||
sudo chroot debian apt update
|
sudo chroot debian apt update
|
||||||
@@ -44,15 +41,22 @@ jobs:
|
|||||||
mv deep-wine-runner/spark-deepin-wine-runner.deb debian/tmp/wine-runner.deb
|
mv deep-wine-runner/spark-deepin-wine-runner.deb debian/tmp/wine-runner.deb
|
||||||
## 安装
|
## 安装
|
||||||
sudo chroot debian apt install locales /tmp/wine-runner.deb winehq-devel fcitx xfce4-terminal -y
|
sudo chroot debian apt install locales /tmp/wine-runner.deb winehq-devel fcitx xfce4-terminal -y
|
||||||
|
sudo chroot debian apt install libxenmisc4.17 libxenstore4 libxenforeignmemory1 -y
|
||||||
# 构建软件包
|
# 构建软件包
|
||||||
mkdir package/opt -p
|
|
||||||
mkdir package/runner -p
|
mkdir package/runner -p
|
||||||
sudo cp debian/opt/wine-devel package/opt -rv
|
|
||||||
sudo cp debian/usr/bin package -rv
|
sudo cp debian/usr/bin package -rv
|
||||||
sudo cp debian/usr/lib package -rv
|
sudo cp debian/usr/lib package -rv
|
||||||
sudo cp debian/usr/lib32 package -rv
|
sudo cp debian/usr/share package -rv
|
||||||
sudo cp debian/usr/lib64 package -rv
|
sudo cp debian/usr/lib64 package -rv
|
||||||
sudo cp debian/opt/apps/deepin-wine-runner/* package/runner -rv
|
sudo cp debian/opt/apps/deepin-wine-runner/* package/runner -rv
|
||||||
|
# 精简运行器体积
|
||||||
|
sudo rm -rf package/runner/2048
|
||||||
|
sudo rm -rf package/runner/geek.exe
|
||||||
|
sudo rm -rf package/runner/BeCyIconGrabber.exe
|
||||||
|
sudo rm -rf package/runner/Icon
|
||||||
|
sudo rm -rf package/runner/RegShot.exe
|
||||||
|
sudo rm -rf package/runner/novnc
|
||||||
|
sudo rm -rf package/bin/wine*
|
||||||
cp deep-wine-runner/off-line-shell/run.sh package -rv
|
cp deep-wine-runner/off-line-shell/run.sh package -rv
|
||||||
cp deep-wine-runner/off-line-shell/bwrap_amd64 package/bwrap -rv
|
cp deep-wine-runner/off-line-shell/bwrap_amd64 package/bwrap -rv
|
||||||
sudo chmod 777 -Rv package ; true
|
sudo chmod 777 -Rv package ; true
|
||||||
@@ -62,17 +66,10 @@ jobs:
|
|||||||
tar -cvf ../spark-deepin-wine-runner-off-line.tar *
|
tar -cvf ../spark-deepin-wine-runner-off-line.tar *
|
||||||
cd ..
|
cd ..
|
||||||
xz -T $cpu spark-deepin-wine-runner-off-line.tar
|
xz -T $cpu spark-deepin-wine-runner-off-line.tar
|
||||||
cp deep-wine-runner/off-line-shell/compression-packager.sh spark-deepin-wine-runner-off-line.sh
|
|
||||||
cat spark-deepin-wine-runner-off-line.tar.xz >> spark-deepin-wine-runner-off-line.sh
|
|
||||||
- name: upload result
|
- name: upload result
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: spark-deepin-wine-runner-off-line.tar.xz
|
name: spark-deepin-wine-runner-off-line.tar.xz
|
||||||
path: /home/runner/spark-deepin-wine-runner-off-line.tar.xz
|
path: /home/runner/spark-deepin-wine-runner-off-line.tar.xz
|
||||||
- name: upload result
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: spark-deepin-wine-runner-off-line.sh
|
|
||||||
path: /home/runner/spark-deepin-wine-runner-off-line.sh
|
|
||||||
|
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#################
|
#################
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import globalenv
|
||||||
import traceback
|
import traceback
|
||||||
import updatekiller
|
import updatekiller
|
||||||
import PyQt5.QtGui as QtGui
|
import PyQt5.QtGui as QtGui
|
||||||
@@ -101,7 +102,10 @@ homePath = os.getenv("HOME")
|
|||||||
iconPath = "{}/deepin-wine-runner.svg".format(programPath)
|
iconPath = "{}/deepin-wine-runner.svg".format(programPath)
|
||||||
#GetDesktopList(f"{homePath}/.local/share/applications")
|
#GetDesktopList(f"{homePath}/.local/share/applications")
|
||||||
#print(desktopList)
|
#print(desktopList)
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
if (__name__ == "__main__"):
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
else:
|
||||||
|
app = globalenv.get_value("app")
|
||||||
window = QtWidgets.QMainWindow()
|
window = QtWidgets.QMainWindow()
|
||||||
widget = QtWidgets.QWidget()
|
widget = QtWidgets.QWidget()
|
||||||
layout = QtWidgets.QGridLayout()
|
layout = QtWidgets.QGridLayout()
|
||||||
@@ -118,6 +122,7 @@ window.setCentralWidget(widget)
|
|||||||
window.setWindowTitle("图标管理")
|
window.setWindowTitle("图标管理")
|
||||||
window.resize(int(window.frameGeometry().width() * 1.5), int(window.frameGeometry().height() * 1.2))
|
window.resize(int(window.frameGeometry().width() * 1.5), int(window.frameGeometry().height() * 1.2))
|
||||||
window.setWindowIcon(QtGui.QIcon(f"{programPath}/deepin-wine-runner.svg"))
|
window.setWindowIcon(QtGui.QIcon(f"{programPath}/deepin-wine-runner.svg"))
|
||||||
window.show()
|
|
||||||
GetDesktopThread()
|
GetDesktopThread()
|
||||||
app.exec_()
|
if (__name__ == "__main__"):
|
||||||
|
window.show()
|
||||||
|
app.exec_()
|
||||||
@@ -8,4 +8,8 @@ import requests
|
|||||||
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
||||||
with open(f"{programPath}/information.json") as file:
|
with open(f"{programPath}/information.json") as file:
|
||||||
version = json.loads(file.read())["Version"]
|
version = json.loads(file.read())["Version"]
|
||||||
print(requests.get(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3NwYXJrLWRlZXBpbi13aW5lLXJ1bm5lci9JbnN0YWxsLnBocD9WZXJzaW9uPQ==").decode("utf-8") + version).text)
|
print(requests.get(base64.b64decode("aHR0cHM6Ly9zb3VyY2Vmb3JnZS5uZXQvcHJvamVjdHMvZGVlcC13aW5lLXJ1bm5lci13aW5lLWRvd25sb2FkL2ZpbGVzL2Rvd25sb2FkLXRpbWUv").decode("utf-8")
|
||||||
|
+ version
|
||||||
|
+ base64.b64decode("L2Rvd25sb2Fk").decode("utf-8"),
|
||||||
|
timeout=5 # timeout 设置为 5S
|
||||||
|
).text)
|
||||||
2
Makefile
2
Makefile
@@ -146,7 +146,9 @@ copy-files:
|
|||||||
cp -rv RemoveQemuUser.sh deb/opt/apps/deepin-wine-runner
|
cp -rv RemoveQemuUser.sh deb/opt/apps/deepin-wine-runner
|
||||||
cp -rv InstallBox86.sh deb/opt/apps/deepin-wine-runner
|
cp -rv InstallBox86.sh deb/opt/apps/deepin-wine-runner
|
||||||
cp -rv InstallRuntime deb/opt/apps/deepin-wine-runner
|
cp -rv InstallRuntime deb/opt/apps/deepin-wine-runner
|
||||||
|
cp -rv globalenv.py deb/opt/apps/deepin-wine-runner
|
||||||
if [[ ! -d novnc/utils/websockify ]]; then git submodule update --init --recursive ; fi
|
if [[ ! -d novnc/utils/websockify ]]; then git submodule update --init --recursive ; fi
|
||||||
|
if [[ ! -d novnc/utils/websockify ]]; then cd novnc/utils; git clone https://github.com/novnc/websockify ; fi
|
||||||
cp -rv novnc deb/opt/apps/deepin-wine-runner
|
cp -rv novnc deb/opt/apps/deepin-wine-runner
|
||||||
mkdir -pv deb/opt/apps/deepin-wine-runner/entries/
|
mkdir -pv deb/opt/apps/deepin-wine-runner/entries/
|
||||||
cp -rv deb/usr/share/applications deb/opt/apps/deepin-wine-runner/entries/applications
|
cp -rv deb/usr/share/applications deb/opt/apps/deepin-wine-runner/entries/applications
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Wine 运行器 QQ 交流群:762985460
|
|||||||
Wine 运行器离线包下载地址:https://www.123pan.com/s/pDSKVv-pAJWv.html
|
Wine 运行器离线包下载地址:https://www.123pan.com/s/pDSKVv-pAJWv.html
|
||||||
|
|
||||||
### Wine 运行器 For Termux 安装方法以及注意事项
|
### Wine 运行器 For Termux 安装方法以及注意事项
|
||||||
|
Wine 运行器 For Termux 使用的是独立的安装包,详细见:
|
||||||
见 https://gitee.com/gfdgd-xi/deep-wine-runner-termux
|
见 https://gitee.com/gfdgd-xi/deep-wine-runner-termux
|
||||||
https://github.com/gfdgd-xi/deep-wine-runner-termux
|
https://github.com/gfdgd-xi/deep-wine-runner-termux
|
||||||
|
|
||||||
@@ -57,14 +58,6 @@ https://gfdgdxi.lanzouw.com/b0plly5cj
|
|||||||
spark-deepin-wine-runner 是普通包,spark-deepin-wine-runner-ace 是使用 ace 兼容环境运行的运行器
|
spark-deepin-wine-runner 是普通包,spark-deepin-wine-runner-ace 是使用 ace 兼容环境运行的运行器
|
||||||
|
|
||||||
## Wine 运行器离线包
|
## Wine 运行器离线包
|
||||||
### X86
|
|
||||||
123pan:https://www.123pan.com/s/pDSKVv-pAJWv.html
|
|
||||||
百度网盘:https://pan.baidu.com/s/1klBw63tw2_ZQLzmi11dDBw?pwd=7bu5 提取码: 7bu5
|
|
||||||
诚通网盘:http://ctfile.gfdgdxi.top/d/31540479-59254792-909739?p=2061 (访问密码: 2061)
|
|
||||||
Github:https://github.com/gfdgd-xi/deep-wine-runner/releases/
|
|
||||||
Sourceforge:https://sourceforge.net/projects/deep-wine-runner/files/
|
|
||||||
更多需求需要私聊作者定制(有偿服务)
|
|
||||||
### ARM
|
|
||||||
需要私聊作者定制(有偿服务)
|
需要私聊作者定制(有偿服务)
|
||||||
|
|
||||||
## 软件架构
|
## 软件架构
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
qemuUEFI="-vga virtio -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 "
|
qemuUEFI="-vga virtio -machine usb=on -device usb-tablet "
|
||||||
fi
|
fi
|
||||||
echo $qemuUEFI
|
echo $qemuUEFI
|
||||||
./VM/kvm-ok
|
./VM/kvm-ok
|
||||||
@@ -70,9 +70,10 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
|
|||||||
echo X86 架构,使用 kvm 加速
|
echo X86 架构,使用 kvm 加速
|
||||||
$qemuMore qemu-system-x86_64 --enable-kvm -cpu host --hda "$HOME/Qemu/Windows/Windows.qcow2" \
|
$qemuMore qemu-system-x86_64 --enable-kvm -cpu host --hda "$HOME/Qemu/Windows/Windows.qcow2" \
|
||||||
-smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
|
-smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
|
||||||
-m ${use}G -display vnc=:5 -display gtk -usb -nic model=rtl8139 $qemuUEFI \
|
-m ${use}G -display vnc=:5 -display gtk -nic model=rtl8139 $qemuUEFI \
|
||||||
-device AC97 -device ES1370 -device intel-hda -device hda-duplex \
|
-device AC97 -device ES1370 -device intel-hda -device hda-duplex \
|
||||||
--boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
|
--boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
|
||||||
|
-usb \
|
||||||
> $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all
|
> $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
@@ -99,7 +100,7 @@ if [[ $? == 0 ]] && [[ -f "$HOME/Qemu/Windows/Windows.qcow2" ]]; then
|
|||||||
echo 不使用 kvm 加速
|
echo 不使用 kvm 加速
|
||||||
$qemuPath --hda "$HOME/Qemu/Windows/Windows.qcow2" \
|
$qemuPath --hda "$HOME/Qemu/Windows/Windows.qcow2" \
|
||||||
-smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
|
-smp $CpuCount,sockets=$CpuSocketNum,cores=$(($CpuCoreNum / $CpuSocketNum)),threads=$(($CpuCount / $CpuCoreNum / $CpuSocketNum)) \
|
||||||
-m ${use}G -display vnc=:5 -display gtk -usb -nic model=rtl8139 $qemuUEFI \
|
-m ${use}G -display vnc=:5 -display gtk -nic model=rtl8139 $qemuUEFI \
|
||||||
-device AC97 -device ES1370 -device intel-hda -device hda-duplex \
|
-device AC97 -device ES1370 -device intel-hda -device hda-duplex \
|
||||||
--boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
|
--boot 'splash=VM/boot.jpg,menu=on,splash-time=2000' \
|
||||||
> $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all
|
> $TMPDIR/tmp/windows-virtual-machine-installer-for-wine-runner-run.log 2>&1 # 最新的 qemu 已经移除参数 -soundhw all
|
||||||
|
|||||||
1
VM/buildvbox.py
Normal file
1
VM/buildvbox.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
248
VM/qemu.py
Normal file
248
VM/qemu.py
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class qemu:
|
||||||
|
name = ""
|
||||||
|
managerPath = ""
|
||||||
|
vboxVersion = ""
|
||||||
|
commandOption = ""
|
||||||
|
isUEFI = False
|
||||||
|
|
||||||
|
def applicationDirPath(self):
|
||||||
|
return os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
||||||
|
|
||||||
|
def homePath(self):
|
||||||
|
return os.getenv("HOME")
|
||||||
|
|
||||||
|
def __init__(self, name: str, managerPath: str) -> None:
|
||||||
|
if (not os.path.exists(name)):
|
||||||
|
self.name = self.homePath() + "/Qemu/" + name
|
||||||
|
else:
|
||||||
|
self.name = name
|
||||||
|
self.managerPath = managerPath
|
||||||
|
self.qemuPath = "qemu-system-i386"
|
||||||
|
if (os.path.exists("/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh")):
|
||||||
|
# 如果存在拓展 Qemu,则调用此
|
||||||
|
qemuPath = "/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh qemu-system-i386"
|
||||||
|
self.vboxVersion = subprocess.getoutput(qemuPath + " --version")
|
||||||
|
|
||||||
|
def Create(self, type: str):
|
||||||
|
if(not os.path.exists(self.name)):
|
||||||
|
os.makedirs(self.name)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def CreateDisk(self, path: str, size: int):
|
||||||
|
if(os.path.exists(path)):
|
||||||
|
return 0
|
||||||
|
return os.system(("qemu-img create -f qcow2 '" + self.path + "' " + str(size) + "M"))
|
||||||
|
|
||||||
|
def CreateDiskControl(self, controlName: str):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def MountDisk(self, diskPath: str, controlName: str, port: int, device: int):
|
||||||
|
self.commandOption += "-drive 'file=" + diskPath + ",if=ide,index=" + str(device) + "' "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def MountISO(self, isoPath, controlName: str, port: int, device: int):
|
||||||
|
self.commandOption += "-drive 'media=cdrom,file=" + isoPath + ",if=ide,index=" + str(device) + "' "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def BootFirst(self, bootDrive: str):
|
||||||
|
self.commandOption += "-boot '" + bootDrive + "' "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetNetBridge(self, netDriver: str):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetCPU(self, number: int, cpuNum: int, coreNum: int):
|
||||||
|
# self.commandOption += "-smp " + str(number) + " "
|
||||||
|
# 调整调用方法
|
||||||
|
#qDebug() << number << " " << cpuNum << " " << coreNum
|
||||||
|
print("Socket: ", cpuNum)
|
||||||
|
print("Core: ", coreNum)
|
||||||
|
print("Threads: ", number)
|
||||||
|
self.commandOption += "-smp " + str(number) + ",sockets=" + str(cpuNum) + ",cores=" + str(coreNum / cpuNum) + ",threads=" + str(number / cpuNum / coreNum) + " "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetMemory(self, memory: str):
|
||||||
|
self.commandOption += "-m " + str(memory) + "M "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetRemote(self, setting: bool):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetRemoteConnectSetting(self, port: int):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def MountMainDisk(self, diskPath: str):
|
||||||
|
self.commandOption += " --hda '" + diskPath + "' "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def StartArmhf(self):
|
||||||
|
print(self.commandOption)
|
||||||
|
qemuPath = "qemu-system-arm"
|
||||||
|
if(os.path.exists("/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh")):
|
||||||
|
# 如果存在拓展 Qemu,则调用此
|
||||||
|
qemuPath = "/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh qemu-system-arm"
|
||||||
|
if(subprocess.getoutput("arch").replace("\n", "").replace(" ", "") == "aarch64" and not os.system((self.applicationDirPath() + "/kvm-ok").toUtf8())):
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk --enable-kvm -cpu host -M virt " + self.commandOption + " -device virtio-gpu-pci -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 -device usb-kbd,id=keyboard,bus=xhci.0,port=2 > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk -cpu max -M virt " + self.commandOption + " -device virtio-gpu-pci -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 -device usb-kbd,id=keyboard,bus=xhci.0,port=2 > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
|
||||||
|
def StartAarch64(self):
|
||||||
|
bootScreenLogo = ""
|
||||||
|
qemuPath = "qemu-system-aarch64"
|
||||||
|
# 判断 boot 文件是否存在
|
||||||
|
if (os.path.exists(self.applicationDirPath() + "/boot.jpg")):
|
||||||
|
bootScreenLogo = self.applicationDirPath() + "/boot.jpg"
|
||||||
|
else:
|
||||||
|
# 写入 logo
|
||||||
|
shutil.copy(":/boot.jpg", "/tmp/deep-wine-runner-boot.jpg")
|
||||||
|
bootScreenLogo = "/tmp/deep-wine-runner-boot.jpg"
|
||||||
|
|
||||||
|
if(os.path.exists("/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh")):
|
||||||
|
# 如果存在拓展 Qemu,则调用此
|
||||||
|
qemuPath = "/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh qemu-system-aarch64"
|
||||||
|
|
||||||
|
print(self.commandOption)
|
||||||
|
if(subprocess.getoutput("arch").replace("\n", "").replace(" ", "") == "aarch64" and not os.system((self.applicationDirPath() + "/kvm-ok").toUtf8())):
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk --enable-kvm -cpu host -M virt " + self.commandOption + " -device virtio-gpu-pci -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 -device usb-kbd,id=keyboard,bus=xhci.0,port=2 > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk -cpu max -M virt " + self.commandOption + " -device virtio-gpu-pci -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 -device usb-kbd,id=keyboard,bus=xhci.0,port=2 > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
|
||||||
|
def StartLoong64(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def Start(self, unShown: bool):
|
||||||
|
newCommandOption = self.commandOption
|
||||||
|
qemuPath = "qemu-system-x86_64"
|
||||||
|
print(self.GetBootLogoPath())
|
||||||
|
if (self.isUEFI):
|
||||||
|
newcommandOption += " -vga none -device virtio-gpu-pci -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 -device usb-kbd,id=keyboard,bus=xhci.0,port=2 "
|
||||||
|
else:
|
||||||
|
newcommandOption += " -vga virtio -device nec-usb-xhci,id=xhci,addr=0x1b -device usb-tablet,id=tablet,bus=xhci.0,port=1 "
|
||||||
|
# UOS 3a4000 使用程序自带的 qemu
|
||||||
|
info = self.SystemInfo().lower()
|
||||||
|
if("uos" in info or "unio" in info):
|
||||||
|
# 判断架构
|
||||||
|
arch = self.GetArch()
|
||||||
|
if(arch == "mips64" or arch == "mipsel64"):
|
||||||
|
qemuPath = "bwrap --dev-bind / / --bind '" + self.applicationDirPath() + "/MipsQemu/usr/lib/mips64el-linux-gnuabi64/qemu/ui-gtk.so' /usr/lib/mips64el-linux-gnuabi64/qemu/ui-gtk.so '" + self.applicationDirPath() + "/MipsQemu/usr/bin/qemu-system-x86_64' "
|
||||||
|
if(os.path.exists("/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh")):
|
||||||
|
# 如果存在拓展 Qemu,则调用此
|
||||||
|
qemuPath = "/opt/apps/deepin-wine-runner-qemu-system-extra/files/run.sh qemu-system-x86_64"
|
||||||
|
print(self.commandOption)
|
||||||
|
if(subprocess.getoutput("arch").replace("\n", "").replace(" ", "") == "x86_64" and not os.system((self.applicationDirPath() + "/kvm-ok").toUtf8())):
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk --enable-kvm -cpu host " + newCommandOption + " > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
return os.system((qemuPath + " --boot 'splash=" + self.GetBootLogoPath() + ",order=d,menu=on,splash-time=2000' -display vnc=:5 -display gtk -nic model=rtl8139 " + newCommandOption + " > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1 &"))
|
||||||
|
|
||||||
|
def Stop(self):
|
||||||
|
os.system("killall qemu-system-x86_64 -9")
|
||||||
|
os.system("killall qemu-system-aarch64 -9")
|
||||||
|
os.system("killall qemu-system-arm -9")
|
||||||
|
os.system("killall kvm -9")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def Delete(self):
|
||||||
|
return os.system(("rm -rfv '" + self.name + "'"))
|
||||||
|
|
||||||
|
def SetDisplayMemory(self, memory: int):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def InstallGuessAdditions(self, controlName: str, port: int, device: int):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def EnabledAudio(self):
|
||||||
|
self.commandOption += "-device AC97 -device ES1370 -device intel-hda -device hda-duplex "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def EnabledClipboardMode(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def EnabledDraganddrop(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def ShareFile(self, name: str, path: str):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetVBoxSVGA(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetMousePS2(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def SetKeyboardPS2(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def OpenUSB(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def UseAarch64EFI(self):
|
||||||
|
if(os.path.exists("/usr/share/qemu-efi-aarch64/QEMU_EFI.fd")):
|
||||||
|
self.commandOption += "--bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd "
|
||||||
|
return 0
|
||||||
|
if(os.path.exists(self.applicationDirPath() + "/QEMU_AARCH64_EFI.fd")):
|
||||||
|
self.commandOption += "--bios '" + self.applicationDirPath() + "/QEMU_AARCH64_EFI.fd' "
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def UseArmhfEFI(self):
|
||||||
|
if(os.path.exists("/usr/share/AAVMF/AAVMF32_CODE.fd")):
|
||||||
|
self.commandOption += "--bios /usr/share/AAVMF/AAVMF32_CODE.fd "
|
||||||
|
return 0
|
||||||
|
if(os.path.exists(self.applicationDirPath() + "/AAVMF32_CODE.fd")):
|
||||||
|
self.commandOption += "--bios '" + self.applicationDirPath() + "/AAVMF32_CODE.fd' "
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def UseLoongarch64EFI(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def UseOtherEFI(self, fdFilePath: str):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def EnabledUEFI(self, status: str):
|
||||||
|
isUEFI = status
|
||||||
|
if(not status):
|
||||||
|
return 0
|
||||||
|
if(os.path.exists("/usr/share/qemu/OVMF.fd")):
|
||||||
|
self.commandOption += "--bios /usr/share/qemu/OVMF.fd "
|
||||||
|
return 0
|
||||||
|
if(os.path.exists(self.applicationDirPath() + "/OVMF.fd")):
|
||||||
|
self.commandOption += "--bios '" + self.applicationDirPath() + "/OVMF.fd' "
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def MountMainISO(self, isoPath: str):
|
||||||
|
self.commandOption += "--cdrom '" + isoPath + "' "
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def AutoInstall(self, iso: str):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def AddDiskSpace(self, path, data: float):
|
||||||
|
return os.system(("qemu-img resize '" + self.path + "' +" + str(data) + "G").toUtf8())
|
||||||
|
|
||||||
|
def GetBootLogoPath(self) -> str:
|
||||||
|
bootScreenLogo = ""
|
||||||
|
# 判断 boot 文件是否存在
|
||||||
|
if(os.path.exists(self.applicationDirPath() + "/boot.jpg")):
|
||||||
|
bootScreenLogo = self.applicationDirPath() + "/boot.jpg"
|
||||||
|
else:
|
||||||
|
# 写入 logo
|
||||||
|
if(os.path.exists("/tmp/deep-wine-runner-boot.jpg")):
|
||||||
|
os.remove("/tmp/deep-wine-runner-boot.jpg")
|
||||||
|
shutil.copy(":/boot.jpg", "/tmp/deep-wine-runner-boot.jpg")
|
||||||
|
bootScreenLogo = "/tmp/deep-wine-runner-boot.jpg"
|
||||||
|
return bootScreenLogo
|
||||||
|
|
||||||
|
|
||||||
|
def SystemInfo(self) -> str:
|
||||||
|
with open("/etc/os-version", "r") as file:
|
||||||
|
data = file.read()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def GetArch(self) -> str:
|
||||||
|
return subprocess.getoutput("uname -m").replace("\n", "").replace(" ", "")
|
||||||
|
|
||||||
107
VM/vbox.py
Normal file
107
VM/vbox.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class vbox:
|
||||||
|
name = ""
|
||||||
|
managerPath = ""
|
||||||
|
vboxVersion = ""
|
||||||
|
|
||||||
|
def __init__(self, name: str, managerPath: str) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.managerPath = managerPath
|
||||||
|
self.vboxVersion = subprocess.getoutput("'" + managerPath + "' -v")
|
||||||
|
|
||||||
|
def Create(self, type: str):
|
||||||
|
os.system(("\"" + self.managerPath + "\" createvm --name \""
|
||||||
|
+ self.name + "\" --ostype \"" + type +
|
||||||
|
"\" --register"))
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \""
|
||||||
|
+ self.name + "\" --ostype \"" + type +
|
||||||
|
"\" "))
|
||||||
|
# vboxmanage modifyvm testvm --ostype
|
||||||
|
|
||||||
|
def CreateDisk(self, path: str, size: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" createvdi --filename \"" + path + "\" --size \"" + str(size) + "\""))
|
||||||
|
|
||||||
|
def CreateDiskControl(self, controlName: str):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" storagectl \"" + self.name + "\" --name \"" + controlName + "\" --add ide"))
|
||||||
|
|
||||||
|
def MountDisk(self, diskPath: str, controlName: str, port: int, device: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" storageattach \"" + self.name +
|
||||||
|
"\" --storagectl \"" + controlName + "\" --type hdd --port "
|
||||||
|
+ str(port) + " --device " + str(device) + " --medium \"" + diskPath + "\""))
|
||||||
|
|
||||||
|
def MountISO(self, isoPath: str, controlName: str, port: int, device: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" storageattach \"" + self.name + "\" --storagectl \"" +
|
||||||
|
controlName + "\" --type dvddrive --port " + str(port) + " --device " + str(device)
|
||||||
|
+ " --medium \"" + isoPath + "\""))
|
||||||
|
|
||||||
|
def BootFirst(self, bootDrive: str):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --boot1 " + bootDrive))
|
||||||
|
|
||||||
|
def SetNetBridge(self, netDriver: str):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name +
|
||||||
|
"\" --nic1 bridged --cableconnected1 on --nictype1 82540EM --bridgeadapter1 \"" + netDriver + "\" --intnet1 brigh1 --macaddress1 auto"))
|
||||||
|
|
||||||
|
def SetCPU(self, number: int, cpuNum: int, coreNum: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --cpus " + str(number)))
|
||||||
|
|
||||||
|
def SetMemory(self, memory: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --memory " + str(memory)))
|
||||||
|
|
||||||
|
def SetRemote(self, setting: bool):
|
||||||
|
if (setting):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --vrde on"))
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --vrde off"))
|
||||||
|
|
||||||
|
def SetRemoteConnectSetting(self, port: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --vrdeport " + str(port) + " --vrdeaddress """))
|
||||||
|
|
||||||
|
def Start(self, unShown: bool):
|
||||||
|
if (unShown):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1"))
|
||||||
|
return os.system(("\"" + self.managerPath + "\" startvm \"" + self.name + "\" > /tmp/windows-virtual-machine-installer-for-wine-runner-install.log 2>&1"))
|
||||||
|
|
||||||
|
def Stop(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" controlvm \"" + self.name + "\" poweroff"))
|
||||||
|
|
||||||
|
def Delete(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" unregistervm --delete \"" + self.name + "\""))
|
||||||
|
|
||||||
|
def SetDisplayMemory(self, memory: int):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --vram " + str(memory)))
|
||||||
|
|
||||||
|
def InstallGuessAdditions(self, controlName: str, port: int, device: int):
|
||||||
|
return self.MountISO("/usr/share/virtualbox/VBoxGuestAdditions.iso", controlName, port, device);
|
||||||
|
|
||||||
|
def EnabledAudio(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --audio-driver pulse --audiocontroller hda --audioin on --audioout on"))
|
||||||
|
|
||||||
|
def EnabledClipboardMode(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --clipboard-mode bidirectional"))
|
||||||
|
|
||||||
|
def EnabledDraganddrop(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --draganddrop bidirectional"))
|
||||||
|
|
||||||
|
def ShareFile(self, name: str, path: str):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" sharedfolder add \"" + this->name + "\" -name \"" + self.name + "\" -hostpath \"" + path + "\""))
|
||||||
|
|
||||||
|
def SetVBoxSVGA(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --graphicscontroller vboxsvga"))
|
||||||
|
|
||||||
|
def SetMousePS2(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --mouse usb"))
|
||||||
|
|
||||||
|
def SetKeyboardPS2(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --keyboard usb"))
|
||||||
|
|
||||||
|
def OpenUSB(self):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --usbohci on"))
|
||||||
|
|
||||||
|
def EnabledUEFI(self, status: bool):
|
||||||
|
if (status):
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --firmware=efi"))
|
||||||
|
return os.system(("\"" + self.managerPath + "\" modifyvm \"" + self.name + "\" --firmware=bios"))
|
||||||
|
|
||||||
|
def AutoInstall(self, iso: str):
|
||||||
|
return os.system(("vboxmanage unattended install '" + self.name + "' '--iso=" + iso + "'"))
|
||||||
@@ -24,11 +24,11 @@ Depends: python3,
|
|||||||
fakeroot,
|
fakeroot,
|
||||||
which,
|
which,
|
||||||
git,
|
git,
|
||||||
xfwm4,
|
|
||||||
tigervnc,
|
|
||||||
proot,
|
proot,
|
||||||
bash
|
bash
|
||||||
Recommends: winbind,
|
Recommends: winbind,
|
||||||
|
tigervnc | termux-x11-nightly | termux-x11,
|
||||||
|
xfwm4,
|
||||||
wimtools | wimlib,
|
wimtools | wimlib,
|
||||||
python3-pyquery,
|
python3-pyquery,
|
||||||
python3-pyqt5.qtwebengine | pyqtwebengine,
|
python3-pyqt5.qtwebengine | pyqtwebengine,
|
||||||
@@ -53,7 +53,14 @@ Conflicts: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, s
|
|||||||
Replaces: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52
|
Replaces: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52
|
||||||
Provides: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52
|
Provides: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52
|
||||||
Installed-Size: @@SIZE@@
|
Installed-Size: @@SIZE@@
|
||||||
Description: Wine运行器是一个能让Linux用户更加方便地运行Windows应用的程序。原版的 Wine 只能使用命令操作,且安装过程较为繁琐,对小白不友好。于是该运行器为了解决该痛点,内置了对Wine图形化的支持、Wine 安装器、微型应用商店、各种Wine工具、自制的Wine程序打包器、运行库安装工具等。
|
Description: Wine运行器是一个能让Linux用户更加方便地运行Windows应用的
|
||||||
它同时还内置了基于Qemu/VirtualBox制作的、专供小白使用的Windows虚拟机安装工具,可以做到只需下载系统镜像并点击安装即可,无需考虑虚拟机的安装、创建、分区等操作,也能在非 X86 架构安装 X86 架构的 Windows 操作系统(但是效率较低,可以运行些老系统)。
|
程序。原版的 Wine 只能使用命令操作,且安装过程较为繁琐,对小白不友好。于是该运行器为了解决
|
||||||
而且对于部分 Wine 应用适配者来说,提供了图形化的打包工具,以及提供了一些常用工具以及运行库的安装方式,以及能安装多种不同的 Wine 以测试效果,能极大提升适配效率。
|
该痛点,内置了对Wine图形化的支持、Wine 安装器、微型应用商店、各种Wine工具、自制的
|
||||||
且对于 Deepin23 用户做了特别优化,以便能在缺少 i386 运行库的情况下运行 Wine32。同时也为非 X86 架构用户提供了 Box86/64、Qemu User 的安装方式
|
Wine程序打包器、运行库安装工具等。
|
||||||
|
它同时还内置了基于Qemu/VirtualBox制作的、专供小白使用的Windows虚拟机安装工具,
|
||||||
|
可以做到只需下载系统镜像并点击安装即可,无需考虑虚拟机的安装、创建、分区等操作,也能在非 X86 架
|
||||||
|
构安装 X86 架构的 Windows 操作系统(但是效率较低,可以运行些老系统)。
|
||||||
|
而且对于部分 Wine 应用适配者来说,提供了图形化的打包工具,以及提供了一些常用工具以及运行库的安
|
||||||
|
装方式,以及能安装多种不同的 Wine 以测试效果,能极大提升适配效率。
|
||||||
|
且对于 Deepin23 用户做了特别优化,以便能在缺少 i386 运行库的情况下运行 Wine32。
|
||||||
|
同时也为非 X86 架构用户提供了 Box86/64、Qemu User 的安装方式
|
||||||
|
|||||||
@@ -108,7 +108,10 @@ fi
|
|||||||
# 设置目录权限,让用户可读可写,方便后续删除组件
|
# 设置目录权限,让用户可读可写,方便后续删除组件
|
||||||
chmod 777 -R /opt/apps/deepin-wine-runner > /dev/null 2>&1 | true
|
chmod 777 -R /opt/apps/deepin-wine-runner > /dev/null 2>&1 | true
|
||||||
|
|
||||||
if [[ ! -f /data/data/com.termux/files/home/.vnc/passwd ]]; then
|
# 向服务器返回安装数加1(不显示内容且忽略错误)
|
||||||
|
python3 /opt/apps/deepin-wine-runner/Download.py $version > /dev/null 2>&1 | true
|
||||||
|
|
||||||
|
if [[ ! -f /data/data/com.termux/files/home/.vnc/passwd ]] && [[ -f /data/data/com.termux/files/usr/bin/vncpasswd ]]; then
|
||||||
echo 开始配置 VNCServer
|
echo 开始配置 VNCServer
|
||||||
echo 接下来需要设置 VNCServer 的密码
|
echo 接下来需要设置 VNCServer 的密码
|
||||||
set +e
|
set +e
|
||||||
@@ -121,11 +124,11 @@ if [[ ! -f /data/data/com.termux/files/home/.vnc/passwd ]]; then
|
|||||||
echo 设置有误,需重新设置
|
echo 设置有误,需重新设置
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
echo 'Wine 运行器安装完成,如果没有设置 $DISPLAY 变量的情况下打开 Wine 运行器'
|
||||||
|
echo '可以在浏览器输入网址 http://localhost:6080/vnc.html 远程访问'
|
||||||
|
echo '或者用 VNC 远程工具输入 localhost:5 远程访问'
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 向服务器返回安装数加1(不显示内容且忽略错误)
|
|
||||||
python3 /opt/apps/deepin-wine-runner/Download.py $version > /dev/null 2>&1 | true
|
|
||||||
|
|
||||||
echo 'Wine 运行器安装完成,如果没有设置 $DISPLAY 变量的情况下打开 Wine 运行器'
|
|
||||||
echo '可以在浏览器输入网址 http://localhost:6080/vnc.html 远程访问'
|
|
||||||
echo '或者用 VNC 远程工具输入 localhost:5 远程访问'
|
|
||||||
|
|||||||
@@ -49,7 +49,14 @@ Conflicts: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, s
|
|||||||
Replaces: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52, spark-deepin-wine-runner-termux
|
Replaces: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52, spark-deepin-wine-runner-termux
|
||||||
Provides: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52, spark-deepin-wine-runner-termux
|
Provides: spark.deepin-venturi-setter, spark-deepin-wine5-application-packer, spark-deepin-wine-runner-52, spark-deepin-wine-runner-termux
|
||||||
Installed-Size: @@SIZE@@
|
Installed-Size: @@SIZE@@
|
||||||
Description: Wine运行器是一个能让Linux用户更加方便地运行Windows应用的程序。原版的 Wine 只能使用命令操作,且安装过程较为繁琐,对小白不友好。于是该运行器为了解决该痛点,内置了对Wine图形化的支持、Wine 安装器、微型应用商店、各种Wine工具、自制的Wine程序打包器、运行库安装工具等。
|
Description: Wine运行器是一个能让Linux用户更加方便地运行Windows应用的
|
||||||
它同时还内置了基于Qemu/VirtualBox制作的、专供小白使用的Windows虚拟机安装工具,可以做到只需下载系统镜像并点击安装即可,无需考虑虚拟机的安装、创建、分区等操作,也能在非 X86 架构安装 X86 架构的 Windows 操作系统(但是效率较低,可以运行些老系统)。
|
程序。原版的 Wine 只能使用命令操作,且安装过程较为繁琐,对小白不友好。于是该运行器为了解决
|
||||||
而且对于部分 Wine 应用适配者来说,提供了图形化的打包工具,以及提供了一些常用工具以及运行库的安装方式,以及能安装多种不同的 Wine 以测试效果,能极大提升适配效率。
|
该痛点,内置了对Wine图形化的支持、Wine 安装器、微型应用商店、各种Wine工具、自制的
|
||||||
且对于 Deepin23 用户做了特别优化,以便能在缺少 i386 运行库的情况下运行 Wine32。同时也为非 X86 架构用户提供了 Box86/64、Qemu User 的安装方式
|
Wine程序打包器、运行库安装工具等。
|
||||||
|
它同时还内置了基于Qemu/VirtualBox制作的、专供小白使用的Windows虚拟机安装工具,
|
||||||
|
可以做到只需下载系统镜像并点击安装即可,无需考虑虚拟机的安装、创建、分区等操作,也能在非 X86 架
|
||||||
|
构安装 X86 架构的 Windows 操作系统(但是效率较低,可以运行些老系统)。
|
||||||
|
而且对于部分 Wine 应用适配者来说,提供了图形化的打包工具,以及提供了一些常用工具以及运行库的安
|
||||||
|
装方式,以及能安装多种不同的 Wine 以测试效果,能极大提升适配效率。
|
||||||
|
且对于 Deepin23 用户做了特别优化,以便能在缺少 i386 运行库的情况下运行 Wine32。
|
||||||
|
同时也为非 X86 架构用户提供了 Box86/64、Qemu User 的安装方式
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import PyQt5.QtCore as QtCore
|
|||||||
import PyQt5.QtWidgets as QtWidgets
|
import PyQt5.QtWidgets as QtWidgets
|
||||||
from DefaultSetting import *
|
from DefaultSetting import *
|
||||||
|
|
||||||
|
import globalenv
|
||||||
|
|
||||||
def ShowText(text: str):
|
def ShowText(text: str):
|
||||||
if text.replace(" ", "").replace("\n", "") == "":
|
if text.replace(" ", "").replace("\n", "") == "":
|
||||||
return
|
return
|
||||||
@@ -669,82 +671,94 @@ def BrowserExe():
|
|||||||
exePath.setText(filePath[0])
|
exePath.setText(filePath[0])
|
||||||
|
|
||||||
chooseWine = ""
|
chooseWine = ""
|
||||||
if __name__ == "__main__":
|
|
||||||
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
||||||
iconPath = "{}/deepin-wine-runner.svg".format(programPath)
|
iconPath = "{}/deepin-wine-runner.svg".format(programPath)
|
||||||
information = json.loads(ReadTxt(f"{programPath}/information.json"))
|
information = json.loads(ReadTxt(f"{programPath}/information.json"))
|
||||||
iconListUnBuild = json.loads(ReadTxt(f"{programPath}/IconList.json"))[0]
|
iconListUnBuild = json.loads(ReadTxt(f"{programPath}/IconList.json"))[0]
|
||||||
iconList = json.loads(ReadTxt(f"{programPath}/IconList.json"))[1]
|
iconList = json.loads(ReadTxt(f"{programPath}/IconList.json"))[1]
|
||||||
for i in iconListUnBuild:
|
for i in iconListUnBuild:
|
||||||
iconList.append(i)
|
iconList.append(i)
|
||||||
|
if (__name__ == "__main__"):
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
version = information["Version"]
|
else:
|
||||||
window = QtWidgets.QMainWindow()
|
app = globalenv.get_value("app")
|
||||||
widget = QtWidgets.QWidget()
|
version = information["Version"]
|
||||||
layout = QtWidgets.QGridLayout()
|
window = QtWidgets.QMainWindow()
|
||||||
exePath = QtWidgets.QLineEdit()
|
widget = QtWidgets.QWidget()
|
||||||
wineChooser = QtWidgets.QComboBox()
|
layout = QtWidgets.QGridLayout()
|
||||||
browserExeButton = QtWidgets.QPushButton("浏览……")
|
exePath = QtWidgets.QLineEdit()
|
||||||
logText = QtWidgets.QTextBrowser()
|
wineChooser = QtWidgets.QComboBox()
|
||||||
logText.setStyleSheet("""
|
browserExeButton = QtWidgets.QPushButton("浏览……")
|
||||||
background-color: black;
|
logText = QtWidgets.QTextBrowser()
|
||||||
color: white;
|
logText.setStyleSheet("""
|
||||||
""")
|
background-color: black;
|
||||||
wineChooserList = [
|
color: white;
|
||||||
"使用 Deepin Wine8 Stable 打包应用",
|
""")
|
||||||
"使用 Spark Wine9 wow 打包应用",
|
wineChooserList = [
|
||||||
"使用 Spark Wine9 打包应用",
|
"使用 Deepin Wine8 Stable 打包应用",
|
||||||
"使用 Spark Wine8 打包应用",
|
"使用 Spark Wine9 wow 打包应用",
|
||||||
"使用 Spark Wine7 Devel 打包应用",
|
"使用 Spark Wine9 打包应用",
|
||||||
"使用 Deepin Wine6 Stable 打包应用",
|
"使用 Spark Wine8 打包应用",
|
||||||
"使用 Deepin Wine5 Stable 打包应用",
|
"使用 Spark Wine7 Devel 打包应用",
|
||||||
"使用 Deepin Wine5 打包应用",
|
"使用 Deepin Wine6 Stable 打包应用",
|
||||||
"使用 Deepin Wine2 打包应用",
|
"使用 Deepin Wine5 Stable 打包应用",
|
||||||
"使用 Spark Wine 打包应用"
|
"使用 Deepin Wine5 打包应用",
|
||||||
]
|
"使用 Deepin Wine2 打包应用",
|
||||||
wineChooserIndex = 2
|
"使用 Spark Wine 打包应用"
|
||||||
wineList = ["deepin-wine8-stable", "spark-wine9-wow", "spark-wine9", "spark-wine8", "spark-wine7-devel", "deepin-wine6-stable", "deepin-wine6-vannila", "spark-wine8-wow", "deepin-wine5-stable", "deepin-wine5", "deepin-wine", "spark-wine"]
|
]
|
||||||
for i in range(len(wineList)):
|
wineChooserIndex = 2
|
||||||
if not os.system(f"which '{wineList[i]}'"):
|
wineList = ["deepin-wine8-stable", "spark-wine9-wow", "spark-wine9", "spark-wine8", "spark-wine7-devel", "deepin-wine6-stable", "deepin-wine6-vannila", "spark-wine8-wow", "deepin-wine5-stable", "deepin-wine5", "deepin-wine", "spark-wine"]
|
||||||
wineChooserIndex = i
|
for i in range(len(wineList)):
|
||||||
break
|
if not os.system(f"which '{wineList[i]}'"):
|
||||||
chooseWine = wineList[wineChooserIndex]
|
wineChooserIndex = i
|
||||||
wineChooserList[wineChooserIndex] = f"{wineChooserList[wineChooserIndex]}(推荐,如无特殊需求不建议更换)"
|
break
|
||||||
wineChooser.addItems(wineChooserList)
|
chooseWine = wineList[wineChooserIndex]
|
||||||
wineChooser.setCurrentIndex(wineChooserIndex)
|
wineChooserList[wineChooserIndex] = f"{wineChooserList[wineChooserIndex]}(推荐,如无特殊需求不建议更换)"
|
||||||
controlLayout = QtWidgets.QHBoxLayout()
|
wineChooser.addItems(wineChooserList)
|
||||||
buildButton = QtWidgets.QPushButton("现在打包……")
|
wineChooser.setCurrentIndex(wineChooserIndex)
|
||||||
installCmpleteButton = QtWidgets.QPushButton("安装程序执行完成")
|
controlLayout = QtWidgets.QHBoxLayout()
|
||||||
helpButton = QtWidgets.QPushButton("帮助")
|
buildButton = QtWidgets.QPushButton("现在打包……")
|
||||||
installUosPackingTool = QtWidgets.QPushButton("安装维护工具箱(可以安装测试 deb)")
|
installCmpleteButton = QtWidgets.QPushButton("安装程序执行完成")
|
||||||
browserExeButton.clicked.connect(BrowserExe)
|
helpButton = QtWidgets.QPushButton("帮助")
|
||||||
buildButton.clicked.connect(RunBuildThread)
|
installUosPackingTool = QtWidgets.QPushButton("安装维护工具箱(可以安装测试 deb)")
|
||||||
installCmpleteButton.clicked.connect(PressCompleteDownload)
|
browserExeButton.clicked.connect(BrowserExe)
|
||||||
helpButton.clicked.connect(ReadMe)
|
buildButton.clicked.connect(RunBuildThread)
|
||||||
def InstallUosPackingTool():
|
installCmpleteButton.clicked.connect(PressCompleteDownload)
|
||||||
if os.system("which spark-store"):
|
helpButton.clicked.connect(ReadMe)
|
||||||
QtWidgets.QMessageBox.critical(window, "提示", "未安装星火应用商店,无法继续\n星火应用商店官网:https://spark-app.store/")
|
def InstallUosPackingTool():
|
||||||
return 0
|
if os.system("which spark-store"):
|
||||||
threading.Thread(target=os.system, args=["spark-store spk://store/tools/uos-packaging-tools"]).start()
|
QtWidgets.QMessageBox.critical(window, "提示", "未安装星火应用商店,无法继续\n星火应用商店官网:https://spark-app.store/")
|
||||||
installUosPackingTool.clicked.connect(InstallUosPackingTool)
|
return 0
|
||||||
installCmpleteButton.setDisabled(True)
|
threading.Thread(target=os.system, args=["spark-store spk://store/tools/uos-packaging-tools"]).start()
|
||||||
controlLayout.addWidget(buildButton)
|
installUosPackingTool.clicked.connect(InstallUosPackingTool)
|
||||||
controlLayout.addWidget(installCmpleteButton)
|
installCmpleteButton.setDisabled(True)
|
||||||
controlLayout.addWidget(helpButton)
|
controlLayout.addWidget(buildButton)
|
||||||
controlLayout.addWidget(installUosPackingTool)
|
controlLayout.addWidget(installCmpleteButton)
|
||||||
layout.addWidget(QtWidgets.QLabel("选择 EXE:"), 0, 0)
|
controlLayout.addWidget(helpButton)
|
||||||
layout.addWidget(exePath, 0, 1)
|
controlLayout.addWidget(installUosPackingTool)
|
||||||
layout.addWidget(browserExeButton, 0, 2)
|
layout.addWidget(QtWidgets.QLabel("选择 EXE:"), 0, 0)
|
||||||
layout.addWidget(wineChooser, 1, 1)
|
layout.addWidget(exePath, 0, 1)
|
||||||
layout.addLayout(controlLayout, 2, 1)
|
layout.addWidget(browserExeButton, 0, 2)
|
||||||
layout.addWidget(logText, 3, 0, 1, 3)
|
layout.addWidget(wineChooser, 1, 1)
|
||||||
widget.setLayout(layout)
|
layout.addLayout(controlLayout, 2, 1)
|
||||||
window.setCentralWidget(widget)
|
layout.addWidget(logText, 3, 0, 1, 3)
|
||||||
window.setWindowTitle(f"Wine 运行器 {version}——简易打包器")
|
widget.setLayout(layout)
|
||||||
try:
|
window.setCentralWidget(widget)
|
||||||
exePath.setText(sys.argv[1])
|
window.setWindowTitle(f"Wine 运行器 {version}——简易打包器")
|
||||||
except:
|
try:
|
||||||
pass
|
exePath.setText(sys.argv[1])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if (__name__ != "__main__"):
|
||||||
|
# 设置滚动条
|
||||||
|
areaScroll = QtWidgets.QScrollArea(window)
|
||||||
|
areaScroll.setWidgetResizable(True)
|
||||||
|
areaScroll.setWidget(widget)
|
||||||
|
areaScroll.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
|
window.setCentralWidget(areaScroll)
|
||||||
|
|
||||||
|
if (__name__ == "__main__"):
|
||||||
window.resize(int(window.frameGeometry().width() * 1.2), int(window.frameGeometry().height() * 1.1))
|
window.resize(int(window.frameGeometry().width() * 1.2), int(window.frameGeometry().height() * 1.1))
|
||||||
window.show()
|
window.show()
|
||||||
# 设置字体
|
# 设置字体
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import PyQt5.QtWidgets as QtWidgets
|
|||||||
from trans import *
|
from trans import *
|
||||||
from DefaultSetting import *
|
from DefaultSetting import *
|
||||||
from Model import *
|
from Model import *
|
||||||
|
import globalenv
|
||||||
|
|
||||||
TMPDIR = os.getenv("TMPDIR")
|
TMPDIR = os.getenv("TMPDIR")
|
||||||
if (TMPDIR == None):
|
if (TMPDIR == None):
|
||||||
@@ -414,7 +415,7 @@ class make_deb_threading(QtCore.QThread):
|
|||||||
"Architecture": debFirstArch.currentText(),
|
"Architecture": debFirstArch.currentText(),
|
||||||
"Depends": [
|
"Depends": [
|
||||||
f"{wine[wineVersion.currentText()]}, deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei",
|
f"{wine[wineVersion.currentText()]}, deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei",
|
||||||
f"{wine[wineVersion.currentText()]}, spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepinwinerunner, fonts-wqy-microhei, fonts-wqy-zenhei"
|
f"{wine[wineVersion.currentText()]}, spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei"
|
||||||
][int(chooseWineHelperValue.isChecked())],
|
][int(chooseWineHelperValue.isChecked())],
|
||||||
"postinst": ['', f'''#!/bin/bash
|
"postinst": ['', f'''#!/bin/bash
|
||||||
PACKAGE_NAME="{e1_text.text()}"
|
PACKAGE_NAME="{e1_text.text()}"
|
||||||
@@ -1360,7 +1361,7 @@ true
|
|||||||
print("c")
|
print("c")
|
||||||
if os.path.exists(wine[wineVersion.currentText()]):
|
if os.path.exists(wine[wineVersion.currentText()]):
|
||||||
debInformation[0]["Depends"] = ["deepin-wine-helper | com.wine-helper.deepin",
|
debInformation[0]["Depends"] = ["deepin-wine-helper | com.wine-helper.deepin",
|
||||||
"spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepinwinerunner"
|
"spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepin"
|
||||||
][int(chooseWineHelperValue.isChecked())] #+ ["", "libasound2 (>= 1.0.16), libc6 (>= 2.28), libglib2.0-0 (>= 2.12.0), libgphoto2-6 (>= 2.5.10), libgphoto2-port12 (>= 2.5.10), libgstreamer-plugins-base1.0-0 (>= 1.0.0), libgstreamer1.0-0 (>= 1.4.0), liblcms2-2 (>= 2.2+git20110628), libldap-2.4-2 (>= 2.4.7), libmpg123-0 (>= 1.13.7), libopenal1 (>= 1.14), libpcap0.8 (>= 0.9.8), libpulse0 (>= 0.99.1), libudev1 (>= 183), libvkd3d1 (>= 1.0), libx11-6, libxext6, libxml2 (>= 2.9.0), ocl-icd-libopencl1 | libopencl1, udis86, zlib1g (>= 1:1.1.4), libasound2-plugins, libncurses6 | libncurses5 | libncurses, deepin-wine-plugin-virtual\nRecommends: libcapi20-3, libcups2, libdbus-1-3, libfontconfig1, libfreetype6, libglu1-mesa | libglu1, libgnutls30 | libgnutls28 | libgnutls26, libgsm1, libgssapi-krb5-2, libjpeg62-turbo | libjpeg8, libkrb5-3, libodbc1, libosmesa6, libpng16-16 | libpng12-0, libsane | libsane1, libsdl2-2.0-0, libtiff5, libv4l-0, libxcomposite1, libxcursor1, libxfixes3, libxi6, libxinerama1, libxrandr2, libxrender1, libxslt1.1, libxxf86vm1"][]
|
][int(chooseWineHelperValue.isChecked())] #+ ["", "libasound2 (>= 1.0.16), libc6 (>= 2.28), libglib2.0-0 (>= 2.12.0), libgphoto2-6 (>= 2.5.10), libgphoto2-port12 (>= 2.5.10), libgstreamer-plugins-base1.0-0 (>= 1.0.0), libgstreamer1.0-0 (>= 1.4.0), liblcms2-2 (>= 2.2+git20110628), libldap-2.4-2 (>= 2.4.7), libmpg123-0 (>= 1.13.7), libopenal1 (>= 1.14), libpcap0.8 (>= 0.9.8), libpulse0 (>= 0.99.1), libudev1 (>= 183), libvkd3d1 (>= 1.0), libx11-6, libxext6, libxml2 (>= 2.9.0), ocl-icd-libopencl1 | libopencl1, udis86, zlib1g (>= 1:1.1.4), libasound2-plugins, libncurses6 | libncurses5 | libncurses, deepin-wine-plugin-virtual\nRecommends: libcapi20-3, libcups2, libdbus-1-3, libfontconfig1, libfreetype6, libglu1-mesa | libglu1, libgnutls30 | libgnutls28 | libgnutls26, libgsm1, libgssapi-krb5-2, libjpeg62-turbo | libjpeg8, libkrb5-3, libodbc1, libosmesa6, libpng16-16 | libpng12-0, libsane | libsane1, libsdl2-2.0-0, libtiff5, libv4l-0, libxcomposite1, libxcursor1, libxfixes3, libxi6, libxinerama1, libxrandr2, libxrender1, libxslt1.1, libxxf86vm1"][]
|
||||||
print("d")
|
print("d")
|
||||||
debInformation[0]["run.sh"] = f'''#!/bin/sh
|
debInformation[0]["run.sh"] = f'''#!/bin/sh
|
||||||
@@ -1918,14 +1919,14 @@ def BrowserHelperConfigPathText():
|
|||||||
def ChangeWine():
|
def ChangeWine():
|
||||||
useInstallWineArch.setEnabled(os.path.exists(wine[wineVersion.currentText()]))
|
useInstallWineArch.setEnabled(os.path.exists(wine[wineVersion.currentText()]))
|
||||||
debDepends.setText([f"{wine[wineVersion.currentText()]} | {wine[wineVersion.currentText()]}-bcm | {wine[wineVersion.currentText()]}-dcm | com.{wine[wineVersion.currentText()]}.deepin, deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei",
|
debDepends.setText([f"{wine[wineVersion.currentText()]} | {wine[wineVersion.currentText()]}-bcm | {wine[wineVersion.currentText()]}-dcm | com.{wine[wineVersion.currentText()]}.deepin, deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei",
|
||||||
f"{wine[wineVersion.currentText()]} | {wine[wineVersion.currentText()]}-bcm | {wine[wineVersion.currentText()]}-dcm | com.{wine[wineVersion.currentText()]}.deepin, spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepinwinerunner, fonts-wqy-microhei, fonts-wqy-zenhei"
|
f"{wine[wineVersion.currentText()]} | {wine[wineVersion.currentText()]}-bcm | {wine[wineVersion.currentText()]}-dcm | com.{wine[wineVersion.currentText()]}.deepin, spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepin, fonts-wqy-microhei, fonts-wqy-zenhei"
|
||||||
][int(chooseWineHelperValue.isChecked())])
|
][int(chooseWineHelperValue.isChecked())])
|
||||||
debRecommend.setText("")
|
debRecommend.setText("")
|
||||||
helperConfigPathText.setEnabled(chooseWineHelperValue.isChecked())
|
helperConfigPathText.setEnabled(chooseWineHelperValue.isChecked())
|
||||||
helperConfigPathButton.setEnabled(chooseWineHelperValue.isChecked())
|
helperConfigPathButton.setEnabled(chooseWineHelperValue.isChecked())
|
||||||
if os.path.exists(wine[wineVersion.currentText()]):
|
if os.path.exists(wine[wineVersion.currentText()]):
|
||||||
debDepends.setText(["deepin-wine-helper | com.wine-helper.deepin",
|
debDepends.setText(["deepin-wine-helper | com.wine-helper.deepin",
|
||||||
"spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepinwinerunner"
|
"spark-dwine-helper | store.spark-app.spark-dwine-helper | deepin-wine-helper | com.wine-helper.deepin"
|
||||||
][int(chooseWineHelperValue.isChecked())])
|
][int(chooseWineHelperValue.isChecked())])
|
||||||
#if "deepin-wine5-stable" in wine[wineVersion.currentText()]:
|
#if "deepin-wine5-stable" in wine[wineVersion.currentText()]:
|
||||||
# debDepends.setText("libasound2 (>= 1.0.16), libc6 (>= 2.28), libglib2.0-0 (>= 2.12.0), libgphoto2-6 (>= 2.5.10), libgphoto2-port12 (>= 2.5.10), libgstreamer-plugins-base1.0-0 (>= 1.0.0), libgstreamer1.0-0 (>= 1.4.0), liblcms2-2 (>= 2.2+git20110628), libldap-2.4-2 (>= 2.4.7), libmpg123-0 (>= 1.13.7), libopenal1 (>= 1.14), libpcap0.8 (>= 0.9.8), libpulse0 (>= 0.99.1), libudev1 (>= 183), libvkd3d1 (>= 1.0), libx11-6, libxext6, libxml2 (>= 2.9.0), ocl-icd-libopencl1 | libopencl1, udis86, zlib1g (>= 1:1.1.4), libasound2-plugins, libncurses6 | libncurses5 | libncurses, deepin-wine-plugin-virtual")
|
# debDepends.setText("libasound2 (>= 1.0.16), libc6 (>= 2.28), libglib2.0-0 (>= 2.12.0), libgphoto2-6 (>= 2.5.10), libgphoto2-port12 (>= 2.5.10), libgstreamer-plugins-base1.0-0 (>= 1.0.0), libgstreamer1.0-0 (>= 1.4.0), liblcms2-2 (>= 2.2+git20110628), libldap-2.4-2 (>= 2.4.7), libmpg123-0 (>= 1.13.7), libopenal1 (>= 1.14), libpcap0.8 (>= 0.9.8), libpulse0 (>= 0.99.1), libudev1 (>= 183), libvkd3d1 (>= 1.0), libx11-6, libxext6, libxml2 (>= 2.9.0), ocl-icd-libopencl1 | libopencl1, udis86, zlib1g (>= 1:1.1.4), libasound2-plugins, libncurses6 | libncurses5 | libncurses, deepin-wine-plugin-virtual")
|
||||||
@@ -2366,7 +2367,10 @@ tips = transla.transe("U", """提示:
|
|||||||
# 窗口创建
|
# 窗口创建
|
||||||
###############
|
###############
|
||||||
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
if (__name__ == "__main__"):
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
else:
|
||||||
|
app = globalenv.get_value("app")
|
||||||
window = QtWidgets.QMainWindow()
|
window = QtWidgets.QMainWindow()
|
||||||
widget = QtWidgets.QWidget()
|
widget = QtWidgets.QWidget()
|
||||||
|
|
||||||
@@ -2703,7 +2707,8 @@ SetFont(app)
|
|||||||
window.setCentralWidget(widget)
|
window.setCentralWidget(widget)
|
||||||
# 判断是否为小屏幕,是则设置滚动条并全屏
|
# 判断是否为小屏幕,是则设置滚动条并全屏
|
||||||
if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().size().width() * 0.8 or
|
if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().size().width() * 0.8 or
|
||||||
window.frameGeometry().height() > app.primaryScreen().availableGeometry().size().height() * 0.9):
|
window.frameGeometry().height() > app.primaryScreen().availableGeometry().size().height() * 0.9 or
|
||||||
|
__name__ != "__main__"):
|
||||||
# 设置滚动条
|
# 设置滚动条
|
||||||
areaScroll = QtWidgets.QScrollArea(window)
|
areaScroll = QtWidgets.QScrollArea(window)
|
||||||
areaScroll.setWidgetResizable(True)
|
areaScroll.setWidgetResizable(True)
|
||||||
@@ -2711,8 +2716,8 @@ if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().siz
|
|||||||
areaScroll.setFrameShape(QtWidgets.QFrame.NoFrame)
|
areaScroll.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
window.setCentralWidget(areaScroll)
|
window.setCentralWidget(areaScroll)
|
||||||
window.showMaximized() # 设置全屏
|
window.showMaximized() # 设置全屏
|
||||||
window.show()
|
|
||||||
|
|
||||||
window.show()
|
if (__name__ == "__main__"):
|
||||||
sys.exit(app.exec_())
|
window.show()
|
||||||
# Flag:解包只读control和解包全部读取
|
sys.exit(app.exec_())
|
||||||
|
# Flag:解包只读control和解包全部读取
|
||||||
|
|||||||
20
globalenv.py
Normal file
20
globalenv.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 用于实现主模块的变量可被子模块调用和读取
|
||||||
|
# 要在主模块和需要的子模块分别 import globalenv
|
||||||
|
# 然后需要在主模块进行初始化(_init),子模块不要重复 init
|
||||||
|
# 接着即可调用 set_value 和 get_value 存放/读取变量
|
||||||
|
def _init(): #初始化(在主模块初始化,不要在子模块重复 init)
|
||||||
|
global _global_dict
|
||||||
|
_global_dict = {}
|
||||||
|
|
||||||
|
def set_value(key :str, value):
|
||||||
|
""" 定义一个全局变量 """
|
||||||
|
_global_dict[key] = value
|
||||||
|
|
||||||
|
""" 获得一个全局变量,不存在则返回默认值 """
|
||||||
|
def get_value(key, defValue=None):
|
||||||
|
try:
|
||||||
|
return _global_dict[key]
|
||||||
|
except KeyError:
|
||||||
|
return defValue
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Version": "4.0.0",
|
"Version": "4.0.0.2",
|
||||||
"Time": "未知",
|
"Time": "未知",
|
||||||
"Thank": [
|
"Thank": [
|
||||||
"感谢 @り哥拽的冇气质° 和 @杨 提供了 3a5000(新世界的测试机器)",
|
"感谢 @り哥拽的冇气质° 和 @杨 提供了 3a5000(新世界的测试机器)",
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ CURRENT_DIR=$(dirname $(readlink -f "$0"))
|
|||||||
if [[ ! -d $TMPDIR/tmp ]]; then
|
if [[ ! -d $TMPDIR/tmp ]]; then
|
||||||
mkdir -p $TMPDIR/tmp
|
mkdir -p $TMPDIR/tmp
|
||||||
fi
|
fi
|
||||||
noVNCOption="--vnc localhost:5901"
|
noVNCOption="--listen localhost:6080"
|
||||||
VNCServerOption="-localhost yes"
|
VNCServerOption="-localhost yes"
|
||||||
if [[ -f $HOME/.config/deepin-wine-runner/vnc-public ]]; then
|
if [[ -f $HOME/.config/deepin-wine-runner/vnc-public ]]; then
|
||||||
unset noVNCOption
|
unset noVNCOption
|
||||||
unset VNCServerOption
|
unset VNCServerOption
|
||||||
fi
|
fi
|
||||||
if [[ $DISPLAY == "" ]] && [[ $WAYLAND_DISPLAY == "" ]]; then
|
if [[ $DISPLAY == "" ]] && [[ $WAYLAND_DISPLAY == "" ]] && [[ -f /data/data/com.termux/files/usr/bin/vncpasswd ]]; then
|
||||||
# 自动配置 NoVNC
|
# 自动配置 NoVNC
|
||||||
export DISPLAY=:5
|
export DISPLAY=:5
|
||||||
vncserver -kill :5
|
vncserver -kill :5
|
||||||
|
|||||||
@@ -50,8 +50,13 @@ from trans import *
|
|||||||
from Model import *
|
from Model import *
|
||||||
from DefaultSetting import *
|
from DefaultSetting import *
|
||||||
|
|
||||||
|
import globalenv
|
||||||
|
|
||||||
def PythonLower():
|
def PythonLower():
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
if (__name__ == "__main__"):
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
else:
|
||||||
|
app = globalenv.get_value("app")
|
||||||
QtWidgets.QMessageBox.critical(None, "错误", "Python 至少需要 3.6 及以上版本,目前版本:" + platform.python_version() + "")
|
QtWidgets.QMessageBox.critical(None, "错误", "Python 至少需要 3.6 及以上版本,目前版本:" + platform.python_version() + "")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -448,7 +453,7 @@ def about_this_program()->"显示“关于这个程序”窗口":
|
|||||||
if i[-4:] == ".svg" or i[-4:] == ".png":
|
if i[-4:] == ".svg" or i[-4:] == ".png":
|
||||||
iconPathList.append(f"{programPath}/Icon/{k}/{i}")
|
iconPathList.append(f"{programPath}/Icon/{k}/{i}")
|
||||||
except:
|
except:
|
||||||
traceback.print_exec()
|
traceback.print_exc()
|
||||||
randomNumber = random.randint(0, len(iconPathList) - 1)
|
randomNumber = random.randint(0, len(iconPathList) - 1)
|
||||||
iconShow.setText(f"<a href='https://www.gfdgdxi.top'><img width=256 src='{iconPathList[randomNumber]}'></a><p align='center'>{randomNumber + 1}/{len(iconPathList)}</p>")
|
iconShow.setText(f"<a href='https://www.gfdgdxi.top'><img width=256 src='{iconPathList[randomNumber]}'></a><p align='center'>{randomNumber + 1}/{len(iconPathList)}</p>")
|
||||||
iconShow.linkActivated.connect(ChangeIcon)
|
iconShow.linkActivated.connect(ChangeIcon)
|
||||||
@@ -2456,7 +2461,6 @@ def CheckWine():
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
|
||||||
QtWidgets.QMessageBox.critical(None, "错误", f"无法读取配置,无法继续\n{traceback.format_exc()}")
|
QtWidgets.QMessageBox.critical(None, "错误", f"无法读取配置,无法继续\n{traceback.format_exc()}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
CheckWine()
|
CheckWine()
|
||||||
@@ -2468,7 +2472,10 @@ print(wine)
|
|||||||
###########################
|
###########################
|
||||||
# 程序信息
|
# 程序信息
|
||||||
###########################
|
###########################
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
if (__name__ == "__main__"):
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
else:
|
||||||
|
app = globalenv.get_value("app")
|
||||||
trans = QtCore.QTranslator()
|
trans = QtCore.QTranslator()
|
||||||
transeObject = QtCore.QObject()
|
transeObject = QtCore.QObject()
|
||||||
transla = QtCore.QCoreApplication.translate
|
transla = QtCore.QCoreApplication.translate
|
||||||
@@ -2615,7 +2622,7 @@ else:
|
|||||||
#<h1>©2020~{time.strftime("%Y")} <a href="https://gitee.com/gfdgd-xi">By gfdgd xi</h1>'''
|
#<h1>©2020~{time.strftime("%Y")} <a href="https://gitee.com/gfdgd-xi">By gfdgd xi</h1>'''
|
||||||
updateThings = "{} 更新内容:\n{}\n更新时间:{}".format(version, updateThingsString, updateTime, time.strftime("%Y"))
|
updateThings = "{} 更新内容:\n{}\n更新时间:{}".format(version, updateThingsString, updateTime, time.strftime("%Y"))
|
||||||
try:
|
try:
|
||||||
threading.Thread(target=requests.get, args=[parse.unquote(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3NwYXJrLWRlZXBpbi13aW5lLXJ1bm5lci9vcGVuL0luc3RhbGwucGhw").decode("utf-8")) + "?Version=" + version]).start()
|
threading.Thread(target=requests.get, args=[parse.unquote(base64.b64decode("aHR0cHM6Ly9zb3VyY2Vmb3JnZS5uZXQvcHJvamVjdHMvZGVlcC13aW5lLXJ1bm5lci13aW5lLWRvd25sb2FkL2ZpbGVzL29wZW4tdGltZS8=").decode("utf-8")) + version + base64.b64decode("L2Rvd25sb2Fk").decode("utf-8")]).start()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
iconListUnBuild = json.loads(readtxt(f"{programPath}/IconList.json"))[0]
|
iconListUnBuild = json.loads(readtxt(f"{programPath}/IconList.json"))[0]
|
||||||
@@ -3381,8 +3388,10 @@ SetFont(setting["FontSize"])
|
|||||||
|
|
||||||
window.setCentralWidget(widget)
|
window.setCentralWidget(widget)
|
||||||
# 判断是否为小屏幕,是则设置滚动条并全屏
|
# 判断是否为小屏幕,是则设置滚动条并全屏
|
||||||
|
# 获取为 import 为控件,也默认开启滚动条
|
||||||
if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().size().width() * 0.8 or
|
if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().size().width() * 0.8 or
|
||||||
window.frameGeometry().height() > app.primaryScreen().availableGeometry().size().height() * 0.9):
|
window.frameGeometry().height() > app.primaryScreen().availableGeometry().size().height() * 0.9 or
|
||||||
|
__name__ != "__main__"):
|
||||||
# 设置滚动条
|
# 设置滚动条
|
||||||
areaScroll = QtWidgets.QScrollArea(window)
|
areaScroll = QtWidgets.QScrollArea(window)
|
||||||
areaScroll.setWidgetResizable(True)
|
areaScroll.setWidgetResizable(True)
|
||||||
@@ -3390,9 +3399,13 @@ if (window.frameGeometry().width() > app.primaryScreen().availableGeometry().siz
|
|||||||
areaScroll.setFrameShape(QtWidgets.QFrame.NoFrame)
|
areaScroll.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
window.setCentralWidget(areaScroll)
|
window.setCentralWidget(areaScroll)
|
||||||
window.showMaximized() # 设置全屏
|
window.showMaximized() # 设置全屏
|
||||||
window.show()
|
|
||||||
|
|
||||||
|
|
||||||
# Mini 模式
|
if (__name__ == "__main__"):
|
||||||
# MiniMode(True)
|
window.show()
|
||||||
sys.exit(app.exec_())
|
# Mini 模式
|
||||||
|
# MiniMode(True)
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
44
novnc-client/main.py
Normal file
44
novnc-client/main.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMainWindow
|
||||||
|
from qvncwidget import QVNCWidget
|
||||||
|
#logging.basicConfig(level=logging.DEBUG) # DEBUG及以上的日志信息都会显示
|
||||||
|
class Window(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super(Window, self).__init__()
|
||||||
|
|
||||||
|
self.setWindowTitle("QVNCWidget")
|
||||||
|
|
||||||
|
self.vnc = QVNCWidget(
|
||||||
|
parent=self,
|
||||||
|
host="127.0.0.1", port=5905,
|
||||||
|
readOnly=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setCentralWidget(self.vnc)
|
||||||
|
|
||||||
|
# you can disable mouse tracking if desired
|
||||||
|
self.vnc.setMouseTracking(True)
|
||||||
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
|
|
||||||
|
self.vnc.start()
|
||||||
|
|
||||||
|
def keyPressEvent(self, ev):
|
||||||
|
self.vnc.keyPressEvent(ev)
|
||||||
|
return super().keyPressEvent(ev) # in case you need the signal somewhere else in the window
|
||||||
|
|
||||||
|
def keyReleaseEvent(self, ev):
|
||||||
|
self.vnc.keyReleaseEvent(ev)
|
||||||
|
return super().keyReleaseEvent(ev) # in case you need the signal somewhere else in the window
|
||||||
|
|
||||||
|
def closeEvent(self, ev):
|
||||||
|
self.vnc.stop()
|
||||||
|
return super().closeEvent(ev)
|
||||||
|
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
window = Window()
|
||||||
|
window.resize(800, 600)
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
||||||
1
novnc-client/qvncwidget/__init__.py
Normal file
1
novnc-client/qvncwidget/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .qvncwidget import QVNCWidget
|
||||||
237
novnc-client/qvncwidget/easystruct.py
Normal file
237
novnc-client/qvncwidget/easystruct.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import struct
|
||||||
|
|
||||||
|
##
|
||||||
|
# reading
|
||||||
|
##
|
||||||
|
|
||||||
|
def read_float_buff(buffer, big_endian=False) -> float:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">f", buffer.read(4))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<f", buffer.read(4))[0]
|
||||||
|
|
||||||
|
def read_double_buff(buffer, big_endian=False) -> float:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">d", buffer.read(8))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<d", buffer.read(8))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_uint8_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">B", buffer.read(1))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<B", buffer.read(1))[0]
|
||||||
|
|
||||||
|
def read_uint16_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">H", buffer.read(2))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<H", buffer.read(2))[0]
|
||||||
|
|
||||||
|
def read_uint32_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">I", buffer.read(4))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<I", buffer.read(4))[0]
|
||||||
|
|
||||||
|
def read_uint64_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">Q", buffer.read(8))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<Q", buffer.read(8))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_sint8_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">b", buffer.read(1))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<b", buffer.read(1))[0]
|
||||||
|
|
||||||
|
def read_sint16_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">h", buffer.read(2))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<h", buffer.read(2))[0]
|
||||||
|
|
||||||
|
def read_sint32_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">i", buffer.read(4))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<i", buffer.read(4))[0]
|
||||||
|
|
||||||
|
def read_sint64_buff(buffer, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">q", buffer.read(8))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<q", buffer.read(8))[0]
|
||||||
|
|
||||||
|
##
|
||||||
|
# writing
|
||||||
|
##
|
||||||
|
|
||||||
|
def write_float_buff(buffer, value: float, big_endian=False) -> None:
|
||||||
|
buffer.write(return_float_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_double_buff(buffer, value: float, big_endian=False) -> None:
|
||||||
|
buffer.write(return_double_bytes(value, big_endian))
|
||||||
|
|
||||||
|
|
||||||
|
def write_uint8_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_uint8_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_uint16_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_uint16_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_uint32_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_uint32_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_uint64_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_uint64_bytes(value, big_endian))
|
||||||
|
|
||||||
|
|
||||||
|
def write_sint8_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_sint8_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_sint16_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_sint16_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_sint32_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_sint32_bytes(value, big_endian))
|
||||||
|
|
||||||
|
def write_sint64_buff(buffer, value: int, big_endian=False) -> None:
|
||||||
|
buffer.write(return_sint64_bytes(value, big_endian))
|
||||||
|
|
||||||
|
##
|
||||||
|
# return bytes
|
||||||
|
##
|
||||||
|
|
||||||
|
def return_float_bytes(value: float, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">f", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<f", value)
|
||||||
|
|
||||||
|
def return_double_bytes(value: float, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">d", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<d", value)
|
||||||
|
|
||||||
|
|
||||||
|
def return_uint8_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">B", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<B", value)
|
||||||
|
|
||||||
|
def return_uint16_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">H", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<H", value)
|
||||||
|
|
||||||
|
def return_uint32_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">I", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<I", value)
|
||||||
|
|
||||||
|
def return_uint64_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">Q", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<Q", value)
|
||||||
|
|
||||||
|
|
||||||
|
def return_sint8_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">b", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<b", value)
|
||||||
|
|
||||||
|
def return_sint16_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">h", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<h", value)
|
||||||
|
|
||||||
|
def return_sint32_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">i", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<i", value)
|
||||||
|
|
||||||
|
def return_sint64_bytes(value: int, big_endian=False) -> bytes:
|
||||||
|
if big_endian:
|
||||||
|
return struct.pack(">q", value)
|
||||||
|
else:
|
||||||
|
return struct.pack("<q", value)
|
||||||
|
|
||||||
|
##
|
||||||
|
# return val
|
||||||
|
##
|
||||||
|
|
||||||
|
def return_float_val(data: bytes, big_endian=False) -> float:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">f", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<f", data)[0]
|
||||||
|
|
||||||
|
def return_double_val(data: bytes, big_endian=False) -> float:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">d", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<d", data)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def return_uint8_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">B", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<B", data)[0]
|
||||||
|
|
||||||
|
def return_uint16_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">H", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<H", data)[0]
|
||||||
|
|
||||||
|
def return_uint32_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">I", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<I", data)[0]
|
||||||
|
|
||||||
|
def return_uint64_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">Q", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<Q", data)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def return_sint8_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">b", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<b", data)[0]
|
||||||
|
|
||||||
|
def return_sint16_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">h", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<h", data)[0]
|
||||||
|
|
||||||
|
def return_sint32_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">i", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<i", data)[0]
|
||||||
|
|
||||||
|
def return_sint64_val(data: bytes, big_endian=False) -> int:
|
||||||
|
if big_endian:
|
||||||
|
return struct.unpack(">q", data)[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("<q", data)[0]
|
||||||
690
novnc-client/qvncwidget/qvncwidget.py
Normal file
690
novnc-client/qvncwidget/qvncwidget.py
Normal file
@@ -0,0 +1,690 @@
|
|||||||
|
"""
|
||||||
|
Qt Widget for displaying VNC framebuffer using RFB protocol
|
||||||
|
|
||||||
|
(c) zocker-160 2024
|
||||||
|
licensed under GPLv3
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QSize,
|
||||||
|
Qt,
|
||||||
|
pyqtSignal,
|
||||||
|
QSemaphore
|
||||||
|
)
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QImage,
|
||||||
|
QPaintEvent,
|
||||||
|
QPainter,
|
||||||
|
QColor,
|
||||||
|
QBrush,
|
||||||
|
QPixmap,
|
||||||
|
QResizeEvent,
|
||||||
|
QKeyEvent,
|
||||||
|
QMouseEvent
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QLabel,
|
||||||
|
QWidget,
|
||||||
|
QOpenGLWidget
|
||||||
|
)
|
||||||
|
|
||||||
|
from qvncwidget.rfb import RFBClient
|
||||||
|
from qvncwidget.rfbhelpers import RFBPixelformat, RFBInput
|
||||||
|
|
||||||
|
log = logging.getLogger("QVNCWidget")
|
||||||
|
|
||||||
|
class QVNCWidget(QWidget, RFBClient):
|
||||||
|
|
||||||
|
onInitialResize = pyqtSignal(QSize)
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget,
|
||||||
|
host: str, port = 5900, password: str = None,
|
||||||
|
readOnly = False):
|
||||||
|
super().__init__(
|
||||||
|
parent=parent,
|
||||||
|
host=host, port=port, password=password
|
||||||
|
)
|
||||||
|
self.readOnly = readOnly
|
||||||
|
|
||||||
|
self.backbuffer: QImage = None
|
||||||
|
self.frontbuffer: QImage = None
|
||||||
|
|
||||||
|
self.setMouseTracking(not self.readOnly)
|
||||||
|
self.setMinimumSize(1, 1) # make window scalable
|
||||||
|
|
||||||
|
self.mouseButtonMask = 0
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.startConnection()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.closeConnection()
|
||||||
|
|
||||||
|
def onConnectionMade(self):
|
||||||
|
log.info("VNC handshake done")
|
||||||
|
|
||||||
|
self.setPixelFormat(RFBPixelformat.getRGB32())
|
||||||
|
|
||||||
|
self.PIX_FORMAT = QImage.Format.Format_RGB32
|
||||||
|
self.backbuffer = QImage(self.vncWidth, self.vncHeight, self.PIX_FORMAT)
|
||||||
|
self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
|
||||||
|
|
||||||
|
def onRectangleUpdate(self,
|
||||||
|
x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
|
||||||
|
if self.backbuffer is None:
|
||||||
|
log.warning("backbuffer is None")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
log.debug("drawing backbuffer")
|
||||||
|
|
||||||
|
#with open(f"{width}x{height}.data", "wb") as f:
|
||||||
|
# f.write(data)
|
||||||
|
|
||||||
|
t1 = time.time()
|
||||||
|
|
||||||
|
painter = QPainter(self.backbuffer)
|
||||||
|
painter.drawImage(x, y, QImage(data, width, height, self.PIX_FORMAT))
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
log.debug(f"painting took: {(time.time() - t1)*1e3} ms")
|
||||||
|
|
||||||
|
del painter
|
||||||
|
del data
|
||||||
|
|
||||||
|
def onFramebufferUpdateFinished(self):
|
||||||
|
log.debug("FB Update finished")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def paintEvent(self, a0: QPaintEvent):
|
||||||
|
#log.debug("Paint event")
|
||||||
|
painter = QPainter(self)
|
||||||
|
|
||||||
|
if self.backbuffer is None:
|
||||||
|
log.debug("backbuffer is None")
|
||||||
|
painter.fillRect(0, 0, self.width(), self.height(), Qt.GlobalColor.black)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.frontbuffer = self.backbuffer.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
|
Qt.TransformationMode.SmoothTransformation
|
||||||
|
)
|
||||||
|
painter.drawImage(0, 0, self.frontbuffer)
|
||||||
|
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
# Mouse events
|
||||||
|
|
||||||
|
def mousePressEvent(self, ev: QMouseEvent):
|
||||||
|
if self.readOnly or not self.frontbuffer: return
|
||||||
|
self.mouseButtonMask = RFBInput.fromQMouseEvent(ev, True, self.mouseButtonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev: QMouseEvent):
|
||||||
|
if self.readOnly or not self.frontbuffer: return
|
||||||
|
self.mouseButtonMask = RFBInput.fromQMouseEvent(ev, False, self.mouseButtonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, ev: QMouseEvent):
|
||||||
|
if self.readOnly or not self.frontbuffer: return
|
||||||
|
try:
|
||||||
|
# 忽略拖动导致的问题
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.mouseButtonMask)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
|
||||||
|
xPos = (ev.localPos().x() / self.frontbuffer.width()) * self.vncWidth
|
||||||
|
yPos = (ev.localPos().y() / self.frontbuffer.height()) * self.vncHeight
|
||||||
|
|
||||||
|
return int(xPos), int(yPos)
|
||||||
|
|
||||||
|
# Key events
|
||||||
|
|
||||||
|
def keyPressEvent(self, ev: QKeyEvent):
|
||||||
|
if self.readOnly: return
|
||||||
|
self.keyEvent(RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
|
||||||
|
|
||||||
|
def keyReleaseEvent(self, ev: QKeyEvent):
|
||||||
|
if self.readOnly: return
|
||||||
|
self.keyEvent(RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
|
||||||
|
|
||||||
|
|
||||||
|
# other experimental implementations
|
||||||
|
|
||||||
|
class QVNCWidgetGL(QOpenGLWidget, RFBClient):
|
||||||
|
|
||||||
|
IMG_FORMAT = QImage.Format_RGB32
|
||||||
|
|
||||||
|
onInitialResize = pyqtSignal(QSize)
|
||||||
|
#onUpdatePixmap = pyqtSignal(int, int, int, int, bytes)
|
||||||
|
onUpdatePixmap = pyqtSignal()
|
||||||
|
onSetPixmap = pyqtSignal()
|
||||||
|
|
||||||
|
onKeyPress = pyqtSignal(QKeyEvent)
|
||||||
|
onKeyRelease = pyqtSignal(QKeyEvent)
|
||||||
|
|
||||||
|
def __init__(self, parent,
|
||||||
|
host, port=5900, password: str=None,
|
||||||
|
mouseTracking=False):
|
||||||
|
|
||||||
|
#super(QOpenGLWidget, self).__init__()
|
||||||
|
#super(RFBClient, self).__init__(
|
||||||
|
super().__init__(
|
||||||
|
parent=parent,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
password=password,
|
||||||
|
daemonThread=True
|
||||||
|
)
|
||||||
|
|
||||||
|
#self.setAlignment(Qt.AlignCenter)
|
||||||
|
|
||||||
|
#self.onUpdatePixmap.connect(self._updateImage)
|
||||||
|
#self.onSetPixmap.connect(self._setImage)
|
||||||
|
self.onSetPixmap.connect(self._updateImage)
|
||||||
|
|
||||||
|
self.acceptMouseEvents = False # mouse events are not accepted at first
|
||||||
|
self.setMouseTracking(mouseTracking)
|
||||||
|
|
||||||
|
# Allow Resizing
|
||||||
|
self.setMinimumSize(1, 1)
|
||||||
|
|
||||||
|
self.data = list(tuple())
|
||||||
|
self.dataMonitor = QSemaphore(0)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.startConnection()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.closeConnection()
|
||||||
|
|
||||||
|
def onConnectionMade(self):
|
||||||
|
log.info("VNC handshake done")
|
||||||
|
|
||||||
|
self.setPixelFormat(RFBPixelformat.getRGB32())
|
||||||
|
self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
|
||||||
|
self._initKeypress()
|
||||||
|
self._initMouse()
|
||||||
|
|
||||||
|
def onRectangleUpdate(self,
|
||||||
|
x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
#img = QImage(data, width, height, self.IMG_FORMAT)
|
||||||
|
#self.onUpdatePixmap.emit(x, y, width, height, data)
|
||||||
|
|
||||||
|
#self.dataMonitor.acquire(1)
|
||||||
|
|
||||||
|
self.data.append((x, y, width, height, data))
|
||||||
|
#self.data = (x, y, width, height, data)
|
||||||
|
#self.dataMonitor.release(1)
|
||||||
|
|
||||||
|
#self.onUpdatePixmap.emit()
|
||||||
|
|
||||||
|
#else:
|
||||||
|
# print("AAAAAAAAAAAAAA", "MONITOR AQUIRE FAILED")
|
||||||
|
|
||||||
|
def onFramebufferUpdateFinished(self):
|
||||||
|
self.onSetPixmap.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.pixmap:
|
||||||
|
#self.setPixmap(QPixmap.fromImage(self.image))
|
||||||
|
self.resizeEvent(None)
|
||||||
|
|
||||||
|
def onFatalError(self, error: Exception):
|
||||||
|
log.error(str(error))
|
||||||
|
#logging.exception(str(error))
|
||||||
|
#self.reconnect()
|
||||||
|
|
||||||
|
#def _updateImage(self, x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
def _updateImage(self):
|
||||||
|
print("update image")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
#if not self.screen:
|
||||||
|
# self.screen = QImage(width, height, self.IMG_FORMAT)
|
||||||
|
# self.screen.fill(Qt.red)
|
||||||
|
# self.screenPainter = QPainter(self.screen)
|
||||||
|
|
||||||
|
#self.painter.beginNativePainting()
|
||||||
|
#self.painter.drawPixmapFragments()
|
||||||
|
|
||||||
|
#with open("/tmp/images/test.raw", "wb") as f:
|
||||||
|
# f.write(data)
|
||||||
|
|
||||||
|
#p = QPainter(self.screen)
|
||||||
|
|
||||||
|
#self.screenPainter.drawImage(
|
||||||
|
# x, y, QImage(data, width, height, self.IMG_FORMAT))
|
||||||
|
|
||||||
|
#p.end()
|
||||||
|
|
||||||
|
#self.repaint()
|
||||||
|
#self.update()
|
||||||
|
|
||||||
|
def _setPixmap(self):
|
||||||
|
if self.pixmap:
|
||||||
|
self.setPixmap(
|
||||||
|
self.pixmap.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _setImage(self):
|
||||||
|
if self.screen:
|
||||||
|
self.setPixmap(QPixmap.fromImage(
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
))
|
||||||
|
self.acceptMouseEvents = True # mouse events are getting accepted
|
||||||
|
|
||||||
|
# Passed events
|
||||||
|
|
||||||
|
def _keyPress(self, ev: QKeyEvent):
|
||||||
|
self.keyEvent(
|
||||||
|
RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
|
||||||
|
|
||||||
|
def _keyRelease(self, ev: QKeyEvent):
|
||||||
|
self.keyEvent(
|
||||||
|
RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
|
||||||
|
|
||||||
|
# Window events
|
||||||
|
|
||||||
|
def paintEvent(self, e: QPaintEvent):
|
||||||
|
print("paint event")
|
||||||
|
|
||||||
|
#self.dataMonitor.acquire(1)
|
||||||
|
|
||||||
|
#while self.dataMonitor.tryAcquire(1):
|
||||||
|
while len(self.data) > 0:
|
||||||
|
x, y, w, h, data = self.data.pop(0)
|
||||||
|
|
||||||
|
p = QPainter(self)
|
||||||
|
|
||||||
|
#p.setPen(QColor(255, 0, 0))
|
||||||
|
#p.drawText(e.rect(), Qt.AlignCenter, str(self.dataMonitor.available()))
|
||||||
|
|
||||||
|
p.drawImage(x, y, QImage(data, w, h, self.IMG_FORMAT))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
#self.dataMonitor.release(1)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
p = QPainter(self)
|
||||||
|
p.fillRect(e.rect(), QBrush(QColor(255, 255, 255)))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.dataMonitor.tryAcquire(1):
|
||||||
|
x, y, w, h, data = self.data
|
||||||
|
|
||||||
|
p = QPainter(self)
|
||||||
|
p.drawImage(x, y, QImage(data, w, h, self.IMG_FORMAT))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
self.dataMonitor.release(1)
|
||||||
|
|
||||||
|
print("CCCCC", "Image painted diggah")
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("BBBBBBBBB", "aquire monitor failed")
|
||||||
|
|
||||||
|
#return super().paintEvent(a0)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.screen:
|
||||||
|
self.screen = QImage(self.size(), self.IMG_FORMAT)
|
||||||
|
self.screen.fill(Qt.red)
|
||||||
|
self.screenPainter = QPainter(self.screen)
|
||||||
|
|
||||||
|
p = QPainter()
|
||||||
|
p.begin(self)
|
||||||
|
p.drawImage(0, 0,
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
def resizeEvent(self, e: QResizeEvent):
|
||||||
|
return super().resizeEvent(e)
|
||||||
|
|
||||||
|
def resizeGL(self, w: int, h: int):
|
||||||
|
print("RESIZE THAT BITCH!!!", w, h)
|
||||||
|
#return super().resizeGL(w, h)
|
||||||
|
|
||||||
|
def resizeEvent_(self, a0: QResizeEvent):
|
||||||
|
#print("RESIZE!", self.width(), self.height())
|
||||||
|
#return super().resizeEvent(a0)
|
||||||
|
if self.screen:
|
||||||
|
self.setPixmap(QPixmap.fromImage(
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return super().resizeEvent(a0)
|
||||||
|
|
||||||
|
def mousePressEvent(self, ev: QMouseEvent):
|
||||||
|
#print(ev.localPos(), ev.button())
|
||||||
|
#print(self.height() - self.pixmap().height())
|
||||||
|
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.buttonMask = RFBInput.fromQMouseEvent(ev, True, self.buttonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
return super().mousePressEvent(ev)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev: QMouseEvent):
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.buttonMask = RFBInput.fromQMouseEvent(ev, False, self.buttonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
return super().mouseReleaseEvent(ev)
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, ev: QMouseEvent):
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
# FIXME: The pixmap is assumed to be aligned center.
|
||||||
|
def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
|
||||||
|
# FIXME: this code is ugly as fk
|
||||||
|
|
||||||
|
# y coord is kinda fucked up
|
||||||
|
yDiff = (self.height() - self.pixmap().height()) / 2
|
||||||
|
yPos = ev.localPos().y() - yDiff
|
||||||
|
if yPos < 0: yPos = 0
|
||||||
|
if yPos > self.pixmap().height(): yPos = self.pixmap().height()
|
||||||
|
|
||||||
|
yPos = self._calcRemoteRel(
|
||||||
|
yPos, self.pixmap().height(), self.vncHeight)
|
||||||
|
|
||||||
|
# x coord is kinda fucked up, too
|
||||||
|
xDiff = (self.width() - self.pixmap().width()) / 2
|
||||||
|
xPos = ev.localPos().x() - xDiff
|
||||||
|
if xPos < 0: xPos = 0
|
||||||
|
if xPos > self.pixmap().width(): xPos = self.pixmap().width()
|
||||||
|
|
||||||
|
xPos = self._calcRemoteRel(
|
||||||
|
xPos, self.pixmap().width(), self.vncWidth)
|
||||||
|
|
||||||
|
return xPos, yPos
|
||||||
|
|
||||||
|
def _calcRemoteRel(self, locRel, locMax, remoteMax) -> int:
|
||||||
|
return int( (locRel / locMax) * remoteMax )
|
||||||
|
|
||||||
|
def _initMouse(self):
|
||||||
|
self.buttonMask = 0 # pressed buttons (bit fields)
|
||||||
|
|
||||||
|
def _initKeypress(self):
|
||||||
|
self.onKeyPress.connect(self._keyPress)
|
||||||
|
self.onKeyRelease.connect(self._keyRelease)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.stop()
|
||||||
|
self.deleteLater()
|
||||||
|
|
||||||
|
class QVNCWidget_old(QLabel, RFBClient):
|
||||||
|
|
||||||
|
IMG_FORMAT = QImage.Format_RGB32
|
||||||
|
|
||||||
|
onInitialResize = pyqtSignal(QSize)
|
||||||
|
onUpdatePixmap = pyqtSignal(int, int, int, int, bytes)
|
||||||
|
onSetPixmap = pyqtSignal()
|
||||||
|
|
||||||
|
onKeyPress = pyqtSignal(QKeyEvent)
|
||||||
|
onKeyRelease = pyqtSignal(QKeyEvent)
|
||||||
|
|
||||||
|
def __init__(self, parent,
|
||||||
|
host, port=5900, password: str=None,
|
||||||
|
mouseTracking=False):
|
||||||
|
super().__init__(
|
||||||
|
parent=parent,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
password=password,
|
||||||
|
daemonThread=True
|
||||||
|
)
|
||||||
|
#import faulthandler
|
||||||
|
#faulthandler.enable()
|
||||||
|
self.screen: QImage = None
|
||||||
|
|
||||||
|
# FIXME: The pixmap is assumed to be aligned center.
|
||||||
|
self.setAlignment(Qt.AlignCenter)
|
||||||
|
|
||||||
|
self.onUpdatePixmap.connect(self._updateImage)
|
||||||
|
self.onSetPixmap.connect(self._setImage)
|
||||||
|
|
||||||
|
self.acceptMouseEvents = False # mouse events are not accepted at first
|
||||||
|
self.setMouseTracking(mouseTracking)
|
||||||
|
|
||||||
|
# Allow Resizing
|
||||||
|
self.setMinimumSize(1,1)
|
||||||
|
|
||||||
|
def _initMouse(self):
|
||||||
|
self.buttonMask = 0 # pressed buttons (bit fields)
|
||||||
|
|
||||||
|
def _initKeypress(self):
|
||||||
|
self.onKeyPress.connect(self._keyPress)
|
||||||
|
self.onKeyRelease.connect(self._keyRelease)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.startConnection()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.closeConnection()
|
||||||
|
if self.screenPainter: self.screenPainter.end()
|
||||||
|
|
||||||
|
def onConnectionMade(self):
|
||||||
|
self.onInitialResize.emit(QSize(self.vncWidth, self.vncHeight))
|
||||||
|
self.setPixelFormat(RFBPixelformat.getRGB32())
|
||||||
|
self._initKeypress()
|
||||||
|
self._initMouse()
|
||||||
|
|
||||||
|
def onRectangleUpdate(self,
|
||||||
|
x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
#img = QImage(data, width, height, self.IMG_FORMAT)
|
||||||
|
self.onUpdatePixmap.emit(x, y, width, height, data)
|
||||||
|
|
||||||
|
def onFramebufferUpdateFinished(self):
|
||||||
|
self.onSetPixmap.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.pixmap:
|
||||||
|
#self.setPixmap(QPixmap.fromImage(self.image))
|
||||||
|
self.resizeEvent(None)
|
||||||
|
|
||||||
|
def onFatalError(self, error: Exception):
|
||||||
|
log.error(str(error))
|
||||||
|
#logging.exception(str(error))
|
||||||
|
#self.reconnect()
|
||||||
|
|
||||||
|
def _updateImage(self, x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
if not self.screen:
|
||||||
|
self.screen = QImage(width, height, self.IMG_FORMAT)
|
||||||
|
self.screen.fill(Qt.red)
|
||||||
|
self.screenPainter = QPainter(self.screen)
|
||||||
|
|
||||||
|
#self.painter.beginNativePainting()
|
||||||
|
#self.painter.drawPixmapFragments()
|
||||||
|
|
||||||
|
#with open("/tmp/images/test.raw", "wb") as f:
|
||||||
|
# f.write(data)
|
||||||
|
|
||||||
|
#p = QPainter(self.screen)
|
||||||
|
self.screenPainter.drawImage(
|
||||||
|
x, y, QImage(data, width, height, self.IMG_FORMAT))
|
||||||
|
#p.end()
|
||||||
|
|
||||||
|
#self.repaint()
|
||||||
|
#self.update()
|
||||||
|
|
||||||
|
def _drawPixmap(self, x: int, y: int, pix: QPixmap):
|
||||||
|
#self.paintLock.acquire()
|
||||||
|
self.pixmap = pix
|
||||||
|
|
||||||
|
if not self.painter:
|
||||||
|
self.painter = QPainter(self.pixmap)
|
||||||
|
else:
|
||||||
|
print("DRAW PIXMAP:", x, y, self.pixmap, self.painter, pix, pix.isNull())
|
||||||
|
self.painter.drawPixmap(x, y, self.pixmap)
|
||||||
|
#self.paintLock.release()
|
||||||
|
|
||||||
|
def _drawPixmap2(self, x: int, y: int, pix: QPixmap, data: bytes):
|
||||||
|
if not self.pixmap or (
|
||||||
|
x == 0 and y == 0 and
|
||||||
|
pix.width() == self.pixmap.width() and pix.height() == self.pixmap.height()):
|
||||||
|
|
||||||
|
self.pixmap = pix.copy()
|
||||||
|
self._setPixmap()
|
||||||
|
return
|
||||||
|
|
||||||
|
import time
|
||||||
|
print("DRAW PIXMAP:", x, y, self.pixmap.width(), self.pixmap.height(), pix.width(), pix.height())
|
||||||
|
_t = time.time()
|
||||||
|
#self.pixmap.save(f"/tmp/images/imgP_{_t}", "jpg")
|
||||||
|
#with open(f"/tmp/images/img_{_t}.raw", "wb") as f:
|
||||||
|
# f.write(data)
|
||||||
|
#pix.save(f"/tmp/images/img_{_t}", "jpg")
|
||||||
|
|
||||||
|
painter = QPainter(self.pixmap)
|
||||||
|
painter.drawPixmap(x, y, pix)
|
||||||
|
painter.end()
|
||||||
|
#self._setPixmap()
|
||||||
|
|
||||||
|
def _setPixmap(self):
|
||||||
|
if self.pixmap:
|
||||||
|
self.setPixmap(
|
||||||
|
self.pixmap.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _setImage(self):
|
||||||
|
if self.screen:
|
||||||
|
self.setPixmap(QPixmap.fromImage(
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
))
|
||||||
|
self.acceptMouseEvents = True # mouse events are getting accepted
|
||||||
|
|
||||||
|
# Passed events
|
||||||
|
|
||||||
|
def _keyPress(self, ev: QKeyEvent):
|
||||||
|
self.keyEvent(
|
||||||
|
RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=1)
|
||||||
|
|
||||||
|
def _keyRelease(self, ev: QKeyEvent):
|
||||||
|
self.keyEvent(
|
||||||
|
RFBInput.fromQKeyEvent(ev.key(), ev.text()), down=0)
|
||||||
|
|
||||||
|
# Window events
|
||||||
|
|
||||||
|
def paintEvent(self, a0: QPaintEvent):
|
||||||
|
return super().paintEvent(a0)
|
||||||
|
if not self.screen:
|
||||||
|
self.screen = QImage(self.size(), self.IMG_FORMAT)
|
||||||
|
self.screen.fill(Qt.red)
|
||||||
|
self.screenPainter = QPainter(self.screen)
|
||||||
|
|
||||||
|
p = QPainter()
|
||||||
|
p.begin(self)
|
||||||
|
p.drawImage(0, 0,
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
def resizeEvent(self, a0: QResizeEvent):
|
||||||
|
#print("RESIZE!", self.width(), self.height())
|
||||||
|
#return super().resizeEvent(a0)
|
||||||
|
if self.screen:
|
||||||
|
self.setPixmap(QPixmap.fromImage(
|
||||||
|
self.screen.scaled(
|
||||||
|
self.width(), self.height(),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return super().resizeEvent(a0)
|
||||||
|
|
||||||
|
def mousePressEvent(self, ev: QMouseEvent):
|
||||||
|
#print(ev.localPos(), ev.button())
|
||||||
|
#print(self.height() - self.pixmap().height())
|
||||||
|
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.buttonMask = RFBInput.fromQMouseEvent(ev, True, self.buttonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
return super().mousePressEvent(ev)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev: QMouseEvent):
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.buttonMask = RFBInput.fromQMouseEvent(ev, False, self.buttonMask)
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
return super().mouseReleaseEvent(ev)
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, ev: QMouseEvent):
|
||||||
|
if self.acceptMouseEvents: # need pixmap instance
|
||||||
|
self.pointerEvent(*self._getRemoteRel(ev), self.buttonMask)
|
||||||
|
|
||||||
|
# FIXME: The pixmap is assumed to be aligned center.
|
||||||
|
def _getRemoteRel(self, ev: QMouseEvent) -> tuple:
|
||||||
|
# FIXME: this code is ugly as fk
|
||||||
|
|
||||||
|
# y coord is kinda fucked up
|
||||||
|
yDiff = (self.height() - self.pixmap().height()) / 2
|
||||||
|
yPos = ev.localPos().y() - yDiff
|
||||||
|
if yPos < 0: yPos = 0
|
||||||
|
if yPos > self.pixmap().height(): yPos = self.pixmap().height()
|
||||||
|
|
||||||
|
yPos = self._calcRemoteRel(
|
||||||
|
yPos, self.pixmap().height(), self.vncHeight)
|
||||||
|
|
||||||
|
# x coord is kinda fucked up, too
|
||||||
|
xDiff = (self.width() - self.pixmap().width()) / 2
|
||||||
|
xPos = ev.localPos().x() - xDiff
|
||||||
|
if xPos < 0: xPos = 0
|
||||||
|
if xPos > self.pixmap().width(): xPos = self.pixmap().width()
|
||||||
|
|
||||||
|
xPos = self._calcRemoteRel(
|
||||||
|
xPos, self.pixmap().width(), self.vncWidth)
|
||||||
|
|
||||||
|
return xPos, yPos
|
||||||
|
|
||||||
|
def _calcRemoteRel(self, locRel, locMax, remoteMax) -> int:
|
||||||
|
return int( (locRel / locMax) * remoteMax )
|
||||||
|
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.stop()
|
||||||
|
self.deleteLater()
|
||||||
485
novnc-client/qvncwidget/rfb.py
Normal file
485
novnc-client/qvncwidget/rfb.py
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
"""
|
||||||
|
RFB protocol implementation, client side
|
||||||
|
|
||||||
|
(c) zocker-160 2024
|
||||||
|
licensed under GPLv3
|
||||||
|
|
||||||
|
References:
|
||||||
|
- http://www.realvnc.com/docs/rfbproto.pdf
|
||||||
|
- https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
|
||||||
|
"""
|
||||||
|
|
||||||
|
from qvncwidget.rfbhelpers import RFBPixelformat, RFBRectangle
|
||||||
|
from qvncwidget.rfbdes import RFBDes
|
||||||
|
import qvncwidget.rfbconstants as c
|
||||||
|
import qvncwidget.easystruct as es
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
from socket import SHUT_RDWR
|
||||||
|
import struct as s
|
||||||
|
import time
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
|
class RFBUnexpectedResponse(Exception):
|
||||||
|
pass
|
||||||
|
class RFBNoResponse(Exception):
|
||||||
|
pass
|
||||||
|
class RFBUnknownVersion(Exception):
|
||||||
|
pass
|
||||||
|
class RFBHandshakeFailed(Exception):
|
||||||
|
pass
|
||||||
|
class VNCAuthentificationFailed(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
SUPPORTED_VERSIONS = [
|
||||||
|
(3,3)
|
||||||
|
]
|
||||||
|
KNOWN_VERSIONS = [
|
||||||
|
(3,3), (3,6), (3,7), (3,8),
|
||||||
|
(4,0), (4,1),
|
||||||
|
(5,0)
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
3.3: official minimum version
|
||||||
|
3.6: UltraVNC
|
||||||
|
3.7: official
|
||||||
|
3.8: official
|
||||||
|
4.0: Intel AMT KVM
|
||||||
|
4.1: RealVNC 4.6
|
||||||
|
5.0: RealVNC 5.3
|
||||||
|
"""
|
||||||
|
|
||||||
|
SUPPORTED_ENCODINGS = [
|
||||||
|
c.ENC_RAW
|
||||||
|
]
|
||||||
|
|
||||||
|
MAX_BUFF_SIZE: int = 10*1024*1024 # 10MB
|
||||||
|
|
||||||
|
class RFBClient:
|
||||||
|
|
||||||
|
log = logging.getLogger("RFB Client")
|
||||||
|
logc = logging.getLogger("RFB -> Server")
|
||||||
|
logs = logging.getLogger("RFB Client <-")
|
||||||
|
|
||||||
|
pixformat: RFBPixelformat
|
||||||
|
numRectangles = 0
|
||||||
|
#rectanglePositions = list() # list[RFBRectangle]
|
||||||
|
|
||||||
|
_stop = False
|
||||||
|
_connected = False
|
||||||
|
_requestFrameBufferUpdate = False
|
||||||
|
_incrementalFrameBufferUpdate = True
|
||||||
|
|
||||||
|
def __init__(self, host, port = 5900,
|
||||||
|
password: str = None,
|
||||||
|
sharedConnection = True,
|
||||||
|
keepRequesting = True,
|
||||||
|
requestIncremental = True):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.password = password
|
||||||
|
self.sharedConn = sharedConnection
|
||||||
|
self._requestFrameBufferUpdate = keepRequesting
|
||||||
|
self._incrementalFrameBufferUpdate = requestIncremental
|
||||||
|
|
||||||
|
self._mainLoop: Thread = None
|
||||||
|
|
||||||
|
def __recv(self, expectedSize: int = None, maxSize=MAX_BUFF_SIZE) -> bytes:
|
||||||
|
if not expectedSize:
|
||||||
|
buffer = self.connection.recv(4096)
|
||||||
|
else:
|
||||||
|
buffer = self.connection.recv(expectedSize, socket.MSG_WAITALL)
|
||||||
|
|
||||||
|
if len(buffer) <= 50:
|
||||||
|
self.logs.debug(f"len: {len(buffer)} | {buffer}")
|
||||||
|
else:
|
||||||
|
self.logs.debug(f"{len(buffer)} Bytes | {len(buffer)//1024} KB")
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
def __send(self, data: bytes):
|
||||||
|
self.connection.send(data)
|
||||||
|
self.logc.debug(data)
|
||||||
|
|
||||||
|
def __start(self):
|
||||||
|
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.connection.connect( (self.host, self.port) )
|
||||||
|
self._handleInitial()
|
||||||
|
|
||||||
|
def __close(self):
|
||||||
|
self.log.debug("Closing connection")
|
||||||
|
|
||||||
|
if self.connection:
|
||||||
|
try:
|
||||||
|
self.connection.shutdown(SHUT_RDWR)
|
||||||
|
self.connection.close()
|
||||||
|
except OSError:
|
||||||
|
self.log.debug("TCP Connection already closed")
|
||||||
|
|
||||||
|
def _handleInitial(self):
|
||||||
|
buffer = self.__recv(12)
|
||||||
|
|
||||||
|
if b'\n' in buffer and buffer.startswith(b'RFB'):
|
||||||
|
maj, min = [int(x) for x in buffer[3:-1].split(b'.')]
|
||||||
|
self.log.info(f"RFB from server: {maj}.{min}")
|
||||||
|
|
||||||
|
if (maj, min) not in KNOWN_VERSIONS:
|
||||||
|
raise RFBUnknownVersion(f"Unknown RFB version by server: {maj}.{min}")
|
||||||
|
|
||||||
|
if (maj, min) not in SUPPORTED_VERSIONS:
|
||||||
|
# request highest supported version
|
||||||
|
|
||||||
|
# TODO: requested version must not be higher than
|
||||||
|
# the one offered by the server
|
||||||
|
maj, min = SUPPORTED_VERSIONS[-1]
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.__close()
|
||||||
|
raise RFBUnknownVersion(buffer)
|
||||||
|
|
||||||
|
self.version_maj, self.version_min = maj, min
|
||||||
|
|
||||||
|
# request supported RFB version
|
||||||
|
self.__send(f"RFB 00{maj}.00{min}\n".encode())
|
||||||
|
self.log.info("VNC connected")
|
||||||
|
|
||||||
|
if (maj, min) == (3,3):
|
||||||
|
self._handleAuth33(self.__recv(4))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.log.error(f"Missing AUTH implementation for {maj}.{min}")
|
||||||
|
|
||||||
|
def _handleAuth33(self, data: bytes):
|
||||||
|
"""
|
||||||
|
Handle security handshake for protocol version 3.3.
|
||||||
|
In this version, the server decides the protocol (failed, none or VNCAuth)
|
||||||
|
"""
|
||||||
|
auth = es.return_uint32_val(data, True)
|
||||||
|
|
||||||
|
if auth == c.AUTH_FAIL:
|
||||||
|
self._handleConnFailed(self.__recv(4))
|
||||||
|
elif auth == c.AUTH_NONE:
|
||||||
|
self._doClientInit()
|
||||||
|
elif auth == c.AUTH_VNCAUTH:
|
||||||
|
self._handleVNCAuth(self.__recv(16))
|
||||||
|
else:
|
||||||
|
self.__close()
|
||||||
|
raise RFBUnexpectedResponse(f"Unknown auth response {auth}")
|
||||||
|
|
||||||
|
def _doClientInit(self):
|
||||||
|
shared = 1 if self.sharedConn else 0
|
||||||
|
self.__send(es.return_uint8_bytes(shared, True))
|
||||||
|
self._handleServerInit(self.__recv(24))
|
||||||
|
|
||||||
|
def _handleServerInit(self, data: bytes):
|
||||||
|
try:
|
||||||
|
self.vncWidth, self.vncHeight, pixformat, namelen = s.unpack("!HH16sI", data)
|
||||||
|
except s.error as e:
|
||||||
|
self.log.error("Handshake failed")
|
||||||
|
self.__close()
|
||||||
|
raise RFBHandshakeFailed(e)
|
||||||
|
|
||||||
|
threading.Thread(target=a).start()
|
||||||
|
self.desktopname = self.__recv(namelen).decode()
|
||||||
|
self.log.debug(f"Connecting to \"{self.desktopname}\"")
|
||||||
|
|
||||||
|
pixformatData = s.unpack("!BBBBHHHBBBxxx", pixformat)
|
||||||
|
self.pixformat = RFBPixelformat(*pixformatData)
|
||||||
|
|
||||||
|
self.log.debug(f"Server Pixelformat: {self.pixformat}")
|
||||||
|
self.log.debug(f"Resolution: {self.vncWidth}x{self.vncHeight}")
|
||||||
|
|
||||||
|
# this should not be required, but some VNC servers (like QT QPA VNC)
|
||||||
|
# require this to send FramebufferUpdate
|
||||||
|
self.setEncodings(SUPPORTED_ENCODINGS)
|
||||||
|
|
||||||
|
self.onConnectionMade()
|
||||||
|
self._connected = True
|
||||||
|
|
||||||
|
# enter main request loop
|
||||||
|
self._mainRequestLoop()
|
||||||
|
|
||||||
|
def _handleVNCAuth(self, data: bytes):
|
||||||
|
self._VNCAuthChallenge = data
|
||||||
|
|
||||||
|
self.log.info("Requesting password")
|
||||||
|
self.vncRequestPassword()
|
||||||
|
self._handleVNCAuthResult(self.__recv(4))
|
||||||
|
|
||||||
|
def _handleVNCAuthResult(self, data: bytes):
|
||||||
|
try:
|
||||||
|
result = es.return_uint32_val(data)
|
||||||
|
except s.error as e:
|
||||||
|
raise VNCAuthentificationFailed(f"Authentication failed ({str(e)})")
|
||||||
|
self.log.debug(f"Auth result {result}")
|
||||||
|
|
||||||
|
if result == c.SMSG_AUTH_OK:
|
||||||
|
self._doClientInit()
|
||||||
|
elif result == c.SMSG_AUTH_FAIL:
|
||||||
|
if self.version_min > 7:
|
||||||
|
self._handleVNCAuthError(self.__recv(4))
|
||||||
|
else:
|
||||||
|
raise VNCAuthentificationFailed("Authentication failed")
|
||||||
|
elif result == c.SMSG_AUTH_TOOMANY:
|
||||||
|
raise VNCAuthentificationFailed("Too many login attempts")
|
||||||
|
else:
|
||||||
|
self.log.error(f"Unknown Auth response ({result})")
|
||||||
|
|
||||||
|
def _handleVNCAuthError(self, data: bytes):
|
||||||
|
waitfor = es.return_uint32_val(data)
|
||||||
|
raise VNCAuthentificationFailed(
|
||||||
|
f"Authentication failed ({self.__recv(waitfor)})")
|
||||||
|
|
||||||
|
def _handleConnFailed(self, data: bytes):
|
||||||
|
waitfor = es.return_uint32_val(data)
|
||||||
|
resp = self.__recv(waitfor)
|
||||||
|
|
||||||
|
self.__close()
|
||||||
|
raise RFBHandshakeFailed(resp)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Main request loop
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _mainRequestLoop(self):
|
||||||
|
time.sleep(0.2)
|
||||||
|
# first request is non incremental
|
||||||
|
self.framebufferUpdateRequest(incremental=False)
|
||||||
|
|
||||||
|
while not self._stop and self.connection:
|
||||||
|
try:
|
||||||
|
dType = self.__recv(1)
|
||||||
|
|
||||||
|
# when self.connection.close() is being called
|
||||||
|
# dType will be empty with length of 0
|
||||||
|
if len(dType) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
self._handleConnection(dType)
|
||||||
|
|
||||||
|
self.log.debug(f"processing update took: {(time.time() - start)*1e3} ms")
|
||||||
|
except socket.timeout:
|
||||||
|
self.log.debug("timeout triggered")
|
||||||
|
continue
|
||||||
|
except s.error as e:
|
||||||
|
self.log.exception(str(e))
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
self.onFatalError(e)
|
||||||
|
|
||||||
|
#print("AAA")
|
||||||
|
if self._requestFrameBufferUpdate:
|
||||||
|
self.framebufferUpdateRequest(
|
||||||
|
incremental=self._incrementalFrameBufferUpdate)
|
||||||
|
#print("BBB")
|
||||||
|
|
||||||
|
self.log.debug("loop exit")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Server -> Client messages
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _handleConnection(self, data: bytes):
|
||||||
|
msgid = es.return_uint8_val(data)
|
||||||
|
|
||||||
|
if msgid == c.SMSG_FBUPDATE:
|
||||||
|
# Framebuffer Update
|
||||||
|
self._handleFramebufferUpdate(self.__recv(3))
|
||||||
|
elif msgid == c.SMSG_BELL:
|
||||||
|
# bell
|
||||||
|
self.onBell()
|
||||||
|
elif msgid == c.SMSG_SERVERCUTTEXT:
|
||||||
|
# server cut text
|
||||||
|
self._handleServerCutText(self.__recv(7))
|
||||||
|
elif msgid == c.SMSG_SETCOLORMAP:
|
||||||
|
# set color map entries
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.log.warning(f"Unknown message type recieved (id {msgid})")
|
||||||
|
raise RFBUnexpectedResponse
|
||||||
|
|
||||||
|
def _handleServerCutText(self, data: bytes):
|
||||||
|
datalength = s.unpack("!xxxI", data)[0]
|
||||||
|
data = self.__recv(datalength)
|
||||||
|
|
||||||
|
self.log.debug(f"Server clipboard: {data}")
|
||||||
|
# TODO: create callback
|
||||||
|
|
||||||
|
def _handleFramebufferUpdate(self, data: bytes):
|
||||||
|
numRectangles = s.unpack("!xH", data)[0]
|
||||||
|
self.log.debug(f"numRectangles: {numRectangles}")
|
||||||
|
|
||||||
|
self.onBeginUpdate()
|
||||||
|
|
||||||
|
for _ in range(numRectangles):
|
||||||
|
self._handleRectangle(self.__recv(12))
|
||||||
|
|
||||||
|
self.onFramebufferUpdateFinished()
|
||||||
|
|
||||||
|
def _handleRectangle(self, data: bytes):
|
||||||
|
xPos, yPos, width, height, encoding = s.unpack("!HHHHI", data)
|
||||||
|
|
||||||
|
rect = RFBRectangle(xPos, yPos, width, height)
|
||||||
|
self.log.debug(f"RECT: {rect}")
|
||||||
|
|
||||||
|
if encoding == c.ENC_RAW:
|
||||||
|
size = (width*height*self.pixformat.bitspp) // 8
|
||||||
|
self.log.debug(f"expected size: {size}")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
data = self.__recv(expectedSize=size)
|
||||||
|
self.log.debug(f"fetching data took: {(time.time() - start)*1e3} ms")
|
||||||
|
|
||||||
|
self._decodeRAW(data, rect)
|
||||||
|
del data
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Unsupported encoding received ({encoding})")
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Image decoding stuff
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _decodeRAW(self, data: bytes, rectangle: RFBRectangle):
|
||||||
|
self.onRectangleUpdate(*rectangle.asTuple(), data)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Client -> Server messages
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def setPixelFormat(self, pixelformat: RFBPixelformat):
|
||||||
|
self.pixformat = pixelformat
|
||||||
|
pformat = s.pack("!BBBBHHHBBBxxx", *pixelformat.asTuple())
|
||||||
|
self.__send(s.pack("!Bxxx16s", c.CMSG_SETPIXELFORMAT, pformat))
|
||||||
|
|
||||||
|
def setEncodings(self, encodings: list):
|
||||||
|
self.__send(s.pack("!BxH", c.CMSG_SETENCODINGS, len(encodings)))
|
||||||
|
for encoding in encodings:
|
||||||
|
self.__send(es.return_sint32_bytes(encoding, True))
|
||||||
|
|
||||||
|
def framebufferUpdateRequest(self,
|
||||||
|
xPos=0, yPos=0, width=None, height=None,
|
||||||
|
incremental=False):
|
||||||
|
|
||||||
|
if not width: width = self.vncWidth - xPos
|
||||||
|
if not height: height = self.vncHeight - yPos
|
||||||
|
inc = 1 if incremental else 0
|
||||||
|
|
||||||
|
self.__send(s.pack(
|
||||||
|
"!BBHHHH",
|
||||||
|
c.CMSG_FBUPDATEREQ, inc,
|
||||||
|
xPos, yPos, width, height))
|
||||||
|
|
||||||
|
def keyEvent(self, key, down=1):
|
||||||
|
"""
|
||||||
|
For most ordinary keys, the "keysym" is the same as the corresponding ASCII value.
|
||||||
|
Other common keys are shown in the KEY_ constants
|
||||||
|
"""
|
||||||
|
self.log.debug(f'keyEvent: {key}, {"down" if down else "up"}')
|
||||||
|
|
||||||
|
self.__send(s.pack(
|
||||||
|
"!BBxxI",
|
||||||
|
c.CMSG_KEYEVENT, down, key))
|
||||||
|
|
||||||
|
def pointerEvent(self, x: int, y: int, buttommask=0):
|
||||||
|
"""
|
||||||
|
Indicates either pointer movement or a pointer button press or release. The pointer is
|
||||||
|
now at (x-position, y-position), and the current state of buttons 1 to 8 are represented
|
||||||
|
by bits 0 to 7 of button-mask respectively, 0 meaning up, 1 meaning down (pressed)
|
||||||
|
"""
|
||||||
|
if not self._connected: return
|
||||||
|
|
||||||
|
self.log.debug(f"pointerEvent: {x}, {y}, {buttommask}")
|
||||||
|
|
||||||
|
self.__send(s.pack(
|
||||||
|
"!BBHH",
|
||||||
|
c.CMSG_POINTEREVENT, buttommask, x, y))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Direct Calls
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def startConnection(self):
|
||||||
|
self._mainLoop = Thread(target=self.__start)
|
||||||
|
self._mainLoop.start()
|
||||||
|
|
||||||
|
def sendPassword(self, password):
|
||||||
|
if type(password) is str:
|
||||||
|
password = password.encode("ascii")
|
||||||
|
password = (password + bytes(8))[:8]
|
||||||
|
des = RFBDes(password)
|
||||||
|
self.__send(des.encrypt(self._VNCAuthChallenge))
|
||||||
|
|
||||||
|
def reconnect(self):
|
||||||
|
self.closeConnection()
|
||||||
|
self.startConnection()
|
||||||
|
|
||||||
|
def closeConnection(self):
|
||||||
|
self._stop = True
|
||||||
|
self._connected = False
|
||||||
|
self.__close()
|
||||||
|
|
||||||
|
if self._mainLoop and self._mainLoop.is_alive():
|
||||||
|
self.log.debug("waiting for main loop to exit")
|
||||||
|
self._mainLoop.join()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
## Callbacks
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def onConnectionMade(self):
|
||||||
|
"""
|
||||||
|
connection is initialized and ready
|
||||||
|
the pixel format and encodings can be set here using
|
||||||
|
|
||||||
|
setPixelFormat() and setEncodings()
|
||||||
|
|
||||||
|
the RFB main update loop will start after this function is done
|
||||||
|
"""
|
||||||
|
|
||||||
|
def onBeginUpdate(self):
|
||||||
|
"""
|
||||||
|
called before a series of updateRectangle(),
|
||||||
|
copyRectangle() or fillRectangle().
|
||||||
|
"""
|
||||||
|
|
||||||
|
def onRectangleUpdate(self,
|
||||||
|
x: int, y: int, width: int, height: int, data: bytes):
|
||||||
|
"""
|
||||||
|
new bitmap data. data are bytes in the pixel format set
|
||||||
|
up earlier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def onFramebufferUpdateFinished(self):
|
||||||
|
"""
|
||||||
|
called after a series of updateRectangle(), copyRectangle()
|
||||||
|
or fillRectangle() are finished.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def onBell(self):
|
||||||
|
"""
|
||||||
|
a bell, yes that's right a BELL
|
||||||
|
"""
|
||||||
|
|
||||||
|
def vncRequestPassword(self):
|
||||||
|
"""
|
||||||
|
a password is needed to log on, use sendPassword() to
|
||||||
|
send one.
|
||||||
|
"""
|
||||||
|
if not self.password:
|
||||||
|
raise VNCAuthentificationFailed("No password specified")
|
||||||
|
else:
|
||||||
|
self.sendPassword(self.password)
|
||||||
|
|
||||||
|
def onFatalError(self, error: Exception):
|
||||||
|
"""
|
||||||
|
called when fatal error occurs
|
||||||
|
which caused the main loop to crash
|
||||||
|
|
||||||
|
you can try to reconnect here with reconnect()
|
||||||
|
"""
|
||||||
|
raise error
|
||||||
165
novnc-client/qvncwidget/rfbconstants.py
Normal file
165
novnc-client/qvncwidget/rfbconstants.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
## Encoding Type for SetEncodings()
|
||||||
|
# publicly documented
|
||||||
|
ENC_RAW = 0 # Raw
|
||||||
|
ENC_COPYRECT = 1 # CopyRect
|
||||||
|
ENC_RRE = 2 # RRE
|
||||||
|
ENC_HEXTILE = 5 # Hextile
|
||||||
|
ENC_TRLE = 15 # TRLE
|
||||||
|
ENC_ZRLE = 16 # ZRLE
|
||||||
|
|
||||||
|
# pseudo-encodings
|
||||||
|
ENC_CURSOR = -239 # Cursor position pseudo-encoding
|
||||||
|
ENC_DESKTOPSIZE = -223 # DesktopSize pseudo-encoding
|
||||||
|
|
||||||
|
# additional
|
||||||
|
ENC_CORRE = 4
|
||||||
|
ENC_ZLIB = 6
|
||||||
|
ENC_TIGHT = 7
|
||||||
|
ENC_ZLIBHEX = 8
|
||||||
|
|
||||||
|
|
||||||
|
## Keycodes for KeyEvent()
|
||||||
|
KEY_BackSpace = 0xff08
|
||||||
|
KEY_Tab = 0xff09
|
||||||
|
KEY_Return = 0xff0d
|
||||||
|
KEY_Escape = 0xff1b
|
||||||
|
KEY_Insert = 0xff63
|
||||||
|
KEY_Delete = 0xffff
|
||||||
|
KEY_Home = 0xff50
|
||||||
|
KEY_End = 0xff57
|
||||||
|
KEY_PageUp = 0xff55
|
||||||
|
KEY_PageDown = 0xff56
|
||||||
|
KEY_Left = 0xff51
|
||||||
|
KEY_Up = 0xff52
|
||||||
|
KEY_Right = 0xff53
|
||||||
|
KEY_Down = 0xff54
|
||||||
|
KEY_F1 = 0xffbe
|
||||||
|
KEY_F2 = 0xffbf
|
||||||
|
KEY_F3 = 0xffc0
|
||||||
|
KEY_F4 = 0xffc1
|
||||||
|
KEY_F5 = 0xffc2
|
||||||
|
KEY_F6 = 0xffc3
|
||||||
|
KEY_F7 = 0xffc4
|
||||||
|
KEY_F8 = 0xffc5
|
||||||
|
KEY_F9 = 0xffc6
|
||||||
|
KEY_F10 = 0xffc7
|
||||||
|
KEY_F11 = 0xffc8
|
||||||
|
KEY_F12 = 0xffc9
|
||||||
|
KEY_F13 = 0xFFCA
|
||||||
|
KEY_F14 = 0xFFCB
|
||||||
|
KEY_F15 = 0xFFCC
|
||||||
|
KEY_F16 = 0xFFCD
|
||||||
|
KEY_F17 = 0xFFCE
|
||||||
|
KEY_F18 = 0xFFCF
|
||||||
|
KEY_F19 = 0xFFD0
|
||||||
|
KEY_F20 = 0xFFD1
|
||||||
|
KEY_ShiftLeft = 0xffe1
|
||||||
|
KEY_ShiftRight = 0xffe2
|
||||||
|
KEY_ControlLeft = 0xffe3
|
||||||
|
KEY_ControlRight = 0xffe4
|
||||||
|
KEY_MetaLeft = 0xffe7
|
||||||
|
KEY_MetaRight = 0xffe8
|
||||||
|
KEY_AltLeft = 0xffe9
|
||||||
|
KEY_AltRight = 0xffea
|
||||||
|
|
||||||
|
KEY_Scroll_Lock = 0xFF14
|
||||||
|
KEY_Sys_Req = 0xFF15
|
||||||
|
KEY_Num_Lock = 0xFF7F
|
||||||
|
KEY_Caps_Lock = 0xFFE5
|
||||||
|
KEY_Pause = 0xFF13
|
||||||
|
KEY_Super_L = 0xFFEB
|
||||||
|
KEY_Super_R = 0xFFEC
|
||||||
|
KEY_Hyper_L = 0xFFED
|
||||||
|
KEY_Hyper_R = 0xFFEE
|
||||||
|
|
||||||
|
KEY_KP_0 = 0xFFB0
|
||||||
|
KEY_KP_1 = 0xFFB1
|
||||||
|
KEY_KP_2 = 0xFFB2
|
||||||
|
KEY_KP_3 = 0xFFB3
|
||||||
|
KEY_KP_4 = 0xFFB4
|
||||||
|
KEY_KP_5 = 0xFFB5
|
||||||
|
KEY_KP_6 = 0xFFB6
|
||||||
|
KEY_KP_7 = 0xFFB7
|
||||||
|
KEY_KP_8 = 0xFFB8
|
||||||
|
KEY_KP_9 = 0xFFB9
|
||||||
|
KEY_KP_Enter = 0xFF8D
|
||||||
|
|
||||||
|
# thanks to ken3 (https://github.com/ken3) for this
|
||||||
|
KEY_TRANSLATION_SPECIAL = {
|
||||||
|
Qt.Key.Key_Backspace: KEY_BackSpace,
|
||||||
|
Qt.Key.Key_Tab: KEY_Tab,
|
||||||
|
Qt.Key.Key_Return: KEY_Return,
|
||||||
|
Qt.Key.Key_Escape: KEY_Escape,
|
||||||
|
Qt.Key.Key_Insert: KEY_Insert,
|
||||||
|
Qt.Key.Key_Delete: KEY_Delete,
|
||||||
|
Qt.Key.Key_Home: KEY_Home,
|
||||||
|
Qt.Key.Key_End: KEY_End,
|
||||||
|
Qt.Key.Key_PageUp: KEY_PageUp,
|
||||||
|
Qt.Key.Key_PageDown: KEY_PageDown,
|
||||||
|
Qt.Key.Key_Left: KEY_Left,
|
||||||
|
Qt.Key.Key_Up: KEY_Up,
|
||||||
|
Qt.Key.Key_Right: KEY_Right,
|
||||||
|
Qt.Key.Key_Down: KEY_Down,
|
||||||
|
Qt.Key.Key_F1: KEY_F1,
|
||||||
|
Qt.Key.Key_F2: KEY_F2,
|
||||||
|
Qt.Key.Key_F3: KEY_F3,
|
||||||
|
Qt.Key.Key_F4: KEY_F4,
|
||||||
|
Qt.Key.Key_F5: KEY_F5,
|
||||||
|
Qt.Key.Key_F6: KEY_F6,
|
||||||
|
Qt.Key.Key_F7: KEY_F7,
|
||||||
|
Qt.Key.Key_F8: KEY_F8,
|
||||||
|
Qt.Key.Key_F9: KEY_F9,
|
||||||
|
Qt.Key.Key_F10: KEY_F10,
|
||||||
|
Qt.Key.Key_F11: KEY_F11,
|
||||||
|
Qt.Key.Key_F12: KEY_F12,
|
||||||
|
Qt.Key.Key_F13: KEY_F13,
|
||||||
|
Qt.Key.Key_F14: KEY_F14,
|
||||||
|
Qt.Key.Key_F15: KEY_F15,
|
||||||
|
Qt.Key.Key_F16: KEY_F16,
|
||||||
|
Qt.Key.Key_F17: KEY_F17,
|
||||||
|
Qt.Key.Key_F18: KEY_F18,
|
||||||
|
Qt.Key.Key_F19: KEY_F19,
|
||||||
|
Qt.Key.Key_F20: KEY_F20,
|
||||||
|
Qt.Key.Key_Shift: KEY_ShiftLeft,
|
||||||
|
Qt.Key.Key_Control: KEY_ControlLeft,
|
||||||
|
Qt.Key.Key_Meta: KEY_MetaLeft,
|
||||||
|
Qt.Key.Key_Alt: KEY_AltLeft,
|
||||||
|
Qt.Key.Key_ScrollLock: KEY_Scroll_Lock,
|
||||||
|
Qt.Key.Key_SysReq: KEY_Sys_Req,
|
||||||
|
Qt.Key.Key_NumLock: KEY_Num_Lock,
|
||||||
|
Qt.Key.Key_CapsLock: KEY_Caps_Lock,
|
||||||
|
Qt.Key.Key_Pause: KEY_Pause,
|
||||||
|
Qt.Key.Key_Super_L: KEY_Super_L,
|
||||||
|
Qt.Key.Key_Super_R: KEY_Super_R,
|
||||||
|
Qt.Key.Key_Hyper_L: KEY_Hyper_L,
|
||||||
|
Qt.Key.Key_Hyper_R: KEY_Hyper_R,
|
||||||
|
Qt.Key.Key_Enter: KEY_KP_Enter,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Authentication protocol types
|
||||||
|
AUTH_FAIL = 0
|
||||||
|
AUTH_NONE = 1
|
||||||
|
AUTH_VNCAUTH = 2
|
||||||
|
|
||||||
|
# Authentication result types
|
||||||
|
SMSG_AUTH_OK = 0
|
||||||
|
SMSG_AUTH_FAIL = 1
|
||||||
|
SMSG_AUTH_TOOMANY = 2
|
||||||
|
|
||||||
|
# Server message types
|
||||||
|
SMSG_FBUPDATE = 0
|
||||||
|
SMSG_SETCOLORMAP = 1
|
||||||
|
SMSG_BELL = 2
|
||||||
|
SMSG_SERVERCUTTEXT = 3
|
||||||
|
|
||||||
|
|
||||||
|
# Client message types
|
||||||
|
CMSG_SETPIXELFORMAT = 0
|
||||||
|
CMSG_SETENCODINGS = 2
|
||||||
|
CMSG_FBUPDATEREQ = 3
|
||||||
|
CMSG_KEYEVENT = 4
|
||||||
|
CMSG_POINTEREVENT = 5
|
||||||
|
CMSG_CLIENTCUTTEXT = 6
|
||||||
19
novnc-client/qvncwidget/rfbdes.py
Normal file
19
novnc-client/qvncwidget/rfbdes.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
import pyDes
|
||||||
|
|
||||||
|
class RFBDes(pyDes.des):
|
||||||
|
def setKey(self, key):
|
||||||
|
"""
|
||||||
|
RFB protocol for authentication requires client to encrypt
|
||||||
|
challenge sent by server with password using DES method. However,
|
||||||
|
bits in each byte of the password are put in reverse order before
|
||||||
|
using it as encryption key.
|
||||||
|
"""
|
||||||
|
newkey = list()
|
||||||
|
for bsrc in key:
|
||||||
|
btgt = 0
|
||||||
|
for i in range(8):
|
||||||
|
if bsrc & (1 << i):
|
||||||
|
btgt = btgt | (1 << 7-i)
|
||||||
|
newkey.append(btgt)
|
||||||
|
super(RFBDes, self).setKey(newkey)
|
||||||
106
novnc-client/qvncwidget/rfbhelpers.py
Normal file
106
novnc-client/qvncwidget/rfbhelpers.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
import logging
|
||||||
|
import qvncwidget.rfbconstants as c
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QMouseEvent
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
class RFBPixelformat:
|
||||||
|
def __init__(self,
|
||||||
|
bpp=32, depth=24, bigendian=False, truecolor=True,
|
||||||
|
redmax=255, greenmax=255, bluemax=255,
|
||||||
|
redshift=0, greenshift=0, blueshift=16):
|
||||||
|
|
||||||
|
self.bitspp = bpp
|
||||||
|
self.depth = depth
|
||||||
|
self.bigendian = 1 if bigendian else 0
|
||||||
|
self.truecolor = 1 if truecolor else 0
|
||||||
|
|
||||||
|
self.redmax = redmax
|
||||||
|
self.greenmax = greenmax
|
||||||
|
self.bluemax = bluemax
|
||||||
|
|
||||||
|
self.redshift = redshift
|
||||||
|
self.greenshift = greenshift
|
||||||
|
self.blueshift = blueshift
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getRGB32():
|
||||||
|
return RFBPixelformat(
|
||||||
|
bpp=32, depth=32,
|
||||||
|
redshift=16, greenshift=8, blueshift=0
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getRGB16():
|
||||||
|
return RFBPixelformat(
|
||||||
|
bpp=16, depth=16,
|
||||||
|
redmax=31, greenmax=63, bluemax=31,
|
||||||
|
redshift=11, greenshift=5, blueshift=0
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getRGB555():
|
||||||
|
return RFBPixelformat(
|
||||||
|
bpp=16, depth=15,
|
||||||
|
redmax=31, greenmax=31, bluemax=31,
|
||||||
|
redshift=10, greenshift=5, blueshift=0
|
||||||
|
)
|
||||||
|
|
||||||
|
def asTuple(self) -> tuple:
|
||||||
|
return (
|
||||||
|
self.bitspp, self.depth, self.bigendian, self.truecolor,
|
||||||
|
self.redmax, self.greenmax, self.bluemax,
|
||||||
|
self.redshift, self.greenshift, self.blueshift
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return ";".join(str(x) for x in self.asTuple())
|
||||||
|
|
||||||
|
class RFBRectangle:
|
||||||
|
def __init__(self, xPos: int, yPos: int, width: int, height: int):
|
||||||
|
self.xPos = xPos
|
||||||
|
self.yPos = yPos
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
def asTuple(self) -> tuple:
|
||||||
|
return (self.xPos, self.yPos, self.width, self.height)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"x: {self.xPos} y: {self.yPos} width: {self.width} height: {self.height}"
|
||||||
|
|
||||||
|
class RFBInput:
|
||||||
|
|
||||||
|
# thanks to ken3 (https://github.com/ken3) for this
|
||||||
|
MOUSE_MAPPING = {
|
||||||
|
Qt.LeftButton: 1 << 0,
|
||||||
|
Qt.MidButton: 1 << 1,
|
||||||
|
Qt.RightButton: 1 << 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fromQKeyEvent(eventID: int, eventStr: str) -> int:
|
||||||
|
rfbKey = c.KEY_TRANSLATION_SPECIAL.get(eventID)
|
||||||
|
|
||||||
|
if not rfbKey:
|
||||||
|
try:
|
||||||
|
rfbKey = ord(eventStr)
|
||||||
|
except TypeError:
|
||||||
|
logging.warning(f"Unknown keytype: {eventID} | {eventStr}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return rfbKey
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fromQMouseEvent(eventID: QMouseEvent, pressEvent: bool, mask) -> int:
|
||||||
|
_mask = RFBInput.MOUSE_MAPPING.get(eventID.button())
|
||||||
|
|
||||||
|
# FIXME: return previous bitmask in case unknown key is pressed
|
||||||
|
# TODO: implement all RFB supported buttons
|
||||||
|
if not _mask: return mask
|
||||||
|
|
||||||
|
if pressEvent:
|
||||||
|
return mask | _mask
|
||||||
|
else:
|
||||||
|
return mask & ~_mask
|
||||||
@@ -2,43 +2,19 @@
|
|||||||
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
|
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
|
||||||
# /opt 目录识别
|
# /opt 目录识别
|
||||||
option=""
|
option=""
|
||||||
for path in `ls /opt`
|
|
||||||
do
|
if [[ -d /usr/lib32 ]] && [[ -d $SHELL_FOLDER/lib32 ]]; then
|
||||||
echo /opt/$path
|
option="$option --dev-bind $SHELL_FOLDER/lib32 /usr/lib32 "
|
||||||
if [[ $path != wine-staging ]]; then
|
fi
|
||||||
# 支持识别正确的 wine
|
|
||||||
mkdir -pv "$SHELL_FOLDER/opt/$path"
|
if [[ -d /usr/lib64 ]] && [[ -d $SHELL_FOLDER/lib64 ]]; then
|
||||||
option="$option --dev-bind /opt/$path /opt/$path"
|
option="$option --dev-bind $SHELL_FOLDER/lib64 /usr/lib64 "
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
wineName=(deepin-wine
|
|
||||||
deepin-wine8-stable
|
|
||||||
deepin-wine6-stable
|
|
||||||
deepin-wine5-stable
|
|
||||||
spark-wine
|
|
||||||
spark-wine8
|
|
||||||
deepin-wine6-vannila
|
|
||||||
spark-wine7-devel
|
|
||||||
spark-wine8-wow
|
|
||||||
deepin-wine5
|
|
||||||
ukylin-wine
|
|
||||||
okylin-wine
|
|
||||||
bookworm-run
|
|
||||||
)
|
|
||||||
for i in ${wineName[*]}; do
|
|
||||||
if [[ -e /usr/bin/$i ]]; then
|
|
||||||
option="$option --dev-bind /usr/bin/$i /usr/bin/$i"
|
|
||||||
if [[ ! -e "$SHELL_FOLDER/bin/$i" ]]; then
|
|
||||||
touch "$SHELL_FOLDER/bin/$i"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
"$SHELL_FOLDER/bwrap" --dev-bind / / \
|
"$SHELL_FOLDER/bwrap" --dev-bind / / \
|
||||||
--dev-bind "$SHELL_FOLDER/opt" /opt \
|
|
||||||
--dev-bind "$SHELL_FOLDER/bin" /usr/bin \
|
--dev-bind "$SHELL_FOLDER/bin" /usr/bin \
|
||||||
--dev-bind "$SHELL_FOLDER/lib" /usr/lib \
|
--dev-bind "$SHELL_FOLDER/lib" /usr/lib \
|
||||||
--dev-bind "$SHELL_FOLDER/lib32" /usr/lib32 \
|
|
||||||
--dev-bind "$SHELL_FOLDER/lib64" /usr/lib64 \
|
|
||||||
--dev-bind /usr/lib/locale /usr/lib/locale \
|
--dev-bind /usr/lib/locale /usr/lib/locale \
|
||||||
|
--dev-bind "$SHELL_FOLDER/share" /usr/share \
|
||||||
$option \
|
$option \
|
||||||
$SHELL_FOLDER/runner/deepin-wine-runner
|
$SHELL_FOLDER/runner/deepin-wine-runner $*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Version": "4.0.0",
|
"Version": "4.0.0.2",
|
||||||
"Time": "未知",
|
"Time": "未知",
|
||||||
"Thank": [
|
"Thank": [
|
||||||
"感谢 @り哥拽的冇气质° 和 @杨 提供了 3a5000(新世界的测试机器)",
|
"感谢 @り哥拽的冇气质° 和 @杨 提供了 3a5000(新世界的测试机器)",
|
||||||
|
|||||||
18
test.py
Normal file
18
test.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import globalenv
|
||||||
|
import PyQt5.QtWidgets as QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
window = QtWidgets.QMainWindow()
|
||||||
|
# globalenv 的 init 是必须的,这样才能正确的 import Wine 运行器的窗口
|
||||||
|
globalenv._init()
|
||||||
|
globalenv.set_value("app", app) # 用于将该部分的 app 给子模块的 Qt 控件调用以解决 UI 异常以及其它问题
|
||||||
|
#import deepin_wine_packager
|
||||||
|
#modules = __import__("deepin-wine-packager")
|
||||||
|
#modules = __import__("deepin-wine-easy-packager")
|
||||||
|
#import mainwindow
|
||||||
|
# 使用 __import__ 可以引入带 - 文件名的模块
|
||||||
|
import wine.installwine
|
||||||
|
window.setCentralWidget(wine.installwine.window)
|
||||||
|
window.show()
|
||||||
|
app.exec_()
|
||||||
154
wine/installwine
154
wine/installwine
@@ -21,6 +21,7 @@ import webbrowser
|
|||||||
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
||||||
sys.path.append(f"{programPath}/../")
|
sys.path.append(f"{programPath}/../")
|
||||||
from Model import *
|
from Model import *
|
||||||
|
import globalenv
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
|
||||||
# UI 布局(自动生成)
|
# UI 布局(自动生成)
|
||||||
@@ -230,7 +231,29 @@ class GetInfo():
|
|||||||
"/usr/lib/mips64el-linux-gnuabi64/ld.so.1",
|
"/usr/lib/mips64el-linux-gnuabi64/ld.so.1",
|
||||||
"/usr/lib/powerpc64le-linux-gnu/ld64.so.2",
|
"/usr/lib/powerpc64le-linux-gnu/ld64.so.2",
|
||||||
"/usr/lib/loongarch64-linux-gnu/ld-linux-loongarch-lp64d.so.1",
|
"/usr/lib/loongarch64-linux-gnu/ld-linux-loongarch-lp64d.so.1",
|
||||||
"/usr/lib/loongarch64-linux-gnu/ld.so.1"
|
"/usr/lib/loongarch64-linux-gnu/ld.so.1",
|
||||||
|
"/usr/lib/ld-linux.so.2",
|
||||||
|
"/usr/lib/ld-linux-x86-64.so.2",
|
||||||
|
"/usr/lib/ld-linux-armhf.so.3",
|
||||||
|
"/usr/lib/ld-linux.so.3",
|
||||||
|
"/usr/lib/ld-linux-aarch64.so.1",
|
||||||
|
"/usr/lib/ld-linux-aarch64.so.1",
|
||||||
|
"/usr/lib/ld-linux-riscv64-lp64d.so.1",
|
||||||
|
"/usr/lib/ld.so.1",
|
||||||
|
"/usr/lib/ld64.so.2",
|
||||||
|
"/usr/lib/ld-linux-loongarch-lp64d.so.1",
|
||||||
|
"/usr/lib64/ld.so.1",
|
||||||
|
"/usr/lib64/ld-linux.so.2",
|
||||||
|
"/usr/lib64/ld-linux-x86-64.so.2",
|
||||||
|
"/usr/lib64/ld-linux-armhf.so.3",
|
||||||
|
"/usr/lib64/ld-linux.so.3",
|
||||||
|
"/usr/lib64/ld-linux-aarch64.so.1",
|
||||||
|
"/usr/lib64/ld-linux-aarch64.so.1",
|
||||||
|
"/usr/lib64/ld-linux-riscv64-lp64d.so.1",
|
||||||
|
"/usr/lib64/ld.so.1",
|
||||||
|
"/usr/lib64/ld64.so.2",
|
||||||
|
"/usr/lib64/ld-linux-loongarch-lp64d.so.1",
|
||||||
|
"/usr/lib64/ld.so.1"
|
||||||
]
|
]
|
||||||
for i in glibcPathList:
|
for i in glibcPathList:
|
||||||
if (os.path.exists(i)):
|
if (os.path.exists(i)):
|
||||||
@@ -250,11 +273,15 @@ class GetInfo():
|
|||||||
if (self.compareVersion(newestVersion, i) == -1):
|
if (self.compareVersion(newestVersion, i) == -1):
|
||||||
newestVersion = i
|
newestVersion = i
|
||||||
version = newestVersion
|
version = newestVersion
|
||||||
|
if (version == None):
|
||||||
|
version = "2.28" # 默认值
|
||||||
self.glibcVersion = version
|
self.glibcVersion = version
|
||||||
return version
|
return version
|
||||||
|
|
||||||
def compareVersion(self, version1: str, version2: str):
|
def compareVersion(self, version1: str, version2: str):
|
||||||
# 判断是不是版本号
|
# 判断是不是版本号
|
||||||
|
if (type(version1) != str or type(version2) != str):
|
||||||
|
return -2
|
||||||
if ((not "." in version1) or (not "." in version2)):
|
if ((not "." in version1) or (not "." in version2)):
|
||||||
return -2
|
return -2
|
||||||
if (version1 == version2):
|
if (version1 == version2):
|
||||||
@@ -324,8 +351,13 @@ class GetInfo():
|
|||||||
result = True
|
result = True
|
||||||
if (data == "termux" and self.isTermux):
|
if (data == "termux" and self.isTermux):
|
||||||
result = True
|
result = True
|
||||||
if (self.compareVersion(self.glibcVersion, data) == 1):
|
try:
|
||||||
result = True
|
if (self.compareVersion(self.glibcVersion, data) == 1 or
|
||||||
|
self.compareVersion(self.glibcVersion, data) == 0):
|
||||||
|
result = True
|
||||||
|
except:
|
||||||
|
# 如果检查 tag 出现问题,强制返回符合条件
|
||||||
|
return True
|
||||||
if (os.path.exists(data)):
|
if (os.path.exists(data)):
|
||||||
result = True
|
result = True
|
||||||
if (no):
|
if (no):
|
||||||
@@ -553,65 +585,69 @@ def on_downloadWineFromCloudDisk_clicked():
|
|||||||
def get_now_lang()->"获取当前语言":
|
def get_now_lang()->"获取当前语言":
|
||||||
return os.getenv('LANG')
|
return os.getenv('LANG')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
localJsonList = []
|
|
||||||
internetJsonList = []
|
|
||||||
internetWineSourceList = [
|
|
||||||
"http://wine.wine-runner.gfdgdxi.top/",
|
|
||||||
"http://wine.wine-runner.gfdgdxi.top/sourceforge",
|
|
||||||
"https://gitee.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
|
|
||||||
"https://github.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
|
|
||||||
"http://127.0.0.1/wine-mirrors/" # 本地测试源
|
|
||||||
]
|
|
||||||
|
|
||||||
|
localJsonList = []
|
||||||
|
internetJsonList = []
|
||||||
|
internetWineSourceList = [
|
||||||
|
"http://wine.wine-runner.gfdgdxi.top/",
|
||||||
|
"http://wine.wine-runner.gfdgdxi.top/sourceforge",
|
||||||
|
"https://gitee.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
|
||||||
|
"https://github.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
|
||||||
|
"http://127.0.0.1/wine-mirrors/" # 本地测试源
|
||||||
|
]
|
||||||
|
|
||||||
|
if (__name__ == "__main__"):
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
# 读取翻译
|
else:
|
||||||
if not get_now_lang() == "zh_CN.UTF-8":
|
app = globalenv.get_value("app")
|
||||||
trans = QtCore.QTranslator()
|
# 读取翻译
|
||||||
trans.load(f"{programPath}/../LANG/installwine-en_US.qm")
|
if not get_now_lang() == "zh_CN.UTF-8":
|
||||||
app.installTranslator(trans)
|
trans = QtCore.QTranslator()
|
||||||
# 获取信息
|
trans.load(f"{programPath}/../LANG/installwine-en_US.qm")
|
||||||
systemInfo = GetInfo()
|
app.installTranslator(trans)
|
||||||
# 窗口构建
|
# 获取信息
|
||||||
window = QtWidgets.QMainWindow()
|
systemInfo = GetInfo()
|
||||||
ui = Ui_MainWindow()
|
# 窗口构建
|
||||||
window.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg"))
|
window = QtWidgets.QMainWindow()
|
||||||
ui.setupUi(window)
|
ui = Ui_MainWindow()
|
||||||
|
window.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg"))
|
||||||
|
ui.setupUi(window)
|
||||||
|
if (__name__ == "__main__"):
|
||||||
window.show()
|
window.show()
|
||||||
# 隐藏选项
|
# 隐藏选项
|
||||||
ui.unzip.setVisible(False)
|
ui.unzip.setVisible(False)
|
||||||
ui.deleteZip.setVisible(False)
|
ui.deleteZip.setVisible(False)
|
||||||
# 判断机器所在国家并自动分配源
|
# 判断机器所在国家并自动分配源
|
||||||
try:
|
try:
|
||||||
isChina = requests.get("https://ip.useragentinfo.com/json").json()["country"] == "中国"
|
isChina = requests.get("https://ip.useragentinfo.com/json").json()["country"] == "中国"
|
||||||
print("IsChina", isChina)
|
print("IsChina", isChina)
|
||||||
if isChina:
|
if isChina:
|
||||||
internetWineSource = internetWineSourceList[0]
|
internetWineSource = internetWineSourceList[0]
|
||||||
ui.ipv6Action.setChecked(True)
|
ui.ipv6Action.setChecked(True)
|
||||||
else:
|
else:
|
||||||
internetWineSource = internetWineSourceList[1]
|
|
||||||
ui.kgithubAction.setChecked(True)
|
|
||||||
except:
|
|
||||||
traceback.print_exc()
|
|
||||||
print("IsChina", False)
|
|
||||||
# 请求失败,默认使用国际源
|
|
||||||
internetWineSource = internetWineSourceList[1]
|
internetWineSource = internetWineSourceList[1]
|
||||||
ui.kgithubAction.setChecked(True)
|
ui.kgithubAction.setChecked(True)
|
||||||
# 连接信号
|
except:
|
||||||
ui.addButton.clicked.connect(on_addButton_clicked)
|
traceback.print_exc()
|
||||||
ui.delButton.clicked.connect(on_delButton_clicked)
|
print("IsChina", False)
|
||||||
ui.downloadWineFromCloudDisk.clicked.connect(on_downloadWineFromCloudDisk_clicked)
|
# 请求失败,默认使用国际源
|
||||||
ui.addOtherWine.clicked.connect(InstallOtherWine)
|
internetWineSource = internetWineSourceList[1]
|
||||||
ui.changeSourcesGroup.triggered.connect(ChangeSources)
|
ui.kgithubAction.setChecked(True)
|
||||||
ui.m_recommendWine.clicked.connect(ReadInternetInformation)
|
# 连接信号
|
||||||
## 加载内容
|
ui.addButton.clicked.connect(on_addButton_clicked)
|
||||||
# 设置列表双击不会编辑
|
ui.delButton.clicked.connect(on_delButton_clicked)
|
||||||
ui.localWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
ui.downloadWineFromCloudDisk.clicked.connect(on_downloadWineFromCloudDisk_clicked)
|
||||||
ui.internetWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
ui.addOtherWine.clicked.connect(InstallOtherWine)
|
||||||
# 读取信息
|
ui.changeSourcesGroup.triggered.connect(ChangeSources)
|
||||||
ReadLocalInformation()
|
ui.m_recommendWine.clicked.connect(ReadInternetInformation)
|
||||||
ReadInternetInformation()
|
## 加载内容
|
||||||
# 图标
|
# 设置列表双击不会编辑
|
||||||
ui.centralWidget.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg"))
|
ui.localWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
ui.internetWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
# 读取信息
|
||||||
|
ReadLocalInformation()
|
||||||
|
ReadInternetInformation()
|
||||||
|
# 图标
|
||||||
|
ui.centralWidget.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg"))
|
||||||
|
if (__name__ == "__main__"):
|
||||||
app.exec_()
|
app.exec_()
|
||||||
1
wine/installwine.py
Symbolic link
1
wine/installwine.py
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
installwine
|
||||||
5856
winetricks
5856
winetricks
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user