We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Lutris can not launch a game through a .desktop file
Hello, first of all thank you for the amazing work you have done in the linux gaming scene.

When Lutris imports a game from the local source (a desktop file in .local/share/applications), it can not be launched.
I am using ULWGL for launching the game through the desktop file.

[Desktop Entry]
Type=Application
Categories=Game
Name=Grim Dawn
Icon=/usr/share/icons/Papirus-Dark/16x16/places/folder-games.svg
Path=/HDD/GranAlmacen/Juegos/Grim Dawn
Exec=env GAMEID="" WINEPREFIX="/HDD/GranAlmacen/Juegos/Grim Dawn/wine" ulwgl-run-cli "x64/Grim Dawn.exe"

Cheers !

--- 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/services/xdg.py`
Content:
```
1 """XDG applications service"""
2 import json
3 import os
4 import re
5 import shlex
6 import subprocess
7 from gettext import gettext as _
8 
9 from gi.repository import Gio
10 
11 from lutris import settings
12 from lutris.database.games import get_games_where
13 from lutris.services.base import BaseService
14 from lutris.services.service_game import ServiceGame
15 from lutris.services.service_media import ServiceMedia
16 from lutris.util import system
17 from lutris.util.log import logger
18 from lutris.util.strings import slugify
19 
20 
21 def get_appid(app):
22     """Get the appid for the game"""
23     try:
24         return os.path.splitext(app.get_id())[0]
25     except UnicodeDecodeError:
26         logger.exception(
27             "Failed to read ID for app %s (non UTF-8 encoding). Reverting to executable name.",
28             app,
29         )
30         return app.get_executable()
31 
32 
33 class XDGMedia(ServiceMedia):
34     service = "xdg"
35     source = "local"
36     size = (64, 64)
37     dest_path = os.path.join(settings.CACHE_DIR, "xdg/icons")
38     file_patterns = ["%s.png"]
39 
40 
41 class XDGService(BaseService):
42     id = "xdg"
43     name = _("Local")
44     icon = "linux"
45     runner = "linux"
46     online = False
47     local = True
48     medias = {"icon": XDGMedia}
49 
50     ignored_games = ("lutris",)
51     ignored_executables = ("lutris", "steam")
52     ignored_categories = ("Emulator", "Development", "Utility")
53 
54     @classmethod
55     def iter_xdg_games(cls):
56         """Iterates through XDG games only"""
57         for app in Gio.AppInfo.get_all():
58             if cls._is_importable(app):
59                 yield app
60 
61     @property
62     def lutris_games(self):
63         """Iterates through Lutris games imported from XDG"""
64         for game in get_games_where(runner=XDGGame.runner, installer_slug=XDGGame.installer_slug, installed=1):
65             yield game
66 
67     @classmethod
68     def _is_importable(cls, app):
69         """Returns whether a XDG game is importable to Lutris"""
70         appid = get_appid(app)
71         executable = app.get_executable() or ""
72         if any(
73             [
74                 app.get_nodisplay() or app.get_is_hidden(),  # App is hidden
75                 not executable,  # Check app has an executable
76                 appid.startswith("net.lutris"),  # Skip lutris created shortcuts
77                 appid.lower() in map(str.lower, cls.ignored_games),  # game blacklisted
78                 executable.lower() in cls.ignored_executables,  # exe blacklisted
79             ]
80         ):
81             return False
82 
83         # must be in Game category
84         categories = app.get_categories() or ""
85         categories = list(filter(None, categories.lower().split(";")))
86         if "game" not in categories:
87             return False
88 
89         # contains a blacklisted category
90         ignored_categories = set(c.casefold() for c in cls.ignored_categories)
91         if any(c for c in categories if c in ignored_categories):
92             return False
93         return True
94 
95     def match_games(self):
96         """XDG games aren't on the lutris website"""
97         return
98 
99     def load(self):
100         """Return the list of games stored in the XDG menu."""
101         xdg_games = [XDGGame.new_from_xdg_app(app) for app in self.iter_xdg_games()]
102         for game in xdg_games:
103             game.save()
104         return xdg_games
105 
106     def generate_installer(self, db_game):
107         details = json.loads(db_game["details"])
108         return {
109             "name": db_game["name"],
110             "version": "XDG",
111             "slug": db_game["slug"],
112             "game_slug": self.get_installed_slug(db_game),
113             "runner": self.get_installed_runner_name(db_game),
114             "script": {
115                 "game": {
116                     "exe": details["exe"],
117                     "args": details["args"],
118                 },
119                 "system": {"disable_runtime": True},
120             },
121         }
122 
123     def get_installed_runner_name(self, db_game):
124         return self.runner
125 
126     def get_game_directory(self, installer):
127         """Pull install location from installer"""
128         return os.path.dirname(installer["script"]["game"]["exe"])
129 
130 
131 class XDGGame(ServiceGame):
132     """XDG game (Linux game with a desktop launcher)"""
133 
134     service = "xdg"
135     runner = "linux"
136     installer_slug = "desktopapp"
137 
138     @staticmethod
139     def get_app_icon(xdg_app):
140         """Return the name of the icon for an XDG app if one if set"""
141         icon = xdg_app.get_icon()
142         if not icon:
143             return ""
144         return icon.to_string()
145 
146     @classmethod
147     def new_from_xdg_app(cls, xdg_app):
148         """Create a service game from a XDG entry"""
149         service_game = cls()
150         service_game.name = xdg_app.get_display_name()
151         service_game.icon = cls.get_app_icon(xdg_app)
152         service_game.appid = get_appid(xdg_app)
153         service_game.slug = cls.get_slug(xdg_app)
154         exe, args = cls.get_command_args(xdg_app)
155         service_game.details = json.dumps(
156             {
157                 "exe": exe,
158                 "args": args,
159             }
160         )
161         return service_game
162 
163     @staticmethod
164     def get_command_args(app):
165         """Return a tuple with absolute command path and an argument string"""
166         command = shlex.split(app.get_commandline())
167         # remove %U etc. and change %% to % in arguments
168         args = list(map(lambda arg: re.sub("%[^%]", "", arg).replace("%%", "%"), command[1:]))
169         exe = command[0]
170         if not exe.startswith("/"):
171             exe = system.find_executable(exe)
172         return exe, subprocess.list2cmdline(args)
173 
174     @staticmethod
175     def get_slug(xdg_app):
176         """Get the slug from the game name"""
177         return slugify(xdg_app.get_display_name()) or slugify(get_appid(xdg_app))
178
```
--- 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/services/xdg.py b/lutris/services/xdg.py
--- a/lutris/services/xdg.py
+++ b/lutris/services/xdg.py
@@ -115,6 +115,7 @@
                 "game": {
                     "exe": details["exe"],
                     "args": details["args"],
+                    "working_dir": details["path"],
                 },
                 "system": {"disable_runtime": True},
             },
@@ -152,14 +153,27 @@
         service_game.appid = get_appid(xdg_app)
         service_game.slug = cls.get_slug(xdg_app)
         exe, args = cls.get_command_args(xdg_app)
+        path = cls.get_desktop_entry_path(xdg_app)
         service_game.details = json.dumps(
             {
                 "exe": exe,
                 "args": args,
+                "path": path,
             }
         )
         return service_game
 
+    @staticmethod
+    def get_desktop_entry_path(xdg_app):
+        """Retrieve the Path variable from the .desktop file"""
+        desktop_entry = xdg_app.get_filename()
+        with open(desktop_entry, 'r') as f:
+            contents = f.read()
+            match = re.search(r'^Path=(.*)$', contents, re.MULTILINE)
+            if match:
+                return match.group(1)
+        return None
+
     @staticmethod
     def get_command_args(app):
         """Return a tuple with absolute command path and an argument string"""

{"golden_diff": "diff --git a/lutris/services/xdg.py b/lutris/services/xdg.py\n--- a/lutris/services/xdg.py\n+++ b/lutris/services/xdg.py\n@@ -115,6 +115,7 @@\n                 \"game\": {\n                     \"exe\": details[\"exe\"],\n                     \"args\": details[\"args\"],\n+                    \"working_dir\": details[\"path\"],\n                 },\n                 \"system\": {\"disable_runtime\": True},\n             },\n@@ -152,14 +153,27 @@\n         service_game.appid = get_appid(xdg_app)\n         service_game.slug = cls.get_slug(xdg_app)\n         exe, args = cls.get_command_args(xdg_app)\n+        path = cls.get_desktop_entry_path(xdg_app)\n         service_game.details = json.dumps(\n             {\n                 \"exe\": exe,\n                 \"args\": args,\n+                \"path\": path,\n             }\n         )\n         return service_game\n \n+    @staticmethod\n+    def get_desktop_entry_path(xdg_app):\n+        \"\"\"Retrieve the Path variable from the .desktop file\"\"\"\n+        desktop_entry = xdg_app.get_filename()\n+        with open(desktop_entry, 'r') as f:\n+            contents = f.read()\n+            match = re.search(r'^Path=(.*)$', contents, re.MULTILINE)\n+            if match:\n+                return match.group(1)\n+        return None\n+\n     @staticmethod\n     def get_command_args(app):\n         \"\"\"Return a tuple with absolute command path and an argument string\"\"\"\n", "issue": "Lutris can not launch a game through a .desktop file\nHello, first of all thank you for the amazing work you have done in the linux gaming scene.\r\n\r\nWhen Lutris imports a game from the local source (a desktop file in .local/share/applications), it can not be launched.\r\nI am using ULWGL for launching the game through the desktop file.\r\n\r\n[Desktop Entry]\r\nType=Application\r\nCategories=Game\r\nName=Grim Dawn\r\nIcon=/usr/share/icons/Papirus-Dark/16x16/places/folder-games.svg\r\nPath=/HDD/GranAlmacen/Juegos/Grim Dawn\r\nExec=env GAMEID=\"\" WINEPREFIX=\"/HDD/GranAlmacen/Juegos/Grim Dawn/wine\" ulwgl-run-cli \"x64/Grim Dawn.exe\"\r\n\r\nCheers !\n", "before_files": [{"content": "\"\"\"XDG applications service\"\"\"\nimport json\nimport os\nimport re\nimport shlex\nimport subprocess\nfrom gettext import gettext as _\n\nfrom gi.repository import Gio\n\nfrom lutris import settings\nfrom lutris.database.games import get_games_where\nfrom lutris.services.base import BaseService\nfrom lutris.services.service_game import ServiceGame\nfrom lutris.services.service_media import ServiceMedia\nfrom lutris.util import system\nfrom lutris.util.log import logger\nfrom lutris.util.strings import slugify\n\n\ndef get_appid(app):\n    \"\"\"Get the appid for the game\"\"\"\n    try:\n        return os.path.splitext(app.get_id())[0]\n    except UnicodeDecodeError:\n        logger.exception(\n            \"Failed to read ID for app %s (non UTF-8 encoding). Reverting to executable name.\",\n            app,\n        )\n        return app.get_executable()\n\n\nclass XDGMedia(ServiceMedia):\n    service = \"xdg\"\n    source = \"local\"\n    size = (64, 64)\n    dest_path = os.path.join(settings.CACHE_DIR, \"xdg/icons\")\n    file_patterns = [\"%s.png\"]\n\n\nclass XDGService(BaseService):\n    id = \"xdg\"\n    name = _(\"Local\")\n    icon = \"linux\"\n    runner = \"linux\"\n    online = False\n    local = True\n    medias = {\"icon\": XDGMedia}\n\n    ignored_games = (\"lutris\",)\n    ignored_executables = (\"lutris\", \"steam\")\n    ignored_categories = (\"Emulator\", \"Development\", \"Utility\")\n\n    @classmethod\n    def iter_xdg_games(cls):\n        \"\"\"Iterates through XDG games only\"\"\"\n        for app in Gio.AppInfo.get_all():\n            if cls._is_importable(app):\n                yield app\n\n    @property\n    def lutris_games(self):\n        \"\"\"Iterates through Lutris games imported from XDG\"\"\"\n        for game in get_games_where(runner=XDGGame.runner, installer_slug=XDGGame.installer_slug, installed=1):\n            yield game\n\n    @classmethod\n    def _is_importable(cls, app):\n        \"\"\"Returns whether a XDG game is importable to Lutris\"\"\"\n        appid = get_appid(app)\n        executable = app.get_executable() or \"\"\n        if any(\n            [\n                app.get_nodisplay() or app.get_is_hidden(),  # App is hidden\n                not executable,  # Check app has an executable\n                appid.startswith(\"net.lutris\"),  # Skip lutris created shortcuts\n                appid.lower() in map(str.lower, cls.ignored_games),  # game blacklisted\n                executable.lower() in cls.ignored_executables,  # exe blacklisted\n            ]\n        ):\n            return False\n\n        # must be in Game category\n        categories = app.get_categories() or \"\"\n        categories = list(filter(None, categories.lower().split(\";\")))\n        if \"game\" not in categories:\n            return False\n\n        # contains a blacklisted category\n        ignored_categories = set(c.casefold() for c in cls.ignored_categories)\n        if any(c for c in categories if c in ignored_categories):\n            return False\n        return True\n\n    def match_games(self):\n        \"\"\"XDG games aren't on the lutris website\"\"\"\n        return\n\n    def load(self):\n        \"\"\"Return the list of games stored in the XDG menu.\"\"\"\n        xdg_games = [XDGGame.new_from_xdg_app(app) for app in self.iter_xdg_games()]\n        for game in xdg_games:\n            game.save()\n        return xdg_games\n\n    def generate_installer(self, db_game):\n        details = json.loads(db_game[\"details\"])\n        return {\n            \"name\": db_game[\"name\"],\n            \"version\": \"XDG\",\n            \"slug\": db_game[\"slug\"],\n            \"game_slug\": self.get_installed_slug(db_game),\n            \"runner\": self.get_installed_runner_name(db_game),\n            \"script\": {\n                \"game\": {\n                    \"exe\": details[\"exe\"],\n                    \"args\": details[\"args\"],\n                },\n                \"system\": {\"disable_runtime\": True},\n            },\n        }\n\n    def get_installed_runner_name(self, db_game):\n        return self.runner\n\n    def get_game_directory(self, installer):\n        \"\"\"Pull install location from installer\"\"\"\n        return os.path.dirname(installer[\"script\"][\"game\"][\"exe\"])\n\n\nclass XDGGame(ServiceGame):\n    \"\"\"XDG game (Linux game with a desktop launcher)\"\"\"\n\n    service = \"xdg\"\n    runner = \"linux\"\n    installer_slug = \"desktopapp\"\n\n    @staticmethod\n    def get_app_icon(xdg_app):\n        \"\"\"Return the name of the icon for an XDG app if one if set\"\"\"\n        icon = xdg_app.get_icon()\n        if not icon:\n            return \"\"\n        return icon.to_string()\n\n    @classmethod\n    def new_from_xdg_app(cls, xdg_app):\n        \"\"\"Create a service game from a XDG entry\"\"\"\n        service_game = cls()\n        service_game.name = xdg_app.get_display_name()\n        service_game.icon = cls.get_app_icon(xdg_app)\n        service_game.appid = get_appid(xdg_app)\n        service_game.slug = cls.get_slug(xdg_app)\n        exe, args = cls.get_command_args(xdg_app)\n        service_game.details = json.dumps(\n            {\n                \"exe\": exe,\n                \"args\": args,\n            }\n        )\n        return service_game\n\n    @staticmethod\n    def get_command_args(app):\n        \"\"\"Return a tuple with absolute command path and an argument string\"\"\"\n        command = shlex.split(app.get_commandline())\n        # remove %U etc. and change %% to % in arguments\n        args = list(map(lambda arg: re.sub(\"%[^%]\", \"\", arg).replace(\"%%\", \"%\"), command[1:]))\n        exe = command[0]\n        if not exe.startswith(\"/\"):\n            exe = system.find_executable(exe)\n        return exe, subprocess.list2cmdline(args)\n\n    @staticmethod\n    def get_slug(xdg_app):\n        \"\"\"Get the slug from the game name\"\"\"\n        return slugify(xdg_app.get_display_name()) or slugify(get_appid(xdg_app))\n", "path": "lutris/services/xdg.py"}], "after_files": [{"content": "\"\"\"XDG applications service\"\"\"\nimport json\nimport os\nimport re\nimport shlex\nimport subprocess\nfrom gettext import gettext as _\n\nfrom gi.repository import Gio\n\nfrom lutris import settings\nfrom lutris.database.games import get_games_where\nfrom lutris.services.base import BaseService\nfrom lutris.services.service_game import ServiceGame\nfrom lutris.services.service_media import ServiceMedia\nfrom lutris.util import system\nfrom lutris.util.log import logger\nfrom lutris.util.strings import slugify\n\n\ndef get_appid(app):\n    \"\"\"Get the appid for the game\"\"\"\n    try:\n        return os.path.splitext(app.get_id())[0]\n    except UnicodeDecodeError:\n        logger.exception(\n            \"Failed to read ID for app %s (non UTF-8 encoding). Reverting to executable name.\",\n            app,\n        )\n        return app.get_executable()\n\n\nclass XDGMedia(ServiceMedia):\n    service = \"xdg\"\n    source = \"local\"\n    size = (64, 64)\n    dest_path = os.path.join(settings.CACHE_DIR, \"xdg/icons\")\n    file_patterns = [\"%s.png\"]\n\n\nclass XDGService(BaseService):\n    id = \"xdg\"\n    name = _(\"Local\")\n    icon = \"linux\"\n    runner = \"linux\"\n    online = False\n    local = True\n    medias = {\"icon\": XDGMedia}\n\n    ignored_games = (\"lutris\",)\n    ignored_executables = (\"lutris\", \"steam\")\n    ignored_categories = (\"Emulator\", \"Development\", \"Utility\")\n\n    @classmethod\n    def iter_xdg_games(cls):\n        \"\"\"Iterates through XDG games only\"\"\"\n        for app in Gio.AppInfo.get_all():\n            if cls._is_importable(app):\n                yield app\n\n    @property\n    def lutris_games(self):\n        \"\"\"Iterates through Lutris games imported from XDG\"\"\"\n        for game in get_games_where(runner=XDGGame.runner, installer_slug=XDGGame.installer_slug, installed=1):\n            yield game\n\n    @classmethod\n    def _is_importable(cls, app):\n        \"\"\"Returns whether a XDG game is importable to Lutris\"\"\"\n        appid = get_appid(app)\n        executable = app.get_executable() or \"\"\n        if any(\n            [\n                app.get_nodisplay() or app.get_is_hidden(),  # App is hidden\n                not executable,  # Check app has an executable\n                appid.startswith(\"net.lutris\"),  # Skip lutris created shortcuts\n                appid.lower() in map(str.lower, cls.ignored_games),  # game blacklisted\n                executable.lower() in cls.ignored_executables,  # exe blacklisted\n            ]\n        ):\n            return False\n\n        # must be in Game category\n        categories = app.get_categories() or \"\"\n        categories = list(filter(None, categories.lower().split(\";\")))\n        if \"game\" not in categories:\n            return False\n\n        # contains a blacklisted category\n        ignored_categories = set(c.casefold() for c in cls.ignored_categories)\n        if any(c for c in categories if c in ignored_categories):\n            return False\n        return True\n\n    def match_games(self):\n        \"\"\"XDG games aren't on the lutris website\"\"\"\n        return\n\n    def load(self):\n        \"\"\"Return the list of games stored in the XDG menu.\"\"\"\n        xdg_games = [XDGGame.new_from_xdg_app(app) for app in self.iter_xdg_games()]\n        for game in xdg_games:\n            game.save()\n        return xdg_games\n\n    def generate_installer(self, db_game):\n        details = json.loads(db_game[\"details\"])\n        return {\n            \"name\": db_game[\"name\"],\n            \"version\": \"XDG\",\n            \"slug\": db_game[\"slug\"],\n            \"game_slug\": self.get_installed_slug(db_game),\n            \"runner\": self.get_installed_runner_name(db_game),\n            \"script\": {\n                \"game\": {\n                    \"exe\": details[\"exe\"],\n                    \"args\": details[\"args\"],\n                    \"working_dir\": details[\"path\"],\n                },\n                \"system\": {\"disable_runtime\": True},\n            },\n        }\n\n    def get_installed_runner_name(self, db_game):\n        return self.runner\n\n    def get_game_directory(self, installer):\n        \"\"\"Pull install location from installer\"\"\"\n        return os.path.dirname(installer[\"script\"][\"game\"][\"exe\"])\n\n\nclass XDGGame(ServiceGame):\n    \"\"\"XDG game (Linux game with a desktop launcher)\"\"\"\n\n    service = \"xdg\"\n    runner = \"linux\"\n    installer_slug = \"desktopapp\"\n\n    @staticmethod\n    def get_app_icon(xdg_app):\n        \"\"\"Return the name of the icon for an XDG app if one if set\"\"\"\n        icon = xdg_app.get_icon()\n        if not icon:\n            return \"\"\n        return icon.to_string()\n\n    @classmethod\n    def new_from_xdg_app(cls, xdg_app):\n        \"\"\"Create a service game from a XDG entry\"\"\"\n        service_game = cls()\n        service_game.name = xdg_app.get_display_name()\n        service_game.icon = cls.get_app_icon(xdg_app)\n        service_game.appid = get_appid(xdg_app)\n        service_game.slug = cls.get_slug(xdg_app)\n        exe, args = cls.get_command_args(xdg_app)\n        path = cls.get_desktop_entry_path(xdg_app)\n        service_game.details = json.dumps(\n            {\n                \"exe\": exe,\n                \"args\": args,\n                \"path\": path,\n            }\n        )\n        return service_game\n\n    @staticmethod\n    def get_desktop_entry_path(xdg_app):\n        \"\"\"Retrieve the Path variable from the .desktop file\"\"\"\n        desktop_entry = xdg_app.get_filename()\n        with open(desktop_entry, 'r') as f:\n            contents = f.read()\n            match = re.search(r'^Path=(.*)$', contents, re.MULTILINE)\n            if match:\n                return match.group(1)\n        return None\n\n    @staticmethod\n    def get_command_args(app):\n        \"\"\"Return a tuple with absolute command path and an argument string\"\"\"\n        command = shlex.split(app.get_commandline())\n        # remove %U etc. and change %% to % in arguments\n        args = list(map(lambda arg: re.sub(\"%[^%]\", \"\", arg).replace(\"%%\", \"%\"), command[1:]))\n        exe = command[0]\n        if not exe.startswith(\"/\"):\n            exe = system.find_executable(exe)\n        return exe, subprocess.list2cmdline(args)\n\n    @staticmethod\n    def get_slug(xdg_app):\n        \"\"\"Get the slug from the game name\"\"\"\n        return slugify(xdg_app.get_display_name()) or slugify(get_appid(xdg_app))\n", "path": "lutris/services/xdg.py"}]}