node-native-win-utils 1.0.3 → 1.1.0
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 +72 -6
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/package.json +2 -1
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/src/cpp/capturewindow.cpp +183 -0
- package/src/cpp/getWindowData.cpp +46 -0
- package/src/cpp/helpers.cpp +15 -0
- package/src/cpp/helpers.h +8 -0
- package/src/cpp/keyboard.cpp +205 -0
- package/src/cpp/main.cpp +21 -0
- package/src/cpp/mouse.cpp +175 -0
package/README.md
CHANGED
|
@@ -2,25 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
# Node Native Win Utils
|
|
4
4
|
|
|
5
|
-
I did it for myself because I didn't feel like dealing with libraries like 'node-ffi' to implement this functionality. Maybe someone will find it useful.
|
|
5
|
+
I did it for myself because I didn't feel like dealing with libraries like 'node-ffi' to implement this functionality. Maybe someone will find it useful. It's WINDOWS OS ONLY
|
|
6
6
|
|
|
7
|
-
This package
|
|
7
|
+
This package is a native addon for Node.js that allows you to perform various utility operations on Windows systems. It includes key event listeners, window data retrieval, window screenshot capture functionality, mouse movement, mouse click, mouse drag, and typing functionality.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
11
|
You can install the package using npm:
|
|
12
12
|
|
|
13
13
|
```shell
|
|
14
|
-
|
|
15
|
-
npm install node-native-win-utils
|
|
16
|
-
|
|
14
|
+
npm install node-native-win-utils
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
## Usage
|
|
20
18
|
|
|
21
19
|
### Importing the Package
|
|
22
20
|
|
|
23
|
-
To use the package, import the necessary functions and
|
|
21
|
+
To use the package, import the necessary functions, types, and classes:
|
|
24
22
|
|
|
25
23
|
```javascript
|
|
26
24
|
import {
|
|
@@ -28,6 +26,10 @@ import {
|
|
|
28
26
|
keyUpHandler,
|
|
29
27
|
getWindowData,
|
|
30
28
|
captureWindow,
|
|
29
|
+
mouseMove,
|
|
30
|
+
mouseClick,
|
|
31
|
+
mouseDrag,
|
|
32
|
+
typeString,
|
|
31
33
|
KeyListener,
|
|
32
34
|
} from "node-native-win-utils";
|
|
33
35
|
```
|
|
@@ -72,6 +74,43 @@ captureWindow("Window Name", "output.png");
|
|
|
72
74
|
// Output: output.png with a screenshot of the window
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
### Mouse Movement
|
|
78
|
+
|
|
79
|
+
The `mouseMove` function allows you to move the mouse to a specific position on the screen. Provide the `posX` and `posY` coordinates as parameters:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
mouseMove(100, 200);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Mouse Click
|
|
86
|
+
|
|
87
|
+
The `mouseClick` function allows you to perform a mouse click event. Optionally, you can specify the mouse button as a parameter (`"left"`, `"middle"`, or `"right"`). If no button is specified, a left mouse click is performed by default:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
mouseClick(); // Left mouse click
|
|
91
|
+
mouseClick("right"); // Right mouse click
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Mouse Drag
|
|
95
|
+
|
|
96
|
+
The `mouseDrag` function allows you to simulate dragging the mouse from one position to another. Provide the starting and ending coordinates (`startX`, `startY`, `endX`, `endY`) as parameters. Optionally, you can specify the speed at which the mouse should be dragged:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
mouseDrag(100, 200, 300, 400);
|
|
100
|
+
mouseDrag(100, 200, 300, 400, 100); // Drag with speed 100
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Typing
|
|
104
|
+
|
|
105
|
+
The `typeString` function allows you to simulate typing a string of characters. Provide the string to type as the `stringToType` parameter. Optionally, you can specify
|
|
106
|
+
|
|
107
|
+
a delay between each character (in milliseconds) using the `delay` parameter:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
typeString("Hello, world!");
|
|
111
|
+
typeString("Hello, world!", 100); // Type with a delay of 100ms between characters
|
|
112
|
+
```
|
|
113
|
+
|
|
75
114
|
### Key Listener Class
|
|
76
115
|
|
|
77
116
|
The `KeyListener` class extends the EventEmitter class and simplifies working with the `keyDownHandler` and `keyUpHandler` functions. You can register event listeners for the "keyDown" and "keyUp" events using the `on` method:
|
|
@@ -100,6 +139,10 @@ listener.on("keyUp", (data) => {
|
|
|
100
139
|
| keyUpHandler | `callback: (keyCode: number) => void` | `void` |
|
|
101
140
|
| getWindowData | `windowName: string` | `WindowData` |
|
|
102
141
|
| captureWindow | `windowName: string, outputPath: string` | `void` |
|
|
142
|
+
| mouseMove | `posX: number, posY: number` | `boolean` |
|
|
143
|
+
| mouseClick | `button?: "left" \| "middle" \| "right"` | `boolean` |
|
|
144
|
+
| mouseDrag | `startX: number, startY: number, endX: number, endY: number, speed?: number` | `boolean` |
|
|
145
|
+
| typeString | `stringToType: string, delay?: number` | `boolean` |
|
|
103
146
|
| KeyListener.on | `event: "keyDown"`,<br>`callback: (data: { keyCode: number; keyName: string }) => void` | `this` (EventListener) |
|
|
104
147
|
| KeyListener.on | `event: "keyUp"`,<br>`callback: (data: { keyCode: number; keyName: string }) => void` | `this` (EventListener) |
|
|
105
148
|
|
|
@@ -113,6 +156,10 @@ import {
|
|
|
113
156
|
keyUpHandler,
|
|
114
157
|
getWindowData,
|
|
115
158
|
captureWindow,
|
|
159
|
+
mouseMove,
|
|
160
|
+
mouseClick,
|
|
161
|
+
mouseDrag,
|
|
162
|
+
typeString,
|
|
116
163
|
KeyListener,
|
|
117
164
|
} from "node-native-win-utils";
|
|
118
165
|
|
|
@@ -144,6 +191,25 @@ captureWindow("My Window", "output.png");
|
|
|
144
191
|
|
|
145
192
|
// Output: output.png with a screenshot of the window
|
|
146
193
|
|
|
194
|
+
// Move the mouse
|
|
195
|
+
|
|
196
|
+
mouseMove(100, 200);
|
|
197
|
+
|
|
198
|
+
// Perform mouse click
|
|
199
|
+
|
|
200
|
+
mouseClick(); // Left mouse click
|
|
201
|
+
mouseClick("right"); // Right mouse click
|
|
202
|
+
|
|
203
|
+
// Simulate mouse drag
|
|
204
|
+
|
|
205
|
+
mouseDrag(100, 200, 300, 400);
|
|
206
|
+
mouseDrag(100, 200, 300, 400, 100); // Drag with speed 100
|
|
207
|
+
|
|
208
|
+
// Simulate typing
|
|
209
|
+
|
|
210
|
+
typeString("Hello, world!");
|
|
211
|
+
typeString("Hello, world!", 100); // Type with a delay of 100ms between characters
|
|
212
|
+
|
|
147
213
|
// Use KeyListener class
|
|
148
214
|
|
|
149
215
|
const listener = new KeyListener();
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export type GetWindowData = (windowName: string) => WindowData;
|
|
|
10
10
|
export type CaptureWindow = (windowName: string, outputPath: string) => void;
|
|
11
11
|
export type KeyDownHandler = (callback: (keyCode: number) => void) => void;
|
|
12
12
|
export type KeyUpHandler = (callback: (keyCode: number) => void) => void;
|
|
13
|
+
export type MouseMove = (posX: number, posY: number) => boolean;
|
|
14
|
+
export type MouseClick = (button?: "left" | "middle" | "right") => boolean;
|
|
15
|
+
export type TypeString = (stringToType: string, delay?: number) => boolean;
|
|
16
|
+
export type MouseDrag = (starX: number, startY: number, endX: Number, endY: number, speed?: number) => boolean;
|
|
13
17
|
export interface KeyListener extends EventEmitter {
|
|
14
18
|
on(event: "keyDown", callback: (data: {
|
|
15
19
|
keyCode: number;
|
|
@@ -20,8 +24,8 @@ export interface KeyListener extends EventEmitter {
|
|
|
20
24
|
keyName: string;
|
|
21
25
|
}) => void): this;
|
|
22
26
|
}
|
|
23
|
-
declare const keyDownHandler: KeyDownHandler, keyUpHandler: KeyUpHandler, getWindowData: GetWindowData, captureWindow: CaptureWindow;
|
|
27
|
+
declare const keyDownHandler: KeyDownHandler, keyUpHandler: KeyUpHandler, getWindowData: GetWindowData, captureWindow: CaptureWindow, mouseMove: MouseMove, mouseClick: MouseClick, mouseDrag: MouseDrag, typeString: TypeString;
|
|
24
28
|
export declare class KeyListener extends EventEmitter {
|
|
25
29
|
constructor();
|
|
26
30
|
}
|
|
27
|
-
export { keyDownHandler, keyUpHandler, getWindowData, captureWindow };
|
|
31
|
+
export { keyDownHandler, keyUpHandler, getWindowData, captureWindow, mouseMove, mouseClick, mouseDrag, typeString, };
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.captureWindow = exports.getWindowData = exports.keyUpHandler = exports.keyDownHandler = exports.KeyListener = void 0;
|
|
6
|
+
exports.typeString = exports.mouseDrag = exports.mouseClick = exports.mouseMove = exports.captureWindow = exports.getWindowData = exports.keyUpHandler = exports.keyDownHandler = exports.KeyListener = void 0;
|
|
7
7
|
const EventEmitter = require("events");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const bindings = require("node-gyp-build")(path_1.default.resolve(__dirname, ".."));
|
|
10
10
|
const keyCodes_1 = __importDefault(require("./keyCodes"));
|
|
11
|
-
const { keyDownHandler, keyUpHandler, getWindowData, captureWindow, } = bindings;
|
|
11
|
+
const { keyDownHandler, keyUpHandler, getWindowData, captureWindow, mouseMove, mouseClick, mouseDrag, typeString, } = bindings;
|
|
12
12
|
exports.keyDownHandler = keyDownHandler;
|
|
13
13
|
exports.keyUpHandler = keyUpHandler;
|
|
14
14
|
exports.getWindowData = getWindowData;
|
|
15
15
|
exports.captureWindow = captureWindow;
|
|
16
|
+
exports.mouseMove = mouseMove;
|
|
17
|
+
exports.mouseClick = mouseClick;
|
|
18
|
+
exports.mouseDrag = mouseDrag;
|
|
19
|
+
exports.typeString = typeString;
|
|
16
20
|
class KeyListener extends EventEmitter {
|
|
17
21
|
constructor() {
|
|
18
22
|
super();
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-native-win-utils",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"author": "RynerNO",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"git": "https://github.com/RynerNO/node-native-win-utils.git",
|
|
6
7
|
"description": "Native addon for Node.js providing utility operations on Windows systems",
|
|
7
8
|
"keywords": [
|
|
8
9
|
"node.js",
|
|
Binary file
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#include <iostream>
|
|
3
|
+
#include <Windows.h>
|
|
4
|
+
#include <dxgi.h>
|
|
5
|
+
#include <inspectable.h>
|
|
6
|
+
#include <dxgi1_2.h>
|
|
7
|
+
#include <d3d11.h>
|
|
8
|
+
#include <winrt/Windows.Foundation.h>
|
|
9
|
+
#include <winrt/Windows.System.h>
|
|
10
|
+
#include <winrt/Windows.Graphics.Capture.h>
|
|
11
|
+
#include <windows.graphics.capture.interop.h>
|
|
12
|
+
#include <windows.graphics.directx.direct3d11.interop.h>
|
|
13
|
+
#include <roerrorapi.h>
|
|
14
|
+
#include <shlobj_core.h>
|
|
15
|
+
#include <dwmapi.h>
|
|
16
|
+
#include <helpers.h>
|
|
17
|
+
|
|
18
|
+
#pragma comment(lib, "Dwmapi.lib")
|
|
19
|
+
#pragma comment(lib, "windowsapp.lib")
|
|
20
|
+
|
|
21
|
+
void CaptureWindow(const Napi::CallbackInfo &info)
|
|
22
|
+
{
|
|
23
|
+
|
|
24
|
+
Napi::Env env = info.Env();
|
|
25
|
+
|
|
26
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString())
|
|
27
|
+
{
|
|
28
|
+
Napi::TypeError::New(env, "Window name and file path must be provided as strings").ThrowAsJavaScriptException();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
std::string windowName = info[0].As<Napi::String>().Utf8Value();
|
|
33
|
+
std::string filePath = info[1].As<Napi::String>().Utf8Value();
|
|
34
|
+
std::wstring outputFilePath = ToWChar(filePath);
|
|
35
|
+
HWND hwndTarget = GetWindowByName(windowName.c_str());
|
|
36
|
+
// Init COM
|
|
37
|
+
winrt::init_apartment(winrt::apartment_type::multi_threaded);
|
|
38
|
+
|
|
39
|
+
// Create Direct 3D Device
|
|
40
|
+
winrt::com_ptr<ID3D11Device> d3dDevice;
|
|
41
|
+
|
|
42
|
+
winrt::check_hresult(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
|
43
|
+
nullptr, 0, D3D11_SDK_VERSION, d3dDevice.put(), nullptr, nullptr));
|
|
44
|
+
|
|
45
|
+
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device;
|
|
46
|
+
const auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
|
|
47
|
+
{
|
|
48
|
+
winrt::com_ptr<::IInspectable> inspectable;
|
|
49
|
+
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), inspectable.put()));
|
|
50
|
+
device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
auto idxgiDevice2 = dxgiDevice.as<IDXGIDevice2>();
|
|
54
|
+
winrt::com_ptr<IDXGIAdapter> adapter;
|
|
55
|
+
winrt::check_hresult(idxgiDevice2->GetParent(winrt::guid_of<IDXGIAdapter>(), adapter.put_void()));
|
|
56
|
+
winrt::com_ptr<IDXGIFactory2> factory;
|
|
57
|
+
winrt::check_hresult(adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void()));
|
|
58
|
+
|
|
59
|
+
ID3D11DeviceContext *d3dContext = nullptr;
|
|
60
|
+
d3dDevice->GetImmediateContext(&d3dContext);
|
|
61
|
+
|
|
62
|
+
RECT rect{};
|
|
63
|
+
DwmGetWindowAttribute(hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT));
|
|
64
|
+
const auto size = winrt::Windows::Graphics::SizeInt32{rect.right - rect.left, rect.bottom - rect.top};
|
|
65
|
+
|
|
66
|
+
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_framePool =
|
|
67
|
+
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
|
|
68
|
+
device,
|
|
69
|
+
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
|
70
|
+
2,
|
|
71
|
+
size);
|
|
72
|
+
|
|
73
|
+
const auto activationFactory = winrt::get_activation_factory<
|
|
74
|
+
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
|
75
|
+
auto interopFactory = activationFactory.as<IGraphicsCaptureItemInterop>();
|
|
76
|
+
winrt::Windows::Graphics::Capture::GraphicsCaptureItem captureItem = {nullptr};
|
|
77
|
+
interopFactory->CreateForWindow(hwndTarget, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
|
|
78
|
+
reinterpret_cast<void **>(winrt::put_abi(captureItem)));
|
|
79
|
+
|
|
80
|
+
auto isFrameArrived = false;
|
|
81
|
+
winrt::com_ptr<ID3D11Texture2D> texture;
|
|
82
|
+
const auto session = m_framePool.CreateCaptureSession(captureItem);
|
|
83
|
+
m_framePool.FrameArrived([&](auto &framePool, auto &)
|
|
84
|
+
{
|
|
85
|
+
if (isFrameArrived) return;
|
|
86
|
+
auto frame = framePool.TryGetNextFrame();
|
|
87
|
+
|
|
88
|
+
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
|
|
89
|
+
IDirect3DDxgiInterfaceAccess : ::IUnknown
|
|
90
|
+
{
|
|
91
|
+
virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
auto access = frame.Surface().as<IDirect3DDxgiInterfaceAccess>();
|
|
95
|
+
access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void());
|
|
96
|
+
isFrameArrived = true;
|
|
97
|
+
return; });
|
|
98
|
+
|
|
99
|
+
session.IsCursorCaptureEnabled(false);
|
|
100
|
+
session.StartCapture();
|
|
101
|
+
|
|
102
|
+
// Message pump
|
|
103
|
+
MSG msg;
|
|
104
|
+
clock_t timer = clock();
|
|
105
|
+
while (!isFrameArrived)
|
|
106
|
+
{
|
|
107
|
+
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
|
|
108
|
+
DispatchMessage(&msg);
|
|
109
|
+
|
|
110
|
+
if (clock() - timer > 20000)
|
|
111
|
+
{
|
|
112
|
+
// TODO: try to make here a better error handling
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
session.Close();
|
|
118
|
+
|
|
119
|
+
D3D11_TEXTURE2D_DESC capturedTextureDesc;
|
|
120
|
+
texture->GetDesc(&capturedTextureDesc);
|
|
121
|
+
|
|
122
|
+
capturedTextureDesc.Usage = D3D11_USAGE_STAGING;
|
|
123
|
+
capturedTextureDesc.BindFlags = 0;
|
|
124
|
+
capturedTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
125
|
+
capturedTextureDesc.MiscFlags = 0;
|
|
126
|
+
|
|
127
|
+
winrt::com_ptr<ID3D11Texture2D> userTexture = nullptr;
|
|
128
|
+
winrt::check_hresult(d3dDevice->CreateTexture2D(&capturedTextureDesc, NULL, userTexture.put()));
|
|
129
|
+
|
|
130
|
+
d3dContext->CopyResource(userTexture.get(), texture.get());
|
|
131
|
+
|
|
132
|
+
D3D11_MAPPED_SUBRESOURCE resource;
|
|
133
|
+
winrt::check_hresult(d3dContext->Map(userTexture.get(), NULL, D3D11_MAP_READ, 0, &resource));
|
|
134
|
+
|
|
135
|
+
BITMAPINFO lBmpInfo;
|
|
136
|
+
|
|
137
|
+
// BMP 32 bpp
|
|
138
|
+
ZeroMemory(&lBmpInfo, sizeof(BITMAPINFO));
|
|
139
|
+
lBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
140
|
+
lBmpInfo.bmiHeader.biBitCount = 32;
|
|
141
|
+
lBmpInfo.bmiHeader.biCompression = BI_RGB;
|
|
142
|
+
lBmpInfo.bmiHeader.biWidth = capturedTextureDesc.Width;
|
|
143
|
+
lBmpInfo.bmiHeader.biHeight = capturedTextureDesc.Height;
|
|
144
|
+
lBmpInfo.bmiHeader.biPlanes = 1;
|
|
145
|
+
lBmpInfo.bmiHeader.biSizeImage = capturedTextureDesc.Width * capturedTextureDesc.Height * 4;
|
|
146
|
+
|
|
147
|
+
std::unique_ptr<BYTE> pBuf(new BYTE[lBmpInfo.bmiHeader.biSizeImage]);
|
|
148
|
+
UINT lBmpRowPitch = capturedTextureDesc.Width * 4;
|
|
149
|
+
auto sptr = static_cast<BYTE *>(resource.pData);
|
|
150
|
+
auto dptr = pBuf.get() + lBmpInfo.bmiHeader.biSizeImage - lBmpRowPitch;
|
|
151
|
+
|
|
152
|
+
UINT lRowPitch = std::min<UINT>(lBmpRowPitch, resource.RowPitch);
|
|
153
|
+
|
|
154
|
+
for (size_t h = 0; h < capturedTextureDesc.Height; ++h)
|
|
155
|
+
{
|
|
156
|
+
memcpy_s(dptr, lBmpRowPitch, sptr, lRowPitch);
|
|
157
|
+
sptr += resource.RowPitch;
|
|
158
|
+
dptr -= lBmpRowPitch;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Save bitmap buffer into the provided output file path
|
|
162
|
+
FILE *lfile = nullptr;
|
|
163
|
+
|
|
164
|
+
if (auto lerr = _wfopen_s(&lfile, outputFilePath.c_str(), L"wb"); lerr != 0)
|
|
165
|
+
return;
|
|
166
|
+
|
|
167
|
+
if (lfile != nullptr)
|
|
168
|
+
{
|
|
169
|
+
BITMAPFILEHEADER bmpFileHeader;
|
|
170
|
+
|
|
171
|
+
bmpFileHeader.bfReserved1 = 0;
|
|
172
|
+
bmpFileHeader.bfReserved2 = 0;
|
|
173
|
+
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lBmpInfo.bmiHeader.biSizeImage;
|
|
174
|
+
bmpFileHeader.bfType = 'MB';
|
|
175
|
+
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
|
|
176
|
+
|
|
177
|
+
fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, lfile);
|
|
178
|
+
fwrite(&lBmpInfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, lfile);
|
|
179
|
+
fwrite(pBuf.get(), lBmpInfo.bmiHeader.biSizeImage, 1, lfile);
|
|
180
|
+
|
|
181
|
+
fclose(lfile);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#include <Windows.h>
|
|
3
|
+
#include <iostream>
|
|
4
|
+
#include <dwmapi.h>
|
|
5
|
+
#include <helpers.h>
|
|
6
|
+
|
|
7
|
+
Napi::Value GetWindowData(const Napi::CallbackInfo &info)
|
|
8
|
+
{
|
|
9
|
+
Napi::Env env = info.Env();
|
|
10
|
+
|
|
11
|
+
if (info.Length() < 1 || !info[0].IsString())
|
|
12
|
+
{
|
|
13
|
+
Napi::TypeError::New(env, "Window name must be provided as a string").ThrowAsJavaScriptException();
|
|
14
|
+
return env.Null();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
std::string windowName = info[0].As<Napi::String>().Utf8Value();
|
|
18
|
+
|
|
19
|
+
HWND windowHandle = GetWindowByName(windowName.c_str());
|
|
20
|
+
|
|
21
|
+
if (windowHandle == NULL)
|
|
22
|
+
{
|
|
23
|
+
Napi::Error::New(env, "Window not found").ThrowAsJavaScriptException();
|
|
24
|
+
return env.Null();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
RECT windowRect;
|
|
28
|
+
DwmGetWindowAttribute(windowHandle, DWMWA_EXTENDED_FRAME_BOUNDS, &windowRect, sizeof(windowRect));
|
|
29
|
+
HDC hdc = GetDC(windowHandle);
|
|
30
|
+
int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
31
|
+
int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
32
|
+
ReleaseDC(windowHandle, hdc);
|
|
33
|
+
|
|
34
|
+
int width = windowRect.right - windowRect.left;
|
|
35
|
+
int height = windowRect.bottom - windowRect.top;
|
|
36
|
+
int x = windowRect.left;
|
|
37
|
+
int y = windowRect.top;
|
|
38
|
+
|
|
39
|
+
Napi::Object result = Napi::Object::New(env);
|
|
40
|
+
result.Set("width", Napi::Number::New(env, width));
|
|
41
|
+
result.Set("height", Napi::Number::New(env, height));
|
|
42
|
+
result.Set("x", Napi::Number::New(env, x));
|
|
43
|
+
result.Set("y", Napi::Number::New(env, y));
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
#include "helpers.h"
|
|
3
|
+
|
|
4
|
+
std::wstring ToWChar(const std::string &str)
|
|
5
|
+
{
|
|
6
|
+
int bufferSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
|
|
7
|
+
std::wstring wstr(bufferSize, 0);
|
|
8
|
+
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], bufferSize);
|
|
9
|
+
return wstr;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
HWND GetWindowByName(const char *windowName)
|
|
13
|
+
{
|
|
14
|
+
return FindWindowA(NULL, windowName);
|
|
15
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#include <Windows.h>
|
|
3
|
+
#include <thread>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
|
|
6
|
+
// Global variable to store the JavaScript callback function
|
|
7
|
+
Napi::ThreadSafeFunction globalJsCallbackKeyDown;
|
|
8
|
+
Napi::ThreadSafeFunction globalJsCallbackKeyUp;
|
|
9
|
+
|
|
10
|
+
// Global variable to store the previous key state
|
|
11
|
+
bool isKeyPressed = false;
|
|
12
|
+
bool handleKeyUp = false;
|
|
13
|
+
bool handleKeyDown = false;
|
|
14
|
+
bool monitorThreadRunning = false;
|
|
15
|
+
int previousKeyState;
|
|
16
|
+
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
17
|
+
{
|
|
18
|
+
if (nCode >= 0)
|
|
19
|
+
{
|
|
20
|
+
if (wParam == WM_KEYDOWN)
|
|
21
|
+
{
|
|
22
|
+
KBDLLHOOKSTRUCT *kbdStruct = (KBDLLHOOKSTRUCT *)lParam;
|
|
23
|
+
int keyCode = kbdStruct->vkCode;
|
|
24
|
+
|
|
25
|
+
// Handle the keyCode here or call your JavaScript callback
|
|
26
|
+
// std::cout << "Key Code: " << keyCode << std::endl;
|
|
27
|
+
|
|
28
|
+
if (keyCode != previousKeyState || !isKeyPressed)
|
|
29
|
+
{
|
|
30
|
+
isKeyPressed = true;
|
|
31
|
+
previousKeyState = keyCode;
|
|
32
|
+
if (handleKeyDown)
|
|
33
|
+
{
|
|
34
|
+
int *keyCodeCopy = new int(keyCode); // Create a copy of the keyCode value
|
|
35
|
+
|
|
36
|
+
napi_status status = globalJsCallbackKeyDown.BlockingCall(
|
|
37
|
+
keyCodeCopy,
|
|
38
|
+
[](Napi::Env env, Napi::Function jsCallback, int *keyCodePtr)
|
|
39
|
+
{
|
|
40
|
+
Napi::HandleScope scope(env);
|
|
41
|
+
int keyCode = *keyCodePtr;
|
|
42
|
+
jsCallback.Call({Napi::Number::New(env, keyCode)});
|
|
43
|
+
delete keyCodePtr; // Clean up the dynamically allocated keyCode copy
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (status != napi_ok)
|
|
47
|
+
{
|
|
48
|
+
// Handle the error
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (wParam == WM_KEYUP)
|
|
54
|
+
{
|
|
55
|
+
KBDLLHOOKSTRUCT *kbdStruct = (KBDLLHOOKSTRUCT *)lParam;
|
|
56
|
+
int keyCode = kbdStruct->vkCode;
|
|
57
|
+
|
|
58
|
+
if (keyCode == previousKeyState)
|
|
59
|
+
{
|
|
60
|
+
isKeyPressed = false;
|
|
61
|
+
if (handleKeyUp)
|
|
62
|
+
{
|
|
63
|
+
int *keyCodeCopy = new int(keyCode); // Create a copy of the keyCode value
|
|
64
|
+
napi_status status = globalJsCallbackKeyUp.BlockingCall(
|
|
65
|
+
keyCodeCopy,
|
|
66
|
+
[](Napi::Env env, Napi::Function jsCallback, int *keyCodePtr)
|
|
67
|
+
{
|
|
68
|
+
Napi::HandleScope scope(env);
|
|
69
|
+
int keyCode = *keyCodePtr;
|
|
70
|
+
jsCallback.Call({Napi::Number::New(env, keyCode)});
|
|
71
|
+
delete keyCodePtr; // Clean up the dynamically allocated keyCode copy
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (status != napi_ok)
|
|
75
|
+
{
|
|
76
|
+
// Handle the error
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Function to monitor keyboard events
|
|
87
|
+
void MonitorKeyboardEvents()
|
|
88
|
+
{
|
|
89
|
+
HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProc, NULL, 0);
|
|
90
|
+
if (keyboardHook == NULL)
|
|
91
|
+
{
|
|
92
|
+
// Error setting hook
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Main loop to process keyboard events
|
|
97
|
+
MSG message;
|
|
98
|
+
while (GetMessage(&message, NULL, 0, 0) > 0)
|
|
99
|
+
{
|
|
100
|
+
TranslateMessage(&message);
|
|
101
|
+
DispatchMessage(&message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
UnhookWindowsHookEx(keyboardHook);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Function called from JavaScript to set the callback function
|
|
108
|
+
Napi::Value SetKeyDownCallback(const Napi::CallbackInfo &info)
|
|
109
|
+
{
|
|
110
|
+
Napi::Env env = info.Env();
|
|
111
|
+
|
|
112
|
+
// Get the callback function from the arguments
|
|
113
|
+
Napi::Function jsCallback = info[0].As<Napi::Function>();
|
|
114
|
+
|
|
115
|
+
// Create a ThreadSafeFunction with the JavaScript callback
|
|
116
|
+
globalJsCallbackKeyDown = Napi::ThreadSafeFunction::New(
|
|
117
|
+
env,
|
|
118
|
+
jsCallback,
|
|
119
|
+
"KeyDownCallback",
|
|
120
|
+
0,
|
|
121
|
+
1,
|
|
122
|
+
[](Napi::Env)
|
|
123
|
+
{
|
|
124
|
+
// Finalizer callback (optional)
|
|
125
|
+
});
|
|
126
|
+
handleKeyDown = true;
|
|
127
|
+
if (!monitorThreadRunning)
|
|
128
|
+
{
|
|
129
|
+
std::thread monitorThread(MonitorKeyboardEvents);
|
|
130
|
+
monitorThread.detach();
|
|
131
|
+
|
|
132
|
+
monitorThreadRunning = true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return env.Undefined();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Function called from JavaScript to set the callback function
|
|
139
|
+
Napi::Value SetKeyUpCallback(const Napi::CallbackInfo &info)
|
|
140
|
+
{
|
|
141
|
+
Napi::Env env = info.Env();
|
|
142
|
+
|
|
143
|
+
// Get the callback function from the arguments
|
|
144
|
+
Napi::Function jsCallback = info[0].As<Napi::Function>();
|
|
145
|
+
|
|
146
|
+
// Create a ThreadSafeFunction with the JavaScript callback
|
|
147
|
+
globalJsCallbackKeyUp = Napi::ThreadSafeFunction::New(
|
|
148
|
+
env,
|
|
149
|
+
jsCallback,
|
|
150
|
+
"KeyUpCallback",
|
|
151
|
+
0,
|
|
152
|
+
1,
|
|
153
|
+
[](Napi::Env)
|
|
154
|
+
{
|
|
155
|
+
// Finalizer callback (optional)
|
|
156
|
+
});
|
|
157
|
+
handleKeyUp = true;
|
|
158
|
+
if (!monitorThreadRunning)
|
|
159
|
+
{
|
|
160
|
+
std::thread monitorThread(MonitorKeyboardEvents);
|
|
161
|
+
monitorThread.detach();
|
|
162
|
+
monitorThreadRunning = true;
|
|
163
|
+
}
|
|
164
|
+
return env.Undefined();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Napi::Value TypeString(const Napi::CallbackInfo &info)
|
|
168
|
+
{
|
|
169
|
+
Napi::Env env = info.Env();
|
|
170
|
+
|
|
171
|
+
if (info.Length() < 1 || !info[0].IsString())
|
|
172
|
+
{
|
|
173
|
+
Napi::TypeError::New(env, "You should provide a string to type").ThrowAsJavaScriptException();
|
|
174
|
+
return env.Null();
|
|
175
|
+
}
|
|
176
|
+
int delay = 15;
|
|
177
|
+
std::string text = info[0].As<Napi::String>();
|
|
178
|
+
if (info.Length() > 1 && info[1].IsNumber())
|
|
179
|
+
{
|
|
180
|
+
delay = info[1].As<Napi::Number>();
|
|
181
|
+
}
|
|
182
|
+
// Convert the string to wide string
|
|
183
|
+
std::wstring wideText(text.begin(), text.end());
|
|
184
|
+
|
|
185
|
+
// Simulate typing by sending keyboard events
|
|
186
|
+
for (const wchar_t &character : wideText)
|
|
187
|
+
{
|
|
188
|
+
INPUT keyboardInput = {0};
|
|
189
|
+
keyboardInput.type = INPUT_KEYBOARD;
|
|
190
|
+
keyboardInput.ki.wVk = 0;
|
|
191
|
+
keyboardInput.ki.wScan = character;
|
|
192
|
+
keyboardInput.ki.dwFlags = KEYEVENTF_UNICODE;
|
|
193
|
+
keyboardInput.ki.time = 0; // System will provide the timestamp
|
|
194
|
+
|
|
195
|
+
SendInput(1, &keyboardInput, sizeof(keyboardInput));
|
|
196
|
+
|
|
197
|
+
keyboardInput.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
|
|
198
|
+
SendInput(1, &keyboardInput, sizeof(keyboardInput));
|
|
199
|
+
|
|
200
|
+
// Sleep for 15 milliseconds between each character input
|
|
201
|
+
Sleep(delay);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return Napi::Boolean::New(env, true);
|
|
205
|
+
}
|
package/src/cpp/main.cpp
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#include <helpers.cpp>
|
|
3
|
+
#include "captureWindow.cpp"
|
|
4
|
+
#include <getWindowData.cpp>
|
|
5
|
+
#include <keyboard.cpp>
|
|
6
|
+
#include <mouse.cpp>
|
|
7
|
+
|
|
8
|
+
Napi::Object Init(Napi::Env env, Napi::Object exports)
|
|
9
|
+
{
|
|
10
|
+
exports.Set("getWindowData", Napi::Function::New(env, GetWindowData));
|
|
11
|
+
exports.Set("captureWindow", Napi::Function::New(env, CaptureWindow));
|
|
12
|
+
exports.Set("keyDownHandler", Napi::Function::New(env, SetKeyDownCallback));
|
|
13
|
+
exports.Set("keyUpHandler", Napi::Function::New(env, SetKeyUpCallback));
|
|
14
|
+
exports.Set("mouseMove", Napi::Function::New(env, MoveMouse));
|
|
15
|
+
exports.Set("mouseClick", Napi::Function::New(env, ClickMouse));
|
|
16
|
+
exports.Set("mouseDrag", Napi::Function::New(env, DragMouse));
|
|
17
|
+
exports.Set("typeString", Napi::Function::New(env, TypeString));
|
|
18
|
+
return exports;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
NODE_API_MODULE(addon, Init)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// patrially used code from https://github.com/octalmage/robotjs witch is under MIT License Copyright (c) 2014 Jason Stallings
|
|
2
|
+
#include <napi.h>
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
/**
|
|
5
|
+
* Move the mouse to a specific point.
|
|
6
|
+
* @param point The coordinates to move the mouse to (x, y).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
Napi::Value MoveMouse(const Napi::CallbackInfo &info)
|
|
10
|
+
{
|
|
11
|
+
Napi::Env env = info.Env();
|
|
12
|
+
|
|
13
|
+
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber())
|
|
14
|
+
{
|
|
15
|
+
Napi::TypeError::New(env, "You should provide x and y position of type number").ThrowAsJavaScriptException();
|
|
16
|
+
return env.Null();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
int posX = info[0].As<Napi::Number>();
|
|
20
|
+
int posY = info[1].As<Napi::Number>();
|
|
21
|
+
|
|
22
|
+
// Get the screen metrics
|
|
23
|
+
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
|
|
24
|
+
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
|
|
25
|
+
|
|
26
|
+
// Convert coordinates to absolute values
|
|
27
|
+
int absoluteX = static_cast<int>((65536 * posX) / screenWidth);
|
|
28
|
+
int absoluteY = static_cast<int>((65536 * posY) / screenHeight);
|
|
29
|
+
|
|
30
|
+
// Move the mouse
|
|
31
|
+
INPUT mouseInput = {0};
|
|
32
|
+
mouseInput.type = INPUT_MOUSE;
|
|
33
|
+
mouseInput.mi.dx = absoluteX;
|
|
34
|
+
mouseInput.mi.dy = absoluteY;
|
|
35
|
+
mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
|
|
36
|
+
mouseInput.mi.time = 0; // System will provide the timestamp
|
|
37
|
+
|
|
38
|
+
SendInput(1, &mouseInput, sizeof(mouseInput));
|
|
39
|
+
|
|
40
|
+
return Napi::Boolean::New(env, true);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Napi::Value ClickMouse(const Napi::CallbackInfo &info)
|
|
44
|
+
{
|
|
45
|
+
Napi::Env env = info.Env();
|
|
46
|
+
std::string button;
|
|
47
|
+
|
|
48
|
+
if (info.Length() < 1 || !info[0].IsString())
|
|
49
|
+
button = "left";
|
|
50
|
+
else
|
|
51
|
+
button = info[0].As<Napi::String>();
|
|
52
|
+
|
|
53
|
+
WORD mouseEvent = 0;
|
|
54
|
+
|
|
55
|
+
if (button == "left")
|
|
56
|
+
{
|
|
57
|
+
mouseEvent = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP;
|
|
58
|
+
}
|
|
59
|
+
else if (button == "right")
|
|
60
|
+
{
|
|
61
|
+
mouseEvent = MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP;
|
|
62
|
+
}
|
|
63
|
+
else if (button == "middle")
|
|
64
|
+
{
|
|
65
|
+
mouseEvent = MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
Napi::TypeError::New(env, "Invalid button name").ThrowAsJavaScriptException();
|
|
70
|
+
return env.Null();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Perform the mouse click
|
|
74
|
+
INPUT mouseInput = {0};
|
|
75
|
+
mouseInput.type = INPUT_MOUSE;
|
|
76
|
+
mouseInput.mi.dwFlags = mouseEvent;
|
|
77
|
+
mouseInput.mi.time = 0; // System will provide the timestamp
|
|
78
|
+
|
|
79
|
+
SendInput(1, &mouseInput, sizeof(mouseInput));
|
|
80
|
+
|
|
81
|
+
return Napi::Boolean::New(env, true);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Napi::Value DragMouse(const Napi::CallbackInfo &info)
|
|
85
|
+
{
|
|
86
|
+
Napi::Env env = info.Env();
|
|
87
|
+
|
|
88
|
+
if (info.Length() < 4 || !info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber())
|
|
89
|
+
{
|
|
90
|
+
Napi::TypeError::New(env, "You should provide startX, startY, endX, endY").ThrowAsJavaScriptException();
|
|
91
|
+
return env.Null();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
int startX = info[0].As<Napi::Number>();
|
|
95
|
+
int startY = info[1].As<Napi::Number>();
|
|
96
|
+
int endX = info[2].As<Napi::Number>();
|
|
97
|
+
int endY = info[3].As<Napi::Number>();
|
|
98
|
+
int speed = 100;
|
|
99
|
+
if (info.Length() > 4 && info[4].IsNumber())
|
|
100
|
+
{
|
|
101
|
+
speed = info[4].As<Napi::Number>();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get the screen metrics
|
|
105
|
+
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
|
|
106
|
+
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
|
|
107
|
+
|
|
108
|
+
// Convert coordinates to absolute values
|
|
109
|
+
int absoluteStartX = static_cast<int>((65536 * startX) / screenWidth);
|
|
110
|
+
int absoluteStartY = static_cast<int>((65536 * startY) / screenHeight);
|
|
111
|
+
int absoluteEndX = static_cast<int>((65536 * endX) / screenWidth);
|
|
112
|
+
int absoluteEndY = static_cast<int>((65536 * endY) / screenHeight);
|
|
113
|
+
|
|
114
|
+
// Calculate the distance and duration based on speed
|
|
115
|
+
double distanceX = absoluteEndX - absoluteStartX;
|
|
116
|
+
double distanceY = absoluteEndY - absoluteStartY;
|
|
117
|
+
double distance = sqrt(distanceX * distanceX + distanceY * distanceY);
|
|
118
|
+
double duration = distance / speed;
|
|
119
|
+
|
|
120
|
+
// Move the mouse to the starting position
|
|
121
|
+
INPUT startMouseInput = {0};
|
|
122
|
+
startMouseInput.type = INPUT_MOUSE;
|
|
123
|
+
startMouseInput.mi.dx = absoluteStartX;
|
|
124
|
+
startMouseInput.mi.dy = absoluteStartY;
|
|
125
|
+
startMouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
|
|
126
|
+
startMouseInput.mi.time = 0; // System will provide the timestamp
|
|
127
|
+
|
|
128
|
+
SendInput(1, &startMouseInput, sizeof(startMouseInput));
|
|
129
|
+
|
|
130
|
+
// Perform mouse button down event
|
|
131
|
+
INPUT mouseDownInput = {0};
|
|
132
|
+
mouseDownInput.type = INPUT_MOUSE;
|
|
133
|
+
mouseDownInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
|
134
|
+
mouseDownInput.mi.time = 0; // System will provide the timestamp
|
|
135
|
+
|
|
136
|
+
SendInput(1, &mouseDownInput, sizeof(mouseDownInput));
|
|
137
|
+
|
|
138
|
+
// Calculate the number of steps based on the duration and desired speed
|
|
139
|
+
const int steps = 100; // Adjust the number of steps for smoother movement
|
|
140
|
+
|
|
141
|
+
// Calculate the incremental values for each step
|
|
142
|
+
double stepX = distanceX / steps;
|
|
143
|
+
double stepY = distanceY / steps;
|
|
144
|
+
|
|
145
|
+
// Move the mouse in increments to simulate dragging with speed control
|
|
146
|
+
for (int i = 0; i < steps; ++i)
|
|
147
|
+
{
|
|
148
|
+
// Calculate the position for the current step
|
|
149
|
+
int currentX = static_cast<int>(absoluteStartX + (stepX * i));
|
|
150
|
+
int currentY = static_cast<int>(absoluteStartY + (stepY * i));
|
|
151
|
+
|
|
152
|
+
// Move the mouse to the current position
|
|
153
|
+
INPUT mouseMoveInput = {0};
|
|
154
|
+
mouseMoveInput.type = INPUT_MOUSE;
|
|
155
|
+
mouseMoveInput.mi.dx = currentX;
|
|
156
|
+
mouseMoveInput.mi.dy = currentY;
|
|
157
|
+
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
|
|
158
|
+
mouseMoveInput.mi.time = 0; // System will provide the timestamp
|
|
159
|
+
|
|
160
|
+
SendInput(1, &mouseMoveInput, sizeof(mouseMoveInput));
|
|
161
|
+
|
|
162
|
+
// Sleep for a short duration to control the speed
|
|
163
|
+
Sleep(static_cast<DWORD>(duration / steps));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Perform mouse button up event
|
|
167
|
+
INPUT mouseUpInput = {0};
|
|
168
|
+
mouseUpInput.type = INPUT_MOUSE;
|
|
169
|
+
mouseUpInput.mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
|
170
|
+
mouseUpInput.mi.time = 0; // System will provide the timestamp
|
|
171
|
+
|
|
172
|
+
SendInput(1, &mouseUpInput, sizeof(mouseUpInput));
|
|
173
|
+
|
|
174
|
+
return Napi::Boolean::New(env, true);
|
|
175
|
+
}
|