#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basement.raw.h" static int touchscreen_fd; static uint32_t *fb; #define MAX_MT_SLOTS 16 static struct mtslot { int x, y, touch; int lastx, lasty; } mtslots[MAX_MT_SLOTS] = {0}; static struct mtslot *curslot = &mtslots[0]; // TODO: query the current value of ABS_MT_SLOT static void poll_touchscreen() { struct input_event buf[64]; int nread; retry_read: nread = read(touchscreen_fd, buf, sizeof buf); if(nread < 0) { if(errno == EINTR) goto retry_read; if(errno == EAGAIN) return; error(1, errno, "read touchscreen"); } if(nread % sizeof(struct input_event)) error(1, 0, "odd size read from touchscreen"); for(int i = 0; i < nread; i += sizeof(struct input_event)) { struct input_event *evt = (struct input_event*)((char*)buf + i); //printf("type=%d code=%d value=%d\n", evt->type, evt->code, evt->value); if(evt->type == EV_SYN && evt->code == SYN_DROPPED) error(0, 0, "got SYN_DROPPED"); // TODO: resynchronize if(evt->type == EV_SYN && evt->code == SYN_REPORT) { for(int j = 0; j < MAX_MT_SLOTS; j++) { struct mtslot *s = &mtslots[j]; if(s->touch) { //printf("slot %d x=%d y=%d\n", j, s->x, s->y); uint32_t col; switch(j & 3) { case 0: col = 0xFFFFFFFF; break; case 1: col = 0xFF0000FF; break; case 2: col = 0x00FF00FF; break; case 3: col = 0x0080FFFF; break; } int segs = s->lastx<0 ? 1 : (int)(0.9 + 2*sqrt((s->lastx-s->x)*(s->lastx-s->x) + (s->lasty-s->y)*(s->lasty-s->y))); if(segs<1) segs=1; for(int f = 0; f <= segs; f++) { int _x, _y; if(s->lastx != -1) { _x = (s->lastx + (s->x-s->lastx)*f/segs); _y = (s->lasty + (s->y-s->lasty)*f/segs); } else { _x = s->x; _y = s->y; } for(int dy = -5; dy <= 5; dy++) { for(int dx = -5; dx <= 5; dx++) { if(dx*dx + dy*dy <= 5*5) { int x = dx+_x, y = dy+_y; if(x >= 0 && y >= 0 && x < 800 && y < 1280) { *(uint32_t*)(fb + (y*800 + x)) = col; } } } } } s->lastx = s->x; s->lasty = s->y; } } } if(evt->type == EV_ABS && evt->code == ABS_MT_SLOT) { if(evt->value >= 0 && evt->value < MAX_MT_SLOTS) curslot = &mtslots[evt->value]; else curslot = NULL; } if(evt->type == EV_ABS && evt->code == ABS_MT_POSITION_X) { if(curslot) curslot->x = evt->value; } if(evt->type == EV_ABS && evt->code == ABS_MT_POSITION_Y) { if(curslot) curslot->y = evt->value; } if(evt->type == EV_ABS && evt->code == ABS_MT_TRACKING_ID) { if(curslot) { if(!curslot->touch) { curslot->lastx = curslot->lasty = -1; } curslot->touch = evt->value != -1; } } } } int main(int argc, char **argv) { if(getuid() != 0) { execlp("sudo", "sudo", argv[0], NULL); error(1, errno, "execlp"); return 1; } int fd = open("/dev/dri/card0", O_RDWR); if(fd < 0) error(1, errno, "open display"); touchscreen_fd = open("/dev/input/event1", O_RDONLY | O_NONBLOCK); if(touchscreen_fd < 0) error(1, errno, "open touchscreen"); printf("opened\n"); if(ioctl(fd, DRM_IOCTL_SET_MASTER, NULL) < 0) error(1, errno, "set master"); // attach CRTC 51, connector 56, to a new framebuffer with a new dumbbuffer struct drm_mode_create_dumb createdumb = { .height = 1280, .width = 800, .bpp = 32, .flags = 0, }; if(ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &createdumb) < 0) error(1, errno, "create dumb buffer"); struct drm_mode_fb_cmd2 addfb = { .width = 800, .height = 1280, .pixel_format = DRM_FORMAT_XRGB8888, .flags = 0, .handles = {createdumb.handle, 0, 0, 0}, .pitches = {createdumb.pitch, 0, 0, 0}, .offsets = {0, 0, 0, 0}, }; if(ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &addfb) < 0) error(1, errno, "add framebuffer"); struct drm_mode_map_dumb mapdumb = { .handle = createdumb.handle }; if(ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mapdumb) < 0) error(1, errno, "map dumb buffer"); fb = (uint32_t*)mmap(NULL, createdumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mapdumb.offset); if(!fb) error(1, errno, "mmap dumb buffer"); //getrandom(fb, createdumb.size, 0); memset(fb, 0, createdumb.size); memcpy(fb, basement.pixel_data, createdumb.size); uint32_t connectors[1] = {56}; struct drm_mode_crtc crtc = {.crtc_id = 51}; if(ioctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc) < 0) error(1, errno, "get crtc"); //struct drm_mode_crtc oldcrtc = crtc; crtc.set_connectors_ptr = (uint64_t)connectors; crtc.count_connectors=1; crtc.fb_id = addfb.fb_id; if(ioctl(fd, DRM_IOCTL_MODE_SETCRTC, &crtc) < 0) error(1, errno, "set crtc"); int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if(timerfd < 0) error(1, errno, "timerfd_create"); { struct itimerspec tspec = {.it_interval = {.tv_nsec = 250000000}, .it_value = {.tv_nsec=250000000}}; if(timerfd_settime(timerfd, 0, &tspec, NULL) < 0) error(1, errno, "timerfd_settime"); } printf("ready\n"); for(;;) { struct pollfd polls[] = { {.fd = touchscreen_fd, .events = POLLIN}, {.fd = timerfd, .events = POLLIN}, }; int npoll = poll(polls, sizeof(polls)/sizeof(polls[0]), 0); if(npoll < 0) error(1, errno, "poll"); // POLLERR, POLLHUP, POLLNVAL can always be returned if((polls[0].revents | polls[1].revents) & POLLNVAL) error(1, 0, "poll: got POLLNVAL"); if(polls[0].revents & POLLERR) error(1, 0, "got POLLERR on touchscreen"); if(polls[0].revents & POLLHUP) error(1, 0, "got POLLHUP on touchscreen"); if(polls[1].revents & POLLERR) error(1, 0, "got POLLERR on animation timer"); if(polls[1].revents & POLLHUP) error(1, 0, "got POLLHUP on animation timer"); if(polls[0].revents & POLLIN) { poll_touchscreen(); } if(polls[1].revents & POLLIN) { uint64_t dummy; // don't care about expiration count if(read(timerfd, &dummy, sizeof dummy) < 0) error(1, errno, "read (timerfd)"); printf("animation frame\n"); } } //if(ioctl(fd, DRM_IOCTL_MODE_SETCRTC, &oldcrtc) < 0) error(1, errno, "restore crtc"); // doesn't work: EINVAL (maybe because framebuffer id is local to our connection) }