Skip to content

Commit

Permalink
Display error dialog when launched from desktop
Browse files Browse the repository at this point in the history
Display pre-determined error or uncaught exception using an error dialog
when launched from desktop. Prior to this, Protontricks would fail to
show any kind of feedback that an error had occurred if Protontricks had
been launched from desktop.

All log messages generated by Protontricks are also included in the
error dialog. This is achieved by registering an additional log handler
that logs all messages to a temporary file. This file is then used when
an error occurs and the error dialog is displayed.
  • Loading branch information
Matoking committed Aug 3, 2021
1 parent 8e8fa1b commit cd2944a
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 71 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add `protontricks-launch` script to launch Windows executables using Proton app specific Wine prefixes
- Add desktop integration for Windows executables, which can now be launched using Protontricks
- Add `protontricks-desktop-install` to install desktop integration for the local user. This is only necessary if the installation method doesn't do this automatically.
- Add error dialog for displaying error information when Protontricks has been launched from desktop and no user-visible terminal is available.

### Changed
- Improve GUI dialog. The prompt to select the Steam app now uses zenity's "list" dialog, which supports scrolling and search.
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@
install_requires=["vdf>=3.2"],
entry_points={
"console_scripts": [
"protontricks = protontricks.cli.main:main",
"protontricks-launch = protontricks.cli.launch:main",
"protontricks = protontricks.cli.main:cli",
"protontricks-launch = protontricks.cli.launch:cli",
# `protontricks-desktop-install` is only responsible for installing
# .desktop files and should be omitted if the distro package
# already ships .desktop files properly
("protontricks-desktop-install "
"= protontricks.cli.desktop_install:main")
"= protontricks.cli.desktop_install:cli")
]
},
include_package_data=True,
Expand Down
4 changes: 4 additions & 0 deletions src/protontricks/cli/desktop_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def install_desktop_entries():
return applications_dir


def cli(args=None):
main(args)


def main(args=None):
"""
'protontricks-desktop-install' script entrypoint
Expand Down
45 changes: 21 additions & 24 deletions src/protontricks/cli/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,17 @@
from ..gui import select_steam_app_with_gui
from ..steam import find_steam_path, get_steam_apps, get_steam_lib_paths
from .main import main as cli_main
from .util import CustomArgumentParser, enable_logging
from .util import (CustomArgumentParser, cli_error_handler, enable_logging,
exit_with_error)

logger = logging.getLogger("protontricks")


def exit_with_error(error, display_dialog=False):
"""
Print or display an error, and then exit.
"""
if display_dialog:
run([
"zenity", "--error", "--width", "400", "--text", error
], check=True)
else:
print(error)

sys.exit(-1)
def cli(args=None):
main(args)


@cli_error_handler
def main(args=None):
"""
'protontricks-launch' script entrypoint
Expand Down Expand Up @@ -57,10 +49,11 @@ def main(args=None):
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"--from-desktop", action="store_true",
"--no-term", action="store_true",
help=(
"Display error messages using error dialogs instead of printing "
"to stderr."
"Program was launched from desktop and no user-visible "
"terminal is available. Error will be shown in a dialog instead "
"of being printed."
)
)
parser.add_argument(
Expand All @@ -81,7 +74,15 @@ def main(args=None):

args = parser.parse_args(args)

enable_logging(args.verbose)
# 'cli_error_handler' relies on this to know whether to use error dialog or
# not
main.no_term = args.no_term

# Shorthand function for aborting with error message
def exit_(error):
exit_with_error(error, args.no_term)

enable_logging(args.verbose, record_to_file=args.no_term)

try:
executable_path = Path(args.executable).resolve(strict=True)
Expand All @@ -91,10 +92,7 @@ def main(args=None):
# 1. Find Steam path
steam_path, steam_root = find_steam_path()
if not steam_path:
exit_with_error(
"Steam installation directory could not be found.",
args.from_desktop
)
exit_("Steam installation directory could not be found.")

# 2. Find any Steam library folders
steam_lib_paths = get_steam_lib_paths(steam_path)
Expand All @@ -109,10 +107,9 @@ def main(args=None):
]

if not steam_apps:
exit_with_error(
exit_(
"No Proton enabled Steam apps were found. Have you launched one "
"of the apps at least once?",
args.from_desktop
"of the apps at least once?"
)

if not args.appid:
Expand Down
56 changes: 34 additions & 22 deletions src/protontricks/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
find_steam_path, get_steam_apps, get_steam_lib_paths)
from ..util import is_flatpak_sandbox, run_command
from ..winetricks import get_winetricks_path

from .util import CustomArgumentParser, enable_logging
from .util import (CustomArgumentParser, cli_error_handler, enable_logging,
exit_with_error)

logger = logging.getLogger("protontricks")


def cli(args=None):
main(args)


@cli_error_handler
def main(args=None):
"""
'protontricks' script entrypoint
Expand Down Expand Up @@ -63,7 +68,16 @@ def main(args=None):
)
parser.add_argument(
"--verbose", "-v", action="store_true",
help="Print debug information")
help="Print debug information"
)
parser.add_argument(
"--no-term", action="store_true",
help=(
"Program was launched from desktop. This is used automatically "
"when lauching Protontricks from desktop and no user-visible "
"terminal is available."
)
)
parser.add_argument(
"-s", "--search", type=str, dest="search", nargs="+",
required=False, help="Search for game(s) with the given name")
Expand Down Expand Up @@ -92,6 +106,14 @@ def main(args=None):

args = parser.parse_args(args)

# 'cli_error_handler' relies on this to know whether to use error dialog or
# not
main.no_term = args.no_term

# Shorthand function for aborting with error message
def exit_(error):
exit_with_error(error, args.no_term)

do_command = bool(args.command)
do_search = bool(args.search)
do_gui = bool(args.gui)
Expand All @@ -108,7 +130,7 @@ def main(args=None):
parser.print_help()
return

enable_logging(args.verbose)
enable_logging(args.verbose, record_to_file=args.no_term)

if is_flatpak_sandbox():
use_bwrap = False
Expand All @@ -122,10 +144,7 @@ def main(args=None):
# 1. Find Steam path
steam_path, steam_root = find_steam_path()
if not steam_path:
print(
"Steam installation directory could not be found."
)
sys.exit(-1)
exit_("Steam installation directory could not be found.")

# 2. Find the pre-installed legacy Steam Runtime if enabled
legacy_steam_runtime_path = None
Expand All @@ -137,20 +156,18 @@ def main(args=None):
)

if not legacy_steam_runtime_path:
print("Steam Runtime was enabled but couldn't be found!")
sys.exit(-1)
exit_("Steam Runtime was enabled but couldn't be found!")
else:
use_steam_runtime = False
logger.info("Steam Runtime disabled.")

# 3. Find Winetricks
winetricks_path = get_winetricks_path()
if not winetricks_path:
print(
exit_(
"Winetricks isn't installed, please install "
"winetricks in order to use this script!"
)
sys.exit(-1)

# 4. Find any Steam library folders
steam_lib_paths = get_steam_lib_paths(steam_path)
Expand All @@ -175,26 +192,23 @@ def main(args=None):
])

if not has_installed_apps:
print("Found no games. You need to launch a game at least once "
exit_("Found no games. You need to launch a game at least once "
"before protontricks can find it.")
sys.exit(-1)

try:
steam_app = select_steam_app_with_gui(steam_apps=steam_apps)
except FileNotFoundError:
print(
exit_(
"Zenity is not installed. Zenity is required for the "
"Protontricks GUI."
)
sys.exit(-1)

# 6. Find Proton version of selected app
proton_app = find_proton_app(
steam_path=steam_path, steam_apps=steam_apps, appid=steam_app.appid
)
if not proton_app:
print("Proton installation could not be found!")
sys.exit(-1)
exit_("Proton installation could not be found!")

run_command(
winetricks_path=winetricks_path,
Expand Down Expand Up @@ -244,8 +258,7 @@ def main(args=None):
steam_path=steam_path, steam_apps=steam_apps, appid=args.appid)

if not proton_app:
print("Proton installation could not be found!")
sys.exit(-1)
exit_("Proton installation could not be found!")

# If neither search or GUI are set, do a normal Winetricks command
# Find game by appid
Expand All @@ -256,14 +269,13 @@ def main(args=None):
if not app.is_proton and app.appid == steam_appid
and app.prefix_path_exists)
except StopIteration:
print(
exit_(
"Steam app with the given app ID could not be found. "
"Is it installed, Proton compatible and have you launched it at "
"least once? You can search for the app ID using the following "
"command:\n"
"$ protontricks -s <GAME NAME>"
)
sys.exit(-1)

if args.winetricks_command:
run_command(
Expand Down

0 comments on commit cd2944a

Please sign in to comment.