Skip to content

Instantly share code, notes, and snippets.

@Youka
Last active July 15, 2022 03:36
Show Gist options
  • Save Youka/193afdec83321f4f51a2 to your computer and use it in GitHub Desktop.
Save Youka/193afdec83321f4f51a2 to your computer and use it in GitHub Desktop.
Picking colors at mouse position with LuaJIT
-- Load FFI
local ffi = require("ffi")
-- Define FFI functions & structures by OS
local x11
if ffi.os == "Windows" then
ffi.cdef([[
typedef int BOOL;
typedef long LONG;
typedef struct{
LONG x, y;
}POINT, *LPPOINT;
typedef void* HANDLE;
typedef HANDLE HDC;
typedef HANDLE HWND;
typedef unsigned long DWORD;
typedef DWORD COLORREF;
BOOL GetCursorPos(LPPOINT);
HDC GetDC(HWND);
int ReleaseDC(HWND, HDC);
COLORREF GetPixel(HDC, int, int);
]])
else
x11 = ffi.load("X11")
ffi.cdef(
[[
typedef void Display;
typedef unsigned long XID;
typedef XID Window;
typedef XID Colormap;
typedef struct{
void* ext_data;
void* display;
Window root;
int width, height;
int mwidth, mheight;
int ndepths;
void* depths;
int root_depth;
void* root_visual;
void* default_gc;
Colormap cmap;
// Rest doesn't matter
}Screen;
typedef char* XPointer;
typedef struct{
void* ext_data;
void* private1;
int fd;
int private2;
int proto_major_version;
int proto_minor_version;
char* vendor;
XID private3;
XID private4;
XID private5;
int private6;
XID (*resource_alloc)(void*);
int byte_order;
int bitmap_unit;
int bitmap_pad;
int bitmap_bit_order;
int nformats;
void* pixmap_format;
int private8;
int release;
void* private9, *private10;
int qlen;
unsigned long last_request_read;
unsigned long request;
XPointer private11;
XPointer private12;
XPointer private13;
XPointer private14;
unsigned max_request_size;
void* db;
int (*private15)(void*);
char* display_name;
int default_screen;
// Rest doesn't matter
}*_XPrivDisplay;
typedef int Bool;
typedef struct{
int x, y;
int width, height;
int border_width;
int depth;
void* visual;
Window root;
int class;
int bit_gravity;
int win_gravity;
int backing_store;
unsigned long backing_planes;
unsigned long backing_pixel;
Bool save_under;
Colormap colormap;
Bool map_installed;
int map_state;
long all_event_masks;
long your_event_mask;
long do_not_propagate_mask;
Bool override_redirect;
Screen *screen;
}XWindowAttributes;
typedef void XImage;
typedef XID Drawable;
static const unsigned long AllPlanes = (unsigned long)~0L;
static const int XYPixmap = 1;
typedef struct{
unsigned long pixel;
unsigned short red, green, blue;
char flags;
char pad;
}XColor;
Display* XOpenDisplay(char*);
int XCloseDisplay(Display*);
Screen* XScreenOfDisplay(Display*, int);
Bool XQueryPointer(Display*, Window, Window*, Window*, int*, int*, int*, int*, unsigned int*);
int XGetWindowAttributes(Display*, Window, XWindowAttributes*);
XImage* XGetImage(Display*, Drawable, int, int, unsigned int, unsigned int, unsigned long, int);
int XFree(void*);
unsigned long XGetPixel(XImage*, int, int);
Colormap XDefaultColormap(Display*, int);
int XQueryColor(Display*, Colormap, XColor*);
]]
)
end
-- Get screen color(s) at cursor position and cursor position
local function pick_screen_color(range)
-- Check arguments
if range ~= nil and (type(range) ~= "number" or range < 0) then
error("Invalid color pick range!", 2)
end
-- Fix range to valid integer
range = range and math.floor(range) or 0
-- Output buffers
local bmp, mx, my = {n = 0}
-- Continue by OS
if ffi.os == "Windows" then
-- Get cursor position
local ppoint = ffi.new("POINT[1]")
if ffi.C.GetCursorPos(ppoint) == 0 then
error("Couldn't get cursor position!", 2)
end
mx, my = ppoint[0].x, ppoint[0].y
-- Get desktop device context
local dc = ffi.gc(ffi.C.GetDC(nil), function(dc) ffi.C.ReleaseDC(nil, dc) end)
if not dc then
error("Couldn't get desktop device context!", 2)
end
-- Get desktop color under & around cursor
for y=-range, range do
for x=-range, range do
bmp.n = bmp.n + 1
local color = ffi.C.GetPixel(dc, mx + x, my + y)
if color < 16777216 then -- No error on pixel get
bmp[bmp.n] = {r = color % 256, g = math.floor(color / 256) % 256, b = math.floor(color / 65536)}
end
end
end
else
-- Get display & root window
local display = ffi.gc(x11.XOpenDisplay(nil), x11.XCloseDisplay)
if not display then
error("Couldn't open display!", 2)
end
local root = x11.XScreenOfDisplay(display, ffi.cast("_XPrivDisplay", display)[0].default_screen)[0].root
-- Get cursor position
local root_window, child_window, root_x, root_y, win_x, win_y, mask = ffi.new("Window[1]"), ffi.new("Window[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("unsigned int[1]")
if x11.XQueryPointer(display, root, root_window, child_window, root_x, root_y, win_x, win_y, mask) == 0 then
error("Couldn't get cursor position!", 2)
end
mx, my = win_x[0], win_y[0]
-- Get desktop color under & around cursor
local attr = ffi.new("XWindowAttributes[1]")
if x11.XGetWindowAttributes(display, root, attr) == 0 then
error("Couldn't get window attributes!", 2)
end
local w, h, color, color_map = attr[0].width, attr[0].height, ffi.new("XColor[1]"), x11.XScreenOfDisplay(display, ffi.cast("_XPrivDisplay", display)[0].default_screen)[0].cmap
for y=-range, range do
for x=-range, range do
bmp.n = bmp.n + 1
local ix, iy = mx + x, my + y
if ix >= 0 and iy >= 0 and ix < w and iy < h then
local image = ffi.gc(x11.XGetImage(display, root, ix, iy, 1, 1, ffi.C.AllPlanes, ffi.C.XYPixmap), x11.XFree)
if image then
color[0].pixel = x11.XGetPixel(image, 0, 0)
x11.XQueryColor(display, color_map, color)
bmp[bmp.n] = {r = math.floor(color[0].red / 256), g = math.floor(color[0].green / 256), b = math.floor(color[0].blue / 256)}
end
end
end
end
end
-- Return collected pixels & mouse position
return bmp, mx, my
end
-- Test
local colors, mouse_x, mouse_y = pick_screen_color(1)
print(mouse_x, mouse_y)
for i=1, colors.n do
local color = colors[i]
if color then
print(color.r, color.g, color.b)
else
print(color)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment