added control buttons for dxvk/vkd3d
This commit is contained in:
		
							
								
								
									
										86
									
								
								winehelper
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								winehelper
									
									
									
									
									
								
							| @@ -776,9 +776,10 @@ init_wined3d () { | |||||||
| init_dxvk () { | init_dxvk () { | ||||||
|     check_variables USE_DXVK_VER "$1" |     check_variables USE_DXVK_VER "$1" | ||||||
|  |  | ||||||
|     get_dxvk () { |     get_dxvk() { | ||||||
|         DXVK_URL="$1" |         local DXVK_URL="$1" | ||||||
|         DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/dxvk-${DXVK_VAR_VER}.tar.$(echo ${DXVK_URL#*.tar.})" |         local DXVK_VAR_VER="$2" | ||||||
|  |         local DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}.tar.$(echo "${DXVK_URL#*.tar.}")" | ||||||
|         if try_download "$DXVK_URL" "$DXVK_PACKAGE" check256sum \ |         if try_download "$DXVK_URL" "$DXVK_PACKAGE" check256sum \ | ||||||
|         && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR" |         && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR" | ||||||
|         then |         then | ||||||
| @@ -789,8 +790,8 @@ init_dxvk () { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for DXVK_VAR_VER in "$USE_DXVK_VER" $@ ; do |     for DXVK_VAR_VER in "$USE_DXVK_VER" $@ ; do | ||||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/dxvk-$DXVK_VAR_VER" ]] ; then |         if [[ ! -d "${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}" ]] ; then | ||||||
|             get_dxvk "$CLOUD_URL/dxvk-${DXVK_VAR_VER}.tar.xz" |             get_dxvk "$CLOUD_URL/${DXVK_VAR_VER}.tar.xz" "$DXVK_VAR_VER" | ||||||
|         fi |         fi | ||||||
|     done |     done | ||||||
|  |  | ||||||
| @@ -803,8 +804,8 @@ init_dxvk () { | |||||||
|     fi |     fi | ||||||
|  |  | ||||||
|     for dxvkfiles in $DXVK_FILES ; do |     for dxvkfiles in $DXVK_FILES ; do | ||||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x64/$dxvkfiles.dll" |         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x64/$dxvkfiles.dll" | ||||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x32/$dxvkfiles.dll" |         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x32/$dxvkfiles.dll" | ||||||
|         then var_winedlloverride_update "$dxvkfiles=n" |         then var_winedlloverride_update "$dxvkfiles=n" | ||||||
|         fi |         fi | ||||||
|     done |     done | ||||||
| @@ -813,9 +814,10 @@ init_dxvk () { | |||||||
| init_vkd3d () { | init_vkd3d () { | ||||||
|     check_variables USE_VKD3D_VER "$1" |     check_variables USE_VKD3D_VER "$1" | ||||||
|  |  | ||||||
|     get_vkd3d () { |     get_vkd3d() { | ||||||
|         VKD3D_URL="$1" |         local VKD3D_URL="$1" | ||||||
|         VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/vkd3d-proton-${VKD3D_VAR_VER}.tar.$(echo ${VKD3D_URL#*.tar.})" |         local VKD3D_VAR_VER="$2" | ||||||
|  |         local VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}.tar.$(echo "${VKD3D_URL#*.tar.}")" | ||||||
|         if try_download "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \ |         if try_download "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \ | ||||||
|         && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR" |         && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR" | ||||||
|         then |         then | ||||||
| @@ -826,15 +828,15 @@ init_vkd3d () { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for VKD3D_VAR_VER in "$USE_VKD3D_VER" $@ ; do |     for VKD3D_VAR_VER in "$USE_VKD3D_VER" $@ ; do | ||||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/vkd3d-proton-$VKD3D_VAR_VER" ]] ; then |         if [[ ! -d "${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}" ]] ; then | ||||||
|             get_vkd3d "$CLOUD_URL/vkd3d-proton-${VKD3D_VAR_VER}.tar.xz" |             get_vkd3d "$CLOUD_URL/${VKD3D_VAR_VER}.tar.xz" "$VKD3D_VAR_VER" | ||||||
|         fi |         fi | ||||||
|     done |     done | ||||||
|  |  | ||||||
|     VKD3D_FILES="d3d12 d3d12core libvkd3d-shader-1 libvkd3d-1" # libvkd3d-proton-utils-3 |     VKD3D_FILES="d3d12 d3d12core libvkd3d-shader-1 libvkd3d-1" # libvkd3d-proton-utils-3 | ||||||
|     for vkd3dfiles in $VKD3D_FILES ; do |     for vkd3dfiles in $VKD3D_FILES ; do | ||||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x64/$vkd3dfiles.dll" |         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x64/$vkd3dfiles.dll" | ||||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x86/$vkd3dfiles.dll" |         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x86/$vkd3dfiles.dll" | ||||||
|         then var_winedlloverride_update "$vkd3dfiles=n" |         then var_winedlloverride_update "$vkd3dfiles=n" | ||||||
|         fi |         fi | ||||||
|     done |     done | ||||||
| @@ -2011,6 +2013,57 @@ restore_prefix() { | |||||||
|     return 0 |     return 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | update_last_conf_var() { | ||||||
|  |     local var_name="$1" | ||||||
|  |     local new_value="$2" | ||||||
|  |     local conf_file="$WINEPREFIX/last.conf" | ||||||
|  |  | ||||||
|  |     if [[ ! -f "$conf_file" ]]; then | ||||||
|  |         print_warning "Файл last.conf не найден, не могу обновить переменную $var_name." | ||||||
|  |         return 1 | ||||||
|  |     fi | ||||||
|  |  | ||||||
|  |     if grep -q "export $var_name=" "$conf_file"; then | ||||||
|  |         sed -i "s|^export $var_name=.*|export $var_name=\"$new_value\"|" "$conf_file" | ||||||
|  |     else | ||||||
|  |         echo "export $var_name=\"$new_value\"" >> "$conf_file" | ||||||
|  |     fi | ||||||
|  | } | ||||||
|  |  | ||||||
|  | run_install_dxvk() { | ||||||
|  |     local version="$1" | ||||||
|  |     check_prefix_var | ||||||
|  |     init_database | ||||||
|  |     init_wine_ver | ||||||
|  |     init_wineprefix | ||||||
|  |     if [[ "$version" == "none" ]] ; then | ||||||
|  |         print_info "Удаление DXVK..." | ||||||
|  |         init_wined3d | ||||||
|  |         update_last_conf_var "DXVK_VER" "" | ||||||
|  |     else | ||||||
|  |         init_dxvk "$version" | ||||||
|  |         update_last_conf_var "DXVK_VER" "$USE_DXVK_VER" | ||||||
|  |     fi | ||||||
|  |     wait_wineserver | ||||||
|  | } | ||||||
|  |  | ||||||
|  | run_install_vkd3d() { | ||||||
|  |     local version="$1" | ||||||
|  |     check_prefix_var | ||||||
|  |     init_database | ||||||
|  |     init_wine_ver | ||||||
|  |     init_wineprefix | ||||||
|  |     if [[ "$version" == "none" ]] ; then | ||||||
|  |         print_info "Удаление VKD3D..." | ||||||
|  |         init_wined3d | ||||||
|  |         update_last_conf_var "VKD3D_VER" "" | ||||||
|  |     else | ||||||
|  |         init_vkd3d "$version" | ||||||
|  |         update_last_conf_var "VKD3D_VER" "$USE_VKD3D_VER" | ||||||
|  |     fi | ||||||
|  |     wait_wineserver | ||||||
|  | } | ||||||
|  |  | ||||||
| wh_info () { | wh_info () { | ||||||
|     echo "Использование: $SCRIPT_NAME [команда] |     echo "Использование: $SCRIPT_NAME [команда] | ||||||
|  |  | ||||||
| @@ -2019,6 +2072,9 @@ wh_info () { | |||||||
|     install [скрипт]                запустить скрипт установки программы |     install [скрипт]                запустить скрипт установки программы | ||||||
|     install [скрипт] --clear-pfx    не использовать готовый префикс для установки ПО |     install [скрипт] --clear-pfx    не использовать готовый префикс для установки ПО | ||||||
|  |  | ||||||
|  |     install-dxvk [версия|none]      установить/удалить DXVK в выбранный префикс | ||||||
|  |     install-vkd3d [версия|none]     установить/удалить VKD3D в выбранный префикс | ||||||
|  |  | ||||||
|     installed                       список установленных программ |     installed                       список установленных программ | ||||||
|     run [программа]                 запуск программы (отладка) |     run [программа]                 запуск программы (отладка) | ||||||
|     remove-all                      удалить WineHelper и все связанные данные |     remove-all                      удалить WineHelper и все связанные данные | ||||||
| @@ -2066,6 +2122,8 @@ case "$arg1" in | |||||||
|     winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;; |     winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;; | ||||||
|     desktop) create_desktop "$@" ; exit 0 ;; |     desktop) create_desktop "$@" ; exit 0 ;; | ||||||
|     install|-i) run_autoinstall "$@" ;; |     install|-i) run_autoinstall "$@" ;; | ||||||
|  |     install-dxvk) run_install_dxvk "$@" ;; | ||||||
|  |     install-vkd3d) run_install_vkd3d "$@" ;; | ||||||
|     installed) check_installed_programs "$1" ;; |     installed) check_installed_programs "$1" ;; | ||||||
|     run|-r) run_installed_programs "$1" ;; |     run|-r) run_installed_programs "$1" ;; | ||||||
|     backup-prefix) backup_prefix "$@" ;; |     backup-prefix) backup_prefix "$@" ;; | ||||||
|   | |||||||
| @@ -1078,7 +1078,7 @@ class WineVersionSelectionDialog(QDialog): | |||||||
|                     if not line: |                     if not line: | ||||||
|                         continue |                         continue | ||||||
|  |  | ||||||
|                     match = re.match(r'^#+\s+([A-Z_]+)\s+#*$', line) |                     match = re.match(r'^#+\s*([^#]+?)\s*#*$', line) | ||||||
|                     if match: |                     if match: | ||||||
|                         group_name = match.group(1) |                         group_name = match.group(1) | ||||||
|                         allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"} |                         allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"} | ||||||
| @@ -1354,6 +1354,118 @@ class CreatePrefixDialog(QDialog): | |||||||
|  |  | ||||||
|         self.accept() |         self.accept() | ||||||
|  |  | ||||||
|  | class ComponentVersionSelectionDialog(QDialog): | ||||||
|  |     """Диалог для выбора версии компонента (DXVK, VKD3D).""" | ||||||
|  |  | ||||||
|  |     def __init__(self, component_group, title, parent=None, add_extra_options=True): | ||||||
|  |         super().__init__(parent) | ||||||
|  |         self.component_group = component_group | ||||||
|  |         self.selected_version = None | ||||||
|  |         self.versions_data = [] | ||||||
|  |  | ||||||
|  |         self.setWindowTitle(title) | ||||||
|  |         self.setMinimumSize(600, 400) | ||||||
|  |         self.setModal(True) | ||||||
|  |  | ||||||
|  |         main_layout = QVBoxLayout(self) | ||||||
|  |  | ||||||
|  |         self.search_edit = QLineEdit() | ||||||
|  |         self.search_edit.setPlaceholderText("Поиск версии...") | ||||||
|  |         self.search_edit.textChanged.connect(self.filter_versions) | ||||||
|  |         main_layout.addWidget(self.search_edit) | ||||||
|  |  | ||||||
|  |         self.scroll_area = QScrollArea() | ||||||
|  |         self.scroll_area.setWidgetResizable(True) | ||||||
|  |         main_layout.addWidget(self.scroll_area) | ||||||
|  |  | ||||||
|  |         scroll_content = QWidget() | ||||||
|  |         self.scroll_area.setWidget(scroll_content) | ||||||
|  |  | ||||||
|  |         self.grid_layout = QGridLayout(scroll_content) | ||||||
|  |         self.grid_layout.setAlignment(Qt.AlignTop) | ||||||
|  |  | ||||||
|  |         self.buttons = [] | ||||||
|  |         # Кнопка "Удалить" теперь находится вне сетки, поэтому начинаем с 0 строки. | ||||||
|  |         self.load_versions(start_row=0) | ||||||
|  |  | ||||||
|  |         # --- Панель с кнопками действий внизу диалога --- | ||||||
|  |         button_layout = QHBoxLayout() | ||||||
|  |  | ||||||
|  |         if add_extra_options: | ||||||
|  |             uninstall_btn = QPushButton("Удалить из префикса") | ||||||
|  |             uninstall_btn.setToolTip("Удаляет текущую установленную версию компонента из префикса.") | ||||||
|  |             uninstall_btn.clicked.connect(partial(self.on_version_selected, "none")) | ||||||
|  |             # Добавляем кнопку слева | ||||||
|  |             button_layout.addWidget(uninstall_btn) | ||||||
|  |  | ||||||
|  |         button_layout.addStretch(1)  # Растягиваем пространство, чтобы разнести кнопки | ||||||
|  |  | ||||||
|  |         cancel_button = QPushButton("Отмена") | ||||||
|  |         cancel_button.clicked.connect(self.reject) | ||||||
|  |         button_layout.addWidget(cancel_button) | ||||||
|  |  | ||||||
|  |         main_layout.addLayout(button_layout) | ||||||
|  |  | ||||||
|  |     def load_versions(self, start_row): | ||||||
|  |         """Загружает и отображает версии.""" | ||||||
|  |         self._parse_sha256_list() | ||||||
|  |         self.populate_ui(start_row) | ||||||
|  |  | ||||||
|  |     def _parse_sha256_list(self): | ||||||
|  |         """Парсит sha256sum.list для получения списка версий.""" | ||||||
|  |         sha256_path = os.path.join(Var.DATA_PATH, "sha256sum.list") | ||||||
|  |         if not os.path.exists(sha256_path): | ||||||
|  |             self.versions_data = [] | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         current_group = None | ||||||
|  |         try: | ||||||
|  |             with open(sha256_path, 'r', encoding='utf-8') as f: | ||||||
|  |                 for line in f: | ||||||
|  |                     line = line.strip() | ||||||
|  |                     if not line: | ||||||
|  |                         continue | ||||||
|  |                     match = re.match(r'^#+\s*([^#]+?)\s*#*$', line) | ||||||
|  |                     if match: | ||||||
|  |                         current_group = match.group(1).strip() | ||||||
|  |                         continue | ||||||
|  |                     if current_group == self.component_group and re.match(r'^[a-f0-9]{64}', line): | ||||||
|  |                         parts = line.split(maxsplit=1) | ||||||
|  |                         if len(parts) == 2: | ||||||
|  |                             filename = parts[1] | ||||||
|  |                             if filename.endswith('.tar.xz'): | ||||||
|  |                                 version_name = filename[:-7] | ||||||
|  |                                 self.versions_data.append(version_name) | ||||||
|  |         except IOError: | ||||||
|  |             self.versions_data = [] | ||||||
|  |  | ||||||
|  |     def populate_ui(self, start_row): | ||||||
|  |         """Заполняет UI кнопками версий.""" | ||||||
|  |         versions = sorted(self.versions_data, reverse=True) | ||||||
|  |         num_columns = 3 | ||||||
|  |         row, col = start_row, 0 | ||||||
|  |         for version_name in versions: | ||||||
|  |             btn = QPushButton(version_name) | ||||||
|  |             btn.clicked.connect(partial(self.on_version_selected, version_name)) | ||||||
|  |             self.grid_layout.addWidget(btn, row, col) | ||||||
|  |             self.buttons.append(btn) | ||||||
|  |             col += 1 | ||||||
|  |             if col >= num_columns: | ||||||
|  |                 col = 0 | ||||||
|  |                 row += 1 | ||||||
|  |  | ||||||
|  |     def filter_versions(self): | ||||||
|  |         """Фильтрует видимость кнопок версий на основе текста поиска.""" | ||||||
|  |         search_text = self.search_edit.text().lower() | ||||||
|  |         for btn_widget in self.buttons: | ||||||
|  |             is_match = search_text in btn_widget.text().lower() | ||||||
|  |             btn_widget.setVisible(is_match) | ||||||
|  |  | ||||||
|  |     def on_version_selected(self, version_name): | ||||||
|  |         """Обрабатывает выбор версии.""" | ||||||
|  |         self.selected_version = version_name | ||||||
|  |         self.accept() | ||||||
|  |  | ||||||
| class WineHelperGUI(QMainWindow): | class WineHelperGUI(QMainWindow): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         super().__init__() |         super().__init__() | ||||||
| @@ -1980,11 +2092,29 @@ class WineHelperGUI(QMainWindow): | |||||||
|         self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.") |         self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.") | ||||||
|         management_layout.addWidget(self.prefix_winefile_button, 2, 1) |         management_layout.addWidget(self.prefix_winefile_button, 2, 1) | ||||||
|  |  | ||||||
|  |         # Добавляем небольшой отступ | ||||||
|  |         spacer_widget = QWidget() | ||||||
|  |         spacer_widget.setFixedHeight(5) | ||||||
|  |         management_layout.addWidget(spacer_widget, 3, 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) | ||||||
|  |  | ||||||
|  |         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) | ||||||
|  |  | ||||||
|         # --- Правая сторона: Информационный блок --- |         # --- Правая сторона: Информационный блок --- | ||||||
|         self.prefix_info_display = QTextBrowser() |         self.prefix_info_display = QTextBrowser() | ||||||
|         self.prefix_info_display.setReadOnly(True) |         self.prefix_info_display.setReadOnly(True) | ||||||
|         self.prefix_info_display.setFrameStyle(QFrame.StyledPanel) |         self.prefix_info_display.setFrameStyle(QFrame.StyledPanel) | ||||||
|         management_layout.addWidget(self.prefix_info_display, 0, 2, 3, 1) |         # Увеличиваем rowspan, чтобы учесть добавленный отступ | ||||||
|  |         management_layout.addWidget(self.prefix_info_display, 0, 2, 5, 1) | ||||||
|  |  | ||||||
|         management_layout.setColumnStretch(0, 1) |         management_layout.setColumnStretch(0, 1) | ||||||
|         management_layout.setColumnStretch(1, 1) |         management_layout.setColumnStretch(1, 1) | ||||||
| @@ -1994,7 +2124,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|         separator = QFrame() |         separator = QFrame() | ||||||
|         separator.setFrameShape(QFrame.HLine) |         separator.setFrameShape(QFrame.HLine) | ||||||
|         separator.setFrameShadow(QFrame.Sunken) |         separator.setFrameShadow(QFrame.Sunken) | ||||||
|         management_layout.addWidget(separator, 3, 0, 1, 3) |         management_layout.addWidget(separator, 5, 0, 1, 3) | ||||||
|  |  | ||||||
|         install_group = QWidget() |         install_group = QWidget() | ||||||
|         install_layout = QVBoxLayout(install_group) |         install_layout = QVBoxLayout(install_group) | ||||||
| @@ -2027,7 +2157,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|         action_buttons_layout.addWidget(self.create_launcher_button) |         action_buttons_layout.addWidget(self.create_launcher_button) | ||||||
|         install_layout.addLayout(action_buttons_layout) |         install_layout.addLayout(action_buttons_layout) | ||||||
|  |  | ||||||
|         management_layout.addWidget(install_group, 4, 0, 1, 3) |         management_layout.addWidget(install_group, 6, 0, 1, 3) | ||||||
|  |  | ||||||
|         container_layout.addWidget(self.prefix_management_groupbox) |         container_layout.addWidget(self.prefix_management_groupbox) | ||||||
|         layout.addWidget(self.management_container_groupbox) |         layout.addWidget(self.management_container_groupbox) | ||||||
| @@ -2220,8 +2350,10 @@ class WineHelperGUI(QMainWindow): | |||||||
|             "WINEARCH": ("Архитектура", lambda v: "64-bit" if v == "win64" else "32-bit"), |             "WINEARCH": ("Архитектура", lambda v: "64-bit" if v == "win64" else "32-bit"), | ||||||
|             "WH_WINE_USE": ("Версия Wine", lambda v: "Системная" if v == "system" else v), |             "WH_WINE_USE": ("Версия Wine", lambda v: "Системная" if v == "system" else v), | ||||||
|             "BASE_PFX": ("Тип", lambda v: 'Чистый' if v == "none" else 'С рекомендуемыми библиотеками'), |             "BASE_PFX": ("Тип", lambda v: 'Чистый' if v == "none" else 'С рекомендуемыми библиотеками'), | ||||||
|  |             "DXVK_VER": ("Версия DXVK", lambda v: v if v else "Не установлено"), | ||||||
|  |             "VKD3D_VER": ("Версия VKD3D", lambda v: v if v else "Не установлено"), | ||||||
|         } |         } | ||||||
|         display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX"] |         display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER"] | ||||||
|  |  | ||||||
|         html_content = f'<p style="line-height: 1.3; font-size: 9pt;">' |         html_content = f'<p style="line-height: 1.3; font-size: 9pt;">' | ||||||
|         html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>" |         html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>" | ||||||
| @@ -2310,6 +2442,128 @@ class WineHelperGUI(QMainWindow): | |||||||
|         self.command_process.start(wine_executable, args) |         self.command_process.start(wine_executable, args) | ||||||
|         self.command_dialog.exec_() |         self.command_dialog.exec_() | ||||||
|  |  | ||||||
|  |     def _get_prefix_component_version(self, prefix_name, component_key): | ||||||
|  |         """ | ||||||
|  |         Читает last.conf префикса и возвращает версию указанного компонента. | ||||||
|  |         :param prefix_name: Имя префикса. | ||||||
|  |         :param component_key: Ключ компонента (например, 'DXVK_VER'). | ||||||
|  |         :return: Строку с версией или None, если не найдено или значение пустое. | ||||||
|  |         """ | ||||||
|  |         if not prefix_name: | ||||||
|  |             return None | ||||||
|  |  | ||||||
|  |         last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf") | ||||||
|  |         if not os.path.exists(last_conf_path): | ||||||
|  |             return None | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             with open(last_conf_path, 'r', encoding='utf-8') as f: | ||||||
|  |                 for line in f: | ||||||
|  |                     line = line.strip() | ||||||
|  |                     # Ищем строку вида 'export KEY="value"' или 'export KEY=value' | ||||||
|  |                     if line.startswith('export '): | ||||||
|  |                         parts = line[7:].split('=', 1) | ||||||
|  |                         if len(parts) == 2: | ||||||
|  |                             key = parts[0].strip() | ||||||
|  |                             if key == component_key: | ||||||
|  |                                 value = parts[1].strip().strip('"\'') | ||||||
|  |                                 # Возвращаем значение, только если оно не пустое. | ||||||
|  |                                 return value if value else None | ||||||
|  |         except IOError as e: | ||||||
|  |             print(f"Ошибка чтения last.conf для {prefix_name}: {e}") | ||||||
|  |             return None | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |     def open_component_version_manager(self, component): | ||||||
|  |         """Открывает диалог выбора версии для DXVK/VKD3D и запускает установку.""" | ||||||
|  |         prefix_name = self.current_managed_prefix_name | ||||||
|  |         if not prefix_name: | ||||||
|  |             QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.") | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         component_key = None | ||||||
|  |         if component == 'dxvk': | ||||||
|  |             group = 'DXVK' | ||||||
|  |             title = f"Управление DXVK для префикса: {prefix_name}" | ||||||
|  |             command = 'install-dxvk' | ||||||
|  |             component_key = 'DXVK_VER' | ||||||
|  |         elif component == 'vkd3d-proton': | ||||||
|  |             group = 'VKD3D' | ||||||
|  |             title = f"Управление vkd3d для префикса: {prefix_name}" | ||||||
|  |             command = 'install-vkd3d' | ||||||
|  |             component_key = 'VKD3D_VER' | ||||||
|  |         else: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         dialog = ComponentVersionSelectionDialog(group, title, self) | ||||||
|  |         if dialog.exec_() == QDialog.Accepted and dialog.selected_version: | ||||||
|  |             version = dialog.selected_version | ||||||
|  |  | ||||||
|  |             if version == "none": | ||||||
|  |                 # Удаление: сначала проверяем, есть ли что удалять. | ||||||
|  |                 installed_version = self._get_prefix_component_version(prefix_name, component_key) | ||||||
|  |                 if not installed_version: | ||||||
|  |                     QMessageBox.information(self, "Информация", "Установленных версий нет.") | ||||||
|  |                     return  # Прерываем выполнение, т.к. удалять нечего | ||||||
|  |                 # Для удаления лицензия не нужна, запускаем сразу. | ||||||
|  |                 self.run_component_install_command(prefix_name, command, version) | ||||||
|  |             else: | ||||||
|  |                 # Установка: сначала показываем лицензионное соглашение. | ||||||
|  |                 if not self._show_license_agreement_dialog(): | ||||||
|  |                     return  # Пользователь отклонил лицензию | ||||||
|  |  | ||||||
|  |                 # Если лицензия принята, запускаем установку. | ||||||
|  |                 self.run_component_install_command(prefix_name, command, version) | ||||||
|  |  | ||||||
|  |     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) | ||||||
|  |  | ||||||
|  |         self.command_dialog = QDialog(self) | ||||||
|  |         self.command_dialog.setWindowTitle(f"Выполнение: {command} {version}") | ||||||
|  |         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_process = QProcess(self.command_dialog) | ||||||
|  |         self.command_process.setProcessChannelMode(QProcess.MergedChannels) | ||||||
|  |         self.command_process.readyReadStandardOutput.connect(self._handle_command_output) | ||||||
|  |         self.command_process.finished.connect( | ||||||
|  |             lambda exit_code, exit_status: self._handle_component_install_finished( | ||||||
|  |                 prefix_name, exit_code, exit_status | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         env = QProcessEnvironment.systemEnvironment() | ||||||
|  |         env.insert("WINEPREFIX", prefix_path) | ||||||
|  |         self.command_process.setProcessEnvironment(env) | ||||||
|  |  | ||||||
|  |         args = [command, 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 _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) | ||||||
|  |  | ||||||
|     def create_launcher_for_prefix(self): |     def create_launcher_for_prefix(self): | ||||||
|         """ |         """ | ||||||
|         Открывает диалог для создания ярлыка для приложения внутри выбранного префикса. |         Открывает диалог для создания ярлыка для приложения внутри выбранного префикса. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user