-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented auto-typing into starbase
- Loading branch information
1 parent
dbb6653
commit d83340b
Showing
10 changed files
with
417 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// +build !windows | ||
|
||
package langserver | ||
|
||
// ListenForHotkeys is only supported on windows | ||
func (ls *LangServer) ListenForHotkeys() { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// +build windows | ||
|
||
package langserver | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/dbaumgarten/yodk/pkg/langserver/win32" | ||
) | ||
|
||
var ( | ||
// AutotypeHotkey is the hotkey to trigger auto-typing | ||
AutotypeHotkey = &win32.Hotkey{ | ||
ID: 1, | ||
Modifiers: win32.ModCtrl, | ||
KeyCode: 'I', | ||
} | ||
// AutodeleteHotkey is the hotkey to trigger auto-deletion | ||
AutodeleteHotkey = &win32.Hotkey{ | ||
ID: 2, | ||
Modifiers: win32.ModCtrl, | ||
KeyCode: 'D', | ||
} | ||
// AutooverwriteHotkey is the hotkey to overwrite the current line with new ones | ||
AutooverwriteHotkey = &win32.Hotkey{ | ||
ID: 3, | ||
Modifiers: win32.ModCtrl, | ||
KeyCode: 'O', | ||
} | ||
) | ||
|
||
const typeDelay = 40 * time.Millisecond | ||
|
||
// ListenForHotkeys listens for global hotkeys and dispatches the registered actions | ||
func (ls *LangServer) ListenForHotkeys() { | ||
go func() { | ||
err := win32.ListenForHotkeys(nil, ls.hotkeyHandler, AutotypeHotkey, AutodeleteHotkey, AutooverwriteHotkey) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error when registering hotkeys: %s", err) | ||
} | ||
}() | ||
} | ||
|
||
func (ls *LangServer) hotkeyHandler(hk win32.Hotkey) { | ||
win32.SendInput(win32.KeyUpInput(win32.KeycodeCtrl)) | ||
win32.SendInput(win32.KeyUpInput(uint16(hk.KeyCode))) | ||
switch hk.ID { | ||
case AutotypeHotkey.ID: | ||
if code := ls.getLastOpenedCode(); code == code { | ||
typeYololCode(code) | ||
} | ||
case AutodeleteHotkey.ID: | ||
deleteAllLines() | ||
case AutooverwriteHotkey.ID: | ||
if code := ls.getLastOpenedCode(); code == code { | ||
overwriteYololCode(code) | ||
} | ||
} | ||
} | ||
|
||
func (ls *LangServer) getLastOpenedCode() string { | ||
ls.cache.Lock.Lock() | ||
lastOpened := ls.cache.LastOpenedYololFile | ||
ls.cache.Lock.Unlock() | ||
|
||
if lastOpened != "" { | ||
code, err := ls.cache.Get(lastOpened) | ||
if err == nil { | ||
return code | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
func typeYololCode(code string) { | ||
lines := strings.Split(code, "\n") | ||
for _, line := range lines { | ||
win32.SendString(line) | ||
time.Sleep(typeDelay) | ||
win32.SendInput(win32.KeyDownInput(win32.KeycodeDown), win32.KeyUpInput(win32.KeycodeDown)) | ||
} | ||
} | ||
|
||
func overwriteYololCode(code string) { | ||
lines := strings.Split(code, "\n") | ||
for _, line := range lines { | ||
deleteLine() | ||
win32.SendString(line) | ||
time.Sleep(typeDelay) | ||
win32.SendInput(win32.KeyDownInput(win32.KeycodeDown), win32.KeyUpInput(win32.KeycodeDown)) | ||
} | ||
} | ||
|
||
func deleteAllLines() { | ||
for i := 0; i < 20; i++ { | ||
deleteLine() | ||
win32.SendInput(win32.KeyDownInput(win32.KeycodeDown), win32.KeyUpInput(win32.KeycodeDown)) | ||
} | ||
} | ||
|
||
func deleteLine() { | ||
win32.SendInput(win32.KeyDownInput(win32.KeycodeCtrl), win32.KeyDownInput('A')) | ||
time.Sleep(typeDelay) | ||
win32.SendInput(win32.KeyUpInput('A'), win32.KeyUpInput(win32.KeycodeCtrl)) | ||
win32.SendInput(win32.KeyDownInput(win32.KeycodeBackspace), win32.KeyUpInput(win32.KeycodeBackspace)) | ||
time.Sleep(typeDelay) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// +build windows | ||
|
||
package win32 | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"runtime" | ||
"unsafe" | ||
) | ||
|
||
// most of the code is taken from: https://stackoverflow.com/questions/38646794/implement-a-global-hotkey-in-golang#38954281 | ||
|
||
// Modifiers for Hotkeys | ||
const ( | ||
ModAlt = 1 << iota | ||
ModCtrl | ||
ModShift | ||
ModWin | ||
) | ||
|
||
var ( | ||
reghotkey = user32.MustFindProc("RegisterHotKey") | ||
getmsg = user32.MustFindProc("GetMessageW") | ||
) | ||
|
||
// Hotkey represents a key-combination pressed by a user | ||
type Hotkey struct { | ||
// Id, must be unique for each registered hotkey | ||
ID int // Unique id | ||
// Modifiers is a bitmask containing modifiers for the hotkey | ||
Modifiers int // Mask of modifiers | ||
// KeyCode is the keycode for the hotkey | ||
KeyCode int // Key code, e.g. 'A' | ||
} | ||
|
||
// String returns a human-friendly display name of the hotkey | ||
// such as "Hotkey[Id: 1, Alt+Ctrl+O]" | ||
func (h Hotkey) String() string { | ||
mod := &bytes.Buffer{} | ||
if h.Modifiers&ModAlt != 0 { | ||
mod.WriteString("Alt+") | ||
} | ||
if h.Modifiers&ModCtrl != 0 { | ||
mod.WriteString("Ctrl+") | ||
} | ||
if h.Modifiers&ModShift != 0 { | ||
mod.WriteString("Shift+") | ||
} | ||
if h.Modifiers&ModWin != 0 { | ||
mod.WriteString("Win+") | ||
} | ||
return fmt.Sprintf("Hotkey[Id: %d, %s%c]", h.ID, mod, h.KeyCode) | ||
} | ||
|
||
// HotkeyHandler is the callback for registered hotkeys | ||
type HotkeyHandler func(Hotkey) | ||
|
||
type msg struct { | ||
HWND uintptr | ||
UINT uintptr | ||
WPARAM int16 | ||
LPARAM int64 | ||
DWORD int32 | ||
POINT struct{ X, Y int64 } | ||
} | ||
|
||
// ListenForHotkeys registers an listens for the given global Hotkeys. If a hotkey is pressed, the hendler function is executed | ||
// This function blocks, so it shoue have it's own goroutine | ||
func ListenForHotkeys(ctx context.Context, handler HotkeyHandler, hotkeys ...*Hotkey) error { | ||
|
||
runtime.LockOSThread() | ||
defer runtime.UnlockOSThread() | ||
|
||
hotkeymap := make(map[int16]*Hotkey) | ||
for _, v := range hotkeys { | ||
hotkeymap[int16(v.ID)] = v | ||
r1, _, err := reghotkey.Call( | ||
0, uintptr(v.ID), uintptr(v.Modifiers), uintptr(v.KeyCode)) | ||
if r1 != 1 { | ||
return err | ||
} | ||
} | ||
|
||
for { | ||
if ctx != nil && ctx.Err() != nil { | ||
return nil | ||
} | ||
var msg = &msg{} | ||
getmsg.Call(uintptr(unsafe.Pointer(msg)), 0, 0, 0) | ||
|
||
// Registered id is in the WPARAM field: | ||
if id := msg.WPARAM; id != 0 { | ||
hk, exists := hotkeymap[id] | ||
if exists { | ||
handler(*hk) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.