initial commit from fbtest coded on the train home from FOSDEM, drawing a fixed picture instead of a black screen, and with an unused animation timer
commit
1ca8e611af
|
@ -0,0 +1,35 @@
|
|||
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
OBJS = pinetab2_framework.o
|
||||
|
||||
ifeq ($(BUILD_MODE),debug)
|
||||
CFLAGS += -g
|
||||
else ifeq ($(BUILD_MODE),run)
|
||||
CFLAGS += -O2
|
||||
else ifeq ($(BUILD_MODE),linuxtools)
|
||||
CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
|
||||
LDFLAGS += -pg -fprofile-arcs -ftest-coverage
|
||||
EXTRA_CLEAN += pinetab2_framework.gcda pinetab2_framework.gcno $(PROJECT_ROOT)gmon.out
|
||||
EXTRA_CMDS = rm -rf pinetab2_framework.gcda
|
||||
else
|
||||
$(error Build mode $(BUILD_MODE) not supported by this Makefile)
|
||||
endif
|
||||
|
||||
CXX := aarch64-linux-gnu-$(CXX)
|
||||
CC := aarch64-linux-gnu-$(CC)
|
||||
CFLAGS += -I/usr/include
|
||||
|
||||
all: pinetab2_framework
|
||||
|
||||
pinetab2_framework: $(OBJS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $^
|
||||
$(EXTRA_CMDS)
|
||||
|
||||
%.o: $(PROJECT_ROOT)%.cpp
|
||||
$(CXX) -c $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
|
||||
|
||||
%.o: $(PROJECT_ROOT)%.c
|
||||
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -fr pinetab2_framework $(OBJS) $(EXTRA_CLEAN)
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,215 @@
|
|||
#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)
|
||||
}
|
Loading…
Reference in New Issue