From 0bd820f8c6ba7ad658c6c9bc7ae386d9e34adf5b Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 11:34:49 -0300 Subject: [PATCH 01/29] [volt] improve MANGOHUD_CONFIG handling --- src/gpu_launch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gpu_launch.py b/src/gpu_launch.py index a55c1e8..c162c08 100644 --- a/src/gpu_launch.py +++ b/src/gpu_launch.py @@ -1086,6 +1086,7 @@ def generate_env_vars(widgets, category_name): if category_name == "MangoHud": mangohud_parts = [] has_non_default_settings = False + has_default_settings = False for setting_key, widget in widgets.items(): if setting_key.endswith('_apply_button') or setting_key.endswith('_browse') or setting_key.endswith('_clear'): @@ -1105,6 +1106,7 @@ def generate_env_vars(widgets, category_name): if value == "unset": continue elif "(default)" in value: + has_default_settings = True continue else: has_non_default_settings = True @@ -1130,7 +1132,7 @@ def generate_env_vars(widgets, category_name): if mangohud_parts and has_non_default_settings: config_value = ','.join(mangohud_parts) env_vars.append(f'MANGOHUD_CONFIG={config_value}') - elif not has_non_default_settings: + elif has_default_settings and not has_non_default_settings: unset_vars.append('MANGOHUD_CONFIG') else: for setting_key, widget in widgets.items(): From e9f7dd98132265cd8edc866456a70513e6038284 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 13:46:36 -0300 Subject: [PATCH 02/29] [install] ensure that the target dirs exist --- install.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 1f6ecb9..8ff7608 100755 --- a/install.sh +++ b/install.sh @@ -28,13 +28,19 @@ if [[ ! -f "$HELPER_SCRIPT" ]]; then exit 1 fi +echo -e "\033[34mEnsuring installation directory exists...\033[0m" +mkdir -p "$INSTALL_DIR" + echo -e "\033[34mInstalling main executable...\033[0m" install -v -m 755 -T "$EXECUTABLE" "$INSTALL_DIR/volt-gui" echo -e "\n\033[34mInstalling helper script...\033[0m" install -v -m 755 -T "$HELPER_SCRIPT" "$INSTALL_DIR/volt-helper" -echo -e "\n\033[34mCreating desktop entry...\033[0m" +echo -e "\n\033[34mEnsuring desktop applications directory exists...\033[0m" +mkdir -p "$(dirname "$DESKTOP_FILE")" + +echo -e "\033[34mCreating desktop entry...\033[0m" cat > "$DESKTOP_FILE" << EOF [Desktop Entry] Name=volt-gui From ee036554d7446022b0911081324f4746f2de1318 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 15:21:31 -0300 Subject: [PATCH 03/29] [volt] add and use stdout and stderr --- src/cpu.py | 4 +++- src/gpu_launch.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/cpu.py b/src/cpu.py index 99b1b2a..021aaea 100644 --- a/src/cpu.py +++ b/src/cpu.py @@ -143,7 +143,9 @@ def get_current_scheduler(): process.start("ps", ["-eo", "comm"]) if process.waitForFinished(10000): - output = process.readAllStandardOutput().data().decode() + stdout = process.readAllStandardOutput().data().decode() + stderr = process.readAllStandardError().data().decode() + output = stdout + stderr processes = output.strip().splitlines() return next((p.strip() for p in processes if p.strip().startswith("scx_")), "none") return "none" diff --git a/src/gpu_launch.py b/src/gpu_launch.py index c162c08..20dc98c 100644 --- a/src/gpu_launch.py +++ b/src/gpu_launch.py @@ -614,7 +614,9 @@ def get_available(program_name, search_flatpak): process.start("flatpak", ["list"]) if process.waitForFinished(10000): - output = process.readAllStandardOutput().data().decode() + stdout = process.readAllStandardOutput().data().decode() + stderr = process.readAllStandardError().data().decode() + output = stdout + stderr if program_name.lower() in output.lower(): return True except Exception: @@ -686,7 +688,9 @@ def get_opengl_device_options(): process.start("glxinfo") if process.waitForFinished(10000): - output = process.readAllStandardOutput().data().decode() + stdout = process.readAllStandardOutput().data().decode() + stderr = process.readAllStandardError().data().decode() + output = stdout + stderr for line in output.split('\n'): if "OpenGL renderer string:" in line: device_name = line.split(':', 1)[1].strip() @@ -712,7 +716,9 @@ def get_opengl_device_options(): process.start("glxinfo") if process.waitForFinished(10000): - output = process.readAllStandardOutput().data().decode() + stdout = process.readAllStandardOutput().data().decode() + stderr = process.readAllStandardError().data().decode() + output = stdout + stderr renderer_found = False for line in output.split('\n'): @@ -775,7 +781,9 @@ def get_vulkan_device_options(): process.start("vulkaninfo") if process.waitForFinished(10000): - output = process.readAllStandardOutput().data().decode() + stdout = process.readAllStandardOutput().data().decode() + stderr = process.readAllStandardError().data().decode() + output = stdout + stderr lines = output.split('\n') current_device = {} From e3cbc19f6678a710979412c18736101a659b1d91 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 15:44:36 -0300 Subject: [PATCH 04/29] [volt] remove unneded else --- src/gpu_launch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gpu_launch.py b/src/gpu_launch.py index 20dc98c..8d52cae 100644 --- a/src/gpu_launch.py +++ b/src/gpu_launch.py @@ -805,8 +805,6 @@ def get_vulkan_device_options(): if 'llvmpipe' in display_name: display_name = 'llvmpipe (software rendering)' - else: - display_name = truncated_name.lower() device_key = f"{current_device['vendorID']}:{current_device['deviceID']}" devices.append(display_name) From 149043e2c9fa25d18ea2ac1093b1a3ebfc22b2b1 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 20:11:44 -0300 Subject: [PATCH 05/29] [install] ensure that the target dirs exist, but keep it simple --- install.sh | 8 ++------ scripts/volt-helper | 1 + test.sh | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/install.sh b/install.sh index 8ff7608..2be56dc 100755 --- a/install.sh +++ b/install.sh @@ -28,19 +28,15 @@ if [[ ! -f "$HELPER_SCRIPT" ]]; then exit 1 fi -echo -e "\033[34mEnsuring installation directory exists...\033[0m" -mkdir -p "$INSTALL_DIR" - echo -e "\033[34mInstalling main executable...\033[0m" +mkdir -p "$INSTALL_DIR" install -v -m 755 -T "$EXECUTABLE" "$INSTALL_DIR/volt-gui" echo -e "\n\033[34mInstalling helper script...\033[0m" install -v -m 755 -T "$HELPER_SCRIPT" "$INSTALL_DIR/volt-helper" -echo -e "\n\033[34mEnsuring desktop applications directory exists...\033[0m" +echo -e "\n\033[34mCreating desktop entry...\033[0m" mkdir -p "$(dirname "$DESKTOP_FILE")" - -echo -e "\033[34mCreating desktop entry...\033[0m" cat > "$DESKTOP_FILE" << EOF [Desktop Entry] Name=volt-gui diff --git a/scripts/volt-helper b/scripts/volt-helper index 713123c..bd26401 100755 --- a/scripts/volt-helper +++ b/scripts/volt-helper @@ -182,6 +182,7 @@ create_gpu_script() { local script_content="$1" local volt_script="/usr/local/bin/volt" + mkdir -p "$(dirname "$volt_script")" echo -e "$script_content" > "$volt_script" 2>/dev/null chmod 755 "$volt_script" 2>/dev/null } diff --git a/test.sh b/test.sh index 955bbe0..e284977 100755 --- a/test.sh +++ b/test.sh @@ -65,6 +65,7 @@ install_helper() { if [[ "$COPY_HELPER" == true ]]; then if [[ -f "$HELPER_SCRIPT" ]]; then echo -e "${CYAN}Installing helper script...${NC}" + mkdir -p "$INSTALL_DIR" if [[ ! -w "$INSTALL_DIR" ]]; then echo -e "${YELLOW}Installing to $INSTALL_DIR requires sudo privileges${NC}" From 96ee9f7c8678bf062bf7e17558acc59ef721b7e3 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 20:38:54 -0300 Subject: [PATCH 06/29] [readme/test] improve the test script --- README.md | 6 +++--- test.sh | 62 ++++++++++++++++++++----------------------------------- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 0a475a4..d716e90 100644 --- a/README.md +++ b/README.md @@ -126,13 +126,13 @@ If this software is not provided, its options will be locked. In the case you want to contribute to the project you can use the provided `test.sh` script to test the changes you made. This script will create a Python virtual environment if one does not already exist. This way, you don't have to install the program dependencies systemwide. -The first time you run it, use the -c flag that will also copy the `volt-helper` to `/usr/local/bin/`, as the program requires it for appliying the settings: +The first time you run it, use the -c flag and sudo, that will copy the `volt-helper` to `/usr/local/bin/`, as the program requires it for appliying the settings: ``` -./test.sh -c +sudo ./test.sh -c ``` -After this unless you make changes to the `volt-helper`, or the script have been updated, just run it without the flag to avoid unnecessary overwrites of the script: +After this unless you make changes to the `volt-helper`, or the script have been updated, just run it without the flag and sudo, this will create the pyenv and run the program: ``` ./test.sh diff --git a/test.sh b/test.sh index e284977..0bace5a 100755 --- a/test.sh +++ b/test.sh @@ -15,8 +15,6 @@ SRC_FILE="src/volt-gui.py" HELPER_SCRIPT="scripts/volt-helper" INSTALL_DIR="/usr/local/bin" -COPY_HELPER=false - check_commands() { local commands=("python3" "pip") for cmd in "${commands[@]}"; do @@ -62,25 +60,15 @@ update_dependencies() { } install_helper() { - if [[ "$COPY_HELPER" == true ]]; then - if [[ -f "$HELPER_SCRIPT" ]]; then - echo -e "${CYAN}Installing helper script...${NC}" - mkdir -p "$INSTALL_DIR" - - if [[ ! -w "$INSTALL_DIR" ]]; then - echo -e "${YELLOW}Installing to $INSTALL_DIR requires sudo privileges${NC}" - sudo cp "$HELPER_SCRIPT" "$INSTALL_DIR/" - sudo chmod +x "$INSTALL_DIR/$(basename "$HELPER_SCRIPT")" - else - cp "$HELPER_SCRIPT" "$INSTALL_DIR/" - chmod +x "$INSTALL_DIR/$(basename "$HELPER_SCRIPT")" - fi - - echo -e "${GREEN}Helper script installed to: ${YELLOW}$INSTALL_DIR/$(basename "$HELPER_SCRIPT")${NC}" - else - echo -e "${YELLOW}Warning: Helper script $HELPER_SCRIPT not found, skipping installation${NC}" - fi + if [[ ! -f "$HELPER_SCRIPT" ]]; then + echo -e "${RED}Error: Helper script $HELPER_SCRIPT not found${NC}" >&2 + exit 1 fi + + echo -e "${CYAN}Installing helper script...${NC}" + cp "$HELPER_SCRIPT" "$INSTALL_DIR/" + chmod +x "$INSTALL_DIR/$(basename "$HELPER_SCRIPT")" + echo -e "${GREEN}Helper script installed to: ${YELLOW}$INSTALL_DIR/$(basename "$HELPER_SCRIPT")${NC}" } run_application() { @@ -95,27 +83,22 @@ run_application() { fi } -parse_args() { - while [[ $# -gt 0 ]]; do - case $1 in - -c) - COPY_HELPER=true - shift - ;; - *) - echo -e "${RED}Error: Unknown option '$1'${NC}" >&2 - echo -e "${CYAN}Usage: $0 [-c]${NC}" >&2 - echo -e " -c Copy volt-helper script to $INSTALL_DIR" >&2 - exit 1 - ;; - esac - done -} - main() { - trap EXIT + if [[ "${1:-}" == "-c" ]]; then + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: Installing helper script requires sudo privileges${NC}" >&2 + echo -e "${YELLOW}Please run: sudo $0 -c${NC}" >&2 + exit 1 + fi + install_helper + exit 0 + fi - parse_args "$@" + if [[ $EUID -eq 0 ]]; then + echo -e "${RED}Error: Do not run the application with sudo${NC}" >&2 + echo -e "${YELLOW}Please run without sudo: $0${NC}" >&2 + exit 1 + fi check_commands verify_files @@ -125,7 +108,6 @@ main() { source "$VENV_DIR/bin/activate" update_dependencies - install_helper echo -e "\n${GREEN}Setup complete! Starting application...${NC}" echo -e "${CYAN}────────────────────────────────────────${NC}" From 951cf7f32bc8580e49223ada2f0f5c040e92301b Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 21:04:04 -0300 Subject: [PATCH 07/29] [scripts] reorganice the script and remove comments --- scripts/volt-helper | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/volt-helper b/scripts/volt-helper index bd26401..493f634 100755 --- a/scripts/volt-helper +++ b/scripts/volt-helper @@ -151,10 +151,10 @@ read_gpu_settings() { if [ "$key" = "launch_options" ]; then continue - elif [[ "$key" == unset:* ]]; then - script_content="${script_content}unset ${key#unset:}\n" elif [ -n "$value" ]; then script_content="${script_content}export ${key}=\"${value}\"\n" + elif [[ "$key" == unset:* ]]; then + script_content="${script_content}unset ${key#unset:}\n" fi done < "$settings_file" @@ -166,12 +166,10 @@ add_launch_options() { local script_content="$2" local launch_options=$(grep "^launch_options=" "$settings_file" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - script_content="${script_content}\n# Handle launch options if present\n" + script_content="${script_content}\n\n" if [ -n "$launch_options" ]; then - script_content="${script_content}# Execute the specified program with environment variables\n" script_content="${script_content}${launch_options} \"\$@\"\n" else - script_content="${script_content}# Launch the specified program with the environment variables\n" script_content="${script_content}\"\$@\"\n" fi From c145010bd04c9aea5a1d916de6b690554cee0a82 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Sat, 11 Oct 2025 14:42:46 -0300 Subject: [PATCH 08/29] [readme/test] add the -r flag --- README.md | 16 ++++++++++++++-- test.sh | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d716e90..c71117e 100644 --- a/README.md +++ b/README.md @@ -132,14 +132,26 @@ The first time you run it, use the -c flag and sudo, that will copy the `volt-he sudo ./test.sh -c ``` -After this unless you make changes to the `volt-helper`, or the script have been updated, just run it without the flag and sudo, this will create the pyenv and run the program: +After this unless you make changes to the `volt-helper`, or the script have been updated, just run it without the flag and sudo, this will create the `py_env` folder and run the program: ``` ./test.sh ``` +To delete the `py_env` folder you can use: + +``` +./test.sh -r +``` + +or: + +``` +sudo ./test.sh -r +``` + > [!NOTE] -> You can use the `remove.sh` script to remove the `volt-helper`. The `py_env` folder should be deleted in the case you created it with your system python, and you want to use a python version that its inside a `distrobox` box, or vice versa. +> You can use the `remove.sh` script to remove the `volt-helper`. The `py_env` folder should be deleted if it becomes corrupted, or if it was created with your system python, and you want to use a python version that its inside a `distrobox` box, or vice versa. ## How to use `volt-gui`: diff --git a/test.sh b/test.sh index 0bace5a..31632d8 100755 --- a/test.sh +++ b/test.sh @@ -15,6 +15,12 @@ SRC_FILE="src/volt-gui.py" HELPER_SCRIPT="scripts/volt-helper" INSTALL_DIR="/usr/local/bin" +cleanup() { + if [[ -n "${VIRTUAL_ENV:-}" ]]; then + deactivate 2>/dev/null || true + fi +} + check_commands() { local commands=("python3" "pip") for cmd in "${commands[@]}"; do @@ -83,7 +89,24 @@ run_application() { fi } +remove_venv() { + if [[ -d "$VENV_DIR" ]]; then + echo -e "${CYAN}Removing virtual environment: $VENV_DIR${NC}" + rm -rf "$VENV_DIR" + echo -e "${GREEN}Virtual environment removed successfully${NC}" + else + echo -e "${YELLOW}No virtual environment found at: $VENV_DIR${NC}" + fi +} + main() { + trap cleanup EXIT + + if [[ "${1:-}" == "-r" ]]; then + remove_venv + exit 0 + fi + if [[ "${1:-}" == "-c" ]]; then if [[ $EUID -ne 0 ]]; then echo -e "${RED}Error: Installing helper script requires sudo privileges${NC}" >&2 From a41648ae53806bba1f489598c868f846613ebc4d Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Sat, 11 Oct 2025 15:21:23 -0300 Subject: [PATCH 09/29] [install/build/test] standarice colors on the scripts --- install.sh | 28 ++++++++++++++++------------ make-nuitka.sh | 20 +++++++++----------- make-pyinstaller.sh | 20 +++++++++----------- make-release.sh | 26 ++++++++++++-------------- remove.sh | 16 ++++++++++------ test.sh | 36 +++++++++++++++++------------------- 6 files changed, 73 insertions(+), 73 deletions(-) diff --git a/install.sh b/install.sh index 2be56dc..84c9b2c 100755 --- a/install.sh +++ b/install.sh @@ -2,10 +2,9 @@ set -euo pipefail -if [[ $EUID -ne 0 ]]; then - echo -e "\033[31mError: Please run this script as root (use sudo)\033[0m" >&2 - exit 1 -fi +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' INSTALL_DIR="/usr/local/bin" BIN_DIR="bin" @@ -13,29 +12,34 @@ EXECUTABLE="$BIN_DIR/volt-gui" HELPER_SCRIPT="scripts/volt-helper" DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" +if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 + exit 1 +fi + if [[ ! -d "$BIN_DIR" ]]; then - echo -e "\033[31mError: bin directory not found. Run make-pyinstaller.sh or make-nuitka.sh first.\033[0m" >&2 + echo -e "${RED}Error: bin directory not found. Run make-pyinstaller.sh or make-nuitka.sh first.${NC}" >&2 exit 1 fi if [[ ! -f "$EXECUTABLE" ]]; then - echo -e "\033[31mError: Executable 'volt-gui' not found in bin directory. Run make-pyinstaller.sh or make-nuitka.sh first.\033[0m" >&2 + echo -e "${RED}Error: Executable 'volt-gui' not found in bin directory. Run make-pyinstaller.sh or make-nuitka.sh first.${NC}" >&2 exit 1 fi if [[ ! -f "$HELPER_SCRIPT" ]]; then - echo -e "\033[31mError: Helper script $HELPER_SCRIPT not found.\033[0m" >&2 + echo -e "${RED}Error: Helper script $HELPER_SCRIPT not found.${NC}" >&2 exit 1 fi -echo -e "\033[34mInstalling main executable...\033[0m" +echo -e "${BLUE}Installing main executable...${NC}" mkdir -p "$INSTALL_DIR" install -v -m 755 -T "$EXECUTABLE" "$INSTALL_DIR/volt-gui" -echo -e "\n\033[34mInstalling helper script...\033[0m" +echo -e "\n${BLUE}Installing helper script...${NC}" install -v -m 755 -T "$HELPER_SCRIPT" "$INSTALL_DIR/volt-helper" -echo -e "\n\033[34mCreating desktop entry...\033[0m" +echo -e "\n${BLUE}Creating desktop entry...${NC}" mkdir -p "$(dirname "$DESKTOP_FILE")" cat > "$DESKTOP_FILE" << EOF [Desktop Entry] @@ -50,8 +54,8 @@ EOF echo "Desktop entry created at $DESKTOP_FILE" -echo -e "\n\033[34mUpdating desktop database...\033[0m" +echo -e "\n${BLUE}Updating desktop database...${NC}" update-desktop-database "$(dirname "$DESKTOP_FILE")" -echo -e "\n\033[32mInstallation completed successfully!\033[0m" +echo -e "\nInstallation completed successfully!" echo "You can now run 'volt-gui' from the terminal or application menu." diff --git a/make-nuitka.sh b/make-nuitka.sh index 8c77834..6653b8a 100755 --- a/make-nuitka.sh +++ b/make-nuitka.sh @@ -3,9 +3,7 @@ set -euo pipefail RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' +BLUE='\033[0;34m' NC='\033[0m' VENV_DIR="py_env" @@ -38,7 +36,7 @@ check_commands() { create_venv() { if [[ ! -d "$VENV_DIR" ]]; then - echo -e "${CYAN}Creating python3 virtual environment...${NC}" + echo -e "${BLUE}Creating python3 virtual environment...${NC}" python3 -m venv "$VENV_DIR" fi } @@ -56,7 +54,7 @@ update_dependencies() { stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then - echo -e "${CYAN}Updating dependencies...${NC}" + echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" echo "$current_hash" > "$REQ_HASH_FILE" @@ -64,8 +62,8 @@ update_dependencies() { } build_executable() { - echo -e "${CYAN}Building executable with Nuitka...${NC}" - echo -e "${YELLOW}Nuitka options: ${NUITKA_OPTS[*]}${NC}" + echo -e "${BLUE}Building executable with Nuitka...${NC}" + echo -e "Nuitka options: ${NUITKA_OPTS[*]}" if ! nuitka "${NUITKA_OPTS[@]}" "$SRC_FILE"; then echo -e "${RED}Error: Nuitka failed to build executable${NC}" >&2 @@ -84,19 +82,19 @@ main() { verify_requirements create_venv - echo -e "${CYAN}Activating virtual environment...${NC}" + echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" update_dependencies build_executable move_to_bin - echo -e "\n${GREEN}Build successful!${NC}" - echo -e "Executable: ${YELLOW}$BIN_DIR/$(basename "$BASE_FILENAME")${NC}" + echo -e "\nBuild successful!" + echo -e "Executable: $BIN_DIR/$(basename "$BASE_FILENAME")" if command -v du &> /dev/null; then local size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: ${YELLOW}$size${NC}" + echo -e "File size: $size" fi } diff --git a/make-pyinstaller.sh b/make-pyinstaller.sh index 624fcb1..7dcf501 100755 --- a/make-pyinstaller.sh +++ b/make-pyinstaller.sh @@ -3,9 +3,7 @@ set -euo pipefail RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' +BLUE='\033[0;34m' NC='\033[0m' VENV_DIR="py_env" @@ -37,7 +35,7 @@ check_commands() { create_venv() { if [[ ! -d "$VENV_DIR" ]]; then - echo -e "${CYAN}Creating python3 virtual environment...${NC}" + echo -e "${BLUE}Creating python3 virtual environment...${NC}" python3 -m venv "$VENV_DIR" fi } @@ -55,7 +53,7 @@ update_dependencies() { stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then - echo -e "${CYAN}Updating dependencies...${NC}" + echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" echo "$current_hash" > "$REQ_HASH_FILE" @@ -63,8 +61,8 @@ update_dependencies() { } build_executable() { - echo -e "${CYAN}Building executable with PyInstaller...${NC}" - echo -e "${YELLOW}PyInstaller options: ${PYINSTALLER_OPTS[*]}${NC}" + echo -e "${BLUE}Building executable with PyInstaller...${NC}" + echo -e "PyInstaller options: ${PYINSTALLER_OPTS[*]}" if ! pyinstaller "${PYINSTALLER_OPTS[@]}" "$SRC_FILE"; then echo -e "${RED}Error: PyInstaller failed to build executable${NC}" >&2 @@ -83,19 +81,19 @@ main() { verify_requirements create_venv - echo -e "${CYAN}Activating virtual environment...${NC}" + echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" update_dependencies build_executable move_to_bin - echo -e "\n${GREEN}Build successful!${NC}" - echo -e "Executable: ${YELLOW}$BIN_DIR/$BASE_FILENAME${NC}" + echo -e "\nBuild successful!" + echo -e "Executable: $BIN_DIR/$BASE_FILENAME" if command -v du &> /dev/null; then local size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: ${YELLOW}$size${NC}" + echo -e "File size: $size" fi } diff --git a/make-release.sh b/make-release.sh index 2dce7c4..fdda82b 100755 --- a/make-release.sh +++ b/make-release.sh @@ -3,9 +3,7 @@ set -euo pipefail RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' +BLUE='\033[0;34m' NC='\033[0m' RELEASE_DIR="releases" @@ -31,27 +29,27 @@ build_and_copy() { local build_script=$1 local target_dir=$2 - echo -e "${CYAN}Executing build script: $build_script${NC}" + echo -e "${BLUE}Executing build script: $build_script${NC}" if ! (cd .. && ./"$build_script"); then echo -e "${RED}Error: Build script $build_script failed${NC}" >&2 exit 1 fi - echo -e "${CYAN}Copying artifacts to $target_dir${NC}" + echo -e "${BLUE}Copying artifacts to $target_dir${NC}" mkdir -p "$target_dir" for item in bin install.sh remove.sh scripts; do if [[ -e "../$item" ]]; then cp -r "../$item" "$target_dir/" else - echo -e "${YELLOW}Warning: $item not found, skipping${NC}" + echo "Warning: $item not found, skipping" fi done } compress_release() { local dir_name=$1 - echo -e "${CYAN}Compressing $dir_name to ${dir_name}.tar.gz${NC}" + echo -e "${BLUE}Compressing $dir_name to ${dir_name}.tar.gz${NC}" tar -czf "${dir_name}.tar.gz" "$dir_name" } @@ -61,25 +59,25 @@ main() { ORIGINAL_DIR=$(pwd) - echo -e "${CYAN}Preparing release directory...${NC}" + echo -e "${BLUE}Preparing release directory...${NC}" rm -rf "$RELEASE_DIR" mkdir -p "$RELEASE_DIR" cd "$RELEASE_DIR" - echo -e "\n${YELLOW}=== Processing PyInstaller Build ===${NC}" + echo -e "\n=== Processing PyInstaller Build ===" build_and_copy "${BUILD_SCRIPTS[0]}" "$PYINSTALLER_BUILD" compress_release "$PYINSTALLER_BUILD" - echo -e "\n${YELLOW}=== Processing Nuitka Build ===${NC}" + echo -e "\n=== Processing Nuitka Build ===" build_and_copy "${BUILD_SCRIPTS[1]}" "$NUITKA_BUILD" compress_release "$NUITKA_BUILD" cd "$ORIGINAL_DIR" - echo -e "\n${GREEN}Release build completed successfully!${NC}" - echo -e "Created archives in ${YELLOW}$RELEASE_DIR${NC}:" - echo -e " ${YELLOW}${PYINSTALLER_BUILD}.tar.gz${NC}" - echo -e " ${YELLOW}${NUITKA_BUILD}.tar.gz${NC}" + echo -e "\nRelease build completed successfully!" + echo -e "Created archives in $RELEASE_DIR:" + echo -e " ${PYINSTALLER_BUILD}.tar.gz" + echo -e " ${NUITKA_BUILD}.tar.gz" if command -v du &> /dev/null; then echo -e "\nArchive sizes:" diff --git a/remove.sh b/remove.sh index a5355a1..ea7ff57 100755 --- a/remove.sh +++ b/remove.sh @@ -2,8 +2,12 @@ set -euo pipefail +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' + if [[ $EUID -ne 0 ]]; then - echo -e "\033[31mError: Please run this script as root (use sudo)\033[0m" >&2 + echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 exit 1 fi @@ -11,23 +15,23 @@ INSTALL_DIR="/usr/local/bin" TARGETS=("volt" "volt-gui" "volt-helper") DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" -echo -e "\033[34mRemoving installed files...\033[0m" +echo -e "${BLUE}Removing installed files...${NC}" for target in "${TARGETS[@]}"; do file="$INSTALL_DIR/$target" if [[ -f "$file" ]]; then rm -v "$file" else - echo -e "\033[33mWarning: $file not found\033[0m" + echo "Warning: $file not found" fi done if [[ -f "$DESKTOP_FILE" ]]; then rm -v "$DESKTOP_FILE" - echo -e "\n\033[34mUpdating desktop database...\033[0m" + echo -e "\n${BLUE}Updating desktop database...${NC}" update-desktop-database "$(dirname "$DESKTOP_FILE")" else - echo -e "\033[33mWarning: Desktop entry $DESKTOP_FILE not found\033[0m" + echo "Warning: Desktop entry $DESKTOP_FILE not found" fi -echo -e "\n\033[32mRemoval completed successfully!\033[0m" +echo -e "\nRemoval completed successfully!" diff --git a/test.sh b/test.sh index 31632d8..eba4e41 100755 --- a/test.sh +++ b/test.sh @@ -3,9 +3,7 @@ set -euo pipefail RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' +BLUE='\033[0;34m' NC='\033[0m' VENV_DIR="py_env" @@ -33,7 +31,7 @@ check_commands() { create_venv() { if [[ ! -d "$VENV_DIR" ]]; then - echo -e "${CYAN}Creating python3 virtual environment...${NC}" + echo -e "${BLUE}Creating python3 virtual environment...${NC}" python3 -m venv "$VENV_DIR" fi } @@ -56,12 +54,12 @@ update_dependencies() { stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then - echo -e "${CYAN}Updating dependencies...${NC}" + echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" echo "$current_hash" > "$REQ_HASH_FILE" else - echo -e "${GREEN}Dependencies are up to date${NC}" + echo "Dependencies are up to date" fi } @@ -71,16 +69,16 @@ install_helper() { exit 1 fi - echo -e "${CYAN}Installing helper script...${NC}" + echo -e "${BLUE}Installing helper script...${NC}" cp "$HELPER_SCRIPT" "$INSTALL_DIR/" chmod +x "$INSTALL_DIR/$(basename "$HELPER_SCRIPT")" - echo -e "${GREEN}Helper script installed to: ${YELLOW}$INSTALL_DIR/$(basename "$HELPER_SCRIPT")${NC}" + echo "Helper script installed to: $INSTALL_DIR/$(basename "$HELPER_SCRIPT")" } run_application() { - echo -e "${CYAN}Running application in development mode...${NC}" - echo -e "${YELLOW}Source file: $SRC_FILE${NC}" - echo -e "${YELLOW}Virtual environment: $VENV_DIR${NC}" + echo -e "${BLUE}Running application in development mode...${NC}" + echo "Source file: $SRC_FILE" + echo "Virtual environment: $VENV_DIR" echo "" if ! python3 "$SRC_FILE"; then @@ -91,11 +89,11 @@ run_application() { remove_venv() { if [[ -d "$VENV_DIR" ]]; then - echo -e "${CYAN}Removing virtual environment: $VENV_DIR${NC}" + echo -e "${BLUE}Removing virtual environment: $VENV_DIR${NC}" rm -rf "$VENV_DIR" - echo -e "${GREEN}Virtual environment removed successfully${NC}" + echo "Virtual environment removed successfully" else - echo -e "${YELLOW}No virtual environment found at: $VENV_DIR${NC}" + echo "No virtual environment found at: $VENV_DIR" fi } @@ -110,7 +108,7 @@ main() { if [[ "${1:-}" == "-c" ]]; then if [[ $EUID -ne 0 ]]; then echo -e "${RED}Error: Installing helper script requires sudo privileges${NC}" >&2 - echo -e "${YELLOW}Please run: sudo $0 -c${NC}" >&2 + echo "Please run: sudo $0 -c" >&2 exit 1 fi install_helper @@ -119,7 +117,7 @@ main() { if [[ $EUID -eq 0 ]]; then echo -e "${RED}Error: Do not run the application with sudo${NC}" >&2 - echo -e "${YELLOW}Please run without sudo: $0${NC}" >&2 + echo "Please run without sudo: $0" >&2 exit 1 fi @@ -127,13 +125,13 @@ main() { verify_files create_venv - echo -e "${CYAN}Activating virtual environment...${NC}" + echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" update_dependencies - echo -e "\n${GREEN}Setup complete! Starting application...${NC}" - echo -e "${CYAN}────────────────────────────────────────${NC}" + echo -e "\nSetup complete! Starting application..." + echo -e "${BLUE}────────────────────────────────────────${NC}" run_application } From 3fc94874b42ebe32befc99ab1fe4e6d6fe1fc1cc Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Sat, 11 Oct 2025 17:20:04 -0300 Subject: [PATCH 10/29] [remove] group all variable definitions --- remove.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/remove.sh b/remove.sh index ea7ff57..e4e6f71 100755 --- a/remove.sh +++ b/remove.sh @@ -6,15 +6,15 @@ RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' +INSTALL_DIR="/usr/local/bin" +TARGETS=("volt" "volt-gui" "volt-helper") +DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" + if [[ $EUID -ne 0 ]]; then echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 exit 1 fi -INSTALL_DIR="/usr/local/bin" -TARGETS=("volt" "volt-gui" "volt-helper") -DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" - echo -e "${BLUE}Removing installed files...${NC}" for target in "${TARGETS[@]}"; do From 232e1bbdefd862daf528da7407ad18d6a6b57e37 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Sat, 11 Oct 2025 17:22:08 -0300 Subject: [PATCH 11/29] [volt] bump reported version to 1.3.1 --- src/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.py b/src/version.py index 7b49cf1..4cf03a8 100644 --- a/src/version.py +++ b/src/version.py @@ -1 +1 @@ -VERSION = "1.3.0" +VERSION = "1.3.1" From fe132ff5ab7d083377d9b6d697173005143e4a6e Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 21:59:45 -0300 Subject: [PATCH 12/29] [volt/make/install/remove/test] add appimage support --- .gitignore | 1 + README.md | 17 ++- install.sh | 10 -- make-appimage.sh | 128 ++++++++++++++++++++ make-nuitka.sh | 28 ++--- make-pyinstaller.sh | 26 ++-- make-release.sh | 75 ++++++++---- remove.sh | 26 ++-- scripts/volt-helper => src/script_helper.py | 34 ++++-- src/volt-gui.py | 4 +- test.sh | 45 ++----- 11 files changed, 264 insertions(+), 130 deletions(-) create mode 100755 make-appimage.sh rename scripts/volt-helper => src/script_helper.py (86%) mode change 100755 => 100644 diff --git a/.gitignore b/.gitignore index 1efd599..68a02f3 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +*.AppImage # PyInstaller # Usually these files are written by a python script from a template diff --git a/README.md b/README.md index c71117e..0504d7e 100644 --- a/README.md +++ b/README.md @@ -59,18 +59,25 @@ These will be passed to the executed program. Example: - Each profile has its own set of configurations, which can be applied through the program or system tray. ## Build/Test Requirements: - +- Linux operating system - Python 3.9 or higher - Pip -- The `python3-venv` package its required on Debian/Debian based distros. -- Linux operating system - -## Additional requirements in the case you build the program using Nuitka: +- The `python3-venv` package is required on Debian/Debian based distros. +- bash +- tar +- cp, mkdir, mv, du (standard coreutils) +- shasum (for dependency hash checking) +## Additional Requirements for Building with Nuitka: - C/C++ Compiler - patchelf - ccache (optional, for optimizing compiling times) +## Additional Requirements for Creating AppImage: +- fuse +- wget +- chmod + ## Additional requirements for some Options: If this software is not provided, its options will be locked. diff --git a/install.sh b/install.sh index 84c9b2c..18f5c16 100755 --- a/install.sh +++ b/install.sh @@ -5,11 +5,9 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - INSTALL_DIR="/usr/local/bin" BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" -HELPER_SCRIPT="scripts/volt-helper" DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" if [[ $EUID -ne 0 ]]; then @@ -27,18 +25,10 @@ if [[ ! -f "$EXECUTABLE" ]]; then exit 1 fi -if [[ ! -f "$HELPER_SCRIPT" ]]; then - echo -e "${RED}Error: Helper script $HELPER_SCRIPT not found.${NC}" >&2 - exit 1 -fi - echo -e "${BLUE}Installing main executable...${NC}" mkdir -p "$INSTALL_DIR" install -v -m 755 -T "$EXECUTABLE" "$INSTALL_DIR/volt-gui" -echo -e "\n${BLUE}Installing helper script...${NC}" -install -v -m 755 -T "$HELPER_SCRIPT" "$INSTALL_DIR/volt-helper" - echo -e "\n${BLUE}Creating desktop entry...${NC}" mkdir -p "$(dirname "$DESKTOP_FILE")" cat > "$DESKTOP_FILE" << EOF diff --git a/make-appimage.sh b/make-appimage.sh new file mode 100755 index 0000000..25d9d0d --- /dev/null +++ b/make-appimage.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +set -euo pipefail + +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' +APP_NAME="volt-gui" +BIN_DIR="bin" +EXECUTABLE="$BIN_DIR/volt-gui" +APPDIR="AppDir" +DESKTOP_FILE="volt-gui.desktop" +ICON_FILE="preferences-system.png" +SOURCE_ICON="images/1.png" +APPIMAGETOOL="appimagetool-x86_64.AppImage" +OUTPUT_FILE="${APP_NAME}-x86_64.AppImage" + +cleanup() { + rm -rf "$APPDIR" 2>/dev/null || true +} + +check_commands() { + for cmd in wget chmod; do + if ! command -v "$cmd" &> /dev/null; then + echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + exit 1 + fi + done +} + +verify_files() { + if [[ ! -f "$EXECUTABLE" ]]; then + echo -e "${RED}Error: Executable $EXECUTABLE not found${NC}" >&2 + echo "Please run the build script first to create the executable" >&2 + exit 1 + fi + if [[ ! -f "$SOURCE_ICON" ]]; then + echo -e "${RED}Error: Icon file $SOURCE_ICON not found${NC}" >&2 + exit 1 + fi +} + +create_appdir_structure() { + echo -e "${BLUE}Creating AppDir structure...${NC}" + mkdir -p "$APPDIR" +} + +copy_icon() { + echo -e "${BLUE}Copying icon...${NC}" + cp "$SOURCE_ICON" "$APPDIR/$ICON_FILE" +} + +create_desktop_file() { + echo -e "${BLUE}Creating desktop file...${NC}" + cat > "$APPDIR/$DESKTOP_FILE" << 'EOF' +[Desktop Entry] +Name=volt-gui +Comment=A simple GUI program to modify and create the "volt" script and more +Exec=volt-gui +Icon=preferences-system +Terminal=false +Type=Application +Categories=Utility; +EOF +} + +create_apprun() { + echo -e "${BLUE}Creating AppRun script...${NC}" + cat > "$APPDIR/AppRun" << 'EOF' +#!/bin/bash +HERE="$(dirname "$(readlink -f "${0}")")" +export APPDIR="${HERE}" +cd "${HOME}" 2>/dev/null || cd /tmp +exec "${HERE}/volt-gui" "$@" +EOF + chmod +x "$APPDIR/AppRun" +} + +copy_executable() { + echo -e "${BLUE}Copying executable...${NC}" + cp "$EXECUTABLE" "$APPDIR/$APP_NAME" + chmod +x "$APPDIR/$APP_NAME" +} + +download_appimagetool() { + if [[ ! -f "$APPIMAGETOOL" ]]; then + echo -e "${BLUE}Downloading appimagetool...${NC}" + wget -q --show-progress \ + "https://github.com/AppImage/AppImageKit/releases/download/continuous/$APPIMAGETOOL" + chmod +x "$APPIMAGETOOL" + else + echo "appimagetool already downloaded" + fi +} + +build_appimage() { + echo -e "${BLUE}Building AppImage...${NC}" + if ! ./"$APPIMAGETOOL" "$APPDIR" "$OUTPUT_FILE"; then + echo -e "${RED}Error: Failed to build AppImage${NC}" >&2 + exit 1 + fi + chmod +x "$OUTPUT_FILE" +} + +print_success() { + echo -e "\nBuild successful!" + echo -e "AppImage: $OUTPUT_FILE" + if command -v du &> /dev/null; then + size=$(du -h "$OUTPUT_FILE" 2>/dev/null | cut -f1 || echo "Unknown") + echo -e "File size: $size" + fi +} + +main() { + trap cleanup EXIT + check_commands + verify_files + create_appdir_structure + copy_icon + create_desktop_file + copy_executable + create_apprun + download_appimagetool + build_appimage + print_success +} + +main "$@" diff --git a/make-nuitka.sh b/make-nuitka.sh index 6653b8a..97b2d37 100755 --- a/make-nuitka.sh +++ b/make-nuitka.sh @@ -5,28 +5,28 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" SRC_FILE="src/volt-gui.py" BIN_DIR="bin" BASE_FILENAME=$(basename "$SRC_FILE" .py) - NUITKA_OPTS=( "--onefile" "--output-filename=$BASE_FILENAME" "--assume-yes-for-downloads" "--enable-plugin=pyside6" ) +CURRENT_HASH="" +STORED_HASH="" +SIZE="" cleanup() { rm -rf "$BASE_FILENAME.build/" "$BASE_FILENAME.dist/" "$BASE_FILENAME.onefile-build/" 2>/dev/null || true } check_commands() { - local commands=("python3" "pip") - for cmd in "${commands[@]}"; do + for cmd in python3 pip; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 @@ -49,22 +49,20 @@ verify_requirements() { } update_dependencies() { - local current_hash stored_hash - current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) - stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$current_hash" > "$REQ_HASH_FILE" + echo "$CURRENT_HASH" > "$REQ_HASH_FILE" fi } build_executable() { echo -e "${BLUE}Building executable with Nuitka...${NC}" echo -e "Nuitka options: ${NUITKA_OPTS[*]}" - if ! nuitka "${NUITKA_OPTS[@]}" "$SRC_FILE"; then echo -e "${RED}Error: Nuitka failed to build executable${NC}" >&2 exit 1 @@ -81,20 +79,16 @@ main() { check_commands verify_requirements create_venv - echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" - update_dependencies build_executable move_to_bin - echo -e "\nBuild successful!" - echo -e "Executable: $BIN_DIR/$(basename "$BASE_FILENAME")" - + echo -e "Executable: $BIN_DIR/$BASE_FILENAME" if command -v du &> /dev/null; then - local size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: $size" + SIZE=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") + echo -e "File size: $SIZE" fi } diff --git a/make-pyinstaller.sh b/make-pyinstaller.sh index 7dcf501..2eaa5fc 100755 --- a/make-pyinstaller.sh +++ b/make-pyinstaller.sh @@ -5,7 +5,6 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" @@ -13,19 +12,20 @@ SRC_FILE="src/volt-gui.py" BIN_DIR="bin" BASE_FILENAME=$(basename "$SRC_FILE" .py) SPEC_FILE="$BASE_FILENAME.spec" - PYINSTALLER_OPTS=( "--onefile" "--name=volt-gui" ) +CURRENT_HASH="" +STORED_HASH="" +SIZE="" cleanup() { rm -rf dist/ build/ "${SPEC_FILE}" 2>/dev/null || true } check_commands() { - local commands=("python3" "pip") - for cmd in "${commands[@]}"; do + for cmd in python3 pip; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 @@ -48,22 +48,20 @@ verify_requirements() { } update_dependencies() { - local current_hash stored_hash - current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) - stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$current_hash" > "$REQ_HASH_FILE" + echo "$CURRENT_HASH" > "$REQ_HASH_FILE" fi } build_executable() { echo -e "${BLUE}Building executable with PyInstaller...${NC}" echo -e "PyInstaller options: ${PYINSTALLER_OPTS[*]}" - if ! pyinstaller "${PYINSTALLER_OPTS[@]}" "$SRC_FILE"; then echo -e "${RED}Error: PyInstaller failed to build executable${NC}" >&2 exit 1 @@ -80,20 +78,16 @@ main() { check_commands verify_requirements create_venv - echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" - update_dependencies build_executable move_to_bin - echo -e "\nBuild successful!" echo -e "Executable: $BIN_DIR/$BASE_FILENAME" - if command -v du &> /dev/null; then - local size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: $size" + SIZE=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") + echo -e "File size: $SIZE" fi } diff --git a/make-release.sh b/make-release.sh index fdda82b..7d62d2b 100755 --- a/make-release.sh +++ b/make-release.sh @@ -5,19 +5,25 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - RELEASE_DIR="releases" PYINSTALLER_BUILD="volt-gui-pyinstaller" NUITKA_BUILD="volt-gui-nuitka" BUILD_SCRIPTS=("make-pyinstaller.sh" "make-nuitka.sh") +APPIMAGE_SCRIPT="make-appimage.sh" +ORIGINAL_DIR=$(pwd) +BUILD_SCRIPT="" +TARGET_DIR="" +DIR_NAME="" +BUILD_TYPE="" +APPIMAGE_FILE="" +RENAMED_APPIMAGE="" cleanup() { true } check_commands() { - local commands=("tar" "cp" "mkdir") - for cmd in "${commands[@]}"; do + for cmd in tar cp mkdir mv; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 @@ -26,62 +32,85 @@ check_commands() { } build_and_copy() { - local build_script=$1 - local target_dir=$2 + BUILD_SCRIPT=$1 + TARGET_DIR=$2 + BUILD_TYPE=$3 - echo -e "${BLUE}Executing build script: $build_script${NC}" - if ! (cd .. && ./"$build_script"); then - echo -e "${RED}Error: Build script $build_script failed${NC}" >&2 + echo -e "${BLUE}Executing build script: $BUILD_SCRIPT${NC}" + if ! (cd "$ORIGINAL_DIR" && ./"$BUILD_SCRIPT"); then + echo -e "${RED}Error: Build script $BUILD_SCRIPT failed${NC}" >&2 exit 1 fi - echo -e "${BLUE}Copying artifacts to $target_dir${NC}" - mkdir -p "$target_dir" + echo -e "${BLUE}Building AppImage for $BUILD_TYPE${NC}" + if ! (cd "$ORIGINAL_DIR" && ./"$APPIMAGE_SCRIPT"); then + echo -e "${RED}Error: AppImage build failed${NC}" >&2 + exit 1 + fi - for item in bin install.sh remove.sh scripts; do - if [[ -e "../$item" ]]; then - cp -r "../$item" "$target_dir/" + APPIMAGE_FILE="volt-gui-x86_64.AppImage" + RENAMED_APPIMAGE="volt-gui-${BUILD_TYPE}-x86_64.AppImage" + + echo -e "${BLUE}Renaming AppImage to $RENAMED_APPIMAGE${NC}" + if [[ -f "$ORIGINAL_DIR/$APPIMAGE_FILE" ]]; then + mv "$ORIGINAL_DIR/$APPIMAGE_FILE" "$ORIGINAL_DIR/$RENAMED_APPIMAGE" + else + echo -e "${RED}Error: AppImage file not found${NC}" >&2 + exit 1 + fi + + echo -e "${BLUE}Copying artifacts to $TARGET_DIR${NC}" + mkdir -p "$TARGET_DIR" + for item in bin install.sh remove.sh; do + if [[ -e "$ORIGINAL_DIR/$item" ]]; then + cp -r "$ORIGINAL_DIR/$item" "$TARGET_DIR/" else echo "Warning: $item not found, skipping" fi done + + echo -e "${BLUE}Moving AppImage to release directory${NC}" + mv "$ORIGINAL_DIR/$RENAMED_APPIMAGE" . } compress_release() { - local dir_name=$1 - echo -e "${BLUE}Compressing $dir_name to ${dir_name}.tar.gz${NC}" - tar -czf "${dir_name}.tar.gz" "$dir_name" + DIR_NAME=$1 + echo -e "${BLUE}Compressing $DIR_NAME to ${DIR_NAME}.tar.gz${NC}" + tar -czf "${DIR_NAME}.tar.gz" "$DIR_NAME" } main() { trap cleanup EXIT check_commands - ORIGINAL_DIR=$(pwd) - echo -e "${BLUE}Preparing release directory...${NC}" rm -rf "$RELEASE_DIR" mkdir -p "$RELEASE_DIR" cd "$RELEASE_DIR" echo -e "\n=== Processing PyInstaller Build ===" - build_and_copy "${BUILD_SCRIPTS[0]}" "$PYINSTALLER_BUILD" + build_and_copy "${BUILD_SCRIPTS[0]}" "$PYINSTALLER_BUILD" "pyinstaller" compress_release "$PYINSTALLER_BUILD" echo -e "\n=== Processing Nuitka Build ===" - build_and_copy "${BUILD_SCRIPTS[1]}" "$NUITKA_BUILD" + build_and_copy "${BUILD_SCRIPTS[1]}" "$NUITKA_BUILD" "nuitka" compress_release "$NUITKA_BUILD" cd "$ORIGINAL_DIR" echo -e "\nRelease build completed successfully!" echo -e "Created archives in $RELEASE_DIR:" - echo -e " ${PYINSTALLER_BUILD}.tar.gz" - echo -e " ${NUITKA_BUILD}.tar.gz" + echo -e " ${PYINSTALLER_BUILD}.tar.gz" + echo -e " ${NUITKA_BUILD}.tar.gz" + echo -e "\nCreated AppImages in $RELEASE_DIR:" + echo -e " volt-gui-pyinstaller-x86_64.AppImage" + echo -e " volt-gui-nuitka-x86_64.AppImage" if command -v du &> /dev/null; then echo -e "\nArchive sizes:" - du -h "$RELEASE_DIR"/*.tar.gz | sed 's/^/ /' + du -h "$RELEASE_DIR"/*.tar.gz | sed 's/^/ /' + echo -e "\nAppImage sizes:" + du -h "$RELEASE_DIR"/*.AppImage | sed 's/^/ /' fi } diff --git a/remove.sh b/remove.sh index e4e6f71..296030a 100755 --- a/remove.sh +++ b/remove.sh @@ -5,33 +5,33 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - INSTALL_DIR="/usr/local/bin" TARGETS=("volt" "volt-gui" "volt-helper") DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" +FILE="" if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 - exit 1 + echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 + exit 1 fi echo -e "${BLUE}Removing installed files...${NC}" for target in "${TARGETS[@]}"; do - file="$INSTALL_DIR/$target" - if [[ -f "$file" ]]; then - rm -v "$file" - else - echo "Warning: $file not found" - fi + FILE="$INSTALL_DIR/$target" + if [[ -f "$FILE" ]]; then + rm -v "$FILE" + else + echo "Warning: $FILE not found" + fi done if [[ -f "$DESKTOP_FILE" ]]; then - rm -v "$DESKTOP_FILE" - echo -e "\n${BLUE}Updating desktop database...${NC}" - update-desktop-database "$(dirname "$DESKTOP_FILE")" + rm -v "$DESKTOP_FILE" + echo -e "\n${BLUE}Updating desktop database...${NC}" + update-desktop-database "$(dirname "$DESKTOP_FILE")" else - echo "Warning: Desktop entry $DESKTOP_FILE not found" + echo "Warning: Desktop entry $DESKTOP_FILE not found" fi echo -e "\nRemoval completed successfully!" diff --git a/scripts/volt-helper b/src/script_helper.py old mode 100755 new mode 100644 similarity index 86% rename from scripts/volt-helper rename to src/script_helper.py index 493f634..f9892d9 --- a/scripts/volt-helper +++ b/src/script_helper.py @@ -1,4 +1,11 @@ -#!/bin/bash +import os, stat + +class HelperManager: + """ + Manages the creation of the volt-helper script. + """ + + BASH_SCRIPT_CONTENT = """#!/bin/bash SCRIPT_NAME="$0" @@ -139,7 +146,7 @@ read_gpu_settings() { local settings_file="$1" - local script_content="#!/bin/bash\n\n" + local script_content="#!/bin/bash\\n\\n" while IFS='=' read -r key value || [ -n "$key" ]; do if [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]]; then @@ -152,9 +159,9 @@ if [ "$key" = "launch_options" ]; then continue elif [ -n "$value" ]; then - script_content="${script_content}export ${key}=\"${value}\"\n" + script_content="${script_content}export ${key}=\\"${value}\\"\\n" elif [[ "$key" == unset:* ]]; then - script_content="${script_content}unset ${key#unset:}\n" + script_content="${script_content}unset ${key#unset:}\\n" fi done < "$settings_file" @@ -166,11 +173,11 @@ local script_content="$2" local launch_options=$(grep "^launch_options=" "$settings_file" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - script_content="${script_content}\n\n" + script_content="${script_content}\\n\\n" if [ -n "$launch_options" ]; then - script_content="${script_content}${launch_options} \"\$@\"\n" + script_content="${script_content}${launch_options} \\"\\$@\\"\\n" else - script_content="${script_content}\"\$@\"\n" + script_content="${script_content}\\"\\$@\\"\\n" fi echo -e "$script_content" @@ -236,3 +243,16 @@ } parse_arguments "$@" +""" + + @staticmethod + def create_helper_script(): + """ + Creates the volt-helper bash script in /tmp with executable permissions. + """ + script_path = "/tmp/volt-helper" + + with open(script_path, 'w') as f: + f.write(HelperManager.BASH_SCRIPT_CONTENT) + + os.chmod(script_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) diff --git a/src/volt-gui.py b/src/volt-gui.py index 21cfdc8..af2031c 100644 --- a/src/volt-gui.py +++ b/src/volt-gui.py @@ -14,6 +14,7 @@ from workarounds import WorkaroundManager from welcome import WelcomeManager from update_checker import UpdateChecker +from script_helper import HelperManager def check_sudo_execution(): """ @@ -700,7 +701,8 @@ def apply_all_settings(self): if settings_file: gpu_args.extend(["-g", settings_file]) - all_args = ["pkexec", "/usr/local/bin/volt-helper"] + cpu_args + disk_args + kernel_args + gpu_args + HelperManager.create_helper_script() + all_args = ["pkexec", "/tmp/volt-helper"] + cpu_args + disk_args + kernel_args + gpu_args process = QProcess() WorkaroundManager.setup_clean_process(process) diff --git a/test.sh b/test.sh index eba4e41..3dab50a 100755 --- a/test.sh +++ b/test.sh @@ -5,13 +5,12 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' - VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" SRC_FILE="src/volt-gui.py" -HELPER_SCRIPT="scripts/volt-helper" -INSTALL_DIR="/usr/local/bin" +CURRENT_HASH="" +STORED_HASH="" cleanup() { if [[ -n "${VIRTUAL_ENV:-}" ]]; then @@ -20,8 +19,7 @@ cleanup() { } check_commands() { - local commands=("python3" "pip") - for cmd in "${commands[@]}"; do + for cmd in python3 pip; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 @@ -41,7 +39,6 @@ verify_files() { echo -e "${RED}Error: Requirements file $REQ_FILE not found${NC}" >&2 exit 1 fi - if [[ ! -f "$SRC_FILE" ]]; then echo -e "${RED}Error: Source file $SRC_FILE not found${NC}" >&2 exit 1 @@ -49,38 +46,24 @@ verify_files() { } update_dependencies() { - local current_hash stored_hash - current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) - stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$current_hash" > "$REQ_HASH_FILE" + echo "$CURRENT_HASH" > "$REQ_HASH_FILE" else echo "Dependencies are up to date" fi } -install_helper() { - if [[ ! -f "$HELPER_SCRIPT" ]]; then - echo -e "${RED}Error: Helper script $HELPER_SCRIPT not found${NC}" >&2 - exit 1 - fi - - echo -e "${BLUE}Installing helper script...${NC}" - cp "$HELPER_SCRIPT" "$INSTALL_DIR/" - chmod +x "$INSTALL_DIR/$(basename "$HELPER_SCRIPT")" - echo "Helper script installed to: $INSTALL_DIR/$(basename "$HELPER_SCRIPT")" -} - run_application() { echo -e "${BLUE}Running application in development mode...${NC}" echo "Source file: $SRC_FILE" echo "Virtual environment: $VENV_DIR" echo "" - if ! python3 "$SRC_FILE"; then echo -e "\n${RED}Application exited with error${NC}" >&2 exit 1 @@ -105,16 +88,6 @@ main() { exit 0 fi - if [[ "${1:-}" == "-c" ]]; then - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: Installing helper script requires sudo privileges${NC}" >&2 - echo "Please run: sudo $0 -c" >&2 - exit 1 - fi - install_helper - exit 0 - fi - if [[ $EUID -eq 0 ]]; then echo -e "${RED}Error: Do not run the application with sudo${NC}" >&2 echo "Please run without sudo: $0" >&2 @@ -124,15 +97,11 @@ main() { check_commands verify_files create_venv - echo -e "${BLUE}Activating virtual environment...${NC}" source "$VENV_DIR/bin/activate" - update_dependencies - echo -e "\nSetup complete! Starting application..." echo -e "${BLUE}────────────────────────────────────────${NC}" - run_application } From f788f74fc38377ef724b110f03811b86342d565b Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 22:06:48 -0300 Subject: [PATCH 13/29] [volt/install] consider that usr/local/bin might be a symlink --- install.sh | 2 -- src/script_helper.py | 1 - 2 files changed, 3 deletions(-) diff --git a/install.sh b/install.sh index 18f5c16..4ced51c 100755 --- a/install.sh +++ b/install.sh @@ -5,7 +5,6 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' -INSTALL_DIR="/usr/local/bin" BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" @@ -26,7 +25,6 @@ if [[ ! -f "$EXECUTABLE" ]]; then fi echo -e "${BLUE}Installing main executable...${NC}" -mkdir -p "$INSTALL_DIR" install -v -m 755 -T "$EXECUTABLE" "$INSTALL_DIR/volt-gui" echo -e "\n${BLUE}Creating desktop entry...${NC}" diff --git a/src/script_helper.py b/src/script_helper.py index f9892d9..dec2303 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -187,7 +187,6 @@ class HelperManager: local script_content="$1" local volt_script="/usr/local/bin/volt" - mkdir -p "$(dirname "$volt_script")" echo -e "$script_content" > "$volt_script" 2>/dev/null chmod 755 "$volt_script" 2>/dev/null } From 68ff01c5c421af4aee2043c0dbddf4228fdb7592 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 22:24:55 -0300 Subject: [PATCH 14/29] [volt/make/install/remove/test] update or add check_commands() --- install.sh | 11 +++++++++++ make-appimage.sh | 2 +- make-nuitka.sh | 2 +- make-pyinstaller.sh | 2 +- make-release.sh | 2 +- remove.sh | 11 +++++++++++ src/script_helper.py | 10 ++++++++++ test.sh | 3 ++- 8 files changed, 38 insertions(+), 5 deletions(-) diff --git a/install.sh b/install.sh index 4ced51c..99b4cbf 100755 --- a/install.sh +++ b/install.sh @@ -9,11 +9,22 @@ BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" +check_commands() { + for cmd in install mkdir cat update-desktop-database dirname; do + if ! command -v "$cmd" &> /dev/null; then + echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + exit 1 + fi + done +} + if [[ $EUID -ne 0 ]]; then echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 exit 1 fi +check_commands + if [[ ! -d "$BIN_DIR" ]]; then echo -e "${RED}Error: bin directory not found. Run make-pyinstaller.sh or make-nuitka.sh first.${NC}" >&2 exit 1 diff --git a/make-appimage.sh b/make-appimage.sh index 25d9d0d..7d2e465 100755 --- a/make-appimage.sh +++ b/make-appimage.sh @@ -20,7 +20,7 @@ cleanup() { } check_commands() { - for cmd in wget chmod; do + for cmd in wget chmod mkdir cp cat dirname readlink du cut; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 diff --git a/make-nuitka.sh b/make-nuitka.sh index 97b2d37..51bab1c 100755 --- a/make-nuitka.sh +++ b/make-nuitka.sh @@ -26,7 +26,7 @@ cleanup() { } check_commands() { - for cmd in python3 pip; do + for cmd in python3 pip shasum cut cat basename mkdir mv du; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 diff --git a/make-pyinstaller.sh b/make-pyinstaller.sh index 2eaa5fc..9c38323 100755 --- a/make-pyinstaller.sh +++ b/make-pyinstaller.sh @@ -25,7 +25,7 @@ cleanup() { } check_commands() { - for cmd in python3 pip; do + for cmd in python3 pip shasum cut cat basename mkdir mv du; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 diff --git a/make-release.sh b/make-release.sh index 7d62d2b..f7d2b19 100755 --- a/make-release.sh +++ b/make-release.sh @@ -23,7 +23,7 @@ cleanup() { } check_commands() { - for cmd in tar cp mkdir mv; do + for cmd in tar cp mkdir mv pwd cd du sed rm; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 diff --git a/remove.sh b/remove.sh index 296030a..da6493f 100755 --- a/remove.sh +++ b/remove.sh @@ -10,11 +10,22 @@ TARGETS=("volt" "volt-gui" "volt-helper") DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" FILE="" +check_commands() { + for cmd in rm dirname update-desktop-database; do + if ! command -v "$cmd" &> /dev/null; then + echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + exit 1 + fi + done +} + if [[ $EUID -ne 0 ]]; then echo -e "${RED}Error: Please run this script as root (use sudo)${NC}" >&2 exit 1 fi +check_commands + echo -e "${BLUE}Removing installed files...${NC}" for target in "${TARGETS[@]}"; do diff --git a/src/script_helper.py b/src/script_helper.py index dec2303..c971621 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -9,6 +9,15 @@ class HelperManager: SCRIPT_NAME="$0" +check_commands() { + for cmd in pgrep kill sleep echo chmod grep cut sed tr; do + if ! command -v "$cmd" &> /dev/null; then + echo "Error: Required command '$cmd' not found" >&2 + exit 1 + fi + done +} + apply_governor() { local governor="$1" @@ -241,6 +250,7 @@ class HelperManager: done } +check_commands parse_arguments "$@" """ diff --git a/test.sh b/test.sh index 3dab50a..3261737 100755 --- a/test.sh +++ b/test.sh @@ -19,7 +19,7 @@ cleanup() { } check_commands() { - for cmd in python3 pip; do + for cmd in python3 pip shasum cut cat rm; do if ! command -v "$cmd" &> /dev/null; then echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 exit 1 @@ -84,6 +84,7 @@ main() { trap cleanup EXIT if [[ "${1:-}" == "-r" ]]; then + check_commands remove_venv exit 0 fi From 08e225292f4b8e679290f5ac28a4b850aefd625c Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 22:28:52 -0300 Subject: [PATCH 15/29] [readme] update requirements --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0504d7e..4bd31b2 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ These will be passed to the executed program. Example: - The `python3-venv` package is required on Debian/Debian based distros. - bash - tar -- cp, mkdir, mv, du (standard coreutils) +- coreutils - shasum (for dependency hash checking) ## Additional Requirements for Building with Nuitka: @@ -74,7 +74,7 @@ These will be passed to the executed program. Example: - ccache (optional, for optimizing compiling times) ## Additional Requirements for Creating AppImage: -- fuse +- fuse or fuse3 - wget - chmod From 39e413b91b79a7dcbcd90e32af421ea403cebc53 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 22:34:18 -0300 Subject: [PATCH 16/29] [install] fix missing env var error --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index 99b4cbf..b801d50 100755 --- a/install.sh +++ b/install.sh @@ -5,6 +5,7 @@ set -euo pipefail RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' +INSTALL_DIR="/usr/local/bin" BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" From ada9a5a462bf8429c79df0e5ba2dde53baa335ea Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 13 Oct 2025 22:38:41 -0300 Subject: [PATCH 17/29] [make/install] update .desktop comments --- install.sh | 2 +- make-appimage.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index b801d50..fad7c48 100755 --- a/install.sh +++ b/install.sh @@ -44,7 +44,7 @@ mkdir -p "$(dirname "$DESKTOP_FILE")" cat > "$DESKTOP_FILE" << EOF [Desktop Entry] Name=volt-gui -Comment=A simple GUI program to modify and create the "volt" script and more +Comment=My AMD Adrenaline / NVIDIA Settings Linux Alternative Exec=volt-gui Icon=preferences-system Terminal=false diff --git a/make-appimage.sh b/make-appimage.sh index 7d2e465..e21f493 100755 --- a/make-appimage.sh +++ b/make-appimage.sh @@ -55,7 +55,7 @@ create_desktop_file() { cat > "$APPDIR/$DESKTOP_FILE" << 'EOF' [Desktop Entry] Name=volt-gui -Comment=A simple GUI program to modify and create the "volt" script and more +Comment=My AMD Adrenaline / NVIDIA Settings Linux Alternative Exec=volt-gui Icon=preferences-system Terminal=false From 05f8b1fad8fc2b411c1891895e0905a35431d133 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Wed, 15 Oct 2025 17:28:28 -0300 Subject: [PATCH 18/29] [volt] add support for different volt paths --- src/options.py | 23 +++++++++++++++++++++++ src/script_helper.py | 13 ++++++++++--- src/volt-gui.py | 3 ++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/options.py b/src/options.py index 521446d..0a147d2 100644 --- a/src/options.py +++ b/src/options.py @@ -59,6 +59,12 @@ class OptionsManager: 'text': 'Check for new versions on startup (checks once per session).', 'items': ["enable", "disable"], 'default': 'disable' + }, + 'volt_path': { + 'label': 'volt Script Path:', + 'text': 'Location for the volt script. /usr/local/bin/volt allows global "volt" command, while /tmp/volt requires the full path, but should work on inmutable/atomic distros.', + 'items': ['/usr/local/bin/volt', '/tmp/volt'], + 'default': '/usr/local/bin/volt' } } } @@ -238,6 +244,7 @@ def apply_all_options(widgets): OptionsManager.apply_scaling_options(widgets) OptionsManager.apply_welcome_message_options(widgets) OptionsManager.apply_check_updates_options(widgets) + OptionsManager.apply_volt_path_options(widgets) @staticmethod def apply_theme_options(widgets): @@ -320,6 +327,15 @@ def apply_check_updates_options(widgets): check_updates = widgets['check_updates'].currentText() == OptionsManager.OPTIONS_SETTINGS['check_updates']['items'][0] main_window.check_updates = check_updates + @staticmethod + def apply_volt_path_options(widgets): + """ + Apply the volt path option to the application. + """ + main_window = widgets['main_window'] + volt_path = widgets['volt_path'].currentText() + main_window.volt_path = volt_path + @staticmethod def apply_scaling_options(widgets): """ @@ -344,6 +360,13 @@ def get_check_updates_setting(widgets): """ return widgets['check_updates'].currentText() == OptionsManager.OPTIONS_SETTINGS['check_updates']['items'][0] + @staticmethod + def get_volt_path_setting(widgets): + """ + Get the current volt path. + """ + return widgets['volt_path'].currentText() + @staticmethod def save_and_apply_options(widgets): """ diff --git a/src/script_helper.py b/src/script_helper.py index c971621..936343f 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -194,19 +194,22 @@ class HelperManager: create_gpu_script() { local script_content="$1" - local volt_script="/usr/local/bin/volt" + local volt_script="$2" + local script_dir=$(dirname "$volt_script") + mkdir -p "$script_dir" 2>/dev/null echo -e "$script_content" > "$volt_script" 2>/dev/null chmod 755 "$volt_script" 2>/dev/null } manage_gpu() { local settings_file="$1" + local volt_script="$2" local script_content=$(read_gpu_settings "$settings_file") script_content=$(add_launch_options "$settings_file" "$script_content") - create_gpu_script "$script_content" + create_gpu_script "$script_content" "$volt_script" } parse_arguments() { @@ -239,8 +242,12 @@ class HelperManager: done manage_kernel "${kernel_args[@]}" ;; + -p|--path) + volt_path="$2" + shift 2 + ;; -g|--gpu) - manage_gpu "$2" + manage_gpu "$2" "$volt_path" shift 2 ;; *) diff --git a/src/volt-gui.py b/src/volt-gui.py index af2031c..3e25652 100644 --- a/src/volt-gui.py +++ b/src/volt-gui.py @@ -132,6 +132,7 @@ def __init__(self, instance_checker): self.use_system_tray = False self.start_minimized = False self.start_maximized = False + self.volt_path = "/usr/local/bin/volt" self.current_profile = "Default" self.cpu_widgets = {} self.kernel_widgets = {} @@ -699,7 +700,7 @@ def apply_all_settings(self): self.gpu_widgets['LaunchOptions'] ) if settings_file: - gpu_args.extend(["-g", settings_file]) + gpu_args.extend(["-p", self.volt_path, "-g", settings_file]) HelperManager.create_helper_script() all_args = ["pkexec", "/tmp/volt-helper"] + cpu_args + disk_args + kernel_args + gpu_args From e591228d3fe8942ca2ad75a4aaf195d407ccb9bb Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Wed, 15 Oct 2025 17:54:31 -0300 Subject: [PATCH 19/29] [volt] refactor the volt-helper script --- src/script_helper.py | 162 +++++++++++++------------------------------ 1 file changed, 47 insertions(+), 115 deletions(-) diff --git a/src/script_helper.py b/src/script_helper.py index 936343f..ac77b9b 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -7,7 +7,7 @@ class HelperManager: BASH_SCRIPT_CONTENT = """#!/bin/bash -SCRIPT_NAME="$0" +set -euo pipefail check_commands() { for cmd in pgrep kill sleep echo chmod grep cut sed tr; do @@ -19,148 +19,90 @@ class HelperManager: } apply_governor() { - local governor="$1" - for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do - echo "$governor" > "$CPU_PATH" + echo "$1" > "$CPU_PATH" done } apply_max_freq() { - local max_freq="$1" for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do - echo "$max_freq" > "$CPU_PATH" + echo "$1" > "$CPU_PATH" done } apply_min_freq() { - local min_freq="$1" for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do - echo "$min_freq" > "$CPU_PATH" + echo "$1" > "$CPU_PATH" done } terminate_existing_schedulers() { - local scheduler_pid=$(pgrep -f '^scx_' 2>/dev/null | head -n1) - - if [ -z "$scheduler_pid" ]; then - return - fi - - kill -INT "$scheduler_pid" 2>/dev/null - sleep 0.5 - - if kill -0 "$scheduler_pid" 2>/dev/null; then - kill -TERM "$scheduler_pid" 2>/dev/null + if pgrep -f '^scx_' &>/dev/null; then + pkill -INT -f '^scx_' 2>/dev/null sleep 0.5 - fi - - if kill -0 "$scheduler_pid" 2>/dev/null; then - kill -KILL "$scheduler_pid" 2>/dev/null + pkill -TERM -f '^scx_' 2>/dev/null + sleep 0.5 + pkill -KILL -f '^scx_' 2>/dev/null sleep 0.2 fi } start_new_scheduler() { - local scheduler="$1" - - "$scheduler" & - local scheduler_pid=$! + "$1" & sleep 1 } handle_scheduler() { terminate_existing_schedulers - - if [ -n "$scheduler" ] && [ "$scheduler" != "none" ]; then - start_new_scheduler "$scheduler" - fi + [ -n "$scheduler" ] && [ "$scheduler" != "none" ] && start_new_scheduler "$scheduler" } manage_cpu() { - local cpu_args=("$@") - - local governor="" - local scheduler="" - local max_freq="" - local min_freq="" - - for arg in "${cpu_args[@]}"; do - if [[ "$arg" == governor:* ]]; then - governor="${arg#governor:}" - elif [[ "$arg" == scheduler:* ]]; then - scheduler="${arg#scheduler:}" - elif [[ "$arg" == max_freq:* ]]; then - max_freq="${arg#max_freq:}" - elif [[ "$arg" == min_freq:* ]]; then - min_freq="${arg#min_freq:}" - fi + governor="" + scheduler="" + max_freq="" + min_freq="" + + for arg in "$@"; do + case "$arg" in + governor:*) governor="${arg#governor:}" ;; + scheduler:*) scheduler="${arg#scheduler:}" ;; + max_freq:*) max_freq="${arg#max_freq:}" ;; + min_freq:*) min_freq="${arg#min_freq:}" ;; + esac done - if [ -n "$governor" ]; then - apply_governor "$governor" - fi - - if [ -n "$min_freq" ]; then - apply_min_freq "$min_freq" - fi - - if [ -n "$max_freq" ]; then - apply_max_freq "$max_freq" - fi - - if [ -n "$scheduler" ]; then - handle_scheduler "$scheduler" - fi + [ -n "$governor" ] && apply_governor "$governor" + [ -n "$min_freq" ] && apply_min_freq "$min_freq" + [ -n "$max_freq" ] && apply_max_freq "$max_freq" + [ -n "$scheduler" ] && handle_scheduler } apply_disk_scheduler() { - local disk_name="$1" - local scheduler="$2" - local scheduler_path="/sys/block/$disk_name/queue/scheduler" - - echo "$scheduler" > "$scheduler_path" + echo "$2" > "/sys/block/$1/queue/scheduler" } manage_disk() { - local disk_args=("$@") - - for arg in "${disk_args[@]}"; do - if [[ "$arg" == *":"* ]]; then - local disk_name="${arg%%:*}" - local scheduler="${arg#*:}" - - apply_disk_scheduler "$disk_name" "$scheduler" - fi + for arg in "$@"; do + [[ "$arg" == *":"* ]] && apply_disk_scheduler "${arg%%:*}" "${arg#*:}" done } apply_kernel_parameter() { - local path="$1" - local value="$2" - - echo "$value" > "$path" 2>/dev/null + echo "$2" > "$1" 2>/dev/null } manage_kernel() { - local kernel_args=("$@") - - for setting in "${kernel_args[@]}"; do - local path="${setting%%:*}" - local value="${setting#*:}" - - apply_kernel_parameter "$path" "$value" + for setting in "$@"; do + apply_kernel_parameter "${setting%%:*}" "${setting#*:}" done } read_gpu_settings() { - local settings_file="$1" - local script_content="#!/bin/bash\\n\\n" + script_content="#!/bin/bash\\n\\n" while IFS='=' read -r key value || [ -n "$key" ]; do - if [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]]; then - continue - fi + [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]] && continue key=$(echo "$key" | tr -d ' ') value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') @@ -172,17 +114,15 @@ class HelperManager: elif [[ "$key" == unset:* ]]; then script_content="${script_content}unset ${key#unset:}\\n" fi - done < "$settings_file" + done < "$1" echo -e "$script_content" } add_launch_options() { - local settings_file="$1" - local script_content="$2" - local launch_options=$(grep "^launch_options=" "$settings_file" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + script_content="$2\\n\\n" + launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - script_content="${script_content}\\n\\n" if [ -n "$launch_options" ]; then script_content="${script_content}${launch_options} \\"\\$@\\"\\n" else @@ -193,23 +133,15 @@ class HelperManager: } create_gpu_script() { - local script_content="$1" - local volt_script="$2" - local script_dir=$(dirname "$volt_script") - - mkdir -p "$script_dir" 2>/dev/null - echo -e "$script_content" > "$volt_script" 2>/dev/null - chmod 755 "$volt_script" 2>/dev/null + mkdir -p "$(dirname "$2")" 2>/dev/null + echo -e "$1" > "$2" 2>/dev/null + chmod 755 "$2" 2>/dev/null } manage_gpu() { - local settings_file="$1" - local volt_script="$2" - - local script_content=$(read_gpu_settings "$settings_file") - script_content=$(add_launch_options "$settings_file" "$script_content") - - create_gpu_script "$script_content" "$volt_script" + script_content=$(read_gpu_settings "$1") + script_content=$(add_launch_options "$1" "$script_content") + create_gpu_script "$script_content" "$2" } parse_arguments() { @@ -217,7 +149,7 @@ class HelperManager: case "$1" in -c|--cpu) shift - local cpu_args=() + cpu_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do cpu_args+=("$1") shift @@ -226,7 +158,7 @@ class HelperManager: ;; -d|--disk) shift - local disk_args=() + disk_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do disk_args+=("$1") shift @@ -235,7 +167,7 @@ class HelperManager: ;; -k|--kernel) shift - local kernel_args=() + kernel_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do kernel_args+=("$1") shift From e882057ae789c47b8a0c940a8cb2e453e282d448 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Wed, 15 Oct 2025 18:32:34 -0300 Subject: [PATCH 20/29] [volt] fix errors created by the volt-helper refactor --- src/script_helper.py | 61 ++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/src/script_helper.py b/src/script_helper.py index ac77b9b..c739ad4 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -37,31 +37,27 @@ class HelperManager: } terminate_existing_schedulers() { - if pgrep -f '^scx_' &>/dev/null; then - pkill -INT -f '^scx_' 2>/dev/null - sleep 0.5 - pkill -TERM -f '^scx_' 2>/dev/null - sleep 0.5 - pkill -KILL -f '^scx_' 2>/dev/null - sleep 0.2 - fi -} - -start_new_scheduler() { - "$1" & - sleep 1 + pkill -INT -f '^scx_' 2>/dev/null || true + sleep 0.5 + pkill -TERM -f '^scx_' 2>/dev/null || true + sleep 0.5 + pkill -KILL -f '^scx_' 2>/dev/null || true + sleep 0.2 } handle_scheduler() { + local scheduler="$1" + terminate_existing_schedulers - [ -n "$scheduler" ] && [ "$scheduler" != "none" ] && start_new_scheduler "$scheduler" + + if [ -n "$scheduler" ] && [ "$scheduler" != "none" ]; then + "$scheduler" & + sleep 1 + fi } manage_cpu() { - governor="" - scheduler="" - max_freq="" - min_freq="" + local governor="" scheduler="" max_freq="" min_freq="" for arg in "$@"; do case "$arg" in @@ -75,7 +71,7 @@ class HelperManager: [ -n "$governor" ] && apply_governor "$governor" [ -n "$min_freq" ] && apply_min_freq "$min_freq" [ -n "$max_freq" ] && apply_max_freq "$max_freq" - [ -n "$scheduler" ] && handle_scheduler + [ -n "$scheduler" ] && handle_scheduler "$scheduler" } apply_disk_scheduler() { @@ -99,7 +95,7 @@ class HelperManager: } read_gpu_settings() { - script_content="#!/bin/bash\\n\\n" + local script_content="#!/bin/bash\\n\\n" while IFS='=' read -r key value || [ -n "$key" ]; do [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]] && continue @@ -120,8 +116,8 @@ class HelperManager: } add_launch_options() { - script_content="$2\\n\\n" - launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + local script_content="$2" + local launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') if [ -n "$launch_options" ]; then script_content="${script_content}${launch_options} \\"\\$@\\"\\n" @@ -132,24 +128,23 @@ class HelperManager: echo -e "$script_content" } -create_gpu_script() { - mkdir -p "$(dirname "$2")" 2>/dev/null - echo -e "$1" > "$2" 2>/dev/null - chmod 755 "$2" 2>/dev/null -} - manage_gpu() { - script_content=$(read_gpu_settings "$1") + local script_content=$(read_gpu_settings "$1") script_content=$(add_launch_options "$1" "$script_content") - create_gpu_script "$script_content" "$2" + + mkdir -p "$(dirname "$2")" 2>/dev/null + echo -e "$script_content" > "$2" 2>/dev/null + chmod 755 "$2" 2>/dev/null } parse_arguments() { + local volt_path="" + while [ $# -gt 0 ]; do case "$1" in -c|--cpu) shift - cpu_args=() + local cpu_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do cpu_args+=("$1") shift @@ -158,7 +153,7 @@ class HelperManager: ;; -d|--disk) shift - disk_args=() + local disk_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do disk_args+=("$1") shift @@ -167,7 +162,7 @@ class HelperManager: ;; -k|--kernel) shift - kernel_args=() + local kernel_args=() while [ $# -gt 0 ] && [[ "$1" != -* ]]; do kernel_args+=("$1") shift From e46571639cc7ba4aca89d4a5a50ba63e6080acec Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Wed, 15 Oct 2025 18:36:34 -0300 Subject: [PATCH 21/29] [volt] remove not used cmds --- src/script_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script_helper.py b/src/script_helper.py index c739ad4..3a31831 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -10,7 +10,7 @@ class HelperManager: set -euo pipefail check_commands() { - for cmd in pgrep kill sleep echo chmod grep cut sed tr; do + for cmd in chmod grep cut sed tr; do if ! command -v "$cmd" &> /dev/null; then echo "Error: Required command '$cmd' not found" >&2 exit 1 From 1558d611f91c35c28dd7195154a367e2e34ccb08 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 13:46:26 -0300 Subject: [PATCH 22/29] [volt/build/install/remove/test] prefer " " --- install.sh | 12 +- make-appimage.sh | 12 +- make-nuitka.sh | 10 +- make-pyinstaller.sh | 10 +- make-release.sh | 12 +- remove.sh | 8 +- src/config.py | 66 +-- src/cpu.py | 122 ++--- src/disk.py | 60 +-- src/gpu_launch.py | 1038 ++++++++++++++++++++--------------------- src/kernel.py | 644 ++++++++++++------------- src/options.py | 214 ++++----- src/script_helper.py | 18 +- src/theme.py | 284 +++++------ src/update_checker.py | 2 +- src/volt-gui.py | 90 ++-- src/welcome.py | 76 +-- src/workarounds.py | 16 +- test.sh | 10 +- 19 files changed, 1352 insertions(+), 1352 deletions(-) diff --git a/install.sh b/install.sh index fad7c48..15e3c35 100755 --- a/install.sh +++ b/install.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" INSTALL_DIR="/usr/local/bin" BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" @@ -13,7 +13,7 @@ DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" check_commands() { for cmd in install mkdir cat update-desktop-database dirname; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -32,7 +32,7 @@ if [[ ! -d "$BIN_DIR" ]]; then fi if [[ ! -f "$EXECUTABLE" ]]; then - echo -e "${RED}Error: Executable 'volt-gui' not found in bin directory. Run make-pyinstaller.sh or make-nuitka.sh first.${NC}" >&2 + echo -e "${RED}Error: Executable "volt-gui" not found in bin directory. Run make-pyinstaller.sh or make-nuitka.sh first.${NC}" >&2 exit 1 fi @@ -58,4 +58,4 @@ echo -e "\n${BLUE}Updating desktop database...${NC}" update-desktop-database "$(dirname "$DESKTOP_FILE")" echo -e "\nInstallation completed successfully!" -echo "You can now run 'volt-gui' from the terminal or application menu." +echo "You can now run "volt-gui" from the terminal or application menu." diff --git a/make-appimage.sh b/make-appimage.sh index e21f493..3754498 100755 --- a/make-appimage.sh +++ b/make-appimage.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" APP_NAME="volt-gui" BIN_DIR="bin" EXECUTABLE="$BIN_DIR/volt-gui" @@ -22,7 +22,7 @@ cleanup() { check_commands() { for cmd in wget chmod mkdir cp cat dirname readlink du cut; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -52,7 +52,7 @@ copy_icon() { create_desktop_file() { echo -e "${BLUE}Creating desktop file...${NC}" - cat > "$APPDIR/$DESKTOP_FILE" << 'EOF' + cat > "$APPDIR/$DESKTOP_FILE" << "EOF" [Desktop Entry] Name=volt-gui Comment=My AMD Adrenaline / NVIDIA Settings Linux Alternative @@ -66,7 +66,7 @@ EOF create_apprun() { echo -e "${BLUE}Creating AppRun script...${NC}" - cat > "$APPDIR/AppRun" << 'EOF' + cat > "$APPDIR/AppRun" << "EOF" #!/bin/bash HERE="$(dirname "$(readlink -f "${0}")")" export APPDIR="${HERE}" diff --git a/make-nuitka.sh b/make-nuitka.sh index 51bab1c..087e953 100755 --- a/make-nuitka.sh +++ b/make-nuitka.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" @@ -28,7 +28,7 @@ cleanup() { check_commands() { for cmd in python3 pip shasum cut cat basename mkdir mv du; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -49,7 +49,7 @@ verify_requirements() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then diff --git a/make-pyinstaller.sh b/make-pyinstaller.sh index 9c38323..8d2cc75 100755 --- a/make-pyinstaller.sh +++ b/make-pyinstaller.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" @@ -27,7 +27,7 @@ cleanup() { check_commands() { for cmd in python3 pip shasum cut cat basename mkdir mv du; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -48,7 +48,7 @@ verify_requirements() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then diff --git a/make-release.sh b/make-release.sh index f7d2b19..112780a 100755 --- a/make-release.sh +++ b/make-release.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" RELEASE_DIR="releases" PYINSTALLER_BUILD="volt-gui-pyinstaller" NUITKA_BUILD="volt-gui-nuitka" @@ -25,7 +25,7 @@ cleanup() { check_commands() { for cmd in tar cp mkdir mv pwd cd du sed rm; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -108,9 +108,9 @@ main() { if command -v du &> /dev/null; then echo -e "\nArchive sizes:" - du -h "$RELEASE_DIR"/*.tar.gz | sed 's/^/ /' + du -h "$RELEASE_DIR"/*.tar.gz | sed "s/^/ /" echo -e "\nAppImage sizes:" - du -h "$RELEASE_DIR"/*.AppImage | sed 's/^/ /' + du -h "$RELEASE_DIR"/*.AppImage | sed "s/^/ /" fi } diff --git a/remove.sh b/remove.sh index da6493f..e2c40bb 100755 --- a/remove.sh +++ b/remove.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" INSTALL_DIR="/usr/local/bin" TARGETS=("volt" "volt-gui" "volt-helper") DESKTOP_FILE="/usr/share/applications/volt-gui.desktop" @@ -13,7 +13,7 @@ FILE="" check_commands() { for cmd in rm dirname update-desktop-database; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done diff --git a/src/config.py b/src/config.py index 083fee1..a10e8f9 100644 --- a/src/config.py +++ b/src/config.py @@ -44,15 +44,15 @@ def save_config(cpu_widgets, gpu_widgets, kernel_widgets, disk_widgets, profile_ cpu_config = {} for setting_key in CPUManager.CPU_SETTINGS.keys(): - if setting_key in cpu_widgets and hasattr(cpu_widgets[setting_key], 'currentText'): + if setting_key in cpu_widgets and hasattr(cpu_widgets[setting_key], "currentText"): cpu_config[setting_key] = cpu_widgets[setting_key].currentText() if cpu_config: - config['CPU'] = cpu_config + config["CPU"] = cpu_config gpu_config = {} for setting_key in GPULaunchManager.GPU_SETTINGS.keys(): for category_name, category_widgets in gpu_widgets.items(): - if category_name != 'LaunchOptions' and setting_key in category_widgets: + if category_name != "LaunchOptions" and setting_key in category_widgets: widget = category_widgets[setting_key] if isinstance(widget, QComboBox): gpu_config[setting_key] = widget.currentText() @@ -60,31 +60,31 @@ def save_config(cpu_widgets, gpu_widgets, kernel_widgets, disk_widgets, profile_ gpu_config[setting_key] = widget.text() break if gpu_config: - config['GPU'] = gpu_config + config["GPU"] = gpu_config - if 'LaunchOptions' in gpu_widgets and 'launch_options_input' in gpu_widgets['LaunchOptions']: - launch_options = gpu_widgets['LaunchOptions']['launch_options_input'].text().replace('%', '%%') - config['LaunchOptions'] = {'launch_options': launch_options} + if "LaunchOptions" in gpu_widgets and "launch_options_input" in gpu_widgets["LaunchOptions"]: + launch_options = gpu_widgets["LaunchOptions"]["launch_options_input"].text().replace("%", "%%") + config["LaunchOptions"] = {"launch_options": launch_options} kernel_config = {} for setting_key in KernelManager.KERNEL_SETTINGS.keys(): - widget_key = f'{setting_key}_input' + widget_key = f"{setting_key}_input" if widget_key in kernel_widgets: value = kernel_widgets[widget_key].text().strip() if value: kernel_config[setting_key] = value if kernel_config: - config['Kernel'] = kernel_config + config["Kernel"] = kernel_config disk_config = {} - for disk_name, disk_widgets_dict in disk_widgets['disk_settings'].items(): + for disk_name, disk_widgets_dict in disk_widgets["disk_settings"].items(): for setting_key in DiskManager.DISK_SETTINGS.keys(): if setting_key in disk_widgets_dict: disk_config[f"{disk_name}_{setting_key}"] = disk_widgets_dict[setting_key].currentText() if disk_config: - config['Disk'] = disk_config + config["Disk"] = disk_config - with open(ConfigManager.get_config_path(profile_name), 'w') as configfile: + with open(ConfigManager.get_config_path(profile_name), "w") as configfile: config.write(configfile) @staticmethod @@ -100,41 +100,41 @@ def load_config(cpu_widgets, gpu_widgets, kernel_widgets, disk_widgets, profile_ config.read(config_path) - if 'CPU' in config: + if "CPU" in config: for setting_key in CPUManager.CPU_SETTINGS.keys(): - if setting_key in config['CPU'] and setting_key in cpu_widgets: - cpu_widgets[setting_key].setCurrentText(config['CPU'][setting_key]) + if setting_key in config["CPU"] and setting_key in cpu_widgets: + cpu_widgets[setting_key].setCurrentText(config["CPU"][setting_key]) - if 'GPU' in config: + if "GPU" in config: for setting_key in GPULaunchManager.GPU_SETTINGS.keys(): - if setting_key in config['GPU']: + if setting_key in config["GPU"]: for category_name, category_widgets in gpu_widgets.items(): - if category_name != 'LaunchOptions' and setting_key in category_widgets: + if category_name != "LaunchOptions" and setting_key in category_widgets: widget = category_widgets[setting_key] - value = config['GPU'][setting_key] + value = config["GPU"][setting_key] if isinstance(widget, QComboBox): widget.setCurrentText(value) elif isinstance(widget, QLineEdit): widget.setText(value) break - if 'LaunchOptions' in config and 'LaunchOptions' in gpu_widgets and 'launch_options_input' in gpu_widgets['LaunchOptions']: - launch_options = config['LaunchOptions'].get('launch_options', '').replace('%%', '%') - gpu_widgets['LaunchOptions']['launch_options_input'].setText(launch_options) + if "LaunchOptions" in config and "LaunchOptions" in gpu_widgets and "launch_options_input" in gpu_widgets["LaunchOptions"]: + launch_options = config["LaunchOptions"].get("launch_options", "").replace("%%", "%") + gpu_widgets["LaunchOptions"]["launch_options_input"].setText(launch_options) - if kernel_widgets and 'Kernel' in config: + if kernel_widgets and "Kernel" in config: for setting_key in KernelManager.KERNEL_SETTINGS.keys(): - if setting_key in config['Kernel']: - widget_key = f'{setting_key}_input' + if setting_key in config["Kernel"]: + widget_key = f"{setting_key}_input" if widget_key in kernel_widgets: - kernel_widgets[widget_key].setText(config['Kernel'][setting_key]) - - if disk_widgets and 'disk_settings' in disk_widgets and 'Disk' in config: - for config_key, value in config['Disk'].items(): - if '_' in config_key: - disk_name, setting_key = config_key.rsplit('_', 1) - if disk_name in disk_widgets['disk_settings'] and setting_key in disk_widgets['disk_settings'][disk_name]: - disk_widgets['disk_settings'][disk_name][setting_key].setCurrentText(value) + kernel_widgets[widget_key].setText(config["Kernel"][setting_key]) + + if disk_widgets and "disk_settings" in disk_widgets and "Disk" in config: + for config_key, value in config["Disk"].items(): + if "_" in config_key: + disk_name, setting_key = config_key.rsplit("_", 1) + if disk_name in disk_widgets["disk_settings"] and setting_key in disk_widgets["disk_settings"][disk_name]: + disk_widgets["disk_settings"][disk_name][setting_key].setCurrentText(value) return True diff --git a/src/cpu.py b/src/cpu.py index 021aaea..b732a8d 100644 --- a/src/cpu.py +++ b/src/cpu.py @@ -7,42 +7,42 @@ class CPUManager: CPU_SETTINGS_CATEGORIES = { "Frequency": { - 'scaling_governor': { - 'label': "Governor:", - 'text': "Controls CPU frequency scaling policy to balance performance and power consumption.", - 'items': ["unset"], - 'path': "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", - 'available_path': "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", - 'is_dynamic': True + "scaling_governor": { + "label": "Governor:", + "text": "Controls CPU frequency scaling policy to balance performance and power consumption.", + "items": ["unset"], + "path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", + "available_path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", + "is_dynamic": True }, - 'scaling_max_freq': { - 'label': "Max Frequency (MHz):", - 'text': "Upper limit for CPU frequency. Higher values increase performance but consume more power.", - 'items': ["unset"], - 'path': "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", - 'min_path': "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq", - 'max_path': "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", - 'is_dynamic': False, - 'convert_to_mhz': True + "scaling_max_freq": { + "label": "Max Frequency (MHz):", + "text": "Upper limit for CPU frequency. Higher values increase performance but consume more power.", + "items": ["unset"], + "path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", + "min_path": "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq", + "max_path": "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", + "is_dynamic": False, + "convert_to_mhz": True }, - 'scaling_min_freq': { - 'label': "Min Frequency (MHz):", - 'text': "Lower limit for CPU frequency. Higher values reduce latency but prevent deep power saving.", - 'items': ["unset"], - 'path': "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", - 'min_path': "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq", - 'max_path': "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", - 'is_dynamic': False, - 'convert_to_mhz': True + "scaling_min_freq": { + "label": "Min Frequency (MHz):", + "text": "Lower limit for CPU frequency. Higher values reduce latency but prevent deep power saving.", + "items": ["unset"], + "path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", + "min_path": "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq", + "max_path": "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", + "is_dynamic": False, + "convert_to_mhz": True } }, "Scheduler": { - 'scheduler': { - 'label': "Pluggable Scheduler:", - 'text': "BPF-based schedulers (sched_ext) for workload-specific optimizations like latency, throughput, or power efficiency.", - 'items': ["unset", "none"], - 'search_paths': ["/usr/bin/", "/usr/local/bin/"], - 'is_dynamic': True + "scheduler": { + "label": "Pluggable Scheduler:", + "text": "BPF-based schedulers (sched_ext) for workload-specific optimizations like latency, throughput, or power efficiency.", + "items": ["unset", "none"], + "search_paths": ["/usr/bin/", "/usr/local/bin/"], + "is_dynamic": True } } } @@ -68,21 +68,21 @@ def get_current_value(setting_info): """ Get the current value for a CPU setting. """ - if 'path' not in setting_info: + if "path" not in setting_info: return None try: - with open(setting_info['path'], "r") as f: + with open(setting_info["path"], "r") as f: value = f.read().strip() - if setting_info.get('convert_to_mhz', False): + if setting_info.get("convert_to_mhz", False): try: value = str(int(value) // 1000) except ValueError: pass - if setting_info.get('is_dynamic', False): - match = re.search(r'\[([^\]]+)\]', value) + if setting_info.get("is_dynamic", False): + match = re.search(r"\[([^\]]+)\]", value) if match: return match.group(1) else: @@ -97,19 +97,19 @@ def get_available_values(setting_info): """ Get available values for a CPU setting. """ - base_items = setting_info.get('items', ["unset"]).copy() + base_items = setting_info.get("items", ["unset"]).copy() - if setting_info.get('is_dynamic', False): - if 'available_path' in setting_info: + if setting_info.get("is_dynamic", False): + if "available_path" in setting_info: try: - with open(setting_info['available_path'], "r") as f: + with open(setting_info["available_path"], "r") as f: available_values = f.read().strip().split() return base_items + [item for item in available_values if item not in base_items] except Exception: return base_items - elif 'search_paths' in setting_info: + elif "search_paths" in setting_info: schedulers = base_items.copy() - for search_path in setting_info['search_paths']: + for search_path in setting_info["search_paths"]: try: scx_files = glob.glob(os.path.join(search_path, "scx_*")) for file_path in scx_files: @@ -121,9 +121,9 @@ def get_available_values(setting_info): return schedulers else: try: - with open(setting_info['min_path'], "r") as f: + with open(setting_info["min_path"], "r") as f: min_freq = int(f.read().strip()) // 1000 - with open(setting_info['max_path'], "r") as f: + with open(setting_info["max_path"], "r") as f: max_freq = int(f.read().strip()) // 1000 freq_values = [str(f) for f in range(min_freq, max_freq + 100, 100)] return base_items + freq_values @@ -176,28 +176,28 @@ def create_cpu_tab(): for category_name, category_settings in CPUManager.CPU_SETTINGS_CATEGORIES.items(): for setting_key, setting_info in category_settings.items(): layout = QHBoxLayout() - label = QLabel(setting_info['label']) + label = QLabel(setting_info["label"]) label.setWordWrap(True) label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) widgets[setting_key] = QComboBox() is_accessible = True - if 'path' in setting_info: - is_accessible = CPUManager.get_available_setting(setting_info['path']) + if "path" in setting_info: + is_accessible = CPUManager.get_available_setting(setting_info["path"]) available_values = CPUManager.get_available_values(setting_info) - if setting_key == 'scaling_max_freq' and not setting_info.get('is_dynamic', False): + if setting_key == "scaling_max_freq" and not setting_info.get("is_dynamic", False): available_values = list(reversed(available_values)) widgets[setting_key].addItems(available_values) if is_accessible: - widgets[setting_key].setToolTip(setting_info['text']) + widgets[setting_key].setToolTip(setting_info["text"]) else: widgets[setting_key].setEnabled(False) - if 'path' in setting_info: + if "path" in setting_info: widgets[setting_key].setToolTip(f"Setting file not available - {setting_info['label']} selection disabled") else: widgets[setting_key].setToolTip(f"SCX schedulers not available - {setting_info['label']} selection disabled") @@ -212,7 +212,7 @@ def create_cpu_tab(): current_value_label = QLabel("Updating...") current_value_label.setContentsMargins(0, 0, 0, 10) scroll_layout.addWidget(current_value_label) - widgets[f'current_{setting_key}_value'] = current_value_label + widgets[f"current_{setting_key}_value"] = current_value_label scroll_layout.addStretch(1) scroll_area.setWidget(scroll_widget) @@ -220,9 +220,9 @@ def create_cpu_tab(): CPUManager.create_cpu_apply_button(main_layout, widgets) - widgets['cpu_settings_applied'] = False - widgets['is_process_running'] = False - widgets['process'] = None + widgets["cpu_settings_applied"] = False + widgets["is_process_running"] = False + widgets["process"] = None return cpu_tab, widgets @@ -236,12 +236,12 @@ def create_cpu_apply_button(parent_layout, widgets): button_layout = QHBoxLayout(button_container) button_layout.setContentsMargins(11, 10, 11, 0) - widgets['cpu_apply_button'] = QPushButton("Apply") - widgets['cpu_apply_button'].setMinimumSize(100, 30) - widgets['cpu_apply_button'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + widgets["cpu_apply_button"] = QPushButton("Apply") + widgets["cpu_apply_button"].setMinimumSize(100, 30) + widgets["cpu_apply_button"].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) button_layout.addStretch(1) - button_layout.addWidget(widgets['cpu_apply_button']) + button_layout.addWidget(widgets["cpu_apply_button"]) button_layout.addStretch(1) parent_layout.addWidget(button_container) @@ -253,11 +253,11 @@ def refresh_cpu_values(widgets): Refresh the current CPU values displayed in the interface. """ for setting_key, setting_info in CPUManager.CPU_SETTINGS.items(): - current_value_label = widgets.get(f'current_{setting_key}_value') + current_value_label = widgets.get(f"current_{setting_key}_value") if not current_value_label: continue - if setting_key == 'scheduler': + if setting_key == "scheduler": current_value_label.setText("Updating...") try: running_scheduler = CPUManager.get_current_scheduler() @@ -271,7 +271,7 @@ def refresh_cpu_values(widgets): except Exception: current_value_label.setText("current: Error") else: - is_accessible = CPUManager.get_available_setting(setting_info['path']) + is_accessible = CPUManager.get_available_setting(setting_info["path"]) if not is_accessible: current_value_label.setText("current: unset") else: diff --git a/src/disk.py b/src/disk.py index 9b3216a..af6bea9 100644 --- a/src/disk.py +++ b/src/disk.py @@ -8,10 +8,10 @@ class DiskManager: DISK_SETTINGS_CATEGORIES = { "Scheduler": { - 'scheduler': { - 'label': "Scheduler:", - 'items': ["unset"], - 'text': "Determines how disk I/O requests are scheduled and merged to balance throughput and latency." + "scheduler": { + "label": "Scheduler:", + "items": ["unset"], + "text": "Determines how disk I/O requests are scheduled and merged to balance throughput and latency." } } } @@ -29,13 +29,13 @@ def get_schedulers(): try: scheduler_files = glob.glob(DiskManager.DISK_SCHEDULER_PATH_PATTERN) for file_path in scheduler_files: - disk_name = file_path.split('/')[-3] + disk_name = file_path.split("/")[-3] try: - with open(file_path, 'r') as f: + with open(file_path, "r") as f: content = f.read().strip() scheduler_info = DiskManager.parse_scheduler_content(content) if scheduler_info: - scheduler_info['path'] = file_path + scheduler_info["path"] = file_path disk_info[disk_name] = scheduler_info except Exception: continue @@ -56,9 +56,9 @@ def parse_scheduler_content(content): if not tokens: return None - available = DiskManager.DISK_SETTINGS_CATEGORIES["Scheduler"]['scheduler']['items'].copy() + available = DiskManager.DISK_SETTINGS_CATEGORIES["Scheduler"]["scheduler"]["items"].copy() current = None - bracket_pattern = re.compile(r'\[([^\]]+)\]') + bracket_pattern = re.compile(r"\[([^\]]+)\]") for token in tokens: bracket_match = bracket_pattern.search(token) @@ -83,7 +83,7 @@ def parse_scheduler_content(content): seen.add(scheduler) unique_available.append(scheduler) - return {'current': current, 'available': unique_available} + return {"current": current, "available": unique_available} except Exception: return None @@ -108,7 +108,7 @@ def create_disk_tab(): scroll_layout.setContentsMargins(10, 10, 10, 0) widgets = {} - widgets['disk_settings'] = {} + widgets["disk_settings"] = {} disk_info = DiskManager.get_schedulers() sorted_disk_names = sorted(disk_info.keys()) @@ -126,28 +126,28 @@ def create_disk_tab(): disk_widgets[setting_key] = QComboBox() - available_schedulers = scheduler_info['available'] - if setting_info['items'][0] in available_schedulers: - sorted_schedulers = [setting_info['items'][0]] + sorted([s for s in available_schedulers if s != setting_info['items'][0]]) + available_schedulers = scheduler_info["available"] + if setting_info["items"][0] in available_schedulers: + sorted_schedulers = [setting_info["items"][0]] + sorted([s for s in available_schedulers if s != setting_info["items"][0]]) else: sorted_schedulers = sorted(available_schedulers) disk_widgets[setting_key].addItems(sorted_schedulers) - disk_widgets[setting_key].setCurrentText(setting_info['items'][0]) + disk_widgets[setting_key].setCurrentText(setting_info["items"][0]) disk_widgets[setting_key].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - disk_widgets[setting_key].setToolTip(setting_info['text']) + disk_widgets[setting_key].setToolTip(setting_info["text"]) layout.addWidget(label) layout.addWidget(disk_widgets[setting_key]) scroll_layout.addLayout(layout) - current_scheduler = scheduler_info['current'] + current_scheduler = scheduler_info["current"] current_value_label = QLabel(f"current: {current_scheduler}") current_value_label.setContentsMargins(0, 0, 0, 10) scroll_layout.addWidget(current_value_label) - disk_widgets[f'current_{setting_key}_value'] = current_value_label + disk_widgets[f"current_{setting_key}_value"] = current_value_label - widgets['disk_settings'][disk_name] = disk_widgets + widgets["disk_settings"][disk_name] = disk_widgets scroll_layout.addStretch(1) scroll_area.setWidget(scroll_widget) @@ -155,9 +155,9 @@ def create_disk_tab(): DiskManager.create_disk_apply_button(main_layout, widgets) - widgets['disk_settings_applied'] = False - widgets['is_process_running'] = False - widgets['process'] = None + widgets["disk_settings_applied"] = False + widgets["is_process_running"] = False + widgets["process"] = None return disk_tab, widgets @@ -171,12 +171,12 @@ def create_disk_apply_button(parent_layout, widgets): button_layout = QHBoxLayout(button_container) button_layout.setContentsMargins(11, 10, 11, 0) - widgets['disk_apply_button'] = QPushButton("Apply") - widgets['disk_apply_button'].setMinimumSize(100, 30) - widgets['disk_apply_button'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + widgets["disk_apply_button"] = QPushButton("Apply") + widgets["disk_apply_button"].setMinimumSize(100, 30) + widgets["disk_apply_button"].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) button_layout.addStretch(1) - button_layout.addWidget(widgets['disk_apply_button']) + button_layout.addWidget(widgets["disk_apply_button"]) button_layout.addStretch(1) parent_layout.addWidget(button_container) @@ -190,7 +190,7 @@ def refresh_disk_values(widgets): disk_info = DiskManager.get_schedulers() for disk_name, scheduler_info in disk_info.items(): - if disk_name in widgets['disk_settings']: - disk_widgets = widgets['disk_settings'][disk_name] - current_scheduler = scheduler_info['current'] - disk_widgets['current_scheduler_value'].setText(f"current: {current_scheduler}") + if disk_name in widgets["disk_settings"]: + disk_widgets = widgets["disk_settings"][disk_name] + current_scheduler = scheduler_info["current"] + disk_widgets["current_scheduler_value"].setText(f"current: {current_scheduler}") diff --git a/src/gpu_launch.py b/src/gpu_launch.py index 8d52cae..8b8fcc7 100644 --- a/src/gpu_launch.py +++ b/src/gpu_launch.py @@ -9,154 +9,154 @@ class GPULaunchManager: GPU_SETTINGS_CATEGORIES = { "Mesa": { - 'mesa_gl_vsync': { - 'label': "OpenGL Vsync:", - 'text': "OpenGL vertical synchronization.", - 'items': ["unset", "program decides (default)", "default interval 0", "default interval 1", "on", "off"], - 'env_mapping': { - 'var_names': ['vblank_mode'], - 'values': {'default interval 0': '1', 'default interval 1': '2', 'on': '3', 'off': '0'} + "mesa_gl_vsync": { + "label": "OpenGL Vsync:", + "text": "OpenGL vertical synchronization.", + "items": ["unset", "program decides (default)", "default interval 0", "default interval 1", "on", "off"], + "env_mapping": { + "var_names": ["vblank_mode"], + "values": {"default interval 0": "1", "default interval 1": "2", "on": "3", "off": "0"} } }, - 'mesa_gl_thread_opt': { - 'label': "OpenGL Thread Optimizations:", - 'text': "Multi-threaded OpenGL command processing. Might improve or worsen OpenGL performance depending on the program being run.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['mesa_glthread'], - 'values': {'on': 'true'} + "mesa_gl_thread_opt": { + "label": "OpenGL Thread Optimizations:", + "text": "Multi-threaded OpenGL command processing. Might improve or worsen OpenGL performance depending on the program being run.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["mesa_glthread"], + "values": {"on": "true"} } }, - 'mesa_gl_dither': { - 'label': "OpenGL Texture Dithering:", - 'text': "OpenGL color dithering on low-depth framebuffers. Reduces color banding on displays with limited color depth at minimal performance cost.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['MESA_NO_DITHER'], - 'values': {'off': '1'} + "mesa_gl_dither": { + "label": "OpenGL Texture Dithering:", + "text": "OpenGL color dithering on low-depth framebuffers. Reduces color banding on displays with limited color depth at minimal performance cost.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["MESA_NO_DITHER"], + "values": {"off": "1"} } }, - 'mesa_gl_msaa': { - 'label': "OpenGL MSAA:", - 'text': "Multisample anti-aliasing in OpenGL. Smooths jagged edges by sampling multiple points per pixel, improving image quality with performance impact.", - 'items': ["unset", "on (default)", 'off'], - 'env_mapping': { - 'var_names': ['DRI_NO_MSAA'], - 'values': {'off': '1'} + "mesa_gl_msaa": { + "label": "OpenGL MSAA:", + "text": "Multisample anti-aliasing in OpenGL. Smooths jagged edges by sampling multiple points per pixel, improving image quality with performance impact.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["DRI_NO_MSAA"], + "values": {"off": "1"} } }, - 'mesa_gl_error_check': { - 'label': "OpenGL Error Checking:", - 'text': "OpenGL error checking. Validates API calls for correctness; disable for performance in stable applications.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['MESA_NO_ERROR'], - 'values': {'off': '1'} + "mesa_gl_error_check": { + "label": "OpenGL Error Checking:", + "text": "OpenGL error checking. Validates API calls for correctness; disable for performance in stable applications.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["MESA_NO_ERROR"], + "values": {"off": "1"} } }, - 'mesa_gl_fake': { - 'label': "OpenGL Version Spoofing:", - 'text': "Report a different OpenGL version to applications. Useful for running games that check version numbers but don't need newer features.", - 'items': ["unset", "off (default)", "3.3", "3.3compat", "4.6", "4.6compat"], - 'env_mapping': { - 'var_names': ['MESA_GL_VERSION_OVERRIDE'], - 'direct_value': True + "mesa_gl_fake": { + "label": "OpenGL Version Spoofing:", + "text": "Report a different OpenGL version to applications. Useful for running games that check version numbers but don't need newer features.", + "items": ["unset", "off (default)", "3.3", "3.3compat", "4.6", "4.6compat"], + "env_mapping": { + "var_names": ["MESA_GL_VERSION_OVERRIDE"], + "direct_value": True } }, - 'mesa_glsl_fake': { - 'label': "GLSL Version Spoofing:", - 'text': "Report a different GLSL version to applications. Works with OpenGL version spoofing for compatibility workarounds.", - 'items': ["unset", "off (default)", "330", "460"], - 'env_mapping': { - 'var_names': ['MESA_GLSL_VERSION_OVERRIDE'], - 'direct_value': True + "mesa_glsl_fake": { + "label": "GLSL Version Spoofing:", + "text": "Report a different GLSL version to applications. Works with OpenGL version spoofing for compatibility workarounds.", + "items": ["unset", "off (default)", "330", "460"], + "env_mapping": { + "var_names": ["MESA_GLSL_VERSION_OVERRIDE"], + "direct_value": True } }, - 'mesa_vk_vsync': { - 'label': "Vulkan Vsync:", - 'text': "Vulkan vertical synchronization.", - 'items': ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], - 'env_mapping': { - 'var_names': ['MESA_VK_WSI_PRESENT_MODE'], - 'values': {'mailbox': 'mailbox', 'adaptive vsync': 'relaxed', 'on': 'fifo', 'off': 'immediate'} + "mesa_vk_vsync": { + "label": "Vulkan Vsync:", + "text": "Vulkan vertical synchronization.", + "items": ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], + "env_mapping": { + "var_names": ["MESA_VK_WSI_PRESENT_MODE"], + "values": {"mailbox": "mailbox", "adaptive vsync": "relaxed", "on": "fifo", "off": "immediate"} } }, - 'mesa_vk_submit_thread': { - 'label': "Vulkan Submit Thread:", - 'text': "Dedicated thread for Vulkan command submission. Separates command submission from command recording, might reduce CPU overhead.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['MESA_VK_ENABLE_SUBMIT_THREAD'], - 'values': {'on': '1'} + "mesa_vk_submit_thread": { + "label": "Vulkan Submit Thread:", + "text": "Dedicated thread for Vulkan command submission. Separates command submission from command recording, might reduce CPU overhead.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["MESA_VK_ENABLE_SUBMIT_THREAD"], + "values": {"on": "1"} } }, - 'mesa_vk_fake': { - 'label': "Vulkan Version Spoofing:", - 'text': "Report a different Vulkan version to applications. Bypasses version checks for games that artificially restrict compatibility.", - 'items': ["unset", "off (default)", "1.1", "1.2", "1.3", "1.4"], - 'env_mapping': { - 'var_names': ['MESA_VK_VERSION_OVERRIDE'], - 'direct_value': True + "mesa_vk_fake": { + "label": "Vulkan Version Spoofing:", + "text": "Report a different Vulkan version to applications. Bypasses version checks for games that artificially restrict compatibility.", + "items": ["unset", "off (default)", "1.1", "1.2", "1.3", "1.4"], + "env_mapping": { + "var_names": ["MESA_VK_VERSION_OVERRIDE"], + "direct_value": True } }, - 'mesa_shader_cache': { - 'label': "Shader Cache:", - 'text': "Disk-based shader caching. Stores compiled shaders to disk to eliminate compilation stuttering on subsequent launches.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['MESA_SHADER_CACHE_DISABLE', 'MESA_GLSL_CACHE_DISABLE'], - 'values': {'off': 'true'} + "mesa_shader_cache": { + "label": "Shader Cache:", + "text": "Disk-based shader caching. Stores compiled shaders to disk to eliminate compilation stuttering on subsequent launches.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["MESA_SHADER_CACHE_DISABLE", "MESA_GLSL_CACHE_DISABLE"], + "values": {"off": "true"} } }, - 'mesa_cache_size': { - 'label': "Shader Cache Size (GB):", - 'text': "Maximum size for the shader cache. Larger caches store more compiled shaders but consume more disk space.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(1, 11)] + [str(i) for i in [25, 50, 75, 100]], - 'env_mapping': { - 'var_names': ['MESA_SHADER_CACHE_MAX_SIZE', 'MESA_GLSL_CACHE_MAX_SIZE'], - 'direct_value': True + "mesa_cache_size": { + "label": "Shader Cache Size (GB):", + "text": "Maximum size for the shader cache. Larger caches store more compiled shaders but consume more disk space.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(1, 11)] + [str(i) for i in [25, 50, 75, 100]], + "env_mapping": { + "var_names": ["MESA_SHADER_CACHE_MAX_SIZE", "MESA_GLSL_CACHE_MAX_SIZE"], + "direct_value": True } }, - 'radeonsi_no_infinite_interp': { - 'label': "RadeonSI Disable Infinite Interpolation:", - 'text': "Disable infinite interpolation in RadeonSI. Workaround for rendering bugs in some games on AMD GPUs.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['radeonsi_no_infinite_interp'], - 'values': {'on': 'true'} + "radeonsi_no_infinite_interp": { + "label": "RadeonSI Disable Infinite Interpolation:", + "text": "Disable infinite interpolation in RadeonSI. Workaround for rendering bugs in some games on AMD GPUs.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["radeonsi_no_infinite_interp"], + "values": {"on": "true"} } }, - 'radeonsi_clamp_div_by_zero': { - 'label': "RadeonSI Clamp Division by Zero:", - 'text': "Clamp division by zero results in RadeonSI. Prevents crashes or visual glitches from shader math errors on AMD GPUs.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['radeonsi_clamp_div_by_zero'], - 'values': {'on': 'true'} + "radeonsi_clamp_div_by_zero": { + "label": "RadeonSI Clamp Division by Zero:", + "text": "Clamp division by zero results in RadeonSI. Prevents crashes or visual glitches from shader math errors on AMD GPUs.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["radeonsi_clamp_div_by_zero"], + "values": {"on": "true"} } }, - 'radeonsi_zerovram': { - 'label': "RadeonSI Clear VRAM to Zero:", - 'text': "Clear all allocated VRAM to zero before usage in RadeonSI. Might fix rendering corruptions on AMD GPUs.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['radeonsi_zerovram'], - 'values': {'on': 'true'} + "radeonsi_zerovram": { + "label": "RadeonSI Clear VRAM to Zero:", + "text": "Clear all allocated VRAM to zero before usage in RadeonSI. Might fix rendering corruptions on AMD GPUs.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["radeonsi_zerovram"], + "values": {"on": "true"} } }, - 'radv_anisotropic_filtering': { - 'label': "RADV Anisotropic Filtering:", - 'text': "Anisotropic filtering level for RADV. Improves texture quality at oblique angles with minimal performance impact on modern AMD GPUs.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(0, 17)], - 'env_mapping': { - 'var_names': ['RADV_TEX_ANISO'], - 'direct_value': True + "radv_anisotropic_filtering": { + "label": "RADV Anisotropic Filtering:", + "text": "Anisotropic filtering level for RADV. Improves texture quality at oblique angles with minimal performance impact on modern AMD GPUs.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(0, 17)], + "env_mapping": { + "var_names": ["RADV_TEX_ANISO"], + "direct_value": True } }, - 'radv_profile_pstate': { - 'label': "RADV Profile Pstate:", - 'text': "Performance state profiling in RADV. Forces specific GPU clock levels for consistent performance or power testing on AMD GPUs.", - 'items': [ + "radv_profile_pstate": { + "label": "RADV Profile Pstate:", + "text": "Performance state profiling in RADV. Forces specific GPU clock levels for consistent performance or power testing on AMD GPUs.", + "items": [ "unset", "program decides (default)", "gpu clocks on arbitrary level", @@ -164,415 +164,415 @@ class GPULaunchManager: "minimum memory clock", "maximum gpu clocks" ], - 'env_mapping': { - 'var_names': ['RADV_PROFILE_PSTATE'], - 'values': { - 'gpu clocks on arbitrary level': 'standard', - 'minimum shader clock': 'min_sclk', - 'minimum memory clock': 'min_mclk', - 'maximum gpu clocks': 'peak' + "env_mapping": { + "var_names": ["RADV_PROFILE_PSTATE"], + "values": { + "gpu clocks on arbitrary level": "standard", + "minimum shader clock": "min_sclk", + "minimum memory clock": "min_mclk", + "maximum gpu clocks": "peak" } } }, - 'radv_vrs': { - 'label': "RADV Variable Rate Shading (GFX10.3+):", - 'text': "Variable rate shading in RADV (GFX10.3+). Renders different screen areas at different resolutions to improve performance with minimal quality loss.", - 'items': ["unset", "program decides (default)", "2x2", "1x2", "2x1", "1x1"], - 'env_mapping': { - 'var_names': ['RADV_FORCE_VRS'], - 'direct_value': True + "radv_vrs": { + "label": "RADV Variable Rate Shading (GFX10.3+):", + "text": "Variable rate shading in RADV (GFX10.3+). Renders different screen areas at different resolutions to improve performance with minimal quality loss.", + "items": ["unset", "program decides (default)", "2x2", "1x2", "2x1", "1x1"], + "env_mapping": { + "var_names": ["RADV_FORCE_VRS"], + "direct_value": True } }, - 'intel_precise_trig': { - 'label': "Intel Driver Preference on Trigonometric Functions:", - 'text': "Precision vs performance tradeoff for trigonometric functions on Intel GPUs. Accuracy mode ensures correct results; performance mode may have minor errors but runs faster.", - 'items': ["unset", "accuracy", "performance (default)"], - 'env_mapping': { - 'var_names': ['INTEL_PRECISE_TRIG'], - 'values': {'accuracy': 'true'} + "intel_precise_trig": { + "label": "Intel Driver Preference on Trigonometric Functions:", + "text": "Precision vs performance tradeoff for trigonometric functions on Intel GPUs. Accuracy mode ensures correct results; performance mode may have minor errors but runs faster.", + "items": ["unset", "accuracy", "performance (default)"], + "env_mapping": { + "var_names": ["INTEL_PRECISE_TRIG"], + "values": {"accuracy": "true"} } }, - 'hasvk_always_bindless': { - 'label': "HASVK Bindless Descriptors:", - 'text': "Bindless descriptors in HASVK. Modern descriptor management technique that can improve performance on Intel GPUs.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['HASVK_ALWAYS_BINDLESS'], - 'values': {'on': 'true'} + "hasvk_always_bindless": { + "label": "HASVK Bindless Descriptors:", + "text": "Bindless descriptors in HASVK. Modern descriptor management technique that can improve performance on Intel GPUs.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["HASVK_ALWAYS_BINDLESS"], + "values": {"on": "true"} } }, - 'hasvk_userspace_relocs': { - 'label': "HASVK Userspace Relocations:", - 'text': "Userspace relocations in HASVK. Handles GPU memory address patching in userspace instead of kernel for reduced overhead on older Intel GPUs.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['HASVK_USERSPACE_RELOCS'], - 'values': {'on': 'true'} + "hasvk_userspace_relocs": { + "label": "HASVK Userspace Relocations:", + "text": "Userspace relocations in HASVK. Handles GPU memory address patching in userspace instead of kernel for reduced overhead on older Intel GPUs.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["HASVK_USERSPACE_RELOCS"], + "values": {"on": "true"} } }, - 'anv_sparse': { - 'label': "ANV Sparse Resources (Tiger Lake+):", - 'text': "Sparse resources in ANV (Tiger Lake+). Allows partial allocation of large textures to save memory.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['ANV_SPARSE'], - 'values': {'off': 'false'} + "anv_sparse": { + "label": "ANV Sparse Resources (Tiger Lake+):", + "text": "Sparse resources in ANV (Tiger Lake+). Allows partial allocation of large textures to save memory.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["ANV_SPARSE"], + "values": {"off": "false"} } }, - 'anv_sparse_implementation': { - 'label': "ANV Sparse Implementation (Lunar Lake+):", - 'text': "Sparse resource implementation in ANV (Lunar Lake+). TRTT is the older method, Xe is the newer hardware-accelerated approach.", - 'items': ["unset", "TRTT", "Xe (default)"], - 'env_mapping': { - 'var_names': ['ANV_SPARSE_USE_TRTT'], - 'values': {'TRTT': 'true'} + "anv_sparse_implementation": { + "label": "ANV Sparse Implementation (Lunar Lake+):", + "text": "Sparse resource implementation in ANV (Lunar Lake+). TRTT is the older method, Xe is the newer hardware-accelerated approach.", + "items": ["unset", "TRTT", "Xe (default)"], + "env_mapping": { + "var_names": ["ANV_SPARSE_USE_TRTT"], + "values": {"TRTT": "true"} } }, - 'nvk_broken_driver': { - 'label': "NVK for Experimental/Untested GPUs:", - 'text': "Experimental NVK driver support for untested GPUs. Enables the open-source Vulkan driver on NVIDIA GPUs that lack official support; may be unstable.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['NVK_I_WANT_A_BROKEN_VULKAN_DRIVER'], - 'values': {'on': 'true'} + "nvk_broken_driver": { + "label": "NVK for Experimental/Untested GPUs:", + "text": "Experimental NVK driver support for untested GPUs. Enables the open-source Vulkan driver on NVIDIA GPUs that lack official support; may be unstable.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["NVK_I_WANT_A_BROKEN_VULKAN_DRIVER"], + "values": {"on": "true"} } } }, "NVIDIA": { - 'nvidia_gl_vsync': { - 'label': "OpenGL Vsync:", - 'text': "OpenGL vertical synchronization.", - 'items': ["unset", "program decides (default)", "on", "off"], - 'env_mapping': { - 'var_names': ['__GL_SYNC_TO_VBLANK'], - 'values': {'on': '1', 'off': '0'} + "nvidia_gl_vsync": { + "label": "OpenGL Vsync:", + "text": "OpenGL vertical synchronization.", + "items": ["unset", "program decides (default)", "on", "off"], + "env_mapping": { + "var_names": ["__GL_SYNC_TO_VBLANK"], + "values": {"on": "1", "off": "0"} } }, - 'nvidia_gl_gsync': { - 'label': "OpenGL G-SYNC:", - 'text': "OpenGL G-SYNC/Variable Refresh Rate (VRR). Adaptive sync, that eliminates tearing without the latency penalty of fixed vsync.", - 'items': ["unset", "program decides (default)", "on", "off"], - 'env_mapping': { - 'var_names': ['__GL_VRR_ALLOWED', '__GL_GSYNC_ALLOWED'], - 'values': {'on': '1', 'off': '0'} + "nvidia_gl_gsync": { + "label": "OpenGL G-SYNC:", + "text": "OpenGL G-SYNC/Variable Refresh Rate (VRR). Adaptive sync, that eliminates tearing without the latency penalty of fixed vsync.", + "items": ["unset", "program decides (default)", "on", "off"], + "env_mapping": { + "var_names": ["__GL_VRR_ALLOWED", "__GL_GSYNC_ALLOWED"], + "values": {"on": "1", "off": "0"} } }, - 'nvidia_gl_thread_opt': { - 'label': "OpenGL Thread Optimizations:", - 'text': "Multi-threaded OpenGL command processing. Might improve or worsen OpenGL performance depending on the program being run.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['__GL_THREADED_OPTIMIZATIONS'], - 'values': {'on': '1'} + "nvidia_gl_thread_opt": { + "label": "OpenGL Thread Optimizations:", + "text": "Multi-threaded OpenGL command processing. Might improve or worsen OpenGL performance depending on the program being run.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["__GL_THREADED_OPTIMIZATIONS"], + "values": {"on": "1"} } }, - 'nvidia_gl_yield': { - 'label': "OpenGL Yield Behavior:", - 'text': "NVIDIA driver yields CPU time during OpenGL operations. Controls how the driver waits for GPU, affecting CPU usage and responsiveness.", - 'items': ["unset", "call sched_yield() (default)", "never yield", "call usleep(0) to yield"], - 'env_mapping': { - 'var_names': ['__GL_YIELD'], - 'values': {'never yield': 'NOTHING', 'call usleep(0) to yield': 'USLEEP'} + "nvidia_gl_yield": { + "label": "OpenGL Yield Behavior:", + "text": "NVIDIA driver yields CPU time during OpenGL operations. Controls how the driver waits for GPU, affecting CPU usage and responsiveness.", + "items": ["unset", "call sched_yield() (default)", "never yield", "call usleep(0) to yield"], + "env_mapping": { + "var_names": ["__GL_YIELD"], + "values": {"never yield": "NOTHING", "call usleep(0) to yield": "USLEEP"} } }, - 'nvidia_gl_texture_quality': { - 'label': "OpenGL Texture Quality:", - 'text': "Texture quality vs performance tradeoff in OpenGL. Quality uses better filtering, performance uses faster methods with potential visual degradation.", - 'items': ["unset", "program decides (default)", "quality", "mixed", "performance"], - 'env_mapping': { - 'var_names': ['__GL_OpenGLImageSettings'], - 'values': {'quality': '1', 'mixed': '2', 'performance': '3'} + "nvidia_gl_texture_quality": { + "label": "OpenGL Texture Quality:", + "text": "Texture quality vs performance tradeoff in OpenGL. Quality uses better filtering, performance uses faster methods with potential visual degradation.", + "items": ["unset", "program decides (default)", "quality", "mixed", "performance"], + "env_mapping": { + "var_names": ["__GL_OpenGLImageSettings"], + "values": {"quality": "1", "mixed": "2", "performance": "3"} } }, - 'nvidia_gl_fsaa': { - 'label': "OpenGL Full Scene Antialiasing:", - 'text': "Full scene anti-aliasing level in OpenGL. Reduces jagged edges using multisampling (ms) and coverage sampling (cs/ss) with significant performance cost at higher levels.", - 'items': [ + "nvidia_gl_fsaa": { + "label": "OpenGL Full Scene Antialiasing:", + "text": "Full scene anti-aliasing level in OpenGL. Reduces jagged edges using multisampling (ms) and coverage sampling (cs/ss) with significant performance cost at higher levels.", + "items": [ "unset", "program decides (default)", "0 - off", "1 - 2x (2xms)", "5 - 4x (4xms)", "7 - 8x (4xms, 4xcs)", "8 - 16x (4xms, 12xcs)", "9 - 8x (4xss, 2xms)", "10 - 8x (8xms)", "11 - 16x (4xss, 4xms)", "12 - 16x (8xms, 8xcs)", "14 - 32x (8xms, 24xcs)" ], - 'env_mapping': { - 'var_names': ['__GL_FSAA_MODE'], - 'extract_prefix': True + "env_mapping": { + "var_names": ["__GL_FSAA_MODE"], + "extract_prefix": True } }, - 'nvidia_gl_fxaa': { - 'label': "OpenGL FXAA:", - 'text': "Fast approximate anti-aliasing in OpenGL. Post-process AA that smooths edges with minimal performance cost but may blur textures. FXAA must first be enabled in NVIDIA Control Panel.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['__GL_ALLOW_FXAA_USAGE'], - 'values': {'off': '0'} + "nvidia_gl_fxaa": { + "label": "OpenGL FXAA:", + "text": "Fast approximate anti-aliasing in OpenGL. Post-process AA that smooths edges with minimal performance cost but may blur textures. FXAA must first be enabled in NVIDIA Control Panel.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["__GL_ALLOW_FXAA_USAGE"], + "values": {"off": "0"} } }, - 'nvidia_gl_anisotropic_filtering': { - 'label': "OpenGL Anisotropic Filtering:", - 'text': "Anisotropic filtering level in OpenGL. Improves texture sharpness at oblique viewing angles; higher levels look better but impact performance.", - 'items': [ + "nvidia_gl_anisotropic_filtering": { + "label": "OpenGL Anisotropic Filtering:", + "text": "Anisotropic filtering level in OpenGL. Improves texture sharpness at oblique viewing angles; higher levels look better but impact performance.", + "items": [ "unset", "program decides (default)", "0 - no anisotropic filtering", "1 - 2x anisotropic filtering", "2 - 4x anisotropic filtering", "3 - 8x anisotropic filtering", "4 - 16x anisotropic filtering" ], - 'env_mapping': { - 'var_names': ['__GL_LOG_MAX_ANISO'], - 'extract_prefix': True + "env_mapping": { + "var_names": ["__GL_LOG_MAX_ANISO"], + "extract_prefix": True } }, - 'nvidia_smooth_motion': { - 'label': "Smooth Motion (RTX 40 Series+):", - 'text': "Smooth motion feature (RTX 40 Series+). Optical flow frame interpolation that generates intermediate frames for smoother motion. Vulkan only.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['NVPRESENT_ENABLE_SMOOTH_MOTION'], - 'values': {'on': '1'} + "nvidia_smooth_motion": { + "label": "Smooth Motion (RTX 40 Series+):", + "text": "Smooth motion feature (RTX 40 Series+). Optical flow frame interpolation that generates intermediate frames for smoother motion. Vulkan only.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["NVPRESENT_ENABLE_SMOOTH_MOTION"], + "values": {"on": "1"} } }, - 'nvidia_max_prerendered_frames': { - 'label': "Maximum Pre-rendered Frames:", - 'text': "Maximum number of pre-rendered frames. Lower values reduce input lag but may hurt frame rate consistency; higher values do the opposite.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(1, 5)], - 'env_mapping': { - 'var_names': ['__GL_MaxFramesAllowed'], - 'direct_value': True + "nvidia_max_prerendered_frames": { + "label": "Maximum Pre-rendered Frames:", + "text": "Maximum number of pre-rendered frames. Lower values reduce input lag but may hurt frame rate consistency; higher values do the opposite.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(1, 5)], + "env_mapping": { + "var_names": ["__GL_MaxFramesAllowed"], + "direct_value": True } }, - 'nvidia_sharpen_denoising_enable': { - 'label': "Enable NVIDIA Image Sharpening and Denoising:", - 'text': "Enable NVIDIA Image Sharpening and Denoising. Post-processing filters that enhance image clarity and reduce noise artifacts.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['__GL_SHARPEN_ENABLE'], - 'values': {'on': '1'} + "nvidia_sharpen_denoising_enable": { + "label": "Enable NVIDIA Image Sharpening and Denoising:", + "text": "Enable NVIDIA Image Sharpening and Denoising. Post-processing filters that enhance image clarity and reduce noise artifacts.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["__GL_SHARPEN_ENABLE"], + "values": {"on": "1"} } }, - 'nvidia_image_sharpening': { - 'label': "Image Sharpening:", - 'text': "Image sharpening. Enhances texture and edge definition; higher values increase sharpness but may introduce artifacts.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(0, 101)], - 'env_mapping': { - 'var_names': ['__GL_SHARPEN_VALUE'], - 'direct_value': True + "nvidia_image_sharpening": { + "label": "Image Sharpening:", + "text": "Image sharpening. Enhances texture and edge definition; higher values increase sharpness but may introduce artifacts.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(0, 101)], + "env_mapping": { + "var_names": ["__GL_SHARPEN_VALUE"], + "direct_value": True } }, - 'nvidia_image_denoising': { - 'label': "Image Denoising", - 'text': "Image denoising. Reduces film grain and noise; higher values preserve more texture detail but may keep more noise.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(0, 101)], - 'env_mapping': { - 'var_names': ['__GL_SHARPEN_IGNORE_FILM_GRAIN'], - 'direct_value': True + "nvidia_image_denoising": { + "label": "Image Denoising", + "text": "Image denoising. Reduces film grain and noise; higher values preserve more texture detail but may keep more noise.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(0, 101)], + "env_mapping": { + "var_names": ["__GL_SHARPEN_IGNORE_FILM_GRAIN"], + "direct_value": True } }, - 'nvidia_shader_cache': { - 'label': "Shader Cache:", - 'text': "Disk-based shader caching. Stores compiled shaders to disk to eliminate compilation stuttering on subsequent launches.", - 'items': ["unset", "on (default)", "off"], - 'env_mapping': { - 'var_names': ['__GL_SHADER_DISK_CACHE'], - 'values': {'off': '0'} + "nvidia_shader_cache": { + "label": "Shader Cache:", + "text": "Disk-based shader caching. Stores compiled shaders to disk to eliminate compilation stuttering on subsequent launches.", + "items": ["unset", "on (default)", "off"], + "env_mapping": { + "var_names": ["__GL_SHADER_DISK_CACHE"], + "values": {"off": "0"} } }, - 'nvidia_shader_cache_size': { - 'label': "Shader Cache Size (GB):", - 'text': "Maximum size for the shader cache. Larger caches store more compiled shaders but consume more disk space.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(1, 11)] + [str(i) for i in [25, 50, 75, 100]], - 'env_mapping': { - 'var_names': ['__GL_SHADER_DISK_CACHE_SIZE'], - 'convert_to_bytes': True + "nvidia_shader_cache_size": { + "label": "Shader Cache Size (GB):", + "text": "Maximum size for the shader cache. Larger caches store more compiled shaders but consume more disk space.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(1, 11)] + [str(i) for i in [25, 50, 75, 100]], + "env_mapping": { + "var_names": ["__GL_SHADER_DISK_CACHE_SIZE"], + "convert_to_bytes": True } }, - 'nvidia_glsl_ext_requirements': { - 'label': "Ignore GLSL Extensions Requirements:", - 'text': "Ignore GLSL extension requirements. Allows GLSL shaders to compile without proper #extension directives or compatibility profile declarations. Fixes shader compilation errors in some games.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['__GL_IGNORE_GLSL_EXT_REQS'], - 'values': {'on': '1'} + "nvidia_glsl_ext_requirements": { + "label": "Ignore GLSL Extensions Requirements:", + "text": "Ignore GLSL extension requirements. Allows GLSL shaders to compile without proper #extension directives or compatibility profile declarations. Fixes shader compilation errors in some games.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["__GL_IGNORE_GLSL_EXT_REQS"], + "values": {"on": "1"} } }, - 'nvidia_glx_unofficial_protocol': { - 'label': "Unofficial GLX Protocol:", - 'text': "Unofficial GLX protocol. Enables extensions that aren't part of the official GLX specification.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['__GL_ALLOW_UNOFFICIAL_PROTOCOL'], - 'values': {'on': '1'} + "nvidia_glx_unofficial_protocol": { + "label": "Unofficial GLX Protocol:", + "text": "Unofficial GLX protocol. Enables extensions that aren't part of the official GLX specification.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["__GL_ALLOW_UNOFFICIAL_PROTOCOL"], + "values": {"on": "1"} } }, - 'nvidia_experimental_perf': { - 'label': "Experimental Performance Strategy:", - 'text': "Experimental GPU clock boost management. Allows the driver to more aggressively reduce GPU clocks after boost periods, potentially reducing power consumption.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['__GL_ExperimentalPerfStrategy'], - 'values': {'on': '1'} + "nvidia_experimental_perf": { + "label": "Experimental Performance Strategy:", + "text": "Experimental GPU clock boost management. Allows the driver to more aggressively reduce GPU clocks after boost periods, potentially reducing power consumption.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["__GL_ExperimentalPerfStrategy"], + "values": {"on": "1"} } } }, "RenderSelector": { - 'render_gl_device': { - 'label': "Select OpenGL Device:", - 'text': "OpenGL device to use.", - 'items': ["unset", "program decides (default)"] + "render_gl_device": { + "label": "Select OpenGL Device:", + "text": "OpenGL device to use.", + "items": ["unset", "program decides (default)"] }, - 'render_vk_device': { - 'label': "Select Vulkan Device:", - 'text': "Vulkan device to use.", - 'items': ["unset", "program decides (default)"] + "render_vk_device": { + "label": "Select Vulkan Device:", + "text": "Vulkan device to use.", + "items": ["unset", "program decides (default)"] } }, "MangoHud": { - 'mangohud_enable': { - 'label': "Enable MangoHud:", - 'text': "Enable MangoHud.", - 'items': ["unset", "on", "off (default)"] + "mangohud_enable": { + "label": "Enable MangoHud:", + "text": "Enable MangoHud.", + "items": ["unset", "on", "off (default)"] }, - 'mangohud_display': { - 'label': "Display Elements:", - 'text': "Elements displayed in MangoHud overlay.", - 'items': ["unset", "program decides (default)", "no hud", "fps only", "horizontal", "extended", "detailed"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'no hud': '0', 'fps only': '1', 'horizontal': '2', 'extended': '3', 'detailed': '4'}, - 'prefix': 'preset=' + "mangohud_display": { + "label": "Display Elements:", + "text": "Elements displayed in MangoHud overlay.", + "items": ["unset", "program decides (default)", "no hud", "fps only", "horizontal", "extended", "detailed"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"no hud": "0", "fps only": "1", "horizontal": "2", "extended": "3", "detailed": "4"}, + "prefix": "preset=" } }, - 'mangohud_gl_vsync': { - 'label': "OpenGL Vsync:", - 'text': "OpenGL vertical synchronization mode when using MangoHud.", - 'items': ["unset", "program decides (default)", "adaptive vsync", "on", "off"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'adaptive vsync': '-1', 'on': '1', 'off': '0'}, - 'prefix': 'gl_vsync=' + "mangohud_gl_vsync": { + "label": "OpenGL Vsync:", + "text": "OpenGL vertical synchronization mode when using MangoHud.", + "items": ["unset", "program decides (default)", "adaptive vsync", "on", "off"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"adaptive vsync": "-1", "on": "1", "off": "0"}, + "prefix": "gl_vsync=" } }, - 'mangohud_vk_vsync': { - 'label': "Vulkan Vsync:", - 'text': "Vulkan vertical synchronization mode when using MangoHud.", - 'items': ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'mailbox': '2', 'adaptive vsync': '0', 'on': '3', 'off': '1'}, - 'prefix': 'vsync=' + "mangohud_vk_vsync": { + "label": "Vulkan Vsync:", + "text": "Vulkan vertical synchronization mode when using MangoHud.", + "items": ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"mailbox": "2", "adaptive vsync": "0", "on": "3", "off": "1"}, + "prefix": "vsync=" } }, - 'mangohud_fps_limit': { - 'label': "Fps Limit:", - 'text': "FPS limit when using MangoHud.", - 'items': ["unset", "program decides (default)", "unlimited", "10", "15", "20", "24", "25", "30", "35", "40", "45", "48", "50", "55", "60", "70", "72", "75", "85", "90", "100", "110", "120", "144", "165", "180", "200", "240", "280", "300", "360", "480"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'unlimited': '0'}, - 'direct_value': True, - 'prefix': 'fps_limit=' + "mangohud_fps_limit": { + "label": "Fps Limit:", + "text": "FPS limit when using MangoHud.", + "items": ["unset", "program decides (default)", "unlimited", "10", "15", "20", "24", "25", "30", "35", "40", "45", "48", "50", "55", "60", "70", "72", "75", "85", "90", "100", "110", "120", "144", "165", "180", "200", "240", "280", "300", "360", "480"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"unlimited": "0"}, + "direct_value": True, + "prefix": "fps_limit=" } }, - 'mangohud_fps_method': { - 'label': "Fps Limit Method:", - 'text': "MangoHud FPS limiting implementation.", - 'items': ["unset", "program decides (default)", "early - smoothest frametimes", "late - lowest latency"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'early - smoothest frametimes': 'early', 'late - lowest latency': 'late'}, - 'prefix': 'fps_limit_method=' + "mangohud_fps_method": { + "label": "Fps Limit Method:", + "text": "MangoHud FPS limiting implementation.", + "items": ["unset", "program decides (default)", "early - smoothest frametimes", "late - lowest latency"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"early - smoothest frametimes": "early", "late - lowest latency": "late"}, + "prefix": "fps_limit_method=" } }, - 'mangohud_texture_filter': { - 'label': "Texture Filtering:", - 'text': "Texture filtering method when using MangoHud. Vulkan Only.", - 'items': ["unset", "program decides (default)", "bicubic", "retro", "trilinear"], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'values': {'bicubic': 'bicubic', 'retro': 'retro', 'trilinear': 'trilinear'} + "mangohud_texture_filter": { + "label": "Texture Filtering:", + "text": "Texture filtering method when using MangoHud. Vulkan Only.", + "items": ["unset", "program decides (default)", "bicubic", "retro", "trilinear"], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "values": {"bicubic": "bicubic", "retro": "retro", "trilinear": "trilinear"} } }, - 'mangohud_mipmap_lod_bias': { - 'label': "Mipmap LOD Bias:", - 'text': "Mipmap level-of-detail bias when using MangoHud. Negative values sharpen textures, positive values blur them; affects performance and visual quality. Vulkan Only.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(-16, 17)], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'direct_value': True, - 'prefix': 'picmip=' + "mangohud_mipmap_lod_bias": { + "label": "Mipmap LOD Bias:", + "text": "Mipmap level-of-detail bias when using MangoHud. Negative values sharpen textures, positive values blur them; affects performance and visual quality. Vulkan Only.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(-16, 17)], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "direct_value": True, + "prefix": "picmip=" } }, - 'mangohud_anisotropic_filtering': { - 'label': "Anisotropic Filtering:", - 'text': "Anisotropic filtering level when using MangoHud. Improves texture quality at angles; 16x is maximum quality with minimal modern GPU impact. Vulkan Only.", - 'items': ["unset", "program decides (default)"] + [str(i) for i in range(0, 17)], - 'env_mapping': { - 'var_names': ['MANGOHUD_CONFIG'], - 'direct_value': True, - 'prefix': 'af=' + "mangohud_anisotropic_filtering": { + "label": "Anisotropic Filtering:", + "text": "Anisotropic filtering level when using MangoHud. Improves texture quality at angles; 16x is maximum quality with minimal modern GPU impact. Vulkan Only.", + "items": ["unset", "program decides (default)"] + [str(i) for i in range(0, 17)], + "env_mapping": { + "var_names": ["MANGOHUD_CONFIG"], + "direct_value": True, + "prefix": "af=" } } }, "LSFrameGen": { - 'lsfg_enable': { - 'label': "Enable LSFG-VK:", - 'text': "Enable LSFG-VK. Vulkan Only.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['LSFG_LEGACY'], - 'values': {'on': '1'} + "lsfg_enable": { + "label": "Enable LSFG-VK:", + "text": "Enable LSFG-VK. Vulkan Only.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["LSFG_LEGACY"], + "values": {"on": "1"} } }, - 'lsfg_dll_path': { - 'label': "Lossless.dll Path:", - 'text': "Path to the Lossless Scaling frame generation DLL file (Lossless.dll).", - 'path': True, - 'env_mapping': { - 'var_names': ['LSFG_DLL_PATH'], - 'direct_value': True + "lsfg_dll_path": { + "label": "Lossless.dll Path:", + "text": "Path to the Lossless Scaling frame generation DLL file (Lossless.dll).", + "path": True, + "env_mapping": { + "var_names": ["LSFG_DLL_PATH"], + "direct_value": True } }, - 'lsfg_multiplier': { - 'label': "FPS Multiplier:", - 'text': "Frame generation multiplier. Generates additional frames between real frames.", - 'items': ["unset", "program decides (default)", "2", "3", "4"], - 'env_mapping': { - 'var_names': ['LSFG_MULTIPLIER'], - 'direct_value': True + "lsfg_multiplier": { + "label": "FPS Multiplier:", + "text": "Frame generation multiplier. Generates additional frames between real frames.", + "items": ["unset", "program decides (default)", "2", "3", "4"], + "env_mapping": { + "var_names": ["LSFG_MULTIPLIER"], + "direct_value": True } }, - 'lsfg_flow_scale': { - 'label': "Motion Estimation Quality:", - 'text': "Motion estimation quality. Lower values improve performance at the cost of quality.", - 'items': ["unset", "program decides (default)", "0.25", "0.50", "0.75", "1.0"], - 'env_mapping': { - 'var_names': ['LSFG_FLOW_SCALE'], - 'direct_value': True + "lsfg_flow_scale": { + "label": "Motion Estimation Quality:", + "text": "Motion estimation quality. Lower values improve performance at the cost of quality.", + "items": ["unset", "program decides (default)", "0.25", "0.50", "0.75", "1.0"], + "env_mapping": { + "var_names": ["LSFG_FLOW_SCALE"], + "direct_value": True } }, - 'lsfg_performance_mode': { - 'label': "Performance Mode:", - 'text': "Performance mode which reduces quality for higher frame rates.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['LSFG_PERFORMANCE_MODE'], - 'values': {'on': '1'} + "lsfg_performance_mode": { + "label": "Performance Mode:", + "text": "Performance mode which reduces quality for higher frame rates.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["LSFG_PERFORMANCE_MODE"], + "values": {"on": "1"} } }, - 'lsfg_hdr_mode': { - 'label': "HDR Mode:", - 'text': "HDR support in frame generation.", - 'items': ["unset", "on", "off (default)"], - 'env_mapping': { - 'var_names': ['LSFG_HDR_MODE'], - 'values': {'on': '1'} + "lsfg_hdr_mode": { + "label": "HDR Mode:", + "text": "HDR support in frame generation.", + "items": ["unset", "on", "off (default)"], + "env_mapping": { + "var_names": ["LSFG_HDR_MODE"], + "values": {"on": "1"} } }, - 'lsfg_vk_present_mode': { - 'label': "Vulkan Vsync:", - 'text': "Vulkan vertical synchronization mode when using lsfg-vk.", - 'items': ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], - 'env_mapping': { - 'var_names': ['LSFG_EXPERIMENTAL_PRESENT_MODE'], - 'values': {'mailbox': 'mailbox', 'adaptive vsync': 'relaxed', 'on': 'fifo', 'off': 'immediate'} + "lsfg_vk_present_mode": { + "label": "Vulkan Vsync:", + "text": "Vulkan vertical synchronization mode when using lsfg-vk.", + "items": ["unset", "program decides (default)", "mailbox", "adaptive vsync", "on", "off"], + "env_mapping": { + "var_names": ["LSFG_EXPERIMENTAL_PRESENT_MODE"], + "values": {"mailbox": "mailbox", "adaptive vsync": "relaxed", "on": "fifo", "off": "immediate"} } } } @@ -587,10 +587,10 @@ def truncate_name(name): """ Truncate device name at slash or parenthesis. """ - if '/' in name: - return name.split('/')[0].strip() - if '(' in name: - return name.split('(')[0].strip() + if "/" in name: + return name.split("/")[0].strip() + if "(" in name: + return name.split("(")[0].strip() return name @staticmethod @@ -691,9 +691,9 @@ def get_opengl_device_options(): stdout = process.readAllStandardOutput().data().decode() stderr = process.readAllStandardError().data().decode() output = stdout + stderr - for line in output.split('\n'): + for line in output.split("\n"): if "OpenGL renderer string:" in line: - device_name = line.split(':', 1)[1].strip() + device_name = line.split(":", 1)[1].strip() device_name = GPULaunchManager.truncate_name(device_name) device_name = device_name.lower() @@ -721,9 +721,9 @@ def get_opengl_device_options(): output = stdout + stderr renderer_found = False - for line in output.split('\n'): + for line in output.split("\n"): if "OpenGL renderer string:" in line: - device_name = line.split(':', 1)[1].strip() + device_name = line.split(":", 1)[1].strip() device_name = GPULaunchManager.truncate_name(device_name) device_name = device_name.lower() @@ -784,27 +784,27 @@ def get_vulkan_device_options(): stdout = process.readAllStandardOutput().data().decode() stderr = process.readAllStandardError().data().decode() output = stdout + stderr - lines = output.split('\n') + lines = output.split("\n") current_device = {} for line in lines: line = line.strip() - if 'vendorID' in line and '=' in line: - vendor_id = line.split('=')[1].strip() - current_device['vendorID'] = vendor_id - elif 'deviceID' in line and '=' in line: - device_id = line.split('=')[1].strip() - current_device['deviceID'] = device_id - elif 'deviceName' in line and '=' in line: - device_name = line.split('=')[1].strip() - current_device['deviceName'] = device_name - - if all(key in current_device for key in ['vendorID', 'deviceID', 'deviceName']): - truncated_name = GPULaunchManager.truncate_name(current_device['deviceName']) + if "vendorID" in line and "=" in line: + vendor_id = line.split("=")[1].strip() + current_device["vendorID"] = vendor_id + elif "deviceID" in line and "=" in line: + device_id = line.split("=")[1].strip() + current_device["deviceID"] = device_id + elif "deviceName" in line and "=" in line: + device_name = line.split("=")[1].strip() + current_device["deviceName"] = device_name + + if all(key in current_device for key in ["vendorID", "deviceID", "deviceName"]): + truncated_name = GPULaunchManager.truncate_name(current_device["deviceName"]) display_name = truncated_name.lower() - if 'llvmpipe' in display_name: - display_name = 'llvmpipe (software rendering)' + if "llvmpipe" in display_name: + display_name = "llvmpipe (software rendering)" device_key = f"{current_device['vendorID']}:{current_device['deviceID']}" devices.append(display_name) @@ -841,11 +841,11 @@ def create_gpu_settings_tabs(): gpu_layout.addWidget(gpu_subtabs) widgets = { - 'Mesa': mesa_widgets, - 'NVIDIA': nvidia_widgets, - 'RenderSelector': render_selector_widgets, - 'MangoHud': mangohud_widgets, - 'LSFrameGen': ls_frame_gen_widgets + "Mesa": mesa_widgets, + "NVIDIA": nvidia_widgets, + "RenderSelector": render_selector_widgets, + "MangoHud": mangohud_widgets, + "LSFrameGen": ls_frame_gen_widgets } return gpu_tab, widgets @@ -863,7 +863,7 @@ def create_path_widget(setting_info): path_input = QLineEdit() path_input.setPlaceholderText("No file selected") path_input.setReadOnly(True) - path_input.setToolTip(setting_info['text']) + path_input.setToolTip(setting_info["text"]) layout.addWidget(path_input) button_layout = QHBoxLayout() @@ -919,11 +919,11 @@ def create_category_tab(category_name): for setting_key, setting_info in GPULaunchManager.GPU_SETTINGS_CATEGORIES[category_name].items(): layout = QHBoxLayout() - label = QLabel(setting_info['label']) + label = QLabel(setting_info["label"]) label.setWordWrap(True) label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) - if setting_info.get('path', False): + if setting_info.get("path", False): path_widget, path_input, browse_button, clear_button = GPULaunchManager.create_path_widget(setting_info) widgets[setting_key] = path_input widgets[f"{setting_key}_browse"] = browse_button @@ -932,10 +932,10 @@ def create_category_tab(category_name): layout.addWidget(path_widget) else: widgets[setting_key] = QComboBox() - widgets[setting_key].addItems(setting_info['items']) + widgets[setting_key].addItems(setting_info["items"]) widgets[setting_key].setCurrentText("unset") widgets[setting_key].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - widgets[setting_key].setToolTip(setting_info['text']) + widgets[setting_key].setToolTip(setting_info["text"]) layout.addWidget(label) layout.addWidget(widgets[setting_key]) @@ -944,37 +944,37 @@ def create_category_tab(category_name): if category_name == "RenderSelector": if GPULaunchManager.get_available_glxinfo(): opengl_devices, device_map = GPULaunchManager.get_opengl_device_options() - default_items = GPULaunchManager.GPU_SETTINGS_CATEGORIES["RenderSelector"]['render_gl_device']['items'] + default_items = GPULaunchManager.GPU_SETTINGS_CATEGORIES["RenderSelector"]["render_gl_device"]["items"] opengl_options = default_items + opengl_devices - widgets['render_gl_device'].clear() - widgets['render_gl_device'].addItems(opengl_options) - widgets['render_gl_device'].device_map = device_map + widgets["render_gl_device"].clear() + widgets["render_gl_device"].addItems(opengl_options) + widgets["render_gl_device"].device_map = device_map else: - widgets['render_gl_device'].setEnabled(False) - widgets['render_gl_device'].setToolTip("glxinfo not available - OpenGL device selection disabled") + widgets["render_gl_device"].setEnabled(False) + widgets["render_gl_device"].setToolTip("glxinfo not available - OpenGL device selection disabled") if GPULaunchManager.get_available_vulkaninfo(): vulkan_devices, device_map = GPULaunchManager.get_vulkan_device_options() - default_items = GPULaunchManager.GPU_SETTINGS_CATEGORIES["RenderSelector"]['render_vk_device']['items'] + default_items = GPULaunchManager.GPU_SETTINGS_CATEGORIES["RenderSelector"]["render_vk_device"]["items"] vulkan_options = default_items + vulkan_devices - widgets['render_vk_device'].clear() - widgets['render_vk_device'].addItems(vulkan_options) - widgets['render_vk_device'].device_map = device_map + widgets["render_vk_device"].clear() + widgets["render_vk_device"].addItems(vulkan_options) + widgets["render_vk_device"].device_map = device_map else: - widgets['render_vk_device'].setEnabled(False) - widgets['render_vk_device'].setToolTip("vulkaninfo not available - Vulkan device selection disabled") + widgets["render_vk_device"].setEnabled(False) + widgets["render_vk_device"].setToolTip("vulkaninfo not available - Vulkan device selection disabled") if category_name == "MangoHud": if not GPULaunchManager.get_available_mangohud(): for widget_key, widget in widgets.items(): - if hasattr(widget, 'setEnabled'): + if hasattr(widget, "setEnabled"): widget.setEnabled(False) widget.setToolTip("MangoHUD not available - MangoHUD options disabled") if category_name == "LSFrameGen": if not GPULaunchManager.get_available_lsfg(): for widget_key, widget in widgets.items(): - if hasattr(widget, 'setEnabled'): + if hasattr(widget, "setEnabled"): widget.setEnabled(False) widget.setToolTip("lsfg-vk not available - lsfg-vk options disabled") elif isinstance(widget, QLineEdit): @@ -1035,7 +1035,7 @@ def create_launch_options_tab(): scroll_area.setWidget(scroll_widget) main_layout.addWidget(scroll_area) - widgets = {'launch_options_input': launch_options_input} + widgets = {"launch_options_input": launch_options_input} GPULaunchManager.create_launch_apply_button(main_layout, widgets) @@ -1071,12 +1071,12 @@ def create_launch_apply_button(layout, widgets): button_layout = QHBoxLayout(button_container) button_layout.setContentsMargins(11, 10, 11, 0) - widgets['launch_apply_button'] = QPushButton("Apply") - widgets['launch_apply_button'].setMinimumSize(100, 30) - widgets['launch_apply_button'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + widgets["launch_apply_button"] = QPushButton("Apply") + widgets["launch_apply_button"].setMinimumSize(100, 30) + widgets["launch_apply_button"].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) button_layout.addStretch(1) - button_layout.addWidget(widgets['launch_apply_button']) + button_layout.addWidget(widgets["launch_apply_button"]) button_layout.addStretch(1) layout.addWidget(button_container) layout.addSpacing(9) @@ -1095,15 +1095,15 @@ def generate_env_vars(widgets, category_name): has_default_settings = False for setting_key, widget in widgets.items(): - if setting_key.endswith('_apply_button') or setting_key.endswith('_browse') or setting_key.endswith('_clear'): + if setting_key.endswith("_apply_button") or setting_key.endswith("_browse") or setting_key.endswith("_clear"): continue - if setting_key == 'mangohud_enable': + if setting_key == "mangohud_enable": continue setting_info = GPULaunchManager.GPU_SETTINGS_CATEGORIES[category_name][setting_key] - if setting_info.get('path', False): + if setting_info.get("path", False): value = widget.text().strip() if not value: continue @@ -1117,37 +1117,37 @@ def generate_env_vars(widgets, category_name): else: has_non_default_settings = True - if 'env_mapping' not in setting_info: + if "env_mapping" not in setting_info: continue - mapping = setting_info['env_mapping'] + mapping = setting_info["env_mapping"] - if mapping.get('direct_value', False): - prefix = mapping.get('prefix', '') - if value == 'unlimited': - mapped_value = mapping['values'].get(value, '0') - mangohud_parts.append(f'{prefix}{mapped_value}') + if mapping.get("direct_value", False): + prefix = mapping.get("prefix", "") + if value == "unlimited": + mapped_value = mapping["values"].get(value, "0") + mangohud_parts.append(f"{prefix}{mapped_value}") else: - mangohud_parts.append(f'{prefix}{value}') - elif 'values' in mapping: - mapped_value = mapping['values'].get(value) + mangohud_parts.append(f"{prefix}{value}") + elif "values" in mapping: + mapped_value = mapping["values"].get(value) if mapped_value: - prefix = mapping.get('prefix', '') - mangohud_parts.append(f'{prefix}{mapped_value}') + prefix = mapping.get("prefix", "") + mangohud_parts.append(f"{prefix}{mapped_value}") if mangohud_parts and has_non_default_settings: - config_value = ','.join(mangohud_parts) - env_vars.append(f'MANGOHUD_CONFIG={config_value}') + config_value = ",".join(mangohud_parts) + env_vars.append(f"MANGOHUD_CONFIG={config_value}") elif has_default_settings and not has_non_default_settings: - unset_vars.append('MANGOHUD_CONFIG') + unset_vars.append("MANGOHUD_CONFIG") else: for setting_key, widget in widgets.items(): - if setting_key.endswith('_apply_button') or setting_key.endswith('_browse') or setting_key.endswith('_clear'): + if setting_key.endswith("_apply_button") or setting_key.endswith("_browse") or setting_key.endswith("_clear"): continue setting_info = GPULaunchManager.GPU_SETTINGS_CATEGORIES[category_name][setting_key] - if setting_info.get('path', False): + if setting_info.get("path", False): value = widget.text().strip() if not value: continue @@ -1155,33 +1155,33 @@ def generate_env_vars(widgets, category_name): value = widget.currentText() if value == "unset": continue - elif "(default)" in value and 'env_mapping' in setting_info: - mapping = setting_info['env_mapping'] - unset_vars.extend(mapping['var_names']) + elif "(default)" in value and "env_mapping" in setting_info: + mapping = setting_info["env_mapping"] + unset_vars.extend(mapping["var_names"]) continue - if 'env_mapping' not in setting_info: + if "env_mapping" not in setting_info: continue - mapping = setting_info['env_mapping'] - var_names = mapping['var_names'] + mapping = setting_info["env_mapping"] + var_names = mapping["var_names"] - if mapping.get('direct_value', False): + if mapping.get("direct_value", False): final_value = value - elif mapping.get('extract_prefix', False): - final_value = value.split(' - ')[0] - elif mapping.get('convert_to_bytes', False): + elif mapping.get("extract_prefix", False): + final_value = value.split(" - ")[0] + elif mapping.get("convert_to_bytes", False): final_value = str(int(value) * 1073741824) - elif 'values' in mapping: - mapped_value = mapping['values'].get(value) - prefix = mapping.get('prefix', '') + elif "values" in mapping: + mapped_value = mapping["values"].get(value) + prefix = mapping.get("prefix", "") final_value = f"{prefix}{mapped_value}" if mapped_value else None else: final_value = None if final_value is not None: for var_name in var_names: - env_vars.append(f'{var_name}={final_value}') + env_vars.append(f"{var_name}={final_value}") return env_vars, unset_vars @@ -1193,22 +1193,22 @@ def generate_render_selector_env_vars(render_widgets): env_vars = [] unset_vars = [] - if 'render_gl_device' in render_widgets: - selected = render_widgets['render_gl_device'].currentText() + if "render_gl_device" in render_widgets: + selected = render_widgets["render_gl_device"].currentText() if "(default)" in selected: unset_vars.extend(["__GLX_VENDOR_LIBRARY_NAME", "LIBGL_ALWAYS_SOFTWARE", "MESA_LOADER_DRIVER_OVERRIDE", "LIBGL_KOPPER_DRI2", "DRI_PRIME"]) elif selected != "unset": - device_map = getattr(render_widgets['render_gl_device'], 'device_map', {}) + device_map = getattr(render_widgets["render_gl_device"], "device_map", {}) env_dict = device_map.get(selected, {}) for var, value in env_dict.items(): env_vars.append(f"{var}={value}") - if 'render_vk_device' in render_widgets: - vulkan_selection = render_widgets['render_vk_device'].currentText() + if "render_vk_device" in render_widgets: + vulkan_selection = render_widgets["render_vk_device"].currentText() if "(default)" in vulkan_selection: unset_vars.extend(["MESA_VK_DEVICE_SELECT", "VK_DRIVER_FILES", "VK_ICD_FILENAMES"]) elif vulkan_selection != "unset": - device_map = getattr(render_widgets['render_vk_device'], 'device_map', {}) + device_map = getattr(render_widgets["render_vk_device"], "device_map", {}) device_key = device_map.get(vulkan_selection) if device_key: env_vars.append(f"MESA_VK_DEVICE_SELECT={device_key}!") @@ -1227,10 +1227,10 @@ def write_settings_file(mesa_widgets, nvidia_widgets, render_selector_widgets, m ls_frame_gen_env_vars, lsfg_unset = GPULaunchManager.generate_env_vars(ls_frame_gen_widgets, "LSFrameGen") launch_options = "" - if 'launch_options_input' in launch_options_widgets: - launch_options = launch_options_widgets['launch_options_input'].text().strip() + if "launch_options_input" in launch_options_widgets: + launch_options = launch_options_widgets["launch_options_input"].text().strip() - mangohud_enabled = ('mangohud_enable' in mangohud_widgets and mangohud_widgets['mangohud_enable'].currentText() == "on") + mangohud_enabled = ("mangohud_enable" in mangohud_widgets and mangohud_widgets["mangohud_enable"].currentText() == "on") if mangohud_enabled and launch_options: launch_options = f"mangohud {launch_options}" @@ -1240,7 +1240,7 @@ def write_settings_file(mesa_widgets, nvidia_widgets, render_selector_widgets, m all_env_vars = mesa_env_vars + nvidia_env_vars + render_env_vars + mangohud_env_vars + ls_frame_gen_env_vars all_unset_vars = mesa_unset + nvidia_unset + render_unset + mangohud_unset + lsfg_unset - with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.conf') as temp_file: + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".conf") as temp_file: for env_var in all_env_vars: temp_file.write(f"{env_var}\n") diff --git a/src/kernel.py b/src/kernel.py index 36306f0..0360b02 100644 --- a/src/kernel.py +++ b/src/kernel.py @@ -6,315 +6,315 @@ class KernelManager: KERNEL_SETTINGS_CATEGORIES = { "CPU": { - 'sched_cfs_bandwidth_slice_us': { - 'path': '/proc/sys/kernel/sched_cfs_bandwidth_slice_us', - 'text': 'CFS bandwidth slice duration (microseconds). Higher values reduce scheduler overhead for CPU-bound applications.\nRecommended: 4000', - 'is_dynamic': False - }, - 'sched_autogroup_enabled': { - 'path': '/proc/sys/kernel/sched_autogroup_enabled', - 'text': 'Automatic process grouping for desktop responsiveness. Helps prioritize foreground applications.\nRecommended: 1 or 0 to use nice', - 'is_dynamic': False - }, - 'sched_rt_runtime_us': { - 'path': '/proc/sys/kernel/sched_rt_runtime_us', - 'text': 'Maximum CPU time (microseconds) for realtime tasks per period. -1 allows unlimited usage.\nRecommended: 950000 or -1', - 'is_dynamic': False - }, - 'sched_rt_period_us': { - 'path': '/proc/sys/kernel/sched_rt_period_us', - 'text': 'Period over which realtime task CPU usage is measured (microseconds). Works with sched_rt_runtime_us.\nRecommended: 1000000 (1 second, default)', - 'is_dynamic': False - }, - 'sched_schedstats': { - 'path': '/proc/sys/kernel/sched_schedstats', - 'text': 'Scheduler statistics collection. Disable to eliminate tracing overhead.\nRecommended: 0', - 'is_dynamic': False - }, - 'timer_migration': { - 'path': '/proc/sys/kernel/timer_migration', - 'text': 'Allows timer interrupts to migrate between CPUs. Disabling reduces latency at cost of power efficiency.\nRecommended: 0', - 'is_dynamic': False - }, - 'numa_balancing': { - 'path': '/proc/sys/kernel/numa_balancing', - 'text': 'Automatic NUMA memory migration. Creates overhead without significant benefits for most workloads.\nRecommended: 0', - 'is_dynamic': False + "sched_cfs_bandwidth_slice_us": { + "path": "/proc/sys/kernel/sched_cfs_bandwidth_slice_us", + "text": "CFS bandwidth slice duration (microseconds). Higher values reduce scheduler overhead for CPU-bound applications.\nRecommended: 4000", + "is_dynamic": False + }, + "sched_autogroup_enabled": { + "path": "/proc/sys/kernel/sched_autogroup_enabled", + "text": "Automatic process grouping for desktop responsiveness. Helps prioritize foreground applications.\nRecommended: 1 or 0 to use nice", + "is_dynamic": False + }, + "sched_rt_runtime_us": { + "path": "/proc/sys/kernel/sched_rt_runtime_us", + "text": "Maximum CPU time (microseconds) for realtime tasks per period. -1 allows unlimited usage.\nRecommended: 950000 or -1", + "is_dynamic": False + }, + "sched_rt_period_us": { + "path": "/proc/sys/kernel/sched_rt_period_us", + "text": "Period over which realtime task CPU usage is measured (microseconds). Works with sched_rt_runtime_us.\nRecommended: 1000000 (1 second, default)", + "is_dynamic": False + }, + "sched_schedstats": { + "path": "/proc/sys/kernel/sched_schedstats", + "text": "Scheduler statistics collection. Disable to eliminate tracing overhead.\nRecommended: 0", + "is_dynamic": False + }, + "timer_migration": { + "path": "/proc/sys/kernel/timer_migration", + "text": "Allows timer interrupts to migrate between CPUs. Disabling reduces latency at cost of power efficiency.\nRecommended: 0", + "is_dynamic": False + }, + "numa_balancing": { + "path": "/proc/sys/kernel/numa_balancing", + "text": "Automatic NUMA memory migration. Creates overhead without significant benefits for most workloads.\nRecommended: 0", + "is_dynamic": False } }, "Memory": { - 'compaction_proactiveness': { - 'path': '/proc/sys/vm/compaction_proactiveness', - 'text': 'Controls memory compaction aggressiveness (0-100). Lower values reduce CPU overhead during intensive workloads.\nRecommended: 0', - 'is_dynamic': False - }, - 'watermark_boost_factor': { - 'path': '/proc/sys/vm/watermark_boost_factor', - 'text': 'Memory reclaim aggressiveness during fragmentation (units of 10,000). Lower values prevent background reclaim during high-load scenarios.\nRecommended: 0', - 'is_dynamic': False - }, - 'watermark_scale_factor': { - 'path': '/proc/sys/vm/watermark_scale_factor', - 'text': 'Controls kswapd aggressiveness (units of 10,000). Higher values mean more free memory maintained. Default is 10 (0.1% of memory).\nRecommended: 10 (default) or higher for latency-sensitive workloads', - 'is_dynamic': False - }, - 'extfrag_threshold': { - 'path': '/proc/sys/vm/extfrag_threshold', - 'text': 'External fragmentation threshold that triggers compaction (0-1000). Higher values reduce compaction overhead.\nRecommended: 500', - 'is_dynamic': False - }, - 'compact_unevictable_allowed': { - 'path': '/proc/sys/vm/compact_unevictable_allowed', - 'text': 'Allow compaction to examine unevictable (mlocked) pages. May cause minor page faults but improves compaction effectiveness.\nRecommended: 1 (default), 0 for RT systems', - 'is_dynamic': False - }, - 'defrag_mode': { - 'path': '/proc/sys/vm/defrag_mode', - 'text': 'Proactive fragmentation prevention for hugepage allocations. Reduces long-term fragmentation at cost of immediate overhead.\nRecommended: 0(less overhead), 1 (for long-running systems)', - 'is_dynamic': False - }, - 'swappiness': { - 'path': '/proc/sys/vm/swappiness', - 'text': 'Kernel preference for swap vs RAM reclaim (0-200). Lower values prioritize keeping data in RAM for latency-sensitive workloads.\nRecommended: 10 (16GB+ RAM), 30-60 (8GB RAM). If using zram or zswap, higher values (60-120) are recommended to take advantage of compressed swap benefits.', - 'is_dynamic': False - }, - 'page-cluster': { - 'path': '/proc/sys/vm/page-cluster', - 'text': 'Number of pages to read/write together during swap operations as log2. Set to 0 for SSD-based systems.\nRecommended: 0', - 'is_dynamic': False - }, - 'vfs_cache_pressure': { - 'path': '/proc/sys/vm/vfs_cache_pressure', - 'text': 'Tenderness to reclaim filesystem caches relative to pagecache/swap. Lower values improve asset loading performance by keeping metadata cached.\nRecommended: 50', - 'is_dynamic': False - }, - 'min_free_kbytes': { - 'path': '/proc/sys/vm/min_free_kbytes', - 'text': 'Minimum reserved memory. Do not set below 1024 KB or above 5% of system memory.\nRecommended values: 1024-...', - 'is_dynamic': False - }, - 'overcommit_memory': { - 'path': '/proc/sys/vm/overcommit_memory', - 'text': 'Memory overcommit policy: 0=heuristic, 1=always, 2=strict. Mode 1 maximizes available memory.\nRecommended: 1', - 'is_dynamic': False - }, - 'overcommit_ratio': { - 'path': '/proc/sys/vm/overcommit_ratio', - 'text': 'Percentage of physical RAM (plus swap) available when overcommit_memory=2.\nRecommended: 60', - 'is_dynamic': False - }, - 'admin_reserve_kbytes': { - 'path': '/proc/sys/vm/admin_reserve_kbytes', - 'text': 'Memory reserved for root processes during OOM conditions. Default: min(3% of RAM, 8MB).\nRecommended: 8192 (8GB+ RAM), 4096 (4GB RAM)', - 'is_dynamic': False - }, - 'user_reserve_kbytes': { - 'path': '/proc/sys/vm/user_reserve_kbytes', - 'text': 'Memory reserved for user processes when overcommit_memory=2. Default: min(3% of process size, 128MB).\nRecommended: 131072', - 'is_dynamic': False - }, - 'max_map_count': { - 'path': '/proc/sys/vm/max_map_count', - 'text': 'Maximum memory mappings per process. Essential for applications using many shared libraries and memory-mapped files.\nRecommended: 2147483642 (SteamDeck Value) or 1048576 (Arch Linux)', - 'is_dynamic': False - }, - 'page_lock_unfairness': { - 'path': '/proc/sys/vm/page_lock_unfairness', - 'text': 'Number of times page lock can be stolen from waiter before fair handoff. Higher values favor readers which can improve read performance for asset streaming workloads.\nRecommended: 5', - 'is_dynamic': False - }, - 'percpu_pagelist_high_fraction': { - 'path': '/proc/sys/vm/percpu_pagelist_high_fraction', - 'text': 'Per-CPU page list size as fraction of zone size (0=default kernel algorithm, min value is 8). Higher values reduce contention on multi-core systems.\nRecommended: 8+ or 0 (reverts to the default behavior)', - 'is_dynamic': False - }, - 'zone_reclaim_mode': { - 'path': '/proc/sys/vm/zone_reclaim_mode', - 'text': 'NUMA memory reclaim behavior (bitmask: 0=reclaim off 1=reclaim on, 2=write dirty pages, 4=swap pages). Usually degrades performance due to unnecessary reclaim overhead.\nRecommended: 0', - 'is_dynamic': False - }, - 'min_unmapped_ratio': { - 'path': '/proc/sys/vm/min_unmapped_ratio', - 'text': 'Minimum percentage of unmapped pages before zone reclaim (NUMA only). Higher values delay local reclaim, improving cache locality.\nRecommended: 1', - 'is_dynamic': False - }, - 'min_slab_ratio': { - 'path': '/proc/sys/vm/min_slab_ratio', - 'text': 'Percentage of zone pages that must be reclaimable slab before zone reclaim (NUMA only). Higher values delay expensive remote allocation.\nRecommended: 5 (default), 3-8 range for tuning', - 'is_dynamic': False - }, - 'numa_stat': { - 'path': '/proc/sys/vm/numa_stat', - 'text': 'Enable NUMA statistics collection. Disabling reduces allocation overhead but breaks monitoring tools.\nRecommended: 0 (performance), 1 (monitoring/debugging)', - 'is_dynamic': False - }, - 'nr_hugepages': { - 'path': '/proc/sys/vm/nr_hugepages', - 'text': 'Number of persistent hugepages allocated. Critical for applications requiring guaranteed hugepage memory (databases, VMs, HPC).\nRecommended: 0 (default), or calculated based on application needs', - 'is_dynamic': False - }, - 'nr_overcommit_hugepages': { - 'path': '/proc/sys/vm/nr_overcommit_hugepages', - 'text': 'Maximum number of additional hugepages that can be allocated dynamically beyond nr_hugepages.\nRecommended: 0 (conservative), or set based on peak demand', - 'is_dynamic': False - }, - 'hugetlb_optimize_vmemmap': { - 'path': '/proc/sys/vm/hugetlb_optimize_vmemmap', - 'text': 'Optimize hugepage metadata memory usage (saves ~7 pages per 2MB hugepage). May add overhead during allocation/deallocation.\nRecommended: 1 (enable for memory savings), 0 (disable for allocation speed)', - 'is_dynamic': False - }, - 'stat_interval': { - 'path': '/proc/sys/vm/stat_interval', - 'text': 'VM statistics update interval (seconds). Higher values reduce CPU overhead.\nRecommended: 10', - 'is_dynamic': False - }, - 'thp_enabled': { - 'path': '/sys/kernel/mm/transparent_hugepage/enabled', - 'text': 'Transparent Huge Pages reduce TLB pressure but may cause allocation stalls. "madvise" enables only where beneficial.\nRecommended: madvise', - 'is_dynamic': True - }, - 'thp_shmem_enabled': { - 'path': '/sys/kernel/mm/transparent_hugepage/shmem_enabled', - 'text': 'THP for shared memory segments. "advise" enables only when explicitly requested.\nRecommended: advise', - 'is_dynamic': True - }, - 'thp_defrag': { - 'path': '/sys/kernel/mm/transparent_hugepage/defrag', - 'text': 'THP defragmentation strategy. "defer" prevents allocation stalls during high-priority tasks.\nRecommended: defer', - 'is_dynamic': True + "compaction_proactiveness": { + "path": "/proc/sys/vm/compaction_proactiveness", + "text": "Controls memory compaction aggressiveness (0-100). Lower values reduce CPU overhead during intensive workloads.\nRecommended: 0", + "is_dynamic": False + }, + "watermark_boost_factor": { + "path": "/proc/sys/vm/watermark_boost_factor", + "text": "Memory reclaim aggressiveness during fragmentation (units of 10,000). Lower values prevent background reclaim during high-load scenarios.\nRecommended: 0", + "is_dynamic": False + }, + "watermark_scale_factor": { + "path": "/proc/sys/vm/watermark_scale_factor", + "text": "Controls kswapd aggressiveness (units of 10,000). Higher values mean more free memory maintained. Default is 10 (0.1% of memory).\nRecommended: 10 (default) or higher for latency-sensitive workloads", + "is_dynamic": False + }, + "extfrag_threshold": { + "path": "/proc/sys/vm/extfrag_threshold", + "text": "External fragmentation threshold that triggers compaction (0-1000). Higher values reduce compaction overhead.\nRecommended: 500", + "is_dynamic": False + }, + "compact_unevictable_allowed": { + "path": "/proc/sys/vm/compact_unevictable_allowed", + "text": "Allow compaction to examine unevictable (mlocked) pages. May cause minor page faults but improves compaction effectiveness.\nRecommended: 1 (default), 0 for RT systems", + "is_dynamic": False + }, + "defrag_mode": { + "path": "/proc/sys/vm/defrag_mode", + "text": "Proactive fragmentation prevention for hugepage allocations. Reduces long-term fragmentation at cost of immediate overhead.\nRecommended: 0(less overhead), 1 (for long-running systems)", + "is_dynamic": False + }, + "swappiness": { + "path": "/proc/sys/vm/swappiness", + "text": "Kernel preference for swap vs RAM reclaim (0-200). Lower values prioritize keeping data in RAM for latency-sensitive workloads.\nRecommended: 10 (16GB+ RAM), 30-60 (8GB RAM). If using zram or zswap, higher values (60-120) are recommended to take advantage of compressed swap benefits.", + "is_dynamic": False + }, + "page-cluster": { + "path": "/proc/sys/vm/page-cluster", + "text": "Number of pages to read/write together during swap operations as log2. Set to 0 for SSD-based systems.\nRecommended: 0", + "is_dynamic": False + }, + "vfs_cache_pressure": { + "path": "/proc/sys/vm/vfs_cache_pressure", + "text": "Tenderness to reclaim filesystem caches relative to pagecache/swap. Lower values improve asset loading performance by keeping metadata cached.\nRecommended: 50", + "is_dynamic": False + }, + "min_free_kbytes": { + "path": "/proc/sys/vm/min_free_kbytes", + "text": "Minimum reserved memory. Do not set below 1024 KB or above 5% of system memory.\nRecommended values: 1024-...", + "is_dynamic": False + }, + "overcommit_memory": { + "path": "/proc/sys/vm/overcommit_memory", + "text": "Memory overcommit policy: 0=heuristic, 1=always, 2=strict. Mode 1 maximizes available memory.\nRecommended: 1", + "is_dynamic": False + }, + "overcommit_ratio": { + "path": "/proc/sys/vm/overcommit_ratio", + "text": "Percentage of physical RAM (plus swap) available when overcommit_memory=2.\nRecommended: 60", + "is_dynamic": False + }, + "admin_reserve_kbytes": { + "path": "/proc/sys/vm/admin_reserve_kbytes", + "text": "Memory reserved for root processes during OOM conditions. Default: min(3% of RAM, 8MB).\nRecommended: 8192 (8GB+ RAM), 4096 (4GB RAM)", + "is_dynamic": False + }, + "user_reserve_kbytes": { + "path": "/proc/sys/vm/user_reserve_kbytes", + "text": "Memory reserved for user processes when overcommit_memory=2. Default: min(3% of process size, 128MB).\nRecommended: 131072", + "is_dynamic": False + }, + "max_map_count": { + "path": "/proc/sys/vm/max_map_count", + "text": "Maximum memory mappings per process. Essential for applications using many shared libraries and memory-mapped files.\nRecommended: 2147483642 (SteamDeck Value) or 1048576 (Arch Linux)", + "is_dynamic": False + }, + "page_lock_unfairness": { + "path": "/proc/sys/vm/page_lock_unfairness", + "text": "Number of times page lock can be stolen from waiter before fair handoff. Higher values favor readers which can improve read performance for asset streaming workloads.\nRecommended: 5", + "is_dynamic": False + }, + "percpu_pagelist_high_fraction": { + "path": "/proc/sys/vm/percpu_pagelist_high_fraction", + "text": "Per-CPU page list size as fraction of zone size (0=default kernel algorithm, min value is 8). Higher values reduce contention on multi-core systems.\nRecommended: 8+ or 0 (reverts to the default behavior)", + "is_dynamic": False + }, + "zone_reclaim_mode": { + "path": "/proc/sys/vm/zone_reclaim_mode", + "text": "NUMA memory reclaim behavior (bitmask: 0=reclaim off 1=reclaim on, 2=write dirty pages, 4=swap pages). Usually degrades performance due to unnecessary reclaim overhead.\nRecommended: 0", + "is_dynamic": False + }, + "min_unmapped_ratio": { + "path": "/proc/sys/vm/min_unmapped_ratio", + "text": "Minimum percentage of unmapped pages before zone reclaim (NUMA only). Higher values delay local reclaim, improving cache locality.\nRecommended: 1", + "is_dynamic": False + }, + "min_slab_ratio": { + "path": "/proc/sys/vm/min_slab_ratio", + "text": "Percentage of zone pages that must be reclaimable slab before zone reclaim (NUMA only). Higher values delay expensive remote allocation.\nRecommended: 5 (default), 3-8 range for tuning", + "is_dynamic": False + }, + "numa_stat": { + "path": "/proc/sys/vm/numa_stat", + "text": "Enable NUMA statistics collection. Disabling reduces allocation overhead but breaks monitoring tools.\nRecommended: 0 (performance), 1 (monitoring/debugging)", + "is_dynamic": False + }, + "nr_hugepages": { + "path": "/proc/sys/vm/nr_hugepages", + "text": "Number of persistent hugepages allocated. Critical for applications requiring guaranteed hugepage memory (databases, VMs, HPC).\nRecommended: 0 (default), or calculated based on application needs", + "is_dynamic": False + }, + "nr_overcommit_hugepages": { + "path": "/proc/sys/vm/nr_overcommit_hugepages", + "text": "Maximum number of additional hugepages that can be allocated dynamically beyond nr_hugepages.\nRecommended: 0 (conservative), or set based on peak demand", + "is_dynamic": False + }, + "hugetlb_optimize_vmemmap": { + "path": "/proc/sys/vm/hugetlb_optimize_vmemmap", + "text": "Optimize hugepage metadata memory usage (saves ~7 pages per 2MB hugepage). May add overhead during allocation/deallocation.\nRecommended: 1 (enable for memory savings), 0 (disable for allocation speed)", + "is_dynamic": False + }, + "stat_interval": { + "path": "/proc/sys/vm/stat_interval", + "text": "VM statistics update interval (seconds). Higher values reduce CPU overhead.\nRecommended: 10", + "is_dynamic": False + }, + "thp_enabled": { + "path": "/sys/kernel/mm/transparent_hugepage/enabled", + "text": "Transparent Huge Pages reduce TLB pressure but may cause allocation stalls. 'madvise' enables only where beneficial.\nRecommended: madvise", + "is_dynamic": True + }, + "thp_shmem_enabled": { + "path": "/sys/kernel/mm/transparent_hugepage/shmem_enabled", + "text": "THP for shared memory segments. 'advise' enables only when explicitly requested.\nRecommended: advise", + "is_dynamic": True + }, + "thp_defrag": { + "path": "/sys/kernel/mm/transparent_hugepage/defrag", + "text": "THP defragmentation strategy. 'defer' prevents allocation stalls during high-priority tasks.\nRecommended: defer", + "is_dynamic": True } }, "Disk": { - 'dirty_ratio': { - 'path': '/proc/sys/vm/dirty_ratio', - 'text': 'Maximum percentage of available memory for dirty pages before synchronous writes. Lower values reduce I/O latency spikes. Mutually exclusive with dirty_bytes.\nRecommended: 10', - 'is_dynamic': False - }, - 'dirty_background_ratio': { - 'path': '/proc/sys/vm/dirty_background_ratio', - 'text': 'Percentage of available memory at which background writeback begins. Should be 1/3 of dirty_ratio. Mutually exclusive with dirty_background_bytes.\nRecommended: 3', - 'is_dynamic': False - }, - 'dirty_bytes': { - 'path': '/proc/sys/vm/dirty_bytes', - 'text': 'Absolute dirty memory limit (bytes). Provides consistent behavior regardless of RAM size. Mutually exclusive with dirty_ratio.\nRecommended: 67108864 (64MB)', - 'is_dynamic': False - }, - 'dirty_background_bytes': { - 'path': '/proc/sys/vm/dirty_background_bytes', - 'text': 'Absolute background writeback threshold (bytes). Should be 50% of dirty_bytes. Mutually exclusive with dirty_background_ratio.\nRecommended: 33554432 (32MB)', - 'is_dynamic': False - }, - 'dirty_expire_centisecs': { - 'path': '/proc/sys/vm/dirty_expire_centisecs', - 'text': 'Maximum time dirty data remains in memory (centiseconds). Shorter intervals improve responsiveness.\nRecommended: 3000', - 'is_dynamic': False - }, - 'dirty_writeback_centisecs': { - 'path': '/proc/sys/vm/dirty_writeback_centisecs', - 'text': 'Interval between periodic writeback wakeups (centiseconds). Longer intervals reduce CPU overhead.\nRecommended: 1500', - 'is_dynamic': False - }, - 'dirtytime_expire_seconds': { - 'path': '/proc/sys/vm/dirtytime_expire_seconds', - 'text': 'Interval for lazy timestamp updates on filesystems with dirtytime mount option (seconds).\nRecommended: 43200 (12 hours)', - 'is_dynamic': False - }, - 'laptop_mode': { - 'path': '/proc/sys/vm/laptop_mode', - 'text': 'Power-saving write delay mechanism. Disable for performance-oriented systems.\nRecommended: 0', - 'is_dynamic': False + "dirty_ratio": { + "path": "/proc/sys/vm/dirty_ratio", + "text": "Maximum percentage of available memory for dirty pages before synchronous writes. Lower values reduce I/O latency spikes. Mutually exclusive with dirty_bytes.\nRecommended: 10", + "is_dynamic": False + }, + "dirty_background_ratio": { + "path": "/proc/sys/vm/dirty_background_ratio", + "text": "Percentage of available memory at which background writeback begins. Should be 1/3 of dirty_ratio. Mutually exclusive with dirty_background_bytes.\nRecommended: 3", + "is_dynamic": False + }, + "dirty_bytes": { + "path": "/proc/sys/vm/dirty_bytes", + "text": "Absolute dirty memory limit (bytes). Provides consistent behavior regardless of RAM size. Mutually exclusive with dirty_ratio.\nRecommended: 67108864 (64MB)", + "is_dynamic": False + }, + "dirty_background_bytes": { + "path": "/proc/sys/vm/dirty_background_bytes", + "text": "Absolute background writeback threshold (bytes). Should be 50% of dirty_bytes. Mutually exclusive with dirty_background_ratio.\nRecommended: 33554432 (32MB)", + "is_dynamic": False + }, + "dirty_expire_centisecs": { + "path": "/proc/sys/vm/dirty_expire_centisecs", + "text": "Maximum time dirty data remains in memory (centiseconds). Shorter intervals improve responsiveness.\nRecommended: 3000", + "is_dynamic": False + }, + "dirty_writeback_centisecs": { + "path": "/proc/sys/vm/dirty_writeback_centisecs", + "text": "Interval between periodic writeback wakeups (centiseconds). Longer intervals reduce CPU overhead.\nRecommended: 1500", + "is_dynamic": False + }, + "dirtytime_expire_seconds": { + "path": "/proc/sys/vm/dirtytime_expire_seconds", + "text": "Interval for lazy timestamp updates on filesystems with dirtytime mount option (seconds).\nRecommended: 43200 (12 hours)", + "is_dynamic": False + }, + "laptop_mode": { + "path": "/proc/sys/vm/laptop_mode", + "text": "Power-saving write delay mechanism. Disable for performance-oriented systems.\nRecommended: 0", + "is_dynamic": False } }, "System": { - 'watchdog': { - 'path': '/proc/sys/kernel/watchdog', - 'text': 'Soft lockup detector. Disable to remove periodic checks that can cause stuttering.\nRecommended: 0', - 'is_dynamic': False - }, - 'nmi_watchdog': { - 'path': '/proc/sys/kernel/nmi_watchdog', - 'text': 'NMI-based hard lockup detection. Disables performance-counter based monitoring.\nRecommended: 0', - 'is_dynamic': False - }, - 'hung_task_timeout_secs': { - 'path': '/proc/sys/kernel/hung_task_timeout_secs', - 'text': 'Timeout for detecting hung tasks (seconds). 0 disables detection to prevent false positives during long sessions.\nRecommended: 0 or 120 (Default)', - 'is_dynamic': False - }, - 'pid_max': { - 'path': '/proc/sys/kernel/pid_max', - 'text': 'Maximum process ID value. Higher values support systems running many concurrent processes.\nRecommended: 4194304', - 'is_dynamic': False - }, - 'file_max': { - 'path': '/proc/sys/fs/file-max', - 'text': 'System-wide maximum open file descriptors. Essential for applications opening many files simultaneously.\nRecommended: 2097152', - 'is_dynamic': False - }, - 'oom_kill_allocating_task': { - 'path': '/proc/sys/vm/oom_kill_allocating_task', - 'text': 'OOM killer targets the task that triggered OOM instead of scanning all tasks. Improves recovery speed.\nRecommended: 1', - 'is_dynamic': False - }, - 'oom_dump_tasks': { - 'path': '/proc/sys/vm/oom_dump_tasks', - 'text': 'Enable task dump when OOM killer is invoked. Useful for debugging but adds overhead on large systems.\nRecommended: 0 (disable for performance), 1 (enable for debugging)', - 'is_dynamic': False - }, - 'panic_on_oom': { - 'path': '/proc/sys/vm/panic_on_oom', - 'text': 'System behavior on OOM: 0=kill process, 1=panic on system OOM, 2=always panic.\nRecommended: 0 (default behavior)', - 'is_dynamic': False - }, - 'max_user_freq': { - 'path': '/sys/class/rtc/rtc0/max_user_freq', - 'text': 'Maximum RTC interrupt frequency (Hz) for userspace. Higher values provide better timer precision.\nRecommended: 64', - 'is_dynamic': False + "watchdog": { + "path": "/proc/sys/kernel/watchdog", + "text": "Soft lockup detector. Disable to remove periodic checks that can cause stuttering.\nRecommended: 0", + "is_dynamic": False + }, + "nmi_watchdog": { + "path": "/proc/sys/kernel/nmi_watchdog", + "text": "NMI-based hard lockup detection. Disables performance-counter based monitoring.\nRecommended: 0", + "is_dynamic": False + }, + "hung_task_timeout_secs": { + "path": "/proc/sys/kernel/hung_task_timeout_secs", + "text": "Timeout for detecting hung tasks (seconds). 0 disables detection to prevent false positives during long sessions.\nRecommended: 0 or 120 (Default)", + "is_dynamic": False + }, + "pid_max": { + "path": "/proc/sys/kernel/pid_max", + "text": "Maximum process ID value. Higher values support systems running many concurrent processes.\nRecommended: 4194304", + "is_dynamic": False + }, + "file_max": { + "path": "/proc/sys/fs/file-max", + "text": "System-wide maximum open file descriptors. Essential for applications opening many files simultaneously.\nRecommended: 2097152", + "is_dynamic": False + }, + "oom_kill_allocating_task": { + "path": "/proc/sys/vm/oom_kill_allocating_task", + "text": "OOM killer targets the task that triggered OOM instead of scanning all tasks. Improves recovery speed.\nRecommended: 1", + "is_dynamic": False + }, + "oom_dump_tasks": { + "path": "/proc/sys/vm/oom_dump_tasks", + "text": "Enable task dump when OOM killer is invoked. Useful for debugging but adds overhead on large systems.\nRecommended: 0 (disable for performance), 1 (enable for debugging)", + "is_dynamic": False + }, + "panic_on_oom": { + "path": "/proc/sys/vm/panic_on_oom", + "text": "System behavior on OOM: 0=kill process, 1=panic on system OOM, 2=always panic.\nRecommended: 0 (default behavior)", + "is_dynamic": False + }, + "max_user_freq": { + "path": "/sys/class/rtc/rtc0/max_user_freq", + "text": "Maximum RTC interrupt frequency (Hz) for userspace. Higher values provide better timer precision.\nRecommended: 64", + "is_dynamic": False } }, "Network": { - 'core_rmem_max': { - 'path': '/proc/sys/net/core/rmem_max', - 'text': 'Maximum socket receive buffer size (bytes) per socket. Higher values improve throughput for high-bandwidth connections.\nRecommended: 268435456 (256MB)', - 'is_dynamic': False - }, - 'core_wmem_max': { - 'path': '/proc/sys/net/core/wmem_max', - 'text': 'Maximum socket send buffer size (bytes) per socket. Should match rmem_max for balanced performance.\nRecommended: 268435456 (256MB)', - 'is_dynamic': False - }, - 'tcp_fastopen': { - 'path': '/proc/sys/net/ipv4/tcp_fastopen', - 'text': 'TCP Fast Open reduces connection establishment latency. Bitmask: 1=client, 2=server.\nRecommended: 3 (enable for both incoming/outgoing)', - 'is_dynamic': False - }, - 'tcp_window_scaling': { - 'path': '/proc/sys/net/ipv4/tcp_window_scaling', - 'text': 'Enable TCP window scaling for high-bandwidth connections.\nRecommended: 1', - 'is_dynamic': False - }, - 'tcp_timestamps': { - 'path': '/proc/sys/net/ipv4/tcp_timestamps', - 'text': 'Enable TCP timestamps for RTT measurement and PAWS protection.\nRecommended: 1', - 'is_dynamic': False + "core_rmem_max": { + "path": "/proc/sys/net/core/rmem_max", + "text": "Maximum socket receive buffer size (bytes) per socket. Higher values improve throughput for high-bandwidth connections.\nRecommended: 268435456 (256MB)", + "is_dynamic": False + }, + "core_wmem_max": { + "path": "/proc/sys/net/core/wmem_max", + "text": "Maximum socket send buffer size (bytes) per socket. Should match rmem_max for balanced performance.\nRecommended: 268435456 (256MB)", + "is_dynamic": False + }, + "tcp_fastopen": { + "path": "/proc/sys/net/ipv4/tcp_fastopen", + "text": "TCP Fast Open reduces connection establishment latency. Bitmask: 1=client, 2=server.\nRecommended: 3 (enable for both incoming/outgoing)", + "is_dynamic": False + }, + "tcp_window_scaling": { + "path": "/proc/sys/net/ipv4/tcp_window_scaling", + "text": "Enable TCP window scaling for high-bandwidth connections.\nRecommended: 1", + "is_dynamic": False + }, + "tcp_timestamps": { + "path": "/proc/sys/net/ipv4/tcp_timestamps", + "text": "Enable TCP timestamps for RTT measurement and PAWS protection.\nRecommended: 1", + "is_dynamic": False } }, "Security": { - 'randomize_va_space': { - 'path': '/proc/sys/kernel/randomize_va_space', - 'text': 'Address space layout randomization: 0=disabled, 1=conservative, 2=full. Lower values reduce address translation overhead.\nRecommended: 0 (performance), 2 (security)', - 'is_dynamic': False - }, - 'perf_event_paranoid': { - 'path': '/proc/sys/kernel/perf_event_paranoid', - 'text': 'Performance monitoring access: -1=unrestricted, 0=user+kernel, 1=user only, 2=kernel only, 3=no access.\nRecommended: 2', - 'is_dynamic': False - }, - 'mmap_min_addr': { - 'path': '/proc/sys/vm/mmap_min_addr', - 'text': 'Minimum virtual address for mmap operations. Security feature with minimal performance impact.\nRecommended: 65536', - 'is_dynamic': False + "randomize_va_space": { + "path": "/proc/sys/kernel/randomize_va_space", + "text": "Address space layout randomization: 0=disabled, 1=conservative, 2=full. Lower values reduce address translation overhead.\nRecommended: 0 (performance), 2 (security)", + "is_dynamic": False + }, + "perf_event_paranoid": { + "path": "/proc/sys/kernel/perf_event_paranoid", + "text": "Performance monitoring access: -1=unrestricted, 0=user+kernel, 1=user only, 2=kernel only, 3=no access.\nRecommended: 2", + "is_dynamic": False + }, + "mmap_min_addr": { + "path": "/proc/sys/vm/mmap_min_addr", + "text": "Minimum virtual address for mmap operations. Security feature with minimal performance impact.\nRecommended: 65536", + "is_dynamic": False } } } @@ -329,7 +329,7 @@ def get_current_value(setting_path): Read and return current value from setting file """ try: - with open(setting_path, 'r') as f: + with open(setting_path, "r") as f: return f.read().strip() except Exception: return None @@ -340,10 +340,10 @@ def get_dynamic_current_value(setting_path): Extract current value from dynamic settings (e.g., [current] option1 option2) """ try: - with open(setting_path, 'r') as f: + with open(setting_path, "r") as f: content = f.read().strip() - match = re.search(r'\[([^\]]+)\]', content) + match = re.search(r"\[([^\]]+)\]", content) if match: return match.group(1) else: @@ -361,10 +361,10 @@ def get_dynamic_possible_values(setting_path): Extract all possible values from dynamic settings """ try: - with open(setting_path, 'r') as f: + with open(setting_path, "r") as f: content = f.read().strip() - clean_content = re.sub(r'[\[\]]', '', content) + clean_content = re.sub(r"[\[\]]", "", content) possible_values = clean_content.split() if possible_values: @@ -380,7 +380,7 @@ def get_available_setting(setting_path): Check if setting file is accessible """ try: - with open(setting_path, 'r') as f: + with open(setting_path, "r") as f: f.read() return True except Exception: @@ -424,9 +424,9 @@ def create_kernel_tab(main_window): kernel_layout.addWidget(kernel_subtabs) KernelManager.create_kernel_apply_button(kernel_layout, widgets, main_window) - widgets['kernel_settings_applied'] = False - widgets['is_process_running'] = False - widgets['process'] = None + widgets["kernel_settings_applied"] = False + widgets["is_process_running"] = False + widgets["process"] = None return kernel_tab, widgets @@ -447,14 +447,14 @@ def create_setting_section(kernel_layout, widgets, setting_name, setting_info): current_value_label = QLabel("Updating...") setting_layout.addWidget(current_value_label) - is_accessible = KernelManager.get_available_setting(setting_info['path']) + is_accessible = KernelManager.get_available_setting(setting_info["path"]) input_widget = QLineEdit() input_widget.setPlaceholderText("enter value") if is_accessible: - tooltip_text = setting_info['text'] - if setting_info['is_dynamic']: - possible_values = KernelManager.get_dynamic_possible_values(setting_info['path']) + tooltip_text = setting_info["text"] + if setting_info["is_dynamic"]: + possible_values = KernelManager.get_dynamic_possible_values(setting_info["path"]) if possible_values: values_text = " ".join(possible_values) tooltip_text += f"\nPossible values: {values_text}" @@ -468,8 +468,8 @@ def create_setting_section(kernel_layout, widgets, setting_name, setting_info): setting_layout.addWidget(input_widget) - widgets[f'{setting_name}_input'] = input_widget - widgets[f'{setting_name}_current_value'] = current_value_label + widgets[f"{setting_name}_input"] = input_widget + widgets[f"{setting_name}_current_value"] = current_value_label kernel_layout.addWidget(setting_container) @staticmethod @@ -482,12 +482,12 @@ def create_kernel_apply_button(kernel_layout, widgets, main_window): button_layout = QHBoxLayout(button_container) button_layout.setContentsMargins(10, 10, 10, 0) - widgets['kernel_apply_button'] = QPushButton("Apply") - widgets['kernel_apply_button'].setMinimumSize(100, 30) - widgets['kernel_apply_button'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + widgets["kernel_apply_button"] = QPushButton("Apply") + widgets["kernel_apply_button"].setMinimumSize(100, 30) + widgets["kernel_apply_button"].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) button_layout.addStretch(1) - button_layout.addWidget(widgets['kernel_apply_button']) + button_layout.addWidget(widgets["kernel_apply_button"]) button_layout.addStretch(1) kernel_layout.addWidget(button_container) @@ -500,22 +500,22 @@ def refresh_kernel_values(widgets): """ for category in KernelManager.KERNEL_SETTINGS_CATEGORIES.values(): for name, info in category.items(): - if not KernelManager.get_available_setting(info['path']): - widgets[f'{name}_current_value'].setText("current value: not available") + if not KernelManager.get_available_setting(info["path"]): + widgets[f"{name}_current_value"].setText("current value: not available") continue - if info['is_dynamic']: - current = KernelManager.get_dynamic_current_value(info['path']) - tooltip_text = info['text'] - possible_values = KernelManager.get_dynamic_possible_values(info['path']) + if info["is_dynamic"]: + current = KernelManager.get_dynamic_current_value(info["path"]) + tooltip_text = info["text"] + possible_values = KernelManager.get_dynamic_possible_values(info["path"]) if possible_values: values_text = " ".join(possible_values) tooltip_text += f"\nPossible values: {values_text}" else: tooltip_text += "\nPossible values: Unable to read from system" - widgets[f'{name}_input'].setToolTip(tooltip_text) + widgets[f"{name}_input"].setToolTip(tooltip_text) else: - current = KernelManager.get_current_value(info['path']) + current = KernelManager.get_current_value(info["path"]) if current is not None: - widgets[f'{name}_current_value'].setText(f"current value: {current}") + widgets[f"{name}_current_value"].setText(f"current value: {current}") diff --git a/src/options.py b/src/options.py index 0a147d2..16c676b 100644 --- a/src/options.py +++ b/src/options.py @@ -8,63 +8,63 @@ class OptionsManager: OPTIONS_SETTINGS_CATEGORIES = { "Appearance": { - 'theme': { - 'label': 'Selected Theme:', - 'text': 'Visual theme for the application interface.', - 'items': ["amd", "intel", "nvidia"], - 'default': 'amd' + "theme": { + "label": "Selected Theme:", + "text": "Visual theme for the application interface.", + "items": ["amd", "intel", "nvidia"], + "default": "amd" }, - 'transparency': { - 'label': 'Transparency:', - 'text': 'Window transparency.', - 'items': ["enable", "disable"], - 'default': 'disable' + "transparency": { + "label": "Transparency:", + "text": "Window transparency.", + "items": ["enable", "disable"], + "default": "disable" } }, "Window Behavior": { - 'systray': { - 'label': 'Run in System Tray:', - 'text': 'Run the application in system tray. When enabled, closing the window minimizes to tray instead of exiting.', - 'items': ["enable", "disable"], - 'default': 'disable' + "systray": { + "label": "Run in System Tray:", + "text": "Run the application in system tray. When enabled, closing the window minimizes to tray instead of exiting.", + "items": ["enable", "disable"], + "default": "disable" }, - 'start_maximized': { - 'label': 'Open Maximized:', - 'text': 'Start the application in maximized window mode when launched.', - 'items': ["enable", "disable"], - 'default': 'disable' + "start_maximized": { + "label": "Open Maximized:", + "text": "Start the application in maximized window mode when launched.", + "items": ["enable", "disable"], + "default": "disable" }, - 'start_minimized': { - 'label': 'Open Minimized:', - 'text': 'Start the application minimized to taskbar or system tray when launched.', - 'items': ["enable", "disable"], - 'default': 'disable' + "start_minimized": { + "label": "Open Minimized:", + "text": "Start the application minimized to taskbar or system tray when launched.", + "items": ["enable", "disable"], + "default": "disable" }, - 'scaling': { - 'label': 'Interface Scaling:', - 'text': 'Scale the interface size for high-DPI displays or better readability.', - 'items': ["1.0", "1.25", "1.5", "1.75", "2.0"], - 'default': '1.0' + "scaling": { + "label": "Interface Scaling:", + "text": "Scale the interface size for high-DPI displays or better readability.", + "items": ["1.0", "1.25", "1.5", "1.75", "2.0"], + "default": "1.0" } }, "Application": { - 'welcome_message': { - 'label': 'Welcome Message:', - 'text': 'Show the welcome message dialog when starting the application.', - 'items': ["enable", "disable"], - 'default': 'enable' + "welcome_message": { + "label": "Welcome Message:", + "text": "Show the welcome message dialog when starting the application.", + "items": ["enable", "disable"], + "default": "enable" }, - 'check_updates': { - 'label': 'Check for Updates:', - 'text': 'Check for new versions on startup (checks once per session).', - 'items': ["enable", "disable"], - 'default': 'disable' + "check_updates": { + "label": "Check for Updates:", + "text": "Check for new versions on startup (checks once per session).", + "items": ["enable", "disable"], + "default": "disable" }, - 'volt_path': { - 'label': 'volt Script Path:', - 'text': 'Location for the volt script. /usr/local/bin/volt allows global "volt" command, while /tmp/volt requires the full path, but should work on inmutable/atomic distros.', - 'items': ['/usr/local/bin/volt', '/tmp/volt'], - 'default': '/usr/local/bin/volt' + "volt_path": { + "label": "volt Script Path:", + "text": "Location for the volt script. /usr/local/bin/volt allows global 'volt' command, while /tmp/volt requires the full path, but should work on inmutable/atomic distros.", + "items": ["/usr/local/bin/volt", "/tmp/volt"], + "default": "/usr/local/bin/volt" } } } @@ -96,17 +96,17 @@ def create_options_tab(main_window): for option_key, option_info in OptionsManager.OPTIONS_SETTINGS.items(): option_layout = QHBoxLayout() - option_label = QLabel(option_info['label']) + option_label = QLabel(option_info["label"]) option_label.setWordWrap(True) option_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) widgets[option_key] = QComboBox() - widgets[option_key].addItems(option_info['items']) - widgets[option_key].setCurrentText(option_info['default']) + widgets[option_key].addItems(option_info["items"]) + widgets[option_key].setCurrentText(option_info["default"]) widgets[option_key].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - if 'text' in option_info: - widgets[option_key].setToolTip(option_info['text']) + if "text" in option_info: + widgets[option_key].setToolTip(option_info["text"]) option_layout.addWidget(option_label) option_layout.addWidget(widgets[option_key]) @@ -118,9 +118,9 @@ def create_options_tab(main_window): OptionsManager.create_option_apply_button(main_layout, widgets, main_window) - widgets['main_window'] = main_window - widgets['options_path'] = Path(os.path.expanduser("~/.config/volt-gui/volt-options.ini")) - widgets['options_path'].parent.mkdir(parents=True, exist_ok=True) + widgets["main_window"] = main_window + widgets["options_path"] = Path(os.path.expanduser("~/.config/volt-gui/volt-options.ini")) + widgets["options_path"].parent.mkdir(parents=True, exist_ok=True) OptionsManager.set_default_values(widgets) @@ -136,12 +136,12 @@ def create_option_apply_button(parent_layout, widgets, main_window): button_layout = QHBoxLayout(button_container) button_layout.setContentsMargins(11, 10, 11, 0) - widgets['options_apply_button'] = QPushButton("Apply") - widgets['options_apply_button'].setMinimumSize(100, 30) - widgets['options_apply_button'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + widgets["options_apply_button"] = QPushButton("Apply") + widgets["options_apply_button"].setMinimumSize(100, 30) + widgets["options_apply_button"].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) button_layout.addStretch(1) - button_layout.addWidget(widgets['options_apply_button']) + button_layout.addWidget(widgets["options_apply_button"]) button_layout.addStretch(1) parent_layout.addWidget(button_container) @@ -153,7 +153,7 @@ def set_default_values(widgets): Set default values for all widgets. """ for option_key, option_info in OptionsManager.OPTIONS_SETTINGS.items(): - widgets[option_key].setCurrentText(option_info['default']) + widgets[option_key].setCurrentText(option_info["default"]) @staticmethod def get_early_scaling_factor(): @@ -162,18 +162,18 @@ def get_early_scaling_factor(): This method is called early in the main function. """ config_path = Path(os.path.expanduser("~/.config/volt-gui/volt-options.ini")) - scaling_factor = OptionsManager.OPTIONS_SETTINGS['scaling']['default'] + scaling_factor = OptionsManager.OPTIONS_SETTINGS["scaling"]["default"] if config_path.exists(): options = configparser.ConfigParser() options.read(config_path) scaling_factor = options.get( - 'Options', - 'scaling', - fallback=OptionsManager.OPTIONS_SETTINGS['scaling']['default'] + "Options", + "scaling", + fallback=OptionsManager.OPTIONS_SETTINGS["scaling"]["default"] ) - os.environ['QT_SCALE_FACTOR'] = scaling_factor + os.environ["QT_SCALE_FACTOR"] = scaling_factor return scaling_factor @staticmethod @@ -183,12 +183,12 @@ def load_options(widgets): """ OptionsManager.set_default_values(widgets) - if not widgets['options_path'].exists(): + if not widgets["options_path"].exists(): OptionsManager.save_options(widgets) return options = configparser.ConfigParser() - options.read(widgets['options_path']) + options.read(widgets["options_path"]) OptionsManager.apply_options_values(options, widgets) OptionsManager.apply_all_options(widgets) @@ -199,17 +199,17 @@ def save_options(widgets): Save current options to the configuration file. """ options = configparser.ConfigParser() - main_window = widgets['main_window'] + main_window = widgets["main_window"] - options['Options'] = {} + options["Options"] = {} for option_key in OptionsManager.OPTIONS_SETTINGS.keys(): - options['Options'][option_key] = widgets[option_key].currentText() + options["Options"][option_key] = widgets[option_key].currentText() - options['Profile'] = {'last_active_profile': getattr(main_window, 'current_profile', 'Default')} + options["Profile"] = {"last_active_profile": getattr(main_window, "current_profile", "Default")} - os.makedirs(os.path.dirname(widgets['options_path']), exist_ok=True) + os.makedirs(os.path.dirname(widgets["options_path"]), exist_ok=True) - with open(widgets['options_path'], 'w') as optionsfile: + with open(widgets["options_path"], "w") as optionsfile: options.write(optionsfile) OptionsManager.apply_all_options(widgets) @@ -219,13 +219,13 @@ def apply_options_values(options, widgets): """ Apply values from options file to widgets. """ - main_window = widgets['main_window'] + main_window = widgets["main_window"] for option_key, option_info in OptionsManager.OPTIONS_SETTINGS.items(): - value = options.get('Options', option_key, fallback=option_info['default']) + value = options.get("Options", option_key, fallback=option_info["default"]) widgets[option_key].setCurrentText(value) - last_profile = options.get('Profile', 'last_active_profile', fallback='Default') + last_profile = options.get("Profile", "last_active_profile", fallback="Default") index = main_window.profile_selector.findText(last_profile) if index >= 0: main_window.profile_selector.setCurrentText(last_profile) @@ -251,8 +251,8 @@ def apply_theme_options(widgets): """ Apply the selected theme to the application. """ - main_window = widgets['main_window'] - theme_name = widgets['theme'].currentText() + main_window = widgets["main_window"] + theme_name = widgets["theme"].currentText() ThemeManager.apply_theme(QApplication.instance(), theme_name) @staticmethod @@ -260,20 +260,20 @@ def apply_system_tray_options(widgets): """ Apply system tray options to the main window. """ - main_window = widgets['main_window'] - run_in_tray = widgets['systray'].currentText() == OptionsManager.OPTIONS_SETTINGS['systray']['items'][0] + main_window = widgets["main_window"] + run_in_tray = widgets["systray"].currentText() == OptionsManager.OPTIONS_SETTINGS["systray"]["items"][0] old_option = main_window.use_system_tray main_window.use_system_tray = run_in_tray if old_option != run_in_tray: if run_in_tray: - if not hasattr(main_window, 'tray_icon'): + if not hasattr(main_window, "tray_icon"): main_window.setup_system_tray() else: - if hasattr(main_window, 'tray_icon'): + if hasattr(main_window, "tray_icon"): main_window.tray_icon.hide() main_window.tray_icon.deleteLater() - delattr(main_window, 'tray_icon') + delattr(main_window, "tray_icon") if not main_window.isVisible(): main_window.show_and_activate() @@ -284,8 +284,8 @@ def apply_transparency_options(widgets): """ Apply window transparency options to the main window. """ - main_window = widgets['main_window'] - transparency_enabled = widgets['transparency'].currentText() == OptionsManager.OPTIONS_SETTINGS['transparency']['items'][0] + main_window = widgets["main_window"] + transparency_enabled = widgets["transparency"].currentText() == OptionsManager.OPTIONS_SETTINGS["transparency"]["items"][0] if transparency_enabled: main_window.setWindowOpacity(0.9) else: @@ -296,8 +296,8 @@ def apply_start_minimized_options(widgets): """ Apply the start minimized option to the application. """ - main_window = widgets['main_window'] - start_minimized = widgets['start_minimized'].currentText() == OptionsManager.OPTIONS_SETTINGS['start_minimized']['items'][0] + main_window = widgets["main_window"] + start_minimized = widgets["start_minimized"].currentText() == OptionsManager.OPTIONS_SETTINGS["start_minimized"]["items"][0] main_window.start_minimized = start_minimized @staticmethod @@ -305,8 +305,8 @@ def apply_start_maximized_options(widgets): """ Apply the start maximized option to the application. """ - main_window = widgets['main_window'] - start_maximized = widgets['start_maximized'].currentText() == OptionsManager.OPTIONS_SETTINGS['start_maximized']['items'][0] + main_window = widgets["main_window"] + start_maximized = widgets["start_maximized"].currentText() == OptionsManager.OPTIONS_SETTINGS["start_maximized"]["items"][0] main_window.start_maximized = start_maximized @staticmethod @@ -314,8 +314,8 @@ def apply_welcome_message_options(widgets): """ Apply the welcome message option to the application. """ - main_window = widgets['main_window'] - show_welcome = widgets['welcome_message'].currentText() == OptionsManager.OPTIONS_SETTINGS['welcome_message']['items'][0] + main_window = widgets["main_window"] + show_welcome = widgets["welcome_message"].currentText() == OptionsManager.OPTIONS_SETTINGS["welcome_message"]["items"][0] main_window.show_welcome = show_welcome @staticmethod @@ -323,8 +323,8 @@ def apply_check_updates_options(widgets): """ Apply the check updates option to the application. """ - main_window = widgets['main_window'] - check_updates = widgets['check_updates'].currentText() == OptionsManager.OPTIONS_SETTINGS['check_updates']['items'][0] + main_window = widgets["main_window"] + check_updates = widgets["check_updates"].currentText() == OptionsManager.OPTIONS_SETTINGS["check_updates"]["items"][0] main_window.check_updates = check_updates @staticmethod @@ -332,8 +332,8 @@ def apply_volt_path_options(widgets): """ Apply the volt path option to the application. """ - main_window = widgets['main_window'] - volt_path = widgets['volt_path'].currentText() + main_window = widgets["main_window"] + volt_path = widgets["volt_path"].currentText() main_window.volt_path = volt_path @staticmethod @@ -341,9 +341,9 @@ def apply_scaling_options(widgets): """ Apply interface scaling options to the application. """ - main_window = widgets['main_window'] - scaling_factor = float(widgets['scaling'].currentText()) - os.environ['QT_SCALE_FACTOR'] = str(scaling_factor) + main_window = widgets["main_window"] + scaling_factor = float(widgets["scaling"].currentText()) + os.environ["QT_SCALE_FACTOR"] = str(scaling_factor) main_window.scaling_factor = scaling_factor @staticmethod @@ -351,34 +351,34 @@ def get_welcome_message_setting(widgets): """ Get the current welcome message setting. """ - return widgets['welcome_message'].currentText() == OptionsManager.OPTIONS_SETTINGS['welcome_message']['items'][0] + return widgets["welcome_message"].currentText() == OptionsManager.OPTIONS_SETTINGS["welcome_message"]["items"][0] @staticmethod def get_check_updates_setting(widgets): """ Get the current check updates setting. """ - return widgets['check_updates'].currentText() == OptionsManager.OPTIONS_SETTINGS['check_updates']['items'][0] + return widgets["check_updates"].currentText() == OptionsManager.OPTIONS_SETTINGS["check_updates"]["items"][0] @staticmethod def get_volt_path_setting(widgets): """ Get the current volt path. """ - return widgets['volt_path'].currentText() + return widgets["volt_path"].currentText() @staticmethod def save_and_apply_options(widgets): """ Save current options and apply them to the application. """ - main_window = widgets['main_window'] + main_window = widgets["main_window"] - old_scaling = getattr(main_window, 'scaling_factor', 1.0) - new_scaling = float(widgets['scaling'].currentText()) + old_scaling = getattr(main_window, "scaling_factor", 1.0) + new_scaling = float(widgets["scaling"].currentText()) scaling_changed = old_scaling != new_scaling - widgets['options_apply_button'].setEnabled(False) + widgets["options_apply_button"].setEnabled(False) try: OptionsManager.save_options(widgets) @@ -387,18 +387,18 @@ def save_and_apply_options(widgets): if scaling_changed: message += ".\nInterface scaling will take full effect after restarting the application." - if hasattr(main_window, 'tray_icon'): + if hasattr(main_window, "tray_icon"): main_window.tray_icon.showMessage("volt-gui", message, main_window.tray_icon.MessageIcon.Information, 3000) else: QMessageBox.information(main_window, "volt-gui", message) - QTimer.singleShot(1000, lambda: widgets['options_apply_button'].setEnabled(True)) + QTimer.singleShot(1000, lambda: widgets["options_apply_button"].setEnabled(True)) except Exception as e: error_message = f"Failed to save options: {str(e)}" - if hasattr(main_window, 'tray_icon'): + if hasattr(main_window, "tray_icon"): main_window.tray_icon.showMessage("volt-gui", error_message, main_window.tray_icon.MessageIcon.Critical, 3000) else: QMessageBox.warning(main_window, "volt-gui", error_message) - widgets['options_apply_button'].setEnabled(True) + widgets["options_apply_button"].setEnabled(True) diff --git a/src/script_helper.py b/src/script_helper.py index 3a31831..e2cd9fd 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -12,7 +12,7 @@ class HelperManager: check_commands() { for cmd in chmod grep cut sed tr; do if ! command -v "$cmd" &> /dev/null; then - echo "Error: Required command '$cmd' not found" >&2 + echo "Error: Required command "$cmd" not found" >&2 exit 1 fi done @@ -37,11 +37,11 @@ class HelperManager: } terminate_existing_schedulers() { - pkill -INT -f '^scx_' 2>/dev/null || true + pkill -INT -f "^scx_" 2>/dev/null || true sleep 0.5 - pkill -TERM -f '^scx_' 2>/dev/null || true + pkill -TERM -f "^scx_" 2>/dev/null || true sleep 0.5 - pkill -KILL -f '^scx_' 2>/dev/null || true + pkill -KILL -f "^scx_" 2>/dev/null || true sleep 0.2 } @@ -97,11 +97,11 @@ class HelperManager: read_gpu_settings() { local script_content="#!/bin/bash\\n\\n" - while IFS='=' read -r key value || [ -n "$key" ]; do + while IFS="=" read -r key value || [ -n "$key" ]; do [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]] && continue - key=$(echo "$key" | tr -d ' ') - value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + key=$(echo "$key" | tr -d " ") + value=$(echo "$value" | sed "s/^[[:space:]]*//;s/[[:space:]]*$//") if [ "$key" = "launch_options" ]; then continue @@ -117,7 +117,7 @@ class HelperManager: add_launch_options() { local script_content="$2" - local launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + local launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d"=" -f2- | sed "s/^[[:space:]]*//;s/[[:space:]]*$//") if [ -n "$launch_options" ]; then script_content="${script_content}${launch_options} \\"\\$@\\"\\n" @@ -195,7 +195,7 @@ def create_helper_script(): """ script_path = "/tmp/volt-helper" - with open(script_path, 'w') as f: + with open(script_path, "w") as f: f.write(HelperManager.BASH_SCRIPT_CONTENT) os.chmod(script_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) diff --git a/src/theme.py b/src/theme.py index 1fa45be..f46b3ea 100644 --- a/src/theme.py +++ b/src/theme.py @@ -3,45 +3,45 @@ class ThemeManager: AMD_COLORS = { - 'bg_color': "#1A1A1A", - 'darker_bg': "#0F0F0F", - 'lighter_bg': "#252525", - 'surface_bg': "#202020", - 'text_color': "#FFFFFF", - 'text_secondary': "#B0B0B0", - 'accent_color': "#FF0000", - 'accent_hover': "#FF3333", - 'accent_pressed': "#CC0000", - 'disabled_text': "#666666", - 'selection_bg': "#FF0000", + "bg_color": "#1A1A1A", + "darker_bg": "#0F0F0F", + "lighter_bg": "#252525", + "surface_bg": "#202020", + "text_color": "#FFFFFF", + "text_secondary": "#B0B0B0", + "accent_color": "#FF0000", + "accent_hover": "#FF3333", + "accent_pressed": "#CC0000", + "disabled_text": "#666666", + "selection_bg": "#FF0000", } INTEL_COLORS = { - 'bg_color': "#1A1A1A", - 'darker_bg': "#0F0F0F", - 'lighter_bg': "#252525", - 'surface_bg': "#202020", - 'text_color': "#FFFFFF", - 'text_secondary': "#B0B0B0", - 'accent_color': "#0071C5", - 'accent_hover': "#3399FF", - 'accent_pressed': "#004D87", - 'disabled_text': "#666666", - 'selection_bg': "#0071C5", + "bg_color": "#1A1A1A", + "darker_bg": "#0F0F0F", + "lighter_bg": "#252525", + "surface_bg": "#202020", + "text_color": "#FFFFFF", + "text_secondary": "#B0B0B0", + "accent_color": "#0071C5", + "accent_hover": "#3399FF", + "accent_pressed": "#004D87", + "disabled_text": "#666666", + "selection_bg": "#0071C5", } NVIDIA_COLORS = { - 'bg_color': "#1A1A1A", - 'darker_bg': "#0F0F0F", - 'lighter_bg': "#252525", - 'surface_bg': "#202020", - 'text_color': "#FFFFFF", - 'text_secondary': "#B0B0B0", - 'accent_color': "#76B900", - 'accent_hover': "#9AE62C", - 'accent_pressed': "#5A8A00", - 'disabled_text': "#666666", - 'selection_bg': "#76B900", + "bg_color": "#1A1A1A", + "darker_bg": "#0F0F0F", + "lighter_bg": "#252525", + "surface_bg": "#202020", + "text_color": "#FFFFFF", + "text_secondary": "#B0B0B0", + "accent_color": "#76B900", + "accent_hover": "#9AE62C", + "accent_pressed": "#5A8A00", + "disabled_text": "#666666", + "selection_bg": "#76B900", } THEMES = { @@ -66,15 +66,15 @@ def get_theme_style_sheet(cls): c = cls.COLORS return f""" QWidget {{ - background-color: {c['bg_color']}; - color: {c['text_color']}; + background-color: {c["bg_color"]}; + color: {c["text_color"]}; font-size: 10pt; font-family: "Segoe UI", sans-serif; border-radius: 3px; }} QLabel {{ - color: {c['text_color']}; + color: {c["text_color"]}; background-color: transparent; border: none; border-radius: 3px; @@ -101,46 +101,46 @@ def get_theme_style_sheet(cls): QWidget[buttonContainer="true"] {{ min-height: 50px; - background-color: {c['bg_color']}; + background-color: {c["bg_color"]}; border-radius: 3px; }} QMainWindow {{ - background-color: {c['bg_color']}; + background-color: {c["bg_color"]}; border: none; border-radius: 3px; }} QTabBar::tab:hover:!selected {{ - background-color: {c['lighter_bg']}; - color: {c['text_color']}; - border: 1px solid {c['accent_color']}; + background-color: {c["lighter_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; }} QTabBar::scroller {{ width: 30px; - background-color: {c['surface_bg']}; + background-color: {c["surface_bg"]}; border: none; border-radius: 3px; }} QTabBar QToolButton {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; border: none; border-radius: 3px; padding: 4px; }} QTabBar QToolButton:hover {{ - background-color: {c['lighter_bg']}; + background-color: {c["lighter_bg"]}; border-radius: 3px; - border: 1px solid {c['accent_color']}; + border: 1px solid {c["accent_color"]}; }} QTabBar QToolButton:pressed {{ - background-color: {c['accent_pressed']}; + background-color: {c["accent_pressed"]}; border-radius: 3px; }} @@ -152,27 +152,27 @@ def get_theme_style_sheet(cls): }} QTabWidget {{ - background-color: {c['bg_color']}; + background-color: {c["bg_color"]}; border: none; border-radius: 3px; }} QTabWidget::pane {{ - background-color: {c['bg_color']}; + background-color: {c["bg_color"]}; border: none; border-radius: 3px; }} QTabBar {{ - background-color: {c['bg_color']}; + background-color: {c["bg_color"]}; qproperty-drawBase: 0; border: none; border-radius: 3px; }} QTabBar::tab {{ - background-color: {c['bg_color']}; - color: {c['text_secondary']}; + background-color: {c["bg_color"]}; + color: {c["text_secondary"]}; border: none; border-radius: 3px; padding: 12px 24px; @@ -181,18 +181,18 @@ def get_theme_style_sheet(cls): }} QTabBar::tab:selected {{ - background-color: {c['bg_color']}; - color: {c['text_color']}; - border-left: 3px solid {c['accent_color']}; + background-color: {c["bg_color"]}; + color: {c["text_color"]}; + border-left: 3px solid {c["accent_color"]}; border-radius: 0px 3px 3px 0px; padding-left: 21px; }} QTabBar::tab:hover:!selected {{ - background-color: {c['lighter_bg']}; - color: {c['text_color']}; + background-color: {c["lighter_bg"]}; + color: {c["text_color"]}; border-radius: 3px; - border: 1px solid {c['accent_color']}; + border: 1px solid {c["accent_color"]}; }} QScrollBar:vertical {{ @@ -204,15 +204,15 @@ def get_theme_style_sheet(cls): }} QScrollBar::handle:vertical {{ - background: {c['text_secondary']}; + background: {c["text_secondary"]}; min-height: 30px; border: none; border-radius: 3px; }} QScrollBar::handle:vertical:hover {{ - background: {c['accent_color']}; - border: 1px solid {c['accent_hover']}; + background: {c["accent_color"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; }} @@ -235,15 +235,15 @@ def get_theme_style_sheet(cls): }} QScrollBar::handle:horizontal {{ - background: {c['text_secondary']}; + background: {c["text_secondary"]}; min-width: 30px; border: none; border-radius: 3px; }} QScrollBar::handle:horizontal:hover {{ - background: {c['accent_color']}; - border: 1px solid {c['accent_hover']}; + background: {c["accent_color"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; }} @@ -258,9 +258,9 @@ def get_theme_style_sheet(cls): }} QPushButton {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; outline: none; padding: 10px 16px; @@ -269,48 +269,48 @@ def get_theme_style_sheet(cls): }} QPushButton:disabled {{ - background-color: {c['darker_bg']}; - color: {c['disabled_text']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["darker_bg"]}; + color: {c["disabled_text"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; }} QPushButton:default {{ - background-color: {c['accent_color']}; + background-color: {c["accent_color"]}; color: white; - border: 1px solid {c['accent_hover']}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; outline: none; }} QPushButton:hover {{ - background-color: {c['lighter_bg']}; - border: 1px solid {c['accent_color']}; + background-color: {c["lighter_bg"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; outline: none; }} QPushButton:default:hover {{ - background-color: {c['accent_hover']}; - border: 1px solid {c['accent_hover']}; + background-color: {c["accent_hover"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; outline: none; }} QPushButton:pressed {{ - background-color: {c['accent_pressed']}; - border: 1px solid {c['accent_pressed']}; + background-color: {c["accent_pressed"]}; + border: 1px solid {c["accent_pressed"]}; border-radius: 3px; outline: none; }} QComboBox, QSpinBox, QDoubleSpinBox, QLineEdit {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; padding: 10px 12px; - selection-background-color: {c['selection_bg']}; + selection-background-color: {c["selection_bg"]}; selection-color: white; }} @@ -320,21 +320,21 @@ def get_theme_style_sheet(cls): }} QComboBox:hover, QSpinBox:hover, QDoubleSpinBox:hover, QLineEdit:hover {{ - background-color: {c['lighter_bg']}; - border: 1px solid {c['accent_color']}; + background-color: {c["lighter_bg"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; }} QComboBox:focus, QSpinBox:focus, QDoubleSpinBox:focus, QLineEdit:focus {{ - background-color: {c['lighter_bg']}; - border: 1px solid {c['accent_color']}; + background-color: {c["lighter_bg"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; }} QComboBox:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QLineEdit:disabled {{ - background-color: {c['darker_bg']}; - color: {c['disabled_text']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["darker_bg"]}; + color: {c["disabled_text"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; }} @@ -348,11 +348,11 @@ def get_theme_style_sheet(cls): }} QComboBox QAbstractItemView {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['accent_color']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; - selection-background-color: {c['accent_color']}; + selection-background-color: {c["accent_color"]}; selection-color: white; outline: none; }} @@ -367,14 +367,14 @@ def get_theme_style_sheet(cls): QSpinBox::up-button:hover, QDoubleSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::down-button:hover {{ - background-color: {c['accent_color']}; - border: 1px solid {c['accent_hover']}; + background-color: {c["accent_color"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; }} QLabel[isHeader="true"] {{ font-weight: 600; - color: {c['accent_color']}; + color: {c["accent_color"]}; font-size: 13pt; padding: 12px 0px 8px 0px; background-color: transparent; @@ -383,7 +383,7 @@ def get_theme_style_sheet(cls): }} QCheckBox {{ - color: {c['text_color']}; + color: {c["text_color"]}; spacing: 10px; border: none; border-radius: 3px; @@ -392,40 +392,40 @@ def get_theme_style_sheet(cls): QCheckBox::indicator {{ width: 20px; height: 20px; - background-color: {c['surface_bg']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["surface_bg"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; }} QCheckBox::indicator:unchecked:hover {{ - background-color: {c['lighter_bg']}; - border: 1px solid {c['accent_color']}; + background-color: {c["lighter_bg"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; }} QCheckBox::indicator:checked {{ - background-color: {c['accent_color']}; - border: 1px solid {c['accent_hover']}; + background-color: {c["accent_color"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; }} QCheckBox::indicator:checked:hover {{ - background-color: {c['accent_hover']}; - border: 1px solid {c['accent_hover']}; + background-color: {c["accent_hover"]}; + border: 1px solid {c["accent_hover"]}; border-radius: 3px; }} QWidget[statusContainer="true"] {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; padding: 8px; }} QWidget[statusContainer="true"] QLabel {{ background-color: transparent; - color: {c['text_color']}; + color: {c["text_color"]}; padding: 4px; border: none; border-radius: 3px; @@ -433,8 +433,8 @@ def get_theme_style_sheet(cls): QGroupBox {{ background-color: transparent; - color: {c['text_color']}; - border: 1px solid {c['darker_bg']}; + color: {c["text_color"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; font-weight: 600; font-size: 11pt; @@ -442,7 +442,7 @@ def get_theme_style_sheet(cls): }} QGroupBox::title {{ - color: {c['accent_color']}; + color: {c["accent_color"]}; subcontrol-origin: margin; left: 0px; padding: 0px 0px 8px 0px; @@ -450,15 +450,15 @@ def get_theme_style_sheet(cls): }} QFrame#profileFrame {{ - border: 1px solid {c['accent_color']}; - background-color: {c['bg_color']}; + border: 1px solid {c["accent_color"]}; + background-color: {c["bg_color"]}; border-radius: 0px; }} QMenu {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['accent_color']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["accent_color"]}; border-radius: 3px; padding: 4px; }} @@ -471,37 +471,37 @@ def get_theme_style_sheet(cls): }} QMenu::item:selected {{ - background-color: {c['accent_color']}; + background-color: {c["accent_color"]}; color: white; border-radius: 3px; }} QMenu::separator {{ height: 1px; - background-color: {c['lighter_bg']}; + background-color: {c["lighter_bg"]}; margin: 4px 0px; border-radius: 3px; }} QProgressBar {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['darker_bg']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["darker_bg"]}; border-radius: 3px; text-align: center; padding: 2px; }} QProgressBar::chunk {{ - background-color: {c['accent_color']}; + background-color: {c["accent_color"]}; border: none; border-radius: 3px; }} QToolTip {{ - background-color: {c['surface_bg']}; - color: {c['text_color']}; - border: 1px solid {c['accent_color']}; + background-color: {c["surface_bg"]}; + color: {c["text_color"]}; + border: 1px solid {c["accent_color"]}; border-radius: 5px; padding: 8px 12px; font-size: 10pt; @@ -519,24 +519,24 @@ def get_theme_palette(cls): palette = QPalette() c = cls.COLORS - palette.setColor(QPalette.Window, QColor(c['bg_color'])) - palette.setColor(QPalette.WindowText, QColor(c['text_color'])) - palette.setColor(QPalette.Base, QColor(c['surface_bg'])) - palette.setColor(QPalette.AlternateBase, QColor(c['lighter_bg'])) - palette.setColor(QPalette.Text, QColor(c['text_color'])) + palette.setColor(QPalette.Window, QColor(c["bg_color"])) + palette.setColor(QPalette.WindowText, QColor(c["text_color"])) + palette.setColor(QPalette.Base, QColor(c["surface_bg"])) + palette.setColor(QPalette.AlternateBase, QColor(c["lighter_bg"])) + palette.setColor(QPalette.Text, QColor(c["text_color"])) palette.setColor(QPalette.BrightText, QColor("#FFFFFF")) - palette.setColor(QPalette.Button, QColor(c['surface_bg'])) - palette.setColor(QPalette.ButtonText, QColor(c['text_color'])) - palette.setColor(QPalette.Highlight, QColor(c['accent_color'])) + palette.setColor(QPalette.Button, QColor(c["surface_bg"])) + palette.setColor(QPalette.ButtonText, QColor(c["text_color"])) + palette.setColor(QPalette.Highlight, QColor(c["accent_color"])) palette.setColor(QPalette.HighlightedText, QColor("#FFFFFF")) - palette.setColor(QPalette.Disabled, QPalette.Text, QColor(c['disabled_text'])) - palette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(c['disabled_text'])) - palette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(c['disabled_text'])) - palette.setColor(QPalette.Disabled, QPalette.Window, QColor(c['darker_bg'])) - palette.setColor(QPalette.Disabled, QPalette.Base, QColor(c['darker_bg'])) - palette.setColor(QPalette.Disabled, QPalette.Button, QColor(c['darker_bg'])) - palette.setColor(QPalette.ToolTipBase, QColor(c['surface_bg'])) - palette.setColor(QPalette.ToolTipText, QColor(c['text_color'])) + palette.setColor(QPalette.Disabled, QPalette.Text, QColor(c["disabled_text"])) + palette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(c["disabled_text"])) + palette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(c["disabled_text"])) + palette.setColor(QPalette.Disabled, QPalette.Window, QColor(c["darker_bg"])) + palette.setColor(QPalette.Disabled, QPalette.Base, QColor(c["darker_bg"])) + palette.setColor(QPalette.Disabled, QPalette.Button, QColor(c["darker_bg"])) + palette.setColor(QPalette.ToolTipBase, QColor(c["surface_bg"])) + palette.setColor(QPalette.ToolTipText, QColor(c["text_color"])) return palette diff --git a/src/update_checker.py b/src/update_checker.py index afc5586..fdbdce1 100644 --- a/src/update_checker.py +++ b/src/update_checker.py @@ -37,7 +37,7 @@ def show_update_notification(main_window, new_version): """ message = f"A new volt-gui version is available: {new_version}" - if hasattr(main_window, 'tray_icon') and main_window.use_system_tray: + if hasattr(main_window, "tray_icon") and main_window.use_system_tray: main_window.tray_icon.showMessage( "volt-gui Update", message, diff --git a/src/volt-gui.py b/src/volt-gui.py index 3e25652..b53ebb5 100644 --- a/src/volt-gui.py +++ b/src/volt-gui.py @@ -20,7 +20,7 @@ def check_sudo_execution(): """ Check if the application is run with sudo and exit if it is. """ - if os.environ.get('SUDO_USER'): + if os.environ.get("SUDO_USER"): print("Error: This application should not be run with sudo.") print("Please run as a regular user. The application will request") print("elevated privileges when needed through pkexec.") @@ -38,7 +38,7 @@ def __init__(self, port=47832): """ self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.server_address = ('localhost', self.port) + self.server_address = ("localhost", self.port) self.signals = SingletonSignals() self.listener_thread = None self.running = False @@ -250,7 +250,7 @@ def setup_cpu_tab(self): Set up the CPU management tab. """ cpu_tab, self.cpu_widgets = CPUManager.create_cpu_tab() - self.cpu_widgets['cpu_apply_button'].clicked.connect(self.apply_all_settings) + self.cpu_widgets["cpu_apply_button"].clicked.connect(self.apply_all_settings) CPUManager.refresh_cpu_values(self.cpu_widgets) self.tab_widget.addTab(cpu_tab, "CPU") @@ -259,7 +259,7 @@ def setup_disk_tab(self): Set up the disk management tab. """ disk_tab, self.disk_widgets = DiskManager.create_disk_tab() - self.disk_widgets['disk_apply_button'].clicked.connect(self.apply_all_settings) + self.disk_widgets["disk_apply_button"].clicked.connect(self.apply_all_settings) DiskManager.refresh_disk_values(self.disk_widgets) self.tab_widget.addTab(disk_tab, "Disk") @@ -270,7 +270,7 @@ def setup_gpu_tabs(self): gpu_tab, self.gpu_widgets = GPULaunchManager.create_gpu_settings_tabs() for category_name, category_widgets in self.gpu_widgets.items(): - if category_name != 'LaunchOptions': + if category_name != "LaunchOptions": apply_button_name = f"{category_name.lower()}_apply_button" if apply_button_name in category_widgets: category_widgets[apply_button_name].clicked.connect(self.apply_all_settings) @@ -282,8 +282,8 @@ def setup_launch_options_tab(self): Set up the launch options tab. """ launch_options_tab, self.launch_options_widgets = GPULaunchManager.create_launch_options_tab() - self.gpu_widgets['LaunchOptions'] = self.launch_options_widgets - self.launch_options_widgets['launch_apply_button'].clicked.connect(self.apply_all_settings) + self.gpu_widgets["LaunchOptions"] = self.launch_options_widgets + self.launch_options_widgets["launch_apply_button"].clicked.connect(self.apply_all_settings) self.tab_widget.addTab(launch_options_tab, "Launch Options") def setup_kernel_tab(self): @@ -291,10 +291,10 @@ def setup_kernel_tab(self): Set up the kernel management tab. """ kernel_tab, self.kernel_widgets = KernelManager.create_kernel_tab(self) - self.kernel_widgets['kernel_apply_button'].clicked.connect(self.apply_all_settings) + self.kernel_widgets["kernel_apply_button"].clicked.connect(self.apply_all_settings) for setting_name in KernelManager.KERNEL_SETTINGS.keys(): - widget_key = f'{setting_name}_input' + widget_key = f"{setting_name}_input" self.kernel_widgets[widget_key].installEventFilter(self) KernelManager.refresh_kernel_values(self.kernel_widgets) @@ -312,7 +312,7 @@ def setup_options_tab(self): Set up the options tab. """ options_tab, self.options_widgets = OptionsManager.create_options_tab(self) - self.options_widgets['options_apply_button'].clicked.connect(lambda: OptionsManager.save_and_apply_options(self.options_widgets)) + self.options_widgets["options_apply_button"].clicked.connect(lambda: OptionsManager.save_and_apply_options(self.options_widgets)) self.tab_widget.addTab(options_tab, "Options") def setup_about_tab(self): @@ -378,7 +378,7 @@ def update_tray_profile_menu(self): """ Update the tray profile menu with available profiles. """ - if not hasattr(self, 'profile_submenu'): + if not hasattr(self, "profile_submenu"): return self.profile_submenu.clear() @@ -453,7 +453,7 @@ def on_profile_changed(self, profile_name): if not profile_name or profile_name == self.current_profile or profile_name.isspace(): return - if hasattr(self, '_initial_setup_complete') and self._initial_setup_complete: + if hasattr(self, "_initial_setup_complete") and self._initial_setup_complete: try: ConfigManager.save_config( self.cpu_widgets, @@ -512,7 +512,7 @@ def save_new_profile(self): self.update_profile_list() self.profile_selector.setCurrentText(profile_name) - if hasattr(self, 'tray_icon') and self.use_system_tray: + if hasattr(self, "tray_icon") and self.use_system_tray: self.update_tray_profile_menu() QMessageBox.information(self, "Profile Saved", f"Profile '{profile_name}' has been saved.") @@ -556,7 +556,7 @@ def delete_current_profile(self): self.refresh_disk_values() self.refresh_kernel_values() - if hasattr(self, 'tray_icon') and self.use_system_tray: + if hasattr(self, "tray_icon") and self.use_system_tray: self.update_tray_profile_menu() QMessageBox.information(self, "Profile Deleted", f"Profile '{current_profile}' has been deleted.") @@ -631,28 +631,28 @@ def apply_all_settings(self): Collects all settings (CPU, Disk, GPU, Kernel) and applies them in one go using volt-helper. Uses clean environment to avoid PyInstaller interference. """ - if (self.cpu_widgets.get('is_process_running', False) or - self.disk_widgets.get('is_process_running', False) or - self.kernel_widgets.get('is_process_running', False)): + if (self.cpu_widgets.get("is_process_running", False) or + self.disk_widgets.get("is_process_running", False) or + self.kernel_widgets.get("is_process_running", False)): return try: self.save_settings() cpu_args = [] - cpu_governor = self.cpu_widgets['scaling_governor'].currentText() + cpu_governor = self.cpu_widgets["scaling_governor"].currentText() - if 'max_freq' in self.cpu_widgets: - cpu_max_freq = self.cpu_widgets['scaling_max_freq'].currentText() + if "max_freq" in self.cpu_widgets: + cpu_max_freq = self.cpu_widgets["scaling_max_freq"].currentText() else: cpu_max_freq = "unset" - if 'min_freq' in self.cpu_widgets: - cpu_min_freq = self.cpu_widgets['scaling_min_freq'].currentText() + if "min_freq" in self.cpu_widgets: + cpu_min_freq = self.cpu_widgets["scaling_min_freq"].currentText() else: cpu_min_freq = "unset" - cpu_scheduler = self.cpu_widgets['scheduler'].currentText() + cpu_scheduler = self.cpu_widgets["scheduler"].currentText() cpu_parts = [] if cpu_governor != "unset": @@ -674,8 +674,8 @@ def apply_all_settings(self): cpu_args.extend(cpu_parts) disk_args = [] - for disk_name, disk_widgets in self.disk_widgets['disk_settings'].items(): - selected_scheduler = disk_widgets['scheduler'].currentText() + for disk_name, disk_widgets in self.disk_widgets["disk_settings"].items(): + selected_scheduler = disk_widgets["scheduler"].currentText() if selected_scheduler and selected_scheduler != "" and selected_scheduler != "unset": if not disk_args: disk_args.append("-d") @@ -684,7 +684,7 @@ def apply_all_settings(self): kernel_args = [] for category in KernelManager.KERNEL_SETTINGS_CATEGORIES.values(): for name, info in category.items(): - value = self.kernel_widgets[f'{name}_input'].text().strip() + value = self.kernel_widgets[f"{name}_input"].text().strip() if value: if not kernel_args: kernel_args.append("-k") @@ -692,12 +692,12 @@ def apply_all_settings(self): gpu_args = [] settings_file = GPULaunchManager.write_settings_file( - self.gpu_widgets['Mesa'], - self.gpu_widgets['NVIDIA'], - self.gpu_widgets['RenderSelector'], - self.gpu_widgets['MangoHud'], - self.gpu_widgets['LSFrameGen'], - self.gpu_widgets['LaunchOptions'] + self.gpu_widgets["Mesa"], + self.gpu_widgets["NVIDIA"], + self.gpu_widgets["RenderSelector"], + self.gpu_widgets["MangoHud"], + self.gpu_widgets["LSFrameGen"], + self.gpu_widgets["LaunchOptions"] ) if settings_file: gpu_args.extend(["-p", self.volt_path, "-g", settings_file]) @@ -711,21 +711,21 @@ def apply_all_settings(self): process.start(all_args[0], all_args[1:]) process.finished.connect(lambda: self.on_settings_applied(process.exitCode())) - self.cpu_widgets['cpu_apply_button'].setEnabled(False) - self.disk_widgets['disk_apply_button'].setEnabled(False) - self.kernel_widgets['kernel_apply_button'].setEnabled(False) + self.cpu_widgets["cpu_apply_button"].setEnabled(False) + self.disk_widgets["disk_apply_button"].setEnabled(False) + self.kernel_widgets["kernel_apply_button"].setEnabled(False) for category_name, category_widgets in self.gpu_widgets.items(): - if category_name != 'LaunchOptions': + if category_name != "LaunchOptions": apply_button_name = f"{category_name.lower()}_apply_button" if apply_button_name in category_widgets: category_widgets[apply_button_name].setEnabled(False) - self.launch_options_widgets['launch_apply_button'].setEnabled(False) + self.launch_options_widgets["launch_apply_button"].setEnabled(False) except Exception as e: print(f"Error applying settings: {e}") - if hasattr(self, 'tray_icon'): + if hasattr(self, "tray_icon"): self.tray_icon.showMessage("volt-gui", f"Failed to apply settings: {e}", QSystemTrayIcon.MessageIcon.Critical, 2000) else: QMessageBox.warning(self, "volt-gui", f"Failed to apply settings: {e}") @@ -734,23 +734,23 @@ def on_settings_applied(self, exit_code): """ Handle the completion of the settings application process. """ - self.cpu_widgets['cpu_apply_button'].setEnabled(True) - self.disk_widgets['disk_apply_button'].setEnabled(True) - self.kernel_widgets['kernel_apply_button'].setEnabled(True) + self.cpu_widgets["cpu_apply_button"].setEnabled(True) + self.disk_widgets["disk_apply_button"].setEnabled(True) + self.kernel_widgets["kernel_apply_button"].setEnabled(True) for category_name, category_widgets in self.gpu_widgets.items(): - if category_name != 'LaunchOptions': + if category_name != "LaunchOptions": apply_button_name = f"{category_name.lower()}_apply_button" if apply_button_name in category_widgets: category_widgets[apply_button_name].setEnabled(True) - self.launch_options_widgets['launch_apply_button'].setEnabled(True) + self.launch_options_widgets["launch_apply_button"].setEnabled(True) self.refresh_cpu_values() self.refresh_disk_values() self.refresh_kernel_values() - if hasattr(self, 'tray_icon'): + if hasattr(self, "tray_icon"): self.tray_icon.showMessage("volt-gui", "Settings applied successfully" if exit_code == 0 else "Failed to apply settings", QSystemTrayIcon.MessageIcon.Information if exit_code == 0 else QSystemTrayIcon.MessageIcon.Critical, 2000) else: QMessageBox.information(self, "volt-gui", "Settings applied successfully") @@ -779,7 +779,7 @@ def closeEvent(self, event): Handle the main window close event. If system tray is enabled, hide to tray. If system tray is disabled, quit the application. """ - if self.use_system_tray and hasattr(self, 'tray_icon'): + if self.use_system_tray and hasattr(self, "tray_icon"): self.hide() event.ignore() else: diff --git a/src/welcome.py b/src/welcome.py index af59e79..77870e6 100644 --- a/src/welcome.py +++ b/src/welcome.py @@ -235,29 +235,29 @@ def create_navigation_buttons(parent_layout, widgets): nav_layout = QHBoxLayout() nav_layout.setContentsMargins(0, 16, 0, 0) - widgets['back_button'] = QPushButton("← Back") - widgets['back_button'].setMinimumHeight(32) - widgets['back_button'].setCursor(QCursor(Qt.PointingHandCursor)) - widgets['back_button'].setStyleSheet("QPushButton:disabled { color: #777777; }") + widgets["back_button"] = QPushButton("← Back") + widgets["back_button"].setMinimumHeight(32) + widgets["back_button"].setCursor(QCursor(Qt.PointingHandCursor)) + widgets["back_button"].setStyleSheet("QPushButton:disabled { color: #777777; }") - nav_layout.addWidget(widgets['back_button']) + nav_layout.addWidget(widgets["back_button"]) - widgets['progress_label'] = QLabel() - widgets['progress_label'].setAlignment(Qt.AlignCenter) - widgets['progress_label'].setStyleSheet("font-size: 13px; color: #888888; margin: 0 10px;") - nav_layout.addWidget(widgets['progress_label'], 1, Qt.AlignCenter) + widgets["progress_label"] = QLabel() + widgets["progress_label"].setAlignment(Qt.AlignCenter) + widgets["progress_label"].setStyleSheet("font-size: 13px; color: #888888; margin: 0 10px;") + nav_layout.addWidget(widgets["progress_label"], 1, Qt.AlignCenter) - widgets['next_button'] = QPushButton("Next →") - widgets['next_button'].setMinimumHeight(32) - widgets['next_button'].setCursor(QCursor(Qt.PointingHandCursor)) + widgets["next_button"] = QPushButton("Next →") + widgets["next_button"].setMinimumHeight(32) + widgets["next_button"].setCursor(QCursor(Qt.PointingHandCursor)) - widgets['finish_button'] = QPushButton("Finish") - widgets['finish_button'].setMinimumHeight(32) - widgets['finish_button'].setCursor(QCursor(Qt.PointingHandCursor)) - widgets['finish_button'].hide() + widgets["finish_button"] = QPushButton("Finish") + widgets["finish_button"].setMinimumHeight(32) + widgets["finish_button"].setCursor(QCursor(Qt.PointingHandCursor)) + widgets["finish_button"].hide() - nav_layout.addWidget(widgets['next_button']) - nav_layout.addWidget(widgets['finish_button']) + nav_layout.addWidget(widgets["next_button"]) + nav_layout.addWidget(widgets["finish_button"]) parent_layout.addLayout(nav_layout) @@ -266,24 +266,24 @@ def update_navigation(widgets): """ Updates the navigation buttons and progress indicator based on current step. """ - current_step = widgets['current_step'] + current_step = widgets["current_step"] total_steps = len(WelcomeManager.get_welcome_info()) - widgets['progress_label'].setText(f"Step {current_step + 1} of {total_steps}") - widgets['back_button'].setEnabled(current_step > 0) + widgets["progress_label"].setText(f"Step {current_step + 1} of {total_steps}") + widgets["back_button"].setEnabled(current_step > 0) is_last_step = current_step == total_steps - 1 - widgets['next_button'].setVisible(not is_last_step) - widgets['finish_button'].setVisible(is_last_step) + widgets["next_button"].setVisible(not is_last_step) + widgets["finish_button"].setVisible(is_last_step) @staticmethod def go_back(widgets): """ Go to the previous step. """ - if widgets['current_step'] > 0: - widgets['current_step'] -= 1 - widgets['stacked_widget'].setCurrentIndex(widgets['current_step']) + if widgets["current_step"] > 0: + widgets["current_step"] -= 1 + widgets["stacked_widget"].setCurrentIndex(widgets["current_step"]) WelcomeManager.update_navigation(widgets) @staticmethod @@ -292,9 +292,9 @@ def go_next(widgets): Go to the next step. """ total_steps = len(WelcomeManager.get_welcome_info()) - if widgets['current_step'] < total_steps - 1: - widgets['current_step'] += 1 - widgets['stacked_widget'].setCurrentIndex(widgets['current_step']) + if widgets["current_step"] < total_steps - 1: + widgets["current_step"] += 1 + widgets["stacked_widget"].setCurrentIndex(widgets["current_step"]) WelcomeManager.update_navigation(widgets) @staticmethod @@ -302,7 +302,7 @@ def finish_wizard(widgets): """ Finish the wizard and close the window. """ - welcome_window = widgets.get('welcome_window') + welcome_window = widgets.get("welcome_window") if welcome_window: welcome_window.close() @@ -321,21 +321,21 @@ def create_welcome_window(): main_layout.setSpacing(16) widgets = { - 'current_step': 0, - 'welcome_window': welcome_window, - 'stacked_widget': QStackedWidget() + "current_step": 0, + "welcome_window": welcome_window, + "stacked_widget": QStackedWidget() } for section in WelcomeManager.get_welcome_info(): page = WelcomeManager.create_step_page(section) - widgets['stacked_widget'].addWidget(page) + widgets["stacked_widget"].addWidget(page) - main_layout.addWidget(widgets['stacked_widget'], 1) + main_layout.addWidget(widgets["stacked_widget"], 1) WelcomeManager.create_navigation_buttons(main_layout, widgets) - widgets['back_button'].clicked.connect(lambda: WelcomeManager.go_back(widgets)) - widgets['next_button'].clicked.connect(lambda: WelcomeManager.go_next(widgets)) - widgets['finish_button'].clicked.connect(lambda: WelcomeManager.finish_wizard(widgets)) + widgets["back_button"].clicked.connect(lambda: WelcomeManager.go_back(widgets)) + widgets["next_button"].clicked.connect(lambda: WelcomeManager.go_next(widgets)) + widgets["finish_button"].clicked.connect(lambda: WelcomeManager.finish_wizard(widgets)) WelcomeManager.update_navigation(widgets) welcome_window.setCentralWidget(central_widget) diff --git a/src/workarounds.py b/src/workarounds.py index 2a3f405..1d436f7 100644 --- a/src/workarounds.py +++ b/src/workarounds.py @@ -9,8 +9,8 @@ def setup_qt_platform(): Default to X11 (xcb) Only sets if QT_QPA_PLATFORM is not already defined, allowing users to override if needed. """ - if 'QT_QPA_PLATFORM' not in os.environ: - os.environ['QT_QPA_PLATFORM'] = 'xcb' + if "QT_QPA_PLATFORM" not in os.environ: + os.environ["QT_QPA_PLATFORM"] = "xcb" @staticmethod def get_clean_env(): @@ -19,13 +19,13 @@ def get_clean_env(): Returns a list of strings in QProcess environment format. """ env = os.environ.copy() - if getattr(sys, 'frozen', False): - env.pop('LD_LIBRARY_PATH', None) - env.pop('LD_PRELOAD', None) - if hasattr(sys, '_MEIPASS') and 'PATH' in env: - paths = env['PATH'].split(os.pathsep) + if getattr(sys, "frozen", False): + env.pop("LD_LIBRARY_PATH", None) + env.pop("LD_PRELOAD", None) + if hasattr(sys, "_MEIPASS") and "PATH" in env: + paths = env["PATH"].split(os.pathsep) clean_paths = [p for p in paths if sys._MEIPASS not in p] - env['PATH'] = os.pathsep.join(clean_paths) + env["PATH"] = os.pathsep.join(clean_paths) return [f"{key}={value}" for key, value in env.items()] @staticmethod diff --git a/test.sh b/test.sh index 3261737..de48367 100755 --- a/test.sh +++ b/test.sh @@ -2,9 +2,9 @@ set -euo pipefail -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' +RED="\033[0;31m" +BLUE="\033[0;34m" +NC="\033[0m" VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" @@ -21,7 +21,7 @@ cleanup() { check_commands() { for cmd in python3 pip shasum cut cat rm; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: Required command '$cmd' not found${NC}" >&2 + echo -e "${RED}Error: Required command "$cmd" not found${NC}" >&2 exit 1 fi done @@ -46,7 +46,7 @@ verify_files() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d' ' -f1) + CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then From ab924d866a10136171b7b7bfe2537e26d17c84b5 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 14:03:51 -0300 Subject: [PATCH 23/29] [volt] fix missing spaces --- src/script_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script_helper.py b/src/script_helper.py index e2cd9fd..1acf44d 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -116,7 +116,7 @@ class HelperManager: } add_launch_options() { - local script_content="$2" + local script_content="$2\\n\\n" local launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d"=" -f2- | sed "s/^[[:space:]]*//;s/[[:space:]]*$//") if [ -n "$launch_options" ]; then From c4d64bb663575d98cada2ddd9bf7f83189461b9a Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 14:34:48 -0300 Subject: [PATCH 24/29] [volt] fix scaling_max_freq and scaling_min_freq --- src/volt-gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volt-gui.py b/src/volt-gui.py index b53ebb5..01e710b 100644 --- a/src/volt-gui.py +++ b/src/volt-gui.py @@ -642,12 +642,12 @@ def apply_all_settings(self): cpu_args = [] cpu_governor = self.cpu_widgets["scaling_governor"].currentText() - if "max_freq" in self.cpu_widgets: + if "scaling_max_freq" in self.cpu_widgets: cpu_max_freq = self.cpu_widgets["scaling_max_freq"].currentText() else: cpu_max_freq = "unset" - if "min_freq" in self.cpu_widgets: + if "scaling_min_freq" in self.cpu_widgets: cpu_min_freq = self.cpu_widgets["scaling_min_freq"].currentText() else: cpu_min_freq = "unset" From 67e1e1b36b89876335a5d898b13a6defc39ad2bb Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 14:54:22 -0300 Subject: [PATCH 25/29] [volt] fix error when no scheduler was passed --- src/script_helper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/script_helper.py b/src/script_helper.py index 1acf44d..84d17c5 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -71,7 +71,9 @@ class HelperManager: [ -n "$governor" ] && apply_governor "$governor" [ -n "$min_freq" ] && apply_min_freq "$min_freq" [ -n "$max_freq" ] && apply_max_freq "$max_freq" - [ -n "$scheduler" ] && handle_scheduler "$scheduler" + if [ -n "$scheduler" ]; then + handle_scheduler "$scheduler" + fi } apply_disk_scheduler() { From 6d3c9e7f1f43285ff0fd0f3ddffbb032243b00bc Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 16:20:47 -0300 Subject: [PATCH 26/29] [volt] refactor the volt-helper --- src/script_helper.py | 186 +++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 104 deletions(-) diff --git a/src/script_helper.py b/src/script_helper.py index 84d17c5..0f5ebfe 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -5,178 +5,159 @@ class HelperManager: Manages the creation of the volt-helper script. """ - BASH_SCRIPT_CONTENT = """#!/bin/bash + BASH_SCRIPT_CONTENT = r"""#!/bin/bash set -euo pipefail check_commands() { - for cmd in chmod grep cut sed tr; do + for cmd in pgrep pkill sleep chmod grep cut; do if ! command -v "$cmd" &> /dev/null; then - echo "Error: Required command "$cmd" not found" >&2 + echo "Error: Required command \"$cmd\" not found" >&2 exit 1 fi done } -apply_governor() { - for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do - echo "$1" > "$CPU_PATH" - done -} - -apply_max_freq() { - for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do - echo "$1" > "$CPU_PATH" - done -} - -apply_min_freq() { - for CPU_PATH in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do - echo "$1" > "$CPU_PATH" - done -} - -terminate_existing_schedulers() { - pkill -INT -f "^scx_" 2>/dev/null || true - sleep 0.5 - pkill -TERM -f "^scx_" 2>/dev/null || true - sleep 0.5 - pkill -KILL -f "^scx_" 2>/dev/null || true - sleep 0.2 +terminate_schedulers() { + pgrep -f "^scx_" >/dev/null 2>&1 && pkill -INT -f "^scx_" && sleep 0.5 + pgrep -f "^scx_" >/dev/null 2>&1 && pkill -TERM -f "^scx_" && sleep 0.5 + pgrep -f "^scx_" >/dev/null 2>&1 && pkill -KILL -f "^scx_" && sleep 0.2 + return 0 } -handle_scheduler() { - local scheduler="$1" - - terminate_existing_schedulers - - if [ -n "$scheduler" ] && [ "$scheduler" != "none" ]; then - "$scheduler" & - sleep 1 - fi -} - -manage_cpu() { - local governor="" scheduler="" max_freq="" min_freq="" +apply_cpu() { + local governor="" + local scheduler="" + local max_freq="" + local min_freq="" + local has_scheduler=false for arg in "$@"; do case "$arg" in governor:*) governor="${arg#governor:}" ;; - scheduler:*) scheduler="${arg#scheduler:}" ;; max_freq:*) max_freq="${arg#max_freq:}" ;; min_freq:*) min_freq="${arg#min_freq:}" ;; + scheduler:*) + scheduler="${arg#scheduler:}" + has_scheduler=true + ;; esac done - [ -n "$governor" ] && apply_governor "$governor" - [ -n "$min_freq" ] && apply_min_freq "$min_freq" - [ -n "$max_freq" ] && apply_max_freq "$max_freq" - if [ -n "$scheduler" ]; then - handle_scheduler "$scheduler" + if [[ -n "$governor" ]]; then + for path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + echo "$governor" > "$path" + done fi -} -apply_disk_scheduler() { - echo "$2" > "/sys/block/$1/queue/scheduler" + if [[ -n "$min_freq" ]]; then + for path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do + echo "$min_freq" > "$path" + done + fi + + if [[ -n "$max_freq" ]]; then + for path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do + echo "$max_freq" > "$path" + done + fi + + terminate_schedulers + + if [[ "$has_scheduler" == true && "$scheduler" != "none" ]]; then + "$scheduler" & + sleep 1 + fi } -manage_disk() { +apply_disk() { for arg in "$@"; do - [[ "$arg" == *":"* ]] && apply_disk_scheduler "${arg%%:*}" "${arg#*:}" + [[ "$arg" == *":"* ]] && echo "${arg#*:}" > "/sys/block/${arg%%:*}/queue/scheduler" done } -apply_kernel_parameter() { - echo "$2" > "$1" 2>/dev/null -} - -manage_kernel() { - for setting in "$@"; do - apply_kernel_parameter "${setting%%:*}" "${setting#*:}" +apply_kernel() { + for arg in "$@"; do + echo "${arg#*:}" > "${arg%%:*}" done } -read_gpu_settings() { - local script_content="#!/bin/bash\\n\\n" +apply_gpu() { + local settings_file="$1" + local volt_path="$2" + local script_content="#!/bin/bash\n\n" - while IFS="=" read -r key value || [ -n "$key" ]; do - [ -z "$key" ] || [[ "$key" =~ ^[[:space:]]*# ]] && continue + while IFS="=" read -r key value; do + [[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue - key=$(echo "$key" | tr -d " ") - value=$(echo "$value" | sed "s/^[[:space:]]*//;s/[[:space:]]*$//") + key="${key// /}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" - if [ "$key" = "launch_options" ]; then + if [[ "$key" == "launch_options" ]]; then continue - elif [ -n "$value" ]; then - script_content="${script_content}export ${key}=\\"${value}\\"\\n" + elif [[ -n "$value" ]]; then + script_content+="export ${key}=\"${value}\"\n" elif [[ "$key" == unset:* ]]; then - script_content="${script_content}unset ${key#unset:}\\n" + script_content+="unset ${key#unset:}\n" fi - done < "$1" - - echo -e "$script_content" -} + done < "$settings_file" -add_launch_options() { - local script_content="$2\\n\\n" - local launch_options=$(grep "^launch_options=" "$1" 2>/dev/null | cut -d"=" -f2- | sed "s/^[[:space:]]*//;s/[[:space:]]*$//") + script_content+="\n" - if [ -n "$launch_options" ]; then - script_content="${script_content}${launch_options} \\"\\$@\\"\\n" + if grep -q "^launch_options=" "$settings_file" 2>/dev/null; then + local launch_opts + launch_opts="$(grep "^launch_options=" "$settings_file" | cut -d"=" -f2-)" + launch_opts="${launch_opts#"${launch_opts%%[![:space:]]*}"}" + launch_opts="${launch_opts%"${launch_opts##*[![:space:]]}"}" + script_content+="${launch_opts} \"\$@\"\n" else - script_content="${script_content}\\"\\$@\\"\\n" + script_content+="\"\$@\"\n" fi - echo -e "$script_content" + echo -e "$script_content" > "$volt_path" + chmod 755 "$volt_path" } -manage_gpu() { - local script_content=$(read_gpu_settings "$1") - script_content=$(add_launch_options "$1" "$script_content") - - mkdir -p "$(dirname "$2")" 2>/dev/null - echo -e "$script_content" > "$2" 2>/dev/null - chmod 755 "$2" 2>/dev/null -} - -parse_arguments() { +main() { + check_commands local volt_path="" - while [ $# -gt 0 ]; do + while [[ $# -gt 0 ]]; do case "$1" in -c|--cpu) shift local cpu_args=() - while [ $# -gt 0 ] && [[ "$1" != -* ]]; do + while [[ $# -gt 0 && "$1" != -* ]]; do cpu_args+=("$1") shift done - manage_cpu "${cpu_args[@]}" + apply_cpu "${cpu_args[@]}" ;; -d|--disk) shift local disk_args=() - while [ $# -gt 0 ] && [[ "$1" != -* ]]; do + while [[ $# -gt 0 && "$1" != -* ]]; do disk_args+=("$1") shift done - manage_disk "${disk_args[@]}" + apply_disk "${disk_args[@]}" ;; -k|--kernel) shift local kernel_args=() - while [ $# -gt 0 ] && [[ "$1" != -* ]]; do + while [[ $# -gt 0 && "$1" != -* ]]; do kernel_args+=("$1") shift done - manage_kernel "${kernel_args[@]}" + apply_kernel "${kernel_args[@]}" ;; -p|--path) volt_path="$2" shift 2 ;; -g|--gpu) - manage_gpu "$2" "$volt_path" + apply_gpu "$2" "$volt_path" shift 2 ;; *) @@ -186,8 +167,7 @@ class HelperManager: done } -check_commands -parse_arguments "$@" +main "$@" """ @staticmethod @@ -195,9 +175,7 @@ def create_helper_script(): """ Creates the volt-helper bash script in /tmp with executable permissions. """ - script_path = "/tmp/volt-helper" - - with open(script_path, "w") as f: + with open("/tmp/volt-helper", "w") as f: f.write(HelperManager.BASH_SCRIPT_CONTENT) - os.chmod(script_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + os.chmod("/tmp/volt-helper", stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) From ab747359ea314e5bf16e21840c1653c9d77bba3c Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 16:28:48 -0300 Subject: [PATCH 27/29] [volt] remove update from the title --- src/update_checker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/update_checker.py b/src/update_checker.py index fdbdce1..4a12347 100644 --- a/src/update_checker.py +++ b/src/update_checker.py @@ -39,10 +39,10 @@ def show_update_notification(main_window, new_version): if hasattr(main_window, "tray_icon") and main_window.use_system_tray: main_window.tray_icon.showMessage( - "volt-gui Update", + "volt-gui", message, main_window.tray_icon.MessageIcon.Information, 5000 ) else: - QMessageBox.information(main_window, "volt-gui Update", message) + QMessageBox.information(main_window, "volt-gui", message) From 6519c3ba74fd52709d2c622834e79cf7ac4c321d Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 17 Oct 2025 16:40:36 -0300 Subject: [PATCH 28/29] [volt] set launch_opts to an empy string by default --- src/script_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script_helper.py b/src/script_helper.py index 0f5ebfe..96c718d 100644 --- a/src/script_helper.py +++ b/src/script_helper.py @@ -106,7 +106,7 @@ class HelperManager: script_content+="\n" if grep -q "^launch_options=" "$settings_file" 2>/dev/null; then - local launch_opts + local launch_opts="" launch_opts="$(grep "^launch_options=" "$settings_file" | cut -d"=" -f2-)" launch_opts="${launch_opts#"${launch_opts%%[![:space:]]*}"}" launch_opts="${launch_opts%"${launch_opts##*[![:space:]]}"}" From 5d94f1ec2b16eaf0ed00f61d173f2d31475e95eb Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Sat, 18 Oct 2025 14:29:29 -0300 Subject: [PATCH 29/29] [make/test] use local vars when they make sense --- make-appimage.sh | 2 ++ make-nuitka.sh | 20 ++++++++++--------- make-pyinstaller.sh | 20 ++++++++++--------- make-release.sh | 48 ++++++++++++++++++++------------------------- test.sh | 13 ++++++------ 5 files changed, 52 insertions(+), 51 deletions(-) diff --git a/make-appimage.sh b/make-appimage.sh index 3754498..2d4eb69 100755 --- a/make-appimage.sh +++ b/make-appimage.sh @@ -103,6 +103,8 @@ build_appimage() { } print_success() { + local size="" + echo -e "\nBuild successful!" echo -e "AppImage: $OUTPUT_FILE" if command -v du &> /dev/null; then diff --git a/make-nuitka.sh b/make-nuitka.sh index 087e953..26c862e 100755 --- a/make-nuitka.sh +++ b/make-nuitka.sh @@ -17,9 +17,6 @@ NUITKA_OPTS=( "--assume-yes-for-downloads" "--enable-plugin=pyside6" ) -CURRENT_HASH="" -STORED_HASH="" -SIZE="" cleanup() { rm -rf "$BASE_FILENAME.build/" "$BASE_FILENAME.dist/" "$BASE_FILENAME.onefile-build/" 2>/dev/null || true @@ -49,14 +46,17 @@ verify_requirements() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) - STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + local current_hash="" + local stored_hash="" - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then + current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) + stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$CURRENT_HASH" > "$REQ_HASH_FILE" + echo "$current_hash" > "$REQ_HASH_FILE" fi } @@ -75,6 +75,8 @@ move_to_bin() { } main() { + local size="" + trap cleanup EXIT check_commands verify_requirements @@ -87,8 +89,8 @@ main() { echo -e "\nBuild successful!" echo -e "Executable: $BIN_DIR/$BASE_FILENAME" if command -v du &> /dev/null; then - SIZE=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: $SIZE" + size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") + echo -e "File size: $size" fi } diff --git a/make-pyinstaller.sh b/make-pyinstaller.sh index 8d2cc75..753513a 100755 --- a/make-pyinstaller.sh +++ b/make-pyinstaller.sh @@ -16,9 +16,6 @@ PYINSTALLER_OPTS=( "--onefile" "--name=volt-gui" ) -CURRENT_HASH="" -STORED_HASH="" -SIZE="" cleanup() { rm -rf dist/ build/ "${SPEC_FILE}" 2>/dev/null || true @@ -48,14 +45,17 @@ verify_requirements() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) - STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + local current_hash="" + local stored_hash="" - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then + current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) + stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$CURRENT_HASH" > "$REQ_HASH_FILE" + echo "$current_hash" > "$REQ_HASH_FILE" fi } @@ -74,6 +74,8 @@ move_to_bin() { } main() { + local size="" + trap cleanup EXIT check_commands verify_requirements @@ -86,8 +88,8 @@ main() { echo -e "\nBuild successful!" echo -e "Executable: $BIN_DIR/$BASE_FILENAME" if command -v du &> /dev/null; then - SIZE=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") - echo -e "File size: $SIZE" + size=$(du -h "$BIN_DIR"/* 2>/dev/null | cut -f1 || echo "Unknown") + echo -e "File size: $size" fi } diff --git a/make-release.sh b/make-release.sh index 112780a..7925a89 100755 --- a/make-release.sh +++ b/make-release.sh @@ -11,12 +11,6 @@ NUITKA_BUILD="volt-gui-nuitka" BUILD_SCRIPTS=("make-pyinstaller.sh" "make-nuitka.sh") APPIMAGE_SCRIPT="make-appimage.sh" ORIGINAL_DIR=$(pwd) -BUILD_SCRIPT="" -TARGET_DIR="" -DIR_NAME="" -BUILD_TYPE="" -APPIMAGE_FILE="" -RENAMED_APPIMAGE="" cleanup() { true @@ -32,51 +26,51 @@ check_commands() { } build_and_copy() { - BUILD_SCRIPT=$1 - TARGET_DIR=$2 - BUILD_TYPE=$3 - - echo -e "${BLUE}Executing build script: $BUILD_SCRIPT${NC}" - if ! (cd "$ORIGINAL_DIR" && ./"$BUILD_SCRIPT"); then - echo -e "${RED}Error: Build script $BUILD_SCRIPT failed${NC}" >&2 + local build_script="$1" + local target_dir="$2" + local build_type="$3" + local appimage_file="volt-gui-x86_64.AppImage" + local renamed_appimage="volt-gui-${build_type}-x86_64.AppImage" + + echo -e "${BLUE}Executing build script: $build_script${NC}" + if ! (cd "$ORIGINAL_DIR" && ./"$build_script"); then + echo -e "${RED}Error: Build script $build_script failed${NC}" >&2 exit 1 fi - echo -e "${BLUE}Building AppImage for $BUILD_TYPE${NC}" + echo -e "${BLUE}Building AppImage for $build_type${NC}" if ! (cd "$ORIGINAL_DIR" && ./"$APPIMAGE_SCRIPT"); then echo -e "${RED}Error: AppImage build failed${NC}" >&2 exit 1 fi - APPIMAGE_FILE="volt-gui-x86_64.AppImage" - RENAMED_APPIMAGE="volt-gui-${BUILD_TYPE}-x86_64.AppImage" - - echo -e "${BLUE}Renaming AppImage to $RENAMED_APPIMAGE${NC}" - if [[ -f "$ORIGINAL_DIR/$APPIMAGE_FILE" ]]; then - mv "$ORIGINAL_DIR/$APPIMAGE_FILE" "$ORIGINAL_DIR/$RENAMED_APPIMAGE" + echo -e "${BLUE}Renaming AppImage to $renamed_appimage${NC}" + if [[ -f "$ORIGINAL_DIR/$appimage_file" ]]; then + mv "$ORIGINAL_DIR/$appimage_file" "$ORIGINAL_DIR/$renamed_appimage" else echo -e "${RED}Error: AppImage file not found${NC}" >&2 exit 1 fi - echo -e "${BLUE}Copying artifacts to $TARGET_DIR${NC}" - mkdir -p "$TARGET_DIR" + echo -e "${BLUE}Copying artifacts to $target_dir${NC}" + mkdir -p "$target_dir" for item in bin install.sh remove.sh; do if [[ -e "$ORIGINAL_DIR/$item" ]]; then - cp -r "$ORIGINAL_DIR/$item" "$TARGET_DIR/" + cp -r "$ORIGINAL_DIR/$item" "$target_dir/" else echo "Warning: $item not found, skipping" fi done echo -e "${BLUE}Moving AppImage to release directory${NC}" - mv "$ORIGINAL_DIR/$RENAMED_APPIMAGE" . + mv "$ORIGINAL_DIR/$renamed_appimage" . } compress_release() { - DIR_NAME=$1 - echo -e "${BLUE}Compressing $DIR_NAME to ${DIR_NAME}.tar.gz${NC}" - tar -czf "${DIR_NAME}.tar.gz" "$DIR_NAME" + local dir_name="$1" + + echo -e "${BLUE}Compressing $dir_name to ${dir_name}.tar.gz${NC}" + tar -czf "${dir_name}.tar.gz" "$dir_name" } main() { diff --git a/test.sh b/test.sh index de48367..1116da8 100755 --- a/test.sh +++ b/test.sh @@ -9,8 +9,6 @@ VENV_DIR="py_env" REQ_FILE="requirements.txt" REQ_HASH_FILE="$VENV_DIR/requirements.sha256" SRC_FILE="src/volt-gui.py" -CURRENT_HASH="" -STORED_HASH="" cleanup() { if [[ -n "${VIRTUAL_ENV:-}" ]]; then @@ -46,14 +44,17 @@ verify_files() { } update_dependencies() { - CURRENT_HASH=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) - STORED_HASH=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + local current_hash="" + local stored_hash="" - if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then + current_hash=$(shasum -a 256 "$REQ_FILE" | cut -d" " -f1) + stored_hash=$(cat "$REQ_HASH_FILE" 2>/dev/null || true) + + if [[ ! -f "$REQ_HASH_FILE" ]] || [[ "$current_hash" != "$stored_hash" ]]; then echo -e "${BLUE}Updating dependencies...${NC}" pip install --upgrade pip pip install --no-cache-dir -r "$REQ_FILE" - echo "$CURRENT_HASH" > "$REQ_HASH_FILE" + echo "$current_hash" > "$REQ_HASH_FILE" else echo "Dependencies are up to date" fi