216 lines
6.3 KiB
C++
216 lines
6.3 KiB
C++
|
#include <libdrm/drm.h>
|
||
|
#include <libdrm/drm_fourcc.h>
|
||
|
#include <errno.h>
|
||
|
#include <error.h>
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/random.h>
|
||
|
#include <linux/input.h>
|
||
|
#include <math.h>
|
||
|
#include <sys/timerfd.h>
|
||
|
#include <sys/poll.h>
|
||
|
|
||
|
|
||
|
#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)
|
||
|
}
|