Enhance monitor selection with advanced debugging and logging
- Improved monitor detection with primary/secondary identification - Enhanced logging to track monitor layout and selection process - Added comprehensive debug scripts for troubleshooting monitor issues - Better error handling in monitor selection logic - Created test utilities for validating monitor selection behavior Debug tools added: - debug_monitor_issue.sh: Comprehensive monitor debugging script - test_enhanced_monitor.py: Tests monitor selection logic with logging - debug_rdp_command.py: Tests RDP command generation Fixes: - Monitor detection now identifies primary monitor correctly - Detailed logging shows exactly which monitors are selected - Better fallback handling for monitor detection errors - Enhanced command logging for easier troubleshooting
This commit is contained in:
46
debug_monitor_issue.sh
Executable file
46
debug_monitor_issue.sh
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=== Monitor Selection Debugging ==="
|
||||||
|
echo "This script helps debug monitor selection issues"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Show current monitor setup
|
||||||
|
echo "1. Current monitor configuration:"
|
||||||
|
xfreerdp /monitor-list
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Test different monitor parameter combinations
|
||||||
|
echo "2. Testing different xfreerdp monitor parameters:"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Testing /monitors:1,2 (should use monitors 1 and 2):"
|
||||||
|
echo "Command: xfreerdp /monitor-list /monitors:1,2"
|
||||||
|
echo "Note: This would be used for '2 Monitors' selection"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Testing /monitors:1,2,0 (should use monitors 1, 2, and 0):"
|
||||||
|
echo "Command: xfreerdp /monitor-list /monitors:1,2,0"
|
||||||
|
echo "Note: This would be used for '3 Monitors' selection"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Testing /multimon (should use all monitors):"
|
||||||
|
echo "Command: xfreerdp /monitor-list /multimon"
|
||||||
|
echo "Note: This would be used for 'All Monitors' selection"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "3. To test with actual RDP connection, you can use:"
|
||||||
|
echo "xfreerdp /v:your-server /u:username /p:password /monitors:1,2"
|
||||||
|
echo
|
||||||
|
echo "If /monitors:1,2 still uses only one monitor, this indicates"
|
||||||
|
echo "a limitation or bug in your FreeRDP version (2.11.5)"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "4. Alternative workarounds to try:"
|
||||||
|
echo "- Use /multimon instead of /monitors: (uses all monitors)"
|
||||||
|
echo "- Use /span to span across monitors"
|
||||||
|
echo "- Update to a newer version of FreeRDP"
|
||||||
|
echo "- Check if your RDP server supports multi-monitor"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "5. Check current RDP client logs:"
|
||||||
|
echo "tail -20 ~/.config/rdp-client/rdp_client.log"
|
||||||
99
debug_rdp_command.py
Normal file
99
debug_rdp_command.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
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:
|
||||||
|
# Clean up the line and split by whitespace/tabs
|
||||||
|
cleaned_line = line.strip().replace('*', '').strip()
|
||||||
|
if '[' in cleaned_line and ']' in cleaned_line and 'x' in cleaned_line and '+' in cleaned_line:
|
||||||
|
# Split by whitespace/tabs
|
||||||
|
parts = cleaned_line.split()
|
||||||
|
if len(parts) >= 3:
|
||||||
|
id_part = parts[0] # [0]
|
||||||
|
pos_part = parts[2] # +3840+0
|
||||||
|
if '[' in id_part and ']' in id_part:
|
||||||
|
monitor_id = int(id_part.strip('[]'))
|
||||||
|
x_pos = int(pos_part.split('+')[1])
|
||||||
|
monitors.append((monitor_id, x_pos))
|
||||||
|
|
||||||
|
# Sort monitors by X position (left to right)
|
||||||
|
monitors.sort(key=lambda x: x[1])
|
||||||
|
|
||||||
|
# Return the leftmost monitors
|
||||||
|
selected = [str(m[0]) for m in monitors[:count]]
|
||||||
|
return ','.join(selected)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: simple sequential selection
|
||||||
|
return ','.join([str(i) for i in range(count)])
|
||||||
|
|
||||||
|
def test_command_generation():
|
||||||
|
print("=== Testing RDP Command Generation ===")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Test connection data
|
||||||
|
conn = {
|
||||||
|
'server': 'test-server',
|
||||||
|
'username': 'testuser',
|
||||||
|
'domain': '',
|
||||||
|
'resolution': 'Full Screen',
|
||||||
|
'color_depth': 32,
|
||||||
|
'sound': 'Yes',
|
||||||
|
'clipboard': 'Yes'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test different monitor configurations
|
||||||
|
test_cases = [
|
||||||
|
("2 Monitors", 2),
|
||||||
|
("3 Monitors", 3),
|
||||||
|
("All Monitors", None)
|
||||||
|
]
|
||||||
|
|
||||||
|
for multimon, count in test_cases:
|
||||||
|
print(f"Testing: {multimon}")
|
||||||
|
|
||||||
|
# Build base command
|
||||||
|
cmd = ["xfreerdp"]
|
||||||
|
cmd.append(f"/v:{conn['server']}")
|
||||||
|
cmd.append(f"/u:{conn['username']}")
|
||||||
|
cmd.append("/p:password")
|
||||||
|
|
||||||
|
if conn.get('domain'):
|
||||||
|
cmd.append(f"/d:{conn['domain']}")
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
cmd.append("/f") # Full screen
|
||||||
|
|
||||||
|
# Color depth
|
||||||
|
cmd.append(f"/bpp:{conn['color_depth']}")
|
||||||
|
|
||||||
|
# Monitor handling
|
||||||
|
if multimon == "2 Monitors":
|
||||||
|
monitor_list = get_best_monitor_selection(2)
|
||||||
|
cmd.append(f"/monitors:{monitor_list}")
|
||||||
|
elif multimon == "3 Monitors":
|
||||||
|
monitor_list = get_best_monitor_selection(3)
|
||||||
|
cmd.append(f"/monitors:{monitor_list}")
|
||||||
|
elif multimon == "All Monitors":
|
||||||
|
cmd.append("/multimon")
|
||||||
|
|
||||||
|
# Other options
|
||||||
|
cmd.append("/sound:sys")
|
||||||
|
cmd.append("+clipboard")
|
||||||
|
|
||||||
|
# Print the command
|
||||||
|
cmd_str = ' '.join(cmd).replace("/p:password", "/p:***")
|
||||||
|
print(f"Command: {cmd_str}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_command_generation()
|
||||||
@@ -253,9 +253,14 @@ class RDPClient:
|
|||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
lines = result.stdout.strip().split('\n')
|
lines = result.stdout.strip().split('\n')
|
||||||
monitors = []
|
monitors = []
|
||||||
|
primary_monitor = None
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Clean up the line and split by whitespace/tabs
|
# Clean up the line and split by whitespace/tabs
|
||||||
cleaned_line = line.strip().replace('*', '').strip()
|
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:
|
if '[' in cleaned_line and ']' in cleaned_line and 'x' in cleaned_line and '+' in cleaned_line:
|
||||||
# Split by whitespace/tabs
|
# Split by whitespace/tabs
|
||||||
parts = cleaned_line.split()
|
parts = cleaned_line.split()
|
||||||
@@ -265,19 +270,31 @@ class RDPClient:
|
|||||||
if '[' in id_part and ']' in id_part:
|
if '[' in id_part and ']' in id_part:
|
||||||
monitor_id = int(id_part.strip('[]'))
|
monitor_id = int(id_part.strip('[]'))
|
||||||
x_pos = int(pos_part.split('+')[1])
|
x_pos = int(pos_part.split('+')[1])
|
||||||
monitors.append((monitor_id, x_pos))
|
monitor_info = (monitor_id, x_pos, is_primary)
|
||||||
|
monitors.append(monitor_info)
|
||||||
|
|
||||||
|
if is_primary:
|
||||||
|
primary_monitor = monitor_id
|
||||||
|
|
||||||
# Sort monitors by X position (left to right)
|
# Sort monitors by X position (left to right)
|
||||||
monitors.sort(key=lambda x: x[1])
|
monitors.sort(key=lambda x: x[1])
|
||||||
|
|
||||||
# Return the leftmost monitors
|
# For debugging, log the monitor layout
|
||||||
|
self.logger.info(f"Monitor layout detected: {[(m[0], m[1], 'primary' if m[2] else 'secondary') for m in monitors]}")
|
||||||
|
|
||||||
|
# Return the leftmost monitors (excluding position and primary flag)
|
||||||
selected = [str(m[0]) for m in monitors[:count]]
|
selected = [str(m[0]) for m in monitors[:count]]
|
||||||
return ','.join(selected)
|
selected_str = ','.join(selected)
|
||||||
except:
|
|
||||||
pass
|
self.logger.info(f"Selected {count} monitors: {selected_str}")
|
||||||
|
return selected_str
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error detecting monitors: {e}")
|
||||||
|
|
||||||
# Fallback: simple sequential selection
|
# Fallback: simple sequential selection
|
||||||
return ','.join([str(i) for i in range(count)])
|
fallback = ','.join([str(i) for i in range(count)])
|
||||||
|
self.logger.warning(f"Using fallback monitor selection: {fallback}")
|
||||||
|
return fallback
|
||||||
|
|
||||||
def _setup_gui(self):
|
def _setup_gui(self):
|
||||||
"""Setup the main GUI"""
|
"""Setup the main GUI"""
|
||||||
@@ -840,17 +857,23 @@ Multi-Monitor Support:
|
|||||||
multimon = conn.get("multimon", "No")
|
multimon = conn.get("multimon", "No")
|
||||||
if multimon == "2 Monitors":
|
if multimon == "2 Monitors":
|
||||||
monitor_list = self._get_best_monitor_selection(2)
|
monitor_list = self._get_best_monitor_selection(2)
|
||||||
|
# Try using /monitors without /multimon for specific monitor selection
|
||||||
cmd.append(f"/monitors:{monitor_list}")
|
cmd.append(f"/monitors:{monitor_list}")
|
||||||
|
self.logger.info(f"Using specific monitors for 2 monitors: {monitor_list}")
|
||||||
elif multimon == "3 Monitors":
|
elif multimon == "3 Monitors":
|
||||||
monitor_list = self._get_best_monitor_selection(3)
|
monitor_list = self._get_best_monitor_selection(3)
|
||||||
cmd.append(f"/monitors:{monitor_list}")
|
cmd.append(f"/monitors:{monitor_list}")
|
||||||
|
self.logger.info(f"Using specific monitors for 3 monitors: {monitor_list}")
|
||||||
elif multimon == "4 Monitors":
|
elif multimon == "4 Monitors":
|
||||||
monitor_list = self._get_best_monitor_selection(4)
|
monitor_list = self._get_best_monitor_selection(4)
|
||||||
cmd.append(f"/monitors:{monitor_list}")
|
cmd.append(f"/monitors:{monitor_list}")
|
||||||
|
self.logger.info(f"Using specific monitors for 4 monitors: {monitor_list}")
|
||||||
elif multimon == "All Monitors":
|
elif multimon == "All Monitors":
|
||||||
cmd.append("/multimon")
|
cmd.append("/multimon")
|
||||||
|
self.logger.info("Using all available monitors")
|
||||||
elif multimon == "Span":
|
elif multimon == "Span":
|
||||||
cmd.append("/span")
|
cmd.append("/span")
|
||||||
|
self.logger.info("Using span mode across monitors")
|
||||||
|
|
||||||
# Sound
|
# Sound
|
||||||
sound = conn.get("sound", "Yes")
|
sound = conn.get("sound", "Yes")
|
||||||
|
|||||||
69
test_enhanced_monitor.py
Normal file
69
test_enhanced_monitor.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Set up logging to see the output
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
primary_monitor = None
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
# Clean up the line and split by whitespace/tabs
|
||||||
|
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:
|
||||||
|
# Split by whitespace/tabs
|
||||||
|
parts = cleaned_line.split()
|
||||||
|
if len(parts) >= 3:
|
||||||
|
id_part = parts[0] # [0]
|
||||||
|
pos_part = parts[2] # +3840+0
|
||||||
|
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)
|
||||||
|
|
||||||
|
if is_primary:
|
||||||
|
primary_monitor = monitor_id
|
||||||
|
|
||||||
|
# Sort monitors by X position (left to right)
|
||||||
|
monitors.sort(key=lambda x: x[1])
|
||||||
|
|
||||||
|
# For debugging, log the monitor layout
|
||||||
|
logger.info(f"Monitor layout detected: {[(m[0], m[1], 'primary' if m[2] else 'secondary') for m in monitors]}")
|
||||||
|
|
||||||
|
# Return the leftmost monitors (excluding position and primary flag)
|
||||||
|
selected = [str(m[0]) for m in monitors[:count]]
|
||||||
|
selected_str = ','.join(selected)
|
||||||
|
|
||||||
|
logger.info(f"Selected {count} monitors: {selected_str}")
|
||||||
|
return selected_str
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error detecting monitors: {e}")
|
||||||
|
|
||||||
|
# Fallback: simple sequential selection
|
||||||
|
fallback = ','.join([str(i) for i in range(count)])
|
||||||
|
logger.warning(f"Using fallback monitor selection: {fallback}")
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=== Testing Enhanced Monitor Selection Logic ===")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for count in [2, 3]:
|
||||||
|
print(f"Testing {count} monitors:")
|
||||||
|
result = _get_best_monitor_selection(count)
|
||||||
|
print(f"Result: {result}")
|
||||||
|
print()
|
||||||
Reference in New Issue
Block a user