From 99963bc6fc4a942bfb0b614da356c75156eb4993 Mon Sep 17 00:00:00 2001 From: vcaesar Date: Mon, 31 Jan 2022 23:12:24 -0800 Subject: [PATCH] Refactor some keyboard C code to go --- examples/key/main.go | 6 +- key.go | 672 +++++++++++++++++++++++++++++++++++++++++++ key/goKey.h | 392 +------------------------ key/keycode.h | 68 +++++ key/keycode_c.h | 110 +------ keycode.go | 11 + robotgo.go | 399 +------------------------ 7 files changed, 768 insertions(+), 890 deletions(-) create mode 100644 key.go diff --git a/examples/key/main.go b/examples/key/main.go index c282891..142ae34 100644 --- a/examples/key/main.go +++ b/examples/key/main.go @@ -49,15 +49,15 @@ func keyTap() { fmt.Println("robotgo.KeyTap run error is: ", err) } - robotgo.KeyTap("h", "cmd", 12) + robotgo.KeyTap("h", "cmd") // press "i", "alt", "command" Key combination robotgo.KeyTap("i", "alt", "command") - robotgo.KeyTap("i", "alt", "cmd", 11) + robotgo.KeyTap("i", "alt", "cmd") arr := []string{"alt", "cmd"} robotgo.KeyTap("i", arr) - robotgo.KeyTap("i", arr, 12) + robotgo.KeyTap("i", arr) robotgo.KeyTap("i", "cmd", " alt", "shift") diff --git a/key.go b/key.go new file mode 100644 index 0000000..2c7dd95 --- /dev/null +++ b/key.go @@ -0,0 +1,672 @@ +// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://github.com/go-vgo/robotgo/blob/master/LICENSE +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +package robotgo + +/* +// #include "key/keycode.h" +#include "key/goKey.h" +*/ +import "C" + +import ( + "errors" + "math/rand" + "reflect" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/go-vgo/robotgo/clipboard" + "github.com/vcaesar/tt" +) + +const ( + // KeyA define key "a" + KeyA = "a" + KeyB = "b" + KeyC = "c" + KeyD = "d" + KeyE = "e" + KeyF = "f" + KeyG = "g" + KeyH = "h" + KeyI = "i" + KeyJ = "j" + KeyK = "k" + KeyL = "l" + KeyM = "m" + KeyN = "n" + KeyO = "o" + KeyP = "p" + KeyQ = "q" + KeyR = "r" + KeyS = "s" + KeyT = "t" + KeyU = "u" + KeyV = "v" + KeyW = "w" + KeyX = "x" + KeyY = "y" + KeyZ = "z" + // + Key0 = "0" + Key1 = "1" + Key2 = "2" + Key3 = "3" + Key4 = "4" + Key5 = "5" + Key6 = "6" + Key7 = "7" + Key8 = "8" + Key9 = "9" + + // Backspace backspace key string + Backspace = "backspace" + Delete = "delete" + Enter = "enter" + Tab = "tab" + Esc = "esc" + Escape = "escape" + Up = "up" // Up arrow key + Down = "down" // Down arrow key + Right = "right" // Right arrow key + Left = "left" // Left arrow key + Home = "home" + End = "end" + Pageup = "pageup" + Pagedown = "pagedown" + + F1 = "f1" + F2 = "f2" + F3 = "f3" + F4 = "f4" + F5 = "f5" + F6 = "f6" + F7 = "f7" + F8 = "f8" + F9 = "f9" + F10 = "f10" + F11 = "f11" + F12 = "f12" + F13 = "f13" + F14 = "f14" + F15 = "f15" + F16 = "f16" + F17 = "f17" + F18 = "f18" + F19 = "f19" + F20 = "f20" + F21 = "f21" + F22 = "f22" + F23 = "f23" + F24 = "f24" + + Cmd = "cmd" // is the "win" key for windows + Lcmd = "lcmd" // left command + Rcmd = "rcmd" // right command + // "command" + Alt = "alt" + Lalt = "lalt" // left alt + Ralt = "ralt" // right alt + Ctrl = "ctrl" + Lctrl = "lctrl" // left ctrl + Rctrl = "rctrl" // right ctrl + Control = "control" + Shift = "shift" + Lshift = "lshift" // left shift + Rshift = "rshift" // right shift + // "right_shift" + Capslock = "capslock" + Space = "space" + Print = "print" + Printscreen = "printscreen" // No Mac support + Insert = "insert" + Menu = "menu" // Windows only + + AudioMute = "audio_mute" // Mute the volume + AudioVolDown = "audio_vol_down" // Lower the volume + AudioVolUp = "audio_vol_up" // Increase the volume + AudioPlay = "audio_play" + AudioStop = "audio_stop" + AudioPause = "audio_pause" + AudioPrev = "audio_prev" // Previous Track + AudioNext = "audio_next" // Next Track + AudioRewind = "audio_rewind" // Linux only + AudioForward = "audio_forward" // Linux only + AudioRepeat = "audio_repeat" // Linux only + AudioRandom = "audio_random" // Linux only + + Num0 = "num0" // numpad 0 + Num1 = "num1" + Num2 = "num2" + Num3 = "num3" + Num4 = "num4" + Num5 = "num5" + Num6 = "num6" + Num7 = "num7" + Num8 = "num8" + Num9 = "num9" + NumLock = "num_lock" + + NumDecimal = "num." + NumPlus = "num+" + NumMinus = "num-" + NumMul = "num*" + NumDiv = "num/" + NumClear = "num_clear" + NumEnter = "num_enter" + NumEqual = "num_equal" + + LightsMonUp = "lights_mon_up" // Turn up monitor brightness No Windows support + LightsMonDown = "lights_mon_down" // Turn down monitor brightness No Windows support + LightsKbdToggle = "lights_kbd_toggle" // Toggle keyboard backlight on/off No Windows support + LightsKbdUp = "lights_kbd_up" // Turn up keyboard backlight brightness No Windows support + LightsKbdDown = "lights_kbd_down" +) + +// keyNames define MMKeyCode map +var keyNames = map[string]C.MMKeyCode{ + "backspace": C.K_BACKSPACE, + "delete": C.K_DELETE, + "enter": C.K_RETURN, + "tab": C.K_TAB, + "esc": C.K_ESCAPE, + "escape": C.K_ESCAPE, + "up": C.K_UP, + "down": C.K_DOWN, + "right": C.K_RIGHT, + "left": C.K_LEFT, + "home": C.K_HOME, + "end": C.K_END, + "pageup": C.K_PAGEUP, + "pagedown": C.K_PAGEDOWN, + // + "f1": C.K_F1, + "f2": C.K_F2, + "f3": C.K_F3, + "f4": C.K_F4, + "f5": C.K_F5, + "f6": C.K_F6, + "f7": C.K_F7, + "f8": C.K_F8, + "f9": C.K_F9, + "f10": C.K_F10, + "f11": C.K_F11, + "f12": C.K_F12, + "f13": C.K_F13, + "f14": C.K_F14, + "f15": C.K_F15, + "f16": C.K_F16, + "f17": C.K_F17, + "f18": C.K_F18, + "f19": C.K_F19, + "f20": C.K_F20, + "f21": C.K_F21, + "f22": C.K_F22, + "f23": C.K_F23, + "f24": C.K_F24, + // + "cmd": C.K_META, + "lcmd": C.K_LMETA, + "rcmd": C.K_RMETA, + "command": C.K_META, + "alt": C.K_ALT, + "lalt": C.K_LALT, + "ralt": C.K_RALT, + "ctrl": C.K_CONTROL, + "lctrl": C.K_LCONTROL, + "rctrl": C.K_RCONTROL, + "control": C.K_CONTROL, + "shift": C.K_SHIFT, + "lshift": C.K_LSHIFT, + "rshift": C.K_RSHIFT, + "right_shift": C.K_RSHIFT, + "capslock": C.K_CAPSLOCK, + "space": C.K_SPACE, + "print": C.K_PRINTSCREEN, + "printscreen": C.K_PRINTSCREEN, + "insert": C.K_INSERT, + "menu": C.K_MENU, + + "audio_mute": C.K_AUDIO_VOLUME_MUTE, + "audio_vol_down": C.K_AUDIO_VOLUME_DOWN, + "audio_vol_up": C.K_AUDIO_VOLUME_UP, + "audio_play": C.K_AUDIO_PLAY, + "audio_stop": C.K_AUDIO_STOP, + "audio_pause": C.K_AUDIO_PAUSE, + "audio_prev": C.K_AUDIO_PREV, + "audio_next": C.K_AUDIO_NEXT, + "audio_rewind": C.K_AUDIO_REWIND, + "audio_forward": C.K_AUDIO_FORWARD, + "audio_repeat": C.K_AUDIO_REPEAT, + "audio_random": C.K_AUDIO_RANDOM, + + "num0": C.K_NUMPAD_0, + "num1": C.K_NUMPAD_1, + "num2": C.K_NUMPAD_2, + "num3": C.K_NUMPAD_3, + "num4": C.K_NUMPAD_4, + "num5": C.K_NUMPAD_5, + "num6": C.K_NUMPAD_6, + "num7": C.K_NUMPAD_7, + "num8": C.K_NUMPAD_8, + "num9": C.K_NUMPAD_9, + "num_lock": C.K_NUMPAD_LOCK, + + // todo: removed + "numpad_0": C.K_NUMPAD_0, + "numpad_1": C.K_NUMPAD_1, + "numpad_2": C.K_NUMPAD_2, + "numpad_3": C.K_NUMPAD_3, + "numpad_4": C.K_NUMPAD_4, + "numpad_5": C.K_NUMPAD_5, + "numpad_6": C.K_NUMPAD_6, + "numpad_7": C.K_NUMPAD_7, + "numpad_8": C.K_NUMPAD_8, + "numpad_9": C.K_NUMPAD_9, + "numpad_lock": C.K_NUMPAD_LOCK, + + "num.": C.K_NUMPAD_DECIMAL, + "num+": C.K_NUMPAD_PLUS, + "num-": C.K_NUMPAD_MINUS, + "num*": C.K_NUMPAD_MUL, + "num/": C.K_NUMPAD_DIV, + "num_clear": C.K_NUMPAD_CLEAR, + "num_enter": C.K_NUMPAD_ENTER, + "num_equal": C.K_NUMPAD_EQUAL, + + "lights_mon_up": C.K_LIGHTS_MON_UP, + "lights_mon_down": C.K_LIGHTS_MON_DOWN, + "lights_kbd_toggle": C.K_LIGHTS_KBD_TOGGLE, + "lights_kbd_up": C.K_LIGHTS_KBD_UP, + "lights_kbd_down": C.K_LIGHTS_KBD_DOWN, + + // { NULL: C.K_NOT_A_KEY } +} + +func tapKeyCode(code C.MMKeyCode, flags C.MMKeyFlags) { + C.toggleKeyCode(code, true, flags) + MilliSleep(5) + C.toggleKeyCode(code, false, flags) +} + +var keyErr = errors.New("Invalid key flag specified.") + +func checkKeyCodes(k string) (key C.MMKeyCode, err error) { + if k == "" { + return + } + + if len(k) == 1 { + val := C.CString(k) + key = C.keyCodeForChar(*val) + C.free(unsafe.Pointer(val)) + if key == C.K_NOT_A_KEY { + err = keyErr + return + } + return + } + + if v, ok := keyNames[k]; ok { + key = v + if key == C.K_NOT_A_KEY { + err = keyErr + return + } + } + return +} + +func checkKeyFlags(f string) (flags C.MMKeyFlags) { + m := map[string]C.MMKeyFlags{ + "alt": C.MOD_ALT, + "ralt": C.MOD_ALT, + "lalt": C.MOD_ALT, + "cmd": C.MOD_META, + "rcmd": C.MOD_META, + "lcmd": C.MOD_META, + "ctrl": C.MOD_CONTROL, + "rctrl": C.MOD_CONTROL, + "lctrl": C.MOD_CONTROL, + "shift": C.MOD_SHIFT, + "rshift": C.MOD_SHIFT, + "lshift": C.MOD_SHIFT, + "none": C.MOD_NONE, + } + + if v, ok := m[f]; ok { + return v + } + return +} + +func getFlagsFromValue(value []string) (flags C.MMKeyFlags) { + if len(value) <= 0 { + return + } + + for i := 0; i < len(value); i++ { + var f C.MMKeyFlags = C.MOD_NONE + + f = checkKeyFlags(value[i]) + flags = (C.MMKeyFlags)(flags | f) + } + + return +} + +func keyTaps(k string, keyArr []string) error { + flags := getFlagsFromValue(keyArr) + key, err := checkKeyCodes(k) + if err != nil { + return err + } + + tapKeyCode(key, flags) + MilliSleep(KeySleep) + return nil +} + +func keyToggles(k string, keyArr []string) error { + down := false + if keyArr[0] == "down" { + down = true + } + + flags := getFlagsFromValue(keyArr) + key, err := checkKeyCodes(k) + if err != nil { + return err + } + + C.toggleKeyCode(key, C.bool(down), flags) + MilliSleep(KeySleep) + return nil +} + +/* + __ ___ ___________ ____ .______ ______ ___ .______ _______ +| |/ / | ____\ \ / / | _ \ / __ \ / \ | _ \ | \ +| ' / | |__ \ \/ / | |_) | | | | | / ^ \ | |_) | | .--. | +| < | __| \_ _/ | _ < | | | | / /_\ \ | / | | | | +| . \ | |____ | | | |_) | | `--' | / _____ \ | |\ \----.| '--' | +|__|\__\ |_______| |__| |______/ \______/ /__/ \__\ | _| `._____||_______/ + +*/ + +// ToInterfaces []string to []interface{} +func ToInterfaces(fields []string) []interface{} { + res := make([]interface{}, 0, len(fields)) + for _, s := range fields { + res = append(res, s) + } + return res +} + +// ToStrings []interface{} to []string +func ToStrings(fields []interface{}) []string { + res := make([]string, 0, len(fields)) + for _, s := range fields { + res = append(res, s.(string)) + } + return res +} + +func toErr(str *C.char) error { + gstr := C.GoString(str) + if gstr == "" { + return nil + } + return errors.New(gstr) +} + +// KeyTap tap the keyboard code; +// +// See keys: +// https://github.com/go-vgo/robotgo/blob/master/docs/keys.md +// +// Examples: +// robotgo.KeySleep = 100 // 100 millisecond +// robotgo.KeyTap("a") +// robotgo.KeyTap("i", "alt", "command") +// +// arr := []string{"alt", "command"} +// robotgo.KeyTap("i", arr) +// +func KeyTap(tapKey string, args ...interface{}) error { + var keyArr []string + + tapKey = strings.ToLower(tapKey) + if _, ok := Special[tapKey]; ok { + tapKey = Special[tapKey] + if len(args) <= 0 { + args = append(args, "shift") + } + } + + if len(args) > 0 { + if reflect.TypeOf(args[0]) == reflect.TypeOf(keyArr) { + keyArr = args[0].([]string) + } else { + keyArr = ToStrings(args) + } + } + + return keyTaps(tapKey, keyArr) +} + +// KeyToggle toggle the keyboard, if there not have args default is "down" +// +// See keys: +// https://github.com/go-vgo/robotgo/blob/master/docs/keys.md +// +// Examples: +// robotgo.KeyToggle("a") +// robotgo.KeyToggle("a", "up") +// +// robotgo.KeyToggle("a", "up", "alt", "cmd") +// +func KeyToggle(key string, args ...string) error { + if len(args) <= 0 { + args = append(args, "down") + } + + key = strings.ToLower(key) + if _, ok := Special[key]; ok { + key = Special[key] + if len(args) <= 1 { + args = append(args, "shift") + } + } + + return keyToggles(key, args) +} + +// KeyPress press key string +func KeyPress(key string) error { + err := KeyDown(key) + if err != nil { + return err + } + + MilliSleep(1 + rand.Intn(3)) + return KeyUp(key) +} + +// KeyDown press down a key +func KeyDown(key string) error { + return KeyToggle(key) +} + +// KeyUp press up a key +func KeyUp(key string) error { + return KeyToggle(key, "up") +} + +// ReadAll read string from clipboard +func ReadAll() (string, error) { + return clipboard.ReadAll() +} + +// WriteAll write string to clipboard +func WriteAll(text string) error { + return clipboard.WriteAll(text) +} + +// CharCodeAt char code at utf-8 +func CharCodeAt(s string, n int) rune { + i := 0 + for _, r := range s { + if i == n { + return r + } + i++ + } + + return 0 +} + +// UnicodeType tap uint32 unicode +func UnicodeType(str uint32) { + cstr := C.uint(str) + C.unicodeType(cstr) +} + +// ToUC trans string to unicode []string +func ToUC(text string) []string { + var uc []string + + for _, r := range text { + textQ := strconv.QuoteToASCII(string(r)) + textUnQ := textQ[1 : len(textQ)-1] + + st := strings.Replace(textUnQ, "\\u", "U", -1) + if st == "\\\\" { + st = "\\" + } + if st == `\"` { + st = `"` + } + uc = append(uc, st) + } + + return uc +} + +func inputUTF(str string) { + cstr := C.CString(str) + C.input_utf(cstr) + + C.free(unsafe.Pointer(cstr)) +} + +// TypeStr send a string, support UTF-8 +// +// robotgo.TypeStr(string: The string to send, float64: microsleep time, x11 option) +// +// Examples: +// robotgo.TypeStr("abc@123, hi, こんにちは") +// +func TypeStr(str string, args ...float64) { + var tm, tm1 = 0.0, 7.0 + + if len(args) > 0 { + tm = args[0] + } + if len(args) > 1 { + tm1 = args[1] + } + + if runtime.GOOS == "linux" { + strUc := ToUC(str) + for i := 0; i < len(strUc); i++ { + ru := []rune(strUc[i]) + if len(ru) <= 1 { + ustr := uint32(CharCodeAt(strUc[i], 0)) + UnicodeType(ustr) + } else { + inputUTF(strUc[i]) + MicroSleep(tm1) + } + + MicroSleep(tm) + } + return + } + + for i := 0; i < len([]rune(str)); i++ { + ustr := uint32(CharCodeAt(str, i)) + UnicodeType(ustr) + // if len(args) > 0 { + MicroSleep(tm) + // } + } + MilliSleep(KeySleep) +} + +// PasteStr paste a string, support UTF-8, +// write the string to clipboard and tap `cmd + v` +func PasteStr(str string) error { + err := clipboard.WriteAll(str) + if err != nil { + return err + } + + if runtime.GOOS == "darwin" { + return KeyTap("v", "command") + } + + return KeyTap("v", "control") +} + +// TypeStrDelay type string delayed +func TypeStrDelay(str string, delay int) { + TypeStr(str) + Sleep(delay) +} + +// Deprecated: use the TypeStr(), +// +// TypeStringDelayed type string delayed, Wno-deprecated +// +// This function will be removed in version v1.0.0 +func TypeStringDelayed(str string, delay int) { + tt.Drop("TypeStringDelayed", "TypeStrDelay") + TypeStrDelay(str, delay) +} + +// SetKeyDelay set keyboard delay +func SetKeyDelay(delay int) { + C.set_keyboard_delay(C.size_t(delay)) +} + +// Deprecated: use the SetKeyDelay(), +// +// SetKeyboardDelay set keyboard delay, Wno-deprecated, +// +// This function will be removed in version v1.0.0 +func SetKeyboardDelay(delay int) { + tt.Drop("SetKeyboardDelay", "SetKeyDelay") + SetKeyDelay(delay) +} + +// SetDelay set the key and mouse delay +func SetDelay(d ...int) { + v := 10 + if len(d) > 0 { + v = d[0] + } + + SetMouseDelay(v) + SetKeyDelay(v) +} diff --git a/key/goKey.h b/key/goKey.h index b1f79c6..2c2021c 100644 --- a/key/goKey.h +++ b/key/goKey.h @@ -1,397 +1,7 @@ -// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// https://github.com/go-vgo/robotgo/blob/master/LICENSE -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// Copyright 2016 The go-vgo Project Developers. #include "../base/types.h" // #include "keycode.h" // #include "keypress.h" #include "keypress_c.h" #include "keycode_c.h" - - -int keyboardDelay = 0; - -struct KeyNames{ - const char* name; - MMKeyCode key; -}key_names[] = { - { "backspace", K_BACKSPACE }, - { "delete", K_DELETE }, - { "enter", K_RETURN }, - { "tab", K_TAB }, - { "esc", K_ESCAPE }, - { "escape", K_ESCAPE }, - { "up", K_UP }, - { "down", K_DOWN }, - { "right", K_RIGHT }, - { "left", K_LEFT }, - { "home", K_HOME }, - { "end", K_END }, - { "pageup", K_PAGEUP }, - { "pagedown", K_PAGEDOWN }, - // - { "f1", K_F1 }, - { "f2", K_F2 }, - { "f3", K_F3 }, - { "f4", K_F4 }, - { "f5", K_F5 }, - { "f6", K_F6 }, - { "f7", K_F7 }, - { "f8", K_F8 }, - { "f9", K_F9 }, - { "f10", K_F10 }, - { "f11", K_F11 }, - { "f12", K_F12 }, - { "f13", K_F13 }, - { "f14", K_F14 }, - { "f15", K_F15 }, - { "f16", K_F16 }, - { "f17", K_F17 }, - { "f18", K_F18 }, - { "f19", K_F19 }, - { "f20", K_F20 }, - { "f21", K_F21 }, - { "f22", K_F22 }, - { "f23", K_F23 }, - { "f24", K_F24 }, - // - { "cmd", K_META }, - { "lcmd", K_LMETA }, - { "rcmd", K_RMETA }, - { "command", K_META }, - { "alt", K_ALT }, - { "lalt", K_LALT }, - { "ralt", K_RALT }, - { "ctrl", K_CONTROL }, - { "lctrl", K_LCONTROL }, - { "rctrl", K_RCONTROL }, - { "control", K_CONTROL }, - { "shift", K_SHIFT }, - { "lshift", K_LSHIFT }, - { "rshift", K_RSHIFT }, - { "right_shift", K_RSHIFT }, - { "capslock", K_CAPSLOCK }, - { "space", K_SPACE }, - { "print", K_PRINTSCREEN }, - { "printscreen", K_PRINTSCREEN }, - { "insert", K_INSERT }, - { "menu", K_MENU }, - - { "audio_mute", K_AUDIO_VOLUME_MUTE }, - { "audio_vol_down", K_AUDIO_VOLUME_DOWN }, - { "audio_vol_up", K_AUDIO_VOLUME_UP }, - { "audio_play", K_AUDIO_PLAY }, - { "audio_stop", K_AUDIO_STOP }, - { "audio_pause", K_AUDIO_PAUSE }, - { "audio_prev", K_AUDIO_PREV }, - { "audio_next", K_AUDIO_NEXT }, - { "audio_rewind", K_AUDIO_REWIND }, - { "audio_forward", K_AUDIO_FORWARD }, - { "audio_repeat", K_AUDIO_REPEAT }, - { "audio_random", K_AUDIO_RANDOM }, - - { "num0", K_NUMPAD_0 }, - { "num1", K_NUMPAD_1 }, - { "num2", K_NUMPAD_2 }, - { "num3", K_NUMPAD_3 }, - { "num4", K_NUMPAD_4 }, - { "num5", K_NUMPAD_5 }, - { "num6", K_NUMPAD_6 }, - { "num7", K_NUMPAD_7 }, - { "num8", K_NUMPAD_8 }, - { "num9", K_NUMPAD_9 }, - { "num_lock", K_NUMPAD_LOCK }, - - // todo: removed - { "numpad_0", K_NUMPAD_0 }, - { "numpad_1", K_NUMPAD_1 }, - { "numpad_2", K_NUMPAD_2 }, - { "numpad_3", K_NUMPAD_3 }, - { "numpad_4", K_NUMPAD_4 }, - { "numpad_5", K_NUMPAD_5 }, - { "numpad_6", K_NUMPAD_6 }, - { "numpad_7", K_NUMPAD_7 }, - { "numpad_8", K_NUMPAD_8 }, - { "numpad_9", K_NUMPAD_9 }, - { "numpad_lock", K_NUMPAD_LOCK }, - - {"num.", K_NUMPAD_DECIMAL }, - {"num+", K_NUMPAD_PLUS }, - {"num-", K_NUMPAD_MINUS }, - {"num*", K_NUMPAD_MUL }, - {"num/", K_NUMPAD_DIV }, - {"num_clear", K_NUMPAD_CLEAR }, - {"num_enter", K_NUMPAD_ENTER }, - {"num_equal", K_NUMPAD_EQUAL }, - - { "lights_mon_up", K_LIGHTS_MON_UP }, - { "lights_mon_down", K_LIGHTS_MON_DOWN }, - { "lights_kbd_toggle",K_LIGHTS_KBD_TOGGLE }, - { "lights_kbd_up", K_LIGHTS_KBD_UP }, - { "lights_kbd_down", K_LIGHTS_KBD_DOWN }, - - { NULL, K_NOT_A_KEY } /* end marker */ -}; - -int CheckKeyCodes(char* k, MMKeyCode *key){ - if (!key) { return -1; } - - if (strlen(k) == 1) { - *key = keyCodeForChar(*k); - if (*key == K_NOT_A_KEY) { - return -2; - } - - return 0; - } - - *key = K_NOT_A_KEY; - - struct KeyNames* kn = key_names; - while (kn->name) { - if (strcmp(k, kn->name) == 0){ - *key = kn->key; - break; - } - kn++; - } - - if (*key == K_NOT_A_KEY) { - return -2; - } - - return 0; -} - -int CheckKeyFlags(char* f, MMKeyFlags* flags){ - if (!flags) { return -1; } - - if (strcmp(f, "alt") == 0 || strcmp(f, "ralt") == 0 || - strcmp(f, "lalt") == 0 ) { - *flags = MOD_ALT; - } - else if(strcmp(f, "command") == 0 || strcmp(f, "cmd") == 0 || - strcmp(f, "rcmd") == 0 || strcmp(f, "lcmd") == 0 ) { - *flags = MOD_META; - } - else if(strcmp(f, "control") == 0 || strcmp(f, "ctrl") == 0 || - strcmp(f, "rctrl") == 0 || strcmp(f, "lctrl") == 0 ) { - *flags = MOD_CONTROL; - } - else if(strcmp(f, "shift") == 0 || strcmp(f, "right_shift") == 0 || - strcmp(f, "rshift") == 0 || strcmp(f, "lshift") == 0 ) { - *flags = MOD_SHIFT; - } - else if(strcmp(f, "none") == 0 ) { - *flags = (MMKeyFlags) MOD_NONE; - } else { - return -2; - } - - return 0; -} - -int GetFlagsFromValue(char* value[], MMKeyFlags* flags, int num){ - if (!flags) {return -1;} - - int i; - for ( i= 0; i ', XK_greater}, + {'=', XK_equal}, + {'@', XK_at}, + {':', XK_colon}, + {';', XK_semicolon}, + {'{', XK_braceleft}, + {'}', XK_braceright}, + {'|', XK_bar}, + {'^', XK_asciicircum}, + {'(', XK_parenleft}, + {')', XK_parenright}, + {' ', XK_space}, + {'/', XK_slash}, + {'\\', XK_backslash}, + {'`', XK_grave}, + {'"', XK_quoteright}, + {'\'', XK_quotedbl}, + {'\t', XK_Tab}, + {'\n', XK_Return} +}; + #elif defined(IS_WINDOWS) enum _MMKeyCode { @@ -308,6 +369,13 @@ enum _MMKeyCode { K_NUMPAD_CLEAR = K_NOT_A_KEY, K_NUMPAD_ENTER = VK_RETURN, K_NUMPAD_EQUAL = VK_OEM_PLUS, + K_NUMPAD_LB = VK_OEM_4, + K_NUMPAD_RB = VK_OEM_6, + K_Backslash = VK_OEM_5, + K_Semicolon = VK_OEM_1, + K_Quote = VK_OEM_7, + K_Slash = VK_OEM_2, + K_Grave = VK_OEM_3, K_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE, K_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN, diff --git a/key/keycode_c.h b/key/keycode_c.h index 97c5a50..6336ae7 100644 --- a/key/keycode_c.h +++ b/key/keycode_c.h @@ -8,61 +8,9 @@ * Ownership follows the Create Rule; that is, it is the caller's * responsibility to release the returned object. */ CFStringRef createStringForKey(CGKeyCode keyCode); - - MMKeyCode keyCodeForCharFallBack(const char c); -#elif defined(USE_X11) -/* - * Structs to store key mappings not handled by XStringToKeysym() on some - * Linux systems. - */ - -struct XSpecialCharacterMapping { - char name; - MMKeyCode code; -}; - -struct XSpecialCharacterMapping XSpecialCharacterTable[] = { - {'~', XK_asciitilde}, - {'_', XK_underscore}, - {'[', XK_bracketleft}, - {']', XK_bracketright}, - {'!', XK_exclam}, - {'#', XK_numbersign}, - {'$', XK_dollar}, - {'%', XK_percent}, - {'&', XK_ampersand}, - {'*', XK_asterisk}, - {'+', XK_plus}, - {',', XK_comma}, - {'-', XK_minus}, - {'.', XK_period}, - {'?', XK_question}, - {'<', XK_less}, - {'>', XK_greater}, - {'=', XK_equal}, - {'@', XK_at}, - {':', XK_colon}, - {';', XK_semicolon}, - {'{', XK_braceleft}, - {'}', XK_braceright}, - {'|', XK_bar}, - {'^', XK_asciicircum}, - {'(', XK_parenleft}, - {')', XK_parenright}, - {' ', XK_space}, - {'/', XK_slash}, - {'\\', XK_backslash}, - {'`', XK_grave}, - {'"', XK_quoteright}, - {'\'', XK_quotedbl}, - // {'\'', XK_quoteright}, - {'\t', XK_Tab}, - {'\n', XK_Return} -}; - #endif -MMKeyCode keyCodeForChar(const char c){ +MMKeyCode keyCodeForChar(const char c) { #if defined(IS_MACOSX) /* OS X does not appear to have a built-in function for this, so instead we * have to write our own. */ @@ -89,20 +37,13 @@ MMKeyCode keyCodeForChar(const char c){ } charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); - /* Our values may be NULL (0), so we need to use this function. */ - if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, - (const void **)&code)) { + if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, (const void **)&code)) { code = UINT16_MAX; /* Error */ } - CFRelease(charStr); // TISGetInputSourceProperty may return nil so we need fallback - if (code == UINT16_MAX) { - code = keyCodeForCharFallBack(c); - } - if (code == UINT16_MAX) { return K_NOT_A_KEY; } @@ -142,7 +83,6 @@ MMKeyCode keyCodeForChar(const char c){ if (code == NoSymbol) { return K_NOT_A_KEY; } - return code; #endif } @@ -167,50 +107,4 @@ CFStringRef createStringForKey(CGKeyCode keyCode){ return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); } - -MMKeyCode keyCodeForCharFallBack(const char c) { - switch (c) { - case 'A': return kVK_ANSI_A; - case 'B': return kVK_ANSI_B; - case 'C': return kVK_ANSI_C; - case 'D': return kVK_ANSI_D; - case 'E': return kVK_ANSI_E; - case 'F': return kVK_ANSI_F; - case 'G': return kVK_ANSI_G; - case 'H': return kVK_ANSI_H; - case 'I': return kVK_ANSI_I; - case 'J': return kVK_ANSI_J; - case 'K': return kVK_ANSI_K; - case 'L': return kVK_ANSI_L; - case 'M': return kVK_ANSI_M; - case 'N': return kVK_ANSI_N; - case 'O': return kVK_ANSI_O; - case 'P': return kVK_ANSI_P; - case 'Q': return kVK_ANSI_Q; - case 'R': return kVK_ANSI_R; - case 'S': return kVK_ANSI_S; - case 'T': return kVK_ANSI_T; - case 'U': return kVK_ANSI_U; - case 'V': return kVK_ANSI_V; - case 'W': return kVK_ANSI_W; - case 'X': return kVK_ANSI_X; - case 'Y': return kVK_ANSI_Y; - case 'Z': return kVK_ANSI_Z; - - - case '0': return kVK_ANSI_0; - case '1': return kVK_ANSI_1; - case '2': return kVK_ANSI_2; - case '3': return kVK_ANSI_3; - case '4': return kVK_ANSI_4; - case '5': return kVK_ANSI_5; - case '6': return kVK_ANSI_6; - case '7': return kVK_ANSI_7; - case '8': return kVK_ANSI_8; - case '9': return kVK_ANSI_9; - } - - return K_NOT_A_KEY; -} - #endif diff --git a/keycode.go b/keycode.go index bd4108f..4b9e934 100644 --- a/keycode.go +++ b/keycode.go @@ -19,6 +19,17 @@ type uMap map[string]uint16 // MouseMap robotgo hook mouse's code map var MouseMap = keycode.MouseMap +const ( + // Mleft mouse left button + Mleft = "left" + Mright = "right" + Center = "center" + WheelDown = "wheelDown" + WheelUp = "wheelUp" + WheelLeft = "wheelLeft" + WheelRight = "wheelRight" +) + // Keycode robotgo hook key's code map var Keycode = keycode.Keycode diff --git a/robotgo.go b/robotgo.go index 8e7c830..6f17866 100644 --- a/robotgo.go +++ b/robotgo.go @@ -33,16 +33,12 @@ package robotgo #cgo darwin LDFLAGS: -framework Carbon -framework CoreFoundation #cgo linux CFLAGS: -I/usr/src -#cgo linux LDFLAGS: -L/usr/src -lX11 -lXtst -lm -// #cgo linux LDFLAGS: -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11 +#cgo linux LDFLAGS: -L/usr/src -lm -lX11 -lXtst -// #cgo windows LDFLAGS: -lgdi32 -luser32 -lpng -lz #cgo windows LDFLAGS: -lgdi32 -luser32 // -// #include #include "screen/goScreen.h" #include "mouse/goMouse.h" -#include "key/goKey.h" #include "window/goWindow.h" */ import "C" @@ -50,19 +46,10 @@ import "C" import ( "errors" "image" - - // "os" - "reflect" "runtime" - "strconv" - "strings" "time" "unsafe" - // "syscall" - "math/rand" - - "github.com/go-vgo/robotgo/clipboard" "github.com/vcaesar/tt" ) @@ -142,7 +129,7 @@ func Sleep(tm int) { time.Sleep(time.Duration(tm) * time.Second) } -// MicroSleep time C.microsleep(tm) +// MicroSleep time C.microsleep(tm), use the MilliSleep func MicroSleep(tm float64) { C.microsleep(C.double(tm)) } @@ -351,6 +338,13 @@ func FreeBitmap(bitmap CBitmap) { C.bitmap_dealloc(C.MMBitmapRef(bitmap)) } +// FreeBitmapArr free and dealloc the C bitmap array +func FreeBitmapArr(bit ...CBitmap) { + for i := 0; i < len(bit); i++ { + FreeBitmap(bit[i]) + } +} + // ToMMBitmapRef trans CBitmap to C.MMBitmapRef func ToMMBitmapRef(bit CBitmap) C.MMBitmapRef { return C.MMBitmapRef(bit) @@ -396,14 +390,12 @@ func ToRGBA(bit CBitmap) *image.RGBA { } // SetXDisplayName set XDisplay name (Linux) -func SetXDisplayName(name string) string { +func SetXDisplayName(name string) error { cname := C.CString(name) str := C.set_XDisplay_name(cname) - - gstr := C.GoString(str) C.free(unsafe.Pointer(cname)) - return gstr + return toErr(str) } // GetXDisplayName get XDisplay name (Linux) @@ -823,375 +815,6 @@ func SetMouseDelay(delay int) { C.set_mouse_delay(cdelay) } -/* - __ ___ ___________ ____ .______ ______ ___ .______ _______ -| |/ / | ____\ \ / / | _ \ / __ \ / \ | _ \ | \ -| ' / | |__ \ \/ / | |_) | | | | | / ^ \ | |_) | | .--. | -| < | __| \_ _/ | _ < | | | | / /_\ \ | / | | | | -| . \ | |____ | | | |_) | | `--' | / _____ \ | |\ \----.| '--' | -|__|\__\ |_______| |__| |______/ \______/ /__/ \__\ | _| `._____||_______/ - -*/ - -func toErr(str *C.char) error { - gstr := C.GoString(str) - if gstr == "" { - return nil - } - return errors.New(gstr) -} - -// KeyTap tap the keyboard code; -// -// See keys: -// https://github.com/go-vgo/robotgo/blob/master/docs/keys.md -// -// Examples: -// robotgo.KeySleep = 100 // 100 millisecond -// robotgo.KeyTap("a") -// robotgo.KeyTap("i", "alt", "command") -// -// arr := []string{"alt", "command"} -// robotgo.KeyTap("i", arr) -// -func KeyTap(tapKey string, args ...interface{}) error { - var ( - akey string - keyT = "null" - keyArr []string - num int - keyDelay int // This is legacy and drop option, use robotgo.KeySleep - ) - - if _, ok := Special[tapKey]; ok { - tapKey = Special[tapKey] - if len(args) <= 0 { - args = append(args, "shift") - } - } - - // var ckeyArr []*C.char - ckeyArr := make([](*C.char), 0) - // zkey := C.CString(args[0]) - zkey := C.CString(tapKey) - defer C.free(unsafe.Pointer(zkey)) - - // args not key delay - if len(args) > 2 && (reflect.TypeOf(args[2]) != reflect.TypeOf(num)) { - num = len(args) - for i := 0; i < num; i++ { - s := args[i].(string) - ckeyArr = append(ckeyArr, (*C.char)(unsafe.Pointer(C.CString(s)))) - } - - str := C.key_Taps(zkey, - (**C.char)(unsafe.Pointer(&ckeyArr[0])), C.int(num), 0) - MilliSleep(KeySleep) - return toErr(str) - } - - // key delay - if len(args) > 0 { - if reflect.TypeOf(args[0]) == reflect.TypeOf(keyArr) { - - keyArr = args[0].([]string) - num = len(keyArr) - for i := 0; i < num; i++ { - ckeyArr = append(ckeyArr, (*C.char)(unsafe.Pointer(C.CString(keyArr[i])))) - } - - if len(args) > 1 { - keyDelay = args[1].(int) - } - } else { - akey = args[0].(string) - - if len(args) > 1 { - if reflect.TypeOf(args[1]) == reflect.TypeOf(akey) { - keyT = args[1].(string) - if len(args) > 2 { - keyDelay = args[2].(int) - } - } else { - keyDelay = args[1].(int) - } - } - } - - } else { - akey = "null" - keyArr = []string{"null"} - } - - if akey == "" && len(keyArr) != 0 { - str := C.key_Taps(zkey, (**C.char)(unsafe.Pointer(&ckeyArr[0])), - C.int(num), C.int(keyDelay)) - - MilliSleep(KeySleep) - return toErr(str) - } - - amod := C.CString(akey) - amodt := C.CString(keyT) - str := C.key_tap(zkey, amod, amodt, C.int(keyDelay)) - - C.free(unsafe.Pointer(amod)) - C.free(unsafe.Pointer(amodt)) - - MilliSleep(KeySleep) - return toErr(str) -} - -// KeyToggle toggle the keyboard, if there not have args default is "down" -// -// See keys: -// https://github.com/go-vgo/robotgo/blob/master/docs/keys.md -// -// Examples: -// robotgo.KeyToggle("a") -// robotgo.KeyToggle("a", "up") -// -// robotgo.KeyToggle("a", "up", "alt", "cmd") -// -func KeyToggle(key string, args ...string) error { - if len(args) <= 0 { - args = append(args, "down") - } - - if _, ok := Special[key]; ok { - key = Special[key] - if len(args) <= 1 { - args = append(args, "shift") - } - } - - ckey := C.CString(key) - defer C.free(unsafe.Pointer(ckey)) - - ckeyArr := make([](*C.char), 0) - if len(args) > 3 { - num := len(args) - for i := 0; i < num; i++ { - ckeyArr = append(ckeyArr, (*C.char)(unsafe.Pointer(C.CString(args[i])))) - } - - str := C.key_Toggles(ckey, (**C.char)(unsafe.Pointer(&ckeyArr[0])), C.int(num)) - MilliSleep(KeySleep) - return toErr(str) - } - - // use key_toggle() - var ( - down, mKey, mKeyT = "null", "null", "null" - // keyDelay = 10 - ) - - if len(args) > 0 { - down = args[0] - - if len(args) > 1 { - mKey = args[1] - if len(args) > 2 { - mKeyT = args[2] - } - } - } - - cdown := C.CString(down) - cmKey := C.CString(mKey) - cmKeyT := C.CString(mKeyT) - - str := C.key_toggle(ckey, cdown, cmKey, cmKeyT) - // str := C.key_Toggle(ckey, cdown, cmKey, cmKeyT, C.int(keyDelay)) - - C.free(unsafe.Pointer(cdown)) - C.free(unsafe.Pointer(cmKey)) - C.free(unsafe.Pointer(cmKeyT)) - - MilliSleep(KeySleep) - return toErr(str) -} - -// KeyPress press key string -func KeyPress(key string) error { - err := KeyDown(key) - if err != nil { - return err - } - MilliSleep(1 + rand.Intn(3)) - return KeyUp(key) -} - -// KeyDown press down a key -func KeyDown(key string) error { - return KeyToggle(key) -} - -// KeyUp press up a key -func KeyUp(key string) error { - return KeyToggle(key, "up") -} - -// ReadAll read string from clipboard -func ReadAll() (string, error) { - return clipboard.ReadAll() -} - -// WriteAll write string to clipboard -func WriteAll(text string) error { - return clipboard.WriteAll(text) -} - -// CharCodeAt char code at utf-8 -func CharCodeAt(s string, n int) rune { - i := 0 - for _, r := range s { - if i == n { - return r - } - i++ - } - - return 0 -} - -// UnicodeType tap uint32 unicode -func UnicodeType(str uint32) { - cstr := C.uint(str) - C.unicodeType(cstr) -} - -// ToUC trans string to unicode []string -func ToUC(text string) []string { - var uc []string - - for _, r := range text { - textQ := strconv.QuoteToASCII(string(r)) - textUnQ := textQ[1 : len(textQ)-1] - - st := strings.Replace(textUnQ, "\\u", "U", -1) - if st == "\\\\" { - st = "\\" - } - if st == `\"` { - st = `"` - } - uc = append(uc, st) - } - - return uc -} - -func inputUTF(str string) { - cstr := C.CString(str) - C.input_utf(cstr) - - C.free(unsafe.Pointer(cstr)) -} - -// TypeStr send a string, support UTF-8 -// -// robotgo.TypeStr(string: The string to send, float64: microsleep time, x11 option) -// -// Examples: -// robotgo.TypeStr("abc@123, hi, こんにちは") -// -func TypeStr(str string, args ...float64) { - var tm, tm1 = 0.0, 7.0 - - if len(args) > 0 { - tm = args[0] - } - if len(args) > 1 { - tm1 = args[1] - } - - if runtime.GOOS == "linux" { - strUc := ToUC(str) - for i := 0; i < len(strUc); i++ { - ru := []rune(strUc[i]) - if len(ru) <= 1 { - ustr := uint32(CharCodeAt(strUc[i], 0)) - UnicodeType(ustr) - } else { - inputUTF(strUc[i]) - MicroSleep(tm1) - } - - MicroSleep(tm) - } - - return - } - - for i := 0; i < len([]rune(str)); i++ { - ustr := uint32(CharCodeAt(str, i)) - UnicodeType(ustr) - - // if len(args) > 0 { - MicroSleep(tm) - // } - } - MilliSleep(KeySleep) -} - -// PasteStr paste a string, support UTF-8, -// write the string to clipboard and tap `cmd + v` -func PasteStr(str string) error { - err := clipboard.WriteAll(str) - if err != nil { - return err - } - - if runtime.GOOS == "darwin" { - return KeyTap("v", "command") - } - - return KeyTap("v", "control") -} - -// TypeStrDelay type string delayed -func TypeStrDelay(str string, delay int) { - TypeStr(str) - Sleep(delay) -} - -// Deprecated: use the TypeStr(), -// -// TypeStringDelayed type string delayed, Wno-deprecated -// -// This function will be removed in version v1.0.0 -func TypeStringDelayed(str string, delay int) { - tt.Drop("TypeStringDelayed", "TypeStrDelay") - TypeStrDelay(str, delay) -} - -// SetKeyDelay set keyboard delay -func SetKeyDelay(delay int) { - C.set_keyboard_delay(C.size_t(delay)) -} - -// Deprecated: use the SetKeyDelay(), -// -// SetKeyboardDelay set keyboard delay, Wno-deprecated, -// -// This function will be removed in version v1.0.0 -func SetKeyboardDelay(delay int) { - tt.Drop("SetKeyboardDelay", "SetKeyDelay") - SetKeyDelay(delay) -} - -// SetDelay set the key and mouse delay -func SetDelay(d ...int) { - v := 10 - if len(d) > 0 { - v = d[0] - } - - SetMouseDelay(v) - SetKeyDelay(v) -} - /* ____ __ ____ __ .__ __. _______ ______ ____ __ ____ \ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /