Défilement AutoHotkey et accélération du clic et de la souris du milieu

Problèmes

Supposons une machine Windows 7, en particulier un ordinateur portable.

  1. Aucun pilote de souris permet de maintenir enfoncé le bouton droit de la souris et de déplacer la souris pour défiler, et pourtant, conserver la fonctionnalité du bouton droit de la souris. Ceci est utile si vous n'avez pas de bouton du milieu.
  2. Les pilotes de la souris ne fonctionnent pas pour certaines applications, et certains comme UltraNav se sentent très terne pour la plupart des applications où ils fonctionnent.
  3. Le pilote UltraNav pour le ThinkPad ne permet pas au bouton central de fonctionner en tant que clic central en même temps que le fait de défiler en le retenant et en déplaçant la souris.
  4. Le pilote UltraNav ne permet pas non plus de faire un clic-et-glisser en même temps que le défilement, ce qui est extrêmement utile dans l'édition de documents.
  5. Un défilement accéléré est impossible sur une roue-souris et mal fait par le pilote UltraNav.
  6. Avec le pilote de souris par défaut, la vitesse de pointage la plus rapide possible pourrait être beaucoup trop lente et fatiguante, en particulier pour les pistes ThinkPad.

Ce que j'ai souhaité était un script AutoHotkey pour résoudre tous ces problèmes, mais rien que j'ai trouvé en ligne ne se rapproche même de résoudre quelques-uns d'entre eux. Après deux ans, j'ai développé une solution complète ci-dessous. Bien sûr, certaines techniques que j'ai utilisées ont été adaptées à partir de différents messages sur Internet, mais souvent, chaque poste ne possédait qu'une ou deux des méthodes et ne fonctionnent pas pour de nombreuses applications.

Les commentaires sont les bienvenus! Toutefois, veuillez comprendre que je n'ai peut-être pas le temps de mettre en œuvre des suggestions ou des problèmes si cela ne fonctionne pas pour vous. Merci!

J'ai conçu mon propre script AutoHotkey qui utilise un crochet de souris de bas niveau pour résoudre tous les problèmes ci-dessus. Le code est trop long afin de voir l'autre réponse pour le code.

Instructions importantes

Utilisez la version 32 bits de AutoHotkey, même si vous utilisez Windows 64 bits, sinon cela ne fonctionnera pas dans certains endroits! Exécutez le script lors de la connexion avec les privilèges les plus élevés possibles pour l'utilisateur, en utilisant Task Scheduler, sinon il ne fonctionnera pas pour un utilisateur admin dans les applications exécutées avec des droits d'administrateur complets. Si UltraNav est installé, sélectionnez "Smooth" pour le bouton central de la souris, sinon cela interférera avec le script pour une raison inconnue et le défilement ne fonctionnera pas.

Dans de rares occasions, quelque chose ne va pas avec le crochet de la souris, surtout si vous essayez de faire défiler pendant qu'une application modifie sa GUI. Je soupçonne que c'est parce que je ne l'exécute pas en priorité en temps réel, mais je pense que c'est trop risqué parce que s'il est bloqué, il se peut qu'il n'y ait aucun moyen de l'arrêter. Actuellement, il est défini pour être exécuté en haute priorité. Si quelque chose se passe mal, essayez les raccourcis clavier suivants dans l'ordre.

