120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
package hotkey
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
user32 = syscall.NewLazyDLL("user32.dll")
|
|
procSendInput = user32.NewProc("SendInput")
|
|
)
|
|
|
|
const (
|
|
INPUT_KEYBOARD = 1
|
|
KEYEVENTF_KEYDOWN = 0x0000
|
|
KEYEVENTF_KEYUP = 0x0002
|
|
)
|
|
|
|
type INPUT struct {
|
|
Type uint32
|
|
Ki KEYBDINPUT
|
|
}
|
|
|
|
type KEYBDINPUT struct {
|
|
WVirtKey uint16
|
|
WScan uint16
|
|
DwFlags uint32
|
|
Time uint32
|
|
DwExtra uint32
|
|
}
|
|
|
|
// Виртуальные коды клавиш
|
|
var keyCodes = map[string]uint16{
|
|
"A": 0x41, "B": 0x42, "C": 0x43, "D": 0x44, "E": 0x45, "F": 0x46, "G": 0x47, "H": 0x48,
|
|
"I": 0x49, "J": 0x4A, "K": 0x4B, "L": 0x4C, "M": 0x4D, "N": 0x4E, "O": 0x4F, "P": 0x50,
|
|
"Q": 0x51, "R": 0x52, "S": 0x53, "T": 0x54, "U": 0x55, "V": 0x56, "W": 0x57, "X": 0x58,
|
|
"Y": 0x59, "Z": 0x5A,
|
|
"0": 0x30, "1": 0x31, "2": 0x32, "3": 0x33, "4": 0x34, "5": 0x35, "6": 0x36, "7": 0x37, "8": 0x38, "9": 0x39,
|
|
"F1": 0x70, "F2": 0x71, "F3": 0x72, "F4": 0x73, "F5": 0x74, "F6": 0x75, "F7": 0x76, "F8": 0x77,
|
|
"F9": 0x78, "F10": 0x79, "F11": 0x7A, "F12": 0x7B,
|
|
"SPACE": 0x20, "ENTER": 0x0D, "TAB": 0x09, "ESC": 0x1B,
|
|
"SHIFT": 0x10, "CTRL": 0x11, "ALT": 0x12, "WIN": 0x5B,
|
|
"LEFT": 0x25, "UP": 0x26, "RIGHT": 0x27, "DOWN": 0x28,
|
|
}
|
|
|
|
var modCodes = map[string]uint16{
|
|
"ALT": 0x12,
|
|
"CTRL": 0x11,
|
|
"SHIFT": 0x10,
|
|
"WIN": 0x5B,
|
|
}
|
|
|
|
var enabled bool = false
|
|
|
|
func Init() error {
|
|
if runtime.GOOS != "windows" {
|
|
return fmt.Errorf("hotkey emulation only supported on Windows")
|
|
}
|
|
enabled = true
|
|
return nil
|
|
}
|
|
|
|
func SetEnabled(e bool) {
|
|
enabled = e
|
|
}
|
|
|
|
func PressCombination(combination string) error {
|
|
if !enabled {
|
|
return fmt.Errorf("hotkey emulation disabled by user")
|
|
}
|
|
parts := strings.Split(combination, "+")
|
|
if len(parts) == 0 {
|
|
return fmt.Errorf("invalid combination")
|
|
}
|
|
|
|
var mods []uint16
|
|
var mainKey uint16
|
|
|
|
for _, p := range parts {
|
|
upper := strings.ToUpper(p)
|
|
if code, ok := modCodes[upper]; ok {
|
|
mods = append(mods, code)
|
|
} else if code, ok := keyCodes[upper]; ok {
|
|
mainKey = code
|
|
} else {
|
|
return fmt.Errorf("unknown key: %s", p)
|
|
}
|
|
}
|
|
if mainKey == 0 {
|
|
return fmt.Errorf("no main key found")
|
|
}
|
|
|
|
for _, mod := range mods {
|
|
pressKey(mod, true)
|
|
}
|
|
pressKey(mainKey, true)
|
|
pressKey(mainKey, false)
|
|
for i := len(mods) - 1; i >= 0; i-- {
|
|
pressKey(mods[i], false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func pressKey(vk uint16, down bool) {
|
|
var flags uint32 = KEYEVENTF_KEYDOWN
|
|
if !down {
|
|
flags = KEYEVENTF_KEYUP
|
|
}
|
|
input := INPUT{
|
|
Type: INPUT_KEYBOARD,
|
|
Ki: KEYBDINPUT{
|
|
WVirtKey: vk,
|
|
DwFlags: flags,
|
|
},
|
|
}
|
|
procSendInput.Call(1, uintptr(unsafe.Pointer(&input)), unsafe.Sizeof(input))
|
|
} |