From 3993163d6dac452926fce484d33e9930092d7470 Mon Sep 17 00:00:00 2001 From: mid <> Date: Sun, 12 Oct 2025 11:23:08 +0300 Subject: [PATCH] Start Windows compatibility screen_capture_lite turned out to be pretty broken and so I brought back my old X11 implementation for the Window node, for Unices only. Hopefully SCL actually works on Windows because lemme tell you, I do not want to go knee-deep in that. Additionally, SAIL was replaced with stb_image because I couldn't get SAIL to build under MinGW. --- Makefile | 39 +++++- hi/img.c | 2 +- hi/kumb.h | 8 +- hi/mkv.c | 9 +- hi/node.c | 31 ++--- hi/opus.c | 2 +- hi/relay_win.c | 149 ++++++++++++++++++++ hi/{relay.c => relay_x11.c} | 0 hi/rtmp.c | 4 + hi/serialize.c | 4 +- hi/webcam.c | 112 +-------------- hi/webcam_v4l2.c | 136 +++++++++++++++++++ hi/window.c | 0 hi/{window.cpp => window_scl.cpp} | 12 +- hi/window_x11.cpp | 218 ++++++++++++++++++++++++++++++ ui/frame.cpp | 117 +++++++++------- ui/frame.h | 2 +- ui/main.cpp | 3 + ui/timeline.cpp | 4 +- 19 files changed, 654 insertions(+), 198 deletions(-) create mode 100644 hi/relay_win.c rename hi/{relay.c => relay_x11.c} (100%) create mode 100644 hi/webcam_v4l2.c delete mode 100644 hi/window.c rename hi/{window.cpp => window_scl.cpp} (89%) create mode 100644 hi/window_x11.cpp diff --git a/Makefile b/Makefile index 6c118a6..d135850 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,26 @@ -CXXFLAGS := -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ -I/usr/local/include/sail -L/usr/local/lib `pkg-config --cflags pango opus libv4l2` '-Wl,-rpath,$$ORIGIN' -Wno-multichar -LDFLAGS := -lwebm -lpng -lvpx -lsail -lsail-manip `pkg-config --libs pango opus libv4l2` -lportaudio -lXtst -lrtmp -lfdk-aac -leebie -lscreen_capture_lite_shared +CXXFLAGS := -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ '-Wl,-rpath,$$ORIGIN' -Wno-multichar +LDFLAGS := -lwebm -lpng -lvpx -lportaudio -lrtmp -lfdk-aac -leebie -lscreen_capture_lite_shared + +ifneq ($(MINGW),0) + CXXFLAGS := `pkg-config --cflags pango opus libv4l2` -I/usr/local/bin -I/usr/local/include/sail $(CXXFLAGS) + LDFLAGS := $(LDFLAGS) -lXtst `pkg-config --libs pango opus libv4l2` -lstdc++ + WXCFLAGS := `wx-config --cflags base,adv,core,aui` + WXLDFLAGS := `wx-config --libs base,adv,core,aui` + RELAY_C := relay_x11.c + WINDOW_C := window_x11.cpp + LIBCUTIHI := libcutihi.so +else + CXXFLAGS := `x86_64-w64-mingw32-pkg-config --cflags pangoft2 freetype2 opus` $(CXXFLAGS) + LDFLAGS := $(LDFLAGS) -pthread -lwinmm -lwebm -lole32 -lsetupapi -lws2_32 -lopus -lssp `x86_64-w64-mingw32-pkg-config --libs pangoft2` -lstdc++ + WXCFLAGS := `wx-config --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32/ --cflags base,adv,core,aui` + WXLDFLAGS := `wx-config --host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32/ --libs base,adv,core,aui` + RELAY_C := relay_win.c + WINDOW_C := window_scl.cpp + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + LIBCUTIHI := libcutihi.dll +endif ifneq ($(RELEASE),0) CXXFLAGS := $(CXXFLAGS) -O0 -gdwarf-2 -DMTR_ENABLED @@ -8,24 +28,29 @@ else CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp -DMTR_ENABLED endif +ifneq ($(SANITIZE),0) +else + CXXFLAGS := -fsanitize=address $(CXXFLAGS) +endif + all: $(CC) $(CXXFLAGS) -std=c99 -shared -c -o node.o hi/node.c $(LDFLAGS) - $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o window.o hi/window.cpp $(LDFLAGS) + $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o window.o hi/$(WINDOW_C) $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o microphone.o hi/microphone.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o mode.o hi/mode.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS) $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS) - $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS) +# $(CXX) $(CXXFLAGS) -std=c++17 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o vpxenc.o hi/vpxenc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS) - $(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/relay.c $(LDFLAGS) + $(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/$(RELAY_C) $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o minitrace.o hi/minitrace.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o h264enc.o hi/h264enc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o rtmp.o hi/rtmp.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o aaclc.o hi/aaclc.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o mkv.o hi/mkv.c $(LDFLAGS) $(CC) $(CXXFLAGS) -std=c99 -shared -c -o serialize.o hi/serialize.c $(LDFLAGS) - $(CC) $(CXXFLAGS) -shared -o libcutihi.so -shared node.o webmdec.o webmenc.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o mkv.o serialize.o $(LDFLAGS) + $(CC) $(CXXFLAGS) -shared -o $(LIBCUTIHI) node.o webmdec.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o mkv.o serialize.o $(LDFLAGS) - $(CXX) $(CXXFLAGS) -std=c++11 `wx-config --cflags base,adv,core,aui` -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) `wx-config --libs base,adv,core,aui` + $(CXX) $(CXXFLAGS) $(WXCFLAGS) -std=c++11 -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) $(WXLDFLAGS) diff --git a/hi/img.c b/hi/img.c index b559178..4d3b7bf 100644 --- a/hi/img.c +++ b/hi/img.c @@ -12,7 +12,7 @@ CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, img->width = width; img->height = height; if(data) img->data16 = data; - else img->data16 = _mm_malloc(bpc * stride * height, 16); + else img->data16 = _mm_malloc(bpc * stride * height + 16, 16); img->owned = !data; assert(stride % 16 == 0); diff --git a/hi/kumb.h b/hi/kumb.h index f5b36df..2062a38 100644 --- a/hi/kumb.h +++ b/hi/kumb.h @@ -219,7 +219,7 @@ _PS_CONST(cephes_log_q2, 0.693359375); */ inline __m128 my_movehl_ps(__m128 a, const __m128 b) { - asm ( + __asm__ ( "movhlps %2,%0\n\t" : "=x" (a) : "0" (a), "x"(b) @@ -229,7 +229,7 @@ inline __m128 my_movehl_ps(__m128 a, const __m128 b) { #define _mm_movehl_ps my_movehl_ps inline __m128 my_cmplt_ps(__m128 a, const __m128 b) { - asm ( + __asm__ ( "cmpltps %2,%0\n\t" : "=x" (a) : "0" (a), "x"(b) @@ -237,7 +237,7 @@ inline __m128 my_cmplt_ps(__m128 a, const __m128 b) { return a; } inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) { - asm ( + __asm__ ( "cmpnleps %2,%0\n\t" : "=x" (a) : "0" (a), "x"(b) @@ -245,7 +245,7 @@ inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) { return a; } inline __m128 my_cmpeq_ps(__m128 a, const __m128 b) { - asm ( + __asm__ ( "cmpeqps %2,%0\n\t" : "=x" (a) : "0" (a), "x"(b) diff --git a/hi/mkv.c b/hi/mkv.c index 9c4ff11..50ad5ff 100644 --- a/hi/mkv.c +++ b/hi/mkv.c @@ -24,8 +24,15 @@ #include -#include +#ifdef _WIN32 +#include +#include +static int getrandom(void *buf, size_t buflen, unsigned int flags) { + return RtlGenRandom(buf, buflen) ? buflen : -1; +} +#else #include +#endif #define NALLENSZ 4 diff --git a/hi/node.c b/hi/node.c index b1492f0..e7bdd6a 100644 --- a/hi/node.c +++ b/hi/node.c @@ -2,8 +2,6 @@ #include #include"img.h" -#include -#include #include #include #include @@ -21,6 +19,9 @@ #include"node_internal.h" +#define STB_IMAGE_IMPLEMENTATION +#include + CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() { static int inited = 0; if(!inited) { @@ -545,32 +546,26 @@ static int image_perform(CHiPubNode *node) { } if(!internal->cacheImg) { - struct sail_image *simg; - if(sail_load_from_file(fn, &simg) != SAIL_OK) { + size_t w = 0, h = 0, n = 4; + float *data = stbi_loadf(fn, &w, &h, &n, 4); + if(!data) { CHi_AddError(node, "invalid file", 0); return 1; } - struct sail_image *cimg; - sail_convert_image(simg, SAIL_PIXEL_FORMAT_BPP64_BGRA, &cimg); - - sail_destroy_image(simg); - simg = NULL; - - CHiImage *img = CHi_Image_New(2, 4, (cimg->bytes_per_line + 15) & ~15, cimg->width, cimg->height, NULL); - CHi_Restride(cimg->pixels, img->data16, cimg->bytes_per_line, img->stride, img->height); + CHiImage *img = CHi_Image_New(2, 4, (w * 8 + 15) & ~15, w, h, NULL); internal->cacheImg = img; for(size_t y = 0; y < img->height; y++) { - for(size_t x = 0; x < img->stride; x += 16) { - __m128i pixels = _mm_load_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x)); - pixels = apply_gamma_epi16(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f)); - _mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixels); + for(size_t x = 0; x < img->width; x++) { + __m128 pixels = _mm_load_ps((__m128*) &data[w * y + x]); + pixels = apply_gamma_ps(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f)); + __m128i pixelsi = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(pixels, _mm_set1_ps(0.0f)), _mm_set1_ps(1.0f)), _mm_set1_ps(65535.0f))); + pixelsi = _mm_shuffle_epi8(pixelsi, _mm_set_epi8(0, 1, 4, 5, 8, 9, 12, 13, -128, -128, -128, -128, -128, -128, -128, -128)); + _mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixelsi); } } - sail_destroy_image(cimg); - free(internal->cachePath); internal->cachePath = strdup(fn); } diff --git a/hi/opus.c b/hi/opus.c index f1f5ec2..a6d9db2 100644 --- a/hi/opus.c +++ b/hi/opus.c @@ -33,7 +33,7 @@ static int encodeopus_perform(CHiPubNode *pubn) { n->pcmSamples += newpcm->width; } - CHiBSFrames *frames = malloc(sizeof(*frames)); + CHiBSFrames *frames = calloc(1, sizeof(*frames)); frames->count = 0; if(!n->firstFrame) { diff --git a/hi/relay_win.c b/hi/relay_win.c new file mode 100644 index 0000000..ecd890c --- /dev/null +++ b/hi/relay_win.c @@ -0,0 +1,149 @@ +#include"relay.h" + +#include"img.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static int scale_perform(CHiPubNode *n) { + float *scales = CHi_Crawl(&n->sinks[0])->data.vec4; + CHiImage *img = CHi_Crawl(&n->sinks[1])->data.sample; + + if(n->sources[0].data.sample) { + CHi_Image_Free(n->sources[0].data.sample); + } + CHiImage *ret = n->sources[0].data.sample = CHi_Image_New(img->bpc, img->channels, img->stride, img->width, img->height, NULL); + + __m128i iscales = _mm_set_epi16( + scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535, + scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535 + ); + + for(size_t y = 0; y < img->height; y++) { + for(size_t x = 0; x < img->width; x += 16) { + __m128i pixels8 = _mm_loadu_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x)); + __m128i mulled = _mm_mulhi_epu16(pixels8, iscales); + _mm_storeu_si128((__m128i*) ((uintptr_t) ret->data16 + y * img->stride + x), mulled); + } + } + + return 1; +} + +CUTIVIS CHiPubNode *CHi_ComponentScale() { + CHiPubNode *n = calloc(1, sizeof(*n)); + n->type = CUTIHI_T('CCmp','nScl'); + n->Start = n->Stop = NULL; + n->Perform = scale_perform; + n->sinkCount = 2; + n->sinks = calloc(sizeof(*n->sinks), n->sinkCount); + n->sourceCount = 1; + n->sources = calloc(sizeof(*n->sources), n->sourceCount); + return n; +} + +typedef struct { + CHiPubNode pub; + pthread_t thrd; + + char key[64]; + atomic_bool on; + atomic_bool active; +} CHiKeyhookNode; + +static _Thread_local CHiKeyhookNode *lookwhatyoumademedo; +static LRESULT CALLBACK keyhook_handler(int nCode, WPARAM wParam, LPARAM lParam) { + bool eatKeystroke = false; + + CHiKeyhookNode *n = lookwhatyoumademedo; + + if(nCode == HC_ACTION) { + switch(wParam) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: { + PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam; + bool press = wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN; + + char keyname[64]; + GetKeyNameTextA(p->vkCode, keyname, sizeof(keyname)); + if(!strcmp(keyname, n->key)) { + n->on = press; + } + break; + } + } + } + + return eatKeystroke ? true : CallNextHookEx(NULL, nCode, wParam, lParam); +} +static void *keyhook_thread(void *ud) { + CHiKeyhookNode *n = ud; + lookwhatyoumademedo = n; + + HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, keyhook_handler, 0, 0); + + MSG msg; + while(n->active && !GetMessage(&msg, NULL, NULL, NULL)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + UnhookWindowsHookEx(hhkLowLevelKybd); + + return NULL; +} + +static int keyhook_perform(CHiPubNode *n) { + CHiKeyhookNode *me = (CHiKeyhookNode*) n; + + strncpy(me->key, CHi_Crawl(&n->sinks[0])->data.text, 63); + me->key[63] = '\0'; + + n->sources[0].type = CUTIHI_VAL_VEC4; + + if(n->ng->compilationStatus == CUTIHI_COMP_READY || n->sinks[1].data.vec4[0] == 0) { + n->sources[0].data.vec4[0] = ((CHiKeyhookNode*) n)->on; + } else if(((CHiKeyhookNode*) n)->on) { + n->sources[0].data.vec4[0] = fminf(1, n->sources[0].data.vec4[0] + CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]); + } else { + n->sources[0].data.vec4[0] = fmaxf(0, n->sources[0].data.vec4[0] - CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]); + } + + return 1; +} + +static void keyhook_destroy(CHiPubNode *pubn) { + CHiKeyhookNode *n = (void*) pubn; + + n->active = false; + pthread_join(n->thrd, NULL); + + free(n); +} + +CUTIVIS CHiPubNode *CHi_Keyhook() { + CHiKeyhookNode *n = calloc(1, sizeof(*n)); + n->pub.type = CUTIHI_T('CKey','hook'); + n->pub.Start = n->pub.Stop = NULL; + n->pub.Perform = keyhook_perform; + n->pub.Destroy = keyhook_destroy; + n->pub.sinkCount = 2; + n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount); + n->pub.sourceCount = 1; + n->pub.sources = calloc(sizeof(*n->pub.sources), n->pub.sourceCount); + + n->on = 0; + n->key[0] = '\n'; + + n->active = true; + pthread_create(&n->thrd, NULL, keyhook_thread, n); + + return &n->pub; +} diff --git a/hi/relay.c b/hi/relay_x11.c similarity index 100% rename from hi/relay.c rename to hi/relay_x11.c diff --git a/hi/rtmp.c b/hi/rtmp.c index fa9cfbf..89b2c7d 100644 --- a/hi/rtmp.c +++ b/hi/rtmp.c @@ -9,8 +9,12 @@ #include #include +#ifdef _WIN32 +#include +#else #include #include +#endif #include"img.h" diff --git a/hi/serialize.c b/hi/serialize.c index 782acb9..5609c56 100644 --- a/hi/serialize.c +++ b/hi/serialize.c @@ -218,8 +218,8 @@ static void ebml_exit_callback(EBMLReader *ebml) { n = CHi_EncodeVP8(); } else if(type == CUTIHI_T('CEnc','GVP9')) { n = CHi_EncodeVP9(); - } else if(type == CUTIHI_T('CExp','Webm')) { - n = CHi_MuxWebm(); +// } else if(type == CUTIHI_T('CExp','Webm')) { +// n = CHi_MuxWebm(); } else if(type == CUTIHI_T('CKey','hook')) { n = CHi_Keyhook(); } else if(type == CUTIHI_T('CKey','hook')) { diff --git a/hi/webcam.c b/hi/webcam.c index 08ad0c6..4b3ca92 100644 --- a/hi/webcam.c +++ b/hi/webcam.c @@ -1,130 +1,20 @@ #include"node.h" -#include -#include -#include #include #include -#include #include #include -#include -#include #include #include"img.h" #include #include -static int camId = -1; - -static struct Buf { - size_t length; - uint8_t *ptr; -} bufs[2]; - -struct v4l2_format fmt; - -static void xioctl(int fh, int request, void *arg) { - int r; - - do { - r = v4l2_ioctl(fh, request, arg); - } while(r == -1 && ((errno == EINTR) || (errno == EAGAIN))); - - if(r == -1) { - fprintf(stderr, "error %d, %s\\n", errno, strerror(errno)); - exit(EXIT_FAILURE); - } -} - static int camera_perform(CHiPubNode *pubn) { - pubn->sources[0].type = CUTIHI_VAL_SAMPLE; - - fd_set fds; - FD_ZERO(&fds); - FD_SET(camId, &fds); - int r = select(camId + 1, &fds, NULL, NULL, &(struct timeval) {.tv_sec = 0, .tv_usec = 100}); - if(r == -1) { - return 1; - } - - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - xioctl(camId, VIDIOC_DQBUF, &buf); - - CHiImage *ret; - if(pubn->sources[0].data.sample) { - ret = pubn->sources[0].data.sample; - } else { - ret = CHi_Image_New(2, 4, 8 * ((fmt.fmt.pix.width + 15) & ~15), fmt.fmt.pix.width, fmt.fmt.pix.height, NULL); - pubn->sources[0].data.sample = ret; - } - - /* 24-to-32 TODO: make faster by processing 48-to-64 bytes in one iteration (like bgra32torgb24 does) */ - for(size_t y = 0; y < ret->height; y++) { - for(size_t x = 0; x < ret->width; x += 2) { - __m128i asdf = _mm_loadu_si128((__m128i*) &bufs[buf.index].ptr[(y * ret->width + x) * 3]); - asdf = _mm_shuffle_epi8(asdf, _mm_set_epi8(-128, -128, 5, -128, 4, -128, 3, -128, -128, -128, 2, -128, 1, -128, 0, -128)); - asdf = _mm_or_si128(asdf, _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0)); - _mm_stream_si128((__m128i*) ((uintptr_t) ret->data16 + y * ret->stride + x * 8), asdf); - } - } - - xioctl(camId, VIDIOC_QBUF, &buf); - + pubn->sources[0].type = CUTIHI_VAL_NONE; return 1; } CUTIVIS CHiPubNode *CHi_Camera() { - if(camId == -1) { - camId = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK, 0); - - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = 640; - fmt.fmt.pix.height = 480; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - xioctl(camId, VIDIOC_S_FMT, &fmt); - assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24); - - struct v4l2_requestbuffers req; - memset(&req, 0, sizeof(req)); - req.count = 2; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; - xioctl(camId, VIDIOC_REQBUFS, &req); - - for(int i = 0; i < 2; i++) { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; - xioctl(camId, VIDIOC_QUERYBUF, &buf); - - bufs[i].length = buf.length; - bufs[i].ptr = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camId, buf.m.offset); - - assert(MAP_FAILED != bufs[i].ptr); - } - - for(int i = 0; i < 2; i++) { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; - xioctl(camId, VIDIOC_QBUF, &buf); - } - - xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE}); - } - CHiPubNode *pubn = calloc(1, sizeof(*pubn)); pubn->type = CUTIHI_T('CWeb','Cam '); pubn->Start = pubn->Stop = NULL; diff --git a/hi/webcam_v4l2.c b/hi/webcam_v4l2.c new file mode 100644 index 0000000..08ad0c6 --- /dev/null +++ b/hi/webcam_v4l2.c @@ -0,0 +1,136 @@ +#include"node.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include"img.h" +#include +#include + +static int camId = -1; + +static struct Buf { + size_t length; + uint8_t *ptr; +} bufs[2]; + +struct v4l2_format fmt; + +static void xioctl(int fh, int request, void *arg) { + int r; + + do { + r = v4l2_ioctl(fh, request, arg); + } while(r == -1 && ((errno == EINTR) || (errno == EAGAIN))); + + if(r == -1) { + fprintf(stderr, "error %d, %s\\n", errno, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static int camera_perform(CHiPubNode *pubn) { + pubn->sources[0].type = CUTIHI_VAL_SAMPLE; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(camId, &fds); + int r = select(camId + 1, &fds, NULL, NULL, &(struct timeval) {.tv_sec = 0, .tv_usec = 100}); + if(r == -1) { + return 1; + } + + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + xioctl(camId, VIDIOC_DQBUF, &buf); + + CHiImage *ret; + if(pubn->sources[0].data.sample) { + ret = pubn->sources[0].data.sample; + } else { + ret = CHi_Image_New(2, 4, 8 * ((fmt.fmt.pix.width + 15) & ~15), fmt.fmt.pix.width, fmt.fmt.pix.height, NULL); + pubn->sources[0].data.sample = ret; + } + + /* 24-to-32 TODO: make faster by processing 48-to-64 bytes in one iteration (like bgra32torgb24 does) */ + for(size_t y = 0; y < ret->height; y++) { + for(size_t x = 0; x < ret->width; x += 2) { + __m128i asdf = _mm_loadu_si128((__m128i*) &bufs[buf.index].ptr[(y * ret->width + x) * 3]); + asdf = _mm_shuffle_epi8(asdf, _mm_set_epi8(-128, -128, 5, -128, 4, -128, 3, -128, -128, -128, 2, -128, 1, -128, 0, -128)); + asdf = _mm_or_si128(asdf, _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0)); + _mm_stream_si128((__m128i*) ((uintptr_t) ret->data16 + y * ret->stride + x * 8), asdf); + } + } + + xioctl(camId, VIDIOC_QBUF, &buf); + + return 1; +} + +CUTIVIS CHiPubNode *CHi_Camera() { + if(camId == -1) { + camId = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK, 0); + + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + xioctl(camId, VIDIOC_S_FMT, &fmt); + assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24); + + struct v4l2_requestbuffers req; + memset(&req, 0, sizeof(req)); + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + xioctl(camId, VIDIOC_REQBUFS, &req); + + for(int i = 0; i < 2; i++) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + xioctl(camId, VIDIOC_QUERYBUF, &buf); + + bufs[i].length = buf.length; + bufs[i].ptr = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camId, buf.m.offset); + + assert(MAP_FAILED != bufs[i].ptr); + } + + for(int i = 0; i < 2; i++) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + xioctl(camId, VIDIOC_QBUF, &buf); + } + + xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE}); + } + + CHiPubNode *pubn = calloc(1, sizeof(*pubn)); + pubn->type = CUTIHI_T('CWeb','Cam '); + pubn->Start = pubn->Stop = NULL; + pubn->Perform = camera_perform; + pubn->sinks = calloc(sizeof(*pubn->sinks), pubn->sinkCount = 0); + pubn->sources = calloc(sizeof(*pubn->sources), pubn->sourceCount = 1); + pubn->ng = NULL; + return pubn; +} diff --git a/hi/window.c b/hi/window.c deleted file mode 100644 index e69de29..0000000 diff --git a/hi/window.cpp b/hi/window_scl.cpp similarity index 89% rename from hi/window.cpp rename to hi/window_scl.cpp index ad1d5b2..2d16291 100644 --- a/hi/window.cpp +++ b/hi/window_scl.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -26,6 +27,7 @@ typedef struct { std::mutex mut; std::vector images; + std::atomic ignoreAlpha; } CHiWindowNode; static int window_perform(CHiPubNode *n) { @@ -33,6 +35,9 @@ static int window_perform(CHiPubNode *n) { MTR_BEGIN("CHi", "window_perform"); + CHiValue *ignoreAlphaVal = CHi_Crawl(&w->pub.sinks[1]); + w->ignoreAlpha.store(ignoreAlphaVal ? (bool) ignoreAlphaVal->data.vec4[0] : true); + const char *expectedTitle = CHi_Crawl(&w->pub.sinks[0])->data.text; if(w->lastWindowString == nullptr || strcmp(w->lastWindowString, expectedTitle)) { @@ -52,6 +57,8 @@ static int window_perform(CHiPubNode *n) { CHiImage *new_image = CHi_Image_New(2, 4, (window.Size.x * 8 + 15) & ~15, window.Size.x, window.Size.y, nullptr); memset(new_image->data8, 0, new_image->stride * new_image->height); + bool ignoreAlpha = w->ignoreAlpha.load(); + #pragma omp parallel for for(size_t y = 0; y < new_image->height; y++) { uint8_t buf[16] = {}; @@ -60,6 +67,9 @@ static int window_perform(CHiPubNode *n) { __m128i c = _mm_loadu_si128((__m128i*) buf); 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_store_si128((__m128i*) ((uintptr_t) new_image->data8 + y * new_image->stride + x * 8), c); } @@ -103,7 +113,7 @@ CUTIVIS CHiPubNode *CHi_Window() { n->pub.Perform = window_perform; n->pub.Destroy = window_destroy; n->pub.sinkCount = 1; - n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), 1); + n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), 2); n->pub.sourceCount = 1; n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), 1); diff --git a/hi/window_x11.cpp b/hi/window_x11.cpp new file mode 100644 index 0000000..c7504e5 --- /dev/null +++ b/hi/window_x11.cpp @@ -0,0 +1,218 @@ +#include"node.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include"img.h" + +#include"linearity.h" + +#include"minitrace.h" + +static Display *d; +static 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(windowName.value, contains)) { + *w = list[i]; + found = 1; + break; + } + } + } + } + + XFree(list); + + return found; + } else { + *w = root; + return 1; + } +} + +typedef struct { + CHiPubNode pub; + + Window xcache; + XImage *ximg; + XShmSegmentInfo shminfo; + + CHiImage *vcache; +} CHiWindowNode; + +static int window_perform(CHiPubNode *n) { + CHiWindowNode *w = (CHiWindowNode*) n; + + 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 = shmat(w->shminfo.shmid, 0, 0); + w->shminfo.readOnly = False; + XShmAttach(d, &w->shminfo); + + w->vcache = CHi_Image_New(2, 4, 8 * attrs.width, 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); + + // 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)); + 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) { + CHiWindowNode *n = (void*) pubn; + + if(n->vcache) { + XShmDetach(d, &n->shminfo); + shmdt(n->shminfo.shmaddr); + XDestroyImage(n->ximg); + } + + free(pubn); +} + +CUTIVIS CHiPubNode *CHi_Window() { + if(!d) { + d = XOpenDisplay(NULL); + root = RootWindow(d, DefaultScreen(d)); + } + + CHiWindowNode *n = calloc(1, sizeof(*n)); + n->pub.type = CUTIHI_T('CWin','dow '); + n->pub.Start = n->pub.Stop = NULL; + n->pub.Perform = window_perform; + n->pub.Destroy = window_destroy; + n->pub.sinkCount = 1; + n->pub.sinks = calloc(sizeof(*n->pub.sinks), 1); + n->pub.sourceCount = 1; + n->pub.sources = calloc(sizeof(*n->pub.sources), 1); + + n->xcache = 0; + n->vcache = NULL; + + return &n->pub; +} + +// All of the following are ews + +CUTIVIS size_t CHi_Window_GetSourceCount() { + Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1); + Atom actualType; + int format; + unsigned long numItems, bytesAfter; + + Window *list; + + int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list); + + //XFree(list); + + return status >= Success ? numItems : 0; +} + +CUTIVIS const char *CHi_Window_GetSourceName(size_t idx) { + 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) { + //XFree(list); + + status = XGetWMName(d, list[idx], &windowName); + if(status >= Success) { + found = 1; + } + } + + return found ? strdup(windowName.value ? windowName.value : "") : NULL; +} + +CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t idx) { + Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1); + Atom actualType; + int format; + unsigned long numItems, bytesAfter; + + Window *list; + + int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list); + + if(status >= Success) { + Window ret = list[idx]; + //XFree(list); + return ret; + } + + return 0; +} + +CUTIVIS size_t CHi_Window_GetNextSource(size_t i) { + return i + 1; +} diff --git a/ui/frame.cpp b/ui/frame.cpp index a49abe8..68e97c4 100644 --- a/ui/frame.cpp +++ b/ui/frame.cpp @@ -135,19 +135,19 @@ static void ShapeGrNode(GrNode *gn) { gn->sinks.push_back({" Size", GrNode::Port::Type::VEC1, true}); } } else if(gn->logical->type == CUTIHI_T('CIma','ge ')) { - gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}}; + gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPENING}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}}; } else if(gn->logical->type == CUTIHI_T('CWin','dow ')) { - gn->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}}; + gn->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}, {"Ignore Alpha", GrNode::Port::Type::CHECKBOX}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}}; } else if(gn->logical->type == CUTIHI_T('CInA','udio')) { gn->sinks = {{"Source", GrNode::Port::Type::MIC_SOURCE}}; gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}}; } else if(gn->logical->type == CUTIHI_T('CExp','Wave')) { - gn->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVE}, {"Audio", GrNode::Port::Type::SAMPLE}}; + gn->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVING}, {"Audio", GrNode::Port::Type::SAMPLE}}; gn->sources = {}; } else if(gn->logical->type == CUTIHI_T('CMov','ie ')) { - gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}, {"Time", GrNode::Port::Type::VEC1}}; + gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPENING}, {"Time", GrNode::Port::Type::VEC1}}; gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Audio", GrNode::Port::Type::SAMPLE}}; } else if(gn->logical->type == CUTIHI_T('CEnc','GVP8')) { gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}}; @@ -159,7 +159,7 @@ static void ShapeGrNode(GrNode *gn) { gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Bitstream"}}; } else if(gn->logical->type == CUTIHI_T('CExp','Webm')) { - gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}}; + gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVING}}; gn->sources = {}; } else if(gn->logical->type == CUTIHI_T('CKey','hook')) { gn->sinks = {{"Key", GrNode::Port::Type::TEXT}, {"Smooth Time", GrNode::Port::Type::VEC1}}; @@ -183,7 +183,7 @@ static void ShapeGrNode(GrNode *gn) { gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}}; gn->sources = {{"Bitstream"}}; } else if(gn->logical->type == CUTIHI_T('CExp','Mkv ')) { - gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}}; + gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVING}}; gn->sources = {}; } @@ -484,8 +484,8 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) int y = 13; int i = 0; for(Port &p : sinks) { - wxColour col = p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0} - : p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0} + wxColour col = p.type == GrNode::Port::Type::FILE_OPENING ? wxColour{255, 0, 0} + : p.type == GrNode::Port::Type::FILE_SAVING ? wxColour{255, 0, 0} : p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255} : p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0} : p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0} @@ -512,8 +512,8 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) y = 13; i = 0; for(Port &p : sources) { - wxColour col = p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0} - : p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0} + wxColour col = p.type == GrNode::Port::Type::FILE_OPENING ? wxColour{255, 0, 0} + : p.type == GrNode::Port::Type::FILE_SAVING ? wxColour{255, 0, 0} : p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255} : p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0} : p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0} @@ -650,7 +650,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) pthread_mutex_unlock(&this->logical->ng->mut); } - } else if(sinks[p].type == Port::Type::FILE_OPEN) { + } else if(sinks[p].type == Port::Type::FILE_OPENING) { wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_PREVIEW); if(dlg.ShowModal() == wxID_OK) { pthread_mutex_lock(&this->logical->ng->mut); @@ -663,7 +663,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) pthread_mutex_unlock(&this->logical->ng->mut); } - } else if(sinks[p].type == Port::Type::FILE_SAVE) { + } else if(sinks[p].type == Port::Type::FILE_SAVING) { wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_PREVIEW | wxFD_OVERWRITE_PROMPT); if(dlg.ShowModal() == wxID_OK) { pthread_mutex_lock(&this->logical->ng->mut); @@ -679,28 +679,29 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) } else if(sinks[p].type >= Port::Type::VEC1 && sinks[p].type <= Port::Type::VEC4) { auto ctrls = std::make_shared>(); for(int i = 0; i <= (int) sinks[p].type - (int) Port::Type::VEC1; i++) { - wxTextCtrl *tc = new wxTextCtrl(GetParent(), wxID_ANY, wxString::Format("%f", this->logical->sinks[p].data.vec4[i]), GetParent()->ScreenToClient(ClientToScreen({5 + 60 * i, (p + 1) * 20}))); - tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){ - if(ev.GetKeyCode() == WXK_RETURN) { - double d; - if(tc->GetValue().ToDouble(&d)) { - pthread_mutex_lock(&this->logical->ng->mut); - - CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]); - newv.type = CUTIHI_VAL_VEC4; - newv.data.vec4[i] = d; - CHi_ConfigureSink(this->logical, p, newv); + wxTextCtrl *tc = new wxTextCtrl(GetParent(), wxID_ANY, wxString::Format("%f", this->logical->sinks[p].data.vec4[i]), GetParent()->ScreenToClient(ClientToScreen({5 + 60 * i, (p + 1) * 20})), wxDefaultSize, wxTE_PROCESS_ENTER); + tc->Bind(wxEVT_TEXT_ENTER, [=](wxCommandEvent &ev){ + double d; + if(tc->GetValue().ToDouble(&d)) { + pthread_mutex_lock(&this->logical->ng->mut); - auto it = std::find(ctrls->begin(), ctrls->end(), tc); - ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus(); - ctrls->erase(it); - - CallAfter([tc](){tc->Destroy();}); - parent->Dirtify(this); - - pthread_mutex_unlock(&this->logical->ng->mut); - } - } else if(ev.GetKeyCode() == WXK_TAB) { + CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]); + newv.type = CUTIHI_VAL_VEC4; + newv.data.vec4[i] = d; + CHi_ConfigureSink(this->logical, p, newv); + + auto it = std::find(ctrls->begin(), ctrls->end(), tc); + ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus(); + ctrls->erase(it); + + CallAfter([tc](){tc->Destroy();}); + parent->Dirtify(this); + + pthread_mutex_unlock(&this->logical->ng->mut); + } + }); + tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){ + if(ev.GetKeyCode() == WXK_TAB) { ctrls->operator[]((i + ctrls->size() + (wxGetKeyState(WXK_SHIFT) ? -1 : 1)) % ctrls->size())->SetFocus(); parent->Dirtify(this); @@ -710,7 +711,7 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) } ctrls->operator[](0)->SetFocus(); } else if(sinks[p].type == Port::Type::TEXT) { - wxTextCtrl *ctrl = new wxTextCtrl(GetParent(), wxID_ANY, this->logical->sinks[p].data.text, GetParent()->ScreenToClient(ClientToScreen({5, (p + 1) * 26}))); + wxTextCtrl *ctrl = new wxTextCtrl(GetParent(), wxID_ANY, this->logical->sinks[p].data.text, GetParent()->ScreenToClient(ClientToScreen({5, (p + 1) * 26})), wxDefaultSize, wxTE_PROCESS_ENTER); ctrl->SetValue(wxString{CHi_Crawl(&this->logical->sinks[p])->data.text}); ctrl->SetFocus(); ctrl->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){ @@ -727,21 +728,19 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) pthread_mutex_unlock(&this->logical->ng->mut); }); - ctrl->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){ - if(ev.GetKeyCode() == WXK_RETURN) { - pthread_mutex_lock(&this->logical->ng->mut); - - CHiValue newv = {}; - newv.type = CUTIHI_VAL_TEXT; - char *c = (char*) malloc(ctrl->GetValue().Len() + 1); - memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1); - newv.data.text = c; - CHi_ConfigureSink(this->logical, p, newv); - CallAfter([ctrl](){ctrl->Destroy();}); - parent->Dirtify(this); - - pthread_mutex_unlock(&this->logical->ng->mut); - } else ev.Skip(); + ctrl->Bind(wxEVT_TEXT_ENTER, [=](wxCommandEvent &ev){ + pthread_mutex_lock(&this->logical->ng->mut); + + CHiValue newv = {}; + newv.type = CUTIHI_VAL_TEXT; + char *c = (char*) malloc(ctrl->GetValue().Len() + 1); + memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1); + newv.data.text = c; + CHi_ConfigureSink(this->logical, p, newv); + CallAfter([ctrl](){ctrl->Destroy();}); + parent->Dirtify(this); + + pthread_mutex_unlock(&this->logical->ng->mut); }); } else if(sinks[p].type == Port::Type::MIC_SOURCE) { std::vector choices; @@ -790,6 +789,26 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) } CHi_Window_FreeList(wbuf); + } else if(sinks[p].type == Port::Type::CHECKBOX) { + wxCheckBox *cb = new wxCheckBox(this, wxID_ANY, sinks[p].name); + cb->SetValue((bool) this->logical->sinks[p].data.vec4[0]); + cb->SetFocus(); + cb->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){ + pthread_mutex_lock(&this->logical->ng->mut); + + CHiValue newv = {}; + newv.type = CUTIHI_VAL_VEC4; + newv.data.vec4[0] = cb->IsChecked(); + newv.data.vec4[1] = cb->IsChecked(); + newv.data.vec4[2] = cb->IsChecked(); + newv.data.vec4[3] = cb->IsChecked(); + CHi_ConfigureSink(this->logical, p, newv); + parent->Dirtify(this); + + CallAfter([cb](){cb->Destroy();}); + + pthread_mutex_unlock(&this->logical->ng->mut); + }); } } }); diff --git a/ui/frame.h b/ui/frame.h index 23b7788..d91ac7d 100644 --- a/ui/frame.h +++ b/ui/frame.h @@ -51,7 +51,7 @@ struct GrNode : wxPanel { struct Port { wxString name; enum class Type { - NONE, FILE_OPEN, COLOR, VEC1, VEC2, VEC3, VEC4, TEXT, SAMPLE, FILE_SAVE, MIC_SOURCE, WINDOW_SOURCE + NONE, FILE_OPENING, COLOR, VEC1, VEC2, VEC3, VEC4, TEXT, SAMPLE, FILE_SAVING, MIC_SOURCE, WINDOW_SOURCE, CHECKBOX } type; bool separator; diff --git a/ui/main.cpp b/ui/main.cpp index 5e1942f..a92dc5c 100644 --- a/ui/main.cpp +++ b/ui/main.cpp @@ -6,6 +6,9 @@ Frame *globaldis; struct App : wxApp { virtual bool OnInit() { +#if wxCHECK_VERSION(3, 3, 0) + SetAppearance(Appearance::System); +#endif (new Frame())->Show(true); return true; } diff --git a/ui/timeline.cpp b/ui/timeline.cpp index f8d7990..714b919 100644 --- a/ui/timeline.cpp +++ b/ui/timeline.cpp @@ -259,8 +259,8 @@ Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) { switch(sinks[i].type) { case GrNode::Port::Type::TEXT: - case GrNode::Port::Type::FILE_OPEN: - case GrNode::Port::Type::FILE_SAVE: + case GrNode::Port::Type::FILE_OPENING: + case GrNode::Port::Type::FILE_SAVING: SetToolTip(wxString{val->text}); break; case GrNode::Port::Type::VEC1: