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
|
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 {
|
type AppConfig struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
MatchWindowTitles []string `json:"match_window_titles"`
|
MatchWindowTitles []string `json:"match_window_titles"`
|
||||||
|
SlowJog int `json:"slow_jog"` // Time in millisecond to use slow jog
|
||||||
windowTitleRegexps []*regexp.Regexp
|
windowTitleRegexps []*regexp.Regexp
|
||||||
Bindings map[string]string `json:"bindings"`
|
Bindings map[string]string `json:"bindings"`
|
||||||
bindings []*deviceBinding
|
bindings []*deviceBinding
|
||||||
|
@ -52,11 +53,12 @@ type deviceBinding struct {
|
||||||
// Output
|
// Output
|
||||||
holdButtons []string
|
holdButtons []string
|
||||||
pressButton string
|
pressButton string
|
||||||
|
original string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppConfig) parseBindings() error {
|
func (ac *AppConfig) parseBindings() error {
|
||||||
for key, value := range ac.Bindings {
|
for key, value := range ac.Bindings {
|
||||||
newBinding := &deviceBinding{heldButtons: make(map[int]bool)}
|
newBinding := &deviceBinding{heldButtons: make(map[int]bool), original: value}
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
input := strings.Split(key, "+")
|
input := strings.Split(key, "+")
|
||||||
|
@ -82,19 +84,19 @@ func (ac *AppConfig) parseBindings() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
output := strings.Split(value, "+")
|
// output := strings.Split(value, "+")
|
||||||
for idx, part := range output {
|
// for idx, part := range output {
|
||||||
cleanPart := strings.TrimSpace(part)
|
// cleanPart := strings.TrimSpace(part)
|
||||||
buttonName := strings.ToUpper(cleanPart)
|
// buttonName := strings.ToUpper(cleanPart)
|
||||||
if keyboardKeysUpper[buttonName] == 0 {
|
// if keyboardKeysUpper[buttonName] == 0 {
|
||||||
return fmt.Errorf("keyboard key unknown: %q", cleanPart)
|
// return fmt.Errorf("keyboard key unknown: %q", cleanPart)
|
||||||
}
|
// }
|
||||||
if idx == len(output)-1 {
|
// if idx == len(output)-1 {
|
||||||
newBinding.pressButton = buttonName
|
// newBinding.pressButton = buttonName
|
||||||
} else {
|
// } else {
|
||||||
newBinding.holdButtons = append(newBinding.holdButtons, buttonName)
|
// newBinding.holdButtons = append(newBinding.holdButtons, buttonName)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
ac.bindings = append(ac.bindings, newBinding)
|
ac.bindings = append(ac.bindings, newBinding)
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ var otherShuttleKeys = map[string]bool{
|
||||||
"S7": true,
|
"S7": true,
|
||||||
"JogL": true,
|
"JogL": true,
|
||||||
"JogR": true,
|
"JogR": true,
|
||||||
|
"SlowJogL": true,
|
||||||
|
"SlowJogR": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyboardKeys = map[string]int{
|
var keyboardKeys = map[string]int{
|
||||||
|
@ -141,6 +143,9 @@ var keyboardKeys = map[string]int{
|
||||||
"KPDot": 83,
|
"KPDot": 83,
|
||||||
"F11": 87,
|
"F11": 87,
|
||||||
"F12": 88,
|
"F12": 88,
|
||||||
|
|
||||||
|
"Henkan": 92,
|
||||||
|
|
||||||
"KPEnter": 96,
|
"KPEnter": 96,
|
||||||
"RightCtrl": 97,
|
"RightCtrl": 97,
|
||||||
"RCtrl": 97,
|
"RCtrl": 97,
|
||||||
|
@ -280,14 +285,14 @@ var keyboardKeys = map[string]int{
|
||||||
"Micmute": 248, /*Mute/UnmuteTheMicrophone*/
|
"Micmute": 248, /*Mute/UnmuteTheMicrophone*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//var reverseShuttleKeys map[int]string
|
var reverseShuttleKeys = map[int]string{}
|
||||||
var keyboardKeysUpper = map[string]int{}
|
var keyboardKeysUpper = map[string]int{}
|
||||||
var otherShuttleKeysUpper = map[string]bool{}
|
var otherShuttleKeysUpper = map[string]bool{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// for k, v := range shuttleKeys {
|
for k, v := range shuttleKeys {
|
||||||
// reverseShuttleKeys[v] = k
|
reverseShuttleKeys[v] = k
|
||||||
// }
|
}
|
||||||
for k, v := range keyboardKeys {
|
for k, v := range keyboardKeys {
|
||||||
keyboardKeysUpper[strings.ToUpper(k)] = v
|
keyboardKeysUpper[strings.ToUpper(k)] = v
|
||||||
}
|
}
|
||||||
|
|
1
main.go
1
main.go
|
@ -51,6 +51,7 @@ func main() {
|
||||||
|
|
||||||
fmt.Println("ready")
|
fmt.Println("ready")
|
||||||
mapper := NewMapper(vk, dev)
|
mapper := NewMapper(vk, dev)
|
||||||
|
mapper.watcher = watcher
|
||||||
for {
|
for {
|
||||||
if err := mapper.Process(); err != nil {
|
if err := mapper.Process(); err != nil {
|
||||||
fmt.Println("Error processing input events (continuing):", err)
|
fmt.Println("Error processing input events (continuing):", err)
|
||||||
|
|
85
mapper.go
85
mapper.go
|
@ -2,8 +2,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/bendahl/uinput"
|
"github.com/bendahl/uinput"
|
||||||
evdev "github.com/gvalkov/golang-evdev"
|
evdev "github.com/gvalkov/golang-evdev"
|
||||||
|
@ -15,12 +17,14 @@ type Mapper struct {
|
||||||
virtualKeyboard uinput.Keyboard
|
virtualKeyboard uinput.Keyboard
|
||||||
inputDevice *evdev.InputDevice
|
inputDevice *evdev.InputDevice
|
||||||
state buttonsState
|
state buttonsState
|
||||||
|
watcher *watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
type buttonsState struct {
|
type buttonsState struct {
|
||||||
jog int
|
jog int
|
||||||
shuttle int
|
shuttle int
|
||||||
buttonsHeld map[int]bool
|
buttonsHeld map[int]bool
|
||||||
|
lastJog time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMapper(virtualKeyboard uinput.Keyboard, inputDevice *evdev.InputDevice) *Mapper {
|
func NewMapper(virtualKeyboard uinput.Keyboard, inputDevice *evdev.InputDevice) *Mapper {
|
||||||
|
@ -39,7 +43,6 @@ func (m *Mapper) Process() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
m.dispatch(evs)
|
m.dispatch(evs)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -49,17 +52,27 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
||||||
newJogVal := jogVal(evs)
|
newJogVal := jogVal(evs)
|
||||||
if m.state.jog != newJogVal {
|
if m.state.jog != newJogVal {
|
||||||
if m.state.jog != -1 {
|
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..
|
// Trigger JL or JR if we're advancing or not..
|
||||||
delta := newJogVal - m.state.jog
|
delta := newJogVal - m.state.jog
|
||||||
if (delta > 0 || delta < -200) && (delta < 200) {
|
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)
|
fmt.Println("Jog right:", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := m.EmitOther("JogL"); err != nil {
|
if err := m.EmitOther(slow + "JogL"); err != nil {
|
||||||
fmt.Println("Jog left:", err)
|
fmt.Println("Jog left:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.state.lastJog = time.Now()
|
||||||
}
|
}
|
||||||
m.state.jog = newJogVal
|
m.state.jog = newJogVal
|
||||||
}
|
}
|
||||||
|
@ -67,6 +80,7 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
||||||
newShuttleVal := shuttleVal(evs)
|
newShuttleVal := shuttleVal(evs)
|
||||||
if m.state.shuttle != newShuttleVal {
|
if m.state.shuttle != newShuttleVal {
|
||||||
keyName := fmt.Sprintf("S%d", newShuttleVal)
|
keyName := fmt.Sprintf("S%d", newShuttleVal)
|
||||||
|
fmt.Println("SHUTTLE", keyName)
|
||||||
if err := m.EmitOther(keyName); err != nil {
|
if err := m.EmitOther(keyName); err != nil {
|
||||||
fmt.Println("Shuttle movement %q: %s\n", keyName, err)
|
fmt.Println("Shuttle movement %q: %s\n", keyName, err)
|
||||||
}
|
}
|
||||||
|
@ -89,12 +103,24 @@ func (m *Mapper) dispatch(evs []evdev.InputEvent) {
|
||||||
m.state.buttonsHeld = heldButtons
|
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
|
// TODO: Lock on configuration changes
|
||||||
|
|
||||||
return
|
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 {
|
func (m *Mapper) EmitOther(key string) error {
|
||||||
conf := currentConfiguration
|
conf := currentConfiguration
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
|
@ -103,9 +129,11 @@ func (m *Mapper) EmitOther(key string) error {
|
||||||
|
|
||||||
upperKey := strings.ToUpper(key)
|
upperKey := strings.ToUpper(key)
|
||||||
|
|
||||||
|
fmt.Println("EmitOther:", key)
|
||||||
|
|
||||||
for _, binding := range conf.bindings {
|
for _, binding := range conf.bindings {
|
||||||
if binding.otherKey == upperKey {
|
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")
|
return fmt.Errorf("No configuration for this Window")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Emit Keys", modifiers, reverseShuttleKeys[keyDown])
|
||||||
|
|
||||||
for _, binding := range conf.bindings {
|
for _, binding := range conf.bindings {
|
||||||
if reflect.DeepEqual(binding.heldButtons, modifiers) && binding.buttonDown == keyDown {
|
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")
|
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)
|
fmt.Println("Executing bindings:", holdButtons, pressButton)
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
for _, button := range holdButtons {
|
for _, button := range holdButtons {
|
||||||
|
fmt.Println("Key down", button)
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
if err := m.virtualKeyboard.KeyDown(keyboardKeysUpper[button]); err != nil {
|
if err := m.virtualKeyboard.KeyDown(keyboardKeysUpper[button]); err != nil {
|
||||||
return err
|
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
|
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 {
|
for _, button := range holdButtons {
|
||||||
|
fmt.Println("Key up", button)
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
if err := m.virtualKeyboard.KeyUp(keyboardKeysUpper[button]); err != nil {
|
if err := m.virtualKeyboard.KeyUp(keyboardKeysUpper[button]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,13 +215,16 @@ func jogVal(evs []evdev.InputEvent) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func shuttleVal(evs []evdev.InputEvent) int {
|
func shuttleVal(evs []evdev.InputEvent) (out int) {
|
||||||
for _, ev := range evs {
|
for idx, ev := range evs {
|
||||||
|
if ev.Type == 0 && idx != len(evs)-1 {
|
||||||
|
out = 0
|
||||||
|
}
|
||||||
if ev.Type == 2 && ev.Code == 8 {
|
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) {
|
func buttonVals(current map[int]bool, ev evdev.InputEvent) (out map[int]bool, lastDown int) {
|
||||||
|
|
Ładowanie…
Reference in New Issue