/* * Copyright © 2018 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct monitor { int fd; }; #define BILLION 1000000000ULL #define MILLION 1000000ULL static uint64_t vid_clock(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ((uint64_t) ts.tv_sec * BILLION + (uint64_t) ts.tv_nsec); } static void flush(int fd) { char buf[1024]; int n; int avail; for (;;) { n = ioctl(fd, FIONREAD, &avail); if (n < 0 || avail <= 0) break; read(fd, buf, avail); } } static uint64_t vid_time[2]; static int vid_value; static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t time_cond = PTHREAD_COND_INITIALIZER; static void *monitor_device(void *arg) { struct monitor *m = arg; int fd = m->fd; char buf[256]; int n; struct termios termios; uint64_t t; int v; tcgetattr(fd, &termios); cfmakeraw(&termios); cfsetospeed(&termios, B9600); cfsetispeed(&termios, B9600); tcsetattr(fd, TCSAFLUSH, &termios); flush(fd); write(fd, "E 0\nV 0\n", 8); sleep(1); flush(fd); write(fd, "V 1\n", 4); for (;;) { n = read(fd, buf, sizeof(buf)); if (n < 0) { perror("read"); return NULL; } t = vid_clock(); switch(buf[0]) { case '1': case '0': v = buf[0] - '0'; pthread_mutex_lock(&time_mutex); vid_value = v; vid_time[v] = t; pthread_mutex_unlock(&time_mutex); pthread_cond_signal(&time_cond); break; } } } #define W 512 #define H 512 #define DEV "/dev/ttyACM0" int main(int argc, char **argv) { Display *dpy; int scr; Window root; Window win; GC gc; Pixmap pix; int v = 0; struct timespec ts = { .tv_sec = 0, .tv_nsec = 100 * MILLION }; pthread_t monitor_thread; struct monitor monitor; uint64_t put, show, got; uint32_t serial = 0; XEvent ev; int ge_event_base, ge_error_base; int present_opcode, present_event_base, present_error_base; bool got_event, exposed; XSetWindowAttributes swa = { .event_mask = ExposureMask }; monitor.fd = open(DEV, O_RDWR); if (monitor.fd < 0) { perror(DEV); exit(1); } pthread_create(&monitor_thread, NULL, monitor_device, &monitor); dpy = XOpenDisplay(NULL); scr = DefaultScreen(dpy); root = RootWindow(dpy, scr); win = XCreateWindow(dpy, root, 0, 0, W, H, 0, DefaultDepth(dpy, scr), InputOutput, CopyFromParent, CWEventMask, &swa); pix = XCreatePixmap(dpy, win, W, H, DefaultDepth(dpy, scr)); gc = XCreateGC(dpy, win, 0, NULL); XMapWindow(dpy, win); XPresentQueryExtension(dpy, &present_opcode, &present_event_base, &present_error_base); XPresentSelectInput(dpy, win, PresentCompleteNotifyMask); for (;;) { XSetForeground(dpy, gc, v ? WhitePixel(dpy, scr) : BlackPixel(dpy, scr)); XFillRectangle(dpy, pix, gc, 0, 0, W, H); XPresentPixmap(dpy, win, pix, ++serial, None, None, 0, 0, None, None, None, PresentOptionNone, 0, 0, 0, NULL, 0); put = vid_clock(); XFlush(dpy); got_event = false; exposed = false; while (!got_event) { XGenericEventCookie *cookie = (void *) &ev; XNextEvent(dpy, &ev); if (XGetEventData(dpy, cookie)) { if (cookie->extension == present_opcode && cookie->evtype == PresentCompleteNotify) { XPresentCompleteNotifyEvent *ce = cookie->data; if (ce->serial_number == serial) { show = ce->ust * 1000; got_event = true; } } XFreeEventData(dpy, cookie); } else { switch (ev.type) { case Expose: if (ev.xexpose.count == 0) exposed = true; break; } } } if (!exposed) { pthread_mutex_lock(&time_mutex); for (;;) { if (vid_value == v) { got = vid_time[v]; break; } pthread_cond_wait (&time_cond, &time_mutex); } pthread_mutex_unlock(&time_mutex); if (show >= put && got >= show) { printf("%d delay put->show %f show->got %f\n", v, (show - put) / 1e9, (got - show) / 1e9); } else { printf("%d invalid\n", v); } } v = 1-v; nanosleep(&ts, NULL); } }