From c0481b114912290cf9fbfd57e034953ae2a29e64 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Wed, 11 Mar 2026 23:36:57 +0545 Subject: [PATCH] refactor(root): remove legacy lapctl.py and update roadmap status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The removed file contained outdated GPU power‑management integration now handled elsewhere; updating the README marks the associated tasks as completed. Enhancements: - Marked CLI structure, GPU mode switching, and hardware status command as completed. --- .gitignore | 1 + README.md | 6 +- lapctl.py | 815 ----------------------------------------------------- 3 files changed, 4 insertions(+), 818 deletions(-) delete mode 100644 lapctl.py diff --git a/.gitignore b/.gitignore index 29db83b..1a22800 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ target /target ./lapctl.py +lapctl.py diff --git a/README.md b/README.md index d4ff49c..b877720 100644 --- a/README.md +++ b/README.md @@ -93,10 +93,10 @@ lapctl ## Roadmap -- [ ] CLI structure -- [ ] GPU mode switching +- [x] CLI structure +- [x] GPU mode switching - [ ] Battery charge limit management -- [ ] Hardware status command +- [x] Hardware status command - [ ] Configuration support --- diff --git a/lapctl.py b/lapctl.py deleted file mode 100644 index 4af4949..0000000 --- a/lapctl.py +++ /dev/null @@ -1,815 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import logging -import os -import re -import shutil -import subprocess -import sys -from contextlib import contextmanager - -# begin constants definition - -VERSION = "3.5.2" - -# Note: Do NOT remove this in cleanup! -CACHE_FILE_PATH = "/var/cache/lapctl/cache.json" - -BLACKLIST_PATH = "/etc/modprobe.d/blacklist-nvidia.conf" - -BLACKLIST_CONTENT = """# Automatically generated by lapctl - -blacklist nouveau -blacklist nova_core -blacklist nova_drm -blacklist nvidia -blacklist nvidia_drm -blacklist nvidia_uvm -blacklist nvidia_modeset -blacklist nvidia_current -blacklist nvidia_current_drm -blacklist nvidia_current_uvm -blacklist nvidia_current_modeset -blacklist i2c_nvidia_gpu -alias nouveau off -alias nova_core off -alias nova_drm off -alias nvidia off -alias nvidia_drm off -alias nvidia_uvm off -alias nvidia_modeset off -alias nvidia_current off -alias nvidia_current_drm off -alias nvidia_current_uvm off -alias nvidia_current_modeset off -alias i2c_nvidia_gpu off -""" - -UDEV_INTEGRATED_PATH = "/etc/udev/rules.d/50-remove-nvidia.rules" - -UDEV_INTEGRATED = """# Automatically generated by lapctl - -# Remove NVIDIA USB xHCI Host Controller devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{power/control}="auto", ATTR{remove}="1" - -# Remove NVIDIA USB Type-C UCSI devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{power/control}="auto", ATTR{remove}="1" - -# Remove NVIDIA Audio devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{power/control}="auto", ATTR{remove}="1" - -# Remove NVIDIA VGA/3D controller devices -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", ATTR{power/control}="auto", ATTR{remove}="1" -""" - -UDEV_PM_PATH = "/etc/udev/rules.d/80-nvidia-pm.rules" - -UDEV_PM_CONTENT = """# Automatically generated by lapctl - -# Remove NVIDIA USB xHCI Host Controller devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1" - -# Remove NVIDIA USB Type-C UCSI devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1" - -# Remove NVIDIA Audio devices, if present -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1" - -# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind -ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto" - -# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind -ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on" -ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on" -""" - -XORG_PATH = "/etc/X11/xorg.conf" - -XORG_INTEL = """# Automatically generated by lapctl - -Section "ServerLayout" - Identifier "layout" - Screen 0 "nvidia" - Inactive "intel" -EndSection - -Section "Device" - Identifier "nvidia" - Driver "nvidia" - BusID "{}" -EndSection - -Section "Screen" - Identifier "nvidia" - Device "nvidia" - Option "AllowEmptyInitialConfiguration" -EndSection - -Section "Device" - Identifier "intel" - Driver "modesetting" -EndSection - -Section "Screen" - Identifier "intel" - Device "intel" -EndSection -""" - -XORG_AMD = """# Automatically generated by lapctl - -Section "ServerLayout" - Identifier "layout" - Screen 0 "nvidia" - Inactive "amdgpu" -EndSection - -Section "Device" - Identifier "nvidia" - Driver "nvidia" - BusID "{}" -EndSection - -Section "Screen" - Identifier "nvidia" - Device "nvidia" - Option "AllowEmptyInitialConfiguration" -EndSection - -Section "Device" - Identifier "amdgpu" - Driver "amdgpu" -EndSection - -Section "Screen" - Identifier "amd" - Device "amdgpu" -EndSection -""" - -EXTRA_XORG_PATH = "/etc/X11/xorg.conf.d/10-nvidia.conf" - -EXTRA_XORG_CONTENT = """# Automatically generated by lapctl - -Section "OutputClass" - Identifier "nvidia" - MatchDriver "nvidia-drm" - Driver "nvidia" -""" - -FORCE_COMP = ' Option "ForceCompositionPipeline" "true"\n' - -COOLBITS = ' Option "Coolbits" "{}"\n' - -MODESET_PATH = "/etc/modprobe.d/nvidia.conf" - -MODESET_CONTENT = """# Automatically generated by lapctl - -options nvidia-drm modeset=1 -options nvidia NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0 -""" - -MODESET_CURRENT_CONTENT = """# Automatically generated by lapctl - -options nvidia-current-drm modeset=1 -options nvidia-current NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0 -""" - -MODESET_RTD3 = """# Automatically generated by lapctl - -options nvidia-drm modeset=1 -options nvidia "NVreg_DynamicPowerManagement=0x0{}" -options nvidia NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0 -""" - -MODESET_CURRENT_RTD3 = """# Automatically generated by lapctl - -options nvidia-current-drm modeset=1 -options nvidia-current "NVreg_DynamicPowerManagement=0x0{}" -options nvidia-current NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0 -""" - -SDDM_XSETUP_PATH = "/usr/share/sddm/scripts/Xsetup" - -SDDM_XSETUP_CONTENT = """#!/bin/sh -# Xsetup - run as root before the login dialog appears - -""" - -LIGHTDM_SCRIPT_PATH = "/etc/lightdm/nvidia.sh" - -LIGHTDM_CONFIG_PATH = "/etc/lightdm/lightdm.conf.d/20-nvidia.conf" - -LIGHTDM_CONFIG_CONTENT = """# Automatically generated by lapctl - -[Seat:*] -display-setup-script=/etc/lightdm/nvidia.sh -""" - -NVIDIA_XRANDR_SCRIPT = """#!/bin/sh -# Automatically generated by lapctl - -current="" - -xrandr --setprovideroutputsource "{}" NVIDIA-0 -xrandr --auto - -for next in $(xrandr --listmonitors | grep -E " *[0-9]+:.*" | cut -d" " -f6); do - [ -z "$current" ] && current=$next && continue - xrandr --output "$current" --auto --output "$next" --auto --right-of "$current" - current=$next -done -""" - -SUPPORTED_MODES = ["integrated", "hybrid", "nvidia"] -SUPPORTED_DISPLAY_MANAGERS = ["gdm", "gdm3", "sddm", "lightdm"] -RTD3_MODES = [0, 1, 2, 3] - -# end constants definition - - -def graphics_mode_switcher( - graphics_mode, - user_display_manager, - enable_force_comp, - coolbits_value, - rtd3_value, - use_nvidia_current, -): - print(f"Switching to {graphics_mode} mode") - - if graphics_mode == "integrated": - if logging.getLogger().level == logging.DEBUG: - service = subprocess.run( - ["systemctl", "disable", "nvidia-persistenced.service"] - ) - else: - service = subprocess.run( - ["systemctl", "disable", "nvidia-persistenced.service"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - if service.returncode == 0: - print("Successfully disabled nvidia-persistenced.service") - else: - logging.error("An error ocurred while disabling service") - - cleanup() - - # blacklist all nouveau, nova and Nvidia modules - create_file(BLACKLIST_PATH, BLACKLIST_CONTENT) - - # power off the Nvidia GPU with udev rules - create_file(UDEV_INTEGRATED_PATH, UDEV_INTEGRATED) - - rebuild_initramfs() - elif graphics_mode == "hybrid": - print( - f"Enable PCI-Express Runtime D3 (RTD3) Power Management: {rtd3_value or False}" - ) - cleanup() - - if logging.getLogger().level == logging.DEBUG: - service = subprocess.run( - ["systemctl", "enable", "nvidia-persistenced.service"] - ) - else: - service = subprocess.run( - ["systemctl", "enable", "nvidia-persistenced.service"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - if service.returncode == 0: - print("Successfully enabled nvidia-persistenced.service") - else: - logging.error("An error ocurred while enabling service") - - if rtd3_value == None: - if use_nvidia_current: - create_file(MODESET_PATH, MODESET_CURRENT_CONTENT) - else: - create_file(MODESET_PATH, MODESET_CONTENT) - else: - # setup rtd3 - if use_nvidia_current: - create_file(MODESET_PATH, MODESET_CURRENT_RTD3.format(rtd3_value)) - else: - create_file(MODESET_PATH, MODESET_RTD3.format(rtd3_value)) - create_file(UDEV_PM_PATH, UDEV_PM_CONTENT) - - rebuild_initramfs() - elif graphics_mode == "nvidia": - print(f"Enable ForceCompositionPipeline: {enable_force_comp}") - print(f"Enable Coolbits: {coolbits_value or False}") - - if logging.getLogger().level == logging.DEBUG: - service = subprocess.run( - ["systemctl", "enable", "nvidia-persistenced.service"] - ) - else: - service = subprocess.run( - ["systemctl", "enable", "nvidia-persistenced.service"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - if service.returncode == 0: - print("Successfully enabled nvidia-persistenced.service") - else: - logging.error("An error ocurred while enabling service") - - cleanup() - # get the Nvidia dGPU PCI bus - nvidia_gpu_pci_bus = get_nvidia_gpu_pci_bus() - - # get iGPU vendor - igpu_vendor = get_igpu_vendor() - - # create the X.org config - if igpu_vendor == "intel": - create_file(XORG_PATH, XORG_INTEL.format(nvidia_gpu_pci_bus)) - elif igpu_vendor == "amd": - create_file(XORG_PATH, XORG_AMD.format(nvidia_gpu_pci_bus)) - - # enable modeset for Nvidia driver - if use_nvidia_current: - create_file(MODESET_PATH, MODESET_CURRENT_CONTENT) - else: - create_file(MODESET_PATH, MODESET_CONTENT) - - # extra Xorg config - if enable_force_comp and coolbits_value != None: - create_file( - EXTRA_XORG_PATH, - EXTRA_XORG_CONTENT - + FORCE_COMP - + COOLBITS.format(coolbits_value) - + "EndSection\n", - ) - elif enable_force_comp: - create_file( - EXTRA_XORG_PATH, EXTRA_XORG_CONTENT + FORCE_COMP + "EndSection\n" - ) - elif coolbits_value != None: - create_file( - EXTRA_XORG_PATH, - EXTRA_XORG_CONTENT + COOLBITS.format(coolbits_value) + "EndSection\n", - ) - - # try to detect the display manager if not provided - if user_display_manager == None: - display_manager = get_display_manager() - else: - display_manager = user_display_manager - - # only sddm and lightdm require further config - if display_manager == "sddm": - # backup Xsetup - if os.path.exists(SDDM_XSETUP_PATH): - logging.info("Creating Xsetup backup") - with open(SDDM_XSETUP_PATH, mode="r", encoding="utf-8") as f: - create_file(SDDM_XSETUP_PATH + ".bak", f.read()) - create_file(SDDM_XSETUP_PATH, generate_xrandr_script(igpu_vendor), True) - elif display_manager == "lightdm": - create_file(LIGHTDM_SCRIPT_PATH, generate_xrandr_script(igpu_vendor), True) - create_file(LIGHTDM_CONFIG_PATH, LIGHTDM_CONFIG_CONTENT) - - rebuild_initramfs() - print("Operation completed successfully") - print("Please reboot your computer for changes to take effect!") - - -def cleanup(): - # define list of files to remove - to_remove = [ - BLACKLIST_PATH, - UDEV_INTEGRATED_PATH, - UDEV_PM_PATH, - XORG_PATH, - EXTRA_XORG_PATH, - MODESET_PATH, - LIGHTDM_SCRIPT_PATH, - LIGHTDM_CONFIG_PATH, - # legacy files - "/etc/X11/xorg.conf.d/90-nvidia.conf", - "/lib/udev/rules.d/50-remove-nvidia.rules", - "/lib/udev/rules.d/80-nvidia-pm.rules", - ] - - # remove each file in the list - for file_path in to_remove: - try: - if os.path.exists(file_path): - os.remove(file_path) - logging.info(f"Removed file {file_path}") - except OSError as e: - # only warn if file exists (code 2) - if e.errno != 2: - logging.error(f"Failed to remove file '{file_path}': {e}") - - # restore Xsetup backup if found - backup_path = SDDM_XSETUP_PATH + ".bak" - if os.path.exists(backup_path): - logging.info("Restoring Xsetup backup") - with open(backup_path, mode="r", encoding="utf-8") as f: - create_file(SDDM_XSETUP_PATH, f.read()) - # remove backup - os.remove(backup_path) - logging.info(f"Removed file {backup_path}") - - -def get_nvidia_gpu_pci_bus(): - lspci_output = subprocess.check_output(["lspci"]).decode("utf-8") - for line in lspci_output.splitlines(): - if "NVIDIA" in line and ( - "VGA compatible controller" in line or "3D controller" in line - ): - # remove leading zeros - pci_bus_id = line.split()[0].replace("0000:", "") - logging.info(f"Found Nvidia GPU at {pci_bus_id}") - break - else: - logging.error("Could not find Nvidia GPU") - print("Try switching to hybrid mode first!") - sys.exit(1) - - # need to return the BusID in 'PCI:bus:device:function' format - # also perform hexadecimal to decimal conversion - bus, device_function = pci_bus_id.split(":") - device, function = device_function.split(".") - return f"PCI:{int(bus, 16)}:{int(device, 16)}:{int(function, 16)}" - - -def get_igpu_vendor(): - lspci_output = subprocess.check_output(["lspci"]).decode("utf-8") - for line in lspci_output.splitlines(): - if "VGA compatible controller" in line or "Display controller" in line: - if "Intel" in line: - logging.info("Found Intel iGPU") - return "intel" - elif "ATI" in line or "AMD" in line or "AMD/ATI" in line: - logging.info("Found AMD iGPU") - return "amd" - logging.warning("Could not find Intel or AMD iGPU") - return None - - -def get_display_manager(): - try: - with open( - "/etc/systemd/system/display-manager.service", "r", encoding="utf-8" - ) as f: - content = f.read() - match = re.search(r"ExecStart=(.+)\n", content) - if match: - # only return the final component of the path - display_manager = os.path.basename(match.group(1)) - logging.info(f"Found {display_manager} Display Manager") - return display_manager - except FileNotFoundError: - logging.warning("Display Manager detection is not available") - - -def generate_xrandr_script(igpu_vendor): - if igpu_vendor == "intel": - return NVIDIA_XRANDR_SCRIPT.format("modesetting") - elif igpu_vendor == "amd": - amd_igpu_name = get_amd_igpu_name() - if amd_igpu_name != None: - return NVIDIA_XRANDR_SCRIPT.format(amd_igpu_name) - else: - return NVIDIA_XRANDR_SCRIPT.format("modesetting") - else: - return NVIDIA_XRANDR_SCRIPT.format("modesetting") - - -def get_amd_igpu_name(): - if not os.path.exists("/usr/bin/xrandr"): - logging.warning( - "The 'xrandr' command is not available. Make sure the package is installed!" - ) - return None - - try: - xrandr_output = subprocess.check_output(["xrandr", "--listproviders"]).decode( - "utf-8" - ) - except subprocess.CalledProcessError: - logging.warning("Failed to run the 'xrandr' command.") - - pattern = re.compile(r"(name:).*(ATI*|AMD*|AMD\/ATI)*") - - if pattern.findall(xrandr_output): - return re.search(pattern, xrandr_output).group(0)[5:] - else: - logging.warning("Could not find AMD iGPU in 'xrandr' output.") - return None - - -def rebuild_initramfs(): - # OSTree systems first - if any(os.path.exists(dir) for dir in ["/ostree", "/sysroot/ostree"]): - print("Rebuilding the initramfs with rpm-ostree...") - command = ["rpm-ostree", "initramfs", "--enable", "--arg=--force"] - - # Debian and Ubuntu derivatives - elif os.path.exists("/etc/debian_version"): - command = ["update-initramfs", "-u", "-k", "all"] - # RHEL and SUSE derivatives - elif os.path.exists("/etc/redhat-release") or os.path.exists("/usr/bin/zypper"): - command = ["dracut", "--force", "--regenerate-all"] - # EndeavourOS with dracut - elif os.path.exists("/usr/lib/endeavouros-release") and os.path.exists( - "/usr/bin/dracut" - ): - command = ["dracut-rebuild"] - # ALT Linux - elif os.path.exists("/etc/altlinux-release"): - command = ["make-initrd"] - # Arch Linux - elif os.path.exists("/etc/arch-release"): - command = ["mkinitcpio", "-P"] - else: - command = [] - - if shutil.which("systemd-inhibit"): - command = [ - "systemd-inhibit", - "--who=lapctl", - "--why", - "Rebuilding initramfs", - "--", - *command, - ] - - if len(command) != 0: - print("Rebuilding the initramfs...") - if logging.getLogger().level == logging.DEBUG: - p = subprocess.run(command) - else: - p = subprocess.run( - command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) - if p.returncode == 0: - print("Successfully rebuilt the initramfs!") - else: - logging.error("An error ocurred while rebuilding the initramfs") - - -def create_file(path, content, executable=False): - try: - # create the parent folders if needed - if not os.path.exists(os.path.dirname(path)): - os.makedirs(os.path.dirname(path)) - with open(path, mode="w", encoding="utf-8") as f: - f.write(content) - logging.info(f"Created file {path}") - if logging.getLogger().level == logging.DEBUG: - print(content) - - # add execution privilege - if executable: - subprocess.run(["chmod", "+x", path], stdout=subprocess.DEVNULL) - logging.info(f"Added execution privilege to file {path}") - except OSError as e: - logging.error(f"Failed to create file '{path}': {e}") - - -def assert_root(): - if os.geteuid() != 0: - logging.error("This operation requires root privileges") - sys.exit(1) - - -def main(): - # define CLI arguments - parser = argparse.ArgumentParser() - parser.add_argument( - "-v", - "--version", - action="version", - version=VERSION, - help="Output the current version", - ) - parser.add_argument( - "-q", "--query", action="store_true", help="Query the current graphics mode" - ) - parser.add_argument( - "-s", - "--switch", - type=str, - metavar="MODE", - action="store", - choices=SUPPORTED_MODES, - help="Switch the graphics mode. Available choices: %(choices)s", - ) - parser.add_argument( - "--dm", - type=str, - metavar="DISPLAY_MANAGER", - action="store", - choices=SUPPORTED_DISPLAY_MANAGERS, - help="Manually specify your Display Manager for Nvidia mode. Available choices: %(choices)s", - ) - parser.add_argument( - "--force-comp", - action="store_true", - help="Enable ForceCompositionPipeline on Nvidia mode", - ) - parser.add_argument( - "--coolbits", - type=int, - nargs="?", - metavar="VALUE", - action="store", - const=28, - help="Enable Coolbits on Nvidia mode. Default if specified: %(const)s", - ) - parser.add_argument( - "--rtd3", - type=int, - nargs="?", - metavar="VALUE", - action="store", - choices=RTD3_MODES, - const=2, - help="Setup PCI-Express Runtime D3 (RTD3) Power Management on Hybrid mode. Available choices: %(choices)s. Default if specified: %(const)s", - ) - parser.add_argument( - "--use-nvidia-current", - action="store_true", - help="Use nvidia-current instead of nvidia for kernel modules", - ) - parser.add_argument( - "--reset-sddm", action="store_true", help="Restore default Xsetup file" - ) - parser.add_argument( - "--reset", action="store_true", help="Revert changes made by lapctl" - ) - parser.add_argument( - "--cache-create", - action="store_true", - help="Create cache used by lapctl; only works in hybrid mode", - ) - parser.add_argument( - "--cache-delete", - action="store_true", - help="Delete cache created by lapctl", - ) - parser.add_argument( - "--cache-query", action="store_true", help="Show cache created by lapctl" - ) - parser.add_argument( - "--verbose", default=False, action="store_true", help="Enable verbose mode" - ) - - # print help if no arg is provided - if len(sys.argv) == 1: - parser.print_help() - sys.exit(1) - - args = parser.parse_args() - - # log formatting - logging.basicConfig(format="%(levelname)s: %(message)s") - - # set debug level for verbose mode - if args.verbose: - logging.getLogger().setLevel(logging.DEBUG) - - if args.query: - mode = get_current_mode() - print(mode) - return - elif args.cache_create: - assert_root() - CachedConfig(args).create_cache_file() - return - elif args.cache_delete: - assert_root() - CachedConfig.delete_cache_file() - return - elif args.cache_query: - CachedConfig.show_cache_file() - return - - if args.switch or args.reset_sddm or args.reset: - with CachedConfig(args).adapter(): - if args.switch: - assert_root() - graphics_mode_switcher( - args.switch, - args.dm, - args.force_comp, - args.coolbits, - args.rtd3, - args.use_nvidia_current, - ) - elif args.reset_sddm: - assert_root() - create_file(SDDM_XSETUP_PATH, SDDM_XSETUP_CONTENT, True) - print("Operation completed successfully") - elif args.reset: - assert_root() - cleanup() - CachedConfig.delete_cache_file() - rebuild_initramfs() - print("Operation completed successfully") - - -class CachedConfig: - """Adapter for config from CACHE_FILE_PATH""" - - def __init__(self, app_args) -> None: - self.app_args = app_args - self.current_mode = get_current_mode() - - @contextmanager - 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 - self.create_cache_file() - - if use_cache: - self.read_cache_file() # might not be in hybrid mode - - # rebind function to use cached value instead of detection - get_nvidia_gpu_pci_bus = self.get_nvidia_gpu_pci_bus - - yield # back to main ... - - def create_cache_file(self): - if not self.is_hybrid(): - raise ValueError( - "--cache-create requires that the system be in the hybrid Optimus mode" - ) - - self.nvidia_gpu_pci_bus = get_nvidia_gpu_pci_bus() - self.obj = self.create_cache_obj(self.nvidia_gpu_pci_bus) - self.write_cache_file() - - def create_cache_obj(self, nvidia_gpu_pci_bus): - return {"nvidia_gpu_pci_bus": nvidia_gpu_pci_bus} - - def is_hybrid(self): - return "hybrid" == self.current_mode - - def get_nvidia_gpu_pci_bus(self): - return self.nvidia_gpu_pci_bus - - @staticmethod - def delete_cache_file(): - os.remove(CACHE_FILE_PATH) - os.removedirs(os.path.dirname(CACHE_FILE_PATH)) - logging.debug(f"Removed file {CACHE_FILE_PATH}") - - def read_cache_file(self): - from json import loads - - if os.path.exists(CACHE_FILE_PATH): - with open(CACHE_FILE_PATH, "r", encoding="utf-8") as f: - content = f.read() - self.obj = loads(content) - self.nvidia_gpu_pci_bus = self.obj["nvidia_gpu_pci_bus"] - elif self.is_hybrid(): - self.nvidia_gpu_pci_bus = get_nvidia_gpu_pci_bus() - else: - raise ValueError( - "No cache present. Operation requires that the system be in the hybrid Optimus mode" - ) - - @staticmethod - def show_cache_file(): - content = f"ERROR: Could not read {CACHE_FILE_PATH}" - if os.path.exists(CACHE_FILE_PATH): - with open(CACHE_FILE_PATH, "r", encoding="utf-8") as f: - content = f.read() - print(content) - - def write_cache_file(self): - from json import dump - - os.makedirs(os.path.dirname(CACHE_FILE_PATH), exist_ok=True) - - with open(CACHE_FILE_PATH, "w", encoding="utf-8") as f: - dump(self.obj, fp=f, indent=4, sort_keys=False) - - logging.debug(f"Created file {CACHE_FILE_PATH}") - - -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") - ): - mode = "integrated" - elif os.path.exists(XORG_PATH) and os.path.exists(MODESET_PATH): - mode = "nvidia" - return mode - - -if __name__ == "__main__": - main()