Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
xulongwu4 committed Dec 2, 2018
0 parents commit 680f000
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 0 deletions.
2 changes: 2 additions & 0 deletions 99-nvidia.conf
@@ -0,0 +1,2 @@
[Seat:*]
display-stopped-script=/usr/bin/nvidia-optimus-manager autoconfigure
3 changes: 3 additions & 0 deletions README
@@ -0,0 +1,3 @@
- `99-nvidia.conf`: copy to `/etc/lightdm/lightdm.conf.d/99-nvidia.conf`
- `nvidia-optimus-autoconfig.service`: copy to `/etc/systemd/system/nvidia-optimus-autoconfig.service`
- `nvidia-optimus-manager`: copy to `/usr/bin/nvidia-optimus-manager`
10 changes: 10 additions & 0 deletions nvidia-optimus-autoconfig.service
@@ -0,0 +1,10 @@
[Unit]
Description=Automatic configuration for nvidia optimus

[Service]
Type=oneshot
ExecStart=/usr/bin/nvidia-optimus-manager autoconfigure
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
298 changes: 298 additions & 0 deletions nvidia-optimus-manager
@@ -0,0 +1,298 @@
#!/usr/bin/env python3
#
# prime-select
#
# Copyright 2013 Canonical Ltd.
# Author: Alberto Milone <alberto.milone@canonical.com>
#
# Script to switch between NVIDIA and Intel graphics driver libraries.
#
# Usage:
# prime-select nvidia|intel|query
# nvidia: switches to NVIDIA's version of libGL.so
# intel: switches to the open-source version of libGL.so
# query: checks which version is currently active and writes
# "nvidia", "intel" or "unknown" to the standard output
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

import glob
import os
import sys
import re
import subprocess
import shutil

from copy import deepcopy
from subprocess import Popen, PIPE, CalledProcessError, DEVNULL, check_output


class Switcher(object):

def __init__(self):
self._power_profile_path = '/proc/acpi/bbswitch'
self._ldm_conf_file = '/etc/X11/xorg.conf.d/00-ldm.conf'
self._blacklist_file = '/etc/modprobe.d/blacklist-nvidia.conf'
self._opengl_settings = {'intel': 'Intel', 'hybrid': 'Intel', 'nvidia': 'NVIDIA'}
if not self._validate_setting():
sys.stdout.write('The program is not supported on current platform\nExiting ...\n')
sys.exit(1)

def _validate_setting(self):
if not self._check_ldm():
sys.stdout.write('The current configuration is not supported\n\n--- The linux-driver-management program is not installed ---\n\n')
return False

if not self._check_optimus():
sys.stdout.write('The current configuration is not supported\n\n--- NVIDIA Optimus hardware is not detected ---\n\n')
return False

if not self._check_nouveau():
sys.stdout.write('The current configuration is not supported\n\n--- The nouveau driver needs to be unloaded ---\n\n')
return False

if not self._check_nvidia_driver():
sys.stdout.write('The current configuration is not supported\n\n--- NVIDIA proprietary driver is not installed ---\n\n')
return False

if not self._check_bbswitch():
sys.stdout.write('The current configuration is not supported\n\n--- The bbswitch module is not loaded ---\n\n')
return False

return True

def _check_ldm(self):
return os.path.exists('/usr/bin/linux-driver-management')

def _check_optimus(self):
p1 = Popen(['/usr/bin/linux-driver-management', 'status'], stdout=PIPE)
status = subprocess.call(['grep', '-i', 'optimus'], stdin=p1.stdout, stdout=DEVNULL)
return not status

def _check_nouveau(self):
p1 = Popen(['lsmod'], stdout=PIPE)
status = subprocess.call(['grep', 'nouveau'], stdin=p1.stdout, stdout=DEVNULL)
return status

def _check_nvidia_driver(self):
status = subprocess.call(['modinfo', 'nvidia'], stdout=DEVNULL, stderr=DEVNULL)
return not status

def _check_bbswitch(self):
p1 = Popen(['lsmod'], stdout=PIPE)
status = subprocess.call(['grep', 'bbswitch'], stdin=p1.stdout, stdout=DEVNULL)
return not status

def _get_profile(self):

opengl_vendor = self._get_opengl_vendor()

if opengl_vendor == 'NVIDIA':
return 'nvidia'
elif opengl_vendor == 'Intel':
if self._is_nvidia_offloaded():
return 'intel';
else:
return 'hybrid'
else:
return 'unknown'

def _get_opengl_vendor(self):
try:
vendor_str = check_output('glxinfo | grep -i opengl | grep -i vendor', shell=True, stderr=DEVNULL).decode('utf-8').lower()

if 'nvidia' in vendor_str:
return 'NVIDIA'
elif 'intel' in vendor_str:
return 'Intel'
else:
return 'unknown'
except:
return 'unknown'

def _get_nvidia_card_power_status(self):
try:
settings = open(self._power_profile_path, 'r')
return settings.read().split()[1]
except:
return 'unknown'

def print_configuration(self):
profile = self._get_profile()
print('Current profile: %s' % profile)

vendor = self._get_opengl_vendor()
print('OpenGL vendor: %s' % vendor)

power = self._get_nvidia_card_power_status()
print('Discrete graphics card power status: %s' % power)

return True

def enable_profile(self, profile):
current_profile = self._get_profile()
current_opengl_vendor = self._get_opengl_vendor()

if profile == current_profile:
# No need to do anything if we're already using the desired
# profile
sys.stdout.write('Info: the %s profile is already set\n' % (profile))
return True

sys.stdout.write('Info: selecting the %s profile\n' % (profile))

new_opengl_vendor = self._opengl_settings[profile]

if profile == 'intel':
self._blacklist_nvidia()
else:
self._nuke_blacklist_file()

if profile == 'nvidia':
self._ldm_configure()
else:
self._nuke_ldm_conf()

self.autoconfigure()

if current_opengl_vendor != new_opengl_vendor:
sys.stdout.write('Log out to take effect\n')

return True

def _ldm_configure(self):
subprocess.call(['/usr/bin/linux-driver-management', 'configure', 'gpu'], stdout=DEVNULL, stderr=DEVNULL)

def _enable_nvidia(self):
if self._get_nvidia_card_power_status() == 'OFF':
self._power_on_nvidia_card()

if self._get_nvidia_card_power_status() == 'ON' and self._is_nvidia_offloaded():
self._load_nvidia_driver()

def _disable_nvidia(self):
self._nuke_ldm_conf()
if not self._is_nvidia_offloaded() and self._get_opengl_vendor() != 'NVIDIA':
self._unload_nvidia_driver()

if self._get_nvidia_card_power_status() == 'ON' and self._is_nvidia_offloaded():
self._power_off_nvidia_card()


def autoconfigure(self):
if os.path.exists(self._blacklist_file):
self._disable_nvidia()
else:
self._enable_nvidia()

def _nuke_blacklist_file(self):
try:
os.unlink(self._blacklist_file)
except:
pass

def _nuke_ldm_conf(self):
try:
os.unlink(self._ldm_conf_file)
except:
pass

def _blacklist_nvidia(self):
blacklist_text = '''# Do not modify
# This file was generated by prime-select
blacklist nvidia
blacklist nvidia_drm
blacklist nvidia_uvm
blacklist nvidia_modeset'''
blacklist_fd = open(self._blacklist_file, 'w')
blacklist_fd.write(blacklist_text)
blacklist_fd.close()

def _unload_nvidia_driver(self):
subprocess.call(['/usr/bin/systemctl', 'stop', 'nvidia-persistenced'])
subprocess.call(['/sbin/modprobe', '-r', 'nvidia_drm'])
subprocess.call(['/sbin/modprobe', '-r', 'nvidia_uvm'])
subprocess.call(['/sbin/modprobe', '-r', 'nvidia_modeset'])
subprocess.call(['/sbin/modprobe', '-r', 'nvidia'])

def _load_nvidia_driver(self):
subprocess.call(['/sbin/modprobe', 'nvidia'])

def _is_nvidia_offloaded(self):
p1 = Popen(['lsmod'], stdout=PIPE)
status = subprocess.call(['grep', 'nvidia'], stdin=p1.stdout, stdout=DEVNULL)
return status

def _power_off_nvidia_card(self):
p1 = Popen(['echo', 'OFF'], stdout=PIPE)
subprocess.call(['/usr/bin/tee', self._power_profile_path], stdin=p1.stdout, stdout=DEVNULL)

def _power_on_nvidia_card(self):
p1 = Popen(['echo', 'ON'], stdout=PIPE)
subprocess.call(['/usr/bin/tee', self._power_profile_path], stdin=p1.stdout, stdout=DEVNULL)

def check_root():
if not os.geteuid() == 0:
sys.stderr.write("This operation requires root privileges\n")
exit(1)

def handle_query_error():
sys.stderr.write("Error: no profile can be found\n")
exit(1)

def usage():
sys.stderr.write("Usage:\n")
sys.stderr.write(" %s status\n" % (sys.argv[0]))
sys.stderr.write(" %s configure nvidia|hybrid|intel\n" % (sys.argv[0]))
sys.stderr.write(" %s autoconfigure\n" % (sys.argv[0]))

if __name__ == '__main__':
try:
arg = sys.argv[1]
except IndexError:
arg = None

switcher = Switcher()

status = 0

if arg == 'status' and len(sys.argv) == 2:
if not switcher.print_configuration():
handle_query_error()
else:
status = 1
elif arg == 'autoconfigure' and len(sys.argv) == 2:
check_root()
switcher.autoconfigure()
status = 1
elif arg == 'configure' and len(sys.argv) == 3:
arg = sys.argv[2]
if arg == 'intel' or arg == 'nvidia' or arg == 'hybrid':
check_root()
switcher.enable_profile(arg)
status = 1

if not status:
usage()
sys.exit(1)

exit(0)

0 comments on commit 680f000

Please sign in to comment.