Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4c39ec3c8e | ||
|
8950d8de2f | ||
|
85bd5fdf5d | ||
|
b98c6e5408 | ||
|
bab49377a3 | ||
|
aa591112ff | ||
|
08090bbb6b |
@@ -4,7 +4,7 @@ _winehelper_completions() {
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
opts="--help --version --debug install installed install-dxvk install-vkd3d -r -i remove-all --clear-pfx killall remove-prefix backup-prefix restore-prefix create-prefix --changelog changelog"
|
||||
opts="--help --version --debug install installed install-dxvk install-vkd3d -r -i remove-all --clear-pfx killall remove-prefix backup-prefix restore-prefix create-prefix --changelog changelog change-wine"
|
||||
wine_cmd="winecfg winereg winefile wineconsole winetricks desktop regedit explorer cmd run"
|
||||
|
||||
case "${prev}" in
|
||||
@@ -34,6 +34,20 @@ _winehelper_completions() {
|
||||
restore-prefix)
|
||||
return 0
|
||||
;;
|
||||
install-dxvk|install-vkd3d)
|
||||
local versions=$(winehelper "${prev}" list 2>/dev/null | grep ' - ' | sed 's/ - //')
|
||||
COMPREPLY=( $(compgen -W "${versions} none list" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
change-wine)
|
||||
local wine_versions=$(awk '
|
||||
/^#+\s*(WINE|WINE_LG|PROTON_LG|PROTON_STEAM)\s*#*$/ { in_group=1 }
|
||||
/^#+/ { if (! ($0 ~ /^#+\s*(WINE|WINE_LG|PROTON_LG|PROTON_STEAM)\s*#*$/)) in_group=0 }
|
||||
/^[a-f0-9]{64}/ && in_group { sub(/\.tar\.xz$/, "", $2); print $2 }
|
||||
' /usr/share/winehelper/sha256sum.list 2>/dev/null)
|
||||
COMPREPLY=( $(compgen -W "system ${wine_versions}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
@@ -20,6 +20,7 @@ _winehelper() {
|
||||
'remove-prefix[Удалить префикс и все связанные данные]'
|
||||
'backup-prefix[Создать резерную копию префикса]'
|
||||
'restore-prefix[восстановить префикс из резервной копии "путь/до/whpack"]'
|
||||
'change-wine[Изменить версию Wine/Proton для префикса]'
|
||||
)
|
||||
|
||||
wine_cmd=(
|
||||
@@ -69,6 +70,9 @@ _winehelper() {
|
||||
install-vkd3d)
|
||||
_get_component_versions 'install-vkd3d'
|
||||
;;
|
||||
change-wine)
|
||||
_get_wine_versions
|
||||
;;
|
||||
*)
|
||||
_values 'winehelper options' "${opts[@]}" "${wine_cmd[@]}"
|
||||
;;
|
||||
@@ -87,6 +91,22 @@ _get_component_versions () {
|
||||
_values 'versions' "${versions[@]}"
|
||||
}
|
||||
|
||||
_get_wine_versions () {
|
||||
local -a versions
|
||||
local sha256_file="/usr/share/winehelper/sha256sum.list"
|
||||
|
||||
if [[ -f "$sha256_file" ]]; then
|
||||
versions=( ${(f)"$(awk '
|
||||
/^#+\s*(WINE|WINE_LG|PROTON_LG|PROTON_STEAM)\s*#*$/ { in_group=1 }
|
||||
/^#+/ { if (! ($0 ~ /^#+\s*(WINE|WINE_LG|PROTON_LG|PROTON_STEAM)\s*#*$/)) in_group=0 }
|
||||
/^[a-f0-9]{64}/ && in_group { sub(/\.tar\.xz$/, "", $2); print $2 }
|
||||
' "$sha256_file" 2>/dev/null)"} )
|
||||
fi
|
||||
|
||||
versions+=(system)
|
||||
_values 'wine/proton versions' "${versions[@]}"
|
||||
}
|
||||
|
||||
_get_prefixes () {
|
||||
prefixes=( ${(f)"$(ls -1 ~/.local/share/winehelper/prefixes 2>/dev/null)"} )
|
||||
|
||||
|
41
winehelper
41
winehelper
@@ -7,18 +7,20 @@ if [[ $(id -u) -eq 0 ]] ; then
|
||||
fi
|
||||
|
||||
##### DEFAULT PATH #####
|
||||
export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT
|
||||
export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT THIRD_PARTY_FILE
|
||||
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
|
||||
# переменные для установленного WineHelper в систему
|
||||
WH_VERSION="$(rpm -q winehelper | awk -F'-' '{print $2}')"
|
||||
USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME"
|
||||
RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
|
||||
DATA_PATH="/usr/share/$SCRIPT_NAME"
|
||||
CHANGELOG_FILE="$(realpath "/usr/share/doc/winehelper"-*/CHANGELOG)"
|
||||
WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg"
|
||||
LICENSE_FILE="$(realpath "/usr/share/doc/winehelper"-*/LICENSE)"
|
||||
AGREEMENT="$(realpath "/usr/share/doc/winehelper"-*/LICENSE_AGREEMENT)"
|
||||
CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG"
|
||||
LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE"
|
||||
AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT"
|
||||
THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD_PARTY"
|
||||
else
|
||||
# переменные для тестового запуска WineHelper из репозитория
|
||||
USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
|
||||
@@ -28,6 +30,7 @@ else
|
||||
WH_ICON_PATH="$DATA_PATH/image/gui/winehelper-devel.svg"
|
||||
LICENSE_FILE="$DATA_PATH/LICENSE"
|
||||
AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
|
||||
THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"
|
||||
|
||||
# минимальная проверка синтаксиса скриптов
|
||||
for self_check_script in "$RUN_SCRIPT" \
|
||||
@@ -1652,8 +1655,8 @@ select_wine_version() {
|
||||
read -p "Введите номер для выбора wine/proton (0-$max_choice): " user_choice
|
||||
if [[ "$user_choice" =~ ^[0-9]+$ ]] && (( user_choice >= 0 && user_choice <= max_choice )); then
|
||||
if [[ "$user_choice" == "0" ]]; then
|
||||
print_info "Создание префикса отменено."
|
||||
exit 0
|
||||
print_info "Операция отменена."
|
||||
return 1
|
||||
fi
|
||||
local selected_opt
|
||||
selected_opt="${selectable_options[$user_choice]}"
|
||||
@@ -1667,6 +1670,7 @@ select_wine_version() {
|
||||
print_error "Неверный выбор. Введите число от 0 до $max_choice."
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
create_prefix() {
|
||||
@@ -1716,7 +1720,7 @@ create_prefix() {
|
||||
*) fatal "Неверный выбор. Операция отменена." ;;
|
||||
esac
|
||||
|
||||
select_wine_version
|
||||
select_wine_version || exit 0
|
||||
|
||||
print_info "Выберите тип создаваемого префикса:"
|
||||
echo " 0) Отмена создания префикса"
|
||||
@@ -2171,6 +2175,27 @@ run_install_vkd3d() {
|
||||
wait_wineserver
|
||||
}
|
||||
|
||||
run_change_wine_version() {
|
||||
local new_version="$1"
|
||||
|
||||
check_prefix_var
|
||||
init_database
|
||||
|
||||
if [[ -z "$new_version" ]]; then
|
||||
select_wine_version || exit 0
|
||||
new_version="$WH_WINE_USE"
|
||||
else
|
||||
export WH_WINE_USE="$new_version"
|
||||
fi
|
||||
|
||||
init_wine_ver
|
||||
|
||||
init_wineprefix
|
||||
|
||||
wait_wineserver
|
||||
print_ok "Версия Wine для префикса $PREFIX_NAME успешно изменена на $WH_WINE_USE."
|
||||
}
|
||||
|
||||
wh_info () {
|
||||
echo "Использование: $SCRIPT_NAME [команда]
|
||||
|
||||
@@ -2181,6 +2206,7 @@ wh_info () {
|
||||
|
||||
install-dxvk [версия|none|list] установить, удалить или показать версии DXVK
|
||||
install-vkd3d [версия|none|list] установить, удалить или показать версии VKD3D
|
||||
change-wine [версия] изменить версию Wine/Proton для текущего префикса
|
||||
|
||||
installed список установленных программ
|
||||
run [программа] запуск программы (отладка)
|
||||
@@ -2231,6 +2257,7 @@ case "$arg1" in
|
||||
install|-i) run_autoinstall "$@" ;;
|
||||
install-dxvk) run_install_dxvk "$@" ;;
|
||||
install-vkd3d) run_install_vkd3d "$@" ;;
|
||||
change-wine) run_change_wine_version "$@" ;;
|
||||
installed) check_installed_programs "$1" ;;
|
||||
run|-r) run_installed_programs "$1" ;;
|
||||
backup-prefix) backup_prefix "$@" ;;
|
||||
|
@@ -28,6 +28,7 @@ class Var:
|
||||
WH_ICON_PATH = os.environ.get("WH_ICON_PATH")
|
||||
LICENSE_FILE = os.environ.get("LICENSE_FILE")
|
||||
LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT")
|
||||
THIRD_PARTY_FILE = os.environ.get("THIRD_PARTY_FILE")
|
||||
|
||||
class DependencyManager:
|
||||
"""Класс для управления проверкой и установкой системных зависимостей."""
|
||||
@@ -42,14 +43,10 @@ class DependencyManager:
|
||||
|
||||
def _get_dependencies_path(self):
|
||||
"""Определяет и возвращает путь к скрипту dependencies.sh."""
|
||||
if Var.DATA_PATH:
|
||||
base_path = Var.DATA_PATH
|
||||
elif Var.RUN_SCRIPT and os.path.exists(Var.RUN_SCRIPT):
|
||||
base_path = os.path.dirname(Var.RUN_SCRIPT)
|
||||
else:
|
||||
if not Var.DATA_PATH:
|
||||
return None
|
||||
|
||||
return os.path.join(base_path, 'dependencies.sh')
|
||||
return os.path.join(Var.DATA_PATH, 'dependencies.sh')
|
||||
|
||||
def _calculate_file_hash(self):
|
||||
"""Вычисляет хэш SHA256 файла зависимостей."""
|
||||
@@ -2109,29 +2106,35 @@ class WineHelperGUI(QMainWindow):
|
||||
self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.")
|
||||
management_layout.addWidget(self.prefix_winefile_button, 2, 1)
|
||||
|
||||
self.change_wine_version_button = QPushButton("Управление Wine/Proton")
|
||||
self.change_wine_version_button.setMinimumHeight(32)
|
||||
self.change_wine_version_button.clicked.connect(self.open_wine_version_manager)
|
||||
self.change_wine_version_button.setToolTip("Изменение версии Wine или Proton для выбранного префикса.")
|
||||
management_layout.addWidget(self.change_wine_version_button, 3, 0, 1, 2)
|
||||
|
||||
# Добавляем небольшой отступ
|
||||
spacer_widget = QWidget()
|
||||
spacer_widget.setFixedHeight(5)
|
||||
management_layout.addWidget(spacer_widget, 3, 0, 1, 2)
|
||||
management_layout.addWidget(spacer_widget, 4, 0, 1, 2)
|
||||
|
||||
self.dxvk_manage_button = QPushButton("Управление DXVK")
|
||||
self.dxvk_manage_button.setMinimumHeight(32)
|
||||
self.dxvk_manage_button.clicked.connect(lambda: self.open_component_version_manager('dxvk'))
|
||||
self.dxvk_manage_button.setToolTip("Установка или удаление определенной версии DXVK в префиксе.")
|
||||
management_layout.addWidget(self.dxvk_manage_button, 4, 0)
|
||||
management_layout.addWidget(self.dxvk_manage_button, 5, 0)
|
||||
|
||||
self.vkd3d_manage_button = QPushButton("Управление VKD3D")
|
||||
self.vkd3d_manage_button.setMinimumHeight(32)
|
||||
self.vkd3d_manage_button.clicked.connect(lambda: self.open_component_version_manager('vkd3d-proton'))
|
||||
self.vkd3d_manage_button.setToolTip("Установка или удаление определенной версии vkd3d-proton в префиксе.")
|
||||
management_layout.addWidget(self.vkd3d_manage_button, 4, 1)
|
||||
management_layout.addWidget(self.vkd3d_manage_button, 5, 1)
|
||||
|
||||
# --- Правая сторона: Информационный блок ---
|
||||
self.prefix_info_display = QTextBrowser()
|
||||
self.prefix_info_display.setReadOnly(True)
|
||||
self.prefix_info_display.setFrameStyle(QFrame.StyledPanel)
|
||||
# Увеличиваем rowspan, чтобы учесть добавленный отступ
|
||||
management_layout.addWidget(self.prefix_info_display, 0, 2, 5, 1)
|
||||
management_layout.addWidget(self.prefix_info_display, 0, 2, 6, 1)
|
||||
|
||||
management_layout.setColumnStretch(0, 1)
|
||||
management_layout.setColumnStretch(1, 1)
|
||||
@@ -2141,7 +2144,7 @@ class WineHelperGUI(QMainWindow):
|
||||
separator = QFrame()
|
||||
separator.setFrameShape(QFrame.HLine)
|
||||
separator.setFrameShadow(QFrame.Sunken)
|
||||
management_layout.addWidget(separator, 5, 0, 1, 3)
|
||||
management_layout.addWidget(separator, 6, 0, 1, 3)
|
||||
|
||||
install_group = QWidget()
|
||||
install_layout = QVBoxLayout(install_group)
|
||||
@@ -2173,8 +2176,7 @@ class WineHelperGUI(QMainWindow):
|
||||
self.create_launcher_button.setEnabled(False) # Изначально неактивна
|
||||
action_buttons_layout.addWidget(self.create_launcher_button)
|
||||
install_layout.addLayout(action_buttons_layout)
|
||||
|
||||
management_layout.addWidget(install_group, 6, 0, 1, 3)
|
||||
management_layout.addWidget(install_group, 7, 0, 1, 3)
|
||||
|
||||
container_layout.addWidget(self.prefix_management_groupbox)
|
||||
layout.addWidget(self.management_container_groupbox)
|
||||
@@ -2532,6 +2534,81 @@ class WineHelperGUI(QMainWindow):
|
||||
# Если лицензия принята, запускаем установку.
|
||||
self.run_component_install_command(prefix_name, command, version)
|
||||
|
||||
def open_wine_version_manager(self):
|
||||
"""Открывает диалог для смены версии Wine/Proton для префикса."""
|
||||
prefix_name = self.current_managed_prefix_name
|
||||
if not prefix_name:
|
||||
QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.")
|
||||
return
|
||||
|
||||
# Определяем архитектуру префикса
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
last_conf_path = os.path.join(prefix_path, "last.conf")
|
||||
architecture = "win64" # По умолчанию
|
||||
if os.path.exists(last_conf_path):
|
||||
try:
|
||||
with open(last_conf_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
if 'WINEARCH=' in line:
|
||||
arch_val = line.split('=', 1)[1].strip().strip('"\'')
|
||||
if arch_val:
|
||||
architecture = arch_val
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Предупреждение: не удалось прочитать архитектуру из {last_conf_path}: {e}")
|
||||
|
||||
dialog = WineVersionSelectionDialog(architecture, self)
|
||||
if dialog.exec_() == QDialog.Accepted and dialog.selected_version:
|
||||
new_version = dialog.selected_version
|
||||
new_version_display = dialog.selected_display_text
|
||||
|
||||
if not self._show_license_agreement_dialog():
|
||||
return # Пользователь отклонил лицензию
|
||||
|
||||
self.run_change_wine_version_command(prefix_name, new_version, new_version_display)
|
||||
|
||||
def run_change_wine_version_command(self, prefix_name, new_version, new_version_display):
|
||||
"""Выполняет команду смены версии Wine/Proton через winehelper."""
|
||||
self.command_dialog = QDialog(self)
|
||||
self.command_dialog.setWindowTitle(f"Смена версии Wine на: {new_version_display}")
|
||||
self.command_dialog.setMinimumSize(750, 400)
|
||||
self.command_dialog.setModal(True)
|
||||
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
self.command_log_output = QTextEdit()
|
||||
self.command_log_output.setReadOnly(True)
|
||||
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
|
||||
layout.addWidget(self.command_log_output)
|
||||
|
||||
self.command_close_button = QPushButton("Закрыть")
|
||||
self.command_close_button.setEnabled(False)
|
||||
self.command_close_button.clicked.connect(self.command_dialog.close)
|
||||
layout.addWidget(self.command_close_button)
|
||||
self.command_dialog.setLayout(layout)
|
||||
|
||||
# Сбрасываем состояние для обработки лога с прогрессом
|
||||
self.command_output_buffer = ""
|
||||
self.command_last_line_was_progress = False
|
||||
|
||||
self.command_process = QProcess(self.command_dialog)
|
||||
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
|
||||
self.command_process.readyReadStandardOutput.connect(self._handle_prefix_creation_output)
|
||||
self.command_process.finished.connect(
|
||||
lambda exit_code, exit_status: self._handle_change_wine_version_finished(
|
||||
prefix_name, exit_code, exit_status
|
||||
)
|
||||
)
|
||||
|
||||
env = QProcessEnvironment.systemEnvironment()
|
||||
env.insert("WINEPREFIX", os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name))
|
||||
self.command_process.setProcessEnvironment(env)
|
||||
|
||||
args = ["change-wine", new_version]
|
||||
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
|
||||
self.command_process.start(self.winehelper_path, args)
|
||||
self.command_dialog.exec_()
|
||||
|
||||
def run_component_install_command(self, prefix_name, command, version):
|
||||
"""Выполняет команду установки компонента (DXVK/VKD3D) через winehelper."""
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
@@ -2572,11 +2649,31 @@ class WineHelperGUI(QMainWindow):
|
||||
self.command_process.start(self.winehelper_path, args)
|
||||
self.command_dialog.exec_()
|
||||
|
||||
def _handle_change_wine_version_finished(self, prefix_name, exit_code, exit_status):
|
||||
"""Обрабатывает завершение смены версии Wine и обновляет информацию о префиксе."""
|
||||
# Обрабатываем остаток в буфере, если он есть
|
||||
if self.command_output_buffer:
|
||||
self._process_command_log_line(self.command_output_buffer)
|
||||
self.command_output_buffer = ""
|
||||
|
||||
# Если последней строкой был прогресс, "завершаем" его переносом строки.
|
||||
if self.command_last_line_was_progress:
|
||||
cursor = self.command_log_output.textCursor()
|
||||
cursor.movePosition(QTextCursor.End)
|
||||
cursor.insertText("\n")
|
||||
self.command_last_line_was_progress = False
|
||||
|
||||
# Вызываем общий обработчик для обновления лога и кнопки закрытия
|
||||
self._handle_command_finished(exit_code, exit_status)
|
||||
|
||||
# В случае успеха обновляем панель информации о префиксе
|
||||
if exit_code == 0:
|
||||
self.update_prefix_info_display(prefix_name)
|
||||
|
||||
def _handle_component_install_finished(self, prefix_name, exit_code, exit_status):
|
||||
"""Обрабатывает завершение установки компонента и обновляет информацию о префиксе."""
|
||||
# Вызываем общий обработчик для обновления лога и кнопки закрытия
|
||||
self._handle_command_finished(exit_code, exit_status)
|
||||
|
||||
# В случае успеха обновляем панель информации о префиксе
|
||||
if exit_code == 0:
|
||||
self.update_prefix_info_display(prefix_name)
|
||||
@@ -2723,8 +2820,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
# Читаем и парсим файл THIRD-PARTY
|
||||
third_party_html = ""
|
||||
third_party_file_path = os.path.join(Var.DATA_PATH, "THIRD-PARTY")
|
||||
if os.path.exists(third_party_file_path):
|
||||
third_party_file_path = Var.THIRD_PARTY_FILE
|
||||
if third_party_file_path and os.path.exists(third_party_file_path):
|
||||
with open(third_party_file_path, 'r', encoding='utf-8') as f_tp:
|
||||
third_party_content = f_tp.read()
|
||||
|
||||
@@ -3196,20 +3293,20 @@ class WineHelperGUI(QMainWindow):
|
||||
QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}")
|
||||
return
|
||||
|
||||
winehelper_dir = os.path.dirname(self.winehelper_path)
|
||||
winetricks_search_dir = Var.DATA_PATH
|
||||
winetricks_path = None
|
||||
try:
|
||||
# Ищем файл, который начинается с 'winetricks_'
|
||||
for filename in os.listdir(winehelper_dir):
|
||||
for filename in os.listdir(winetricks_search_dir):
|
||||
if filename.startswith("winetricks_"):
|
||||
winetricks_path = os.path.join(winehelper_dir, filename)
|
||||
winetricks_path = os.path.join(winetricks_search_dir, filename)
|
||||
break # Нашли, выходим из цикла
|
||||
except OSError as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Не удалось прочитать директорию {winehelper_dir}: {e}")
|
||||
QMessageBox.critical(self, "Ошибка", f"Не удалось прочитать директорию {winetricks_search_dir}: {e}")
|
||||
return
|
||||
|
||||
if not winetricks_path:
|
||||
QMessageBox.critical(self, "Ошибка", f"Скрипт winetricks не найден в директории:\n{winehelper_dir}")
|
||||
QMessageBox.critical(self, "Ошибка", f"Скрипт winetricks не найден в директории:\n{winetricks_search_dir}")
|
||||
return
|
||||
|
||||
wine_executable = self._get_wine_executable_for_prefix(prefix_name)
|
||||
|
Reference in New Issue
Block a user