├── .gitignore ├── LICENSE ├── README.md ├── envycontrol.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Victor Bayas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EnvyControl 2 | 3 | EnvyControl is a program aimed to provide an easy way to switch GPU modes on Nvidia Optimus systems (i.e laptops with Intel + Nvidia or AMD + Nvidia configurations) under Linux. 4 | 5 | ### License 6 | 7 | Envycontrol is licensed under the MIT license which is a permissive, free software license (see LICENSE). 8 | 9 | ### Compatible distros 10 | 11 | EnvyControl should work on any distribution of Linux, see [tested distros](https://github.com/geminis3/envycontrol/wiki/Frequently-Asked-Questions#tested-distros). 12 | 13 | **If you're using Ubuntu please [read this](https://github.com/geminis3/envycontrol/wiki/Frequently-Asked-Questions#a-note-for-ubuntu-users).** 14 | 15 | ### Supported display managers 16 | 17 | - GDM 18 | - SDDM 19 | - LightDM 20 | 21 | If your display manager isn't currently supported by EnvyControl you might have to [manually configure it](https://github.com/geminis3/envycontrol/wiki/Frequently-Asked-Questions#what-to-do-if-my-display-manager-is-not-supported). 22 | 23 | ### A note for SDDM users 24 | 25 | If `/usr/share/sddm/scripts/Xsetup` file is missing on your system please run `sudo envycontrol --reset_sddm`. 26 | 27 | ### Supported graphics modes 28 | 29 | - integrated 30 | - hybrid 31 | - nvidia (X.org only) 32 | 33 | Read a detailed explanation [here](https://github.com/geminis3/envycontrol/wiki/Frequently-Asked-Questions#graphics-modes-explained). 34 | 35 | ## Get EnvyControl 36 | 37 | ### Arch Linux and its derivatives 38 | 39 | Install the [envycontrol](https://aur.archlinux.org/packages/envycontrol/) package from the AUR manually or by using an AUR helper: 40 | 41 | ``` 42 | # with Paru 43 | paru -S envycontrol 44 | 45 | # with Yay 46 | yay -S envycontrol 47 | 48 | # with Pamac (Manjaro) 49 | pamac install envycontrol 50 | ``` 51 | 52 | Now you can run `sudo envycontrol -s ` to switch graphics modes. 53 | 54 | ### Other distros 55 | 56 | - Clone this repository with `git clone https://github.com/geminis3/envycontrol.git` or download the latest tarball from the releases page. 57 | - Run `sudo python envycontrol.py -s ` from the root of the repository to switch to a different graphics mode. 58 | 59 | You can also install EnvyControl globally as a pip package: 60 | 61 | - From the root of the cloned repository run `sudo pip install .` 62 | - Now you can run `sudo envycontrol -s ` from any directory to switch graphics modes. 63 | 64 | ## Usage 65 | 66 | ``` 67 | usage: envycontrol.py [-h] [-v] [-s MODE] [-q] [--dm DISPLAY_MANAGER] [--reset_sddm] 68 | 69 | options: 70 | -h, --help show this help message and exit 71 | -v, --version show this program's version number and exit 72 | -s MODE, --switch MODE 73 | switch the graphics mode, supported modes: integrated, hybrid, nvidia 74 | -q, --query query the current graphics mode set by EnvyControl 75 | --dm DISPLAY_MANAGER Manually specify your Display Manager. This is required only for systems without systemd. Supported DMs: gdm, sddm, lightdm 76 | --reset_sddm restore original SDDM Xsetup file 77 | ``` 78 | 79 | ### Examples 80 | 81 | Set current graphics mode to `integrated` (power off the Nvidia dGPU): 82 | 83 | ``` 84 | sudo envycontrol -s integrated 85 | ``` 86 | 87 | Set current graphics mode to `nvidia` (automatic display manager setup) 88 | 89 | ``` 90 | sudo envycontrol -s nvidia 91 | ``` 92 | 93 | Set current graphics mode to `nvidia` and setup `SDDM` display manager 94 | 95 | ``` 96 | sudo envycontrol -s nvidia --dm sddm 97 | ``` 98 | 99 | Query the current graphics mode: 100 | 101 | ``` 102 | envycontrol --query 103 | ``` 104 | 105 | ### Gnome Extension 106 | 107 | The [GPU profile selector](https://github.com/LorenzoMorelli/GPU_profile_selector) extension provides a simple way to switch between graphics modes in a few clicks, you can get it from [here](https://extensions.gnome.org/extension/5009/gpu-profile-selector/). 108 | 109 | PD: Just make sure to have EnvyControl installed globally ;) 110 | 111 | ![gpu profile selector screenshot](https://github.com/LorenzoMorelli/GPU_profile_selector/raw/main/extension_screenshot.png) 112 | 113 | ## New in 2.0 114 | 115 | The following options can now be enabled when switching graphics mode: 116 | 117 | ### hybrid 118 | 119 | - RTD3 power management (for Turing and newer GPUs) 120 | 121 | ### nvidia 122 | 123 | - ForceCompositionPipeline (fixes tearing on external screens wired to the Nvidia GPU) 124 | - Coolbits (allows overclocking on supported GPUs) 125 | 126 | ## Frequently Asked Questions 127 | 128 | [See here](https://github.com/geminis3/envycontrol/wiki/Frequently-Asked-Questions). 129 | 130 | Also read [fixes for some common problems](https://github.com/DaVikingMan/EnvyControl/wiki/Fixes-for-some-common-problems) 131 | 132 | ## What to do if you have found a bug 133 | 134 | Feel free to open an issue, don't forget to provide some basic info. 135 | 136 | - Linux distribution 137 | - Linux kernel version and type 138 | - Desktop Environment or Window Manager as well as your Display Manager 139 | - Nvidia driver version 140 | - EnvyControl version 141 | -------------------------------------------------------------------------------- /envycontrol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import sys 4 | import os 5 | import re 6 | import subprocess 7 | 8 | VERSION = '2.1.1' 9 | 10 | BLACKLIST_PATH = '/etc/modprobe.d/blacklist-nvidia.conf' 11 | 12 | BLACKLIST_CONTENT = '''# Automatically generated by EnvyControl 13 | 14 | blacklist nouveau 15 | blacklist nvidia 16 | blacklist nvidia_drm 17 | blacklist nvidia_uvm 18 | blacklist nvidia_modeset 19 | alias nouveau off 20 | alias nvidia off 21 | alias nvidia_drm off 22 | alias nvidia_uvm off 23 | alias nvidia_modeset off 24 | ''' 25 | 26 | UDEV_INTEGRATED_PATH = '/lib/udev/rules.d/50-remove-nvidia.rules' 27 | 28 | UDEV_INTEGRATED = '''# Automatically generated by EnvyControl 29 | 30 | # Remove NVIDIA USB xHCI Host Controller devices, if present 31 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{power/control}="auto", ATTR{remove}="1" 32 | 33 | # Remove NVIDIA USB Type-C UCSI devices, if present 34 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{power/control}="auto", ATTR{remove}="1" 35 | 36 | # Remove NVIDIA Audio devices, if present 37 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{power/control}="auto", ATTR{remove}="1" 38 | 39 | # Remove NVIDIA VGA/3D controller devices 40 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", ATTR{power/control}="auto", ATTR{remove}="1" 41 | ''' 42 | 43 | UDEV_PM_PATH = '/lib/udev/rules.d/80-nvidia-pm.rules' 44 | 45 | UDEV_PM = '''# Automatically generated by EnvyControl 46 | 47 | # Remove NVIDIA USB xHCI Host Controller devices, if present 48 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1" 49 | 50 | # Remove NVIDIA USB Type-C UCSI devices, if present 51 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1" 52 | 53 | # Remove NVIDIA Audio devices, if present 54 | ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1" 55 | 56 | # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind 57 | ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto" 58 | ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto" 59 | 60 | # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind 61 | ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on" 62 | ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on" 63 | ''' 64 | 65 | XORG_PATH = '/etc/X11/xorg.conf' 66 | 67 | XORG_INTEL = '''# Automatically generated by EnvyControl 68 | 69 | Section "ServerLayout" 70 | Identifier "layout" 71 | Screen 0 "nvidia" 72 | Inactive "intel" 73 | EndSection 74 | 75 | Section "Device" 76 | Identifier "nvidia" 77 | Driver "nvidia" 78 | BusID "PCI:{}" 79 | EndSection 80 | 81 | Section "Screen" 82 | Identifier "nvidia" 83 | Device "nvidia" 84 | Option "AllowEmptyInitialConfiguration" 85 | EndSection 86 | 87 | Section "Device" 88 | Identifier "intel" 89 | Driver "modesetting" 90 | EndSection 91 | 92 | Section "Screen" 93 | Identifier "intel" 94 | Device "intel" 95 | EndSection 96 | ''' 97 | 98 | XORG_AMD = '''# Automatically generated by EnvyControl 99 | 100 | Section "ServerLayout" 101 | Identifier "layout" 102 | Screen 0 "nvidia" 103 | Inactive "amdgpu" 104 | EndSection 105 | 106 | Section "Device" 107 | Identifier "nvidia" 108 | Driver "nvidia" 109 | BusID "PCI:{}" 110 | EndSection 111 | 112 | Section "Screen" 113 | Identifier "nvidia" 114 | Device "nvidia" 115 | Option "AllowEmptyInitialConfiguration" 116 | EndSection 117 | 118 | Section "Device" 119 | Identifier "amdgpu" 120 | Driver "amdgpu" 121 | EndSection 122 | 123 | Section "Screen" 124 | Identifier "amd" 125 | Device "amdgpu" 126 | EndSection 127 | ''' 128 | 129 | EXTRA_PATH = '/etc/X11/xorg.conf.d/10-nvidia.conf' 130 | 131 | EXTRA_CONTENT = '''# Automatically generated by EnvyControl 132 | 133 | Section "OutputClass" 134 | Identifier "nvidia" 135 | MatchDriver "nvidia-drm" 136 | Driver "nvidia" 137 | ''' 138 | 139 | TEARING_FIX = f' Option "ForceCompositionPipeline" "true"\n' 140 | 141 | COOLBITS = f' Option "Coolbits" "28"\n' 142 | 143 | MODESET_PATH = '/etc/modprobe.d/nvidia.conf' 144 | 145 | MODESET_CONTENT = '''# Automatically generated by EnvyControl 146 | 147 | options nvidia-drm modeset=1 148 | ''' 149 | 150 | MODESET_PM = '''# Automatically generated by EnvyControl 151 | 152 | options nvidia-drm modeset=1 153 | options nvidia "NVreg_DynamicPowerManagement=0x02" 154 | ''' 155 | 156 | SDDM_XSETUP_PATH = '/usr/share/sddm/scripts/Xsetup' 157 | 158 | SDDM_XSETUP_CONTENT = '''#!/bin/sh 159 | # Xsetup - run as root before the login dialog appears 160 | 161 | ''' 162 | 163 | LIGHTDM_SCRIPT_PATH = '/etc/lightdm/nvidia.sh' 164 | 165 | LIGHTDM_CONFIG_PATH = '/etc/lightdm/lightdm.conf.d/20-nvidia.conf' 166 | 167 | LIGHTDM_CONFIG_CONTENT = '''# Automatically generated by EnvyControl 168 | 169 | [Seat:*] 170 | display-setup-script=/etc/lightdm/nvidia.sh 171 | ''' 172 | 173 | NVIDIA_XRANDR_SCRIPT = '''#!/bin/sh 174 | # Automatically generated by EnvyControl 175 | 176 | xrandr --setprovideroutputsource "{}" NVIDIA-0 177 | xrandr --auto 178 | ''' 179 | 180 | def _switcher(mode, display_manager = ''): 181 | _check_root() 182 | yes = ('yes', 'y', 'ye') 183 | if mode == 'integrated': 184 | _cleanup() 185 | try: 186 | # Blacklist all nouveau and Nvidia modules 187 | _create_file(BLACKLIST_PATH, BLACKLIST_CONTENT) 188 | # Power off the Nvidia GPU with udev rules 189 | _create_file(UDEV_INTEGRATED_PATH, UDEV_INTEGRATED) 190 | except Exception as e: 191 | print(f'Error: {e}') 192 | sys.exit(1) 193 | _rebuild_initramfs() 194 | elif mode == 'hybrid': 195 | _cleanup() 196 | # Enable modeset for Nvidia driver 197 | choice = input('Enable RTD3 Power Management? (y/N): ').lower() 198 | if choice in yes: 199 | _create_file(UDEV_PM_PATH, UDEV_PM) 200 | _create_file(MODESET_PATH, MODESET_PM) 201 | else: 202 | _create_file(MODESET_PATH, MODESET_CONTENT) 203 | _rebuild_initramfs() 204 | elif mode == 'nvidia': 205 | _cleanup() 206 | # detect if Intel or AMD iGPU 207 | igpu_vendor = _get_igpu_vendor() 208 | # get the Nvidia dGPU PCI bus 209 | pci_bus = _get_pci_bus() 210 | # get display manager 211 | if display_manager == '': 212 | display_manager = _check_display_manager() 213 | try: 214 | # Create X.org config 215 | if igpu_vendor == 'intel': 216 | _create_file(XORG_PATH, XORG_INTEL.format(pci_bus)) 217 | _setup_display_manager(display_manager) 218 | elif igpu_vendor == 'amd': 219 | _create_file(XORG_PATH, XORG_AMD.format(pci_bus)) 220 | _setup_display_manager(display_manager) 221 | # Enable modeset for Nvidia driver 222 | _create_file(MODESET_PATH, MODESET_CONTENT) 223 | choice = input('Enable ForceCompositionPipeline? (y/N): ').lower() 224 | if choice in yes: 225 | enable_comp = True 226 | else: 227 | enable_comp = False 228 | choice = input('Enable Coolbits? (y/N): ').lower() 229 | if choice in yes: 230 | enable_coolbits = True 231 | else: 232 | enable_coolbits = False 233 | if enable_comp and enable_coolbits: 234 | _create_file(EXTRA_PATH,EXTRA_CONTENT+TEARING_FIX+COOLBITS+'EndSection') 235 | elif enable_comp: 236 | _create_file(EXTRA_PATH,EXTRA_CONTENT+TEARING_FIX+'EndSection') 237 | elif enable_coolbits: 238 | _create_file(EXTRA_PATH,EXTRA_CONTENT+COOLBITS+'EndSection') 239 | except Exception as e: 240 | print(f'Error: {e}') 241 | sys.exit(1) 242 | _rebuild_initramfs() 243 | else: 244 | print('Error: provided graphics mode is not valid') 245 | print('Supported graphics modes: integrated, nvidia, hybrid') 246 | sys.exit(1) 247 | print(f'Graphics mode set to: {mode}\nPlease reboot your computer for changes to apply!') 248 | 249 | def _cleanup(): 250 | # Remove all files created by EnvyControl 251 | to_remove = (BLACKLIST_PATH,UDEV_INTEGRATED_PATH, UDEV_PM_PATH, XORG_PATH, EXTRA_PATH, '/etc/X11/xorg.conf.d/90-nvidia.conf', MODESET_PATH, LIGHTDM_SCRIPT_PATH, LIGHTDM_CONFIG_PATH) 252 | for file in to_remove: 253 | try: 254 | os.remove(file) 255 | except OSError as e: 256 | if e.errno != 2: 257 | print(f'Error: {e}') 258 | sys.exit(1) 259 | # restore Xsetup backup if found 260 | if os.path.exists(SDDM_XSETUP_PATH+'.bak'): 261 | with open(SDDM_XSETUP_PATH+'.bak', mode='r', encoding='utf-8') as f: 262 | _create_file(SDDM_XSETUP_PATH, f.read()) 263 | 264 | def _get_igpu_vendor(): 265 | pattern_intel = re.compile(r'(VGA).*(Intel)') 266 | pattern_amd = re.compile(r'(VGA).*(ATI|AMD|AMD\/ATI)') 267 | lspci = subprocess.run(['lspci'], stdout=subprocess.PIPE).stdout.decode('utf-8') 268 | if pattern_intel.findall(lspci): 269 | return 'intel' 270 | elif pattern_amd.findall(lspci): 271 | return 'amd' 272 | else: 273 | print('Error: could not find Intel or AMD iGPU') 274 | sys.exit(1) 275 | 276 | def _get_amd_igpu_name(): 277 | pattern = re.compile(r'(name:).*(ATI*|AMD*|AMD\/ATI)*') 278 | xrandr = subprocess.run(['xrandr', '--listproviders'], capture_output=True, text=True).stdout 279 | 280 | if pattern.findall(xrandr): 281 | name = re.search(pattern, xrandr).group(0)[5:] 282 | else: 283 | name = "Error: could not find AMD iGPU" 284 | return name 285 | 286 | def _get_pci_bus(): 287 | pattern = re.compile( 288 | r'([0-9a-f]{2}:[0-9a-z]{2}.[0-9]).*(VGA compatible controller: NVIDIA|3D controller: NVIDIA)') 289 | lspci = subprocess.run(['lspci'], stdout=subprocess.PIPE).stdout.decode('utf-8') 290 | try: 291 | # Need to return Bus ID in PCI:X:X:X format 292 | return ':'.join([str(int(element, 16)) for element in pattern.findall(lspci)[0][0].replace('.', ':').split(':')]) 293 | except Exception: 294 | print(f'Error: switching directly from integrated to Nvidia mode is not supported\nTry switching to hybrid mode first!') 295 | sys.exit(1) 296 | 297 | def _check_display_manager(): 298 | # automatically detect the current Display Manager 299 | # this depends on systemd 300 | pattern = re.compile(r'(\/usr\/bin\/|\/usr\/sbin\/)(.*)') 301 | try: 302 | with open('/etc/systemd/system/display-manager.service',mode='r', encoding='utf-8') as f: 303 | display_manager = pattern.findall(f.read())[0][1] 304 | except Exception: 305 | display_manager = '' 306 | print('Warning: automatic Display Manager detection is not available') 307 | finally: 308 | return display_manager 309 | 310 | def _setup_display_manager(display_manager): 311 | # setup the Xrandr script if necessary 312 | # get igpu vendor to use if needed 313 | igpu_vendor = _get_igpu_vendor() 314 | if display_manager == 'sddm': 315 | # backup Xsetup 316 | if os.path.exists(SDDM_XSETUP_PATH): 317 | with open(SDDM_XSETUP_PATH, mode='r', encoding='utf-8') as f: 318 | _create_file(SDDM_XSETUP_PATH+'.bak', f.read()) 319 | if igpu_vendor == "intel": 320 | _create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format("modesetting")) 321 | else: 322 | amd_name = _get_amd_igpu_name() 323 | _create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format(amd_name)) 324 | subprocess.run(['chmod','+x',SDDM_XSETUP_PATH], stdout=subprocess.DEVNULL) 325 | elif display_manager == 'lightdm': 326 | if igpu_vendor == "amd": 327 | amd_name = _get_amd_igpu_name() 328 | _create_file(LIGHTDM_SCRIPT_PATH, NVIDIA_XRANDR_SCRIPT.format(amd_name)) 329 | else: 330 | _create_file(LIGHTDM_SCRIPT_PATH, NVIDIA_XRANDR_SCRIPT.format("modesetting")) 331 | subprocess.run(['chmod','+x',LIGHTDM_SCRIPT_PATH], stdout=subprocess.DEVNULL) 332 | # create config 333 | if not os.path.exists(os.path.dirname(LIGHTDM_CONFIG_PATH)): 334 | _create_file(LIGHTDM_CONFIG_PATH, LIGHTDM_CONFIG_CONTENT) 335 | elif display_manager not in ['', 'gdm', 'gdm3']: 336 | print('Error: provided Display Manager is not valid') 337 | print('Supported Display Managers: gdm, sddm, lightdm') 338 | sys.exit(1) 339 | 340 | def _rebuild_initramfs(): 341 | # Debian and Ubuntu derivatives 342 | if os.path.exists('/etc/debian_version'): 343 | command = ['update-initramfs', '-u', '-k', 'all'] 344 | # RHEL and SUSE derivatives 345 | elif os.path.exists('/etc/redhat-release') or os.path.exists('/usr/bin/zypper'): 346 | command = ['dracut', '--force', '--regenerate-all'] 347 | else: 348 | command = [] 349 | if len(command) != 0: 350 | print('Rebuilding initramfs...') 351 | p = subprocess.run(command, stdout=subprocess.DEVNULL) 352 | if p.returncode == 0: 353 | print('Successfully rebuilt initramfs!') 354 | else: 355 | print('Error: an error ocurred rebuilding the initramfs') 356 | 357 | def _check_root(): 358 | if not os.geteuid() == 0: 359 | print('Error: this operation requires root privileges') 360 | sys.exit(1) 361 | 362 | def _create_file(path, content): 363 | # Create parent folders if needed 364 | if not os.path.exists(os.path.dirname(path)): 365 | os.makedirs(os.path.dirname(path)) 366 | with open(path, mode='w', encoding='utf-8') as f: 367 | f.write(content) 368 | 369 | def _query_mode(): 370 | if os.path.exists(BLACKLIST_PATH) and os.path.exists(UDEV_INTEGRATED_PATH): 371 | mode = 'integrated' 372 | elif os.path.exists(XORG_PATH) and os.path.exists(MODESET_PATH): 373 | mode = 'nvidia' 374 | else: 375 | mode = 'hybrid' 376 | print(f'Current graphics mode is: {mode}') 377 | 378 | def _reset_sddm(): 379 | _check_root() 380 | try: 381 | _create_file(SDDM_XSETUP_PATH, SDDM_XSETUP_CONTENT) 382 | subprocess.run(['chmod', '+x', SDDM_XSETUP_PATH], stdout=subprocess.DEVNULL) 383 | except Exception as e: 384 | print(f'Error: {e}') 385 | sys.exit(1) 386 | print('Operation completed successfully!') 387 | 388 | def _print_version(): 389 | print(f'EnvyControl version {VERSION}') 390 | 391 | def main(): 392 | # argument parsing 393 | parser = argparse.ArgumentParser() 394 | parser.add_argument('-v', '--version', action='store_true', help='show this program\'s version number and exit') 395 | parser.add_argument('-s', '--switch', type=str, metavar='MODE', action='store', help='switch the graphics mode, supported modes: integrated, hybrid, nvidia') 396 | parser.add_argument('-q', '--query', action='store_true', help='query the current graphics mode set by EnvyControl') 397 | parser.add_argument('--dm', type=str, metavar='DISPLAY_MANAGER', action='store', 398 | help='Manually specify your Display Manager. This is required only for systems without systemd. Supported DMs: gdm, sddm, lightdm') 399 | parser.add_argument('--reset_sddm', action='store_true', help='restore original SDDM Xsetup file') 400 | # print help if no arg is provided 401 | if len(sys.argv) == 1: 402 | parser.print_help() 403 | sys.exit(1) 404 | args = parser.parse_args() 405 | if args.query: 406 | _query_mode() 407 | elif args.version: 408 | _print_version() 409 | elif args.reset_sddm: 410 | _reset_sddm() 411 | elif args.switch: 412 | if args.dm and args.switch == 'nvidia': 413 | _switcher(args.switch, args.dm) 414 | else: 415 | _switcher(args.switch) 416 | elif args.dm and not args.switch: 417 | print('Error: this option is intended to be used with --switch nvidia') 418 | print('Example: sudo envycontrol --switch nvidia --dm sddm') 419 | sys.exit(1) 420 | 421 | if __name__ == '__main__': 422 | main() 423 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | import envycontrol 4 | 5 | 6 | setup( 7 | name='envycontrol', 8 | version=envycontrol.VERSION, 9 | description='Easy GPU switching for Nvidia Optimus laptops under Linux', 10 | url='http://github.com/geminis3/EnvyControl', 11 | author='Victor Bayas', 12 | author_email='victorsbayas@gmail.com', 13 | license='MIT', 14 | py_modules=['envycontrol'], 15 | entry_points={ 16 | 'console_scripts': [ 17 | 'envycontrol=envycontrol:main', 18 | ], 19 | }, 20 | keywords=['nvidia', 'optimus', 'prime', 'gpu', 'linux'], 21 | classifiers=[ 22 | 'Development Status :: 4 - Beta', 23 | 'License :: OSI Approved :: MIT License', 24 | 'Programming Language :: Python :: 3', 25 | 'Operating System :: POSIX :: Linux' 26 | ], 27 | ) --------------------------------------------------------------------------------