mirror of
https://github.com/bottlesdevs/Bottles.git
synced 2026-10-18 23:20:48 +08:00
aacc50e673
Co-authored-by: Hari Rana <theevilskeleton@riseup.net>
760 lines
25 KiB
Python
760 lines
25 KiB
Python
#!@PYTHON@
|
|
|
|
# cli.in
|
|
#
|
|
# Copyright 2020 brombinmirko <send@mirko.pm>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
import argparse
|
|
import os
|
|
import signal
|
|
import sys
|
|
import uuid
|
|
import warnings
|
|
|
|
import gi
|
|
|
|
warnings.filterwarnings("ignore") # suppress GTK warnings
|
|
gi.require_version("Gtk", "4.0")
|
|
|
|
APP_VERSION = "@APP_VERSION@"
|
|
pkgdatadir = "@pkgdatadir@"
|
|
# noinspection DuplicatedCode
|
|
gresource_path = f"{pkgdatadir}/bottles.gresource"
|
|
sys.path.insert(1, pkgdatadir)
|
|
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
|
|
# ruff: noqa: E402
|
|
from gi.repository import Gio
|
|
|
|
from bottles.frontend.params import APP_ID
|
|
from bottles.backend.globals import Paths
|
|
from bottles.backend.health import HealthChecker
|
|
from bottles.backend.managers.manager import Manager
|
|
from bottles.backend.models.config import BottleConfig
|
|
from bottles.backend.wine.cmd import CMD
|
|
from bottles.backend.wine.control import Control
|
|
from bottles.backend.wine.executor import WineExecutor
|
|
from bottles.backend.wine.winecommand import WineCommand
|
|
from bottles.backend.wine.reg import Reg
|
|
from bottles.backend.wine.winepath import WinePath
|
|
from bottles.backend.wine.regedit import Regedit
|
|
from bottles.backend.wine.taskmgr import Taskmgr
|
|
from bottles.backend.wine.uninstaller import Uninstaller
|
|
from bottles.backend.wine.winecfg import WineCfg
|
|
from bottles.backend.wine.explorer import Explorer
|
|
from bottles.backend.wine.regkeys import RegKeys
|
|
from bottles.backend.runner import Runner
|
|
from bottles.backend.utils import json
|
|
from bottles.backend.utils.manager import ManagerUtils
|
|
|
|
|
|
# noinspection DuplicatedCode
|
|
class CLI:
|
|
settings = Gio.Settings.new(APP_ID)
|
|
|
|
def __init__(self):
|
|
# self.__clear()
|
|
|
|
self.parser = argparse.ArgumentParser(
|
|
description="Bottles is a tool to manage your bottles"
|
|
)
|
|
self.parser.add_argument(
|
|
"-v", "--version", action="version", version=f"Bottles {APP_VERSION}"
|
|
)
|
|
self.parser.add_argument(
|
|
"-j", "--json", action="store_true", help="Outputs in JSON format"
|
|
)
|
|
|
|
subparsers = self.parser.add_subparsers(dest="command", help="sub-command help")
|
|
|
|
info_parser = subparsers.add_parser(
|
|
"info", help="Show information about Bottles"
|
|
)
|
|
info_parser.add_argument(
|
|
"type", choices=["bottles-path", "health-check"], help="Type of information"
|
|
)
|
|
|
|
list_parser = subparsers.add_parser("list", help="List entities")
|
|
list_parser.add_argument(
|
|
"type", choices=["bottles", "components"], help="Type of entity"
|
|
)
|
|
list_parser.add_argument(
|
|
"-f",
|
|
"--filter",
|
|
help="Filter bottles and components (e.g. '-f 'environment:gaming')",
|
|
)
|
|
|
|
programs_parser = subparsers.add_parser("programs", help="List programs")
|
|
programs_parser.add_argument(
|
|
"-b", "--bottle", help="Bottle name", required=True
|
|
)
|
|
|
|
add_parser = subparsers.add_parser("add", help="Add program")
|
|
add_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
add_parser.add_argument("-n", "--name", help="Program name", required=True)
|
|
add_parser.add_argument("-p", "--path", help="Program path", required=True)
|
|
add_parser.add_argument("-l", "--launch-options", help="Program launch options")
|
|
add_parser.add_argument(
|
|
"--no-dxvk", action="store_true", help="Disable DXVK for the program"
|
|
)
|
|
add_parser.add_argument(
|
|
"--no-vkd3d", action="store_true", help="Disable VKD3D for the program"
|
|
)
|
|
add_parser.add_argument(
|
|
"--no-dxvk-nvapi",
|
|
action="store_true",
|
|
help="Disable DXVK Nvapi for the program",
|
|
)
|
|
|
|
tools_parser = subparsers.add_parser("tools", help="Launch Wine tools")
|
|
tools_parser.add_argument(
|
|
"tool",
|
|
choices=[
|
|
"cmd",
|
|
"winecfg",
|
|
"uninstaller",
|
|
"regedit",
|
|
"taskmgr",
|
|
"control",
|
|
"explorer",
|
|
],
|
|
help="Tool to launch",
|
|
)
|
|
tools_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
|
|
reg_parser = subparsers.add_parser("reg", help="Manage registry")
|
|
reg_parser.add_argument(
|
|
"action", choices=["add", "edit", "del"], help="Action to perform"
|
|
)
|
|
reg_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
reg_parser.add_argument("-k", "--key", help="Registry key", required=True)
|
|
reg_parser.add_argument("-v", "--value", help="Registry value", required=True)
|
|
reg_parser.add_argument("-d", "--data", help="Data to be set")
|
|
reg_parser.add_argument(
|
|
"-t",
|
|
"--key-type",
|
|
help="Data type",
|
|
choices=["REG_DWORD", "REG_SZ", "REG_BINARY", "REG_MULTI_SZ"],
|
|
)
|
|
|
|
edit_parser = subparsers.add_parser("edit", help="Edit a bottle configuration")
|
|
edit_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
edit_parser.add_argument(
|
|
"--params", help="Set parameters (e.g. '-p dxvk:true')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--env-var",
|
|
help="Add new environment variable (e.g. '-env-var WINEDEBUG=-all')",
|
|
)
|
|
edit_parser.add_argument(
|
|
"--win", help="Change Windows version (e.g. '--win win7')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--runner", help="Change Runner (e.g. '--runner caffe-7.2')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--dxvk", help="Change DXVK (e.g. '--dxvk dxvk-1.9.0')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--vkd3d", help="Change VKD3D (e.g. '--vkd3d vkd3d-proton-2.6')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--nvapi", help="Change DXVK-Nvapi (e.g. '--nvapi dxvk-nvapi-1.9.0')"
|
|
)
|
|
edit_parser.add_argument(
|
|
"--latencyflex",
|
|
help="Change LatencyFleX (e.g. '--latencyflex latencyflex-v0.1.0')",
|
|
)
|
|
|
|
new_parser = subparsers.add_parser("new", help="Create a new bottle")
|
|
new_parser.add_argument("--bottle-name", help="Bottle name", required=True)
|
|
new_parser.add_argument(
|
|
"--environment",
|
|
help="Environment to apply (gaming|application|custom)",
|
|
required=True,
|
|
)
|
|
new_parser.add_argument(
|
|
"--custom-environment", help="Path to a custom environment.yml file"
|
|
)
|
|
new_parser.add_argument("--arch", help="Architecture (win32|win64)")
|
|
new_parser.add_argument("--runner", help="Name of the runner to be used")
|
|
new_parser.add_argument("--dxvk", help="Name of the dxvk to be used")
|
|
new_parser.add_argument("--vkd3d", help="Name of the vkd3d to be used")
|
|
new_parser.add_argument("--nvapi", help="Name of the dxvk-nvapi to be used")
|
|
new_parser.add_argument(
|
|
"--latencyflex", help="Name of the latencyflex to be used"
|
|
)
|
|
|
|
run_parser = subparsers.add_parser("run", help="Run a program")
|
|
run_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
run_parser.add_argument("-e", "--executable", help="Path to the executable")
|
|
run_parser.add_argument("-p", "--program", help="Program to run")
|
|
run_parser.add_argument(
|
|
"--args-replace",
|
|
action="store_false",
|
|
dest="keep_args",
|
|
help="Replace current program arguments, instead of append",
|
|
)
|
|
run_parser.add_argument(
|
|
"args",
|
|
nargs="*",
|
|
action="extend",
|
|
help="Arguments to pass to the executable",
|
|
)
|
|
|
|
standalone_parser = subparsers.add_parser(
|
|
"standalone",
|
|
help="Generate a standalone script to launch commands "
|
|
"without passing trough Bottles",
|
|
)
|
|
standalone_parser.add_argument(
|
|
"-b", "--bottle", help="Bottle name", required=True
|
|
)
|
|
|
|
shell_parser = subparsers.add_parser(
|
|
"shell", help="Launch commands in a Wine shell"
|
|
)
|
|
shell_parser.add_argument("-b", "--bottle", help="Bottle name", required=True)
|
|
shell_parser.add_argument(
|
|
"-i", "--input", help="Command to execute", required=True
|
|
)
|
|
|
|
self.__process_args()
|
|
|
|
@staticmethod
|
|
def __clear():
|
|
os.system("clear")
|
|
|
|
def __process_args(self):
|
|
self.args = self.parser.parse_args()
|
|
|
|
# INFO parser
|
|
if self.args.command == "info":
|
|
self.show_info()
|
|
|
|
# LIST parser
|
|
elif self.args.command == "list":
|
|
_filter = None if self.args.filter is None else self.args.filter
|
|
_type = self.args.type
|
|
|
|
if _type == "bottles":
|
|
self.list_bottles(c_filter=_filter)
|
|
elif _type == "components":
|
|
self.list_components(c_filter=_filter)
|
|
|
|
# PROGRAMS parser
|
|
elif self.args.command == "programs":
|
|
self.list_programs()
|
|
|
|
# TOOLS parser
|
|
elif self.args.command == "tools":
|
|
self.launch_tool()
|
|
|
|
# ADD parser
|
|
elif self.args.command == "add":
|
|
self.add_program()
|
|
|
|
# REG parser
|
|
elif self.args.command == "reg":
|
|
self.manage_reg()
|
|
|
|
# EDIT parser
|
|
elif self.args.command == "edit":
|
|
self.edit_bottle()
|
|
|
|
# NEW parser
|
|
elif self.args.command == "new":
|
|
self.new_bottle()
|
|
|
|
# RUN parser
|
|
elif self.args.command == "run":
|
|
self.run_program()
|
|
|
|
# SHELL parser
|
|
elif self.args.command == "shell":
|
|
self.run_shell()
|
|
|
|
# STANDALONE parser
|
|
elif self.args.command == "standalone":
|
|
self.generate_standalone()
|
|
|
|
else:
|
|
self.parser.print_help()
|
|
|
|
# region INFO
|
|
def show_info(self):
|
|
_type = self.args.type
|
|
if _type == "bottles-path":
|
|
res = Paths.bottles
|
|
sys.stdout.write(res)
|
|
exit(0)
|
|
elif _type == "health-check":
|
|
hc = HealthChecker()
|
|
if self.args.json:
|
|
sys.stdout.write(json.dumps(hc.get_results()) + "\n")
|
|
exit(0)
|
|
sys.stdout.write(hc.get_results(plain=True))
|
|
|
|
# endregion
|
|
|
|
# region LIST
|
|
def list_bottles(self, c_filter=None):
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
bottles = mng.local_bottles
|
|
|
|
if c_filter and c_filter.startswith("environment:"):
|
|
environment = c_filter.split(":")[1].lower()
|
|
bottles = [
|
|
name
|
|
for name, bottle in bottles.items()
|
|
if bottle.Environment.lower() == environment
|
|
]
|
|
|
|
if self.args.json:
|
|
sys.stdout.write(json.dumps(bottles))
|
|
exit(0)
|
|
|
|
if len(bottles) > 0:
|
|
sys.stdout.write(f"Found {len(bottles)} bottles:\n")
|
|
for b in bottles:
|
|
sys.stdout.write(f"- {b}\n")
|
|
|
|
def list_components(self, c_filter=None):
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_runners(False)
|
|
mng.check_dxvk(False)
|
|
mng.check_vkd3d(False)
|
|
mng.check_nvapi(False)
|
|
mng.check_latencyflex(False)
|
|
|
|
components = {
|
|
"runners": mng.runners_available,
|
|
"dxvk": mng.dxvk_available,
|
|
"vkd3d": mng.vkd3d_available,
|
|
"nvapi": mng.nvapi_available,
|
|
"latencyflex": mng.latencyflex_available,
|
|
}
|
|
|
|
if c_filter and c_filter.startswith("category:"):
|
|
category = c_filter.split(":")[1].lower()
|
|
if category in components:
|
|
components = {category: components[category]}
|
|
|
|
if self.args.json:
|
|
sys.stdout.write(json.dumps(components))
|
|
exit(0)
|
|
|
|
for c in components:
|
|
sys.stdout.write(f"Found {len(components[c])} {c}\n")
|
|
for i in components[c]:
|
|
sys.stdout.write(f"- {i}\n")
|
|
|
|
# endregion
|
|
|
|
# region PROGRAMS
|
|
def list_programs(self):
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
_bottle = self.args.bottle
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
programs = mng.get_programs(bottle)
|
|
programs = [p for p in programs if not p.get("removed", False)]
|
|
|
|
if self.args.json:
|
|
sys.stdout.write(json.dumps(programs))
|
|
exit(0)
|
|
|
|
if len(programs) > 0:
|
|
sys.stdout.write(f"Found {len(programs)} programs:\n")
|
|
for p in programs:
|
|
sys.stdout.write(f"- {p['name']}\n")
|
|
|
|
# endregion
|
|
|
|
# region TOOLS
|
|
def launch_tool(self):
|
|
_bottle = self.args.bottle
|
|
_tool = self.args.tool
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
|
|
if _tool == "cmd":
|
|
CMD(bottle).launch()
|
|
elif _tool == "winecfg":
|
|
WineCfg(bottle).launch()
|
|
elif _tool == "uninstaller":
|
|
Uninstaller(bottle).launch()
|
|
elif _tool == "regedit":
|
|
Regedit(bottle).launch()
|
|
elif _tool == "taskmgr":
|
|
Taskmgr(bottle).launch()
|
|
elif _tool == "control":
|
|
Control(bottle).launch()
|
|
elif _tool == "explorer":
|
|
Explorer(bottle).launch()
|
|
|
|
# endregion
|
|
|
|
# region ADD
|
|
def add_program(self):
|
|
_bottle = self.args.bottle
|
|
_name = self.args.name
|
|
_path = self.args.path
|
|
_launch_options = self.args.launch_options
|
|
_no_dxvk = self.args.no_dxvk
|
|
_no_vkd3d = self.args.no_vkd3d
|
|
_no_dxvk_nvapi = self.args.no_dxvk_nvapi
|
|
_executable = ""
|
|
_folder = ""
|
|
_uuid = str(uuid.uuid4())
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
winepath = WinePath(bottle)
|
|
|
|
if winepath.is_unix(_path):
|
|
if not os.path.exists(_path):
|
|
sys.stderr.write(f"Path doesn't exists or is unreachable: {_path}")
|
|
exit(1)
|
|
_executable = os.path.basename(_path)
|
|
_folder = os.path.dirname(_path)
|
|
elif winepath.is_windows(_path):
|
|
_executable = _path.split("\\")[-1]
|
|
_folder = ManagerUtils.get_exe_parent_dir(bottle, _path)
|
|
else:
|
|
sys.stderr.write(f"Unsupported path type: {_path}")
|
|
exit(1)
|
|
|
|
_program = {
|
|
"arguments": _launch_options if _launch_options else "",
|
|
"executable": _executable,
|
|
"name": _name,
|
|
"folder": _folder,
|
|
"icon": "",
|
|
"id": _uuid,
|
|
"path": _path,
|
|
"dxvk": not _no_dxvk if _no_dxvk else bottle.Parameters.dxvk,
|
|
"vkd3d": not _no_vkd3d if _no_vkd3d else bottle.Parameters.vkd3d,
|
|
"dxvk_nvapi": (
|
|
not _no_dxvk_nvapi if _no_dxvk_nvapi else bottle.Parameters.dxvk_nvapi
|
|
),
|
|
}
|
|
mng.update_config(bottle, _uuid, _program, scope="External_Programs")
|
|
sys.stdout.write(f"'{_name}' added to '{bottle.Name}'!")
|
|
|
|
# endregion
|
|
|
|
# region REG
|
|
def manage_reg(self):
|
|
_bottle = self.args.bottle
|
|
_action = self.args.action
|
|
_key = self.args.key
|
|
_value = self.args.value
|
|
_data = self.args.data
|
|
_key_type = self.args.key_type
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
allowed_types = ["REG_SZ", "REG_DWORD", "REG_BINARY", "REG_MULTI_SZ"]
|
|
_key_type = "REG_SZ" if _key_type is None else _key_type.upper()
|
|
|
|
if _action in ["add", "edit"]:
|
|
if _data is None or _key_type not in allowed_types:
|
|
sys.stderr.write("Missing or invalid data or key type\n")
|
|
exit(1)
|
|
Reg(bottle).add(_key, _value, _data, _key_type)
|
|
elif _action == "del":
|
|
Reg(bottle).remove(_key, _value)
|
|
|
|
# endregion
|
|
|
|
# region EDIT
|
|
def edit_bottle(self):
|
|
_bottle = self.args.bottle
|
|
_params = self.args.params
|
|
_env_var = self.args.env_var
|
|
_win = self.args.win
|
|
_runner = self.args.runner
|
|
_dxvk = self.args.dxvk
|
|
_vkd3d = self.args.vkd3d
|
|
_nvapi = self.args.nvapi
|
|
_latencyflex = self.args.latencyflex
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.check_bottles()
|
|
|
|
valid_parameters = BottleConfig().Parameters.keys()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
|
|
if _params is not None:
|
|
_params = _params.split(",")
|
|
_params = [p.split(":") for p in _params]
|
|
for k, v in _params:
|
|
if k not in valid_parameters:
|
|
sys.stderr.write(f"Invalid parameter {k}\n")
|
|
exit(1)
|
|
|
|
if v.lower() == "true":
|
|
v = True
|
|
elif v.lower() == "false":
|
|
v = False
|
|
else:
|
|
try:
|
|
v = int(v)
|
|
except ValueError:
|
|
pass
|
|
|
|
mng.update_config(bottle, k, v, scope="Parameters")
|
|
|
|
if _env_var is not None and "=" in _env_var:
|
|
k, v = _env_var.split("=", 1)
|
|
mng.update_config(bottle, k, v, scope="Environment_Variables")
|
|
|
|
if _win is not None:
|
|
RegKeys(bottle).lg_set_windows(_win)
|
|
|
|
if _runner is not None:
|
|
Runner.runner_update(bottle, mng, _runner)
|
|
|
|
if _dxvk is not None:
|
|
mng.check_dxvk(False)
|
|
|
|
if _dxvk not in mng.dxvk_available:
|
|
sys.stderr.write(f"DXVK version {_dxvk} not available\n")
|
|
exit(1)
|
|
|
|
if mng.install_dll_component(bottle, "dxvk", version=_dxvk):
|
|
mng.update_config(bottle, "DXVK", _dxvk)
|
|
|
|
if _vkd3d is not None:
|
|
mng.check_vkd3d(False)
|
|
|
|
if _vkd3d not in mng.vkd3d_available:
|
|
sys.stderr.write(f"VKD3D version {_vkd3d} not available\n")
|
|
exit(1)
|
|
|
|
if mng.install_dll_component(bottle, "vkd3d", version=_vkd3d):
|
|
mng.update_config(bottle, "VKD3D", _vkd3d)
|
|
|
|
if _nvapi is not None:
|
|
mng.check_nvapi(False)
|
|
|
|
if _nvapi not in mng.nvapi_available:
|
|
sys.stderr.write(f"NVAPI version {_nvapi} not available\n")
|
|
exit(1)
|
|
|
|
if mng.install_dll_component(bottle, "nvapi", version=_nvapi):
|
|
mng.update_config(bottle, "NVAPI", _nvapi)
|
|
|
|
if _latencyflex is not None:
|
|
mng.check_latencyflex(False)
|
|
|
|
if _latencyflex not in mng.latencyflex_available:
|
|
sys.stderr.write(f"LatencyFlex version {_latencyflex} not available\n")
|
|
exit(1)
|
|
|
|
if mng.install_dll_component(bottle, "latencyflex", version=_latencyflex):
|
|
mng.update_config(bottle, "LatencyFlex", _latencyflex)
|
|
|
|
# endregion
|
|
|
|
# region NEW
|
|
def new_bottle(self):
|
|
_name = self.args.bottle_name
|
|
_environment = self.args.environment
|
|
_custom_environment = self.args.custom_environment
|
|
_arch = "win64" if self.args.arch is None else self.args.arch
|
|
_runner = self.args.runner
|
|
_dxvk = self.args.dxvk
|
|
_vkd3d = self.args.vkd3d
|
|
_nvapi = self.args.nvapi
|
|
_latencyflex = self.args.latencyflex
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.checks()
|
|
|
|
mng.create_bottle(
|
|
name=_name,
|
|
environment=_environment,
|
|
runner=_runner,
|
|
dxvk=_dxvk,
|
|
vkd3d=_vkd3d,
|
|
nvapi=_nvapi,
|
|
latencyflex=_latencyflex,
|
|
arch=_arch,
|
|
custom_environment=_custom_environment,
|
|
)
|
|
|
|
# endregion
|
|
|
|
# region RUN
|
|
def run_program(self):
|
|
_bottle = self.args.bottle
|
|
_program = self.args.program
|
|
_keep = self.args.keep_args
|
|
_args = " ".join(self.args.args)
|
|
_executable = self.args.executable
|
|
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.checks()
|
|
|
|
if _bottle.startswith('"') and _bottle.endswith('"'):
|
|
_bottle = _bottle[1:-1]
|
|
elif _bottle.startswith("'") and _bottle.endswith("'"):
|
|
_bottle = _bottle[1:-1]
|
|
|
|
for b in mng.local_bottles.keys():
|
|
if b == _bottle:
|
|
break
|
|
else:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
programs = mng.get_programs(bottle)
|
|
|
|
if _program is not None:
|
|
if _executable is not None:
|
|
sys.stderr.write("Cannot specify both --program and --executable\n")
|
|
exit(1)
|
|
|
|
if _program not in [p["name"] for p in programs]:
|
|
sys.stderr.write(f"Program {_program} not found\n")
|
|
exit(1)
|
|
|
|
program = [p for p in programs if p["name"] == _program][0]
|
|
_executable = program.get("path", "")
|
|
_program_args = program.get("arguments")
|
|
if _keep and _program_args:
|
|
_args = _program_args + " " + _args
|
|
program.get("pre_script", None)
|
|
program.get("post_script", None)
|
|
program.get("folder", None)
|
|
program.get("midi_soundfont", None)
|
|
|
|
program.get("dxvk")
|
|
program.get("vkd3d")
|
|
program.get("dxvk_nvapi")
|
|
program.get("fsr")
|
|
program.get("gamescope")
|
|
program.get("virtual_desktop")
|
|
|
|
WineExecutor.run_program(bottle, program | {"arguments": _args})
|
|
|
|
elif _executable:
|
|
_executable = _executable.replace("file://", "")
|
|
if _executable.startswith('"') and _executable.endswith('"'):
|
|
_executable = _executable[1:-1]
|
|
elif _executable.startswith("'") and _executable.endswith("'"):
|
|
_executable = _executable[1:-1]
|
|
|
|
WineExecutor(
|
|
bottle,
|
|
exec_path=_executable,
|
|
args=_args,
|
|
).run_cli()
|
|
else:
|
|
sys.stderr.write(
|
|
"No program or executable specified, you must use either --program or --executable\n"
|
|
)
|
|
exit(1)
|
|
|
|
# endregion
|
|
|
|
# region SHELL
|
|
def run_shell(self):
|
|
_bottle = self.args.bottle
|
|
_input = self.args.input
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.checks()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
winecommand = WineCommand(config=bottle, command=_input, communicate=True)
|
|
res = winecommand.run()
|
|
if not res.ok:
|
|
sys.stdout.write(res.message)
|
|
sys.stdout.write(res.data)
|
|
|
|
# endregion
|
|
|
|
# region STANDALONE
|
|
def generate_standalone(self):
|
|
_bottle = self.args.bottle
|
|
mng = Manager(g_settings=self.settings, is_cli=True)
|
|
mng.checks()
|
|
|
|
if _bottle not in mng.local_bottles:
|
|
sys.stderr.write(f"Bottle {_bottle} not found\n")
|
|
exit(1)
|
|
|
|
bottle = mng.local_bottles[_bottle]
|
|
path = ManagerUtils.get_bottle_path(bottle)
|
|
standalone_path = os.path.join(path, "standalone")
|
|
winecommand = WineCommand(config=bottle, command='"$@"')
|
|
env = winecommand.get_env(return_clean_env=True)
|
|
cmd = winecommand.get_cmd('"$@"', return_clean_cmd=True)
|
|
winecommand.command.replace(
|
|
"/usr/lib/extensions/vulkan/MangoHud/bin/mangohud", ""
|
|
)
|
|
|
|
if os.path.isfile(standalone_path):
|
|
os.remove(standalone_path)
|
|
|
|
with open(standalone_path, "w") as f:
|
|
f.write("#!/bin/bash\n")
|
|
for k, v in env.items():
|
|
f.write(f"export {k}='{v}'\n")
|
|
f.write(f"{cmd}\n")
|
|
|
|
os.chmod(os.path.join(path, "standalone"), 0o755)
|
|
sys.stdout.write(f"Standalone generated in {path}\n")
|
|
sys.stdout.write("Re-generate after every bottle change.\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cli = CLI()
|