LCtrl-RCtrl : Recharger. RCtrl-LCtrl : désactivez le crochet de la souris. (Si votre souris fonctionne maintenant, Recharger.) RCtrl-Esc : Désactivez le script complet. (Vous devrez l'exécuter à nouveau.)

Caractéristiques

  1. Par défaut, le défilement est effectué lorsque le bouton central de la souris est maintenu enfoncé et que la souris est déplacée. On peut régler rightbuttonscroll:=1 pour utiliser le bouton droit de la souris (en particulier pour les ordinateurs portables qui n'ont pas de bouton de la souris du milieu), auquel cas pour obtenir la fonctionnalité originale de glisser-droit, il faudra peut-être appuyer sur un Ctrl supplémentaire / Shift / Alt avant cela, selon l'application spécifique. (Dans la plupart des applications, au moins une de ces combinaisons fait la même chose que le droit-droit original.)
  2. Si l'on n'a pas de bouton de la souris du milieu, on peut également définir leftrighttomiddle:=1 , puis en appuyant sur les boutons de la souris gauche et droit dans clicklimit (millisecondes) génère le bouton du milieu, et la libération génère la sortie du bouton du milieu. Pour garder le bouton central généré, il suffit de garder l'un des deux boutons réels enfoncés.
  3. Le défilement fonctionne indépendamment des autres boutons de la souris; par exemple, on peut défiler n'importe quand en maintenant le bouton gauche de la souris pendant une opération de sélection, tant que l'application le supporte. (Certaines applications n'autorisent pas le défilement en sélectionnant pour des raisons étranges.)
  4. Par défaut, après avoir appuyé et relâché le bouton de défilement (que ce soit le bouton de la souris ou le bouton droit de la souris) dans le clicklimit le bouton de défilement original est généré. Si le bouton de défilement est plus long que celui-ci, le défilement est activé et la libération du bouton de défilement ne générera aucun clic. Pour activer le défilement même pendant ce délai initial, réglez scrollbeforeclick:=1 . Avec ce réglage, le clic est possible même si un léger défilement a déjà été effectué, pourvu qu'il ne dépasse pas le scrolllimit .
  5. Le défilement est légèrement collant, c'est-à-dire que si l'on relâche accidentellement le bouton de défilement pendant le défilement pour moins de resetdelay , il est traité comme s'il ne se produisait pas et que le défilement continuait. En particulier, cela signifie que le fait de cliquer accidentellement à la fin du défilement ne générera pas le bouton de défilement original.
  6. Le fait de cliquer est également légèrement collant, ce qui signifie que si l'on appuie sur un bouton de la souris dans resetdelay après avoir relâché un bouton de la souris, on suppose qu'un
  7. Certaines applications sont très lentes dans la gestion du défilement et donc nous ne pouvons pas trop piloter de nombreuses commandes pour effectuer le défilement. interval est l'intervalle entre les lots de commandes liées au défilement envoyées à l'application. Dans chaque intervalle, toutes les actions de défilement de l'utilisateur sont combinées en une seule. Diminuer l'intervalle rend le défilement plus sensible à l'utilisateur mais peut étouffer les applications mal conçues. Si un contrôle fin est souhaité, on peut toujours définir une valeur par défaut au début de l' gettarget et l'ajuster en fonction de l'application.
  8. Certaines applications prennent vraiment trop de temps pour traiter les commandes de défilement, et donc si une commande n'est pas traitée dans le timelimit , toutes les autres commandes du lot pour cet intervalle sont interrompues.
  9. On peut affiner le défilement en modifiant scrollamount pour changer la vitesse de défilement compte tenu de la vitesse de la souris ou de l'édition, scrolladjust de modifier la façon dont le défilement est effectué compte tenu des montants de défilements accumulés. Par défaut, le défilement est réglé sur la verticale ou l'horizontale si la quantité de défilement dans cette direction est supérieure à 5 fois celle dans l'autre sens.
  10. On peut aussi affiner la vitesse et l'accélération de la souris. Appuyez sur LCtrl-Ralt-D pour afficher la vitesse actuelle de la souris, LCtrl-Ralt-S pour diminuer la vitesse et LCtrl-Ralt-F pour augmenter la vitesse. Cela changera la vitesse du pointeur dans les paramètres de la souris. Modifiez le moveadjust pour modifier la façon dont les mouvements du pointeur sont convertis en mouvements de la souris. Par défaut, les vitesses faibles restent les mêmes et les vitesses élevées sont multipliées par 3, et il y a une transition en douceur, et chaque mouvement de la souris est réparti sur le courant et le suivant, car à un pointeur élevé accélère le matériel ou le défaut Le pilote de souris produit une sortie agitée. Assurez-vous que moveadjust fonctionne rapidement!

Méthodes

Il existe 11 différentes méthodes de défilement que j'ai découvertes dans diverses applications. Certaines applications ne répondent qu'à l'une de celles-ci, ce qui est très ennuyant. Les méthodes sont décrites dans les commentaires du code lui-même, mais il n'existe aucun moyen fiable de déterminer la méthode correcte à utiliser, et il faut donc personnaliser manuellement le gettarget pour choisir la méthode pour chaque application qui ne fonctionne pas avec les messages de roue par défaut . Essayez les méthodes dans l'ordre où elles se produisent dans le scroll . Pour chaque méthode, vous devrez expérimenter la modification des cibles des messages, à savoir ctrl,window,parent le cas échéant.

Pour basculer le débogage, appuyez sur LCtrl-AppMenu . Il montre les mouvements bruts de la souris les montants ajustés, tant pour le mouvement normal que pour le défilement. Si vous maintenez la touche de défilement enfoncée sans déplacer la souris, elle montre l'ascendance de la cible sous la souris et de la commande choisie par gettarget . J'ai parfois dû utiliser Spy ++ pour voir la hiérarchie de contrôle et deviner où envoyer les messages.

commentaires

L'AutoHotkey 64 bits provoque l'échec de mon script pour le Bloc-notes du programmeur, tandis que l'AutoHotkey 32 bits fonctionne parfaitement et je ne sais pas pourquoi. D'autres applications semblent ne pas avoir un tel problème. Si c'est un bug dans mon script, je serais ravi de le savoir!

Le code est trop long, donc il est divisé en deux réponses, et voir la troisième réponse pour les instructions.

Code Partie 1

 #commentflag // ; Change to C++ comment style // Settings // global rightbuttonscroll:=0 // set to 1 iff the right button is to be used for scrolling instead of the middle button global scrollbeforeclick:=1 // set to 1 iff scrolling is allowed before the original button click otherwise scrolling is delayed for (clicklimit) global leftrighttomiddle:=0 // set to 1 iff pressing both left+right buttons together is to be used to generate the middle button press global scrolllimit:=3.5 // maximum scroll allowed between scroll button press and release for the original button click to be generated global clicklimit:=210 // maximum time allowed between scroll button press and release for the original button click to be generated global resetdelay:=210 // minimum time required between scroll button release and press for the original button click to be allowed global interval:=35 // minimum interval between scrolling updates global timelimit:=70 // maximum time allowed for continuing the scrolling update in each direction global dbg:=false // Initialization // #singleinstance,force #keyhistory 0 #usehook on process priority,,H setkeydelay 1 setcontroldelay -1 coordmode mouse,screen global buttondown:=( rightbuttonscroll==1 ? 0x204 : 0x207 ) global buttonup:=( rightbuttonscroll==1 ? 0x205 : 0x208 ) global buttonoriginal:=( rightbuttonscroll==1 ? "RButton" : "MButton" ) global speed global handling:=0 global scrolling:=0 global scrolldrag:=0 global scrollsticky:=0 global clicksticky:=0 global leftphysical:=0 global rightphysical:=0 global lefttopress:=0 global righttopress:=0 global middlepressed:=0 global mx,my global dx,dy global lx:=0 global ly:=0 global sx:=0 global sy:=0 global totalx:=0 global totaly:=0 global ctrl,window,parent global methodx global methody global scrollbarx global scrollbary global max16bit:=32767 global sbinfo varsetcapacity(sbinfo,28) numput(28,sbinfo,0) numput(23,sbinfo,4) global mousehook:=dllcall("SetWindowsHookEx","int",14,"uint",RegisterCallback("handlemouse","fast"),"uint",0,"uint",0) message("AutoHotkey loaded") return // Reload & Debug & Exit & Disable Mousehook // LCtrl & RCtrl::reload LCtrl & AppsKey::dbg:=!dbg RCtrl & Esc::exitapp RCtrl & LCtrl::dllcall("UnhookWindowsHookEx","uint",mousehook) // Message // messageoff: tooltip return message(text) { tooltip % text //,a_screenwidth-strlen(text)*7,a_screenheight-42 settimer messageoff,-1000 } // Mouse Movement // moveadjust(byref x,byref y) { movespread(x,y) z2:=x**2+y**2 r:=(70+z2*4)/(70+z2) x:=rtoz(x*r) y:=rtoz(y*r) } movespread(byref x,byref y) { nx:=rtoz(x/2) ny:=rtoz(y/2) x-=nx y-=ny x+=lx y+=ly lx:=nx ly:=ny settimer movereset,-210 } movereset: lx:=0 ly:=0 return getspeed() { DllCall("SystemParametersInfo","Int",112,"Int",0,"UIntP",speed,"Int",0) } setspeed() { DllCall("SystemParametersInfo","Int",113,"Int",0,"UInt",speed,"Int",2) } showspeed() { message("Mouse speed = " . speed) } <^>!d:: getspeed() showspeed() return <^>!s:: getspeed() if( speed>1 ) { speed:=speed-1 } setspeed() showspeed() return <^>!f:: getspeed() if( speed<20 ) { speed:=speed+1 } setspeed() showspeed() return // Mouse Scrolling // /* Usage Combination : Function ScrollButton-Release : ScrollButton-Click (where the intervening time and scroll do not exceed the limits set) ScrollButton-Modifiers-Move : Modifiers-Scroll (where Modifiers can be any combination of Shift and Ctrl and Alt) If rightbuttonscroll = 1 : LButton-RButton : MButton (where the intervening time does not exceed the limit set) RButton-LButton : MButton (where the intervening time does not exceed the limit set) Methods Send wheel messages (0x20a/0x20e) to the control The wheel amount is only 16-bit which is at most only 32767/120 wheel notches Some applications do not handle the wheel amount correctly so you may need to send integer multiples of wheel notches or many wheel messages Microsoft Word apparently assumes that wheel messages are always posted and behaves incorrectly unless PostMessage is used Send scrollbars' thumb position to the control This works for many scrollable controls but not all Send scroll messages (0x115/0x114) to the control unless its scrollbar thumb position is already at the end Many applications respond rather slowly to scroll messages Send scroll messages to the scrollbars This works for some applications and should be slightly faster than scroll messages to the parent Many applications respond rather slowly so wheel messages are preferable if they work Send scroll messages to the scrollbars' parents This is supposed to be a standard way to control scrollbars that are separate from the control Many applications respond rather slowly so wheel messages are preferable if they work Send keys (Up/Down/Left/Right/PgUp/PgDn) to the scrollbars This is equivalent to clicking the arrows or the empty space on either side of the thumb track Some applications respond slowly so keys should not be sent too fast Send keys to the control Only for rare cases that do not respond to anything else Call Office function SmallScroll This function only exists for Office applications and even then it is broken in some like Powerpoint Customization scrollamount(x) Can assume that x is non-negative Must return non-negative output scrolladjust(x,y) Can modify scroll amounts (x,y) gettarget() Can assume that (mx,my) is the current mouse position Must set ctrl to the handle of the control that is to be scrolled Must set parent to getparent(ctrl) if scrolltoscrollbarparent() or keystoscrollbar() is used */ scrollamount(x) { return x**1.5/8 } scrolladjust(byref x,byref y) { ax:=abs(x) ay:=abs(y) z:=sqrt(x**2+y**2) if( ax>ay*5 ) { x:=( x>0 ? z : -z ) y:=0 } if( ay>ax*5 ) { y:=( y>0 ? z : -z ) x:=0 } } gettarget() { gosub messageoff ctrl0:=getctrlat(mx,my) window:=getwindow(ctrl0) class:=getclass(window) title:=gettitle(window) ctrl:=ctrl0 loop { ctrlname:=getnameaschild(ctrl) ctrlclass:=getclass(ctrl) parent:=getparent(ctrl) parentname:=getnameatroot(parent) parentclass:=getclass(parent) if( ctrl!=window and ( regexmatch(ctrlclass,"^(Button|T?ComboBox|CtrlNotifySink|SysLink)$")==1 or parentclass=="ComboBox" ) ) { ctrl:=parent continue } break } gp:=getparent(parent) ggp:=getparent(gp) gpname:=getnameaschild(gp) ggpname:=getnameaschild(ggp) methodx:="wheel" // needed for: Firefox , Gimp , ... methody:="wheel" // needed for: Firefox , File Chooser , Explorer , Word , Outlook , IE , ... scrollbarx:="" scrollbary:="" if( ctrlclass=="ComboLBox" ) // Standard Combo Boxes dropdown list { methodx:="thumbpos" methody:="thumbpos" } if( ctrlclass=="ListBox" ) // Standard List Boxes { methodx:="thumbpos" methody:="thumbpos" } if( ctrlclass=="ScrollBar" ) // Standard ScrollBar controls { methodx:="scrolltoscrollbarparent" // "scroll" works for most places but not some ( Character Map , ... ) methody:="scrolltoscrollbarparent" // "scroll" works for most places but not some ( Character Map , ... ) scrollbarx:="scrollbar1" scrollbary:="scrollbar1" } if( class=="OpusApp" ) // Microsoft Word { methodx:="office" methody:="postwheel" if( gpname=="_WwB1" ) { ctrl:=getdescendant(window,"_WwG1") } } if( class=="XLMAIN" ) // Microsoft Excel { methodx:="office" if( gpname=="EXCEL71" ) { ctrl:=gp } } if( class=="PPTFrameClass" or class=="PP12FrameClass" ) // Microsoft Powerpoint { methody:="wheelsingle" ctrlnameatroot:=getnameatroot(ctrl) if( ctrlnameatroot=="NetUIHWND3" or ctrlnameatroot=="NetUIHWND4" ) { ctrl:=getdescendant(window,"paneClassDC1") ctrlname:="paneClassDC1" parent:=getparent(ctrl) } if( ctrlname=="paneClassDC1" ) { methodx:="scrolltoscrollbarparent" methody:="scrolltoscrollbarparent" // Powerpoint scroll up is broken when there are 9 slides at 100% zoom in normal mode scrollbarx:="NUIScrollbar2" scrollbary:="NUIScrollbar1" //methody:="office" // Powerpoint does not update the view pane immediately and so it is disorienting } if( ctrlnameatroot=="NetUIHWND5" ) { ctrl:=getdescendant(window,"paneClassDC2") } } if( class=="rctrl_renwnd32" ) // Microsoft Outlook { methodx:="office" gpclass:=getclass(gp) ctrlnameatroot:=getnameatroot(ctrl) if( ctrlclass=="SUPERGRID" ) { methodx:="scroll" methody:="scroll" } if( gpclass=="SUPERGRID" ) { methodx:="scroll" methody:="scroll" ctrl:=gp } if( gpname=="_WwB1" ) { ctrl:=getdescendant(window,"_WwG1") } } if( ctrlclass=="OUTEXVLB" ) // Microsoft Outlook { Address Book , Group membership , ... } { methodx:="thumbpos" if( regexmatch(title,"Global Address List")==0 ) { methody:="scroll" } } if( title=="Symbol" and regexmatch(class,"bosa_sdm_(msword|Microsoft Office Word 12.0|XL9|Mso96)")==1 ) // Microsoft Office Insert Symbol { controlget v,visible,,ScrollBar1,ahk_id %ctrl% if( v==0 ) { ctrl:=getdescendant(window,"Edit1") methody:="thumbpos" } } if( class=="wndclass_desked_gsk" ) // Microsoft Visual Basic { if( ctrlclass=="VbaWindow" ) { methodx:="scrolltoscrollbarparent" parent:=ctrl scrollbarx:="scrollbar2" } } if( regexmatch(ctrlclass,"^RichEdit20W(PT)?$")==1 ) // Windows Text Areas { methodx:="wheel" methody:="scroll" } if( class=="AcrobatSDIWindow" ) // Adobe Reader { if( regexmatch(parentname,"AVL_AVView")==1 ) { ctrl:=getdescendant(parent,"AVL_AVView4") if( ctrl=="" ) { ctrl:=getdescendant(parent,"AVL_AVView1") } methodx:="scrolltoscrollbarparent" scrollbarx:="scrollbar1" methody:="wheel" } } if( ggpname=="SHELLDLL_DefView1" ) // Windows Explorer Scrollbars { ctrl:=gp ctrlname:=gpname parentname:=ggpname } if( ctrlname=="DirectUIHWND1" ) { if( parentname=="SHELLDLL_DefView1" ) // Windows Explorer (including Standard File Choosers) { methodx:="scrolltoscrollbar" // "scrolltoscrollbarparent" also works scrollbarx:="scrollbar1" controlget v,visible,,ScrollBar2,ahk_id %ctrl% methody:=( v==1 ? "wheel" : "" ) } if( class=="CabinetWClass" ) { if( parentname=="XBabyHost1" ) // Control Panel { methody:="scrolltoscrollbar" // "scrolltoscrollbarparent" also works scrollbary:="scrollbar1" if( title=="Personalization" ) { scrollbary:="scrollbar3" } } } } if( ctrlclass=="CharGridWClass" ) // Character Map { methody:="scrolltoscrollbarparent" scrollbary:="scrollbar1" } if( class=="ConsoleWindowClass" ) // Console Window { methodx:="thumbpos" methody:="thumbpos" } if( class=="WordPadClass" ) // WordPad { methodx:="scroll" methody:="scroll" // WordPad scroll down is broken when scrolling horizontally at the same time } if( class=="ATL:006AD5B8" ) // Programmer's Notepad { methodx:="scroll" } if( class=="SWT_Window0" or ctrlclass=="Internet Explorer_Server" ) // Eclipse { methodx:="scroll" } if( ctrlclass=="TSynEdit" ) // TSynEdit ; Dev C++ , ... { methodx:="thumbpos" methody:="thumbpos" } if( ctrlclass=="TListView" ) // TlistView ; Dev C++ , ... { methodx:="scroll" } if( ctrlclass=="TPSSynEdit" ) // TPSSynEdit ; PSPad , ... { methodx:="thumbpos" } if( class=="QWidget" or class=="Qt5QWindowIcon" ) { if( regexmatch(title,"LyX")==1 ) // Lyx { methodx:="" // prevents horizontal scrolling from becoming vertical scrolling in the edit pane // but disables horizontal scrolling everywhere else } if( regexmatch(title,"TeXworks$")>0 ) // TeXWorks { methodx:="wheelint" methody:="wheelint" } } if( class=="gdkWindowToplevel" ) // Gimp { methody:="wheelsingle" // Gimp performs horizontal scrolling when the mouse is scrolled over the horizontal scrollbar } if( class=="SunAwtDialog" ) // Java AWT Dialogs ; GeoGebra , Logisim , ... { methody:="wheelint" } if( regexmatch(ctrlname,"IupCanvas")==1 ) // IupCanvas { methodx:="scroll" } if( class=="SunAwtFrame" ) { if( regexmatch(title,"GeoGebra|.*\.ggb$")==1 ) // GeoGebra { methody:="wheelint" } if( regexmatch(title,"Logisim")==1 ) // Logisim { methodx:="keys" // performs scrolling only if the drawing area has the focus } } if( class=="MSPaintApp" ) // MSPaint { if( parentname=="MSPaintView1" ) { methodx:="thumbpos" methody:="thumbpos" } } if( class=="ATL:643E3490" ) // Real World Paint { if( ctrlclass=="RWViewImageEdit" ) { methodx:="scroll" methody:="scroll" } } if( ctrlclass=="DSUI:PagesView" ) // PDF-XChange Viewer (also as a browser plugin) { methodx:="scroll" methody:="wheelint" } if( ctrlclass=="PuTTY" ) // PuTTY { methody:="scroll" } if( dbg ) { p:=getparent(ctrl) gp:=getparent(p) ggp:=getparent(gp) message( "Root class = " class . "`nRoot title = " title . "`nTarget = [" ctrl0 "]" . "`n`t(as child) " getnameaschild(ctrl0) . "`n`t(at root) " getnameatroot(ctrl0) . "`nControl = [" ctrl "]" . "`n`t(at child) " getnameaschild(ctrl) . "`n`t(as root) " getnameatroot(ctrl) . "`nControl ancestors = " . "`n`t < [" p "] " getnameatroot(p) . "`n`t < [" gp "] " getnameatroot(gp) . "`n`t < [" ggp "] " getnameatroot(ggp) . "`nMethod = (" methodx "," methody ")" . "`nScrollbars = (" scrollbarx "," scrollbary ")" ) } } scroll: critical on if( getwindow(getctrlat(mx,my))!=window ) { scrolling:=0 } if( scrolling==0 ) { return } settimer scroll,-%interval% if( scrollbeforeclick!=1 and scrolldrag==0 ) { sx:=0 sy:=0 return } tx:=sx ty:=sy sx-=tx sy-=ty totalx+=tx totaly+=ty if( totalx**2+totaly**2>scrolllimit ) { scrolldrag:=1 } scrolladjust(tx,ty) rx:=0 ry:=0 comobjerror(false) if( tx!=0 ) { if( methodx=="wheel" ) { sendwheel("h",tx) } else if( methodx=="postwheel" ) { postwheel("h",tx) } else { txi:=rtoz(tx) rx:=tx-txi if( txi!=0 ) { if( methodx=="wheelint" ) { sendwheel("h",txi) } else if( methodx=="wheelsingle" ) { sendwheelsingle("h",txi) } else if( methodx=="thumbpos" ) { sendthumbpos("h",txi) } else if( methodx=="scroll" ) { sendscroll("h",txi) } else if( methodx=="scrolltoscrollbar" ) { sendscrolltoscrollbar(scrollbarx,txi) } else if( methodx=="scrolltoscrollbarparent" ) { sendscrolltoscrollbarparent(scrollbarx,"h",txi) } else if( methodx=="keys" ) { sendkeys("h",txi) } else if( methodx=="keystoscrollbar" ) { sendkeystoscrollbar(scrollbarx,txi) } else if( methodx=="office" ) { Acc_ObjectFromWindow(ctrl,-16).SmallScroll(0,0,(txi>0?txi:0),(txi<0?-txi:0)) } } } } if( ty!=0 ) { if( methody=="wheel" ) { sendwheel("v",-ty) } else if( methody=="postwheel" ) { postwheel("v",-ty) } else { tyi:=rtoz(ty) ry:=ty-tyi if( tyi!=0 ) { if( methody=="wheelint" ) { sendwheel("v",-tyi) } else if( methody=="wheelsingle" ) { sendwheelsingle("v",-tyi) } else if( methody=="thumbpos" ) { sendthumbpos("v",tyi) } else if( methody=="scroll" ) { sendscroll("v",tyi) } else if( methody=="scrolltoscrollbar" ) { sendscrolltoscrollbar(scrollbary,tyi) } else if( methody=="scrolltoscrollbarparent" ) { sendscrolltoscrollbarparent(scrollbary,"v",tyi) } else if( methody=="keys" ) { sendkeys("v",tyi) } else if( methody=="keystoscrollbar" ) { sendkeystoscrollbar(scrollbary,tyi) } else if( methody=="office" ) { Acc_ObjectFromWindow(ctrl,-16).SmallScroll((tyi>0?tyi:0),(tyi<0?-tyi:0)) } } } } comobjerror(true) sx:=rx sy:=ry return sendwheel(dir,amount) { t:=a_tickcount msg:=( dir=="v" ? 0x20a : 0x20e ) flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2 amount*=120 while( amount>max16bit ) { sendmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit amount-=max16bit if( a_tickcount-t>=timelimit ) { return } } while( amount<-max16bit ) { sendmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit amount+=max16bit if( a_tickcount-t>=timelimit ) { return } } sendmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit } postwheel(dir,amount) { msg:=( dir=="v" ? 0x20a : 0x20e ) flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2 amount*=120 while( amount>max16bit ) { postmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl% amount-=max16bit } while( amount<-max16bit ) { postmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl% amount+=max16bit } postmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl% } sendwheelsingle(dir,amount) { t:=a_tickcount msg:=( dir=="v" ? 0x20a : 0x20e ) flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2 loop % abs(amount) { sendmessage msg,(amount<0?-120:120)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit if( a_tickcount-t>=timelimit ) { return } } } sendthumbpos(dir,amount) { msg:=( dir=="v" ? 0x115 : 0x114 ) sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo) if( sb ) { sbmin:=numget(sbinfo,8,"int") sbmax:=numget(sbinfo,12,"int") sbpos:=numget(sbinfo,20,"int") if( amount>max16bit ) { amount=max16bit } if( amount<-max16bit ) { amount=-max16bit } pos:=sbpos+amount if( pos<sbmin ) { pos:=sbmin } if( pos>sbmax ) { pos:=sbmax } sendmessage msg,pos<<16|4,,,ahk_id %ctrl%,,,,timelimit } } sendscroll(dir,amount) { t:=a_tickcount msg:=( dir=="v" ? 0x115 : 0x114 ) flag:=( amount<0 ? 0 : 1 ) loop % abs(amount) { sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo) if( sb ) { sbmin:=numget(sbinfo,8,"int") sbmax:=numget(sbinfo,12,"int") sbpage:=numget(sbinfo,16,"uint") sbpos:=numget(sbinfo,20,"int") if( ( sbpos==sbmin and amount<0 ) or ( sbpos+sbpage==sbmax+1 and amount>0 ) ) { return } } sendmessage msg,flag,,,ahk_id %ctrl%,,,,timelimit if( a_tickcount-t>=timelimit ) { return } } } sendscrolltoscrollbar(name,amount) { t:=a_tickcount flag:=( amount<0 ? 0 : 1 ) loop % abs(amount) { sendmessage 0x115,flag,,%name%,ahk_id %ctrl%,,,,timelimit if( a_tickcount-t>=timelimit ) { return } } } sendscrolltoscrollbarparent(name,dir,amount) { sb:=getdescendant(parent,name) sbp:=getparent(sb) t:=a_tickcount msg:=( dir=="v" ? 0x115 : 0x114 ) flag:=( amount<0 ? 0 : 1 ) loop % abs(amount) { sendmessage msg,flag,sb,,ahk_id %sbp%,,,,timelimit if( a_tickcount-t>=timelimit ) { return } } } sendkeys(dir,amount) { t:=a_tickcount key:=( dir=="v" ? ( amount<0 ? "{Up}" : "{Down}" ) : ( amount<0 ? "{Left}" : "{Right}" ) ) loop % abs(amount) { controlsend, ,%key%,ahk_id %ctrl% if( a_tickcount-t>=timelimit ) { return } } } sendkeystoscrollbar(name,amount) { t:=a_tickcount key:=( amount<0 ? "{Up}" : "{Down}" ) controlget e,enabled,,%name%,ahk_id %parent% if( e==1 ) { loop % abs(amount) { controlsend %name%,%key%,ahk_id %parent% if( a_tickcount-t>=timelimit ) { break } } } } 

Le code est trop long, donc il est divisé en deux réponses, et voir la troisième réponse pour les instructions.

Code Part 2

 scrollbuttonreset: scrollsticky:=0 return scrollbuttoncannotclick: scrolldrag:=1 righttopress:=0 return clickreset: clicksticky:=0 return scrollbuttondown: critical on mousegetpos mx,my gettarget() sx:=0 sy:=0 totalx:=0 totaly:=0 settimer scroll,-%interval% settimer scrollbuttonreset,off return scrollbuttonup: critical on if( scrolldrag==0 and scrollsticky==0 ) { handling++ sendevent {Blind}{%buttonoriginal% down} sendevent {Blind}{%buttonoriginal% up} handling-- scrolling:=0 } scrolldrag:=0 righttopress:=0 settimer scrollbuttondown,off settimer scrollbuttoncannotclick,off scrollsticky:=1 settimer scrollbuttonreset,-%resetdelay% return leftdown: critical on lefttopress:=0 handling++ sendevent {Blind}{LButton down} handling-- return rightdown: critical on righttopress:=0 handling++ sendevent {Blind}{RButton down} handling-- return middledown: critical on scrolling:=0 scrolldrag:=0 middlepressed:=1 settimer scrollbuttondown,off settimer scrollbuttoncannotclick,off handling++ sendevent {Blind}{MButton down} handling-- return leftup: critical on handling++ sendevent {Blind}{LButton up} handling-- clicksticky:=1 settimer clickreset,-%resetdelay% return rightup: critical on handling++ sendevent {Blind}{RButton up} handling-- clicksticky:=1 settimer clickreset,-%resetdelay% return middleup: critical on middlepressed:=0 handling++ sendevent {Blind}{MButton up} handling-- clicksticky:=1 settimer clickreset,-%resetdelay% return leftclick: critical on gosub leftdown gosub leftup return rightclick: critical on gosub rightdown gosub rightup return // Mouse handler // handlemouse(nCode,wParam,lParam) { critical on if( a_ispaused==1 ) { exitapp // something goes wrong if it remains paused } o:=0 if( handling==0 && nCode>=0 ) { if( wParam==0x201 ) { leftphysical:=1 } else if( wParam==0x204 ) { rightphysical:=1 } else if( wParam==0x202 ) { leftphysical:=0 } else if( wParam==0x205 ) { rightphysical:=0 } if( wParam==0x200 ) { // Handle mouse move // mousegetpos mx,my x:=numget(lParam+0,0,"int") // the "+0" is necessary! y:=numget(lParam+0,4,"int") // the "+0" is necessary! dx:=x-mx dy:=y-my if( scrolling==0 ) { // Click immediately on mouse drag // if( leftrighttomiddle==1 ) { if( lefttopress==1 ) { settimer leftdown,-0 } if( righttopress==1 ) { settimer rightdown,-0 } } // Adjust mouse movement // moveadjust(dx,dy) handling++ mousemove dx,dy,0,R handling-- if( dbg ) { message( "Origin = " mx " " my "`nMove = " x-mx " " y-my "`n -> " dx " " dy ) } // Release mouse buttons if out of sync with physical state // if( getkeystate("LButton")>leftphysical ) { settimer leftup,-0 } if( getkeystate("RButton")>rightphysical ) { settimer rightup,-0 } o:=1 } else { // Handle mouse move when scrolling // if( dx!=0 or dy!=0 ) { sx+=( dx>0 ? scrollamount(dx) : -scrollamount(-dx) ) sy+=( dy>0 ? scrollamount(dy) : -scrollamount(-dy) ) if( dbg ) { message( "Origin = " mx " " my "`nMove = " dx " " dy "`n -> " round(sx,2) " " round(sy,2) ) } } o:=1 } } else if( scrolling==0 ) { if( wParam==buttondown and middlepressed==0 and lefttopress==0 ) { // Handle scroll button down // if( getkeystate("Ctrl")==0 and getkeystate("Shift")==0 and getkeystate("Alt")==0 ) { // Handle scroll start // scrolling:=1 scrolldrag:=0 if( buttondown==0x204 ) { righttopress:=1 } settimer scrollbuttondown,-0 settimer scrollbuttoncannotclick,-%clicklimit% o:=1 } } } else if( scrolling==1 ) { if( wParam==buttonup ) { // Handle scroll button up // scrolling:=0 settimer scrollbuttonup,-0 o:=1 } } if( leftrighttomiddle==1 and o==0 ) { if( middlepressed==1 ) { // Release middle button only when both left and right buttons are released // if( leftphysical==0 and rightphysical==0 ) { settimer middleup,-0 } o:=1 } else if( scrolldrag==0 ) { // Process left+right=middle // if( wParam==0x201 and scrollsticky==0 ) { // Handle left button down // if( righttopress==0 ) { if( clicksticky==1 ) { settimer leftdown,-0 } else { lefttopress:=1 settimer leftdown,-%clicklimit% } } else { righttopress:=0 settimer rightdown,off settimer middledown,-0 } o:=1 } else if( wParam==0x204 and scrollsticky==0 ) { // Handle right button down // if( lefttopress==0 ) { if( clicksticky==1 ) { settimer rightdown,-0 } else { righttopress:=1 settimer rightdown,-%clicklimit% } } else { lefttopress:=0 settimer leftdown,off settimer middledown,-0 } o:=1 } else if( wParam==0x202 ) { // Handle left button up // if( lefttopress==1 ) { settimer leftdown,off settimer leftclick,-0 } else { settimer leftup,-0 } o:=1 } else if( wParam==0x205 ) { // Handle right button up // if( righttopress==1 ) { settimer rightdown,off settimer rightclick,-0 } else { settimer rightup,-0 } o:=1 } } } } // Pass on any other mouse events // if( o==0 ) { o:=dllcall("CallNextHookEx","uint",mousehook,"int",nCode,"uint",wParam,"uint",lParam) } return o } // Utilities // rtoz(r) { return ( r>0 ? floor(r) : ceil(r) ) } getparent(handle) { return dllcall("GetParent","uint",handle) } getancestor(handle,steps) { if( steps==0 ) { return handle } if( steps>0 ) { return getancestor(getparent(handle),steps-1) } return "" } getname(root,handle) { local CH,CN,S,P WinGet, CH, ControlListHwnd, ahk_id %root% WinGet, CN, ControlList, ahk_id %root% setformat integerfast,h handle+=0 handle.="" setformat integerfast,d LF:= "`n", CH:= LF CH LF, CN:= LF CN LF, S:= SubStr( CH, 1, InStr( CH, LF handle LF ) ) StringReplace, S, S,`n,`n, UseErrorLevel StringGetPos, P, CN, `n, L%ErrorLevel% Return SubStr( CN, P+2, InStr( CN, LF, 0, P+2 ) -P-2 ) } getdescendant(handle,name) { local CH,CN,S,P WinGet, CH, ControlListHwnd, ahk_id %handle% WinGet, CN, ControlList, ahk_id %handle% setformat integerfast,h handle+=0 handle.="" setformat integerfast,d LF:= "`n", CH:= LF CH LF, CN:= LF CN LF, S:= SubStr( CN, 1, InStr( CN, LF name LF ) ) StringReplace, S, S,`n,`n, UseErrorLevel StringGetPos, P, CH, `n, L%ErrorLevel% Return SubStr( CH, P+2, InStr( CH, LF, 0, P+2 ) -P-2 )*1 } getnameatroot(handle) { return getname(dllcall("GetAncestor","uint",handle,"uint",2),handle) } getnameaschild(handle) { return getname(getparent(handle),handle) } getclass(handle) { local class wingetclass class,ahk_id %handle% return class } gettitle(handle) { local title wingettitle title,ahk_id %handle% return title } getposition(handle,byref left,byref top,byref right,byref bottom) { local rect varsetcapacity(rect,16) dllcall("GetWindowRect","uint",handle,"uint",&rect) left:=numget(rect,0,"int") top:=numget(rect,4,"int") right:=numget(rect,8,"int") bottom:=numget(rect,12,"int") } getctrlat2(x,y,first,current) { /* Pushes the following invisible container controls to the back because they are in front of their contents for no reason SysTabControl32 : The usual class that contains tabbed panes ( Mouse properties , ... ) Static : A class occasionally used to contain tabbed panes ( Programmer's Notepad Options > Fonts and Colours > Advanced , ... ) Button : A typical class used to contain a List Box ( Outlook Contact > Properties > General > Members , ... ) Executes WindowFromPoint again to access the contents of such container controls */ local handle,class,style class:=getclass(current) winget style,style,ahk_id %current% if( class=="SysTabControl32" or class=="Static" or ( class=="Button" and (style&0x7)==0x7 ) ) { dllcall("SetWindowPos","uint",current,"uint",1,"int",0,"int",0,"int",0,"int",0,"uint",0x3) // push it to the back where it belongs handle:=dllcall("WindowFromPoint","int",x,"int",y) //handle:=DllCall( "WindowFromPoint", "int64", (y << 32) | (x & 0xFFFFFFFF), "Ptr") // for negative 64-bit if( handle==first ) { return first } return getctrlat2(x,y,first,handle) } return current } getctrlat(x,y) { local handle handle:=dllcall("WindowFromPoint","int",x,"int",y) //handle:=DllCall( "WindowFromPoint", "int64", (y << 32) | (x & 0xFFFFFFFF), "Ptr") // for negative 64-bit return getctrlat2(x,y,handle,handle) } getwindow(handle) { return dllcall("GetAncestor","uint",handle,"uint",2) } Acc_Init() { Static h If Not h h:=DllCall("LoadLibrary","Str","oleacc","Ptr") } Acc_ObjectFromWindow(hWnd, idObject = -4) { local o Acc_Init() o:=DllCall("oleacc\AccessibleObjectFromWindow" , "Ptr", hWnd , "UInt", idObject&=0xFFFFFFFF , "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81 ,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64") ,"Int64") ,"Ptr*", pacc) if( o==0 ) Return ComObjEnwrap(9,pacc,1) }