ref: 5c08d0f7ce762e0e9ec5e5be2e504ef2359173f3
dir: /gui-fbdev/fbdev.c/
#include "u.h"
#include "lib.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
#include <draw.h>
#include <memdraw.h>
#include <keyboard.h>
#include <cursor.h>
#include "screen.h"
#undef long
#undef ulong
#include <linux/fb.h>
#include <linux/input.h>
uchar* fbp;
Memimage* screenimage;
Memimage* backbuf;
Rectangle screenr;
char* snarfbuf;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int *eventfds = NULL;
int neventfds;
Point mousexy;
char shift_state;
int ttyfd;
char* tty;
char hidden;
int devicesfd;
ulong chan;
int depth;
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <limits.h>
#include <fcntl.h>
#include <termios.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <linux/keyboard.h>
#include <signal.h>
#include <termios.h>
#define ulong p9_ulong
int code2key[] = {
Kesc, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\x08',
'\x09', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
Kctl, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', Kshift,
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', Kshift, '*', Kalt,
' ', Kcaps, KF|1, KF|2, KF|3, KF|4, KF|5, KF|6, KF|7, KF|8, KF|9, KF|10,
Knum, Kscroll,
'7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.',
};
int code2key_shift[] = {
Kesc, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\x08',
'\x09', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
Kctl, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '\"', '~', Kshift,
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', Kshift, '*', Kalt,
' ', Kcaps, KF|1, KF|2, KF|3, KF|4, KF|5, KF|6, KF|7, KF|8, KF|9, KF|10,
Knum, Kscroll,
'7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.',
};
Memimage *gscreen;
char *snarfbuf = nil;
int onevent(struct input_event*);
void termctl(uint32_t o, int or);
void ctrlc(int sig);
void
_fbput(Memimage *m, Rectangle r) {
int y;
for (y = r.min.y; y < r.max.y; y++){
long loc = y * finfo.line_length + r.min.x * depth;
void *ptr = m->data->bdata + y * m->width * 4 + r.min.x * depth;
memcpy(fbp + loc, ptr, Dx(r) * depth);
}
}
Memimage*
fbattach(int fbdevidx)
{
Rectangle r;
char devname[64];
size_t size;
int fd;
/*
* Connect to /dev/fb0
*/
snprintf(devname, sizeof(devname) - 1, "/dev/fb%d", fbdevidx);
if ((fd = open(devname, O_RDWR)) < 0)
goto err;
if (ioctl(fd, FBIOGET_VSCREENINFO, &(vinfo)) < 0)
goto err;
switch (vinfo.bits_per_pixel) {
case 32:
chan = XRGB32;
depth = 4;
break;
case 16:
chan = RGB16;
depth = 2;
break;
default:
goto err;
}
if (ioctl(fd, FBIOGET_FSCREENINFO, &(finfo)) < 0)
goto err;
size = vinfo.yres_virtual * finfo.line_length;
if ((fbp = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0)) < 0)
goto err;
/*
* Figure out underlying screen format.
*/
r = Rect(0, 0, vinfo.xres_virtual, vinfo.yres_virtual);
screenr = r;
screenimage = allocmemimage(r, chan);
backbuf = allocmemimage(r, chan);
return backbuf;
err:
return nil;
}
int
eventattach()
{
char eventfile[PATH_MAX] = "";
char line[PATH_MAX];
FILE *devices;
char *ptr;
neventfds = 0;
devices = fopen("/proc/bus/input/devices", "r");
if (devices == NULL)
return -1;
while (fgets(line, sizeof(line)-1, devices) != NULL)
if (line[0] == 'H') {
ptr = strstr(line, "event");
if (ptr == NULL)
continue;
ptr[strcspn(ptr, " \r\n")] = '\0';
snprintf(eventfile, sizeof(eventfile)-1, "/dev/input/%s", ptr);
neventfds++;
eventfds = realloc(eventfds, neventfds * sizeof(int));
eventfds[neventfds-1] = open(eventfile, O_RDONLY);
if (eventfds[neventfds-1] < 0)
neventfds--;
}
fclose(devices);
if (neventfds == 0)
return -1;
return 1;
}
void
flushmemscreen(Rectangle r)
{
int x, y, i;
Point p;
long fbloc;
int x2, y2;
if (rectclip(&r, screenimage->r) == 0)
return;
if (Dx(r) == 0 || Dy(r) == 0)
return;
assert(!canqlock(&drawlock));
memimagedraw(screenimage, r, backbuf, r.min, nil, r.min, S);
if (hidden != 0)
return;
p = mousexy;
// draw cursor
for (x = 0; x < 16; x++) {
x2 = x + cursor.offset.x;
if ((p.x + x2) < 0)
continue;
if ((p.x + x2) >= screenimage->r.max.x)
break;
for (y = 0; y < 16; y++) {
y2 = y + cursor.offset.y;
if ((p.y + y2) < 0)
continue;
if ((p.y + y2) >= screenimage->r.max.y)
break;
i = y * 2 + x / 8;
fbloc = ((p.y+y2) * screenimage->r.max.x + (p.x+x2)) * depth;
if (cursor.clr[i] & (128 >> (x % 8))) {
switch (depth) {
case 2:
*((uint16_t*)(screenimage->data->bdata + fbloc)) = 0xFFFF;
break;
case 4:
*((uint32_t*)(screenimage->data->bdata + fbloc)) = 0xFFFFFFFF;
break;
}
}
if (cursor.set[i] & (128 >> (x % 8))) {
switch (depth) {
case 2:
*((uint16_t*)(screenimage->data->bdata + fbloc)) = 0x0000;
break;
case 4:
*((uint32_t*)(screenimage->data->bdata + fbloc)) = 0xFF000000;
break;
}
}
}
}
_fbput(screenimage, r);
}
static void
fbproc(void *v)
{
struct input_event data;
char buf[32];
struct pollfd *pfd;
int r;
int ioctlarg;
pfd = calloc(3, sizeof(struct pollfd));
pfd[0].fd = ttyfd; // for virtual console switches
pfd[0].events = POLLPRI;
pfd[1].fd = 0; // stdin goes to nowhere
pfd[1].events = POLLIN;
pfd[2].fd = open("/proc/bus/input/devices", O_RDONLY); // input hotplug
if (pfd[2].fd < 0)
panic("cannot open /proc/bus/input/devices: %r");
pfd[2].events = POLLIN;
TOP:
while(read(pfd[2].fd, buf, 31) > 0);
pfd = realloc(pfd, sizeof(struct pollfd) * (neventfds + 3));
for (r = 0; r < neventfds; r++) {
pfd[r+3].fd = eventfds[r];
pfd[r+3].events = POLLIN;
}
for(;;) {
shift_state = 6;
if (ioctl(0, TIOCLINUX, &shift_state) < 0)
panic("ioctl TIOCLINUX 6: %r");
r = poll(pfd, 3+neventfds, -1);
if (r < 0)
oserror();
if (pfd[0].revents & POLLPRI) {
if ((r = read(ttyfd, buf, 31)) <= 0)
panic("ttyfd read: %r");
buf[r] = '\0';
if (strcmp(buf, tty) == 0) {
hidden = 0;
printf("\e[?25l");
fflush(stdout);
qlock(&drawlock);
flushmemscreen(gscreen->clipr);
qunlock(&drawlock);
}
else
hidden = 1;
close(ttyfd);
ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
if (ttyfd < 0)
panic("cannot open tty active fd: %r");
pfd[0].fd = ttyfd;
read(ttyfd, buf, 0);
}
if (pfd[1].revents & POLLIN)
read(pfd[1].fd, buf, 31);
if (pfd[2].revents & POLLIN) {
for (r = 0; r < neventfds; r++)
close(eventfds[r]);
if(eventattach() < 0) {
panic("cannot open event files: %r");
}
goto TOP;
}
for (r = 0; r < neventfds; r++)
if (pfd[r+3].revents & POLLIN) {
if (read(pfd[r+3].fd, &data, sizeof(data)) != sizeof(data))
panic("eventfd read: %r");
if (onevent(&data) == 0) {
ioctlarg = 15;
if (ioctl(0, TIOCLINUX, &ioctlarg) != 0) {
ioctlarg = 4;
ioctl(0, TIOCLINUX, &ioctlarg);
qlock(&drawlock);
flushmemscreen(gscreen->clipr);
qunlock(&drawlock);
} else {
write(1, "\033[9;30]", 7);
}
}
}
}
printf("\e[?25h");
fflush(stdout);
termctl(ECHO, 1);
free(pfd);
}
void
screensize(Rectangle r, ulong chan)
{
gscreen = backbuf;
gscreen->clipr = ZR;
}
void
screeninit(void)
{
int r;
char buf[1];
// set up terminal
printf("\e[?25l");
fflush(stdout);
termctl(~(ICANON|ECHO), 0);
signal(SIGINT, ctrlc);
memimageinit();
// tty switching
ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
if (ttyfd >= 0) {
tty = malloc(32);
r = read(ttyfd, tty, 31);
if (r >= 0)
tty[r] = '\0';
else
tty[0] = '\0';
close(ttyfd);
ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
}
if (ttyfd < 0)
panic("cannot open tty active fd: %r");
read(ttyfd, buf, 0);
hidden = 0;
if(fbattach(0) == nil) {
panic("cannot open framebuffer: %r");
}
if(eventattach() < 0) {
panic("cannot open event files: %r");
}
screensize(screenr, chan);
if (gscreen == nil)
panic("screensize failed");
gscreen->clipr = screenr;
kproc("fbdev", fbproc, nil);
terminit();
qlock(&drawlock);
flushmemscreen(gscreen->clipr);
qunlock(&drawlock);
}
Memdata*
attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
{
*r = gscreen->clipr;
*chan = gscreen->chan;
*depth = gscreen->depth;
*width = gscreen->width;
*softscreen = 1;
gscreen->data->ref++;
return gscreen->data;
}
void
getcolor(ulong i, ulong *r, ulong *g, ulong *b)
{
ulong v;
v = cmap2rgb(i);
*r = (v>>16)&0xFF;
*g = (v>>8)&0xFF;
*b = v&0xFF;
}
void
setcolor(ulong i, ulong r, ulong g, ulong b)
{
/* no-op */
return;
}
char*
clipread(void)
{
if(snarfbuf)
return strdup(snarfbuf);
return nil;
}
int
clipwrite(char *buf)
{
if(snarfbuf)
free(snarfbuf);
snarfbuf = strdup(buf);
return 0;
}
void
guimain(void)
{
cpubody();
}
void
termctl(uint32_t o, int or)
{
struct termios t;
tcgetattr(0, &t);
if (or)
t.c_lflag |= o;
else
t.c_lflag &= o;
tcsetattr(0, TCSANOW, &t);
}
void
ctrlc(int sig) {
}
int
onevent(struct input_event *data)
{
Rectangle old, new;
ulong msec;
static int buttons;
static Point coord;
static char touched;
static Point startmousept;
static Point startpt;
int key;
static ulong lastmsec = 0;
if (hidden != 0)
return -1;
msec = ticks();
old.min = mousexy;
old.max = addpt(old.min, Pt(16, 16));
buttons &= ~0x18;
switch(data->type) {
case 3:
switch(data->code) {
case 0:
coord.x = data->value;
break;
case 1:
coord.y = data->value;
break;
case 0x18:
case 0x1c:
if (data->value == 0)
touched = 0;
else if (data->value > 24) {
touched = 1;
startmousept = coord;
startpt = mousexy;
}
break;
default:
return -1;
}
if (touched)
mousexy = addpt(startpt, divpt(subpt(coord, startmousept), 4));
break;
case 2:
switch(data->code) {
case 0:
mousexy.x += data->value;
break;
case 1:
mousexy.y += data->value;
break;
case 8:
buttons |= data->value == 1? 8: 16;
break;
default:
return -1;
}
break;
case 1:
switch(data->code) {
case 0x110:
if (data->value == 1)
buttons |= 1;
else
buttons &= ~1;
break;
case 0x111:
if (data->value == 1)
buttons |= shift_state & (1 << KG_SHIFT)? 2: 4;
else
buttons &= ~(shift_state & (1 << KG_SHIFT)? 2: 4);
break;
case 0x112:
if (data->value == 1)
buttons |= 2;
else
buttons &= ~2;
break;
default:
if (hidden)
return 0;
if (data->code > 0 && data->code <= nelem(code2key)) {
if (shift_state & (1 << KG_SHIFT))
key = code2key_shift[data->code-1];
else
key = code2key[data->code-1];
if (key == Kshift)
return -1;
kbdkey(key, data->value);
return 0;
}
switch(data->code) {
case 87:
kbdkey(KF|11, data->value);
break;
case 88:
kbdkey(KF|12, data->value);
break;
case 96:
kbdkey('\n', data->value);
break;
case 97:
kbdkey(Kctl, data->value);
break;
case 98:
kbdkey('/', data->value);
break;
case 100:
kbdkey(Kalt, data->value);
break;
case 102:
kbdkey(Khome, data->value);
break;
case 103:
kbdkey(Kup, data->value);
break;
case 104:
kbdkey(Kpgup, data->value);
break;
case 105:
kbdkey(Kleft, data->value);
break;
case 106:
kbdkey(Kright, data->value);
break;
case 107:
kbdkey(Kend, data->value);
break;
case 108:
kbdkey(Kdown, data->value);
break;
case 109:
kbdkey(Kpgdown, data->value);
break;
case 110:
kbdkey(Kins, data->value);
break;
case 111:
kbdkey(Kdel, data->value);
break;
}
return 0;
}
break;
default:
return -1;
}
if (mousexy.x < screenimage->r.min.x)
mousexy.x = screenimage->r.min.x;
if (mousexy.y < screenimage->r.min.y)
mousexy.y = screenimage->r.min.y;
if (mousexy.x > screenimage->r.max.x)
mousexy.x = screenimage->r.max.x;
if (mousexy.y > screenimage->r.max.y)
mousexy.y = screenimage->r.max.y;
new.min = mousexy;
new.max = addpt(new.min, Pt(16, 16)); // size of cursor bitmap
combinerect(&new, old);
new.min = subpt(new.min, Pt(16, 16)); // to encompass any cursor->offset
qlock(&drawlock);
flushmemscreen(new);
qunlock(&drawlock);
if ((msec - lastmsec) < 10)
if (data->type != 1)
return 0;
lastmsec = msec;
absmousetrack(mousexy.x, mousexy.y, buttons, msec);
return 0;
}
void
mouseset(Point p)
{
qlock(&drawlock);
mousexy = p;
flushmemscreen(screenr);
qunlock(&drawlock);
}
void
setcursor(void)
{
qlock(&drawlock);
flushmemscreen(screenr);
qunlock(&drawlock);
}
void
titlewrite(char* buf)
{
}