Hi community! I see there are a lot of people trying to activate Kando menu using mouse keys. As I am one of you, I just wanted to share my solutions.
The basics
here, I use AutoHotKey (AHK) for activating Kando menu. explanatory diagram here:

Now, the simplest example can be like this:
^XButton1:: { ; binds the button (trigger).
Send("^!+{F13}") ; sends the key combo for specific Kando Menu
return
}
Common modifiers in AHK world:
Win is #
Alt is !
Ctrl is ^
Shift is +
So, ^XButton1 means Ctrl+XButton1. I try not to face conflicts of usual use cases for buttons. usually XButton1 is used to navigate backward. So, I added the need of extra modifier Ctrl, so that I can navigate back using just the XButton1, and open the menu using Ctrl+XButton1.
The inspiration
Now, is there a better solution? like some of us (including me) definitely want to push forward to do something so that we do not need to tediously press the Ctrl key to open the pie menu. I found this:
On windows I use AutoHotKey v2 with this scrip on front mouse button. The button work as usual on click, but on hold or movement it quickly opens kondo menu. It works by pressing hotkey with f18, so normally people will not be able to press it by accident. Hope it will help some users
XButton2::
{
MouseGetPos &startX, &startY
triggered := false
while GetKeyState("XButton2", "P") {
MouseGetPos ¤tX, ¤tY
distance := Sqrt((currentX - startX) ** 2 + (currentY - startY) ** 2)
if (distance > 20 || A_TimeSinceThisHotkey > 300) { ; 20px move or 300ms
triggered := true
Send "{Ctrl Down}{Alt Down}{Shift Down}{F18}"
KeyWait "XButton2" ; Wait for release
Send "{Shift Up}{Alt Up}{Ctrl Up}"
break
}
Sleep 10 ; Reduce CPU usage
}
if (!triggered)
Send "{XButton2}"
return
}
Originally posted by @SlayVict in #433
This code conditionally triggers the pie menu:
- if you press and hold the XButton2 for more than 300ms
- if you drag your mouse more than 20px distance
Otherwise, the original key is triggered. How beautiful!
Improved, yet still simple version
here is an improved, ahk v2 code.
XButton1:: {
MouseGetPos(&startX, &startY)
startTime := A_TickCount
triggered := false
while GetKeyState("XButton1", "P") {
MouseGetPos(¤tX, ¤tY)
distance := Sqrt((currentX - startX) ** 2 + (currentY - startY) ** 2)
timeSinceHotkey := A_TickCount - startTime
if (distance > 20 || timeSinceHotkey > 300) { ; 20px move or 300ms
triggered := true
MouseMove(startX, startY, 0) ; Go where the button press is started
Send("{Ctrl down}{Alt down}{Shift down}{F18}")
MouseMove(currentX, currentY, 0) ; Go back to last mouse position after triggering
KeyWait("XButton1") ; Wait for release
Send("{Shift up}{Alt up}{Ctrl up}")
break
}
Sleep(10) ; Reduce CPU usage
}
if (!triggered)
Send("{XButton1}")
return
}
This example binds the XButton1 button to trigger the Ctrl+Alt+Shift+F18 conditionally.
improvements:
- this is an AHK v2 code
- it always opens the Kando menu where the triggering key (
XButton1) press is started. it feels seamless to use when mouse is moving fast
I felt happy!
it felt natural to use... but... one problem still exists: creating new binds.
This inspired me to dig in further, as I didn't want to copy and paste this code and modify the keys inside to make it work for every new pie menu.
The ultimate one
The main purpose of this version is to:
- easily modify the bindings,
- flexibility with modifiers and some global settings
; --- Global Variables for Easy Modification ---
global sleepTime := 10 ; Sleep time in milliseconds for loop (adjust for CPU usage vs responsiveness)
global timeThresholdGlobal := 300 ; time threshold in milliseconds (adjust for sensitivity)
global triggerRadius := 20 ; Mouse movement trigger radius in pixels (adjust to your preference)
; --- Modifier Key Conversion ---
modifierSymbols := Map( ; DONT TOUCH THIS
"{Win}", "#",
"{Alt}", "!",
"{Ctrl}", "^",
"{Shift}", "+"
)
; --- Hotkey Definitions using the Modular Function ---
; --- Main things to modify ---
; Example 1: XButton1 triggers Ctrl+Alt+Shift+F18 - using global triggerRadius and timeThreshold - No modifiers for the base hotkey
ConditionalHotkey("", "XButton1", "{Ctrl down}{Alt down}{Shift down}{F18}") ; "" for no modifier keys
; Example 2: Ctrl + XButton1 triggers Ctrl+Alt+Shift+F17 - using global triggerRadius and timeThreshold
ConditionalHotkey("{Ctrl}", "XButton1", "{Ctrl down}{Alt down}{Shift down}{F17}")
; Example 3: Shift + XButton1 triggers Ctrl+Alt+Shift+F16 - using global triggerRadius and timeThreshold, but overriding triggerRadius to 25px
ConditionalHotkey("{Shift}", "XButton1", "{Ctrl down}{Alt down}{Shift down}{F16}", 25)
; Example 4: Alt + XButton1 triggers Ctrl+Alt+Shift+F15 - overriding both triggerRadius and timeThreshold
ConditionalHotkey("{Alt}", "XButton1", "{Ctrl down}{Alt down}{Shift down}{F15}", 30, 400)
; Example 5: Ctrl + Shift + Alt + XButton1 triggers Ctrl+Alt+Shift+F14
ConditionalHotkey("{Ctrl}{Shift}{Alt}", "XButton1", "{Ctrl down}{Alt down}{Shift down}{F14}")
;
;
;
; ------------ NO NEED TO TOUCH ANYTHING BELOW THIS LINE ------------
; --- ConditionalHotkey Function Definition ---
/*
Function: ConditionalHotkey
Creates a hotkey that performs an action based on mouse movement or time held, with optional modifier keys.
Parameters:
modifierKeys - (Optional) Modifier keys using AHK's modifier key syntax (e.g., "{Ctrl}{Alt}{Shift}", ""). Default: "".
triggerButton - The button to monitor (e.g., "XButton1", "LButton", "RButton").
hotkeyToSend - The hotkey combination to send when triggered (e.g., "{Ctrl down}{Alt down}{Shift down}{F18}").
distanceThreshold - (Optional) Mouse movement radius in pixels to trigger the action. Overrides global triggerRadius if provided. Default: global triggerRadius.
timeThreshold - (Optional) Time in milliseconds to trigger the action if mouse doesn't move enough. Overrides default timeThreshold if provided. Default: global sleepTime * 30.
Credits:
@SlayVict for the idea
@vfxturjo for modularization
Returns:
None
*/
ConditionalHotkey(modifierKeys, triggerButton, hotkeyToSend, distanceThreshold := -1, timeThreshold := -1) {
local distThreshold := (distanceThreshold == -1) ? triggerRadius : distanceThreshold ; Use global triggerRadius if distanceThreshold is not provided
local timeThresh := (timeThreshold == -1) ? timeThresholdGlobal : timeThreshold ; Use default timeThreshold (scaled sleepTime) if timeThreshold is not provided
local hotkeyName := "" ; Initialize hotkeyName
local remainingModifiers := modifierKeys ; Working string for modifier keys
; --- Convert modifier key strings to symbols ---
for keyString, symbol in modifierSymbols {
if (InStr(remainingModifiers, keyString)) {
hotkeyName .= symbol ; Append the symbol to hotkeyName
remainingModifiers := StrReplace(remainingModifiers, keyString) ; Remove the processed modifier string
}
}
hotkeyName .= triggerButton ; Append the base trigger button
; --- InnerHotkey Function Definition (now nested within ConditionalHotkey) ---
InnerHotkey(_) { ; Inner function to handle hotkey logic
MouseGetPos &startX, &startY ; Get initial mouse position when hotkey is pressed
startTime := A_TickCount ; Record the starting time
triggered := false ; Initialize triggered flag to false
while GetKeyState(triggerButton, "P") { ; Loop as long as the trigger button is physically held down (modifier state is handled by hotkey)
MouseGetPos ¤tX, ¤tY ; Get current mouse position in each loop iteration
distance := Sqrt((currentX - startX) ** 2 + (currentY - startY) ** 2) ; Calculate distance from starting position
timeSinceHotkey := A_TickCount - startTime ; Calculate time elapsed since hotkey press
if (distance > distThreshold || timeSinceHotkey > timeThresh) { ; Check trigger condition (distance OR time)
triggered := true ; Set triggered flag to true
MouseMove startX, startY, 0 ; Move mouse back to the starting position instantly (optional visual cue)
Send hotkeyToSend ; Send the specified hotkey combination
MouseMove currentX, currentY, 0 ; Move mouse back to the current position instantly
KeyWait triggerButton ; Wait for the trigger button to be released
Send "{Shift up}{Alt up}{Ctrl up}" ; Ensure modifier keys are released after sending hotkey - Removed this line, not needed and can cause issues
break ; Exit the while loop as action is triggered
}
Sleep sleepTime ; Pause for a short duration to reduce CPU usage (using global sleepTime)
}
if (!triggered) { ; If the loop finished without triggering the action (button released too quickly or mouse didn't move enough)
Send "{Blind}" . "{" . triggerButton . "}" ; Send the original button press (perform normal button action), also allow for modifier keys to be held down
}
return ; Exit InnerHotkey function
}
; --- End of InnerHotkey Function Definition ---
Hotkey hotkeyName, InnerHotkey ; Dynamically create hotkey with modifiers, linking to InnerHotkey function
}
It works, and this finally makes me content!
There might be scope to optimize the code; feel free to do so and share with the community!
Hi community! I see there are a lot of people trying to activate Kando menu using mouse keys. As I am one of you, I just wanted to share my solutions.
The basics
here, I use AutoHotKey (AHK) for activating Kando menu. explanatory diagram here:
Now, the simplest example can be like this:
Common modifiers in AHK world:
Winis#Altis!Ctrlis^Shiftis+So,
^XButton1meansCtrl+XButton1. I try not to face conflicts of usual use cases for buttons. usuallyXButton1is used tonavigate backward. So, I added the need of extra modifierCtrl, so that I can navigate back using just theXButton1, and open the menu usingCtrl+XButton1.The inspiration
Now, is there a better solution? like some of us (including me) definitely want to push forward to do something so that we do not need to tediously press the
Ctrlkey to open the pie menu. I found this:Originally posted by @SlayVict in #433
This code conditionally triggers the pie menu:
Otherwise, the original key is triggered. How beautiful!
Improved, yet still simple version
here is an improved, ahk v2 code.
This example binds the
XButton1button to trigger theCtrl+Alt+Shift+F18conditionally.improvements:
XButton1) press is started. it feels seamless to use when mouse is moving fastI felt happy!
it felt natural to use... but... one problem still exists: creating new binds.
This inspired me to dig in further, as I didn't want to copy and paste this code and modify the keys inside to make it work for every new pie menu.
The ultimate one
The main purpose of this version is to:
It works, and this finally makes me content!
There might be scope to optimize the code; feel free to do so and share with the community!