/* version 2024-04-14 */ /* gcc -Wall -o gui gui.c -lX11 -pthread */ #include #include #include #include #include #include #include /* shared screen buffer start */ typedef enum { ABGR, ARGB, RGBA, BGRA, RGB, GREY } rgb_t; typedef struct { pthread_mutex_t m; pthread_cond_t t; unsigned char *s; int cur; int curw; volatile int size; volatile int maxsize; int w, h; rgb_t mode; volatile int no_more_data; unsigned char *rgb; /* input buffer for rgb 24 bits mode */ } shared_screen_t; void get_screen(unsigned char *out, shared_screen_t *s) { if (pthread_mutex_lock(&s->m)) abort(); while (!s->no_more_data && s->size == 0) if (pthread_cond_wait(&s->t, &s->m)) abort(); if (s->size == 0) { fflush(stdout); exit(1); } memcpy(out, s->s + s->cur * (s->w * s->h * 4), s->w * s->h * 4); s->cur++; s->cur %= s->maxsize; s->size--; if (pthread_cond_signal(&s->t)) abort(); if (pthread_mutex_unlock(&s->m)) abort(); } int read_screen(shared_screen_t *s) { unsigned char *b; int bytes_per_pixel = 4; if (pthread_mutex_lock(&s->m)) abort(); while (s->size == s->maxsize) if (pthread_cond_wait(&s->t, &s->m)) abort(); if (pthread_mutex_unlock(&s->m)) abort(); b = s->s + s->curw * (s->w * s->h * 4); if (s->mode == GREY) bytes_per_pixel = 1; if (s->mode == RGB) { bytes_per_pixel = 3; b = s->rgb; } if (fread(b, s->w * s->h * bytes_per_pixel, 1, stdin) != 1) { if (pthread_mutex_lock(&s->m)) abort(); s->no_more_data = 1; if (pthread_cond_signal(&s->t)) abort(); if (pthread_mutex_unlock(&s->m)) abort(); return 0; } if (s->mode == ABGR) { int i; unsigned char *in = b; for (i = 0; i < s->w * s->h; i++, in+=4) { int b = in[1]; int g = in[2]; int r = in[3]; in[0] = b; in[1] = g; in[2] = r; in[3] = 255; } } if (s->mode == RGBA) { int i; unsigned char *in = b; for (i = 0; i < s->w * s->h; i++, in+=4) { int b = in[2]; int g = in[1]; int r = in[0]; in[0] = b; in[1] = g; in[2] = r; in[3] = 255; } } if (s->mode == ARGB) { int i; unsigned char *in = b; for (i = 0; i < s->w * s->h; i++, in+=4) { int b = in[3]; int g = in[2]; int r = in[1]; in[0] = b; in[1] = g; in[2] = r; in[3] = 255; } } if (s->mode == BGRA) { int i; unsigned char *in = b; for (i = 0; i < s->w * s->h; i++, in+=4) { int b = in[0]; int g = in[1]; int r = in[2]; in[0] = b; in[1] = g; in[2] = r; in[3] = 255; } } if (s->mode == RGB) { int i; unsigned char *in = s->rgb; unsigned char *out = s->s + s->curw * (s->w * s->h * 4); for (i = 0; i < s->w * s->h; i++, in+=3, out+=4) { int r = in[0]; int g = in[1]; int b = in[2]; out[0] = b; out[1] = g; out[2] = r; out[3] = 255; } } if (s->mode == GREY) { int i; unsigned char *in = b + (s->w * s->h - 1) * 4; for (i = s->w * s->h - 1; i >= 0; i--, in-=4) { int g = b[i]; in[0] = g; in[1] = g; in[2] = g; in[3] = 255; } } if (pthread_mutex_lock(&s->m)) abort(); s->size++; s->curw++; s->curw %= s->maxsize; if (pthread_cond_signal(&s->t)) abort(); if (pthread_mutex_unlock(&s->m)) abort(); return 1; } void init_screen(shared_screen_t *s, int w, int h, rgb_t mode) { s->size = 0; s->maxsize = 100; s->w = w; s->h = h; s->s = malloc(s->maxsize * w * h * 4); if (s->s == NULL) { printf("no memory\n"); exit(1); } s->cur = 0; s->curw = 0; s->mode = mode; s->no_more_data = 0; pthread_mutex_init(&s->m, NULL); pthread_cond_init(&s->t, NULL); if (mode == RGB) { s->rgb = malloc(w * h * 3); if (s->rgb == NULL) { printf("no memory\n"); exit(1); } } } /* shared screen buffer end */ /* print FPS begin */ volatile int frame; void *tfps(void *_) { while (1) { fprintf(stderr, "fps: %d\n", frame); frame = 0; sleep(1); } return 0; } /* printf FPS end */ typedef struct { Display *d; Window w; XImage *i; unsigned char *screen; int width; int height; int fps; } display_t; /* delay management begin */ long double frame_time; void time_start(void) { struct timespec l; if (clock_gettime(CLOCK_REALTIME, &l)) abort(); frame_time = l.tv_sec * 1000000000. + l.tv_nsec; } void delay(int fps) { struct timespec t; struct timespec delay; long double cur_time; frame_time += 1000000000. / fps; if (clock_gettime(CLOCK_REALTIME, &t)) abort(); cur_time = t.tv_sec * 1000000000. + t.tv_nsec; if (frame_time < cur_time) { fprintf(stderr, "ERROR: we late\n"); return; } delay.tv_nsec = ((uint64_t)(frame_time-cur_time)) % 1000000000; delay.tv_sec = ((uint64_t)(frame_time-cur_time)) / 1000000000; if (pselect(0, NULL, NULL, NULL, &delay, NULL)) { fprintf(stderr, "ERROR: pselect fails\n"); exit(1); } } /* delay management end */ void *tin(void *_screen_buffer) { shared_screen_t *screen_buffer = _screen_buffer; while (read_screen(screen_buffer)); return (void *)0; } void do_scale_and_flip(unsigned char *out, unsigned char *in, int width, int height, int scale, int flip) { /* very inefficient flipping code */ if (flip == 1) { for (int y = 0; y < height; y++) memcpy(out + y * width * 4, in + (height-1-y) * width * 4, width * 4); memcpy(in, out, width*height*4); } if (scale == 1) { memcpy(out, in, width * height * 4); return; } int x, y; int i; for (y = 0; y < height; y++) for (i = 0; i < 4; i++) for (x = 0; x < width; x++) { memcpy(out, in+(y*width+x)*4, 4); out+=4; memcpy(out, in+(y*width+x)*4, 4); out+=4; memcpy(out, in+(y*width+x)*4, 4); out+=4; memcpy(out, in+(y*width+x)*4, 4); out+=4; } } void usage(void) { printf("options:\n"); printf(" -w \n"); printf(" -h \n"); printf(" -f \n"); printf(" -rgba\n"); printf(" -argb\n"); printf(" -bgra\n"); printf(" -rgb\n"); printf(" -grey\n"); printf(" select input type (default abgr)\n"); printf(" -stdout\n"); printf(" output to stdout, no gui\n"); printf(" -scale4\n"); printf(" scale image by 4 (1 original pixel gets 4x4 bigger)\n"); printf(" -flip\n"); printf(" flip horizontally\n"); exit(1); } int main(int n, char **v) { shared_screen_t screen_buffer; unsigned char *buf; display_t d; int input_width = 640; int input_height = 360; int output_width = 640; int output_height = 360; int fps = 25; int i; rgb_t mode = ABGR; int to_stdout = 0; int scale = 1; int flip = 0; for (i = 1; i < n; i++) { if (!strcmp(v[i], "--help")) usage(); if (!strcmp(v[i], "-w")) { if (i>n-2) usage(); input_width = atoi(v[++i]); continue; } if (!strcmp(v[i], "-h")) { if (i>n-2) usage(); input_height = atoi(v[++i]); continue; } if (!strcmp(v[i], "-f")) { if (i>n-2) usage(); fps = atoi(v[++i]); continue; } if (!strcmp(v[i], "-rgba")) { mode = RGBA; continue; } if (!strcmp(v[i], "-argb")) { mode = ARGB; continue; } if (!strcmp(v[i], "-bgra")) { mode = BGRA; continue; } if (!strcmp(v[i], "-rgb")) { mode = RGB; continue; } if (!strcmp(v[i], "-grey")) { mode = GREY; continue; } if (!strcmp(v[i], "-stdout")) { to_stdout = 1; continue; } if (!strcmp(v[i], "-scale4")) { scale = 4; continue; } if (!strcmp(v[i], "-flip")) { flip = 1; continue; } usage(); } output_width = input_width * scale; output_height = input_height * scale; d.screen = malloc(output_width * output_height * 4); if (d.screen == NULL) { printf("out of memory\n"); exit(1); } buf = malloc(input_width * input_height * 4); if (buf == NULL) { printf("out of memory\n"); exit(1); } if (to_stdout) goto no_x11; d.d = XOpenDisplay(0); if (d.d == NULL) { printf("no display?\n"); exit(1); } d.w = XCreateSimpleWindow(d.d, DefaultRootWindow(d.d), 0, 0, output_width, output_height, 0, 0, 0); XMapWindow(d.d, d.w); d.i = XCreateImage(d.d, DefaultVisual(d.d, DefaultScreen(d.d)), 24 /*depth*/, ZPixmap, 0 /*offset*/, (char*)d.screen, output_width, output_height, 8, 0); if (d.i == NULL) { printf("create image failed\n"); exit(1); } d.width = output_width; d.height = output_height; d.fps = fps; no_x11: init_screen(&screen_buffer, input_width, input_height, mode); pthread_t tht; pthread_create(&tht, NULL, tfps, NULL); pthread_create(&tht, NULL, tin, &screen_buffer); if (to_stdout) { while (1) { get_screen(buf, &screen_buffer); do_scale_and_flip(d.screen, buf, input_width, input_height, scale, flip); fwrite(d.screen, output_width * output_height * 4, 1, stdout); } } time_start(); while (1) { XEvent e; while (XPending(d.d)) XNextEvent(d.d, &e); get_screen(buf, &screen_buffer); do_scale_and_flip(d.screen, buf, input_width, input_height, scale, flip); XPutImage(d.d, d.w, DefaultGC(d.d, DefaultScreen(d.d)), d.i, 0, 0, 0, 0, d.width, d.height); delay(fps); frame++; } return 0; }