2018-04-11 02:05:46 -04:00
|
|
|
package bootcommand
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
TODO:
|
2018-04-13 15:49:54 -04:00
|
|
|
* pc-at abstraction
|
|
|
|
* check that `<del>` works. It's different now.
|
|
|
|
* parallels
|
|
|
|
* hyperv-
|
2018-04-11 02:05:46 -04:00
|
|
|
*/
|
|
|
|
|
2018-04-12 14:34:21 -04:00
|
|
|
// KeysAction represents what we want to do with a key press.
|
|
|
|
// It can take 3 states. We either want to:
|
2018-04-11 02:05:46 -04:00
|
|
|
// * press the key once
|
|
|
|
// * press and hold
|
|
|
|
// * press and release
|
|
|
|
type KeyAction int
|
|
|
|
|
|
|
|
const (
|
2018-04-12 17:42:53 -04:00
|
|
|
KeyOn KeyAction = 1 << iota
|
2018-04-11 02:05:46 -04:00
|
|
|
KeyOff
|
|
|
|
KeyPress
|
|
|
|
)
|
|
|
|
|
|
|
|
func (k KeyAction) String() string {
|
|
|
|
switch k {
|
|
|
|
case KeyOn:
|
|
|
|
return "On"
|
|
|
|
case KeyOff:
|
|
|
|
return "Off"
|
|
|
|
case KeyPress:
|
|
|
|
return "Press"
|
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("Unknwon KeyAction %d", k))
|
|
|
|
}
|
|
|
|
|
|
|
|
type expression interface {
|
|
|
|
Do(context.Context, BCDriver) error
|
|
|
|
}
|
|
|
|
|
|
|
|
type expressionSequence []expression
|
|
|
|
|
2018-04-13 16:18:55 -04:00
|
|
|
// Do executes every expression in the sequence and then finalizes the driver.
|
2018-04-11 02:05:46 -04:00
|
|
|
func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
|
|
|
|
for _, exp := range s {
|
|
|
|
if err := exp.Do(ctx, b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-04-12 20:00:09 -04:00
|
|
|
return b.Finalize()
|
2018-04-11 02:05:46 -04:00
|
|
|
}
|
|
|
|
|
2018-04-12 14:34:21 -04:00
|
|
|
// GenerateExpressionSequence generates a sequence of expressions from the
|
2018-04-13 16:18:55 -04:00
|
|
|
// given command. This is the primary entry point to the boot command parser.
|
2018-04-11 02:05:46 -04:00
|
|
|
func GenerateExpressionSequence(command string) (expressionSequence, error) {
|
|
|
|
got, err := ParseReader("", strings.NewReader(command))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if got == nil {
|
|
|
|
return nil, fmt.Errorf("No expressions found.")
|
|
|
|
}
|
|
|
|
seq := expressionSequence{}
|
|
|
|
for _, exp := range got.([]interface{}) {
|
|
|
|
seq = append(seq, exp.(expression))
|
|
|
|
}
|
|
|
|
return seq, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type waitExpression struct {
|
|
|
|
d time.Duration
|
|
|
|
}
|
|
|
|
|
2018-04-13 16:18:55 -04:00
|
|
|
// Do waits the amount of time described by the expression. It is cancellable
|
|
|
|
// through the context.
|
2018-04-11 02:05:46 -04:00
|
|
|
func (w *waitExpression) Do(ctx context.Context, _ BCDriver) error {
|
|
|
|
log.Printf("[INFO] Waiting %s", w.d)
|
|
|
|
select {
|
|
|
|
case <-time.After(w.d):
|
|
|
|
return nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *waitExpression) String() string {
|
|
|
|
return fmt.Sprintf("Wait<%s>", w.d)
|
|
|
|
}
|
|
|
|
|
|
|
|
type specialExpression struct {
|
|
|
|
s string
|
|
|
|
action KeyAction
|
|
|
|
}
|
|
|
|
|
2018-04-13 16:18:55 -04:00
|
|
|
// Do sends the special command to the driver, along with the key action.
|
2018-04-11 02:05:46 -04:00
|
|
|
func (s *specialExpression) Do(ctx context.Context, driver BCDriver) error {
|
|
|
|
return driver.SendSpecial(s.s, s.action)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *specialExpression) String() string {
|
|
|
|
return fmt.Sprintf("Spec-%s(%s)", s.action, s.s)
|
|
|
|
}
|
|
|
|
|
|
|
|
type literal struct {
|
|
|
|
s rune
|
|
|
|
action KeyAction
|
|
|
|
}
|
|
|
|
|
2018-04-13 16:18:55 -04:00
|
|
|
// Do sends the key to the driver, along with the key action.
|
2018-04-11 02:05:46 -04:00
|
|
|
func (l *literal) Do(ctx context.Context, driver BCDriver) error {
|
|
|
|
return driver.SendKey(l.s, l.action)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *literal) String() string {
|
|
|
|
return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s))
|
|
|
|
}
|