Repository URL to install this package:
|
Version:
0.5.7.1 ▾
|
"""Module to deal with various aspects of displays"""
# isort:skip_file
import enum
import os
import subprocess
try:
from dbus.exceptions import DBusException
DBUS_AVAILABLE = True
except ImportError:
DBUS_AVAILABLE = False
from gi.repository import Gdk, GLib, GnomeDesktop
from lutris.util import system
from lutris.util.graphics.displayconfig import MutterDisplayManager
from lutris.util.graphics.xrandr import LegacyDisplayManager, change_resolution, get_outputs
from lutris.util.log import logger
class NoScreenDetected(Exception):
"""Raise this when unable to detect screens"""
def restore_gamma():
"""Restores gamma to a normal level."""
xgamma_path = system.find_executable("xgamma")
try:
subprocess.Popen([xgamma_path, "-gamma", "1.0"])
except (FileNotFoundError, TypeError):
logger.warning("xgamma is not available on your system")
except PermissionError:
logger.warning("you do not have permission to call xgamma")
def _get_graphics_adapters():
"""Return the list of graphics cards available on a system
Returns:
list: list of tuples containing PCI ID and description of the display controller
"""
lspci_path = system.find_executable("lspci")
dev_subclasses = ["VGA", "XGA", "3D controller", "Display controller"]
if not lspci_path:
logger.warning("lspci is not available. List of graphics cards not available")
return []
return [
(pci_id, device_desc.split(": ")[1]) for pci_id, device_desc in [
line.split(maxsplit=1) for line in system.execute(lspci_path, timeout=3).split("\n")
if any(subclass in line for subclass in dev_subclasses)
]
]
class DisplayManager:
"""Get display and resolution using GnomeDesktop"""
def __init__(self):
screen = Gdk.Screen.get_default()
if not screen:
raise NoScreenDetected
self.rr_screen = GnomeDesktop.RRScreen.new(screen)
self.rr_config = GnomeDesktop.RRConfig.new_current(self.rr_screen)
self.rr_config.load_current()
def get_display_names(self):
"""Return names of connected displays"""
return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()]
def get_resolutions(self):
"""Return available resolutions"""
resolutions = ["%sx%s" % (mode.get_width(), mode.get_height()) for mode in self.rr_screen.list_modes()]
return sorted(set(resolutions), key=lambda x: int(x.split("x")[0]), reverse=True)
def _get_primary_output(self):
"""Return the RROutput used as a primary display"""
for output in self.rr_screen.list_outputs():
if output.get_is_primary():
return output
return
def get_current_resolution(self):
"""Return the current resolution for the primary display"""
output = self._get_primary_output()
if not output:
logger.error("Failed to get a default output")
return "", ""
current_mode = output.get_current_mode()
return str(current_mode.get_width()), str(current_mode.get_height())
@staticmethod
def set_resolution(resolution):
"""Set the resolution of one or more displays.
The resolution can either be a string, which will be applied to the
primary display or a list of configurations as returned by `get_config`.
This method uses XrandR and will not work on Wayland.
"""
return change_resolution(resolution)
@staticmethod
def get_config():
"""Return the current display resolution
This method uses XrandR and will not work on wayland
The output can be fed in `set_resolution`
"""
return get_outputs()
def get_display_manager():
"""Return the appropriate display manager instance.
Defaults to Mutter if available. This is the only one to support Wayland.
"""
if DBUS_AVAILABLE:
try:
return MutterDisplayManager()
except DBusException as ex:
logger.debug("Mutter DBus service not reachable: %s", ex)
except Exception as ex: # pylint: disable=broad-except
logger.exception("Failed to instanciate MutterDisplayConfig. Please report with exception: %s", ex)
else:
logger.error("DBus is not available, lutris was not properly installed.")
try:
return DisplayManager()
except (GLib.Error, NoScreenDetected):
return LegacyDisplayManager()
DISPLAY_MANAGER = get_display_manager()
USE_DRI_PRIME = len(_get_graphics_adapters()) > 1
class DesktopEnvironment(enum.Enum):
"""Enum of desktop environments."""
PLASMA = 0
MATE = 1
XFCE = 2
DEEPIN = 3
UNKNOWN = 999
def get_desktop_environment():
"""Converts the value of the DESKTOP_SESSION environment variable
to one of the constants in the DesktopEnvironment class.
Returns None if DESKTOP_SESSION is empty or unset.
"""
desktop_session = os.environ.get("DESKTOP_SESSION", "").lower()
if not desktop_session:
return None
if desktop_session.endswith("plasma"):
return DesktopEnvironment.PLASMA
if desktop_session.endswith("mate"):
return DesktopEnvironment.MATE
if desktop_session.endswith("xfce"):
return DesktopEnvironment.XFCE
if desktop_session.endswith("deepin"):
return DesktopEnvironment.DEEPIN
return DesktopEnvironment.UNKNOWN
def _get_command_output(*command):
return subprocess.Popen(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, close_fds=True).communicate()[0]
def is_compositing_enabled():
"""Checks whether compositing is currently disabled or enabled.
Returns True for enabled, False for disabled, and None if unknown.
"""
desktop_environment = get_desktop_environment()
if desktop_environment is DesktopEnvironment.PLASMA:
return _get_command_output(
"qdbus", "org.kde.KWin", "/Compositor", "org.kde.kwin.Compositing.active"
) == b"true\n"
if desktop_environment is DesktopEnvironment.MATE:
return _get_command_output("gsettings", "get org.mate.Marco.general", "compositing-manager") == b"true\n"
if desktop_environment is DesktopEnvironment.XFCE:
return _get_command_output(
"xfconf-query", "--channel=xfwm4", "--property=/general/use_compositing"
) == b"true\n"
if desktop_environment is DesktopEnvironment.DEEPIN:
return _get_command_output(
"dbus-send", "--session", "--dest=com.deepin.WMSwitcher", "--type=method_call",
"--print-reply=literal", "/com/deepin/WMSwitcher", "com.deepin.WMSwitcher.CurrentWM"
) == b"deepin wm\n"
return None
# One element is appended to this for every invocation of disable_compositing:
# True if compositing has been disabled, False if not. enable_compositing
# removes the last element, and only re-enables compositing if that element
# was True.
_COMPOSITING_DISABLED_STACK = []
def _get_compositor_commands():
"""Returns the commands to enable/disable compositing on the current
desktop environment as a 2-tuple.
"""
start_compositor = None
stop_compositor = None
desktop_environment = get_desktop_environment()
if desktop_environment is DesktopEnvironment.PLASMA:
stop_compositor = ("qdbus", "org.kde.KWin", "/Compositor", "org.kde.kwin.Compositing.suspend")
start_compositor = ("qdbus", "org.kde.KWin", "/Compositor", "org.kde.kwin.Compositing.resume")
elif desktop_environment is DesktopEnvironment.MATE:
stop_compositor = ("gsettings", "set org.mate.Marco.general", "compositing-manager", "false")
start_compositor = ("gsettings", "set org.mate.Marco.general", "compositing-manager", "true")
elif desktop_environment is DesktopEnvironment.XFCE:
stop_compositor = ("xfconf-query", "--channel=xfwm4", "--property=/general/use_compositing", "--set=false")
start_compositor = ("xfconf-query", "--channel=xfwm4", "--property=/general/use_compositing", "--set=true")
elif desktop_environment is DesktopEnvironment.DEEPIN:
start_compositor = (
"dbus-send", "--session", "--dest=com.deepin.WMSwitcher", "--type=method_call",
"/com/deepin/WMSwitcher", "com.deepin.WMSwitcher.RequestSwitchWM",
)
stop_compositor = start_compositor
return start_compositor, stop_compositor
def _run_command(*command):
return subprocess.Popen(command, stdin=subprocess.DEVNULL, close_fds=True)
def disable_compositing():
"""Disable compositing if not already disabled."""
compositing_enabled = is_compositing_enabled()
if compositing_enabled is None:
compositing_enabled = True
if any(_COMPOSITING_DISABLED_STACK):
compositing_enabled = False
_COMPOSITING_DISABLED_STACK.append(compositing_enabled)
if not compositing_enabled:
return
_, stop_compositor = _get_compositor_commands()
if stop_compositor:
_run_command(*stop_compositor)
def enable_compositing():
"""Re-enable compositing if the corresponding call to disable_compositing
disabled it."""
compositing_disabled = _COMPOSITING_DISABLED_STACK.pop()
if not compositing_disabled:
return
start_compositor, _ = _get_compositor_commands()
if start_compositor:
_run_command(*start_compositor)