diff --git a/visual_workflow_builder/backend/api_v3/execute.py b/visual_workflow_builder/backend/api_v3/execute.py index 666923652..f0d5476e8 100644 --- a/visual_workflow_builder/backend/api_v3/execute.py +++ b/visual_workflow_builder/backend/api_v3/execute.py @@ -23,6 +23,43 @@ from . import api_v3_bp logger = logging.getLogger(__name__) +_CHAR_TO_KEYSYM = { + ' ': 'space', '\t': 'Tab', '\n': 'Return', + '!': 'exclam', '"': 'quotedbl', '#': 'numbersign', '$': 'dollar', + '%': 'percent', '&': 'ampersand', "'": 'apostrophe', + '(': 'parenleft', ')': 'parenright', '*': 'asterisk', '+': 'plus', + ',': 'comma', '-': 'minus', '.': 'period', '/': 'slash', + ':': 'colon', ';': 'semicolon', '<': 'less', '=': 'equal', + '>': 'greater', '?': 'question', '@': 'at', + '[': 'bracketleft', '\\': 'backslash', ']': 'bracketright', + '^': 'asciicircum', '_': 'underscore', '`': 'grave', + '{': 'braceleft', '|': 'bar', '}': 'braceright', '~': 'asciitilde', +} + + +def _xdotool_type_by_keysym(text): + """Tape du texte via xdotool key, caractère par caractère avec keysyms. + + Indépendant du layout clavier — fonctionne en AZERTY dans une VM QWERTY + et inversement. Plus lent que xdotool type mais 100% fiable. + """ + keys = [] + for ch in text: + if ch in _CHAR_TO_KEYSYM: + keys.append(_CHAR_TO_KEYSYM[ch]) + elif ch.isalnum(): + keys.append(ch) + else: + keys.append(ch) + + for key in keys: + subprocess.run( + ['xdotool', 'key', '--clearmodifiers', key], + timeout=2, check=True + ) + time.sleep(0.02) + + def safe_type_text(text): """Saisie de texte compatible VM/Citrix et claviers AZERTY/QWERTY. @@ -34,20 +71,17 @@ def safe_type_text(text): import shutil import pyautogui - # Méthode 1 : xdotool type (frappes clavier réelles) - # Envoie des événements X11 que QEMU/Citrix/RDP capturent et transmettent. - # Le presse-papier ne traverse PAS les VM — xdotool si. + # Méthode 1 : xdotool key par caractère (frappes clavier réelles) + # xdotool type envoie des scancodes QWERTY que les VM AZERTY interprètent + # mal (: → M, / → !, etc.). xdotool key avec les noms de keysym est + # indépendant du layout et traverse QEMU/Citrix/RDP correctement. if shutil.which('xdotool'): try: - subprocess.run( - ['xdotool', 'type', '--delay', '20', '--clearmodifiers', '--', text], - timeout=max(30, len(text) * 0.05), - check=True - ) - print(f" ✅ Saisie via xdotool type ({len(text)} car.)") + _xdotool_type_by_keysym(text) + print(f" ✅ Saisie via xdotool key ({len(text)} car.)") return except Exception as e: - print(f" ⚠️ xdotool type échoué: {e}") + print(f" ⚠️ xdotool key échoué: {e}") # Méthode 2 : Presse-papier (fonctionne seulement en local, pas en VM) xclip = shutil.which('xclip')