Add Python RDP Client with multi-monitor support and enhanced features
- Complete rewrite of bash RDP script in Python with tkinter GUI - Multi-monitor support with intelligent monitor selection - Encrypted credential storage using Fernet encryption - Connection profiles with advanced configuration options - Fixed authentication issues (STATUS_ACCOUNT_RESTRICTION) - Enhanced monitor detection and selection logic - Professional tabbed interface with General/Advanced/Performance tabs - Install script and documentation included
This commit is contained in:
221
README_rdp_client.md
Normal file
221
README_rdp_client.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# RDP Client - Python Version
|
||||
|
||||
A modern, professional RDP client with a user-friendly GUI, developed as a Python replacement for the shell-based `rdp.sh` script.
|
||||
|
||||
## Features
|
||||
|
||||
### ✨ **Enhanced GUI Interface**
|
||||
- Modern tkinter-based interface with professional styling
|
||||
- Tabbed connection dialog for organized settings
|
||||
- Real-time connection details display
|
||||
- Responsive design with proper scaling
|
||||
|
||||
### 🔐 **Secure Credential Management**
|
||||
- Encrypted password storage using industry-standard cryptography
|
||||
- Per-connection credential saving
|
||||
- Secure key derivation based on user and hostname
|
||||
- Easy credential management and clearing
|
||||
|
||||
### 📊 **Connection History & Recent Connections**
|
||||
- Automatic tracking of connection usage
|
||||
- Recent connections list with usage counts
|
||||
- Quick access to frequently used connections
|
||||
- Connection history persistence
|
||||
|
||||
### 🔄 **Import/Export Functionality**
|
||||
- Export connections to JSON files for backup
|
||||
- Import connections from JSON files
|
||||
- Merge or replace strategies for importing
|
||||
- Include/exclude credentials in exports
|
||||
- Timestamped export files
|
||||
|
||||
### 🧪 **Connection Testing**
|
||||
- Test server reachability before connecting
|
||||
- RDP service availability checking
|
||||
- Detailed test results with troubleshooting hints
|
||||
- Network connectivity validation
|
||||
|
||||
### ⌨️ **Keyboard Shortcuts**
|
||||
- **Ctrl+N** - Create new connection
|
||||
- **Ctrl+O** - Import connections
|
||||
- **Ctrl+S** - Export connections
|
||||
- **Ctrl+T** - Test selected connection
|
||||
- **F5** - Refresh connections list
|
||||
- **Enter** - Connect to selected connection
|
||||
- **Delete** - Delete selected connection
|
||||
- **F2** - Edit selected connection
|
||||
- **F1** - Show help dialog
|
||||
- **Tab/Shift+Tab** - Navigate between connection lists
|
||||
|
||||
### 🔧 **Advanced RDP Features**
|
||||
- Full resolution control (including full screen)
|
||||
- Multiple monitor support (extend/span)
|
||||
- Audio redirection (local/remote/off)
|
||||
- Microphone redirection
|
||||
- Clipboard sharing
|
||||
- Drive sharing (home directory)
|
||||
- Printer redirection
|
||||
- USB device redirection
|
||||
- COM port redirection
|
||||
- Performance optimizations (compression, wallpaper, themes, fonts)
|
||||
|
||||
### 📝 **Comprehensive Logging**
|
||||
- Detailed connection logs
|
||||
- Error tracking and diagnosis
|
||||
- User action logging
|
||||
- Automatic log rotation
|
||||
|
||||
### 🚨 **Enhanced Error Handling**
|
||||
- User-friendly error messages
|
||||
- Detailed troubleshooting information
|
||||
- Network connectivity validation
|
||||
- RDP-specific error interpretation
|
||||
- Connection timeout handling
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.8 or higher
|
||||
- `xfreerdp` (FreeRDP client)
|
||||
- tkinter (usually included with Python)
|
||||
|
||||
### Install FreeRDP
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install freerdp2-x11
|
||||
|
||||
# Fedora/RHEL
|
||||
sudo dnf install freerdp
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S freerdp
|
||||
```
|
||||
|
||||
### Install Python Dependencies
|
||||
The script will automatically create a virtual environment and install dependencies:
|
||||
|
||||
```bash
|
||||
# The cryptography package is automatically installed when first run
|
||||
pip install cryptography # or let the script handle it
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Quick Start
|
||||
```bash
|
||||
# Make executable and run
|
||||
chmod +x rdp_client.sh
|
||||
./rdp_client.sh
|
||||
```
|
||||
|
||||
### Direct Python Execution
|
||||
```bash
|
||||
# Using the virtual environment
|
||||
.venv/bin/python rdp_client.py
|
||||
|
||||
# Or if you have dependencies installed globally
|
||||
python3 rdp_client.py
|
||||
```
|
||||
|
||||
### Creating Your First Connection
|
||||
1. Click "New Connection" or press **Ctrl+N**
|
||||
2. Fill in the connection details:
|
||||
- **Connection Name**: A friendly name for this connection
|
||||
- **Server/IP**: The RDP server address
|
||||
- **Username**: Your username
|
||||
- **Password**: Your password (encrypted and stored securely)
|
||||
- **Domain**: Optional domain name
|
||||
3. Configure advanced settings in the tabs:
|
||||
- **Advanced**: Resolution, monitors, audio, clipboard, drive sharing
|
||||
- **Performance**: Compression, visual effects, hardware redirection
|
||||
4. Click "Save" to store the connection
|
||||
|
||||
### Connecting
|
||||
- **Double-click** any connection to connect immediately
|
||||
- **Select** and click "Connect"
|
||||
- **Select** and press **Enter**
|
||||
- Use **Recent Connections** for quick access to frequently used servers
|
||||
|
||||
### Testing Connections
|
||||
- Select a connection and click "Test Connection" or press **Ctrl+T**
|
||||
- The test will verify:
|
||||
- Network reachability
|
||||
- RDP service availability
|
||||
- Port accessibility
|
||||
- Detailed results help troubleshoot connection issues
|
||||
|
||||
## File Locations
|
||||
|
||||
All configuration files are stored in `~/.config/rdp-client/`:
|
||||
|
||||
- `connections.json` - Saved connection profiles
|
||||
- `credentials.json` - Encrypted credentials
|
||||
- `history.json` - Connection history
|
||||
- `rdp_client.log` - Application logs
|
||||
|
||||
## Migration from rdp.sh
|
||||
|
||||
The Python version can coexist with the original `rdp.sh` script. While they use different storage formats, you can:
|
||||
|
||||
1. Export connections from the old script (if supported)
|
||||
2. Manually recreate important connections in the new GUI
|
||||
3. Use the import functionality to bulk-add connections
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Passwords are encrypted using Fernet (AES 128) with PBKDF2 key derivation
|
||||
- Encryption key is derived from username@hostname combination
|
||||
- Credentials are only decryptable by the same user on the same machine
|
||||
- Log files do not contain passwords (shown as ***)
|
||||
- Export files can optionally include encrypted credentials
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**"xfreerdp not found"**
|
||||
- Install FreeRDP: `sudo apt install freerdp2-x11`
|
||||
|
||||
**"Connection failed - Authentication error"**
|
||||
- Verify username, password, and domain
|
||||
- Check if account is locked or disabled
|
||||
- Use "Test Connection" to verify server accessibility
|
||||
|
||||
**"Server unreachable"**
|
||||
- Check server address and network connectivity
|
||||
- Verify firewall settings
|
||||
- Ensure RDP is enabled on the target server
|
||||
|
||||
**"GUI doesn't start"**
|
||||
- Ensure tkinter is installed: `sudo apt install python3-tk`
|
||||
- Check Python version: `python3 --version` (3.8+ required)
|
||||
|
||||
### Logs and Debugging
|
||||
|
||||
Check the log file for detailed error information:
|
||||
```bash
|
||||
tail -f ~/.config/rdp-client/rdp_client.log
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
The RDP client is designed to be modular and extensible. Key areas for enhancement:
|
||||
|
||||
- Additional RDP client backends (rdesktop, xrdp)
|
||||
- LDAP/Active Directory integration
|
||||
- Connection profiles with templates
|
||||
- Multi-language support
|
||||
- Plugin system for custom authentication
|
||||
|
||||
## License
|
||||
|
||||
This project follows the same license as the original rdp.sh script.
|
||||
|
||||
## Version History
|
||||
|
||||
- **v2.0** - Complete Python rewrite with GUI
|
||||
- **v1.x** - Original bash/zenity implementation
|
||||
|
||||
---
|
||||
|
||||
*For more information about the original shell script, see `rdp.sh` in the same directory.*
|
||||
64
install_rdp_client.sh
Executable file
64
install_rdp_client.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
# RDP Client Installation Script
|
||||
# This script installs the RDP client system-wide
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
INSTALL_DIR="/usr/local/bin"
|
||||
DESKTOP_DIR="/usr/share/applications"
|
||||
|
||||
# Check if running as root for system installation
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
echo "Installing RDP Client system-wide..."
|
||||
|
||||
# Copy the Python script
|
||||
cp "$SCRIPT_DIR/rdp_client.py" "$INSTALL_DIR/rdp-client"
|
||||
chmod +x "$INSTALL_DIR/rdp-client"
|
||||
|
||||
# Create desktop entry
|
||||
cat > "$DESKTOP_DIR/rdp-client.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Name=RDP Client
|
||||
Comment=Professional RDP connection manager
|
||||
Exec=/usr/local/bin/rdp-client
|
||||
Icon=network-workgroup
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;RemoteAccess;
|
||||
EOF
|
||||
|
||||
echo "RDP Client installed successfully!"
|
||||
echo "You can now run it with: rdp-client"
|
||||
echo "Or find it in your applications menu."
|
||||
else
|
||||
echo "Installing RDP Client for current user..."
|
||||
|
||||
# Create user bin directory
|
||||
USER_BIN="$HOME/.local/bin"
|
||||
mkdir -p "$USER_BIN"
|
||||
|
||||
# Copy the Python script
|
||||
cp "$SCRIPT_DIR/rdp_client.py" "$USER_BIN/rdp-client"
|
||||
chmod +x "$USER_BIN/rdp-client"
|
||||
|
||||
# Create user desktop entry
|
||||
USER_DESKTOP="$HOME/.local/share/applications"
|
||||
mkdir -p "$USER_DESKTOP"
|
||||
|
||||
cat > "$USER_DESKTOP/rdp-client.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Name=RDP Client
|
||||
Comment=Professional RDP connection manager
|
||||
Exec=$USER_BIN/rdp-client
|
||||
Icon=network-workgroup
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;RemoteAccess;
|
||||
EOF
|
||||
|
||||
echo "RDP Client installed for current user!"
|
||||
echo "You can now run it with: rdp-client"
|
||||
echo "Make sure $USER_BIN is in your PATH:"
|
||||
echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc"
|
||||
echo " source ~/.bashrc"
|
||||
fi
|
||||
184
rdp_client.py
184
rdp_client.py
@@ -66,6 +66,9 @@ class RDPClient:
|
||||
self.credentials = self._load_credentials()
|
||||
self.history = self._load_history()
|
||||
|
||||
# Migrate legacy multimon settings
|
||||
self._migrate_multimon_settings()
|
||||
|
||||
# Add existing connections to history if history is empty (first run or migration)
|
||||
if not self.history and self.connections:
|
||||
for conn_name in self.connections.keys():
|
||||
@@ -201,6 +204,81 @@ class RDPClient:
|
||||
return entry.get('count', 0)
|
||||
return 0
|
||||
|
||||
def _migrate_multimon_settings(self):
|
||||
"""Migrate legacy 'Yes' multimon settings to new specific monitor options"""
|
||||
changed = False
|
||||
for conn_name, conn_data in self.connections.items():
|
||||
if conn_data.get("multimon") == "Yes":
|
||||
# Default to 2 monitors for legacy "Yes" settings
|
||||
conn_data["multimon"] = "2 Monitors"
|
||||
changed = True
|
||||
self.logger.info(f"Migrated multimon setting for {conn_name} from 'Yes' to '2 Monitors'")
|
||||
|
||||
if changed:
|
||||
self._save_connections()
|
||||
self.logger.info("Completed multimon settings migration")
|
||||
|
||||
def _get_available_monitors_count(self):
|
||||
"""Get the number of available monitors"""
|
||||
try:
|
||||
result = subprocess.run(['xrandr', '--listmonitors'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
lines = result.stdout.strip().split('\n')
|
||||
if lines and lines[0].startswith('Monitors:'):
|
||||
return int(lines[0].split(':')[1].strip())
|
||||
except:
|
||||
pass
|
||||
return 1 # Default to 1 monitor if detection fails
|
||||
|
||||
def _get_multimon_options(self):
|
||||
"""Get available multi-monitor options based on system"""
|
||||
monitor_count = self._get_available_monitors_count()
|
||||
options = ["No"]
|
||||
|
||||
# Add options for available monitors
|
||||
for i in range(2, min(monitor_count + 1, 5)): # Up to 4 monitors
|
||||
options.append(f"{i} Monitors")
|
||||
|
||||
if monitor_count > 1:
|
||||
options.extend(["All Monitors", "Span"])
|
||||
|
||||
return options
|
||||
|
||||
def _get_best_monitor_selection(self, 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 _setup_gui(self):
|
||||
"""Setup the main GUI"""
|
||||
# Configure style
|
||||
@@ -459,7 +537,15 @@ Tips:
|
||||
• Recent connections show usage count (e.g., "Server (5x)")
|
||||
• Double-click any connection to connect quickly
|
||||
• Use Test Connection to verify server availability
|
||||
• Import/Export to backup or share connection profiles"""
|
||||
• Import/Export to backup or share connection profiles
|
||||
|
||||
Multi-Monitor Support:
|
||||
• "2 Monitors" - Use first 2 monitors (0,1)
|
||||
• "3 Monitors" - Use first 3 monitors (0,1,2)
|
||||
• "4 Monitors" - Use first 4 monitors (0,1,2,3)
|
||||
• "All Monitors" - Use all available monitors
|
||||
• "Span" - Span desktop across monitors
|
||||
• "No" - Single monitor only"""
|
||||
|
||||
messagebox.showinfo("Keyboard Shortcuts", help_text)
|
||||
|
||||
@@ -535,7 +621,7 @@ Tips:
|
||||
|
||||
def _new_connection(self):
|
||||
"""Create a new connection"""
|
||||
dialog = ConnectionDialog(self.root, "New Connection")
|
||||
dialog = ConnectionDialog(self.root, "New Connection", rdp_client=self)
|
||||
if dialog.result:
|
||||
name = dialog.result["name"]
|
||||
if name in self.connections:
|
||||
@@ -594,7 +680,7 @@ Tips:
|
||||
conn["name"] = name
|
||||
conn["password"] = password
|
||||
|
||||
dialog = ConnectionDialog(self.root, f"Edit Connection: {name}", conn)
|
||||
dialog = ConnectionDialog(self.root, f"Edit Connection: {name}", conn, rdp_client=self)
|
||||
if dialog.result:
|
||||
new_name = dialog.result["name"]
|
||||
|
||||
@@ -752,7 +838,16 @@ Tips:
|
||||
|
||||
# Multiple monitors
|
||||
multimon = conn.get("multimon", "No")
|
||||
if multimon == "Yes":
|
||||
if multimon == "2 Monitors":
|
||||
monitor_list = self._get_best_monitor_selection(2)
|
||||
cmd.append(f"/monitors:{monitor_list}")
|
||||
elif multimon == "3 Monitors":
|
||||
monitor_list = self._get_best_monitor_selection(3)
|
||||
cmd.append(f"/monitors:{monitor_list}")
|
||||
elif multimon == "4 Monitors":
|
||||
monitor_list = self._get_best_monitor_selection(4)
|
||||
cmd.append(f"/monitors:{monitor_list}")
|
||||
elif multimon == "All Monitors":
|
||||
cmd.append("/multimon")
|
||||
elif multimon == "Span":
|
||||
cmd.append("/span")
|
||||
@@ -779,17 +874,53 @@ Tips:
|
||||
# Performance options
|
||||
if conn.get("compression", "Yes") == "Yes":
|
||||
cmd.append("/compression")
|
||||
else:
|
||||
cmd.append("-compression")
|
||||
|
||||
# Compression level
|
||||
comp_level = conn.get("compression_level", "1 (Medium)")
|
||||
if "0" in comp_level:
|
||||
cmd.append("/compression-level:0")
|
||||
elif "1" in comp_level:
|
||||
cmd.append("/compression-level:1")
|
||||
elif "2" in comp_level:
|
||||
cmd.append("/compression-level:2")
|
||||
|
||||
# Bitmap cache
|
||||
if conn.get("bitmap_cache", "Yes") == "Yes":
|
||||
cmd.append("+bitmap-cache")
|
||||
else:
|
||||
cmd.append("-bitmap-cache")
|
||||
|
||||
# Offscreen cache
|
||||
if conn.get("offscreen_cache", "Yes") == "Yes":
|
||||
cmd.append("+offscreen-cache")
|
||||
else:
|
||||
cmd.append("-offscreen-cache")
|
||||
|
||||
# Font smoothing
|
||||
if conn.get("fonts", "Yes") == "No":
|
||||
cmd.append("-fonts")
|
||||
|
||||
# Desktop composition (Aero)
|
||||
if conn.get("aero", "No") == "Yes":
|
||||
cmd.append("+aero")
|
||||
else:
|
||||
cmd.append("-aero")
|
||||
|
||||
# Wallpaper
|
||||
if conn.get("wallpaper", "No") == "No":
|
||||
cmd.append("-wallpaper")
|
||||
|
||||
# Themes
|
||||
if conn.get("themes", "Yes") == "No":
|
||||
cmd.append("-themes")
|
||||
|
||||
# Other options
|
||||
# Menu animations
|
||||
if conn.get("menu_anims", "No") == "No":
|
||||
cmd.append("-menu-anims")
|
||||
|
||||
# Advanced options
|
||||
if conn.get("printer", "No") == "Yes":
|
||||
cmd.append("/printer")
|
||||
|
||||
@@ -799,6 +930,10 @@ Tips:
|
||||
if conn.get("usb", "No") == "Yes":
|
||||
cmd.append("/usb")
|
||||
|
||||
# Network options
|
||||
if conn.get("network_auto_detect", "Yes") == "No":
|
||||
cmd.append("-network-auto-detect")
|
||||
|
||||
# Execute
|
||||
cmd_str = ' '.join(cmd).replace(f"/p:{password}", "/p:***") # Hide password in logs
|
||||
self.logger.info(f"Executing RDP command: {cmd_str}")
|
||||
@@ -1111,8 +1246,9 @@ Tips:
|
||||
|
||||
|
||||
class ConnectionDialog:
|
||||
def __init__(self, parent, title, initial_data=None):
|
||||
def __init__(self, parent, title, initial_data=None, rdp_client=None):
|
||||
self.result = None
|
||||
self.rdp_client = rdp_client
|
||||
|
||||
# Create dialog
|
||||
self.dialog = tk.Toplevel(parent)
|
||||
@@ -1151,11 +1287,19 @@ class ConnectionDialog:
|
||||
advanced_frame = ttk.Frame(notebook)
|
||||
notebook.add(advanced_frame, text="Advanced")
|
||||
|
||||
# Advanced tab description
|
||||
adv_desc = ttk.Label(advanced_frame, text="Advanced connection and device sharing options",
|
||||
font=("TkDefaultFont", 9, "italic"))
|
||||
adv_desc.grid(row=0, column=0, columnspan=2, pady=(10, 15), padx=10, sticky=tk.W)
|
||||
|
||||
# Performance tab
|
||||
performance_frame = ttk.Frame(notebook)
|
||||
notebook.add(performance_frame, text="Performance")
|
||||
|
||||
# Store field variables
|
||||
# Performance tab description
|
||||
perf_desc = ttk.Label(performance_frame, text="Performance optimization and visual quality settings",
|
||||
font=("TkDefaultFont", 9, "italic"))
|
||||
perf_desc.grid(row=0, column=0, columnspan=2, pady=(10, 15), padx=10, sticky=tk.W) # Store field variables
|
||||
self.fields = {}
|
||||
|
||||
# Basic fields
|
||||
@@ -1181,41 +1325,51 @@ class ConnectionDialog:
|
||||
self.fields[field] = var
|
||||
|
||||
# Advanced fields
|
||||
multimon_options = self.rdp_client._get_multimon_options() if self.rdp_client else ["No", "2 Monitors", "3 Monitors", "All Monitors", "Span"]
|
||||
advanced_fields = [
|
||||
("Resolution:", "resolution", "combo", ["1920x1080", "2560x1440", "1366x768", "1280x1024", "1024x768", "Full Screen"]),
|
||||
("Color Depth:", "color_depth", "combo", ["32", "24", "16", "15"]),
|
||||
("Multiple Monitors:", "multimon", "combo", ["No", "Yes", "Span"]),
|
||||
("Multiple Monitors:", "multimon", "combo", multimon_options),
|
||||
("Sound:", "sound", "combo", ["Yes", "No", "Remote"]),
|
||||
("Microphone:", "microphone", "combo", ["No", "Yes"]),
|
||||
("Clipboard:", "clipboard", "combo", ["Yes", "No"]),
|
||||
("Share Home Drive:", "drives", "combo", ["No", "Yes"]),
|
||||
("Printer Sharing:", "printer", "combo", ["No", "Yes"]),
|
||||
("COM Ports:", "com_ports", "combo", ["No", "Yes"]),
|
||||
("USB Devices:", "usb", "combo", ["No", "Yes"]),
|
||||
("Gateway Mode:", "gateway", "combo", ["Auto", "RPC", "HTTP"]),
|
||||
("Network Detection:", "network_auto_detect", "combo", ["Yes", "No"]),
|
||||
]
|
||||
|
||||
for i, (label, field, widget_type, values) in enumerate(advanced_fields):
|
||||
ttk.Label(advanced_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5, padx=(10, 5))
|
||||
row = i + 1 # Offset by 1 for description label
|
||||
ttk.Label(advanced_frame, text=label).grid(row=row, column=0, sticky=tk.W, pady=5, padx=(10, 5))
|
||||
|
||||
var = tk.StringVar(value=self.initial_data.get(field, values[0]))
|
||||
widget = ttk.Combobox(advanced_frame, textvariable=var, values=values, width=37, state="readonly")
|
||||
widget.grid(row=i, column=1, sticky=tk.W, pady=5, padx=(5, 10))
|
||||
widget.grid(row=row, column=1, sticky=tk.W, pady=5, padx=(5, 10))
|
||||
self.fields[field] = var
|
||||
|
||||
# Performance fields
|
||||
performance_fields = [
|
||||
("Compression:", "compression", "combo", ["Yes", "No"]),
|
||||
("Compression Level:", "compression_level", "combo", ["0 (None)", "1 (Medium)", "2 (High)"]),
|
||||
("Bitmap Cache:", "bitmap_cache", "combo", ["Yes", "No"]),
|
||||
("Offscreen Cache:", "offscreen_cache", "combo", ["Yes", "No"]),
|
||||
("Font Smoothing:", "fonts", "combo", ["Yes", "No"]),
|
||||
("Desktop Composition:", "aero", "combo", ["No", "Yes"]),
|
||||
("Wallpaper:", "wallpaper", "combo", ["No", "Yes"]),
|
||||
("Themes:", "themes", "combo", ["Yes", "No"]),
|
||||
("Printer Sharing:", "printer", "combo", ["No", "Yes"]),
|
||||
("COM Ports:", "com_ports", "combo", ["No", "Yes"]),
|
||||
("USB Devices:", "usb", "combo", ["No", "Yes"]),
|
||||
("Menu Animations:", "menu_anims", "combo", ["No", "Yes"]),
|
||||
]
|
||||
|
||||
for i, (label, field, widget_type, values) in enumerate(performance_fields):
|
||||
ttk.Label(performance_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5, padx=(10, 5))
|
||||
row = i + 1 # Offset by 1 for description label
|
||||
ttk.Label(performance_frame, text=label).grid(row=row, column=0, sticky=tk.W, pady=5, padx=(10, 5))
|
||||
|
||||
var = tk.StringVar(value=self.initial_data.get(field, values[0]))
|
||||
widget = ttk.Combobox(performance_frame, textvariable=var, values=values, width=37, state="readonly")
|
||||
widget.grid(row=i, column=1, sticky=tk.W, pady=5, padx=(5, 10))
|
||||
widget.grid(row=row, column=1, sticky=tk.W, pady=5, padx=(5, 10))
|
||||
self.fields[field] = var
|
||||
|
||||
# Buttons
|
||||
|
||||
25
rdp_client.sh
Executable file
25
rdp_client.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# RDP Client Launcher Script
|
||||
# This script launches the Python RDP client with the correct virtual environment
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PYTHON_VENV="$SCRIPT_DIR/.venv/bin/python"
|
||||
PYTHON_SCRIPT="$SCRIPT_DIR/rdp_client.py"
|
||||
|
||||
# Check if virtual environment exists
|
||||
if [[ ! -f "$PYTHON_VENV" ]]; then
|
||||
echo "Error: Python virtual environment not found at $PYTHON_VENV"
|
||||
echo "Please run: python3 -m venv .venv && .venv/bin/pip install cryptography"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the Python script exists
|
||||
if [[ ! -f "$PYTHON_SCRIPT" ]]; then
|
||||
echo "Error: RDP client script not found at $PYTHON_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Launch the RDP client
|
||||
echo "Starting RDP Client..."
|
||||
exec "$PYTHON_VENV" "$PYTHON_SCRIPT" "$@"
|
||||
Reference in New Issue
Block a user