From 87f94afb4f948aa7f36ff27d3904760943bb9013 Mon Sep 17 00:00:00 2001 From: vjaykrsna Date: Fri, 13 Mar 2026 00:10:56 +0530 Subject: [PATCH 1/2] fix: return None in get_amd_igpu_name when xrandr call fails If xrandr exits with a non-zero code the except block logged a warning but execution fell through to the pattern.findall() call, where xrandr_output was never assigned, causing an immediate NameError crash. Return None explicitly so callers receive the expected sentinel value. --- envycontrol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/envycontrol.py b/envycontrol.py index 320d56e..ba64b69 100755 --- a/envycontrol.py +++ b/envycontrol.py @@ -470,6 +470,7 @@ def get_amd_igpu_name(): except subprocess.CalledProcessError: logging.warning( "Failed to run the 'xrandr' command.") + return None pattern = re.compile(r'(name:).*(ATI*|AMD*|AMD\/ATI)*') From d5df344bbf0a24100240653e849c68e4ef3461eb Mon Sep 17 00:00:00 2001 From: vjaykrsna Date: Fri, 13 Mar 2026 00:11:44 +0530 Subject: [PATCH 2/2] fix: prevent GPU-not-found crash when switching to hybrid mode Two related bugs caused envycontrol to crash with 'Could not find Nvidia GPU' even when switching to a mode that doesn't need the GPU visible: 1. get_current_mode() required BOTH the blacklist file AND the udev removal rule to report 'integrated'. If only the udev rule existed (e.g. after a partial or interrupted previous switch), the function returned 'hybrid', misleading the CachedConfig adapter. Fix: any single EnvyControl-specific integrated-mode file is now sufficient to report integrated mode, since all three paths (/etc/modprobe.d/blacklist-nvidia.conf, /etc/udev/rules.d/50-remove-nvidia.rules, /lib/udev/rules.d/50-remove-nvidia.rules) are written exclusively by envycontrol for that mode. 2. CachedConfig.adapter() called create_cache_file() whenever the current mode was detected as hybrid, including when the requested switch target was also hybrid. create_cache_file() calls get_nvidia_gpu_pci_bus() which parses lspci output; if a stale udev removal rule was hiding the NVIDIA device the function called sys.exit(1), aborting before cleanup() ever ran. Fix: skip cache creation when the target switch is 'hybrid', since hybrid mode never consumes the cached PCI bus value. --- envycontrol.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/envycontrol.py b/envycontrol.py index ba64b69..038e78b 100755 --- a/envycontrol.py +++ b/envycontrol.py @@ -647,7 +647,11 @@ def adapter(self): global get_nvidia_gpu_pci_bus use_cache = os.path.exists(CACHE_FILE_PATH) - if self.is_hybrid(): # recreate cache file when in hybrid mode + # Only cache the GPU PCI bus when leaving hybrid mode. + # Switching *to* hybrid never needs the cached value and the GPU + # may not be visible via lspci if old udev removal rules are still + # in place, which would make create_cache_file() exit early. + if self.is_hybrid() and self.app_args.switch != 'hybrid': self.create_cache_file() if use_cache: @@ -717,7 +721,9 @@ def write_cache_file(self): def get_current_mode(): mode = 'hybrid' - if os.path.exists(BLACKLIST_PATH) and (os.path.exists(UDEV_INTEGRATED_PATH) or os.path.exists('/lib/udev/rules.d/50-remove-nvidia.rules')): + if (os.path.exists(BLACKLIST_PATH) or + os.path.exists(UDEV_INTEGRATED_PATH) or + os.path.exists('/lib/udev/rules.d/50-remove-nvidia.rules')): mode = 'integrated' elif os.path.exists(XORG_PATH) and os.path.exists(MODESET_PATH): mode = 'nvidia'