diff --git a/.github/workflows/deploy-pypi.yml b/.github/workflows/deploy-pypi.yml
index 509543c..258a9de 100644
--- a/.github/workflows/deploy-pypi.yml
+++ b/.github/workflows/deploy-pypi.yml
@@ -122,7 +122,11 @@ jobs:
run: |
sudo apt update
sudo apt install -y libxcb-cursor0 qt6-base-dev patchelf ccache zlib1g-dev
+ sudo apt install -y flatpak flatpak-builder elfutils patchelf
echo "QMAKE=$(which qmake6)" >> $GITHUB_ENV
+ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
+ flatpak update
+ flatpak install flathub org.kde.Platform//6.8 org.kde.Sdk//6.8
if [ "$RUNNER_ARCH" == "X64" ]; then
linuxdeploy_arch="x86_64"
@@ -219,6 +223,8 @@ jobs:
Furious-*.zip
Furious-*.dmg
Furious-*.AppImage
+ Furious-*.deb
+ Furious-*.flatpak
github-release:
name: Upload to GitHub Release
diff --git a/.gitignore b/.gitignore
index 6df27f7..6b65e63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -170,6 +170,10 @@ Furious-*/
# Linux app dir
AppDir/
+# Linux debian dir
+debian/
+# Linux flatpak dir
+flatpak/
# zip files
*.zip
@@ -177,3 +181,7 @@ AppDir/
*.dmg
# AppImage files
*.AppImage
+# deb files
+*.deb
+# flatpak files
+*.flatpak
diff --git a/Deploy.py b/Deploy.py
index b93853a..8da35d6 100644
--- a/Deploy.py
+++ b/Deploy.py
@@ -54,6 +54,8 @@ NUITKA_BINARY_VERSION_OPTION = (
f'--product-version={APPLICATION_VERSION} '
)
+PLATFORM_MACHINE_LOWER = PLATFORM_MACHINE.lower()
+
if PLATFORM == 'Windows':
NUITKA_BINARY_VERSION_OPTION += f'--file-description=\"A GUI proxy client based on PySide6. Support Xray-core & hysteria\" '
@@ -118,7 +120,7 @@ if PLATFORM == 'Windows':
ARTIFACT_NAME = (
f'{APPLICATION_NAME}-{APPLICATION_VERSION}-'
- f'{winVerCompatible}-{PLATFORM_MACHINE.lower()}'
+ f'{winVerCompatible}-{PLATFORM_MACHINE_LOWER}'
)
WIN_UNZIPPED = f'{APPLICATION_NAME}-{APPLICATION_VERSION}-{winVerCompatible}'
@@ -135,7 +137,7 @@ elif PLATFORM == 'Darwin':
ARTIFACT_NAME = (
f'{APPLICATION_NAME}-{APPLICATION_VERSION}-'
- f'{macVerCompatible}-{PLATFORM_MACHINE.lower()}'
+ f'{macVerCompatible}-{PLATFORM_MACHINE_LOWER}'
)
MAC_APP_DIR = ROOT_DIR / 'app'
@@ -159,7 +161,7 @@ elif PLATFORM == 'Linux':
LINUX_DESKTOP_FILE = f'{APPLICATION_NAME}.desktop'
ARTIFACT_NAME = (
- f'{APPLICATION_NAME}-{APPLICATION_VERSION}-linux-{PLATFORM_MACHINE.lower()}'
+ f'{APPLICATION_NAME}-{APPLICATION_VERSION}-linux-{PLATFORM_MACHINE_LOWER}'
)
LINUX_APPIMAGE_FILENAME = f'{ARTIFACT_NAME}.AppImage'
@@ -171,6 +173,12 @@ elif PLATFORM == 'Linux':
f'--plugin qt '
f'--output appimage'
)
+
+ LINUX_DEBIAN_DIR = ROOT_DIR / 'debian'
+ LINUX_DEB_FILENAME = f'{ARTIFACT_NAME}.deb'
+
+ LINUX_FLATPAK_DIR = ROOT_DIR / 'flatpak'
+ LINUX_FLATPAK_FILENAME = f'{ARTIFACT_NAME}.flatpak'
else:
# Deploy: Not implemented
ARTIFACT_NAME = ''
@@ -330,6 +338,7 @@ def cleanup():
logger.info(f'remove app dir success')
elif PLATFORM == 'Linux':
# More cleanup on Linux
+ # Cleanup AppImage environment
try:
# Remove artifact
os.remove(ROOT_DIR / LINUX_APPIMAGE_FILENAME)
@@ -349,6 +358,48 @@ def cleanup():
logger.error(f'remove app dir failed: {ex}')
else:
logger.info(f'remove app dir success')
+
+ # Cleanup deb environment
+ try:
+ # Remove artifact
+ os.remove(ROOT_DIR / LINUX_DEB_FILENAME)
+ except Exception as ex:
+ # Any non-exit exceptions
+
+ logger.error(f'remove artifact failed: {ex}')
+ else:
+ logger.info(f'remove artifact success')
+
+ try:
+ # Remove debian folder
+ shutil.rmtree(LINUX_DEBIAN_DIR)
+ except Exception as ex:
+ # Any non-exit exceptions
+
+ logger.error(f'remove debian dir failed: {ex}')
+ else:
+ logger.info(f'remove debian dir success')
+
+ # Cleanup flatpak environment
+ try:
+ # Remove artifact
+ os.remove(ROOT_DIR / LINUX_FLATPAK_FILENAME)
+ except Exception as ex:
+ # Any non-exit exceptions
+
+ logger.error(f'remove artifact failed: {ex}')
+ else:
+ logger.info(f'remove artifact success')
+
+ try:
+ # Remove flatpak folder
+ shutil.rmtree(LINUX_FLATPAK_DIR)
+ except Exception as ex:
+ # Any non-exit exceptions
+
+ logger.error(f'remove flatpak dir failed: {ex}')
+ else:
+ logger.info(f'remove flatpak dir success')
else:
# Deploy: Not implemented
pass
@@ -527,20 +578,23 @@ def main():
LINUX_APP_DIR,
)
+ DESKTOP_FILE_CONTENT = (
+ f'[Desktop Entry]\n'
+ f'Type=Application\n'
+ f'Name={APPLICATION_NAME}\n'
+ f'Exec={APPLICATION_NAME}.bin\n'
+ f'Icon={iconbase}\n'
+ f'Terminal=false\n'
+ f'Categories=Utility\n'
+ )
+
try:
with open(
LINUX_APP_DIR / LINUX_DESKTOP_FILE,
'w',
encoding='utf-8',
) as file:
- file.write(
- f'[Desktop Entry]\n'
- f'Type=Application\n'
- f'Name={APPLICATION_NAME}\n'
- f'Exec={APPLICATION_NAME}.bin\n'
- f'Icon={iconbase}\n'
- f'Categories=Utility\n'
- )
+ file.write(DESKTOP_FILE_CONTENT)
except Exception:
# Any non-exit exceptions
@@ -589,6 +643,322 @@ def main():
logger.info(f'chmod +x success: {LINUX_APPIMAGE_FILENAME}')
printStandardStream(result.stdout, result.stderr)
+
+ # Create .deb package
+ logger.info('generating deb')
+
+ # Prepare package layout
+ try:
+ os.makedirs(LINUX_DEBIAN_DIR / 'DEBIAN', exist_ok=True)
+ os.makedirs(LINUX_DEBIAN_DIR / 'usr' / 'bin', exist_ok=True)
+ os.makedirs(
+ LINUX_DEBIAN_DIR / 'usr' / 'share' / APPLICATION_NAME,
+ exist_ok=True,
+ )
+ os.makedirs(
+ LINUX_DEBIAN_DIR / 'usr' / 'share' / 'applications',
+ exist_ok=True,
+ )
+ os.makedirs(
+ LINUX_DEBIAN_DIR
+ / 'usr'
+ / 'share'
+ / 'icons'
+ / 'hicolor'
+ / '512x512'
+ / 'apps',
+ exist_ok=True,
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Copy output
+ shutil.copytree(
+ ROOT_DIR / DEPLOY_DIR_NAME / f'{APPLICATION_NAME}.dist',
+ LINUX_DEBIAN_DIR / 'usr' / 'share' / APPLICATION_NAME,
+ dirs_exist_ok=True,
+ )
+ shutil.copy(
+ ROOT_DIR / 'Icons' / 'png' / f'{iconbase}.png',
+ LINUX_DEBIAN_DIR
+ / 'usr'
+ / 'share'
+ / 'icons'
+ / 'hicolor'
+ / '512x512'
+ / 'apps',
+ )
+
+ # Create launcher script
+ try:
+ launcherScript = LINUX_DEBIAN_DIR / 'usr' / 'bin' / APPLICATION_NAME
+
+ with open(
+ launcherScript,
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(
+ f'#!/bin/bash\n'
+ f'exec /usr/share/{APPLICATION_NAME}/{APPLICATION_NAME}.bin "$@"\n'
+ )
+
+ runExternalCommand(
+ f'chmod +x {launcherScript}',
+ shell=True,
+ check=True,
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Create desktop entry file
+ try:
+ with open(
+ LINUX_DEBIAN_DIR
+ / 'usr'
+ / 'share'
+ / 'applications'
+ / LINUX_DESKTOP_FILE,
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(DESKTOP_FILE_CONTENT)
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Create control file
+ try:
+ if PLATFORM_MACHINE_LOWER == 'x86_64':
+ arch = 'amd64'
+ elif PLATFORM_MACHINE_LOWER == 'aarch64':
+ arch = 'arm64'
+ else:
+ raise
+
+ with open(
+ LINUX_DEBIAN_DIR / 'DEBIAN' / 'control',
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(
+ f'Package: {APPLICATION_NAME}\n'
+ f'Version: {APPLICATION_VERSION}\n'
+ f'Section: utils\n'
+ f'Priority: optional\n'
+ f'Architecture: {arch}\n'
+ f'Depends: libc6, libstdc++6, libgl1, libxcb-cursor0, libxcb-xinerama0\n'
+ f'Maintainer: {APPLICATION_AUTHOR_NAME} <{APPLICATION_AUTHOR_EMAIL}>\n'
+ f'Description: A GUI proxy client based on PySide6. Support Xray-core & hysteria\n'
+ f'Homepage: {APPLICATION_ABOUT_PAGE}\n'
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Fix permissions
+ try:
+ runExternalCommand(
+ f'find debian -type d -exec chmod 755 {{}} \\;',
+ shell=True,
+ check=True,
+ )
+ runExternalCommand(
+ f'find debian -type f -exec chmod 644 {{}} \\;',
+ shell=True,
+ check=True,
+ )
+ runExternalCommand(
+ f'chmod 755 '
+ + (LINUX_DEBIAN_DIR / 'usr' / 'bin' / APPLICATION_NAME).as_posix(),
+ shell=True,
+ check=True,
+ )
+ runExternalCommand(
+ f'chmod 755 '
+ + (
+ LINUX_DEBIAN_DIR
+ / 'usr'
+ / 'share'
+ / APPLICATION_NAME
+ / f'{APPLICATION_NAME}.bin'
+ ).as_posix(),
+ shell=True,
+ check=True,
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ try:
+ runExternalCommand(
+ f'dpkg-deb --build debian {LINUX_DEB_FILENAME}',
+ shell=True,
+ check=True,
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Create .flatpak package
+ logger.info('generating flatpak')
+
+ # Prepare package layout
+ try:
+ os.makedirs(LINUX_FLATPAK_DIR / APPLICATION_NAME, exist_ok=True)
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Copy output
+ shutil.copytree(
+ ROOT_DIR / DEPLOY_DIR_NAME / f'{APPLICATION_NAME}.dist',
+ LINUX_FLATPAK_DIR / APPLICATION_NAME,
+ dirs_exist_ok=True,
+ )
+ shutil.copy(
+ ROOT_DIR / 'Icons' / 'png' / f'{iconbase}.png',
+ LINUX_FLATPAK_DIR / f'{APPLICATION_FLATPAK_ID}.png',
+ )
+
+ # Create flatpak manifest file
+ try:
+ with open(
+ LINUX_FLATPAK_DIR / f'{APPLICATION_FLATPAK_ID}.yml',
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(
+ f'app-id: {APPLICATION_FLATPAK_ID}\n'
+ f'runtime: org.kde.Platform\n'
+ f'runtime-version: \"6.8\"\n'
+ f'sdk: org.kde.Sdk\n'
+ f'command: {APPLICATION_NAME}\n'
+ f'\n'
+ f'finish-args:\n'
+ f' - --share=ipc\n'
+ f' - --share=network\n'
+ f' - --socket=wayland\n'
+ f' - --socket=fallback-x11\n'
+ f' - --device=dri\n'
+ f' - --filesystem=home\n'
+ f' - --talk-name=org.freedesktop.Flatpak\n'
+ f'\n'
+ f'modules:\n'
+ f' - name: mit-krb5\n'
+ f' buildsystem: simple\n'
+ f' sources:\n'
+ f' - type: archive\n'
+ f' url: https://kerberos.org/dist/krb5/1.21/krb5-1.21.3.tar.gz\n'
+ f' sha256: b7a4cd5ead67fb08b980b21abd150ff7217e85ea320c9ed0c6dadd304840ad35\n'
+ f' build-commands:\n'
+ f' - cd src && autoreconf -i # Ensure Autotools files are generated\n'
+ f' - cd src && ./configure --prefix=/app --disable-static --enable-shared\n'
+ f' - cd src && make -j$(nproc)\n'
+ f' - cd src && make install\n'
+ f' - name: {APPLICATION_NAME}\n'
+ f' buildsystem: simple\n'
+ f' build-commands:\n'
+ f' # Script\n'
+ f' - install -Dm755 {APPLICATION_NAME}.sh /app/bin/{APPLICATION_NAME}\n'
+ f' # Whole application directory\n'
+ f' - mkdir -p /app/lib/{APPLICATION_NAME}/\n'
+ f' - cp -a . /app/lib/{APPLICATION_NAME}/\n'
+ f' # Desktop + icon\n'
+ f' - install -Dm644 {APPLICATION_FLATPAK_ID}.desktop /app/share/applications/{APPLICATION_FLATPAK_ID}.desktop\n'
+ f' - install -Dm644 {APPLICATION_FLATPAK_ID}.png /app/share/icons/hicolor/512x512/apps/{APPLICATION_FLATPAK_ID}.png\n'
+ f' sources:\n'
+ f' - type: file\n'
+ f' path: {APPLICATION_NAME}.sh\n'
+ f' - type: dir\n'
+ f' path: {APPLICATION_NAME}\n'
+ f' - type: file\n'
+ f' path: {APPLICATION_FLATPAK_ID}.desktop\n'
+ f' - type: file\n'
+ f' path: {APPLICATION_FLATPAK_ID}.png\n'
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Create desktop entry file
+ try:
+ with open(
+ LINUX_FLATPAK_DIR / f'{APPLICATION_FLATPAK_ID}.desktop',
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(
+ f'[Desktop Entry]\n'
+ f'Type=Application\n'
+ f'Name={APPLICATION_NAME}\n'
+ f'Exec={APPLICATION_NAME}\n'
+ f'Icon={APPLICATION_FLATPAK_ID}\n'
+ f'Terminal=false\n'
+ f'Categories=Utility\n'
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ # Create launcher script
+ try:
+ with open(
+ LINUX_FLATPAK_DIR / f'{APPLICATION_NAME}.sh',
+ 'w',
+ encoding='utf-8',
+ ) as file:
+ file.write(
+ f'#!/usr/bin/env bash\n'
+ f'exec /app/lib/{APPLICATION_NAME}/{APPLICATION_NAME}.bin \"$@\"\n'
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+
+ cwd = os.getcwd()
+
+ try:
+ os.chdir(LINUX_FLATPAK_DIR)
+
+ runExternalCommand(
+ f'flatpak-builder build-dir {APPLICATION_FLATPAK_ID}.yml --force-clean',
+ shell=True,
+ check=True,
+ )
+ runExternalCommand(
+ f'flatpak build-export repo build-dir',
+ shell=True,
+ check=True,
+ )
+ runExternalCommand(
+ f'flatpak build-bundle repo {LINUX_FLATPAK_FILENAME} {APPLICATION_FLATPAK_ID}',
+ shell=True,
+ check=True,
+ )
+
+ shutil.copy(
+ LINUX_FLATPAK_FILENAME,
+ ROOT_DIR,
+ )
+ except Exception:
+ # Any non-exit exceptions
+
+ raise
+ finally:
+ os.chdir(cwd)
else:
# Deploy: Not implemented
pass
diff --git a/Furious/Core/CoreManager.py b/Furious/Core/CoreManager.py
index 017905c..1d8026d 100644
--- a/Furious/Core/CoreManager.py
+++ b/Furious/Core/CoreManager.py
@@ -21,7 +21,6 @@ from Furious.Frozenlib import *
from Furious.Interface import *
from Furious.Library import *
from Furious.Qt import *
-from Furious.Utility import *
from Furious.Core.CoreProcessWorker import *
from Furious.Core.XrayCore import *
from Furious.Core.Hysteria1 import *
@@ -30,6 +29,7 @@ from Furious.Core.Tun2socks import *
from typing import Callable, Tuple, Union
+import os
import uuid
import logging
import tempfile
@@ -654,8 +654,13 @@ class CoreManager(Mixins.CleanupOnExit):
)
)
+ if SystemRuntime.flatpakID():
+ tempdir = os.environ.get('TMPDIR')
+ else:
+ tempdir = None
+
with tempfile.NamedTemporaryFile(
- mode='w', encoding='utf-8', suffix='.sh', delete=True
+ mode='w', encoding='utf-8', suffix='.sh', dir=tempdir, delete=True
) as file:
content = '\n'.join(
filter(
diff --git a/Furious/Core/CoreProcessWorker.py b/Furious/Core/CoreProcessWorker.py
index 1bb288e..c4c2246 100644
--- a/Furious/Core/CoreProcessWorker.py
+++ b/Furious/Core/CoreProcessWorker.py
@@ -19,7 +19,6 @@ from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Interface import *
-from Furious.Utility import *
from PySide6 import QtCore
diff --git a/Furious/Core/XrayCore.py b/Furious/Core/XrayCore.py
index aa5f70a..0ff6b0e 100644
--- a/Furious/Core/XrayCore.py
+++ b/Furious/Core/XrayCore.py
@@ -20,7 +20,6 @@ from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Interface import *
from Furious.Library import *
-from Furious.Utility import *
from Furious.Core.CoreProcessWorker import *
from enum import Enum
diff --git a/Furious/Frozenlib/Constants.py b/Furious/Frozenlib/Constants.py
index 00e5f45..2c8c63f 100644
--- a/Furious/Frozenlib/Constants.py
+++ b/Furious/Frozenlib/Constants.py
@@ -26,8 +26,11 @@ import platform
APPLICATION_NAME = 'Furious'
APPLICATION_VERSION = __version__
+APPLICATION_AUTHOR_NAME = 'Loren Eteval'
+APPLICATION_AUTHOR_EMAIL = 'loren.eteval@proton.me'
APPLICATION_MACOS_SIGNATURE = 'com.Furious'
-APPLICATION_REPO_OWNER_NAME = 'LorenEteval'
+APPLICATION_FLATPAK_ID = 'com.Furious.Furious'
+APPLICATION_REPO_OWNER_NAME = APPLICATION_AUTHOR_NAME.replace(' ', '')
APPLICATION_REPO_NAME = 'Furious'
APPLICATION_ABOUT_PAGE = (
f'https://github.com/{APPLICATION_REPO_OWNER_NAME}/{APPLICATION_REPO_NAME}'
diff --git a/Furious/Utility/StartupOnBoot.py b/Furious/Frozenlib/StartupOnBoot.py
similarity index 98%
rename from Furious/Utility/StartupOnBoot.py
rename to Furious/Frozenlib/StartupOnBoot.py
index 7046d51..8fe9e8d 100644
--- a/Furious/Utility/StartupOnBoot.py
+++ b/Furious/Frozenlib/StartupOnBoot.py
@@ -15,8 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from Furious.Frozenlib import *
-from Furious.Utility.SystemRuntime import *
+from Furious.Frozenlib.Constants import *
+from Furious.Frozenlib.SystemRuntime import *
from PySide6 import QtCore
diff --git a/Furious/Utility/SystemProxy.py b/Furious/Frozenlib/SystemProxy.py
similarity index 96%
rename from Furious/Utility/SystemProxy.py
rename to Furious/Frozenlib/SystemProxy.py
index 332a177..2046719 100644
--- a/Furious/Utility/SystemProxy.py
+++ b/Furious/Frozenlib/SystemProxy.py
@@ -17,7 +17,11 @@
from __future__ import annotations
-from Furious.Frozenlib import *
+from Furious.Frozenlib.Constants import *
+from Furious.Frozenlib.Enum import *
+from Furious.Frozenlib.AppSettings import *
+from Furious.Frozenlib.Utility import *
+from Furious.Frozenlib.SystemRuntime import *
import logging
import subprocess
@@ -43,9 +47,14 @@ def handleAppSystemProxyMode() -> bool:
def linuxProxyConfig(proxy_args, arg0, arg1):
+ command = 'gsettings'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
runExternalCommand(
- [
- 'gsettings',
+ command.split()
+ + [
'set',
'org.gnome.system.' + proxy_args,
arg0,
diff --git a/Furious/Frozenlib/SystemRoutingTable.py b/Furious/Frozenlib/SystemRoutingTable.py
index b99d055..90109c2 100644
--- a/Furious/Frozenlib/SystemRoutingTable.py
+++ b/Furious/Frozenlib/SystemRoutingTable.py
@@ -19,6 +19,7 @@ from __future__ import annotations
from Furious.Frozenlib.Constants import *
from Furious.Frozenlib.Utility import *
+from Furious.Frozenlib.SystemRuntime import *
import re
import logging
@@ -335,9 +336,14 @@ class SystemRoutingTable:
def LinuxFindTUNDevice(deviceName: str) -> bool:
assert PLATFORM == 'Linux'
+ command = 'ip tuntap show'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
try:
result = runExternalCommand(
- 'ip tuntap show'.split(),
+ command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
@@ -364,9 +370,14 @@ class SystemRoutingTable:
def LinuxDeleteTUNDevice(deviceName: str) -> bool:
assert PLATFORM == 'Linux'
+ command = 'ip tuntap del mode tun dev'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
try:
result = runExternalCommand(
- 'ip tuntap del mode tun dev'.split() + [deviceName],
+ command.split() + [deviceName],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
@@ -396,9 +407,14 @@ class SystemRoutingTable:
def LinuxExecutePrivilegedScript(filepath, shell='bash') -> bool:
assert PLATFORM == 'Linux'
+ command = 'pkexec'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
try:
result = runExternalCommand(
- ['pkexec', shell, filepath],
+ command.split() + [shell, filepath],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
@@ -428,9 +444,14 @@ class SystemRoutingTable:
def LinuxGetIpRoute() -> str:
assert PLATFORM == 'Linux'
+ command = 'ip route show'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
try:
result = runExternalCommand(
- 'ip route show'.split(),
+ command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
@@ -483,8 +504,13 @@ class SystemRoutingTable:
)
if PLATFORM == 'Linux':
+ command = 'ip route show default'
+
+ if SystemRuntime.flatpakID():
+ command = 'flatpak-spawn --host ' + command
+
result = runExternalCommand(
- 'ip route show default'.split(),
+ command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
diff --git a/Furious/Utility/SystemRuntime.py b/Furious/Frozenlib/SystemRuntime.py
similarity index 90%
rename from Furious/Utility/SystemRuntime.py
rename to Furious/Frozenlib/SystemRuntime.py
index 4e391aa..1d92a08 100644
--- a/Furious/Utility/SystemRuntime.py
+++ b/Furious/Frozenlib/SystemRuntime.py
@@ -17,7 +17,9 @@
from __future__ import annotations
-from Furious.Frozenlib import *
+from Furious.Frozenlib.Constants import *
+from Furious.Frozenlib.Utility import *
+from Furious.Frozenlib.AppSettings import *
import os
import sys
@@ -70,6 +72,16 @@ class SystemRuntime:
else:
return ''
+ @staticmethod
+ def flatpakID() -> str:
+ if PLATFORM != 'Linux':
+ return ''
+
+ if 'FLATPAK_ID' in os.environ:
+ return os.environ['FLATPAK_ID']
+ else:
+ return ''
+
@staticmethod
@functools.lru_cache(None)
def isAdmin() -> bool:
diff --git a/Furious/Frozenlib/__init__.py b/Furious/Frozenlib/__init__.py
index e9f2dfc..31573e3 100644
--- a/Furious/Frozenlib/__init__.py
+++ b/Furious/Frozenlib/__init__.py
@@ -21,7 +21,10 @@ from .Enum import *
from .Globals import *
from .Mixins import *
from .PySide6Legacy import *
+from .StartupOnBoot import *
+from .SystemProxy import *
from .SystemRoutingTable import *
+from .SystemRuntime import *
from .Tcping import *
from .Utility import *
from .Win32Session import *
diff --git a/Furious/Qt/DynamicTheme.py b/Furious/Qt/DynamicTheme.py
index 1fd5541..28f19ed 100644
--- a/Furious/Qt/DynamicTheme.py
+++ b/Furious/Qt/DynamicTheme.py
@@ -19,7 +19,6 @@ from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt.QtGui import *
-from Furious.Utility import *
__all__ = ['AppHue']
diff --git a/Furious/Qt/QtGui.py b/Furious/Qt/QtGui.py
index 50c68a1..e78c5d3 100644
--- a/Furious/Qt/QtGui.py
+++ b/Furious/Qt/QtGui.py
@@ -17,7 +17,6 @@
from Furious.Frozenlib import *
from Furious.Qt.DynamicTranslate import gettext as _
-from Furious.Utility import *
from PySide6 import QtCore
from PySide6.QtGui import *
diff --git a/Furious/TrayActions/Connect.py b/Furious/TrayActions/Connect.py
index 128f2dc..1ef08e1 100644
--- a/Furious/TrayActions/Connect.py
+++ b/Furious/TrayActions/Connect.py
@@ -22,7 +22,6 @@ from Furious.Interface import *
from Furious.Library import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
from Furious.Core import *
from Furious.Widget.ConnectProgressBar import ConnectProgressBar
diff --git a/Furious/TrayActions/Settings.py b/Furious/TrayActions/Settings.py
index d51fde2..6c27d49 100644
--- a/Furious/TrayActions/Settings.py
+++ b/Furious/TrayActions/Settings.py
@@ -20,7 +20,6 @@ from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
__all__ = ['SettingsAction']
diff --git a/Furious/Utility/__init__.py b/Furious/Utility/__init__.py
index 4b07a40..7af5999 100644
--- a/Furious/Utility/__init__.py
+++ b/Furious/Utility/__init__.py
@@ -15,7 +15,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from .StartupOnBoot import *
-from .SystemProxy import *
-from .SystemRuntime import *
from .AppMainProcess import *
diff --git a/Furious/Widget/Application.py b/Furious/Widget/Application.py
index 98276d7..c5ef140 100644
--- a/Furious/Widget/Application.py
+++ b/Furious/Widget/Application.py
@@ -19,7 +19,6 @@ from Furious.Frozenlib import *
from Furious.Interface import *
from Furious.Qt import gettext as _
from Furious.Library import *
-from Furious.Utility import *
from Furious.Widget.SystemTrayIcon import *
from Furious.Window.AppMainWindow import *
from Furious.Window.LogViewerWindow import *
@@ -261,6 +260,12 @@ class Application(ApplicationFactory, SingletonApplication):
# Xray environment variables
os.environ['XRAY_LOCATION_ASSET'] = str(XRAY_ASSET_DIR)
+ if SystemRuntime.flatpakID():
+ # https://github.com/flatpak/flatpak/issues/3438
+ os.environ['TMPDIR'] = os.path.join(
+ os.environ.get('XDG_RUNTIME_DIR'), 'app', APPLICATION_FLATPAK_ID
+ )
+
def addStorage(self):
# Protected storage access
self._userActivatedItemIndex = Storage.UserActivatedItemIndex()
@@ -280,6 +285,9 @@ class Application(ApplicationFactory, SingletonApplication):
return backgroudColor.lightness() < 128
def isDarkModeEnabled(self):
+ if SystemRuntime.flatpakID():
+ return self.isDarkMode()
+
if not SystemRuntime.isAdmin():
return AppSettings.isStateON_('DarkMode')
else:
@@ -393,6 +401,13 @@ class Application(ApplicationFactory, SingletonApplication):
else:
logger.info('not running from Linux AppImage')
+ flatpakId = SystemRuntime.flatpakID()
+
+ if flatpakId:
+ logger.info(f'running from Linux flatpak: \'{flatpakId}\'')
+ else:
+ logger.info('not running from Linux flatpak')
+
logger.info(f'python version: {PLATFORM_PYTHON_VERSION}')
logger.info(f'system version: {sys.version}')
logger.info(f'sys.executable: \'{sys.executable}\'')
diff --git a/Furious/Widget/GuiTUNSettings.py b/Furious/Widget/GuiTUNSettings.py
index 3fdaab0..0f57fab 100644
--- a/Furious/Widget/GuiTUNSettings.py
+++ b/Furious/Widget/GuiTUNSettings.py
@@ -22,7 +22,6 @@ from Furious.Interface import *
from Furious.Library import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
from PySide6.QtGui import *
diff --git a/Furious/Widget/SystemTrayIcon.py b/Furious/Widget/SystemTrayIcon.py
index 5b1308b..39499e2 100644
--- a/Furious/Widget/SystemTrayIcon.py
+++ b/Furious/Widget/SystemTrayIcon.py
@@ -18,7 +18,6 @@
from Furious.Frozenlib import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
from Furious.TrayActions import *
from PySide6 import QtCore
diff --git a/Furious/Widget/XrayAssetViewerQListWidget.py b/Furious/Widget/XrayAssetViewerQListWidget.py
index 263167e..61ba725 100644
--- a/Furious/Widget/XrayAssetViewerQListWidget.py
+++ b/Furious/Widget/XrayAssetViewerQListWidget.py
@@ -20,7 +20,6 @@ from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
from PySide6 import QtCore
from PySide6.QtGui import *
diff --git a/Furious/Window/AppMainWindow.py b/Furious/Window/AppMainWindow.py
index 1379f4f..3d1a1f2 100644
--- a/Furious/Window/AppMainWindow.py
+++ b/Furious/Window/AppMainWindow.py
@@ -22,7 +22,6 @@ from Furious.Interface import *
from Furious.Library import *
from Furious.Qt import *
from Furious.Qt import gettext as _
-from Furious.Utility import *
from Furious.Widget.UserServersQTableWidget import *
from Furious.Widget.GuiTUNSettings import *
from Furious.Window.UserSubsWindow import *