kopia lustrzana https://github.com/abourget/shuttle-go
IMprovements.. attempts to fixup. Implemented Slow Jog, to allow you to send
nudge keybindings.pull/5/head
rodzic
cffc6fd992
commit
0e4e53ef54
19
README.md
19
README.md
|
@ -26,3 +26,22 @@ Buttons layout on the Contour Design Shuttle Pro v2:
|
|||
|
||||
|
||||
See
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
* Fix up timings, make sure we properly support shortcuts with
|
||||
Ctrl+Shift and it doesn't clog the program. Perhaps optimize and
|
||||
keep certain keys pressed, until not needed anymore. Especially
|
||||
using the Jog and Shuttle.
|
||||
|
||||
* Make sure we have a solution to ignore the device as a generic HID
|
||||
under Ubuntu. We can't have mouse clicks on top of our bindings!
|
||||
|
||||
* Check udev, DISPLAY=:0.0 to start ?
|
||||
* Retry ? Check the error message going out.
|
||||
|
||||
* Try the xdotool with the latest bindings, XTest-based.
|
||||
* Use xgb's `xtest` package and send the FakeInput directly there.. should work
|
||||
a lot better.
|
||||
|
|
30
config.go
30
config.go
|
@ -18,6 +18,7 @@ type Config struct {
|
|||
type AppConfig struct {
|
||||
Name string `json:"name"`
|
||||
MatchWindowTitles []string `json:"match_window_titles"`
|
||||
SlowJog int `json:"slow_jog"` // Time in millisecond to use slow jog
|
||||
windowTitleRegexps []*regexp.Regexp
|
||||
Bindings map[string]string `json:"bindings"`
|
||||
bindings []*deviceBinding
|
||||
|
@ -52,11 +53,12 @@ type deviceBinding struct {
|
|||
// Output
|
||||
holdButtons []string
|
||||
pressButton string
|
||||
original string
|
||||
}
|
||||
|
||||
func (ac *AppConfig) parseBindings() error {
|
||||
for key, value := range ac.Bindings {
|
||||
newBinding := &deviceBinding{heldButtons: make(map[int]bool)}
|
||||
newBinding := &deviceBinding{heldButtons: make(map[int]bool), original: value}
|
||||
|
||||
// Input
|
||||
input := strings.Split(key, "+")
|
||||
|
@ -82,19 +84,19 @@ func (ac *AppConfig) parseBindings() error {
|
|||
}
|
||||
|
||||
// Output
|
||||
output := strings.Split(value, "+")
|
||||
for idx, part := range output {
|
||||
cleanPart := strings.TrimSpace(part)
|
||||
buttonName := strings.ToUpper(cleanPart)
|
||||
if keyboardKeysUpper[buttonName] == 0 {
|
||||
return fmt.Errorf("keyboard key unknown: %q", cleanPart)
|
||||
}
|
||||
if idx == len(output)-1 {
|
||||
newBinding.pressButton = buttonName
|
||||
} else {
|
||||
newBinding.holdButtons = append(newBinding.holdButtons, buttonName)
|
||||
}
|
||||
}
|
||||
// output := strings.Split(value, "+")
|
||||
// for idx, part := range output {
|
||||
// cleanPart := strings.TrimSpace(part)
|
||||
// buttonName := strings.ToUpper(cleanPart)
|
||||
// if keyboardKeysUpper[buttonName] == 0 {
|
||||
// return fmt.Errorf("keyboard key unknown: %q", cleanPart)
|
||||
// }
|
||||
// if idx == len(output)-1 {
|
||||
// newBinding.pressButton = buttonName
|
||||
// } else {
|
||||
// newBinding.holdButtons = append(newBinding.holdButtons, buttonName)
|
||||
// }
|
||||
// }
|
||||
|
||||
ac.bindings = append(ac.bindings, newBinding)
|
||||
|
||||
|
|
247
definitions.go
247
definitions.go
|
@ -21,126 +21,131 @@ var shuttleKeys = map[string]int{
|
|||
}
|
||||
|
||||
var otherShuttleKeys = map[string]bool{
|
||||
"S-7": true,
|
||||
"S-6": true,
|
||||
"S-5": true,
|
||||
"S-4": true,
|
||||
"S-3": true,
|
||||
"S-2": true,
|
||||
"S-1": true,
|
||||
"S0": true,
|
||||
"S1": true,
|
||||
"S2": true,
|
||||
"S3": true,
|
||||
"S4": true,
|
||||
"S5": true,
|
||||
"S6": true,
|
||||
"S7": true,
|
||||
"JogL": true,
|
||||
"JogR": true,
|
||||
"S-7": true,
|
||||
"S-6": true,
|
||||
"S-5": true,
|
||||
"S-4": true,
|
||||
"S-3": true,
|
||||
"S-2": true,
|
||||
"S-1": true,
|
||||
"S0": true,
|
||||
"S1": true,
|
||||
"S2": true,
|
||||
"S3": true,
|
||||
"S4": true,
|
||||
"S5": true,
|
||||
"S6": true,
|
||||
"S7": true,
|
||||
"JogL": true,
|
||||
"JogR": true,
|
||||
"SlowJogL": true,
|
||||
"SlowJogR": true,
|
||||
}
|
||||
|
||||
var keyboardKeys = map[string]int{
|
||||
"Esc": 1,
|
||||
"1": 2,
|
||||
"2": 3,
|
||||
"3": 4,
|
||||
"4": 5,
|
||||
"5": 6,
|
||||
"6": 7,
|
||||
"7": 8,
|
||||
"8": 9,
|
||||
"9": 10,
|
||||
"0": 11,
|
||||
"Minus": 12,
|
||||
"-": 12,
|
||||
"Equal": 13,
|
||||
"=": 13,
|
||||
"Backspace": 14,
|
||||
"Tab": 15,
|
||||
"Q": 16,
|
||||
"W": 17,
|
||||
"E": 18,
|
||||
"R": 19,
|
||||
"T": 20,
|
||||
"Y": 21,
|
||||
"U": 22,
|
||||
"I": 23,
|
||||
"O": 24,
|
||||
"P": 25,
|
||||
"LeftBrace": 26,
|
||||
"RightBrace": 27,
|
||||
"{": 26,
|
||||
"}": 27,
|
||||
"Enter": 28,
|
||||
"LeftCtrl": 29,
|
||||
"Ctrl": 29,
|
||||
"A": 30,
|
||||
"S": 31,
|
||||
"D": 32,
|
||||
"F": 33,
|
||||
"G": 34,
|
||||
"H": 35,
|
||||
"J": 36,
|
||||
"K": 37,
|
||||
"L": 38,
|
||||
"Semicolon": 39,
|
||||
";": 39,
|
||||
"Apostrophe": 40,
|
||||
"'": 40,
|
||||
"Grave": 41,
|
||||
"LeftShift": 42,
|
||||
"Shift": 42,
|
||||
"Backslash": 43,
|
||||
"\\": 43,
|
||||
"Z": 44,
|
||||
"X": 45,
|
||||
"C": 46,
|
||||
"V": 47,
|
||||
"B": 48,
|
||||
"N": 49,
|
||||
"M": 50,
|
||||
"Comma": 51,
|
||||
",": 51,
|
||||
"Dot": 52,
|
||||
".": 52,
|
||||
"Slash": 53,
|
||||
"/": 53,
|
||||
"RightShift": 54,
|
||||
"RShift": 54,
|
||||
"KPAsterisk": 55,
|
||||
"*": 55,
|
||||
"LeftAlt": 56,
|
||||
"Alt": 56,
|
||||
"Space": 57,
|
||||
"CapsLock": 58,
|
||||
"F1": 59,
|
||||
"F2": 60,
|
||||
"F3": 61,
|
||||
"F4": 62,
|
||||
"F5": 63,
|
||||
"F6": 64,
|
||||
"F7": 65,
|
||||
"F8": 66,
|
||||
"F9": 67,
|
||||
"F10": 68,
|
||||
"NumLock": 69,
|
||||
"ScrollLock": 70,
|
||||
"KP7": 71,
|
||||
"KP8": 72,
|
||||
"KP9": 73,
|
||||
"KPMinus": 74,
|
||||
"KP4": 75,
|
||||
"KP5": 76,
|
||||
"KP6": 77,
|
||||
"KPPlus": 78,
|
||||
"KP1": 79,
|
||||
"KP2": 80,
|
||||
"KP3": 81,
|
||||
"KP0": 82,
|
||||
"KPDot": 83,
|
||||
"F11": 87,
|
||||
"F12": 88,
|
||||
"Esc": 1,
|
||||
"1": 2,
|
||||
"2": 3,
|
||||
"3": 4,
|
||||
"4": 5,
|
||||
"5": 6,
|
||||
"6": 7,
|
||||
"7": 8,
|
||||
"8": 9,
|
||||
"9": 10,
|
||||
"0": 11,
|
||||
"Minus": 12,
|
||||
"-": 12,
|
||||
"Equal": 13,
|
||||
"=": 13,
|
||||
"Backspace": 14,
|
||||
"Tab": 15,
|
||||
"Q": 16,
|
||||
"W": 17,
|
||||
"E": 18,
|
||||
"R": 19,
|
||||
"T": 20,
|
||||
"Y": 21,
|
||||
"U": 22,
|
||||
"I": 23,
|
||||
"O": 24,
|
||||
"P": 25,
|
||||
"LeftBrace": 26,
|
||||
"RightBrace": 27,
|
||||
"{": 26,
|
||||
"}": 27,
|
||||
"Enter": 28,
|
||||
"LeftCtrl": 29,
|
||||
"Ctrl": 29,
|
||||
"A": 30,
|
||||
"S": 31,
|
||||
"D": 32,
|
||||
"F": 33,
|
||||
"G": 34,
|
||||
"H": 35,
|
||||
"J": 36,
|
||||
"K": 37,
|
||||
"L": 38,
|
||||
"Semicolon": 39,
|
||||
";": 39,
|
||||
"Apostrophe": 40,
|
||||
"'": 40,
|
||||
"Grave": 41,
|
||||
"LeftShift": 42,
|
||||
"Shift": 42,
|
||||
"Backslash": 43,
|
||||
"\\": 43,
|
||||
"Z": 44,
|
||||
"X": 45,
|
||||
"C": 46,
|
||||
"V": 47,
|
||||
"B": 48,
|
||||
"N": 49,
|
||||
"M": 50,
|
||||
"Comma": 51,
|
||||
",": 51,
|
||||
"Dot": 52,
|
||||
".": 52,
|
||||
"Slash": 53,
|
||||
"/": 53,
|
||||
"RightShift": 54,
|
||||
"RShift": 54,
|
||||
"KPAsterisk": 55,
|
||||
"*": 55,
|
||||
"LeftAlt": 56,
|
||||
"Alt": 56,
|
||||
"Space": 57,
|
||||
"CapsLock": 58,
|
||||
"F1": 59,
|
||||
"F2": 60,
|
||||
"F3": 61,
|
||||
"F4": 62,
|
||||
"F5": 63,
|
||||
"F6": 64,
|
||||
"F7": 65,
|
||||
"F8": 66,
|
||||
"F9": 67,
|
||||
"F10": 68,
|
||||
"NumLock": 69,
|
||||
"ScrollLock": 70,
|
||||
"KP7": 71,
|
||||
"KP8": 72,
|
||||
"KP9": 73,
|
||||
"KPMinus": 74,
|
||||
"KP4": 75,
|
||||
"KP5": 76,
|
||||
"KP6": 77,
|
||||
"KPPlus": 78,
|
||||
"KP1": 79,
|
||||
"KP2": 80,
|
||||
"KP3": 81,
|
||||
"KP0": 82,
|
||||
"KPDot": 83,
|
||||
"F11": 87,
|
||||
"F12": 88,
|
||||
|
||||
"Henkan": 92,
|
||||
|
||||
"KPEnter": 96,
|
||||
"RightCtrl": 97,
|
||||
"RCtrl": 97,
|
||||
|
@ -280,14 +285,14 @@ var keyboardKeys = map[string]int{
|
|||
"Micmute": 248, /*Mute/UnmuteTheMicrophone*/
|
||||
}
|
||||
|
||||
//var reverseShuttleKeys map[int]string
|
||||
var reverseShuttleKeys = map[int]string{}
|
||||
var keyboardKeysUpper = map[string]int{}
|
||||
var otherShuttleKeysUpper = map[string]bool{}
|
||||
|
||||
func init() {
|
||||
// for k, v := range shuttleKeys {
|
||||
// reverseShuttleKeys[v] = k
|
||||
// }
|
||||
for k, v := range shuttleKeys {
|
||||
reverseShuttleKeys[v] = k
|
||||
}
|
||||
for k, v := range keyboardKeys {
|
||||
keyboardKeysUpper[strings.ToUpper(k)] = v
|
||||
}
|
||||
|
|
1
main.go
1
main.go
|
@ -51,6 +51,7 @@ func main() {
|
|||
|
||||
fmt.Println("ready")
|
||||
mapper := NewMapper(vk, dev)
|
||||
mapper.watcher = watcher
|
||||
for {
|
||||
if err := mapper.Process(); err != nil {
|
||||
fmt.Println("Error processing input events (continuing):", err)
|
||||
|
|
85
mapper.go
85
mapper.go
|
@ -2,8 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bendahl/uinput"
|
||||
evdev "github.com/gvalkov/golang-evdev"
|
||||
|
@ -15,12 +17,14 @@ type Mapper struct {
|
|||
virtualKeyboard uinput.Keyboard
|
||||
inputDevice *evdev.InputDevice
|
||||
state buttonsState
|
||||
watcher *watcher
|
||||
}
|
||||
|
||||
type buttonsState struct {
|
||||
jog int
|
||||
shuttle int
|
||||
buttonsHeld map[int]bool
|
||||
lastJog time.Time
|
||||
}
|
||||
|
||||
func NewMapper(virtualKeyboard uinput.Keyboard, inputDevice *evdev.InputDevice) *Mapper {
|
||||
|
@ -39,7 +43,6 @@ func (m *Mapper) Process() error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Println("---")
|
||||
m.dispatch(evs)
|
||||
|
||||
return nil
|
||||
|
@ -49,17 +52,27 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
|||
newJogVal := jogVal(evs)
|
||||
if m.state.jog != newJogVal {
|
||||
if m.state.jog != -1 {
|
||||
if m.state.lastJog.IsZero() {
|
||||
m.state.lastJog = time.Now()
|
||||
}
|
||||
|
||||
slow := ""
|
||||
if time.Since(m.state.lastJog) > slowJogTiming() {
|
||||
slow = "Slow"
|
||||
}
|
||||
// Trigger JL or JR if we're advancing or not..
|
||||
delta := newJogVal - m.state.jog
|
||||
if (delta > 0 || delta < -200) && (delta < 200) {
|
||||
if err := m.EmitOther("JogR"); err != nil {
|
||||
if err := m.EmitOther(slow + "JogR"); err != nil {
|
||||
fmt.Println("Jog right:", err)
|
||||
}
|
||||
} else {
|
||||
if err := m.EmitOther("JogL"); err != nil {
|
||||
if err := m.EmitOther(slow + "JogL"); err != nil {
|
||||
fmt.Println("Jog left:", err)
|
||||
}
|
||||
}
|
||||
|
||||
m.state.lastJog = time.Now()
|
||||
}
|
||||
m.state.jog = newJogVal
|
||||
}
|
||||
|
@ -67,6 +80,7 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
|||
newShuttleVal := shuttleVal(evs)
|
||||
if m.state.shuttle != newShuttleVal {
|
||||
keyName := fmt.Sprintf("S%d", newShuttleVal)
|
||||
fmt.Println("SHUTTLE", keyName)
|
||||
if err := m.EmitOther(keyName); err != nil {
|
||||
fmt.Println("Shuttle movement %q: %s\n", keyName, err)
|
||||
}
|
||||
|
@ -89,12 +103,24 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
|||
m.state.buttonsHeld = heldButtons
|
||||
}
|
||||
|
||||
//fmt.Printf("TYPE: %d\tCODE: %d\tVALUE: %d\n", ev.Type, ev.Code, ev.Value)
|
||||
fmt.Println("---")
|
||||
for _, ev := range evs {
|
||||
fmt.Printf("TYPE: %d\tCODE: %d\tVALUE: %d\n", ev.Type, ev.Code, ev.Value)
|
||||
}
|
||||
|
||||
// TODO: Lock on configuration changes
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func slowJogTiming() time.Duration {
|
||||
conf := currentConfiguration
|
||||
if conf == nil {
|
||||
return 200 * time.Millisecond
|
||||
}
|
||||
return time.Duration(conf.SlowJog) * time.Millisecond
|
||||
}
|
||||
|
||||
func (m *Mapper) EmitOther(key string) error {
|
||||
conf := currentConfiguration
|
||||
if conf == nil {
|
||||
|
@ -103,9 +129,11 @@ func (m *Mapper) EmitOther(key string) error {
|
|||
|
||||
upperKey := strings.ToUpper(key)
|
||||
|
||||
fmt.Println("EmitOther:", key)
|
||||
|
||||
for _, binding := range conf.bindings {
|
||||
if binding.otherKey == upperKey {
|
||||
return m.executeBinding(binding.holdButtons, binding.pressButton)
|
||||
return m.executeBinding(binding)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,33 +146,63 @@ func (m *Mapper) EmitKeys(modifiers map[int]bool, keyDown int) error {
|
|||
return fmt.Errorf("No configuration for this Window")
|
||||
}
|
||||
|
||||
fmt.Println("Emit Keys", modifiers, reverseShuttleKeys[keyDown])
|
||||
|
||||
for _, binding := range conf.bindings {
|
||||
if reflect.DeepEqual(binding.heldButtons, modifiers) && binding.buttonDown == keyDown {
|
||||
return m.executeBinding(binding.holdButtons, binding.pressButton)
|
||||
return m.executeBinding(binding)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("No binding for these keys")
|
||||
}
|
||||
|
||||
func (m *Mapper) executeBinding(holdButtons []string, pressButton string) error {
|
||||
func (m *Mapper) executeBinding(binding *deviceBinding) error {
|
||||
holdButtons := binding.holdButtons
|
||||
pressButton := binding.pressButton
|
||||
|
||||
//xtest.FakeInputChecked(m.watcher.conn, m.watcher.rootWin)
|
||||
fmt.Println("xdotool key --clearmodifiers", binding.original)
|
||||
return exec.Command("xdotool", "key", "--clearmodifiers", binding.original).Run()
|
||||
|
||||
fmt.Println("Executing bindings:", holdButtons, pressButton)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
for _, button := range holdButtons {
|
||||
fmt.Println("Key down", button)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
if err := m.virtualKeyboard.KeyDown(keyboardKeysUpper[button]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := m.virtualKeyboard.KeyPress(keyboardKeysUpper[pressButton]); err != nil {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
fmt.Println("Key press", pressButton)
|
||||
if err := m.virtualKeyboard.KeyDown(keyboardKeysUpper[pressButton]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
if err := m.virtualKeyboard.KeyUp(keyboardKeysUpper[pressButton]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
for _, button := range holdButtons {
|
||||
fmt.Println("Key up", button)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if err := m.virtualKeyboard.KeyUp(keyboardKeysUpper[button]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -157,13 +215,16 @@ func jogVal(evs []evdev.InputEvent) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func shuttleVal(evs []evdev.InputEvent) int {
|
||||
for _, ev := range evs {
|
||||
func shuttleVal(evs []evdev.InputEvent) (out int) {
|
||||
for idx, ev := range evs {
|
||||
if ev.Type == 0 && idx != len(evs)-1 {
|
||||
out = 0
|
||||
}
|
||||
if ev.Type == 2 && ev.Code == 8 {
|
||||
return int(ev.Value)
|
||||
out = int(ev.Value)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
||||
func buttonVals(current map[int]bool, ev evdev.InputEvent) (out map[int]bool, lastDown int) {
|
||||
|
|
Ładowanie…
Reference in New Issue