Skip to content

Instantly share code, notes, and snippets.

@Ennea
Last active June 6, 2023 15:44
Show Gist options
  • Save Ennea/fd853331ab2db101a67b37d3879df572 to your computer and use it in GitHub Desktop.
Save Ennea/fd853331ab2db101a67b37d3879df572 to your computer and use it in GitHub Desktop.
Wine Cellar, a lightweight Wine prefix manager
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Wine\FileOpenAssociations]
"Enable"="N"
#!/bin/python
'''Lightweight Wine prefix manager'''
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
BASE_PATH = Path('~/Wine').expanduser()
PREFIX_PATH = BASE_PATH / 'Prefixes'
# parse arguments
parser = argparse.ArgumentParser(prog='Wine Cellar', description='Lightweight Wine prefix manager')
subparsers = parser.add_subparsers(dest='subcommand', required=True)
run = subparsers.add_parser('run', help='Run a command in a given prefix, or run its default command')
run.add_argument('name', help='Prefix name')
run.add_argument('command', nargs='?')
run.add_argument('remainder', nargs=argparse.REMAINDER)
ls = subparsers.add_parser('ls', help='List available prefixes')
mkpfx = subparsers.add_parser('mkpfx', help='Create a new prefix')
mkpfx.add_argument('name', help='Prefix name')
wt = subparsers.add_parser('wt', help='Run winetricks in a given prefix')
wt.add_argument('name', help='Prefix name')
wt.add_argument('remainder', nargs=argparse.REMAINDER)
kill = subparsers.add_parser('kill', help="Kill a given prefix' wine server")
kill.add_argument('name', help='Prefix name')
args = parser.parse_args()
# helper functions
def get_prefixes():
prefixes = {}
for pfx in PREFIX_PATH.glob('*.pfx.json'):
pfx_name = pfx.name[:-9]
with pfx.open('r') as fp:
try:
prefixes[pfx_name] = json.load(fp)
except json.decoder.JSONDecodeError:
print(f'"{str(pfx)}" does not appear to be valid JSON')
return prefixes
prefixes = get_prefixes()
def assert_pfx_name(name):
if name not in prefixes:
print(f'"{name}" is not a known prefix')
sys.exit(1)
def prepare_env(name):
pfx = prefixes[name]
env = os.environ.copy()
env['WINEPREFIX'] = pfx['path']
if 'env' in pfx:
for k, v in pfx['env'].items():
env[k] = v
return env
# do things
if args.subcommand == 'run':
assert_pfx_name(args.name)
env = prepare_env(args.name)
if args.command is None:
if 'default_command' not in prefixes[args.name]:
print('This prefix does not have a default command')
else:
subprocess.run([ 'wine', prefixes[args.name]['default_command'] ], env=env)
elif args.command is not None:
subprocess.run([ 'wine', args.command ] + args.remainder, env=env)
elif args.subcommand == 'ls':
pfx_names = sorted(prefixes.keys())
for pfx in pfx_names:
print(f'{pfx} – {prefixes[pfx]["path"]}')
elif args.subcommand == 'mkpfx':
if ' ' in args.name:
print('You probably do not want a space in your prefix name :)')
sys.exit(1)
if args.name in prefixes:
print(f'Prefix "{args.name}" already exists')
else:
pfx_path = str(PREFIX_PATH / args.name)
env = os.environ.copy()
env['WINEPREFIX'] = pfx_path
env['WINEDLLOVERRIDES'] = 'winemenubuilder.exe=d'
subprocess.run([ 'regedit', str(BASE_PATH / 'disable-file-associations.reg') ], env=env, capture_output=True)
pfx_file_path = PREFIX_PATH / (args.name + '.pfx.json')
with pfx_file_path.open('w') as fp:
json.dump({ 'path': pfx_path }, fp, indent=4)
print(f'Prefix "{args.name}" has been created at "{pfx_path}"')
elif args.subcommand == 'wt':
assert_pfx_name(args.name)
env = prepare_env(args.name)
subprocess.run([ 'sh', str(BASE_PATH / 'winetricks') ] + args.remainder, env=env)
elif args.subcommand == 'kill':
assert_pfx_name(args.name)
env = prepare_env(args.name)
subprocess.run([ 'wineserver', '-k' ], env=env)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment