Posiadasz myszkę, która marzy, by stać się Logitech MX Master? – jeżeli tak, to niniejszy opis jest dla Ciebie 🙂
Postanowiłem dopracować przedstawiony w poprzednim artykule skrypt do obsługi myszki komputerowej. W międzyczasie prosty skrypt przerodził się w pełnoprawny program, a ja przekonałem się, że AutoHotKey (AHK) ma olbrzymie możliwości pozwalające na pisanie całkiem zaawansowanych programów… i kompilowanie ich do postaci wykonywalnej, a przez to uruchamianie bez konieczności użycia żadnego dodatkowego oprogramowania. Niniejszy skrypt to przykład vibe-codingu czystej wody. Nie stworzyłem w nim osobiście ani jednej literki (choć, po prawdzie, kilka usunąłem), miałem natomiast wizję, w jaki sposób program ma działać i jak go przetestować. Robotę za mnie wykonał ChatGPT, aczkolwiek przyznam, że nie odbyło się to bezboleśnie.
Jaką myszkę można usprawnić?
Program służy do obsługi myszki Silver Monkey Business Plus (dostępnej na X-kom), która charakteryzuje się następującymi cechami:
Cechy myszki nieistotne z perspektywy skryptu:
- prawy i lewy przycisk oraz standardowa rolka z możliwością swobodnego obracania się po bardziej gwałtownym zakręceniu nią,
- możliwość zmiany DPI (myszka je zapamiętuje i odtwarza po włączeniu zasilania),
- obsługa 3 urządzeń: dwa po Bluetooth i jedno z użyciem dongle USB,
- ergonomiczny kształt i niezła jakość wykonania – widać inspirację (choć nie tak ordynarną) Logitech MX Master, aczkolwiek mysz jest trochę mniejsza i inaczej wysklepiona, stąd leży w dłoni nieco gorzej, niż produkt od Logi.
Cechy, które uzasadniają użycie skryptu:
- odchylenie głównej rolki na boki, co wywołuje akcję: Ctrl+Wheel_Up/Down – powoduje to powiększenie i zmniejszenie zawartości okna, na którym pracujemy – przycisk ten jest samopowtarzalny,
- obecność bocznej rolki, której obrót wywołuje zdarzenia Wheel_Right/Left, intepretowane przez Windows jako przesunięcie okna w prawo/lewo,
- dwa boczne przyciski pod rolką, które wywołują zdarzenie XButton1/2 – Windows domyślnie intepretuje je jako do przodu/wstecz,
- trzeci boczny przycisk, którego naciśnięcie wywołuje sekwencję Windows+D, czyli Pokaż Pulpit – przycisk ten nie jest samopowtarzalny.
Moim celem było umożliwienie przemapowania wszystkich przycisków wg moich osobistych potrzeb i zależnie od programu, z którego akurat korzystam. Ponadto, w przypadku dwóch przycisków bocznych umieszczonych poniżej rolki, skrypt pozwala na przypisanie akcji: przytrzymaj / zakończ przytrzymanie oraz dwukrotne kliknięcie. W przypadku trzeciego przycisku bocznego zaimplementowałem pojedyncze i dwukrotne kliknięcie (bez przytrzymania). Przechylenie rolki bocznej wywołuje akcję w postaci pojedynczego kliknięcia (brak przytrzymania i bez dwukliku).
Możesz użyć dowolnej myszki – przy czym, jeżeli efekty naciśnięcia dodatkowych klawiszy są inne niż w mojej myszce, to przerobienia wymaga mechanizm interpretacji naciśnięcia tychże klawiszy, czyli wymagane jest grzebanie w skrypcie, nie wystarczy jedynie zmiana pliku konfiguracyjnego. Oczywiście możesz to zrobić, bo załączam źródła skryptu, ale jest to bardziej kłopotliwe.
Intepretacja odchyleń rolki oraz kliknięcia 3-go bocznego przycisku
Sprawa jest tutaj nieco skomplikowana, niż w przypadku rolki i przycisków pod rolką, ponieważ myszka, po naciśnięciu 3-go bocznego przycisku oraz po odchyleniu rolki, zachowuje się jak klawiatura. Zatem, by poprawnie zinterpretować powyższe zdarzenia, skrypt realizuje, co następuje:
- “na twardo” blokuje zdarzenia klawiatury, jakie mogą być wywołane przez mysz, niezależnie z jakiego źródła pochodzą (np. Win+D, czyli “pokaż pulpit”),
- równolegle nasłuchuje strumienia surowych danych z klawiatury (RAW),
- jeżeli pojawią naciśnięcia klawiszy, to skrypt (dzięki podanemu jawnie ID) jest w stanie odróżnić klawiaturę od “udawanej klawiatury” czyli myszki,
- kiedy pojawi się klawisz klawiatury (tak naprawdę myszy), to jest intepretowany jako zaprogramowane zdarzenie, natomiast jeżeli skrypt wykryje, że sekwencja pochodzi od klawiatury, to “wstrzykuje” do Windows wirtualne (wtórne) naciśnięcie zablokowanej globalnie sekwencji klawiszy, np. wspomniane Win+D, tak jakby przeszła ona przez filtr – dla użytkownika to działanie jest niezauważalne.
Brak obsługi zdarzeń dwu-kliknięcia i przytrzymania dla niektórych przycisków wynika z ograniczeń myszki, czyli właśnie stąd, że w odpowiedzi na naciśnięcie niektórych przycisków, mysz wysyła kompletną sekwencję klawiszy (naciśnięcie oraz puszczenie klawiszy) bez informacji o tym, że któryś z klawiszy jest nadal przytrzymany. Z kolei, w przypadku odchylenia głównej rolki, w myszce zaimplementowana jest samopowtarzalność akcji (cykliczne Ctrl+obrót rolki, czyli powiększenie / zmniejszenie), co sprawa, że ewentualna implementacja dwukliku nie ma większego sensu (czasem wywołałby ją użytkownik, a czasami mysz – samoczynnie).
W jaki sposób skonfigurować program?
Program przyjmuje konfigurację z pliku “mouse_config.ini” który musi się znajdować w tym samym folderze, co uruchomiony skrypt. Plik konfiguracji zawiera następujące sekcje i parametry:
[ui]
show_window=0
Jeżeli parametr show_window ustawiony jest na “1”, to po starcie skryptu pokazywana jest konsola, która pozwala na sprawdzenie działania skryptu. Dużo tam się dzieje, ponieważ rejestrowane jest każde zdarzenie związane z klawiaturą i myszką. stąd też, kiedy konsola się wypełni, najprościej jest zamknąć program i uruchomić go ponownie. Ustawienie tego parametru na “0” powoduje, że konsola się nie uruchamia, a skrypt wykonuje się szybciej.
[device]
id=BT_VID_0232C2&PID_0012
hold_delay=250
doubleclick_delay=250
wind_classify_delay=10
id to identyfikator naszej myszki – rzecz absolutnie kluczowa, by skrypt poprawnie rozpoznawał naszego gryzonia. ID podane jawnie służy do odróżnienia myszki od np. touchpada laptopowego, który z perspektywy systemu operacyjnego również jest myszką. Jeżeli pole ID będzie puste, a parametr show_window=1, to po uruchomieniu skryptu dostaniemy informację o braku ID. Mimo tego skrypt się uruchomi, a kliknięcie przyciskiem myszy spowoduje pojawienie się ID tejże myszy na konsoli programu, co pozwoli Wam rozpoznać właściwe ID, po czym wpisać jego wartość do pliku konfiguracyjnego i się tym więcej nie przejmować.
hold_delay to opóźnienie w milisekundach, którego przekroczenie podczas trzymania jakiegokolwiek przycisku traktowane jest jako zdarzenie przytrzymaj / hold – ustawione empirycznie, można sobie skorygować.
doubleclick_delay to opóźnienie graniczne pomiędzy potraktowaniem dwóch kliknięć po sobie jako zdarzenie “dwukliknięcia”. Jeżeli odstęp czasowy między dwoma kliknięciami jest większy niż niniejszy parametr, to będą one intepretowane jako dwa pojedyncze kliknięcia. Parametr wpływa też na opóźnienie reakcji na pojedyncze kliknięcie.
wind_classify_delay opóźnienie pomocnicze do rozróżniania sekwencji czasowej naciśnięć przycisków Ctrl i obrotu rolki. Docelowo prawdopodobnie zniknie, ponieważ mechanizm czasowy udało się wyeliminować z obsługi 3-go przycisku bocznego, zatem z obsługi rolki też pewnie go wyeliminuję.
Przypisanie klawiszy odbywa się w notacji AutoHotKey. Przykładowe (nieintuicyjnie definiowane) klawisze w notacji AHK:
| Shift | + |
| Ctrl | ^ |
| Alt | ! |
| Win | # |
Po więcej informacji odsyłam do dokumentacji AutoHotKey, Google lub do Twojego ulubionego modelu językowego 🙂
Niniejszy plik konfiguracyjny zawiera kilka różnych przykładów użycia klawiszy oraz sekwencji klawiszy, co pozwoli rozeznać się w możliwościach skryptu.
Sekcja [general] zawiera domyślne zdarzenia myszy, które zostaną wywołane, jeżeli nie zostały nadpisane i przechwycone przez obsługę jakiegoś innego programu (w niniejszym przykładzie sekcje: FreeCad oraz Altium Designer). Wylistowane są tutaj wszystkie zdarzenia obsługiwane przez mysz – jak widać trochę tego jest. Na uwagę zasługuje XButton2_Hold i HoldRelease: przytrzymanie tego przycisku powoduje maksymalne spowolnienie kursora myszy, którego tempo ruchu wraca do normy po puszczeniu przycisku.
Ponadto skrypt zawiera przykład uruchamiania aplikacji – tutaj klawiatura ekranowa wywoływana zdarzeniem XButton2_DoubleClick.
Komentarze rozpoczynają się od średnika, ale nie mogą się one znajdować w tej samej linijce, co parametr i jego wartość – interpretacja parametru jest wówczas nieprawidłowa.
[general]
SideWheel_Left={Volume_Down}
SideWheel_Right={Volume_Up}
WheelTiltLeft_Click=^{Tab}
WheelTiltRight_Click=^+{Tab}
XButton1_Click=
XButton1_Hold=
XButton1_HoldRelease=
XButton1_DoubleClick=^0
XButton2_Click=
XButton2_Hold=precision:1
XButton2_HoldRelease=precision:restore
XButton2_DoubleClick=run:osk.exe
XButton3_Click=#d
XButton3_DoubleClick=
[app:FreeCAD.exe]
XButton1_Click=^+1
XButton1_Hold={Shift down}
XButton1_HoldRelease={Shift up}
XButton1_DoubleClick={HOME}
XButton3_Click=^{Tab}
XButton3_Click=^+{Tab}
[app:DXP.exe]
XButton1_Click=vb
XButton1_Hold=+s
XButton1_HoldRelease=
XButton1_DoubleClick=^{PgDn}
WheelTiltLeft_Click=^+{WheelUp}
WheelTiltRight_Click=^+{WheelDown}
XButton3_Click=^{Tab}
XButton3_Click=^+{Tab}
Archiwum zawiera wszystkie pliki skryptów (skrypt podzielony jest na cztery pliki wg struktury logicznej), plik konfiguracyjny INI oraz skompilowany program. Do pracy wymagany jest jedynie program + plik konfiguracyjny INI. Z kolei pliki tekstowe zawierające skrypty można sobie przerobić bądź uruchomić pod egidą AHK jeżeli ktoś tak woli (oczywiście plik konfiguracyjny również jest wówczas wymagany). Niewykluczone, że na komputerze firmowym, gdzie uruchamianie własnych aplikacji może być zablokowane, skrypt + zewnętrzne środowisko AHK będzie jedynym rozwiązaniem by skorzystać z niniejszego usprawnienia myszy.
Należy pamiętać o podaniu właściwego ID myszy, w przeciwnym razie możliwości skryptu są znacząco ograniczone. W praktyce działa wówczas tylko boczna rolka oraz boczne przyciski 1 i 2, czyli elementy traktowane natywnie, bez konieczności przemapowania zdarzeń i odróżniania klawiatury rzeczywistej od wirtualnej (czyli myszy). Jeżeli Twoja myszka posiada tylko takie udogodnienia, to ID można nie podawać, ponieważ nic ono nie zmieni.
Kiedy konsola jest aktywna, to niektóre zdarzenia takie jak dwukliknięcie czy reakcja na ruch rolką rejestrowane są opornie i w sposób opóźniony. Wynika to z czasu potrzebnego do sformatowania i wygenerowania komunikatów na konsoli. Kiedy konsola jest zamknięta, to komunikaty nie są formatowane w tle ani oczywiście wyświetlane, dlatego skrypt będzie działa responsywnie.
Jak skompilować skrypt?
Kompilacja skryptu jest możliwa za pomocą programu AHK2EXE, będący częścią środowiska AutoHotKey. Ważny jest wybór właściwego sposobu kompresji (a raczej interpretera komend skryptu do umieszczenia w EXE). Ja wybrałem taki, jak poniżej. Użycie innego może sprawić, że plik EXE się uruchomi, ale niektóre funkcjonalności będą się zachowywać niezgodnie z oczekiwaniami.

