Aller au contenu

04 / 04 À l'arrêt · dernière version le 27 juin 2026

Dofus Switcher

Outil de bureau pour jouer plusieurs comptes Dofus Retro sous KDE Plasma 6 / Wayland : il fait passer d'une fenêtre de jeu à l'autre au clavier ou avec les boutons latéraux de la souris. Construit pour mon propre usage en juin 2026, plus utilisé depuis. Le projet est à l'arrêt, en version 0.7.2.

Coupe du chemin d'un clic sur un bouton latéral La souris physique est capturée en exclusivité via evdev. Une boucle relaie tous ses événements vers un périphérique virtuel uinput que KWin voit comme une souris ordinaire, sauf les boutons associés à une action. Ceux-ci déclenchent le cycle, qui lit la fenêtre active et demande à kdotool d'activer la suivante. Aucune entrée n'est jamais envoyée à une fenêtre Dofus. EVIOCGRAB, exclusif le reste MB4 / MB5 Souris physique /dev/input/eventN 2 Boucle de relais select() · timeout 0,1 sboutons bindés retirés 2 Sink uinput capacités copiéesdepuis la souris 2 KWin souris ordinaire 2 Cycle sans état active → voisinerelit ×2 · 15 ms 3 kdotool windowactivate 1 Fenêtre Dofus au premier plan 1 CGU Ankama aucune entréevers le jeu 1 Coupe · Dofus Switcher v0.7.2
Coupe du chemin d'un clic sur un bouton latéral. Les numéros renvoient aux passages du texte. Trait tireté : système externe ; hachures : contrainte.

Le problème

En combat, chaque personnage joue à son tour : avec plusieurs comptes ouverts, il faut passer d'une fenêtre de jeu à la suivante à chaque tour, sans se tromper d'ordre. Je le faisais avec un script bash et deux raccourcis KDE. Le besoin de départ tenait en une ligne : faire défiler les fenêtres avec les deux boutons latéraux de la souris, que la main a déjà sous les doigts pendant qu'elle joue, en remplaçant le script par une vraie application.

Ce qui rendait la solution évidente inutilisable

L'outil le plus évident pour du multi-comptes envoie des clics et des touches aux fenêtres du jeu : une action répétée sur tous les comptes, une macro qui change de fenêtre puis agit. C'est précisément ce que les conditions d'utilisation d'Ankama interdisent, et le risque est la suspension de tous les comptes. La règle a été posée avant la première ligne de code, et rappelée dans la spécification comme non négociable : l'outil ne gère que des fenêtres du système. Il les liste, les ordonne, en active une. Il n'envoie jamais une entrée synthétique à une fenêtre Dofus, ne diffuse rien, n'enchaîne aucune action en jeu.

Deuxième obstacle : sous Wayland, une application ne peut pas activer la fenêtre d'une autre, et les outils X11 habituels (xdotool, wmctrl) n'y fonctionnent pas. L'activation passe donc par kdotool, qui passe par KWin, le compositeur de Plasma. C'est ce qui lie l'outil à KDE Plasma 6.

Pour les boutons de souris, l'outil descend sous le compositeur, au niveau evdev, l'interface du noyau Linux pour les périphériques d'entrée. La capture exclusive y donne deux choses à la fois : l'outil voit les clics des boutons latéraux quelle que soit la fenêtre au premier plan, et il peut les retirer du flux avant que le compositeur ne les reçoive, donc avant qu'ils n'atteignent le jeu.

Ce qui a été choisi, et ce que ça a coûté

La souris est capturée en exclusivité (EVIOCGRAB) : le compositeur ne voit plus le périphérique physique. Une boucle relaie tous ses événements vers un périphérique virtuel créé avec uinput, dont les capacités sont copiées depuis la souris d'origine ; KWin y voit une souris ordinaire, quelle que soit la marque. Seuls les boutons associés à une action sont retirés du flux, à l'appui comme au relâchement, avec l'événement MSC_SCAN qui les précède. Les supprimer jusqu'au prochain SYN aurait été plus simple, mais aurait aussi avalé les mouvements regroupés dans la même trame.

