cuticle/hi/window_x11.cpp
mid b1a1b9a1eb Architectural updates
1. Introduce image UUIDs, so nodes may check for source updates.
2. Flip over design. Now, CHiPubNodes contain private data, instead of
CHiPubNodes being contained within private data. Each node must cache
its source data if it wants to conditionally run.
2025-10-15 09:45:38 +03:00

206 lines
5.0 KiB
C++

#include"node.h"
#include<stdlib.h>
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<X11/extensions/XShm.h>
#include<tmmintrin.h>
#include<smmintrin.h>
#include<time.h>
#include<string.h>
#include"img.h"
#include"linearity.h"
#include"minitrace.h"
static thread_local Display *d;
static thread_local Window root;
static int find_window(Display *d, Window *w, const char *contains) {
if(contains) {
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
for(int i = 0; i < numItems; i++) {
status = XGetWMName(d, list[i], &windowName);
if(status >= Success) {
if(windowName.value && strstr((const char*) windowName.value, contains)) {
*w = list[i];
found = 1;
break;
}
}
}
}
XFree(list);
return found;
} else {
*w = root;
return 1;
}
}
typedef struct {
Window xcache;
XImage *ximg;
XShmSegmentInfo shminfo;
CHiImage *vcache;
} WindowImpl;
static int window_perform(CHiPubNode *n) {
auto *w = (WindowImpl*) n->impl;
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
MTR_BEGIN("CHi", "window_perform");
Window toshoot;
if(!find_window(d, &toshoot, CHi_Crawl(&n->sinks[0])->data.text)) return 0;
size_t stride;
if(!w->xcache || w->xcache != toshoot) {
w->xcache = toshoot;
XWindowAttributes attrs;
XGetWindowAttributes(d, toshoot, &attrs);
w->ximg = XShmCreateImage(d, attrs.visual, 32, ZPixmap, NULL, &w->shminfo, attrs.width, attrs.height);
stride = ((w->ximg->bytes_per_line + 15) & ~15);
w->shminfo.shmid = shmget(IPC_PRIVATE, stride * w->ximg->height, IPC_CREAT | 0777);
w->shminfo.shmaddr = w->ximg->data = (char*) shmat(w->shminfo.shmid, 0, 0);
w->shminfo.readOnly = False;
XShmAttach(d, &w->shminfo);
w->vcache = CHi_Image_New(2, 4, (8 * attrs.width + 15) & ~15, attrs.width, attrs.height, NULL);
} else {
stride = ((w->ximg->bytes_per_line + 15) & ~15);
}
XWindowAttributes toshootattrs;
XGetWindowAttributes(d, w->xcache, &toshootattrs);
XShmGetImage(d, w->xcache, w->ximg, 0, 0, AllPlanes);
bool ignoreAlpha = CHi_Crawl(&n->sinks[1])->data.vec4[0] != 0;
// Turn u8 image to u16
#pragma omp parallel for
for(size_t y = 0; y < w->vcache->height; y++) {
for(size_t x = 0; x < w->vcache->width; x += 2) {
__m128i c = _mm_loadu_si128((__m128i*) ((uintptr_t) w->ximg->data + y * w->ximg->bytes_per_line + x * 4));
c = _mm_shuffle_epi8(c, _mm_set_epi8(7, -128, 6, -128, 5, -128, 4, -128, 3, -128, 2, -128, 1, -128, 0, -128));
if(ignoreAlpha) {
c = _mm_or_si128(c, _mm_set_epi16(0xFFFF, 0, 0, 0, 0xFFFF, 0, 0, 0));
}
c = apply_gamma_epi16(c, _mm_set_ps(1, 2.2f, 2.2f, 2.2f));
_mm_storeu_si128((__m128i*) ((uintptr_t) w->vcache->data16 + y * w->vcache->stride + x * 8), c);
}
}
n->sources[0].type = CUTIHI_VAL_SAMPLE;
n->sources[0].data.sample = w->vcache;
MTR_END("CHi", "window_perform");
return 1;
}
static void window_destroy(CHiPubNode *pubn) {
auto *n = (WindowImpl*) pubn->impl;
if(n->vcache) {
XShmDetach(d, &n->shminfo);
shmdt(n->shminfo.shmaddr);
XDestroyImage(n->ximg);
}
free(pubn);
}
CUTIVIS CHiPubNode *CHi_Window() {
CHiPubNode *n = CHi_AllocNode(CUTIHI_T('CWin','dow '), 2, 1, sizeof(WindowImpl));
n->Start = n->Stop = NULL;
n->Perform = window_perform;
n->Destroy = window_destroy;
return n;
}
// All of the following are ews
struct WindowListDatum {
Window handle;
char name[128];
};
CUTIVIS size_t CHi_Window_GetList(void **buf) {
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
WindowListDatum *data = (WindowListDatum*) calloc(numItems, sizeof(*data));
size_t successfulWindows = 0;
for(size_t i = 0; i < numItems; i++) {
status = XGetWMName(d, list[i], &windowName);
if(status >= Success) {
data[successfulWindows].handle = list[i];
strncpy(data[successfulWindows].name, (char*) windowName.value, sizeof(data[successfulWindows].name));
successfulWindows++;
}
}
XFree(list);
*buf = data;
return successfulWindows;
}
*buf = nullptr;
return 0;
}
CUTIVIS const char *CHi_Window_GetName(void *buf, size_t i) {
return ((WindowListDatum*) buf)[i].name;
}
CUTIVIS size_t CHi_Window_GetHandle(void *buf, size_t i) {
return ((WindowListDatum*) buf)[i].handle;
}
CUTIVIS void CHi_Window_FreeList(void *buf) {
free(buf);
}