Fix fullscreen resolution for specific monitor selection

Problem: Previous fix removed /f but didn't provide proper resolution,
causing RDP to open in very low resolution window instead of fullscreen.

Solution: Calculate combined resolution of selected monitors
- 2 monitors (1,2): /size:3840x1080 /monitors:1,2
- 3 monitors (1,2,0): /size:5760x1080 /monitors:1,2,0

Changes:
- Add _get_monitors_combined_resolution() method
- Calculate total width from leftmost to rightmost selected monitor
- Use calculated resolution with /size: parameter
- Eliminate duplicate monitor selection calls
- Enhanced logging for resolution calculation

Expected result:
- '2 Monitors' opens fullscreen across 2 monitors at proper resolution
- '3 Monitors' opens fullscreen across 3 monitors at proper resolution
- Maintains /monitors: parameter for correct monitor targeting
This commit is contained in:
root
2025-09-18 11:05:10 +02:00
parent e727430a4f
commit 0ff0ef1cb1
2 changed files with 166 additions and 16 deletions

View File

@@ -245,7 +245,50 @@ class RDPClient:
return options
def _get_best_monitor_selection(self, count):
def _get_monitors_combined_resolution(self, monitor_list):
"""Get the combined resolution for selected monitors"""
try:
result = subprocess.run(['xfreerdp', '/monitor-list'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
lines = result.stdout.strip().split('\n')
selected_monitors = [int(x.strip()) for x in monitor_list.split(',')]
min_x = float('inf')
max_x = 0
max_y = 0
for line in lines:
cleaned_line = line.strip().replace('*', '').strip()
if '[' in cleaned_line and ']' in cleaned_line:
parts = cleaned_line.split()
if len(parts) >= 3:
id_part = parts[0]
res_part = parts[1] # 1920x1080
pos_part = parts[2] # +3840+0
if '[' in id_part and ']' in id_part:
monitor_id = int(id_part.strip('[]'))
if monitor_id in selected_monitors:
# Parse resolution
width, height = map(int, res_part.split('x'))
# Parse position
pos_coords = pos_part.split('+')[1:] # ['3840', '0']
x_pos = int(pos_coords[0])
min_x = min(min_x, x_pos)
max_x = max(max_x, x_pos + width)
max_y = max(max_y, height)
if min_x != float('inf'):
total_width = max_x - min_x
total_height = max_y
return f"{total_width}x{total_height}"
except Exception as e:
self.logger.error(f"Error calculating combined resolution: {e}")
# Fallback to a reasonable default
return "3840x1080" # Assume dual 1920x1080 monitors
"""Get the best monitor selection based on layout"""
try:
result = subprocess.run(['xfreerdp', '/monitor-list'],
@@ -846,13 +889,25 @@ Multi-Monitor Support:
multimon = conn.get("multimon", "No")
use_specific_monitors = multimon in ["2 Monitors", "3 Monitors", "4 Monitors"]
# Resolution - avoid /f with specific monitor selection due to conflicts
# Get monitor list once if needed
monitor_list = None
if use_specific_monitors:
if multimon == "2 Monitors":
monitor_list = self._get_best_monitor_selection(2)
elif multimon == "3 Monitors":
monitor_list = self._get_best_monitor_selection(3)
elif multimon == "4 Monitors":
monitor_list = self._get_best_monitor_selection(4)
# Resolution - calculate proper size for specific monitor selection
resolution = conn.get("resolution", "1920x1080")
if resolution == "Full Screen" and not use_specific_monitors:
cmd.append("/f")
elif resolution == "Full Screen" and use_specific_monitors:
# Don't use /f with specific monitors - let /monitors: determine the layout
self.logger.info("Skipping /f (fullscreen) due to specific monitor selection")
# Calculate combined resolution for selected monitors
combined_res = self._get_monitors_combined_resolution(monitor_list)
cmd.append(f"/size:{combined_res}")
self.logger.info(f"Using calculated resolution {combined_res} for {multimon}: {monitor_list}")
else:
cmd.append(f"/size:{resolution}")
@@ -860,19 +915,10 @@ Multi-Monitor Support:
color_depth = conn.get("color_depth", 32)
cmd.append(f"/bpp:{color_depth}")
# Multiple monitors - use /monitors: without /multimon for specific selection
if multimon == "2 Monitors":
monitor_list = self._get_best_monitor_selection(2)
# Multiple monitors - use /monitors: for specific selection
if use_specific_monitors:
cmd.append(f"/monitors:{monitor_list}")
self.logger.info(f"Using specific monitors for 2 monitors: {monitor_list}")
elif multimon == "3 Monitors":
monitor_list = self._get_best_monitor_selection(3)
cmd.append(f"/monitors:{monitor_list}")
self.logger.info(f"Using specific monitors for 3 monitors: {monitor_list}")
elif multimon == "4 Monitors":
monitor_list = self._get_best_monitor_selection(4)
cmd.append(f"/monitors:{monitor_list}")
self.logger.info(f"Using specific monitors for 4 monitors: {monitor_list}")
self.logger.info(f"Using specific monitors for {multimon}: {monitor_list}")
elif multimon == "All Monitors":
cmd.append("/multimon")
self.logger.info("Using all available monitors")

104
test_combined_resolution.py Normal file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
import subprocess
def get_monitors_combined_resolution(monitor_list):
"""Get the combined resolution for selected monitors"""
try:
result = subprocess.run(['xfreerdp', '/monitor-list'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
lines = result.stdout.strip().split('\n')
selected_monitors = [int(x.strip()) for x in monitor_list.split(',')]
min_x = float('inf')
max_x = 0
max_y = 0
print(f"Looking for monitors: {selected_monitors}")
for line in lines:
cleaned_line = line.strip().replace('*', '').strip()
if '[' in cleaned_line and ']' in cleaned_line:
parts = cleaned_line.split()
if len(parts) >= 3:
id_part = parts[0]
res_part = parts[1] # 1920x1080
pos_part = parts[2] # +3840+0
if '[' in id_part and ']' in id_part:
monitor_id = int(id_part.strip('[]'))
if monitor_id in selected_monitors:
# Parse resolution
width, height = map(int, res_part.split('x'))
# Parse position
pos_coords = pos_part.split('+')[1:] # ['3840', '0']
x_pos = int(pos_coords[0])
print(f" Monitor {monitor_id}: {width}x{height} at +{x_pos}+0")
min_x = min(min_x, x_pos)
max_x = max(max_x, x_pos + width)
max_y = max(max_y, height)
if min_x != float('inf'):
total_width = max_x - min_x
total_height = max_y
print(f" Combined: {total_width}x{total_height} (from x={min_x} to x={max_x})")
return f"{total_width}x{total_height}"
except Exception as e:
print(f"Error: {e}")
# Fallback to a reasonable default
return "3840x1080" # Assume dual 1920x1080 monitors
def get_best_monitor_selection(count):
"""Get the best monitor selection based on layout"""
try:
result = subprocess.run(['xfreerdp', '/monitor-list'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
lines = result.stdout.strip().split('\n')
monitors = []
for line in lines:
cleaned_line = line.strip()
is_primary = cleaned_line.startswith('*')
cleaned_line = cleaned_line.replace('*', '').strip()
if '[' in cleaned_line and ']' in cleaned_line and 'x' in cleaned_line and '+' in cleaned_line:
parts = cleaned_line.split()
if len(parts) >= 3:
id_part = parts[0]
pos_part = parts[2]
if '[' in id_part and ']' in id_part:
monitor_id = int(id_part.strip('[]'))
x_pos = int(pos_part.split('+')[1])
monitor_info = (monitor_id, x_pos, is_primary)
monitors.append(monitor_info)
monitors.sort(key=lambda x: x[1])
selected = [str(m[0]) for m in monitors[:count]]
return ','.join(selected)
except:
pass
return ','.join([str(i) for i in range(count)])
if __name__ == "__main__":
print("=== Testing Combined Resolution Calculation ===")
print()
print("Current monitor layout:")
subprocess.run(['xfreerdp', '/monitor-list'])
print()
for count in [2, 3]:
monitor_list = get_best_monitor_selection(count)
print(f"Testing {count} monitors (selection: {monitor_list}):")
combined_res = get_monitors_combined_resolution(monitor_list)
print(f"Result: {combined_res}")
print()
# Show what the command would be
print(f"Command would be: xfreerdp /size:{combined_res} /monitors:{monitor_list}")
print("-" * 50)