Skip to content

Commit 2627757

Browse files
committed
fix[close #3427]: Impossible to run .exe from filemanager
1 parent 90fd37d commit 2627757

File tree

2 files changed

+172
-48
lines changed

2 files changed

+172
-48
lines changed

bottles/frontend/cli/cli.py

+154-46
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import gi
2929

3030
warnings.filterwarnings("ignore") # suppress GTK warnings
31-
gi.require_version('Gtk', '4.0')
31+
gi.require_version("Gtk", "4.0")
3232

3333
APP_VERSION = "@APP_VERSION@"
3434
pkgdatadir = "@pkgdatadir@"
@@ -69,83 +69,169 @@ class CLI:
6969
def __init__(self):
7070
# self.__clear()
7171

72-
self.parser = argparse.ArgumentParser(description="Bottles is a tool to manage your bottles")
73-
self.parser.add_argument("-v", "--version", action="version", version=f"Bottles {APP_VERSION}")
74-
self.parser.add_argument("-j", "--json", action="store_true", help="Outputs in JSON format")
72+
self.parser = argparse.ArgumentParser(
73+
description="Bottles is a tool to manage your bottles"
74+
)
75+
self.parser.add_argument(
76+
"-v", "--version", action="version", version=f"Bottles {APP_VERSION}"
77+
)
78+
self.parser.add_argument(
79+
"-j", "--json", action="store_true", help="Outputs in JSON format"
80+
)
7581

76-
subparsers = self.parser.add_subparsers(dest='command', help='sub-command help')
82+
subparsers = self.parser.add_subparsers(dest="command", help="sub-command help")
7783

78-
info_parser = subparsers.add_parser("info", help="Show information about Bottles")
79-
info_parser.add_argument('type', choices=['bottles-path', 'health-check'], help="Type of information")
84+
info_parser = subparsers.add_parser(
85+
"info", help="Show information about Bottles"
86+
)
87+
info_parser.add_argument(
88+
"type", choices=["bottles-path", "health-check"], help="Type of information"
89+
)
8090

8191
list_parser = subparsers.add_parser("list", help="List entities")
82-
list_parser.add_argument('type', choices=['bottles', 'components'], help="Type of entity")
83-
list_parser.add_argument("-f", "--filter", help="Filter bottles and components (e.g. '-f 'environment:gaming')")
92+
list_parser.add_argument(
93+
"type", choices=["bottles", "components"], help="Type of entity"
94+
)
95+
list_parser.add_argument(
96+
"-f",
97+
"--filter",
98+
help="Filter bottles and components (e.g. '-f 'environment:gaming')",
99+
)
84100

85101
programs_parser = subparsers.add_parser("programs", help="List programs")
86-
programs_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
102+
programs_parser.add_argument(
103+
"-b", "--bottle", help="Bottle name", required=True
104+
)
87105

88106
add_parser = subparsers.add_parser("add", help="Add program")
89107
add_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
90108
add_parser.add_argument("-n", "--name", help="Program name", required=True)
91109
add_parser.add_argument("-p", "--path", help="Program path", required=True)
92110
add_parser.add_argument("-l", "--launch-options", help="Program launch options")
93-
add_parser.add_argument("--no-dxvk", action="store_true", help="Disable DXVK for the program")
94-
add_parser.add_argument("--no-vkd3d", action="store_true", help="Disable VKD3D for the program")
95-
add_parser.add_argument("--no-dxvk-nvapi", action="store_true", help="Disable DXVK Nvapi for the program")
111+
add_parser.add_argument(
112+
"--no-dxvk", action="store_true", help="Disable DXVK for the program"
113+
)
114+
add_parser.add_argument(
115+
"--no-vkd3d", action="store_true", help="Disable VKD3D for the program"
116+
)
117+
add_parser.add_argument(
118+
"--no-dxvk-nvapi",
119+
action="store_true",
120+
help="Disable DXVK Nvapi for the program",
121+
)
96122

97123
tools_parser = subparsers.add_parser("tools", help="Launch Wine tools")
98-
tools_parser.add_argument('tool', choices=['cmd', 'winecfg', 'uninstaller', 'regedit', 'taskmgr', 'control',
99-
'explorer'], help="Tool to launch")
124+
tools_parser.add_argument(
125+
"tool",
126+
choices=[
127+
"cmd",
128+
"winecfg",
129+
"uninstaller",
130+
"regedit",
131+
"taskmgr",
132+
"control",
133+
"explorer",
134+
],
135+
help="Tool to launch",
136+
)
100137
tools_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
101138

102139
reg_parser = subparsers.add_parser("reg", help="Manage registry")
103-
reg_parser.add_argument('action', choices=['add', 'edit', 'del'], help="Action to perform")
140+
reg_parser.add_argument(
141+
"action", choices=["add", "edit", "del"], help="Action to perform"
142+
)
104143
reg_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
105144
reg_parser.add_argument("-k", "--key", help="Registry key", required=True)
106145
reg_parser.add_argument("-v", "--value", help="Registry value", required=True)
107146
reg_parser.add_argument("-d", "--data", help="Data to be set")
108-
reg_parser.add_argument("-t", "--key-type", help="Data type",
109-
choices=['REG_DWORD', 'REG_SZ', 'REG_BINARY', 'REG_MULTI_SZ'])
147+
reg_parser.add_argument(
148+
"-t",
149+
"--key-type",
150+
help="Data type",
151+
choices=["REG_DWORD", "REG_SZ", "REG_BINARY", "REG_MULTI_SZ"],
152+
)
110153

111154
edit_parser = subparsers.add_parser("edit", help="Edit a bottle configuration")
112155
edit_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
113-
edit_parser.add_argument("--params", help="Set parameters (e.g. '-p dxvk:true')")
114-
edit_parser.add_argument("--env-var",
115-
help="Add new environment variable (e.g. '-env-var WINEDEBUG=-all')")
116-
edit_parser.add_argument("--win", help="Change Windows version (e.g. '--win win7')")
117-
edit_parser.add_argument("--runner", help="Change Runner (e.g. '--runner caffe-7.2')")
118-
edit_parser.add_argument("--dxvk", help="Change DXVK (e.g. '--dxvk dxvk-1.9.0')")
119-
edit_parser.add_argument("--vkd3d", help="Change VKD3D (e.g. '--vkd3d vkd3d-proton-2.6')")
120-
edit_parser.add_argument("--nvapi", help="Change DXVK-Nvapi (e.g. '--nvapi dxvk-nvapi-1.9.0')")
121-
edit_parser.add_argument("--latencyflex", help="Change LatencyFleX (e.g. '--latencyflex latencyflex-v0.1.0')")
156+
edit_parser.add_argument(
157+
"--params", help="Set parameters (e.g. '-p dxvk:true')"
158+
)
159+
edit_parser.add_argument(
160+
"--env-var",
161+
help="Add new environment variable (e.g. '-env-var WINEDEBUG=-all')",
162+
)
163+
edit_parser.add_argument(
164+
"--win", help="Change Windows version (e.g. '--win win7')"
165+
)
166+
edit_parser.add_argument(
167+
"--runner", help="Change Runner (e.g. '--runner caffe-7.2')"
168+
)
169+
edit_parser.add_argument(
170+
"--dxvk", help="Change DXVK (e.g. '--dxvk dxvk-1.9.0')"
171+
)
172+
edit_parser.add_argument(
173+
"--vkd3d", help="Change VKD3D (e.g. '--vkd3d vkd3d-proton-2.6')"
174+
)
175+
edit_parser.add_argument(
176+
"--nvapi", help="Change DXVK-Nvapi (e.g. '--nvapi dxvk-nvapi-1.9.0')"
177+
)
178+
edit_parser.add_argument(
179+
"--latencyflex",
180+
help="Change LatencyFleX (e.g. '--latencyflex latencyflex-v0.1.0')",
181+
)
122182

123183
new_parser = subparsers.add_parser("new", help="Create a new bottle")
124184
new_parser.add_argument("--bottle-name", help="Bottle name", required=True)
125-
new_parser.add_argument("--environment", help="Environment to apply (gaming|application|custom)", required=True)
126-
new_parser.add_argument("--custom-environment", help="Path to a custom environment.yml file")
185+
new_parser.add_argument(
186+
"--environment",
187+
help="Environment to apply (gaming|application|custom)",
188+
required=True,
189+
)
190+
new_parser.add_argument(
191+
"--custom-environment", help="Path to a custom environment.yml file"
192+
)
127193
new_parser.add_argument("--arch", help="Architecture (win32|win64)")
128194
new_parser.add_argument("--runner", help="Name of the runner to be used")
129195
new_parser.add_argument("--dxvk", help="Name of the dxvk to be used")
130196
new_parser.add_argument("--vkd3d", help="Name of the vkd3d to be used")
131197
new_parser.add_argument("--nvapi", help="Name of the dxvk-nvapi to be used")
132-
new_parser.add_argument("--latencyflex", help="Name of the latencyflex to be used")
198+
new_parser.add_argument(
199+
"--latencyflex", help="Name of the latencyflex to be used"
200+
)
133201

134202
run_parser = subparsers.add_parser("run", help="Run a program")
135203
run_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
136204
run_parser.add_argument("-e", "--executable", help="Path to the executable")
137205
run_parser.add_argument("-p", "--program", help="Program to run")
138-
run_parser.add_argument("--args-replace", action='store_false', dest='keep_args',
139-
help="Replace current program arguments, instead of append")
140-
run_parser.add_argument("args", nargs="*", action="extend", help="Arguments to pass to the executable")
206+
run_parser.add_argument(
207+
"--args-replace",
208+
action="store_false",
209+
dest="keep_args",
210+
help="Replace current program arguments, instead of append",
211+
)
212+
run_parser.add_argument(
213+
"args",
214+
nargs="*",
215+
action="extend",
216+
help="Arguments to pass to the executable",
217+
)
141218

142-
standalone_parser = subparsers.add_parser("standalone", help="Generate a standalone script to launch commands "
143-
"without passing trough Bottles")
144-
standalone_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
219+
standalone_parser = subparsers.add_parser(
220+
"standalone",
221+
help="Generate a standalone script to launch commands "
222+
"without passing trough Bottles",
223+
)
224+
standalone_parser.add_argument(
225+
"-b", "--bottle", help="Bottle name", required=True
226+
)
145227

146-
shell_parser = subparsers.add_parser("shell", help="Launch commands in a Wine shell")
228+
shell_parser = subparsers.add_parser(
229+
"shell", help="Launch commands in a Wine shell"
230+
)
147231
shell_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
148-
shell_parser.add_argument("-i", "--input", help="Command to execute", required=True)
232+
shell_parser.add_argument(
233+
"-i", "--input", help="Command to execute", required=True
234+
)
149235

150236
self.__process_args()
151237

@@ -233,7 +319,11 @@ def list_bottles(self, c_filter=None):
233319

234320
if c_filter and c_filter.startswith("environment:"):
235321
environment = c_filter.split(":")[1].lower()
236-
bottles = [name for name, bottle in bottles.items() if bottle.Environment.lower() == environment]
322+
bottles = [
323+
name
324+
for name, bottle in bottles.items()
325+
if bottle.Environment.lower() == environment
326+
]
237327

238328
if self.args.json:
239329
sys.stdout.write(json.dumps(bottles))
@@ -257,7 +347,7 @@ def list_components(self, c_filter=None):
257347
"dxvk": mng.dxvk_available,
258348
"vkd3d": mng.vkd3d_available,
259349
"nvapi": mng.nvapi_available,
260-
"latencyflex": mng.latencyflex_available
350+
"latencyflex": mng.latencyflex_available,
261351
}
262352

263353
if c_filter and c_filter.startswith("category:"):
@@ -376,7 +466,9 @@ def add_program(self):
376466
"path": _path,
377467
"dxvk": not _no_dxvk if _no_dxvk else bottle.Parameters.dxvk,
378468
"vkd3d": not _no_vkd3d if _no_vkd3d else bottle.Parameters.vkd3d,
379-
"dxvk_nvapi": not _no_dxvk_nvapi if _no_dxvk_nvapi else bottle.Parameters.dxvk_nvapi,
469+
"dxvk_nvapi": (
470+
not _no_dxvk_nvapi if _no_dxvk_nvapi else bottle.Parameters.dxvk_nvapi
471+
),
380472
}
381473
mng.update_config(bottle, _uuid, _program, scope="External_Programs")
382474
sys.stdout.write(f"'{_name}' added to '{bottle.Name}'!")
@@ -529,7 +621,7 @@ def new_bottle(self):
529621
nvapi=_nvapi,
530622
latencyflex=_latencyflex,
531623
arch=_arch,
532-
custom_environment=_custom_environment
624+
custom_environment=_custom_environment,
533625
)
534626

535627
# endregion
@@ -552,7 +644,15 @@ def run_program(self):
552644
mng = Manager(g_settings=self.settings, is_cli=True)
553645
mng.checks()
554646

555-
if _bottle not in mng.local_bottles:
647+
if _bottle.startswith('"') and _bottle.endswith('"'):
648+
_bottle = _bottle[1:-1]
649+
elif _bottle.startswith("'") and _bottle.endswith("'"):
650+
_bottle = _bottle[1:-1]
651+
652+
for b in mng.local_bottles.keys():
653+
if b == _bottle:
654+
break
655+
else:
556656
sys.stderr.write(f"Bottle {_bottle} not found\n")
557657
exit(1)
558658

@@ -582,6 +682,12 @@ def run_program(self):
582682
_program_fsr = program.get("fsr")
583683
_program_virt_desktop = program.get("virtual_desktop")
584684

685+
_executable = _executable.replace("file://", "")
686+
if _executable.startswith('"') and _executable.endswith('"'):
687+
_executable = _executable[1:-1]
688+
elif _executable.startswith("'") and _executable.endswith("'"):
689+
_executable = _executable[1:-1]
690+
585691
WineExecutor(
586692
bottle,
587693
exec_path=_executable,
@@ -592,7 +698,7 @@ def run_program(self):
592698
program_vkd3d=_program_vkd3d,
593699
program_nvapi=_program_dxvk_nvapi,
594700
program_fsr=_program_fsr,
595-
program_virt_desktop=_program_virt_desktop
701+
program_virt_desktop=_program_virt_desktop,
596702
).run_cli()
597703

598704
# endregion
@@ -633,7 +739,9 @@ def generate_standalone(self):
633739
winecommand = WineCommand(config=bottle, command='"$@"')
634740
env = winecommand.get_env(return_clean_env=True)
635741
cmd = winecommand.get_cmd('"$@"', return_clean_cmd=True)
636-
winecommand.command.replace("/usr/lib/extensions/vulkan/MangoHud/bin/mangohud", "")
742+
winecommand.command.replace(
743+
"/usr/lib/extensions/vulkan/MangoHud/bin/mangohud", ""
744+
)
637745

638746
if os.path.isfile(standalone_path):
639747
os.remove(standalone_path)
@@ -649,5 +757,5 @@ def generate_standalone(self):
649757
sys.stdout.write("Re-generate after every bottle change.\n")
650758

651759

652-
if __name__ == '__main__':
760+
if __name__ == "__main__":
653761
cli = CLI()

bottles/frontend/main.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# gi.require_version("Xdp", "1.0")
3030
# gi.require_version("XdpGtk4", "1.0")
3131

32-
from gi.repository import Gtk, Gio, GLib, GObject, Adw
32+
from gi.repository import Gtk, Gio, GLib, GObject, Adw # type: ignore
3333

3434
from bottles.frontend.params import *
3535
from bottles.backend.logger import Logger
@@ -179,8 +179,14 @@ def do_command_line(self, command):
179179
for a in sys.argv:
180180
if a.endswith((".exe", ".msi", ".bat", ".lnk")):
181181
self.arg_exe = a
182+
logging.info(
183+
_("Launching with executable: {0}").format(a),
184+
)
182185

183186
uri = commands.lookup_value(GLib.OPTION_REMAINING)
187+
logging.info(
188+
_("Launching with URI: {0}").format(uri),
189+
)
184190
if uri:
185191
return self.__process_uri(uri)
186192

@@ -193,12 +199,18 @@ def __process_uri(self, uri):
193199
e.g. xdg-open bottles:run/<bottle>/<program>
194200
"""
195201
uri = uri[0]
196-
if os.path.exists(uri):
202+
203+
try:
197204
from bottles.frontend.windows.bottlepicker import BottlePickerDialog
198205

199206
dialog = BottlePickerDialog(application=self, arg_exe=uri)
200207
dialog.present()
201208
return 0
209+
except Exception as e:
210+
logging.error(
211+
_("Error while processing URI: {0}").format(e),
212+
)
213+
pass
202214

203215
_wrong_uri_error = _("Invalid URI (syntax: bottles:run/<bottle>/<program>)")
204216
if (
@@ -229,6 +241,10 @@ def do_activate(self):
229241
This function is called when the application is activated.
230242
"""
231243

244+
logging.info(
245+
_("[Activate] request received."),
246+
)
247+
232248
# create the main window
233249
Adw.Application.do_activate(self)
234250
win = self.props.active_window

0 commit comments

Comments
 (0)