@jian-ai/remote-control 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -4
- package/bin/computer-linux.mjs +537 -0
- package/bin/computer-macos.mjs +205 -62
- package/bin/computer-shared.mjs +61 -0
- package/bin/computer-windows.mjs +516 -0
- package/bin/computer.mjs +63 -0
- package/bin/jian-remote.mjs +819 -105
- package/linux/computer-helper.py +695 -0
- package/macos/computer-helper +0 -0
- package/macos/computer-helper.swift +416 -141
- package/macos/jian-pointer.png +0 -0
- package/package.json +3 -1
- package/windows/computer-helper.ps1 +501 -0
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jian-ai/remote-control",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Jian Remote Control worker — connect a computer to a Jian gateway over HTTPS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"bin",
|
|
15
15
|
"macos",
|
|
16
|
+
"linux",
|
|
17
|
+
"windows",
|
|
16
18
|
"README.md"
|
|
17
19
|
],
|
|
18
20
|
"engines": {
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Jian Remote Control — Windows computer-use helper
|
|
2
|
+
# JSON lines on stdin → JSON lines on stdout (same cmds as macOS/Linux helpers).
|
|
3
|
+
# Overlay: topmost transparent WinForms pointer (white + #2880F8 stroke/glow; macOS uses bitmap).
|
|
4
|
+
# Input: SendInput with save/restore so agent clicks do not strand the user cursor.
|
|
5
|
+
# English-only errors.
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
9
|
+
Add-Type -AssemblyName System.Drawing
|
|
10
|
+
|
|
11
|
+
$script:PointerSize = 36
|
|
12
|
+
$script:HotspotX = 2
|
|
13
|
+
$script:HotspotY = 2
|
|
14
|
+
$script:TipX = 0.0
|
|
15
|
+
$script:TipY = 0.0
|
|
16
|
+
$script:HasTip = $false
|
|
17
|
+
$script:OverlayForm = $null
|
|
18
|
+
$script:SessionActive = $false
|
|
19
|
+
|
|
20
|
+
Add-Type @"
|
|
21
|
+
using System;
|
|
22
|
+
using System.Runtime.InteropServices;
|
|
23
|
+
public static class JianInput {
|
|
24
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
25
|
+
public struct INPUT {
|
|
26
|
+
public uint type;
|
|
27
|
+
public InputUnion U;
|
|
28
|
+
}
|
|
29
|
+
[StructLayout(LayoutKind.Explicit)]
|
|
30
|
+
public struct InputUnion {
|
|
31
|
+
[FieldOffset(0)] public MOUSEINPUT mi;
|
|
32
|
+
[FieldOffset(0)] public KEYBDINPUT ki;
|
|
33
|
+
}
|
|
34
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
35
|
+
public struct MOUSEINPUT {
|
|
36
|
+
public int dx;
|
|
37
|
+
public int dy;
|
|
38
|
+
public uint mouseData;
|
|
39
|
+
public uint dwFlags;
|
|
40
|
+
public uint time;
|
|
41
|
+
public IntPtr dwExtraInfo;
|
|
42
|
+
}
|
|
43
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
44
|
+
public struct KEYBDINPUT {
|
|
45
|
+
public ushort wVk;
|
|
46
|
+
public ushort wScan;
|
|
47
|
+
public uint dwFlags;
|
|
48
|
+
public uint time;
|
|
49
|
+
public IntPtr dwExtraInfo;
|
|
50
|
+
}
|
|
51
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
52
|
+
public struct POINT { public int X; public int Y; }
|
|
53
|
+
|
|
54
|
+
public const uint INPUT_MOUSE = 0;
|
|
55
|
+
public const uint INPUT_KEYBOARD = 1;
|
|
56
|
+
public const uint MOUSEEVENTF_MOVE = 0x0001;
|
|
57
|
+
public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
|
|
58
|
+
public const uint MOUSEEVENTF_LEFTUP = 0x0004;
|
|
59
|
+
public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
|
|
60
|
+
public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
|
|
61
|
+
public const uint MOUSEEVENTF_WHEEL = 0x0800;
|
|
62
|
+
public const uint MOUSEEVENTF_HWHEEL = 0x1000;
|
|
63
|
+
public const uint MOUSEEVENTF_ABSOLUTE = 0x8000;
|
|
64
|
+
public const uint KEYEVENTF_KEYUP = 0x0002;
|
|
65
|
+
public const uint KEYEVENTF_UNICODE = 0x0004;
|
|
66
|
+
|
|
67
|
+
[DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
|
|
68
|
+
[DllImport("user32.dll")] public static extern bool GetCursorPos(out POINT lpPoint);
|
|
69
|
+
[DllImport("user32.dll")] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
70
|
+
[DllImport("user32.dll")] public static extern int ShowCursor(bool bShow);
|
|
71
|
+
[DllImport("user32.dll")] public static extern short VkKeyScan(char ch);
|
|
72
|
+
[DllImport("user32.dll")] public static extern uint MapVirtualKey(uint uCode, uint uMapType);
|
|
73
|
+
}
|
|
74
|
+
"@
|
|
75
|
+
|
|
76
|
+
function Write-Response($obj) {
|
|
77
|
+
$json = ($obj | ConvertTo-Json -Compress -Depth 6)
|
|
78
|
+
[Console]::Out.WriteLine($json)
|
|
79
|
+
[Console]::Out.Flush()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Write-Ok([hashtable]$extra = @{}) {
|
|
83
|
+
$body = @{ ok = $true }
|
|
84
|
+
foreach ($k in $extra.Keys) { $body[$k] = $extra[$k] }
|
|
85
|
+
Write-Response $body
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function Write-Err([string]$message) {
|
|
89
|
+
Write-Response @{ ok = $false; error = $message }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function Get-PrimaryBounds {
|
|
93
|
+
[System.Windows.Forms.Screen]::PrimaryScreen.Bounds
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function Ease-OutCubic([double]$t) {
|
|
97
|
+
$x = [Math]::Min(1.0, [Math]::Max(0.0, $t))
|
|
98
|
+
return 1.0 - [Math]::Pow(1.0 - $x, 3)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function Ensure-Overlay {
|
|
102
|
+
if ($script:OverlayForm -ne $null) { return }
|
|
103
|
+
$form = New-Object System.Windows.Forms.Form
|
|
104
|
+
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
|
|
105
|
+
$form.ShowInTaskbar = $false
|
|
106
|
+
$form.TopMost = $true
|
|
107
|
+
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
|
|
108
|
+
$form.Size = New-Object System.Drawing.Size($script:PointerSize, $script:PointerSize)
|
|
109
|
+
$form.BackColor = [System.Drawing.Color]::Magenta
|
|
110
|
+
$form.TransparencyKey = [System.Drawing.Color]::Magenta
|
|
111
|
+
$form.Opacity = 0.94
|
|
112
|
+
$form.Enabled = $false
|
|
113
|
+
$form.Add_Paint({
|
|
114
|
+
param($sender, $e)
|
|
115
|
+
$g = $e.Graphics
|
|
116
|
+
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
|
117
|
+
$pts = @(
|
|
118
|
+
(New-Object System.Drawing.Point(2, 2)),
|
|
119
|
+
(New-Object System.Drawing.Point(2, 26)),
|
|
120
|
+
(New-Object System.Drawing.Point(10, 20)),
|
|
121
|
+
(New-Object System.Drawing.Point(15, 32)),
|
|
122
|
+
(New-Object System.Drawing.Point(20, 30)),
|
|
123
|
+
(New-Object System.Drawing.Point(13, 17)),
|
|
124
|
+
(New-Object System.Drawing.Point(22, 17))
|
|
125
|
+
)
|
|
126
|
+
# Parity with macOS ship sprite: white fill + #2880F8 stroke/glow (geometric stand-in).
|
|
127
|
+
$glowBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(140, 40, 128, 248))
|
|
128
|
+
$glowPts = @(
|
|
129
|
+
(New-Object System.Drawing.Point(3, 4)),
|
|
130
|
+
(New-Object System.Drawing.Point(3, 28)),
|
|
131
|
+
(New-Object System.Drawing.Point(11, 22)),
|
|
132
|
+
(New-Object System.Drawing.Point(16, 34)),
|
|
133
|
+
(New-Object System.Drawing.Point(21, 32)),
|
|
134
|
+
(New-Object System.Drawing.Point(14, 19)),
|
|
135
|
+
(New-Object System.Drawing.Point(23, 19))
|
|
136
|
+
)
|
|
137
|
+
$g.FillPolygon($glowBrush, $glowPts)
|
|
138
|
+
$fill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(255, 255, 255, 255))
|
|
139
|
+
$g.FillPolygon($fill, $pts)
|
|
140
|
+
$pen = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(255, 40, 128, 248), 2.4)
|
|
141
|
+
$g.DrawPolygon($pen, $pts)
|
|
142
|
+
$glowBrush.Dispose(); $fill.Dispose(); $pen.Dispose()
|
|
143
|
+
})
|
|
144
|
+
$script:OverlayForm = $form
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function Position-Overlay {
|
|
148
|
+
if ($script:OverlayForm -eq $null) { return }
|
|
149
|
+
$x = [int]($script:TipX - $script:HotspotX)
|
|
150
|
+
$y = [int]($script:TipY - $script:HotspotY)
|
|
151
|
+
$script:OverlayForm.Location = New-Object System.Drawing.Point($x, $y)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function Ensure-Session {
|
|
155
|
+
Ensure-Overlay
|
|
156
|
+
if (-not $script:HasTip) {
|
|
157
|
+
$b = Get-PrimaryBounds
|
|
158
|
+
$script:TipX = $b.Width / 2
|
|
159
|
+
$script:TipY = $b.Height / 2
|
|
160
|
+
$script:HasTip = $true
|
|
161
|
+
}
|
|
162
|
+
Position-Overlay
|
|
163
|
+
if (-not $script:OverlayForm.Visible) {
|
|
164
|
+
$script:OverlayForm.Show()
|
|
165
|
+
}
|
|
166
|
+
$script:OverlayForm.Opacity = 0.94
|
|
167
|
+
$script:OverlayForm.Invalidate()
|
|
168
|
+
$script:SessionActive = $true
|
|
169
|
+
[System.Windows.Forms.Application]::DoEvents()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function Set-Idle {
|
|
173
|
+
Ensure-Session
|
|
174
|
+
$script:OverlayForm.Opacity = 0.18
|
|
175
|
+
[System.Windows.Forms.Application]::DoEvents()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function End-Session {
|
|
179
|
+
if ($script:OverlayForm -ne $null) {
|
|
180
|
+
$script:OverlayForm.Hide()
|
|
181
|
+
$script:OverlayForm.Opacity = 0
|
|
182
|
+
}
|
|
183
|
+
$script:SessionActive = $false
|
|
184
|
+
$script:HasTip = $false
|
|
185
|
+
[System.Windows.Forms.Application]::DoEvents()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function Move-Tip([double]$x, [double]$y) {
|
|
189
|
+
$script:TipX = $x
|
|
190
|
+
$script:TipY = $y
|
|
191
|
+
$script:HasTip = $true
|
|
192
|
+
Ensure-Overlay
|
|
193
|
+
if ($script:SessionActive -or $true) {
|
|
194
|
+
if (-not $script:OverlayForm.Visible) { $script:OverlayForm.Show() }
|
|
195
|
+
}
|
|
196
|
+
Position-Overlay
|
|
197
|
+
$script:OverlayForm.Invalidate()
|
|
198
|
+
[System.Windows.Forms.Application]::DoEvents()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Animate-Overlay([double]$tx, [double]$ty) {
|
|
202
|
+
Ensure-Session
|
|
203
|
+
$sx = $script:TipX
|
|
204
|
+
$sy = $script:TipY
|
|
205
|
+
$dx = $tx - $sx
|
|
206
|
+
$dy = $ty - $sy
|
|
207
|
+
$distance = [Math]::Sqrt($dx * $dx + $dy * $dy)
|
|
208
|
+
if ($distance -lt 1.5) {
|
|
209
|
+
Move-Tip $tx $ty
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
$duration = [Math]::Min(0.11, [Math]::Max(0.028, $distance * 0.00025))
|
|
213
|
+
$steps = [Math]::Max(1, [int][Math]::Ceiling($duration * 60))
|
|
214
|
+
$stepSleep = $duration / $steps
|
|
215
|
+
for ($i = 1; $i -le $steps; $i++) {
|
|
216
|
+
$t = Ease-OutCubic ($i / $steps)
|
|
217
|
+
Move-Tip ($sx + $dx * $t) ($sy + $dy * $t)
|
|
218
|
+
if ($i -lt $steps) { Start-Sleep -Milliseconds ([Math]::Max(1, [int]($stepSleep * 1000))) }
|
|
219
|
+
}
|
|
220
|
+
Move-Tip $tx $ty
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function Get-CursorPos {
|
|
224
|
+
$p = New-Object JianInput+POINT
|
|
225
|
+
[void][JianInput]::GetCursorPos([ref]$p)
|
|
226
|
+
return @($p.X, $p.Y)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function Warp-Cursor([int]$x, [int]$y) {
|
|
230
|
+
[void][JianInput]::SetCursorPos($x, $y)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function With-PreservedCursor([scriptblock]$body) {
|
|
234
|
+
$saved = Get-CursorPos
|
|
235
|
+
[void][JianInput]::ShowCursor($false)
|
|
236
|
+
try {
|
|
237
|
+
& $body
|
|
238
|
+
} finally {
|
|
239
|
+
Warp-Cursor $saved[0] $saved[1]
|
|
240
|
+
Start-Sleep -Milliseconds 8
|
|
241
|
+
[void][JianInput]::ShowCursor($true)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function Send-MouseClick([string]$button, [int]$count) {
|
|
246
|
+
$down = if ($button -eq 'right') { [JianInput]::MOUSEEVENTF_RIGHTDOWN } else { [JianInput]::MOUSEEVENTF_LEFTDOWN }
|
|
247
|
+
$up = if ($button -eq 'right') { [JianInput]::MOUSEEVENTF_RIGHTUP } else { [JianInput]::MOUSEEVENTF_LEFTUP }
|
|
248
|
+
for ($i = 0; $i -lt $count; $i++) {
|
|
249
|
+
$inputs = @(
|
|
250
|
+
New-Object JianInput+INPUT
|
|
251
|
+
New-Object JianInput+INPUT
|
|
252
|
+
)
|
|
253
|
+
$inputs[0].type = [JianInput]::INPUT_MOUSE
|
|
254
|
+
$inputs[0].U.mi.dwFlags = $down
|
|
255
|
+
$inputs[1].type = [JianInput]::INPUT_MOUSE
|
|
256
|
+
$inputs[1].U.mi.dwFlags = $up
|
|
257
|
+
[void][JianInput]::SendInput(2, $inputs, [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
258
|
+
if ($i -lt $count - 1) { Start-Sleep -Milliseconds 40 }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function Click-At([double]$x, [double]$y, [string]$button, [int]$count) {
|
|
263
|
+
Animate-Overlay $x $y
|
|
264
|
+
With-PreservedCursor {
|
|
265
|
+
Warp-Cursor ([int]$x) ([int]$y)
|
|
266
|
+
Send-MouseClick $button $count
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function Drag-To([double]$fx, [double]$fy, [double]$tx, [double]$ty) {
|
|
271
|
+
Animate-Overlay $fx $fy
|
|
272
|
+
With-PreservedCursor {
|
|
273
|
+
Warp-Cursor ([int]$fx) ([int]$fy)
|
|
274
|
+
$down = New-Object JianInput+INPUT
|
|
275
|
+
$down.type = [JianInput]::INPUT_MOUSE
|
|
276
|
+
$down.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_LEFTDOWN
|
|
277
|
+
[void][JianInput]::SendInput(1, @($down), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
278
|
+
$distance = [Math]::Sqrt(($tx - $fx) * ($tx - $fx) + ($ty - $fy) * ($ty - $fy))
|
|
279
|
+
$steps = [Math]::Max(6, [Math]::Min(14, [int]($distance / 40) + 6))
|
|
280
|
+
for ($i = 1; $i -le $steps; $i++) {
|
|
281
|
+
$t = Ease-OutCubic ($i / $steps)
|
|
282
|
+
$px = $fx + ($tx - $fx) * $t
|
|
283
|
+
$py = $fy + ($ty - $fy) * $t
|
|
284
|
+
Move-Tip $px $py
|
|
285
|
+
Warp-Cursor ([int]$px) ([int]$py)
|
|
286
|
+
Start-Sleep -Milliseconds 6
|
|
287
|
+
}
|
|
288
|
+
$up = New-Object JianInput+INPUT
|
|
289
|
+
$up.type = [JianInput]::INPUT_MOUSE
|
|
290
|
+
$up.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_LEFTUP
|
|
291
|
+
[void][JianInput]::SendInput(1, @($up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
292
|
+
}
|
|
293
|
+
Move-Tip $tx $ty
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function Scroll-At([double]$x, [double]$y, [string]$direction, [int]$amount) {
|
|
297
|
+
Animate-Overlay $x $y
|
|
298
|
+
$wheel = 0
|
|
299
|
+
$hwheel = 0
|
|
300
|
+
switch ($direction) {
|
|
301
|
+
'up' { $wheel = 120 }
|
|
302
|
+
'down' { $wheel = -120 }
|
|
303
|
+
'left' { $hwheel = -120 }
|
|
304
|
+
'right' { $hwheel = 120 }
|
|
305
|
+
}
|
|
306
|
+
With-PreservedCursor {
|
|
307
|
+
Warp-Cursor ([int]$x) ([int]$y)
|
|
308
|
+
for ($i = 0; $i -lt [Math]::Max(1, $amount); $i++) {
|
|
309
|
+
$inp = New-Object JianInput+INPUT
|
|
310
|
+
$inp.type = [JianInput]::INPUT_MOUSE
|
|
311
|
+
if ($wheel -ne 0) {
|
|
312
|
+
$inp.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_WHEEL
|
|
313
|
+
$inp.U.mi.mouseData = [uint32]([int32]$wheel)
|
|
314
|
+
} else {
|
|
315
|
+
$inp.U.mi.dwFlags = [JianInput]::MOUSEEVENTF_HWHEEL
|
|
316
|
+
$inp.U.mi.mouseData = [uint32]([int32]$hwheel)
|
|
317
|
+
}
|
|
318
|
+
[void][JianInput]::SendInput(1, @($inp), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
319
|
+
Start-Sleep -Milliseconds 12
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function Type-Text([string]$text) {
|
|
325
|
+
foreach ($ch in $text.ToCharArray()) {
|
|
326
|
+
if ($ch -eq "`n" -or $ch -eq "`r") {
|
|
327
|
+
Post-Key 0x0D
|
|
328
|
+
continue
|
|
329
|
+
}
|
|
330
|
+
if ($ch -eq "`t") {
|
|
331
|
+
Post-Key 0x09
|
|
332
|
+
continue
|
|
333
|
+
}
|
|
334
|
+
$inputs = @(
|
|
335
|
+
New-Object JianInput+INPUT
|
|
336
|
+
New-Object JianInput+INPUT
|
|
337
|
+
)
|
|
338
|
+
$inputs[0].type = [JianInput]::INPUT_KEYBOARD
|
|
339
|
+
$inputs[0].U.ki.wScan = [uint16][int][char]$ch
|
|
340
|
+
$inputs[0].U.ki.dwFlags = [JianInput]::KEYEVENTF_UNICODE
|
|
341
|
+
$inputs[1].type = [JianInput]::INPUT_KEYBOARD
|
|
342
|
+
$inputs[1].U.ki.wScan = [uint16][int][char]$ch
|
|
343
|
+
$inputs[1].U.ki.dwFlags = [JianInput]::KEYEVENTF_UNICODE -bor [JianInput]::KEYEVENTF_KEYUP
|
|
344
|
+
[void][JianInput]::SendInput(2, $inputs, [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
345
|
+
Start-Sleep -Milliseconds 2
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function Post-Key([ushort]$vk, [uint32]$flags = 0) {
|
|
350
|
+
$down = New-Object JianInput+INPUT
|
|
351
|
+
$down.type = [JianInput]::INPUT_KEYBOARD
|
|
352
|
+
$down.U.ki.wVk = $vk
|
|
353
|
+
$down.U.ki.dwFlags = $flags
|
|
354
|
+
$up = New-Object JianInput+INPUT
|
|
355
|
+
$up.type = [JianInput]::INPUT_KEYBOARD
|
|
356
|
+
$up.U.ki.wVk = $vk
|
|
357
|
+
$up.U.ki.dwFlags = $flags -bor [JianInput]::KEYEVENTF_KEYUP
|
|
358
|
+
[void][JianInput]::SendInput(2, @($down, $up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function Parse-And-PostKey([string]$keySpec) {
|
|
362
|
+
$parts = $keySpec.Split('+') | ForEach-Object { $_.Trim().ToLowerInvariant() } | Where-Object { $_ }
|
|
363
|
+
if (-not $parts) { return $false }
|
|
364
|
+
$mods = @()
|
|
365
|
+
$key = $null
|
|
366
|
+
foreach ($part in $parts) {
|
|
367
|
+
switch ($part) {
|
|
368
|
+
{ $_ -in @('cmd','command','meta','super','win','windows') } { $mods += 0x5B; break }
|
|
369
|
+
{ $_ -in @('ctrl','control') } { $mods += 0x11; break }
|
|
370
|
+
{ $_ -in @('alt','option','opt') } { $mods += 0x12; break }
|
|
371
|
+
'shift' { $mods += 0x10; break }
|
|
372
|
+
default {
|
|
373
|
+
if ($null -ne $key) { return $false }
|
|
374
|
+
$key = $part
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if ($null -eq $key) { return $false }
|
|
379
|
+
$named = @{
|
|
380
|
+
return = 0x0D; enter = 0x0D; tab = 0x09; escape = 0x1B; esc = 0x1B
|
|
381
|
+
delete = 0x2E; backspace = 0x08; space = 0x20
|
|
382
|
+
up = 0x26; down = 0x28; left = 0x25; right = 0x27
|
|
383
|
+
home = 0x24; end = 0x23; pageup = 0x21; pagedown = 0x22
|
|
384
|
+
}
|
|
385
|
+
$vk = $null
|
|
386
|
+
if ($named.ContainsKey($key)) { $vk = [ushort]$named[$key] }
|
|
387
|
+
elseif ($key.Length -eq 1) {
|
|
388
|
+
$scan = [JianInput]::VkKeyScan([char]$key[0])
|
|
389
|
+
if ($scan -eq -1) { return $false }
|
|
390
|
+
$vk = [ushort]($scan -band 0xFF)
|
|
391
|
+
}
|
|
392
|
+
elseif ($key.StartsWith('f') -and $key.Substring(1) -match '^\d+$') {
|
|
393
|
+
$n = [int]$key.Substring(1)
|
|
394
|
+
if ($n -ge 1 -and $n -le 12) { $vk = [ushort](0x70 + $n - 1) }
|
|
395
|
+
}
|
|
396
|
+
if ($null -eq $vk) { return $false }
|
|
397
|
+
foreach ($m in $mods) {
|
|
398
|
+
$down = New-Object JianInput+INPUT
|
|
399
|
+
$down.type = [JianInput]::INPUT_KEYBOARD
|
|
400
|
+
$down.U.ki.wVk = [ushort]$m
|
|
401
|
+
[void][JianInput]::SendInput(1, @($down), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
402
|
+
}
|
|
403
|
+
Post-Key $vk
|
|
404
|
+
for ($i = $mods.Count - 1; $i -ge 0; $i--) {
|
|
405
|
+
$up = New-Object JianInput+INPUT
|
|
406
|
+
$up.type = [JianInput]::INPUT_KEYBOARD
|
|
407
|
+
$up.U.ki.wVk = [ushort]$mods[$i]
|
|
408
|
+
$up.U.ki.dwFlags = [JianInput]::KEYEVENTF_KEYUP
|
|
409
|
+
[void][JianInput]::SendInput(1, @($up), [System.Runtime.InteropServices.Marshal]::SizeOf([type][JianInput+INPUT]))
|
|
410
|
+
}
|
|
411
|
+
return $true
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function Geometry-Response {
|
|
415
|
+
$b = Get-PrimaryBounds
|
|
416
|
+
return @{
|
|
417
|
+
ok = $true
|
|
418
|
+
originX = 0
|
|
419
|
+
originY = 0
|
|
420
|
+
pointWidth = [double]$b.Width
|
|
421
|
+
pointHeight = [double]$b.Height
|
|
422
|
+
pixelWidth = [double]$b.Width
|
|
423
|
+
pixelHeight = [double]$b.Height
|
|
424
|
+
scaleFactor = 1.0
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function Handle-Command($obj) {
|
|
429
|
+
$cmd = [string]$obj.cmd
|
|
430
|
+
switch ($cmd) {
|
|
431
|
+
'ping' { Write-Ok; break }
|
|
432
|
+
'geometry' { Write-Response (Geometry-Response); break }
|
|
433
|
+
'ensureSession' { Ensure-Session; Write-Ok; break }
|
|
434
|
+
'setIdle' { Set-Idle; Write-Ok; break }
|
|
435
|
+
'endSession' { End-Session; Write-Ok; break }
|
|
436
|
+
'move' {
|
|
437
|
+
Animate-Overlay ([double]$obj.pointX) ([double]$obj.pointY)
|
|
438
|
+
Write-Ok
|
|
439
|
+
break
|
|
440
|
+
}
|
|
441
|
+
'click' {
|
|
442
|
+
$button = if ($obj.button) { [string]$obj.button } else { 'left' }
|
|
443
|
+
$count = if ($obj.count) { [int]$obj.count } else { 1 }
|
|
444
|
+
Click-At $script:TipX $script:TipY $button.ToLowerInvariant() $count
|
|
445
|
+
Write-Ok
|
|
446
|
+
break
|
|
447
|
+
}
|
|
448
|
+
'clickAt' {
|
|
449
|
+
$button = if ($obj.button) { [string]$obj.button } else { 'left' }
|
|
450
|
+
$count = if ($obj.count) { [int]$obj.count } else { 1 }
|
|
451
|
+
Click-At ([double]$obj.pointX) ([double]$obj.pointY) $button.ToLowerInvariant() $count
|
|
452
|
+
Write-Ok
|
|
453
|
+
break
|
|
454
|
+
}
|
|
455
|
+
'drag' {
|
|
456
|
+
Drag-To ([double]$obj.fromX) ([double]$obj.fromY) ([double]$obj.toX) ([double]$obj.toY)
|
|
457
|
+
Write-Ok
|
|
458
|
+
break
|
|
459
|
+
}
|
|
460
|
+
'type' {
|
|
461
|
+
Type-Text ([string]$obj.text)
|
|
462
|
+
Write-Ok
|
|
463
|
+
break
|
|
464
|
+
}
|
|
465
|
+
'key' {
|
|
466
|
+
if (Parse-And-PostKey ([string]$obj.key)) { Write-Ok }
|
|
467
|
+
else { Write-Err ("unrecognized key: " + $obj.key) }
|
|
468
|
+
break
|
|
469
|
+
}
|
|
470
|
+
'scroll' {
|
|
471
|
+
Scroll-At ([double]$obj.pointX) ([double]$obj.pointY) ([string]$obj.direction).ToLowerInvariant() ([int]$obj.amount)
|
|
472
|
+
Write-Ok
|
|
473
|
+
break
|
|
474
|
+
}
|
|
475
|
+
default { Write-Err ("unknown cmd: " + $cmd) }
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
480
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
481
|
+
|
|
482
|
+
while ($true) {
|
|
483
|
+
$line = [Console]::In.ReadLine()
|
|
484
|
+
if ($null -eq $line) { break }
|
|
485
|
+
$line = $line.Trim()
|
|
486
|
+
if (-not $line) { continue }
|
|
487
|
+
try {
|
|
488
|
+
$obj = $line | ConvertFrom-Json
|
|
489
|
+
Handle-Command $obj
|
|
490
|
+
} catch {
|
|
491
|
+
[Console]::Error.WriteLine("computer-helper: $($_.Exception.Message)")
|
|
492
|
+
Write-Err $_.Exception.Message
|
|
493
|
+
}
|
|
494
|
+
[System.Windows.Forms.Application]::DoEvents()
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
End-Session
|
|
498
|
+
if ($script:OverlayForm -ne $null) {
|
|
499
|
+
$script:OverlayForm.Close()
|
|
500
|
+
$script:OverlayForm.Dispose()
|
|
501
|
+
}
|