pt2game/pinetab2_framework.cpp

216 lines
6.3 KiB
C++
Raw Normal View History

#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)
}