-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsequence.go
More file actions
163 lines (140 loc) · 4.32 KB
/
sequence.go
File metadata and controls
163 lines (140 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package makc
import (
"context"
"time"
)
// InputStepKind describes one step in an InputSequence.
type InputStepKind uint8
const (
InputStepNone InputStepKind = iota
InputStepMouse
InputStepKeyboard
InputStepPause
)
// InputStep is one executable sequence step. Mouse and keyboard steps may
// contain pause events; they are executed by the corresponding device API.
type InputStep struct {
// Kind selects which payload the step executes.
Kind InputStepKind
// Mouse contains events for InputStepMouse.
Mouse []MouseEvent
// Keyboard contains events for InputStepKeyboard.
Keyboard []KeyboardEvent
// Duration is the delay for InputStepPause.
Duration time.Duration
}
// InputSequence is an ordered, cancelable list of input steps.
type InputSequence struct {
// Steps is the ordered list of operations run by Client.Run.
Steps []InputStep
}
// NewInputSequence creates a sequence from steps.
func NewInputSequence(steps ...InputStep) InputSequence {
return InputSequence{Steps: cloneInputSteps(steps)}
}
// Append returns a copy of the sequence with steps appended.
func (s InputSequence) Append(steps ...InputStep) InputSequence {
next := InputSequence{Steps: make([]InputStep, 0, len(s.Steps)+len(steps))}
next.Steps = append(next.Steps, cloneInputSteps(s.Steps)...)
next.Steps = append(next.Steps, cloneInputSteps(steps)...)
return next
}
// WithPause returns a copy of the sequence with a pause step appended.
func (s InputSequence) WithPause(duration time.Duration) InputSequence {
return s.Append(PauseStep(duration))
}
// MouseStep creates a mouse event step.
func MouseStep(events ...MouseEvent) InputStep {
return InputStep{
Kind: InputStepMouse,
Mouse: append([]MouseEvent(nil), events...),
}
}
// KeyboardStep creates a keyboard event step.
func KeyboardStep(events ...KeyboardEvent) InputStep {
return InputStep{
Kind: InputStepKeyboard,
Keyboard: append([]KeyboardEvent(nil), events...),
}
}
// PauseStep creates a sequence-level pause step.
func PauseStep(duration time.Duration) InputStep {
return InputStep{
Kind: InputStepPause,
Duration: duration,
}
}
// MoveStep creates a mouse movement step.
func MoveStep(move MouseMove) InputStep {
return MouseStep(MouseMoveEvent(move))
}
// ClickStep creates a mouse click step using a click profile.
func ClickStep(button MouseButton, profile ClickProfile) InputStep {
return MouseStep(profile.Events(button)...)
}
// DoubleClickStep creates a double-click step with fixed hold and interval
// timing.
func DoubleClickStep(button MouseButton, hold, interval time.Duration) InputStep {
return ClickStep(button, DoubleClick(hold, interval))
}
// KeyTapStep creates a key tap step with an optional hold pause.
func KeyTapStep(key Key, hold time.Duration) InputStep {
return KeyboardStep(KeyTapEventsWithHold(key, hold)...)
}
// ComboStep creates a keyboard combo step.
func ComboStep(keys ...Key) InputStep {
return KeyboardStep(ComboEvents(keys...)...)
}
// TextStep creates a text input step using a typing profile.
func TextStep(text string, profile TypingProfile) InputStep {
return KeyboardStep(profile.Events(text)...)
}
// Run executes a sequence with this client.
func (c *Client) Run(ctx context.Context, sequence InputSequence) error {
if err := c.ensureReady(ctx); err != nil {
return err
}
for _, step := range sequence.Steps {
switch step.Kind {
case InputStepNone:
continue
case InputStepMouse:
if len(step.Mouse) == 0 {
continue
}
if err := c.Mouse.Inject(ctx, step.Mouse...); err != nil {
return err
}
case InputStepKeyboard:
if len(step.Keyboard) == 0 {
continue
}
if err := c.Keyboard.Inject(ctx, step.Keyboard...); err != nil {
return err
}
case InputStepPause:
if err := sleepContext(ctx, step.Duration); err != nil {
return err
}
default:
return unsupported("unknown input sequence step")
}
}
return nil
}
// RunSteps executes steps with this client.
func (c *Client) RunSteps(ctx context.Context, steps ...InputStep) error {
return c.Run(ctx, NewInputSequence(steps...))
}
func cloneInputSteps(steps []InputStep) []InputStep {
if len(steps) == 0 {
return nil
}
out := make([]InputStep, len(steps))
for i, step := range steps {
out[i] = step
out[i].Mouse = append([]MouseEvent(nil), step.Mouse...)
out[i].Keyboard = append([]KeyboardEvent(nil), step.Keyboard...)
}
return out
}