node-native-win-utils
Version:
Native addon for Node.js providing utility operations on Windows systems
266 lines (265 loc) • 10.5 kB
JavaScript
import { createRequire as _createRequire } from "module";
const __require = _createRequire(import.meta.url);
import EventEmitter from 'events';
import path from "path";
import fs from "fs";
// @ts-ignore
const nodeGypBuild = __require("node-gyp-build");
import { keyCodes, KeyCodeHelper } from "./keyCodes.mjs";
import { __dirnameLocal } from "./dirnameLocal.mjs";
const bindings = nodeGypBuild(path.resolve(__dirnameLocal, ".."));
const { setKeyDownCallback, setKeyUpCallback, unsetKeyDownCallback, unsetKeyUpCallback, getWindowData, captureWindowN, captureScreenAsync, mouseMove, mouseClick, mouseDrag, typeString, pressKey, imread, imwrite, matchTemplate, blur, bgrToGray, drawRectangle, getRegion, textRecognition, equalizeHist, darkenColor, bringWindowToFront, startMouseListener, stopMouseListener } = bindings;
const rawPressKey = pressKey;
/**
* Captures a window and saves it to a file.
* @param windowName - The name of the window to capture.
* @param path - The file path to save the captured image.
* @returns True if the capture and save operation is successful, otherwise false.
*/
function captureWindow(windowName, path) {
const buffer = captureWindowN(windowName);
if (!buffer)
return false;
fs.writeFileSync(path, new Uint8Array(buffer));
return true;
}
/**
* Captures a screen and saves it to a file.
* @param path - The file path to save the captured image.
* @returns True if the capture and save operation is successful, otherwise false.
*/
function captureScreenToFile(path) {
return new Promise((resolve, reject) => {
captureScreenAsync().then((buffer) => {
fs.writeFileSync(path, new Uint8Array(buffer));
resolve(true);
}).catch((err => {
reject(err);
}));
});
}
/**
* Class that implements a private keyboard listener.
* This class leverages native C++ bindings to hook into system keyboard events.
* The C++ layer uses global ThreadSafeFunction objects to safely dispatch events
* (using a dedicated monitoring thread, mutexes, and atomic flags) to JavaScript.
* @extends EventEmitter
*/
class KeyboardListenerPrivate extends EventEmitter {
/**
* Constructs the keyboard listener and sets up native callbacks.
* The callbacks (set via setKeyDownCallback and setKeyUpCallback) are defined in the
* C++ binding layer. They are responsible for invoking these JavaScript callbacks
* in a thread-safe manner once a key event is detected.
*/
constructor() {
super();
// Set the callback for key down events.
setKeyDownCallback((keyCode) => {
// Look up the human-readable key name from a mapping.
const keyName = keyCodes.get(keyCode.toString());
// Emit the 'keyDown' event to all registered JavaScript listeners.
this.emit("keyDown", {
keyCode,
keyName,
});
});
// Set the callback for key up events.
setKeyUpCallback((keyCode) => {
// Look up the human-readable key name from a mapping.
const keyName = keyCodes.get(keyCode.toString());
// Emit the 'keyUp' event to all registered JavaScript listeners.
this.emit("keyUp", {
keyCode,
keyName,
});
});
}
}
/**
* A singleton manager for the KeyboardListenerPrivate instance.
* This class ensures that only one native keyboard listener is active at any time.
* When the listener is destroyed, it calls unsetKeyDownCallback and unsetKeyUpCallback
* to clean up native resources, mirroring the cleanup logic in the C++ bindings.
*/
class KeyboardListener {
/**
* Holds the singleton instance of KeyboardListenerPrivate.
*/
static listenerInstance = null;
/**
* Returns the singleton instance of KeyboardListenerPrivate. If not already created,
* it instantiates a new instance and sets up the native callbacks.
* @returns The active KeyboardListenerPrivate instance.
*/
static listener() {
if (!this.listenerInstance) {
this.listenerInstance = new KeyboardListenerPrivate();
}
return this.listenerInstance;
}
/**
* Destroys the current KeyboardListenerPrivate instance and cleans up native callbacks.
* This method calls unsetKeyDownCallback and unsetKeyUpCallback to release any
* native resources (such as the global ThreadSafeFunctions) and stops the monitoring thread.
*/
static destroy() {
this.listenerInstance = null;
unsetKeyDownCallback();
unsetKeyUpCallback();
}
}
export var TemplateMatchModes;
(function (TemplateMatchModes) {
TemplateMatchModes[TemplateMatchModes["TM_SQDIFF"] = 0] = "TM_SQDIFF";
TemplateMatchModes[TemplateMatchModes["TM_SQDIFF_NORMED"] = 1] = "TM_SQDIFF_NORMED";
TemplateMatchModes[TemplateMatchModes["TM_CCORR"] = 2] = "TM_CCORR";
TemplateMatchModes[TemplateMatchModes["TM_CCORR_NORMED"] = 3] = "TM_CCORR_NORMED";
TemplateMatchModes[TemplateMatchModes["TM_CCOEFF"] = 4] = "TM_CCOEFF";
TemplateMatchModes[TemplateMatchModes["TM_CCOEFF_NORMED"] = 5] = "TM_CCOEFF_NORMED"; /*!< \f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{
\sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2}
}\f] */
})(TemplateMatchModes || (TemplateMatchModes = {}));
;
/**
* Represents the OpenCV class that provides image processing functionality.
*/
class OpenCV {
imageData;
/**
* Represents the OpenCV class that provides image processing functionality.
*/
constructor(image) {
if (typeof image === "string") {
this.imageData = imread(image);
}
else {
this.imageData = image;
}
}
/**
* The width of the image.
*/
get width() {
return this.imageData.width;
}
/**
* The height of the image.
*/
get height() {
return this.imageData.height;
}
/**
* Matches a template image within the current image.
* @param template - The template image data to search for.
* @param method - The template matching method (optional).
* @param mask - The optional mask image data to apply the operation (optional).
* @returns The result of the template matching operation.
*/
matchTemplate(template, method, mask) {
if (typeof method !== "number")
method = TemplateMatchModes.TM_CCOEFF_NORMED;
return matchTemplate(this.imageData, template, method, mask);
}
/**
* Applies a blur filter to the image.
* @param sizeX - The horizontal size of the blur filter.
* @param sizeY - The vertical size of the blur filter.
* @returns A new OpenCV instance with the blurred image data.
*/
blur(sizeX, sizeY) {
return new OpenCV(blur(this.imageData, sizeX, sizeY));
}
/**
* Converts the image from BGR to grayscale.
* @returns A new OpenCV instance with the grayscale image data.
*/
bgrToGray() {
return new OpenCV(bgrToGray(this.imageData));
}
/**
* Equalize the Histogram by using the OpenCV function cv::equalizeHist.
* @returns A new OpenCV instance with the equalized image data.
*/
equalizeHist() {
return new OpenCV(equalizeHist(this.imageData));
}
rgbToHsv(rgb) {
const r_norm = rgb[0] / 255;
const g_norm = rgb[1] / 255;
const b_norm = rgb[2] / 255;
const Cmax = Math.max(r_norm, g_norm, b_norm);
const Cmin = Math.min(r_norm, g_norm, b_norm);
const delta = Cmax - Cmin;
let Hue = 0;
if (delta !== 0) {
switch (Cmax) {
case r_norm:
Hue = 60 * (((g_norm - b_norm) / delta) % 6);
break;
case g_norm:
Hue = 60 * (((b_norm - r_norm) / delta) + 2);
break;
case b_norm:
Hue = 60 * (((r_norm - g_norm) / delta) + 4);
break;
}
}
let Saturation = 0.0 * 100;
if (Cmax !== 0)
Saturation = (delta / Cmax) * 100;
let Value = Cmax * 100;
return [
[Hue, Saturation, Value]
];
}
darkenColor(lowerBound, upperBound, darkenFactor) {
return new OpenCV(darkenColor(this.imageData, lowerBound, upperBound, darkenFactor));
}
/**
* Draws a rectangle on the image.
* @param start - The starting point of the rectangle.
* @param end - The ending point of the rectangle.
* @param rgb - The color (RGB) of the rectangle.
* @param thickness - The thickness of the rectangle's border.
* @returns A new OpenCV instance with the image containing the drawn rectangle.
*/
drawRectangle(start, end, rgb, thickness) {
return new OpenCV(drawRectangle(this.imageData, start, end, rgb, thickness));
}
/**
* Extracts a region of interest (ROI) from the image.
* @param region - The region of interest defined as [x, y, width, height].
* @returns A new OpenCV instance with the extracted region of interest.
*/
getRegion(region) {
return new OpenCV(getRegion(this.imageData, region));
}
/**
* Writes the image data to a file.
* @param path - The file path to save the image.
*/
imwrite(path) {
const buffer = imwrite(this.imageData);
if (!buffer)
return;
fs.writeFileSync(path, new Uint8Array(buffer));
}
}
function keyPress(keyCode, repeat) {
return new Promise((resolve, reject) => {
if (!repeat) {
let result = rawPressKey(keyCode);
if (!result)
reject('Something went wrong');
return resolve(true);
}
for (let i = 0; i <= repeat; i++) {
let result = rawPressKey(keyCode);
if (!result)
reject('Something went wrong');
}
return resolve(true);
});
}
export { getWindowData, bringWindowToFront, captureWindow, captureWindowN, mouseMove, mouseClick, mouseDrag, typeString, keyPress, rawPressKey, KeyCodeHelper, textRecognition, captureScreenToFile, captureScreenAsync, startMouseListener, stopMouseListener, KeyboardListener, OpenCV };