Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplemented display manager setup, plus amd nvidia fix. #36

Merged
merged 4 commits into from
Apr 7, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 89 additions & 8 deletions envycontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@

Section "Device"
Identifier "amdgpu"
Driver "modesetting"
Driver "amdgpu"
EndSection

Section "Screen"
Expand Down Expand Up @@ -153,14 +153,31 @@
options nvidia "NVreg_DynamicPowerManagement=0x02"
'''

XSETUP_PATH = '/usr/share/sddm/scripts/Xsetup'
SDDM_XSETUP_PATH = '/usr/share/sddm/scripts/Xsetup'

XSETUP_CONTENT = '''#!/bin/sh
SDDM_XSETUP_CONTENT = '''#!/bin/sh
# Xsetup - run as root before the login dialog appears

'''

def _switcher(mode):
LIGHTDM_SCRIPT_PATH = '/etc/lightdm/nvidia.sh'

LIGHTDM_CONFIG_PATH = '/etc/lightdm/lightdm.conf.d/20-nvidia.conf'

LIGHTDM_CONFIG_CONTENT = '''# Automatically generated by EnvyControl

[Seat:*]
display-setup-script=/etc/lightdm/nvidia.sh
'''

NVIDIA_XRANDR_SCRIPT = '''#!/bin/sh
# Automatically generated by EnvyControl

xrandr --setprovideroutputsource "{}" NVIDIA-0
xrandr --auto
'''

def _switcher(mode, display_manager = ''):
_check_root()
yes = ('yes', 'y', 'ye')
if mode == 'integrated':
Expand Down Expand Up @@ -190,12 +207,17 @@ def _switcher(mode):
igpu_vendor = _get_igpu_vendor()
# get the Nvidia dGPU PCI bus
pci_bus = _get_pci_bus()
# get display manager
if display_manager == '':
display_manager = _check_display_manager()
try:
# Create X.org config
if igpu_vendor == 'intel':
_create_file(XORG_PATH, XORG_INTEL.format(pci_bus))
_setup_display_manager(display_manager)
elif igpu_vendor == 'amd':
_create_file(XORG_PATH, XORG_AMD.format(pci_bus))
_setup_display_manager(display_manager)
# Enable modeset for Nvidia driver
_create_file(MODESET_PATH, MODESET_CONTENT)
choice = input('Enable ForceCompositionPipeline? (y/N): ').lower()
Expand Down Expand Up @@ -226,7 +248,7 @@ def _switcher(mode):

def _cleanup():
# Remove all files created by EnvyControl
to_remove = (BLACKLIST_PATH,UDEV_INTEGRATED_PATH, UDEV_PM_PATH, XORG_PATH, EXTRA_PATH, '/etc/X11/xorg.conf.d/90-nvidia.conf', MODESET_PATH, '/etc/lightdm/nvidia.sh', '/etc/lightdm/lightdm.conf.d/20-nvidia.conf')
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, SDDM_XSETUP_PATH)
for file in to_remove:
try:
os.remove(file)
Expand All @@ -247,6 +269,16 @@ def _get_igpu_vendor():
print('Error: could not find Intel or AMD iGPU')
sys.exit(1)

def _get_amd_igpu_name():
pattern = re.compile(r'(name:).*(ATI*|AMD*|AMD\/ATI)*')
xrandr = subprocess.run(['xrandr', '--listproviders'], capture_output=True, text=True).stdout

if pattern.findall(xrandr):
name = re.search(pattern, xrandr).group(0)[5:]
else:
name = "Error: could not find AMD iGPU"
return name

def _get_pci_bus():
pattern = re.compile(
r'([0-9]{2}:[0-9a-z]{2}.[0-9]).*(VGA compatible controller: NVIDIA|3D controller: NVIDIA)')
Expand All @@ -258,6 +290,46 @@ def _get_pci_bus():
print(f'Error: switching directly from integrated to Nvidia mode is not supported\nTry switching to hybrid mode first!')
sys.exit(1)

