Skip to content

Instantly share code, notes, and snippets.

@IAmJSD
Last active October 6, 2022 05:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IAmJSD/202a8924b2b06832380cbf2b9e1b09da to your computer and use it in GitHub Desktop.
Save IAmJSD/202a8924b2b06832380cbf2b9e1b09da to your computer and use it in GitHub Desktop.
#include <nan.h>
#include <node.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
using namespace v8;
void GetScreenshot(const Nan::FunctionCallbackInfo<Value>& args) {
// Get both expected arguments as integers.
Isolate* isolate = args.GetIsolate();
if (args.Length() != 2) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Wrong number of arguments").ToLocalChecked()));
return;
}
Local<Value> firstArg = args[0];
if (!firstArg->IsNumber()) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Argument is not a number").ToLocalChecked()));
return;
}
int boundsX = firstArg.As<Number>()->Value();
Local<Value> secondArg = args[1];
if (!firstArg->IsNumber()) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Argument is not a number").ToLocalChecked()));
return;
}
int boundsY = secondArg.As<Number>()->Value();
// Get the display and its default screen. Note screens are not monitors!
Display* display = XOpenDisplay(NULL);
Screen* screen = DefaultScreenOfDisplay(display);
if (!screen) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Could not get default screen").ToLocalChecked()));
return;
}
// Get the screens root window. This will be what we want to capture.
Window win = XRootWindowOfScreen(screen);
// Find a monitor that matches the top-left corner we were given.
bool found = false;
int monitorCount;
XRRMonitorInfo monitor;
XRRMonitorInfo* monitors = XRRGetMonitors(display, win, true, &monitorCount);
for (int i = 0; i < monitorCount; i++) {
// Get the monitor.
monitor = monitors[i];
// Handle checking the top left.
if (monitor.x == boundsX && monitor.y == boundsY) {
printf("matched monitor at x: %d, y: %d\n", monitor.x, monitor.y);
found = true;
} else {
printf("scanned monitor at x: %d, y: %d - no match!\n", monitor.x, monitor.y);
}
// At the end of everything, break.
if (found) {
break;
}
}
XFree(monitors);
// If said monitor was not found, throw an error.
if (!found) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Could not find monitor").ToLocalChecked()));
return;
}
// Get the image from the window.
XImage* image = XGetImage(display, win, monitor.x, monitor.y, monitor.width, monitor.height, AllPlanes, ZPixmap);
XCloseDisplay(display);
if (image == NULL) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "Could not get image").ToLocalChecked()));
return;
}
// Create a new allocation and turn it into RGBA.
size_t size = image->width * image->height * 4;
uint8_t* bytes = new uint8_t[size];
for (int blockStart = 0; blockStart < size; blockStart += 4) {
bytes[blockStart] = image->data[blockStart + 2];
bytes[blockStart + 1] = image->data[blockStart + 1];
bytes[blockStart + 2] = image->data[blockStart];
bytes[blockStart + 3] = image->data[blockStart + 3];
}
// Create an array buffer from this new allocation and free the old image.
Nan::MaybeLocal<Object> buf = Nan::NewBuffer((char*)bytes, size);
XFree(image);
// Set the buffer as the return value.
args.GetReturnValue().Set(buf.ToLocalChecked());
}
void Initialize(Local<Object> exports) {
Local<Context> context = exports->CreationContext();
exports->Set(context,
Nan::New("getScreenshotForBounds").ToLocalChecked(),
Nan::New<FunctionTemplate>(GetScreenshot)
->GetFunction(context)
.ToLocalChecked());
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment