Files
Jackify/jackify/backend/services/resolution_service.py
T

227 lines
7.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Resolution Service Module
Centralized service for managing resolution settings across CLI and GUI frontends
"""
import logging
from typing import Optional
from ..handlers.config_handler import ConfigHandler
# Initialize logger
logger = logging.getLogger(__name__)
class ResolutionService:
"""
Centralized service for managing resolution settings
Handles saving, loading, and validation of resolution settings
"""
COMMON_RESOLUTIONS = [
"1280x720",
"1280x800 (Steam Deck)",
"1366x768",
"1440x900",
"1600x900",
"1600x1200",
"1680x1050",
"1920x1080",
"1920x1200",
"2048x1152",
"2560x1080",
"2560x1440",
"2560x1600",
"3440x1440",
"3840x1080",
"3840x1600",
"3840x2160",
"3840x2400",
"5120x1440",
"5120x2160",
"7680x4320",
]
def __init__(self):
"""Initialize the resolution service"""
self.config_handler = ConfigHandler()
logger.debug("ResolutionService initialized")
def save_resolution(self, resolution: str) -> bool:
"""
Save a resolution setting to configuration
Args:
resolution (str): The resolution to save (e.g., '1920x1080')
Returns:
bool: True if saved successfully, False otherwise
"""
try:
# Validate resolution format (basic check)
if not self._validate_resolution_format(resolution):
logger.warning("Invalid resolution format provided")
return False
success = self.config_handler.save_resolution(resolution)
if success:
logger.info(f"Resolution saved successfully: {resolution}")
else:
logger.error("Failed to save resolution")
return success
except Exception as e:
logger.error(f"Error in save_resolution: {e}")
return False
def get_saved_resolution(self) -> Optional[str]:
"""
Retrieve the saved resolution from configuration
Returns:
str: The saved resolution or None if not saved
"""
try:
resolution = self.config_handler.get_saved_resolution()
if resolution:
logger.debug(f"Retrieved saved resolution: {resolution}")
else:
logger.debug("No saved resolution found")
return resolution
except Exception as e:
logger.error(f"Error retrieving resolution: {e}")
return None
def has_saved_resolution(self) -> bool:
"""
Check if a resolution is saved in configuration
Returns:
bool: True if resolution exists, False otherwise
"""
try:
return self.config_handler.has_saved_resolution()
except Exception as e:
logger.error(f"Error checking for saved resolution: {e}")
return False
def clear_saved_resolution(self) -> bool:
"""
Clear the saved resolution from configuration
Returns:
bool: True if cleared successfully, False otherwise
"""
try:
success = self.config_handler.clear_saved_resolution()
if success:
logger.info("Resolution cleared successfully")
else:
logger.error("Failed to clear resolution")
return success
except Exception as e:
logger.error(f"Error clearing resolution: {e}")
return False
def _validate_resolution_format(self, resolution: str) -> bool:
"""
Validate resolution format (e.g., '1920x1080' or '1280x800 (Steam Deck)')
Args:
resolution (str): Resolution string to validate
Returns:
bool: True if valid format, False otherwise
"""
import re
if not resolution or resolution == 'Leave unchanged':
return True # Allow 'Leave unchanged' as valid
# Handle Steam Deck format: '1280x800 (Steam Deck)'
if ' (Steam Deck)' in resolution:
resolution = resolution.replace(' (Steam Deck)', '')
# Check for WxH format (e.g., 1920x1080)
if re.match(r'^[0-9]+x[0-9]+$', resolution):
# Extract width and height
try:
width, height = resolution.split('x')
width_int = int(width)
height_int = int(height)
# Basic sanity checks
if width_int > 0 and height_int > 0 and width_int <= 10000 and height_int <= 10000:
return True
else:
logger.warning(f"Resolution dimensions out of reasonable range: {resolution}")
return False
except ValueError:
logger.warning(f"Invalid resolution format: {resolution}")
return False
else:
logger.warning(f"Resolution does not match WxH format: {resolution}")
return False
def get_resolution_list(self) -> list:
"""
Build the resolution dropdown list: the curated common resolutions,
plus any resolution reported by a connected Qt screen that isn't
already covered, so unusual native resolutions (e.g. ultrawide)
still show up. Falls back to the curated list alone if screen
detection is unavailable (headless, sandboxed launch, etc).
Returns:
list: Resolution labels sorted by pixel dimensions
"""
resolutions = {}
for label in self.COMMON_RESOLUTIONS:
key = label.split(' ')[0]
resolutions[key] = label
try:
from PySide6.QtGui import QGuiApplication
for screen in QGuiApplication.screens():
size = screen.size()
key = f"{size.width()}x{size.height()}"
resolutions.setdefault(key, key)
except Exception as e:
logger.debug(f"Could not query Qt screens for resolution detection: {e}")
def sort_key(label: str):
width, height = label.split(' ')[0].split('x')
return (int(width), int(height))
return sorted(resolutions.values(), key=sort_key)
def get_resolution_index(self, resolution: str, combo_items: list) -> int:
"""
Get the index of a resolution in a combo box list
Args:
resolution (str): Resolution to find
combo_items (list): List of combo box items
Returns:
int: Index of the resolution, or 0 (Leave unchanged) if not found
"""
if not resolution:
return 0 # Default to 'Leave unchanged'
# Handle Steam Deck special case
if resolution == '1280x800' and '1280x800 (Steam Deck)' in combo_items:
return combo_items.index('1280x800 (Steam Deck)')
# Try exact match first
if resolution in combo_items:
return combo_items.index(resolution)
# Try partial match (e.g., '1920x1080' in '1920x1080 (Steam Deck)')
for i, item in enumerate(combo_items):
if resolution in item:
return i
# Default to 'Leave unchanged'
return 0