Vibe-codowanie – czy ChatGPT umie w AutoHotKey?
Otóż zadziwiająco dobrze sobie z tym radzi. Najwyraźniej język skryptowy AHK używany jest na sporą skalę, przez co istnieją zasoby do nauki LLMów, by dobrze radziły sobie one z pisaniem skryptów pod to środowisko. Nie korzystałem z pełni możliwości vibe-codowania, mianowicie nie używałem kontroli wersji, a plik readme.md był raczej skutkiem niż przyczyną poczynań w zakresie rozwoju programu przez Czata. Być może to było przyczyną paru potknięć?
Vibe-codując trzeba mieć świadomość, że Czat w każdej iteracji czyta cały kod (chyba że jawnie nakażemy mu działać inaczej) i domyślnie chętnie dogłębnie go pozmienia, przez co może istotnie zmienić architekturę programu, mimo, że prompt wystosowany w jego kierunku wyglądał niewinnie. Zakaz modyfikacji jakiegoś działającego i przetestowanego fragmentu kodu musi być wyrażony jawnie. Zdecydowanie lepiej by było pracować przyrostowo, co pewnie również jest możliwe, ale przyznam, że tego na razie nie potrafię 🙂 W moim przypadku Czat rozrzucał na prawo i lewo opóźnienia między rejestracjami zdarzeń od klawiatury i myszy, kiedy tylko uznał, że kolejność jest niedeterministyczna, nawet jeżeli nie owa niedeterministyczność nie ma realnego wpływu na powtarzalność zachowania programu, a mimo to dodane opóźnienia mają zdecydowanie ujemny wpływ na jego responsywność. Modyfikacje tego typu kwitowane były niewinnym “(…) Ponadto dodałem opóźnienia przy obsłudze Button3 (…)” na końcu podsumowania jego działań, chociaż o nic takiego nie prosiłem. Zapewniam, że przy odpowiednio wysokim poziomie skomplikowania programu, analiza kodu jest niemal niemożliwa i trzeba się oprzeć na testowaniu oraz podsumowaniach informujących czy aktualny rezultat prac pokrywa się z założeniami – co i tak robione przez nasz LLM, a zatem nadal wymaga od nas zaufania do jego podsumowań i zdolności do refactoringu kodu (z tym tez było różnie, ale najpewniej póki co nie potrafię skutecznie współpracować z Czatem w tym zakresie).
Ponadto zauważyłem, że po powiedzmy 20 iteracjach, ChatGPT traci kontekst i zaczyna rozwiązywać problemy, które już wcześniej rozwiązał, albo drepcze w kółko, co objawia się coraz częstszymi propozycjami rozwiązań problemów, którym towarzyszy generowaniem nowych, często w większej liczbie, niż te rozwiązane. Narastająca liczba tego typu irytujących pomyłek jest znakiem, że czas zakończyć bieżącą konwersację, wygenerować archiwum z podsumowaniem dotychczasowego działania i otworzyć nowe świeże okno rozmowy, co przywraca Czatowi jego uprzednią błyskotliwość.
Słowo końcowe
Niniejszy skrypt/program ma, w typowym scenariuszu użycia czyli w zakresie dostosowania zachowania przycisków myszki, możliwości wcale nieodległe od LogitechOptions, a kosztował mnie raptem jeden wieczór pracy. Przyznam, że vibe-coding wywołuje moją ekscytację, ale jednocześnie zdaję sobie sprawę, że nie mając pojęcia w jaki sposób LLM osiąga swoje programistyczne cele, trzeba go stale trzymać w ryzach, wprowadzać niewielkie poprawki na raz, zapisywać kopie zapasowe oraz wnikliwie testować i pytać “co autor (tutaj: LLM) miał na myśli”. No i nie ma mowy o profesjonalizmie tak powstałych programów ze względu na niepowtarzalność wyników. Jednakże, jakkolwiek niedoskonałe jest obecnie vibe-codowanie, tak jednak przy odrobinie wiedzy, wyczucia i cierpliwości, już teraz to naprawdę działa i spodziewam się, że w niedalekiej przyszłości taka metoda tworzenia programów może nieźle namieszać. Z tego co wiem, to zasady vibe-codowania właśnie się wyłaniają i najprawdopodobniej niebawem będzie można uzyskać w miarę powtarzalne efekty pod warunkiem stworzenia poprawnego pliku MD (markdown), w czym oczywiście również pomaga LLM.
Dwa różne skrypty AutoHotKey jak najbardziej są w stanie pracować jednocześnie, ale może się zdarzyć, że będą sobie wówczas przeszkadzać np. jeżeli jeden z nich blokuje sekwencję klawiszy, z której korzysta drugi. Przedstawiony w poprzednim artykule skrypt można ogołocić z fragmentów odpowiedzialnych za obsługę myszki (pozostawić jedynie skróty klawiaturowe) i odpalić jednocześnie (dla przypomnienia dostęp do Autostart: Windows+R -> shell:startup).
Typowy skrypt, nawet relatywnie skomplikowany jak ten tutaj, konsumuje minimalne zasoby obliczeniowe i po skompilowaniu zajmuje kilkaset kilobajtów.
AutoHotKey jest darmowe do użytku komercyjnego, zaś skompilowany program nie musi być udostępniany ze źródłami.