Page 1 of 1

Swap Mouse Buttons and Restore Mouse Button State

Posted: 07 Mar 2019, 14:15
by Olbi
I'm regularly switching the mouse between my left and right hand. For a very long time I used a tiny free utility to do that, but recently it looked like it had stopped working, so I decided to write one using AutoHotKey. When I wrote it, I found out that my script was behaving the same way as the utility and that the issue was, most likely, related to a bad driver behaviour under Windows 10 when the computer is unlocked and or wakes up from sleep.

So, I decided to update my script to correct that. Hope that it could be of use to somebody else!

All comments and suggestions are welcome!

Code: Select all

; AutoHotKey code to swap the buttons of the mouse and restore the previous state
;
; Under Windows 10, some drivers (Synaptics) might reset the mouse to right-handed when the computer is unlocked or
; awaken from suspended state.
; To correct this, two messages are being monitored by the script and the mouse buttons' state is restored after
; these events.
;
; Version: 0.1
;    Date: 2019-03-07

    ; Windows messages to monitor
    msg_WM_WTSSESSION_CHANGE = 0x2b1
    msg_WM_POWERBROADCAST    = 0x218

    ; Registry key and value to store the state of the mouse buttons
    reg_KeyName = HKEY_CURRENT_USER\SessionInformation
    reg_ValueName = LeftHandedMouse

    ; Set taskbar tray icon
    Menu, Tray, Icon, shell32.dll, 44 ; Gold star icon
    Menu, Tray, Tip, Swap Mouse Buttons
    
    ; Initialize
    ; Create an invisile window which is registered to receive WTSRegisterSessionNotification notifications (for lock and unlock messages)
    ; and RegisterPowerSettingNotification notifications (for suspend and wake-up messages)
    Gui,+LastFound
    hwnd:=WinExist()
    
    ; Register the window for WTSRegisterSessionNotification notifications in order to receive the WM_WTSSESSION_CHANGE messages
    DllCall("Wtsapi32.dll\WTSRegisterSessionNotification","UInt",hwnd,"UInt",0)
    
    ; Register the window for RegisterPowerSettingNotification notification in order to receive the WM_POWERBROADCAST messages
    ; hHandle is set to the hidden window
    ; PowerSettingGuid is set to GUID_SYSTEM_AWAYMODE
    ; Flags is 0 (DEVICE_NOTIFY_WINDOW_HANDLE) to register for notifications sent using WM_POWERBROADCAST messages
    DllCall("User32.dll\RegisterPowerSettingNotification",hwnd,"98a7f580-01f7-48aa-9c0f-44352c29e5C0",0) 

    ; Register function to recieive the WM_WTSSESSION_CHANGE messages
    OnMessage(msg_WM_WTSSESSION_CHANGE,"f_WM_Monitor")
    
    ; Register functino to receive the WM_POWERBROADCAST messages
    OnMessage(msg_WM_POWERBROADCAST,"f_WM_Monitor")
    
    ; Set the mouse to the last known state
    RegRead, buttonState, %reg_KeyName%, %reg_ValueName%
    if (ErrorLevel) ; Reistry value not found
    {
        buttonState := DllCall("user32.dll\SwapMouseButton", "UInt", 1) ; Set left-handed mouse
        if buttonState <> 0 ; If the result is non-zero, the mouse was set to left-handed before the above DLL call
        {
            buttonState := 1
        }
        RegWrite, REG_DWORD, %reg_KeyName%, %reg_ValueName%, %buttonState%
    }
    tmpInt := DllCall("user32.dll\SwapMouseButton", "UInt", buttonState)

Return


; Use F12 to swap the mouse buttons
; Show tool tip and store the status in the registry
F12::
    buttonState := DllCall("user32.dll\SwapMouseButton", "UInt", 1) ; Set left-handed mouse
    if buttonState <> 0 ; If the result is non-zero, the mouse was set to left-handed before the above DLL call
    {
        buttonState := 0
        tmpInt := DllCall("user32.dll\SwapMouseButton", "UInt", 0) ; Set right-handed mouse
        ToolTip, Right Handed
    }
    else 
    {
        buttonState := 1
        ToolTip, Left Handed
    }
    RegWrite, REG_DWORD, %reg_KeyName%, %reg_ValueName%, %buttonState%
    
    SetTimer, tRemoveToolTip, -3000 ; Whith negative period, the timer will run only once
Return


; Function to monitor the WM_WTSSESSION_CHANGE and WM_POWERBROADCAST messages
f_WM_Monitor(wParam, lParam, msg) 
{

Global reg_KeyName, reg_ValueName
Global msg_WM_WTSSESSION_CHANGE, msg_WM_POWERBROADCAST

    ; Lock or Suspend
    if ((msg = msg_WM_WTSSESSION_CHANGE and wParam = 7) or (msg = msg_WM_POWERBROADCAST and wParam = 4)) 
    {
        ; Get mouse buttons' state and store it in the registry. Required if the mouse buttons were swapped using the
        ; Control Panel and not by using the script
        buttonState := DllCall("user32.dll\SwapMouseButton", "UInt", 1) ; Note: Sets left-handed mouse
        if buttonState <> 0 ; If the result is non-zero, the mouse was set to left-handed before the above DLL call
            RegWrite, REG_DWORD, %reg_KeyName%, %reg_ValueName%, 1
        else
            RegWrite, REG_DWORD, %reg_KeyName%, %reg_ValueName%, 0
    }

    ; Unlock
    if ((msg = msg_WM_WTSSESSION_CHANGE and wParam = 8) or (msg = msg_WM_POWERBROADCAST and wParam = 7))
    {
        ; Synaptics driver and Windows 10 issues might reset the mouse buttons to right-handed mouse after unlock/wake-up
        ; The timer is to make sure that the state of the mouse buttons is set to what it was before the computer was locked/suspended
        SetTimer, tSetMouseButtons, -1000 ; With negarive period, the timer will run only once
    }
}


; Timers
tRemoveToolTip:
    ToolTip
Return


tSetMouseButtons:
    RegRead, buttonState, %reg_KeyName%, %reg_ValueName%
    tmpInt := DllCall("user32.dll\SwapMouseButton", "UInt", buttonState)
Return
P.S. The script is based on two forum posts from the archived AHK forums. I'm not sure whether I'm allowed to post links or not, so I didn't include them, but I'll gladly post the links to these posts if this is allowed!

Re: Swap Mouse Buttons and Restore Mouse Button State

Posted: 23 Sep 2022, 22:02
by ciavyn
This is great! Thank you so much!