rdp-client/files/mouse-settings.py

198 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""
Mauseinstellungen für RDP Thin Client
Verwendet xinput direkt für zuverlässige Konfiguration
"""
import tkinter as tk
from tkinter import ttk
import subprocess
import re
import configparser
from pathlib import Path
class MouseSettings:
CONFIG_DIR = Path.home() / ".config" / "mouse-settings"
CONFIG_FILE = CONFIG_DIR / "settings.ini"
def __init__(self, root):
self.root = root
self.root.title("Mauseinstellungen")
self.root.geometry("500x300")
# Erstelle Config-Verzeichnis
self.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
self.mouse_ids = self.get_mouse_ids()
if not self.mouse_ids:
tk.Label(root, text="Keine Maus gefunden!", font=('Arial', 14)).pack(pady=50)
return
self.create_widgets()
self.load_saved_settings()
def get_mouse_ids(self):
"""Finde alle Maus-Device-IDs (alle Pointer mit 'mouse' im Namen)"""
try:
result = subprocess.run(['xinput', 'list'], capture_output=True, text=True)
mouse_ids = []
# Suche nach allen Devices mit "mouse" im Namen (nicht Virtual core)
for line in result.stdout.split('\n'):
if 'pointer' in line.lower() and 'virtual core' not in line.lower() and 'mouse' in line.lower():
match = re.search(r'id=(\d+)', line)
if match:
mouse_ids.append(match.group(1))
return mouse_ids if mouse_ids else None
except:
return None
def load_saved_settings(self):
"""Lade gespeicherte Mauseinstellungen"""
if self.CONFIG_FILE.exists():
try:
config = configparser.ConfigParser()
config.read(self.CONFIG_FILE)
speed_percent = config.getint('Mouse', 'speed', fallback=50)
self.speed_var.set(speed_percent)
self.apply_speed()
return
except:
pass
# Fallback: Lade aktuelle System-Einstellungen
self.speed_var.set(50)
def save_settings(self):
"""Speichere Mauseinstellungen persistent"""
try:
config = configparser.ConfigParser()
config['Mouse'] = {
'speed': str(self.speed_var.get())
}
with open(self.CONFIG_FILE, 'w') as f:
config.write(f)
except Exception as e:
print(f"Fehler beim Speichern: {e}")
def create_widgets(self):
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# Geschwindigkeit
ttk.Label(main_frame, text="Mausgeschwindigkeit:", font=('', 12, 'bold')).pack(anchor=tk.W, pady=(0,10))
speed_frame = ttk.Frame(main_frame)
speed_frame.pack(fill=tk.X, pady=10)
ttk.Label(speed_frame, text="Langsam").pack(side=tk.LEFT)
self.speed_var = tk.IntVar(value=50)
self.speed_scale = ttk.Scale(speed_frame, from_=0, to=100,
variable=self.speed_var,
orient=tk.HORIZONTAL,
command=self.apply_speed)
self.speed_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
ttk.Label(speed_frame, text="Schnell").pack(side=tk.LEFT)
# Wert-Anzeige
self.speed_label = ttk.Label(main_frame, text="50%", font=('', 10))
self.speed_label.pack()
# Info
ttk.Separator(main_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=20)
info = ttk.Label(main_frame,
text="Änderungen werden sofort angewendet und gespeichert.\n"
"Die Einstellung wird beim nächsten Start automatisch geladen.",
foreground='gray')
info.pack()
# Buttons
button_frame = ttk.Frame(main_frame)
button_frame.pack(side=tk.BOTTOM, pady=10)
ttk.Button(button_frame, text="Standard (50%)",
command=self.reset_default).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Schließen",
command=self.root.destroy).pack(side=tk.LEFT, padx=5)
def apply_speed(self, value=None):
"""Wende Mausgeschwindigkeit an (für alle gefundenen Mäuse)"""
speed_percent = self.speed_var.get()
# Konvertiere 0-100 zu verschiedenen Formaten
libinput_speed = (speed_percent / 50.0) - 1.0 # -1.0 bis 1.0
evdev_accel = speed_percent / 10.0 # 0 bis 10
matrix_scale = speed_percent / 50.0 # 0 bis 2.0
success_count = 0
methods_used = set()
# Wende Einstellungen auf ALLE Mäuse an
for mouse_id in self.mouse_ids:
success = False
method = ""
try:
# Methode 1: libinput (modern)
result = subprocess.run(['xinput', 'set-prop', mouse_id,
'libinput Accel Speed', str(libinput_speed)],
capture_output=True, text=True)
if result.returncode == 0:
success = True
method = "libinput"
except:
pass
if not success:
try:
# Methode 2: evdev (älter)
result = subprocess.run(['xinput', 'set-prop', mouse_id,
'Device Accel Constant Deceleration',
str(10.0 / max(evdev_accel, 0.1))],
capture_output=True, text=True)
if result.returncode == 0:
success = True
method = "evdev"
except:
pass
if not success:
try:
# Methode 3: Coordinate Transformation Matrix (Fallback für Logitech etc.)
# Matrix für Skalierung: [scale, 0, 0, 0, scale, 0, 0, 0, 1]
# WICHTIG: Muss als einzelne Werte übergeben werden, nicht als String
result = subprocess.run(['xinput', 'set-prop', mouse_id,
'Coordinate Transformation Matrix',
str(matrix_scale), '0', '0',
'0', str(matrix_scale), '0',
'0', '0', '1'],
capture_output=True, text=True)
if result.returncode == 0:
success = True
method = "matrix"
except:
pass
if success:
success_count += 1
methods_used.add(method)
if success_count > 0:
methods_str = "+".join(sorted(methods_used))
self.speed_label.config(text=f"{speed_percent}% ({success_count} Maus/Mäuse: {methods_str})")
self.save_settings()
else:
self.speed_label.config(text=f"{speed_percent}% (Fehler!)")
def reset_default(self):
"""Setze auf Standard zurück"""
self.speed_var.set(50)
self.apply_speed()
if __name__ == '__main__':
root = tk.Tk()
app = MouseSettings(root)
root.mainloop()