Ce que KWin reçoit est donc le flux de la souris, inchangé, moins les clics bindés. Rien n'est fabriqué, et rien n'atteint le jeu : c'est ce qui rend la capture compatible avec la contrainte du point 1.

Relayer chaque événement de la souris ajoute forcément du délai. Avant d'écrire ce lot, une mesure a fixé la règle : p99 sous 3 ms, on continue ; au-delà de 5 ms, on abandonne les binds souris. Le p99 consigné dans le code est d'environ 0,007 ms. Les résultats bruts de la mesure n'ont pas été conservés.

La capture ne doit jamais survivre au programme : une souris capturée que personne ne relâche, c'est un bureau sans pointeur. La session suit un ordre fixe (résoudre, ouvrir, capturer, relayer) et libère tout dans un bloc finally. Il n'y a volontairement pas de filet atexit pour la souris : le noyau relâche la capture quand le descripteur de fichier meurt, y compris sur un crash. SIGINT et SIGTERM passent par set_wakeup_fd pour fermer proprement la fenêtre Qt. Un thread superviseur recapture la souris après un débranchement, en la retrouvant par son identifiant matériel plutôt que par /dev/input/eventN, qui change d'un démarrage à l'autre.

Le prix : l'utilisateur doit appartenir au groupe input et pouvoir écrire dans /dev/uinput. L'autodiagnostic vérifie ces droits deux fois. os.access donne le verdict réel, ACL comprises. getgrouplist distingue deux causes : l'utilisateur n'est pas dans le groupe, ou il y est mais sa session n'a pas été rouverte depuis. Un problème de droits ne fait pas échouer l'autodiagnostic : il sert aussi de test après une mise à jour automatique, et une permission manquante ne doit pas déclencher un retour à la version précédente sans rapport.

La race condition corrigée en 0.7.2 se manifestait ainsi : en clics rapides, le cycle revenait parfois au premier personnage au lieu de passer au suivant. Deux hypothèses ont été écartées. La garde anti-rafale était la même pour le clavier et la souris. Et le cycle ne stocke aucun index, puisqu'il relit la fenêtre active à chaque fois. La cause : avec huit clients ouverts et KWin chargé, kdotool getactivewindow renvoyait par moments une chaîne vide. Sans fenêtre active connue, le cycle repartait du début. Le cas était aggravé par une seconde lecture de la fenêtre active, redondante, alors que la première avait déjà été confirmée.

Le correctif relit au plus deux fois, à 15 ms d'intervalle, puis abandonne, comme le faisaient déjà depuis la 0.6.5 les deux autres appels à kdotool touchés par le même défaut. La fenêtre active déjà lue est transmise au cycle, ce qui supprime la seconde lecture. Quatre tests couvrent le cas.

Un arbitrage a été imposé par une panne. Jusqu'à la 0.5, chaque raccourci lançait un processus Python, et chaque appui créait un service systemd transitoire. Sous la répétition automatique du clavier, environ 30 par seconde, cela a saturé systemd --user, DBus et KWin jusqu'au gel de la machine, le 22 juin 2026. Depuis la 0.6, les raccourcis sont enregistrés via DBus (KGlobalAccel) dans le processus de l'interface. Ils ne fonctionnent donc que tant que la fenêtre est ouverte : ni démon, ni démarrage automatique, ni icône dans la zone de notification. C'est moins pratique, et c'était voulu.

Les 278 tests tournent sans matériel ni compositeur : evdev, uinput et kdotool sont remplacés par des doublures. La contrepartie se lit dans la couverture mesurée le 2 octobre 2026 : le module qui parle vraiment à evdev n'est couvert qu'à 61 %, et ce chemin réel n'a été validé que par la mesure de latence et l'usage.

Vérifiable

Le dépôt est privé et l'outil n'a jamais été distribué : rien n'est consultable de l'extérieur.

Chiffres déclarés, dépôt privé :

Tests
278 tests pytest tous passants, exécutés le 2 octobre 2026
Couverture
90 % sur core/ mesurée le 2 octobre 2026 ; l'interface Qt n'est pas mesurée