We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
“Restore default gamma....” doesn’t restore gamma after game quits
For some reason, with "Restore default gamma..." option enabled, default screen gamma is not restored after a game that changes it quits (even after Lutris kills processes in the prefix)
When running Lutris in debug mode (-d) I get this Warning:
"WARNING  2018-08-08 18:46:47,323 [display.restore_gamma:168]:xgamma is not available on your system"
Would be nice to fix this, as changing screen gamma back manually is a bit annoying.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/util/display.py`
Content:
```
1 import re
2 import subprocess
3 import time
4 
5 import gi
6 gi.require_version('GnomeDesktop', '3.0')
7 
8 from gi.repository import Gdk, GnomeDesktop, GLib
9 
10 from lutris.util import system
11 from lutris.util.log import logger
12 
13 XRANDR_CACHE = None
14 XRANDR_CACHE_SET_AT = None
15 XGAMMA_FOUND = False
16 
17 
18 def cached(function):
19     def wrapper():
20         global XRANDR_CACHE
21         global XRANDR_CACHE_SET_AT
22 
23         if XRANDR_CACHE and time.time() - XRANDR_CACHE_SET_AT < 60:
24             return XRANDR_CACHE
25         XRANDR_CACHE = function()
26         XRANDR_CACHE_SET_AT = time.time()
27         return XRANDR_CACHE
28     return wrapper
29 
30 
31 @cached
32 def get_vidmodes():
33     """Return video modes from XrandR"""
34     logger.debug("Retrieving video modes from XrandR")
35     xrandr_output = subprocess.Popen(["xrandr"],
36                                      stdout=subprocess.PIPE).communicate()[0]
37     return list([line for line in xrandr_output.decode().split("\n")])
38 
39 
40 def get_outputs():
41     """Return list of tuples containing output name and geometry."""
42     outputs = []
43     vid_modes = get_vidmodes()
44     if not vid_modes:
45         logger.error("xrandr didn't return anything")
46         return []
47     for line in vid_modes:
48         parts = line.split()
49         if len(parts) < 2:
50             continue
51         if parts[1] == 'connected':
52             if len(parts) == 2:
53                 continue
54             if parts[2] != 'primary':
55                 geom = parts[2]
56                 rotate = parts[3]
57             else:
58                 geom = parts[3]
59                 rotate = parts[4]
60             if geom.startswith('('):  # Screen turned off, no geometry
61                 continue
62             if rotate.startswith('('):  # Screen not rotated, no need to include
63                 outputs.append((parts[0], geom, "normal"))
64             else:
65                 if rotate in ("left", "right"):
66                     geom_parts = geom.split('+')
67                     x_y = geom_parts[0].split('x')
68                     geom = "{}x{}+{}+{}".format(x_y[1], x_y[0], geom_parts[1], geom_parts[2])
69                 outputs.append((parts[0], geom, rotate))
70     return outputs
71 
72 
73 def get_output_names():
74     """Return output names from XrandR"""
75     return [output[0] for output in get_outputs()]
76 
77 
78 def turn_off_except(display):
79     """Use XrandR to turn off displays except the one referenced by `display`"""
80     if not display:
81         logger.error("No active display given, no turning off every display")
82         return
83     for output in get_outputs():
84         if output[0] != display:
85             logger.info("Turning off %s", output[0])
86             subprocess.Popen(["xrandr", "--output", output[0], "--off"])
87 
88 
89 def get_resolutions():
90     """Return the list of supported screen resolutions."""
91     resolution_list = []
92     for line in get_vidmodes():
93         if line.startswith("  "):
94             resolution_match = re.match(r'.*?(\d+x\d+).*', line)
95             if resolution_match:
96                 resolution_list.append(resolution_match.groups()[0])
97     return resolution_list
98 
99 
100 def get_unique_resolutions():
101     """Return available resolutions, without duplicates and ordered with highest resolution first"""
102     return sorted(set(get_resolutions()), key=lambda x: int(x.split('x')[0]), reverse=True)
103 
104 
105 def get_current_resolution(monitor=0):
106     """Return the current resolution for the desktop."""
107     resolution = list()
108     for line in get_vidmodes():
109         if line.startswith("  ") and "*" in line:
110             resolution_match = re.match(r'.*?(\d+x\d+).*', line)
111             if resolution_match:
112                 resolution.append(resolution_match.groups()[0])
113     if monitor == 'all':
114         return resolution
115     return resolution[monitor]
116 
117 
118 def change_resolution(resolution):
119     """Change display resolution.
120 
121     Takes a string for single monitors or a list of displays as returned
122     by get_outputs().
123     """
124     if not resolution:
125         logger.warning("No resolution provided")
126         return
127     if isinstance(resolution, str):
128         logger.debug("Switching resolution to %s", resolution)
129 
130         if resolution not in get_resolutions():
131             logger.warning("Resolution %s doesn't exist.", resolution)
132         else:
133             logger.info("Changing resolution to %s", resolution)
134             subprocess.Popen(["xrandr", "-s", resolution])
135     else:
136         for display in resolution:
137             display_name = display[0]
138             logger.debug("Switching to %s on %s", display[1], display[0])
139             display_geom = display[1].split('+')
140             display_resolution = display_geom[0]
141             position = (display_geom[1], display_geom[2])
142 
143             if (
144                     len(display) > 2 and
145                     display[2] in ('normal', 'left', 'right', 'inverted')
146             ):
147                 rotation = display[2]
148             else:
149                 rotation = "normal"
150             logger.info("Switching resolution of %s to %s", display_name, display_resolution)
151             subprocess.Popen([
152                 "xrandr",
153                 "--output", display_name,
154                 "--mode", display_resolution,
155                 "--pos", "{}x{}".format(position[0], position[1]),
156                 "--rotate", rotation
157             ]).communicate()
158 
159 
160 def restore_gamma():
161     """Restores gamma to a normal level."""
162     global XGAMMA_FOUND
163     if XGAMMA_FOUND is None:
164         XGAMMA_FOUND = system.find_executable('xgamma')
165     if XGAMMA_FOUND is True:
166         subprocess.Popen(["xgamma", "-gamma", "1.0"])
167     else:
168         logger.warning('xgamma is not available on your system')
169 
170 
171 def get_xrandr_version():
172     """Return the major and minor version of XRandR utility"""
173     pattern = "version"
174     xrandr_output = subprocess.Popen(["xrandr", "--version"],
175                                      stdout=subprocess.PIPE).communicate()[0].decode()
176     position = xrandr_output.find(pattern) + len(pattern)
177     version_str = xrandr_output[position:].strip().split(".")
178     logger.debug("Found XrandR version %s", version_str)
179     try:
180         return {"major": int(version_str[0]), "minor": int(version_str[1])}
181     except ValueError:
182         logger.error("Can't find version in: %s", xrandr_output)
183         return {"major": 0, "minor": 0}
184 
185 
186 def get_graphics_adapaters():
187     """Return the list of graphics cards available on a system
188 
189     Returns:
190         list: list of tuples containing PCI ID and description of the VGA adapter
191     """
192 
193     if not system.find_executable('lspci'):
194         logger.warning('lspci is not available. List of graphics cards not available')
195         return []
196     return [
197         (pci_id, vga_desc.split(': ')[1]) for pci_id, vga_desc in [
198             line.split(maxsplit=1)
199             for line in system.execute('lspci').split('\n')
200             if 'VGA' in line
201         ]
202     ]
203 
204 
205 def get_providers():
206     """Return the list of available graphic cards"""
207     pattern = "name:"
208     providers = []
209     version = get_xrandr_version()
210 
211     if version["major"] == 1 and version["minor"] >= 4:
212         logger.debug("Retrieving providers from XrandR")
213         xrandr_output = subprocess.Popen(["xrandr", "--listproviders"],
214                                          stdout=subprocess.PIPE).communicate()[0].decode()
215         for line in xrandr_output.split("\n"):
216             if line.find("Provider ") != 0:
217                 continue
218             position = line.find(pattern) + len(pattern)
219             providers.append(line[position:].strip())
220 
221     return providers
222 
223 
224 class LegacyDisplayManager:
225     @staticmethod
226     def get_resolutions():
227         return get_resolutions()
228 
229     @staticmethod
230     def get_display_names():
231         return get_output_names()
232 
233 
234 class DisplayManager(object):
235     def __init__(self):
236         self.screen = Gdk.Screen.get_default()
237         self.rr_screen = GnomeDesktop.RRScreen.new(self.screen)
238         self.rr_config = GnomeDesktop.RRConfig.new_current(self.rr_screen)
239         self.rr_config.load_current()
240 
241     @property
242     def outputs(self):
243         return self.rr_screen.list_outputs()
244 
245     def get_display_names(self):
246         return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()]
247 
248     def get_output_modes(self, output):
249         logger.debug("Retrieving modes for %s", output)
250         resolutions = []
251         for mode in output.list_modes():
252             resolution = "%sx%s" % (mode.get_width(), mode.get_height())
253             if resolution not in resolutions:
254                 resolutions.append(resolution)
255         return resolutions
256 
257     def get_resolutions(self):
258         resolutions = []
259         for mode in self.rr_screen.list_modes():
260             resolutions.append("%sx%s" % (mode.get_width(), mode.get_height()))
261         return sorted(set(resolutions), key=lambda x: int(x.split('x')[0]), reverse=True)
262 
263 
264 try:
265     DISPLAY_MANAGER = DisplayManager()
266 except GLib.Error:
267     DISPLAY_MANAGER = LegacyDisplayManager()
268 
269 USE_DRI_PRIME = len(get_graphics_adapaters()) > 1
270 
271 
272 def get_resolution_choices():
273     """Return list of available resolutions as label, value tuples
274     suitable for inclusion in drop-downs.
275     """
276     resolutions = DISPLAY_MANAGER.get_resolutions()
277     resolution_choices = list(zip(resolutions, resolutions))
278     resolution_choices.insert(0, ("Keep current", 'off'))
279     return resolution_choices
280 
281 
282 def get_output_choices():
283     """Return list of outputs for drop-downs"""
284     displays = DISPLAY_MANAGER.get_display_names()
285     output_choices = list(zip(displays, displays))
286     output_choices.insert(0, ("Off", 'off'))
287     return output_choices
288 
289 
290 def get_output_list():
291     """Return a list of output with their index.
292     This is used to indicate to SDL 1.2 which monitor to use.
293     """
294     choices = [
295         ('Off', 'off'),
296     ]
297     displays = DISPLAY_MANAGER.get_display_names()
298     for index, output in enumerate(displays):
299         # Display name can't be used because they might not be in the right order
300         # Using DISPLAYS to get the number of connected monitors
301         choices.append((output, str(index)))
302     return choices
303
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/util/display.py b/lutris/util/display.py
--- a/lutris/util/display.py
+++ b/lutris/util/display.py
@@ -12,8 +12,7 @@
 
 XRANDR_CACHE = None
 XRANDR_CACHE_SET_AT = None