def _check_display_manager():
# automatically detect the current Display Manager
# this depends on systemd
pattern = re.compile(r'(\/usr\/bin\/|\/usr\/sbin\/)(.*)')
try:
with open('/etc/systemd/system/display-manager.service',mode='r', encoding='utf-8') as f:
display_manager = pattern.findall(f.read())[0][1]
except Exception:
display_manager = ''
print('Warning: automatic Display Manager detection is not available')
finally:
return display_manager

def _setup_display_manager(display_manager):
# setup the Xrandr script if necessary
# get igpu vendor to use if needed
igpu_vendor = _get_igpu_vendor()

if display_manager == 'sddm':
if igpu_vendor == "intel":
_create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format("modesetting"))
else:
amd_name = _get_amd_igpu_name()
_create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format(amd_name))
subprocess.run(['chmod','+x',SDDM_XSETUP_PATH], stdout=subprocess.DEVNULL)
elif display_manager == 'lightdm':
if igpu_vendor == "amd":
amd_name = _get_amd_igpu_name()
_create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format(amd_name))
else:
_create_file(SDDM_XSETUP_PATH, NVIDIA_XRANDR_SCRIPT.format("modesetting"))
subprocess.run(['chmod','+x',LIGHTDM_SCRIPT_PATH], stdout=subprocess.DEVNULL)
# create config
if not os.path.exists(os.path.dirname(LIGHTDM_CONFIG_PATH)):
_create_file(LIGHTDM_CONFIG_PATH, LIGHTDM_CONFIG_CONTENT)
elif display_manager not in ['', 'gdm', 'gdm3']:
print('Error: provided Display Manager is not valid')
print('Supported Display Managers: gdm, sddm, lightdm')
sys.exit(1)

def _rebuild_initramfs():
is_debian = os.path.exists('/etc/debian_version')
is_rhel = os.path.exists('/etc/redhat-release')
Expand Down Expand Up @@ -300,8 +372,8 @@ def _query_mode():
def _reset_sddm():
_check_root()
try:
_create_file(XSETUP_PATH, XSETUP_CONTENT)
subprocess.run(['chmod', '+x', XSETUP_PATH], stdout=subprocess.DEVNULL)
_create_file(SDDM_XSETUP_PATH, SDDM_XSETUP_CONTENT)
subprocess.run(['chmod', '+x', SDDM_XSETUP_PATH], stdout=subprocess.DEVNULL)
except Exception as e:
print(f'Error: {e}')
sys.exit(1)
Expand All @@ -316,6 +388,8 @@ def main():
parser.add_argument('-v', '--version', action='store_true', help='show this program\'s version number and exit')
parser.add_argument('-s', '--switch', type=str, metavar='MODE', action='store', help='switch the graphics mode, supported modes: integrated, hybrid, nvidia')
parser.add_argument('-q', '--query', action='store_true', help='query the current graphics mode set by EnvyControl')
parser.add_argument('--dm', type=str, metavar='DISPLAY_MANAGER', action='store',
help='Manually specify your Display Manager. This is required only for systems without systemd. Supported DMs: gdm, sddm, lightdm')
parser.add_argument('--reset_sddm', action='store_true', help='restore original SDDM Xsetup file')
# print help if no arg is provided
if len(sys.argv) == 1:
Expand All @@ -329,7 +403,14 @@ def main():
elif args.reset_sddm:
_reset_sddm()
elif args.switch:
_switcher(args.switch)
if args.dm and args.switch == 'nvidia':
_switcher(args.switch, args.dm)
else:
_switcher(args.switch)
elif args.dm and not args.switch:
print('Error: this option is intended to be used with --switch nvidia')
print('Example: sudo envycontrol --switch nvidia --dm sddm')
sys.exit(1)

if __name__ == '__main__':
main()