198 lines
7.2 KiB
Python
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()
|