-XGAMMA_FOUND = False
-
+XGAMMA_FOUND = None
 
 def cached(function):
     def wrapper():
@@ -161,7 +160,7 @@
     """Restores gamma to a normal level."""
     global XGAMMA_FOUND
     if XGAMMA_FOUND is None:
-        XGAMMA_FOUND = system.find_executable('xgamma')
+        XGAMMA_FOUND = bool(system.find_executable('xgamma'))
     if XGAMMA_FOUND is True:
         subprocess.Popen(["xgamma", "-gamma", "1.0"])
     else:

{"golden_diff": "diff --git a/lutris/util/display.py b/lutris/util/display.py\n--- a/lutris/util/display.py\n+++ b/lutris/util/display.py\n@@ -12,8 +12,7 @@\n \n XRANDR_CACHE = None\n XRANDR_CACHE_SET_AT = None\n-XGAMMA_FOUND = False\n-\n+XGAMMA_FOUND = None\n \n def cached(function):\n     def wrapper():\n@@ -161,7 +160,7 @@\n     \"\"\"Restores gamma to a normal level.\"\"\"\n     global XGAMMA_FOUND\n     if XGAMMA_FOUND is None:\n-        XGAMMA_FOUND = system.find_executable('xgamma')\n+        XGAMMA_FOUND = bool(system.find_executable('xgamma'))\n     if XGAMMA_FOUND is True:\n         subprocess.Popen([\"xgamma\", \"-gamma\", \"1.0\"])\n     else:\n", "issue": "\u201cRestore default gamma....\u201d doesn\u2019t restore gamma after game quits\nFor some reason, with \"Restore default gamma...\" option enabled, default screen gamma is not restored after a game that changes it quits (even after Lutris kills processes in the prefix)\r\nWhen running Lutris in debug mode (-d) I get this Warning:\r\n\"WARNING  2018-08-08 18:46:47,323 [display.restore_gamma:168]:xgamma is not available on your system\"\r\nWould be nice to fix this, as changing screen gamma back manually is a bit annoying.\n", "before_files": [{"content": "import re\nimport subprocess\nimport time\n\nimport gi\ngi.require_version('GnomeDesktop', '3.0')\n\nfrom gi.repository import Gdk, GnomeDesktop, GLib\n\nfrom lutris.util import system\nfrom lutris.util.log import logger\n\nXRANDR_CACHE = None\nXRANDR_CACHE_SET_AT = None\nXGAMMA_FOUND = False\n\n\ndef cached(function):\n    def wrapper():\n        global XRANDR_CACHE\n        global XRANDR_CACHE_SET_AT\n\n        if XRANDR_CACHE and time.time() - XRANDR_CACHE_SET_AT < 60:\n            return XRANDR_CACHE\n        XRANDR_CACHE = function()\n        XRANDR_CACHE_SET_AT = time.time()\n        return XRANDR_CACHE\n    return wrapper\n\n\n@cached\ndef get_vidmodes():\n    \"\"\"Return video modes from XrandR\"\"\"\n    logger.debug(\"Retrieving video modes from XrandR\")\n    xrandr_output = subprocess.Popen([\"xrandr\"],\n                                     stdout=subprocess.PIPE).communicate()[0]\n    return list([line for line in xrandr_output.decode().split(\"\\n\")])\n\n\ndef get_outputs():\n    \"\"\"Return list of tuples containing output name and geometry.\"\"\"\n    outputs = []\n    vid_modes = get_vidmodes()\n    if not vid_modes:\n        logger.error(\"xrandr didn't return anything\")\n        return []\n    for line in vid_modes:\n        parts = line.split()\n        if len(parts) < 2:\n            continue\n        if parts[1] == 'connected':\n            if len(parts) == 2:\n                continue\n            if parts[2] != 'primary':\n                geom = parts[2]\n                rotate = parts[3]\n            else:\n                geom = parts[3]\n                rotate = parts[4]\n            if geom.startswith('('):  # Screen turned off, no geometry\n                continue\n            if rotate.startswith('('):  # Screen not rotated, no need to include\n                outputs.append((parts[0], geom, \"normal\"))\n            else:\n                if rotate in (\"left\", \"right\"):\n                    geom_parts = geom.split('+')\n                    x_y = geom_parts[0].split('x')\n                    geom = \"{}x{}+{}+{}\".format(x_y[1], x_y[0], geom_parts[1], geom_parts[2])\n                outputs.append((parts[0], geom, rotate))\n    return outputs\n\n\ndef get_output_names():\n    \"\"\"Return output names from XrandR\"\"\"\n    return [output[0] for output in get_outputs()]\n\n\ndef turn_off_except(display):\n    \"\"\"Use XrandR to turn off displays except the one referenced by `display`\"\"\"\n    if not display:\n        logger.error(\"No active display given, no turning off every display\")\n        return\n    for output in get_outputs():\n        if output[0] != display:\n            logger.info(\"Turning off %s\", output[0])\n            subprocess.Popen([\"xrandr\", \"--output\", output[0], \"--off\"])\n\n\ndef get_resolutions():\n    \"\"\"Return the list of supported screen resolutions.\"\"\"\n    resolution_list = []\n    for line in get_vidmodes():\n        if line.startswith(\"  \"):\n            resolution_match = re.match(r'.*?(\\d+x\\d+).*', line)\n            if resolution_match:\n                resolution_list.append(resolution_match.groups()[0])\n    return resolution_list\n\n\ndef get_unique_resolutions():\n    \"\"\"Return available resolutions, without duplicates and ordered with highest resolution first\"\"\"\n    return sorted(set(get_resolutions()), key=lambda x: int(x.split('x')[0]), reverse=True)\n\n\ndef get_current_resolution(monitor=0):\n    \"\"\"Return the current resolution for the desktop.\"\"\"\n    resolution = list()\n    for line in get_vidmodes():\n        if line.startswith(\"  \") and \"*\" in line:\n            resolution_match = re.match(r'.*?(\\d+x\\d+).*', line)\n            if resolution_match:\n                resolution.append(resolution_match.groups()[0])\n    if monitor == 'all':\n        return resolution\n    return resolution[monitor]\n\n\ndef change_resolution(resolution):\n    \"\"\"Change display resolution.\n\n    Takes a string for single monitors or a list of displays as returned\n    by get_outputs().\n    \"\"\"\n    if not resolution:\n        logger.warning(\"No resolution provided\")\n        return\n    if isinstance(resolution, str):\n        logger.debug(\"Switching resolution to %s\", resolution)\n\n        if resolution not in get_resolutions():\n            logger.warning(\"Resolution %s doesn't exist.\", resolution)\n        else:\n            logger.info(\"Changing resolution to %s\", resolution)\n            subprocess.Popen([\"xrandr\", \"-s\", resolution])\n    else:\n        for display in resolution:\n            display_name = display[0]\n            logger.debug(\"Switching to %s on %s\", display[1], display[0])\n            display_geom = display[1].split('+')\n            display_resolution = display_geom[0]\n            position = (display_geom[1], display_geom[2])\n\n            if (\n                    len(display) > 2 and\n                    display[2] in ('normal', 'left', 'right', 'inverted')\n            ):\n                rotation = display[2]\n            else:\n                rotation = \"normal\"\n            logger.info(\"Switching resolution of %s to %s\", display_name, display_resolution)\n            subprocess.Popen([\n                \"xrandr\",\n                \"--output\", display_name,\n                \"--mode\", display_resolution,\n                \"--pos\", \"{}x{}\".format(position[0], position[1]),\n                \"--rotate\", rotation\n            ]).communicate()\n\n\ndef restore_gamma():\n    \"\"\"Restores gamma to a normal level.\"\"\"\n    global XGAMMA_FOUND\n    if XGAMMA_FOUND is None:\n        XGAMMA_FOUND = system.find_executable('xgamma')\n    if XGAMMA_FOUND is True:\n        subprocess.Popen([\"xgamma\", \"-gamma\", \"1.0\"])\n    else:\n        logger.warning('xgamma is not available on your system')\n\n\ndef get_xrandr_version():\n    \"\"\"Return the major and minor version of XRandR utility\"\"\"\n    pattern = \"version\"\n    xrandr_output = subprocess.Popen([\"xrandr\", \"--version\"],\n                                     stdout=subprocess.PIPE).communicate()[0].decode()\n    position = xrandr_output.find(pattern) + len(pattern)\n    version_str = xrandr_output[position:].strip().split(\".\")\n    logger.debug(\"Found XrandR version %s\", version_str)\n    try:\n        return {\"major\": int(version_str[0]), \"minor\": int(version_str[1])}\n    except ValueError:\n        logger.error(\"Can't find version in: %s\", xrandr_output)\n        return {\"major\": 0, \"minor\": 0}\n\n\ndef get_graphics_adapaters():\n    \"\"\"Return the list of graphics cards available on a system\n\n    Returns:\n        list: list of tuples containing PCI ID and description of the VGA adapter\n    \"\"\"\n\n    if not system.find_executable('lspci'):\n        logger.warning('lspci is not available. List of graphics cards not available')\n        return []\n    return [\n        (pci_id, vga_desc.split(': ')[1]) for pci_id, vga_desc in [\n            line.split(maxsplit=1)\n            for line in system.execute('lspci').split('\\n')\n            if 'VGA' in line\n        ]\n    ]\n\n\ndef get_providers():\n    \"\"\"Return the list of available graphic cards\"\"\"\n    pattern = \"name:\"\n    providers = []\n    version = get_xrandr_version()\n\n    if version[\"major\"] == 1 and version[\"minor\"] >= 4:\n        logger.debug(\"Retrieving providers from XrandR\")\n        xrandr_output = subprocess.Popen([\"xrandr\", \"--listproviders\"],\n                                         stdout=subprocess.PIPE).communicate()[0].decode()\n        for line in xrandr_output.split(\"\\n\"):\n            if line.find(\"Provider \") != 0:\n                continue\n            position = line.find(pattern) + len(pattern)\n            providers.append(line[position:].strip())\n\n    return providers\n\n\nclass LegacyDisplayManager:\n    @staticmethod\n    def get_resolutions():\n        return get_resolutions()\n\n    @staticmethod\n    def get_display_names():\n        return get_output_names()\n\n\nclass DisplayManager(object):\n    def __init__(self):\n        self.screen = Gdk.Screen.get_default()\n        self.rr_screen = GnomeDesktop.RRScreen.new(self.screen)\n        self.rr_config = GnomeDesktop.RRConfig.new_current(self.rr_screen)\n        self.rr_config.load_current()\n\n    @property\n    def outputs(self):\n        return self.rr_screen.list_outputs()\n\n    def get_display_names(self):\n        return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()]\n\n    def get_output_modes(self, output):\n        logger.debug(\"Retrieving modes for %s\", output)\n        resolutions = []\n        for mode in output.list_modes():\n            resolution = \"%sx%s\" % (mode.get_width(), mode.get_height())\n            if resolution not in resolutions:\n                resolutions.append(resolution)\n        return resolutions\n\n    def get_resolutions(self):\n        resolutions = []\n        for mode in self.rr_screen.list_modes():\n            resolutions.append(\"%sx%s\" % (mode.get_width(), mode.get_height()))\n        return sorted(set(resolutions), key=lambda x: int(x.split('x')[0]), reverse=True)\n\n\ntry:\n    DISPLAY_MANAGER = DisplayManager()\nexcept GLib.Error:\n    DISPLAY_MANAGER = LegacyDisplayManager()\n\nUSE_DRI_PRIME = len(get_graphics_adapaters()) > 1\n\n\ndef get_resolution_choices():\n    \"\"\"Return list of available resolutions as label, value tuples\n    suitable for inclusion in drop-downs.\n    \"\"\"\n    resolutions = DISPLAY_MANAGER.get_resolutions()\n    resolution_choices = list(zip(resolutions, resolutions))\n    resolution_choices.insert(0, (\"Keep current\", 'off'))\n    return resolution_choices\n\n\ndef get_output_choices():\n    \"\"\"Return list of outputs for drop-downs\"\"\"\n    displays = DISPLAY_MANAGER.get_display_names()\n    output_choices = list(zip(displays, displays))\n    output_choices.insert(0, (\"Off\", 'off'))\n    return output_choices\n\n\ndef get_output_list():\n    \"\"\"Return a list of output with their index.\n    This is used to indicate to SDL 1.2 which monitor to use.\n    \"\"\"\n    choices = [\n        ('Off', 'off'),\n    ]\n    displays = DISPLAY_MANAGER.get_display_names()\n    for index, output in enumerate(displays):\n        # Display name can't be used because they might not be in the right order\n        # Using DISPLAYS to get the number of connected monitors\n        choices.append((output, str(index)))\n    return choices\n", "path": "lutris/util/display.py"}], "after_files": [{"content": "import re\nimport subprocess\nimport time\n\nimport gi\ngi.require_version('GnomeDesktop', '3.0')\n\nfrom gi.repository import Gdk, GnomeDesktop, GLib\n\nfrom lutris.util import system\nfrom lutris.util.log import logger\n\nXRANDR_CACHE = None\nXRANDR_CACHE_SET_AT = None\nXGAMMA_FOUND = None\n\ndef cached(function):\n    def wrapper():\n        global XRANDR_CACHE\n        global XRANDR_CACHE_SET_AT\n\n        if XRANDR_CACHE and time.time() - XRANDR_CACHE_SET_AT < 60:\n            return XRANDR_CACHE\n        XRANDR_CACHE = function()\n        XRANDR_CACHE_SET_AT = time.time()\n        return XRANDR_CACHE\n    return wrapper\n\n\n@cached\ndef get_vidmodes():\n    \"\"\"Return video modes from XrandR\"\"\"\n    logger.debug(\"Retrieving video modes from XrandR\")\n    xrandr_output = subprocess.Popen([\"xrandr\"],\n                                     stdout=subprocess.PIPE).communicate()[0]\n    return list([line for line in xrandr_output.decode().split(\"\\n\")])\n\n\ndef get_outputs():\n    \"\"\"Return list of tuples containing output name and geometry.\"\"\"\n    outputs = []\n    vid_modes = get_vidmodes()\n    if not vid_modes:\n        logger.error(\"xrandr didn't return anything\")\n        return []\n    for line in vid_modes:\n        parts = line.split()\n        if len(parts) < 2:\n            continue\n        if parts[1] == 'connected':\n            if len(parts) == 2:\n                continue\n            if parts[2] != 'primary':\n                geom = parts[2]\n                rotate = parts[3]\n            else:\n                geom = parts[3]\n                rotate = parts[4]\n            if geom.startswith('('):  # Screen turned off, no geometry\n                continue\n            if rotate.startswith('('):  # Screen not rotated, no need to include\n                outputs.append((parts[0], geom, \"normal\"))\n            else:\n                if rotate in (\"left\", \"right\"):\n                    geom_parts = geom.split('+')\n                    x_y = geom_parts[0].split('x')\n                    geom = \"{}x{}+{}+{}\".format(x_y[1], x_y[0], geom_parts[1], geom_parts[2])\n                outputs.append((parts[0], geom, rotate))\n    return outputs\n\n\ndef get_output_names():\n    \"\"\"Return output names from XrandR\"\"\"\n    return [output[0] for output in get_outputs()]\n\n\ndef turn_off_except(display):\n    \"\"\"Use XrandR to turn off displays except the one referenced by `display`\"\"\"\n    if not display:\n        logger.error(\"No active display given, no turning off every display\")\n        return\n    for output in get_outputs():\n        if output[0] != display:\n            logger.info(\"Turning off %s\", output[0])\n            subprocess.Popen([\"xrandr\", \"--output\", output[0], \"--off\"])\n\n\ndef get_resolutions():\n    \"\"\"Return the list of supported screen resolutions.\"\"\"\n    resolution_list = []\n    for line in get_vidmodes():\n        if line.startswith(\"  \"):\n            resolution_match = re.match(r'.*?(\\d+x\\d+).*', line)\n            if resolution_match:\n                resolution_list.append(resolution_match.groups()[0])\n    return resolution_list\n\n\ndef get_unique_resolutions():\n    \"\"\"Return available resolutions, without duplicates and ordered with highest resolution first\"\"\"\n    return sorted(set(get_resolutions()), key=lambda x: int(x.split('x')[0]), reverse=True)\n\n\ndef get_current_resolution(monitor=0):\n    \"\"\"Return the current resolution for the desktop.\"\"\"\n    resolution = list()\n    for line in get_vidmodes():\n        if line.startswith(\"  \") and \"*\" in line:\n            resolution_match = re.match(r'.*?(\\d+x\\d+).*', line)\n            if resolution_match:\n                resolution.append(resolution_match.groups()[0])\n    if monitor == 'all':\n        return resolution\n    return resolution[monitor]\n\n\ndef change_resolution(resolution):\n    \"\"\"Change display resolution.\n\n    Takes a string for single monitors or a list of displays as returned\n    by get_outputs().\n    \"\"\"\n    if not resolution:\n        logger.warning(\"No resolution provided\")\n        return\n    if isinstance(resolution, str):\n        logger.debug(\"Switching resolution to %s\", resolution)\n\n        if resolution not in get_resolutions():\n            logger.warning(\"Resolution %s doesn't exist.\", resolution)\n        else:\n            logger.info(\"Changing resolution to %s\", resolution)\n            subprocess.Popen([\"xrandr\", \"-s\", resolution])\n    else:\n        for display in resolution:\n            display_name = display[0]\n            logger.debug(\"Switching to %s on %s\", display[1], display[0])\n            display_geom = display[1].split('+')\n            display_resolution = display_geom[0]\n            position = (display_geom[1], display_geom[2])\n\n            if (\n                    len(display) > 2 and\n                    display[2] in ('normal', 'left', 'right', 'inverted')\n            ):\n                rotation = display[2]\n            else:\n                rotation = \"normal\"\n            logger.info(\"Switching resolution of %s to %s\", display_name, display_resolution)\n            subprocess.Popen([\n                \"xrandr\",\n                \"--output\", display_name,\n                \"--mode\", display_resolution,\n                \"--pos\", \"{}x{}\".format(position[0], position[1]),\n                \"--rotate\", rotation\n            ]).communicate()\n\n\ndef restore_gamma():\n    \"\"\"Restores gamma to a normal level.\"\"\"\n    global XGAMMA_FOUND\n    if XGAMMA_FOUND is None:\n        XGAMMA_FOUND = bool(system.find_executable('xgamma'))\n    if XGAMMA_FOUND is True:\n        subprocess.Popen([\"xgamma\", \"-gamma\", \"1.0\"])\n    else:\n        logger.warning('xgamma is not available on your system')\n\n\ndef get_xrandr_version():\n    \"\"\"Return the major and minor version of XRandR utility\"\"\"\n    pattern = \"version\"\n    xrandr_output = subprocess.Popen([\"xrandr\", \"--version\"],\n                                     stdout=subprocess.PIPE).communicate()[0].decode()\n    position = xrandr_output.find(pattern) + len(pattern)\n    version_str = xrandr_output[position:].strip().split(\".\")\n    logger.debug(\"Found XrandR version %s\", version_str)\n    try:\n        return {\"major\": int(version_str[0]), \"minor\": int(version_str[1])}\n    except ValueError:\n        logger.error(\"Can't find version in: %s\", xrandr_output)\n        return {\"major\": 0, \"minor\": 0}\n\n\ndef get_graphics_adapaters():\n    \"\"\"Return the list of graphics cards available on a system\n\n    Returns:\n        list: list of tuples containing PCI ID and description of the VGA adapter\n    \"\"\"\n\n    if not system.find_executable('lspci'):\n        logger.warning('lspci is not available. List of graphics cards not available')\n        return []\n    return [\n        (pci_id, vga_desc.split(': ')[1]) for pci_id, vga_desc in [\n            line.split(maxsplit=1)\n            for line in system.execute('lspci').split('\\n')\n            if 'VGA' in line\n        ]\n    ]\n\n\ndef get_providers():\n    \"\"\"Return the list of available graphic cards\"\"\"\n    pattern = \"name:\"\n    providers = []\n    version = get_xrandr_version()\n\n    if version[\"major\"] == 1 and version[\"minor\"] >= 4:\n        logger.debug(\"Retrieving providers from XrandR\")\n        xrandr_output = subprocess.Popen([\"xrandr\", \"--listproviders\"],\n                                         stdout=subprocess.PIPE).communicate()[0].decode()\n        for line in xrandr_output.split(\"\\n\"):\n            if line.find(\"Provider \") != 0:\n                continue\n            position = line.find(pattern) + len(pattern)\n            providers.append(line[position:].strip())\n\n    return providers\n\n\nclass LegacyDisplayManager:\n    @staticmethod\n    def get_resolutions():\n        return get_resolutions()\n\n    @staticmethod\n    def get_display_names():\n        return get_output_names()\n\n\nclass DisplayManager(object):\n    def __init__(self):\n        self.screen = Gdk.Screen.get_default()\n        self.rr_screen = GnomeDesktop.RRScreen.new(self.screen)\n        self.rr_config = GnomeDesktop.RRConfig.new_current(self.rr_screen)\n        self.rr_config.load_current()\n\n    @property\n    def outputs(self):\n        return self.rr_screen.list_outputs()\n\n    def get_display_names(self):\n        return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()]\n\n    def get_output_modes(self, output):\n        logger.debug(\"Retrieving modes for %s\", output)\n        resolutions = []\n        for mode in output.list_modes():\n            resolution = \"%sx%s\" % (mode.get_width(), mode.get_height())\n            if resolution not in resolutions:\n                resolutions.append(resolution)\n        return resolutions\n\n    def get_resolutions(self):\n        resolutions = []\n        for mode in self.rr_screen.list_modes():\n            resolutions.append(\"%sx%s\" % (mode.get_width(), mode.get_height()))\n        return sorted(set(resolutions), key=lambda x: int(x.split('x')[0]), reverse=True)\n\n\ntry:\n    DISPLAY_MANAGER = DisplayManager()\nexcept GLib.Error:\n    DISPLAY_MANAGER = LegacyDisplayManager()\n\nUSE_DRI_PRIME = len(get_graphics_adapaters()) > 1\n\n\ndef get_resolution_choices():\n    \"\"\"Return list of available resolutions as label, value tuples\n    suitable for inclusion in drop-downs.\n    \"\"\"\n    resolutions = DISPLAY_MANAGER.get_resolutions()\n    resolution_choices = list(zip(resolutions, resolutions))\n    resolution_choices.insert(0, (\"Keep current\", 'off'))\n    return resolution_choices\n\n\ndef get_output_choices():\n    \"\"\"Return list of outputs for drop-downs\"\"\"\n    displays = DISPLAY_MANAGER.get_display_names()\n    output_choices = list(zip(displays, displays))\n    output_choices.insert(0, (\"Off\", 'off'))\n    return output_choices\n\n\ndef get_output_list():\n    \"\"\"Return a list of output with their index.\n    This is used to indicate to SDL 1.2 which monitor to use.\n    \"\"\"\n    choices = [\n        ('Off', 'off'),\n    ]\n    displays = DISPLAY_MANAGER.get_display_names()\n    for index, output in enumerate(displays):\n        # Display name can't be used because they might not be in the right order\n        # Using DISPLAYS to get the number of connected monitors\n        choices.append((output, str(index)))\n    return choices\n", "path": "lutris/util/display.py"}]}