757 lines · 13.7 KB · C
RawDownload
#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#undef atexit #include <fcntl.h>#include <limits.h>#include <linux/fb.h>#include <linux/input.h>#include <linux/kd.h>#include <linux/kdev_t.h>#include <linux/keyboard.h>#include <linux/vt.h>#include <poll.h>#include <signal.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <sys/stat.h>#include <termios.h> #include <stdatomic.h> #define ulong p9_ulong Memimage *gscreen; static Rectangle	update_rect;static Rectangle	screenr;static struct termios	tcmode;static int	startmode;static int	altdown;static uchar	*fbp;static Memimage	*screenimage;static Memimage	*backbuf;static int	linelength;static Point	mousexy;static ulong	chan;static int	depth;static _Atomic int	dirty;static _Atomic int	switchaway;static Rendez	rendezflush;static QLock	flushlock;static char	*snarfbuf; static Memimage* fbattach(int fbdevidx);static int onevent(struct input_event*);static void ttyswitch(int sig);static void consinit(void);static void consfinal(void);static void consfinalsig(int sig);static int needflush(void *);static void ignore0(void *);static void unblank(void *); static intneedflush(void *v){	USED(v);	return atomic_load(&dirty);} static void_fbput(Memimage *m, Rectangle r) {	int dx, xoffset, ylen;	void *ptr, *p, *max_row; 	ylen = 4 * m->width;	dx = depth * Dx(r);	xoffset = depth * r.min.x;	p = fbp + xoffset + r.min.y * linelength;	ptr = m->data->bdata + xoffset + r.min.y * ylen;	max_row = m->data->bdata + r.max.y * ylen;	while(ptr < max_row){		memcpy(p, ptr, dx);		p += linelength;		ptr += ylen;	}} static Memimage*fbattach(int fbdevidx){	Rectangle r;	char devname[64];	size_t size;	int fd;	struct fb_fix_screeninfo finfo;	struct fb_var_screeninfo vinfo; 	/*	 * Connect to /dev/fb0	 */	snprintf(devname, sizeof(devname) - 1, "/dev/fb%d", fbdevidx);	if ((fd = open(devname, O_RDWR | O_CLOEXEC)) < 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;	linelength = finfo.line_length; 	size = vinfo.yres_virtual * linelength;	if ((fbp = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0)) == MAP_FAILED)		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;} static voideventattach(struct pollfd **pfd, int *nstart){	char eventfile[PATH_MAX] = "/dev/input/event";	char buf[1024], *pbuf, *end;	struct pollfd *p;	int n, m; 	p = *pfd;	n = *nstart;	pbuf = buf; 	while(read(p->fd, buf, sizeof(buf)) > 0 || errno == EINTR)		;	// read left over	lseek(p->fd, 0, SEEK_SET);	while((m = read(p->fd, pbuf, buf+sizeof(buf)-1-pbuf)) > 0 || errno == EINTR){		if(m<=0)			continue;		pbuf[m] = '\0';		pbuf = buf;		do{			end = strchr(pbuf, '\n');			if(!end){				if(buf==pbuf)					panic("event line too long");				for(m=0; pbuf[m]; ++m)					buf[m] = pbuf[m];				pbuf = buf + m;				break;			}			if(*pbuf == 'H'){				for(++pbuf; strncmp(pbuf, "event", 5) && pbuf < end; ++pbuf);				if(pbuf == end){					++pbuf;					continue;				}				pbuf += 5;	// event				m = strcspn(pbuf, " \n");				pbuf[m] = '\0';				if((uint)m < sizeof(eventfile)-17)	// skip "/dev/input/event"					strcpy(eventfile+16, pbuf);				++n;				p = realloc(p, n * sizeof(struct pollfd));				if(!p)					panic("realloc pollfd: %r");				p[n-1].fd = open(eventfile, O_RDONLY|O_CLOEXEC);				p[n-1].events = POLLIN;				if (p[n-1].fd < 0)					n--;			}			pbuf = end + 1;		}while(*pbuf);	}	if(n <= *nstart)		panic("no input events: %r"); 	*pfd = p;	*nstart = n;} static voideventdetach(struct pollfd pfd[], int npfd){	int n; 	for(n = 0; n < npfd; ++n)		close(pfd[n].fd);} voidflushmemscreen(Rectangle r){	if (rectclip(&r, screenimage->r) == 0)		return;	if (Dx(r) == 0 || Dy(r) == 0)		return; 	assert(!canqlock(&drawlock)); 	if (Dx(update_rect) == 0 || Dy(update_rect) == 0)		update_rect = r;	else		combinerect(&update_rect, r);	atomic_store(&dirty, 1);	wakeup(&rendezflush);} static voidfbflush(void *v){	USED(v);	Rectangle r;	int x, y, i;	Point p;	long fbloc;	int x2, y2;	ulong del;	ulong ms; 	del = 16; 	for(;;){		ksleep(&rendezflush, needflush, 0);		qlock(&flushlock); 		ms = ticks(); 		qlock(&drawlock);		r = update_rect;		update_rect = Rect(0,0,0,0);		memimagedraw(screenimage, r, backbuf, r.min, nil, r.min, S);		atomic_store(&dirty, 0);		qunlock(&drawlock); 		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);		qunlock(&flushlock); 		ms = ms + del - ticks();		if(ms > 0 && ms < del)			osmsleep(ms);	}} static voidignore0(void *){	int n;	char buf[32]; 	while((n = read(0, buf, sizeof buf)) > 0 || errno == EINTR)		if (n <= 0 && errno != EINTR)			panic("read 0: %r");} static voidunblank(void *){	for(;;){		struct vt_event e = {.event = VT_EVENT_UNBLANK};		if(!ioctl(0, VT_WAITEVENT, &e)){			qlock(&drawlock);			flushmemscreen(gscreen->clipr);			qunlock(&drawlock);		}	}} static voidconsinit(void){	struct vt_mode vm;	struct termios t; 	if(tcgetattr(0, &tcmode))		panic("tcgetattr: %r");	t = tcmode;	t.c_lflag &= ~(ICANON|ISIG|ECHO);	if(tcsetattr(0, TCSAFLUSH, &t))		panic("tcsetattr: %r");	if(ioctl(0, KDGETMODE, &startmode) < 0)		panic("ioctl KDGETMODE: %r");	write(0, "\e[9;30]", 7);	// blank time 30 min	write(0, "\e[?25l", 6);		// hide cursor	signal(SIGUSR1, ttyswitch);	signal(SIGHUP, consfinalsig);	signal(SIGINT, consfinalsig);	signal(SIGQUIT, consfinalsig);	signal(SIGABRT, consfinalsig);	signal(SIGTERM, consfinalsig);	atexit(consfinal); 	vm.mode = VT_PROCESS;	vm.waitv = 0;	vm.relsig = SIGUSR1;	vm.acqsig = 0;	vm.frsig = 0;	if(ioctl(0, VT_SETMODE, &vm) < 0)		panic("ioctl VT_SETMODE: %r");} static voidconsfinal(void){	write(0, "\e[?25h", 6);	if (startmode == KD_TEXT)		ioctl(0, KDSETMODE, KD_GRAPHICS);	else		ioctl(0, KDSETMODE, KD_TEXT);	ioctl(0, KDSETMODE, startmode);	tcsetattr(0, TCSAFLUSH, &tcmode);} static voidconsfinalsig(int sig){	exit(128+sig);} static voidfbproc(void *v){	USED(v);	struct input_event data;	struct stat ts;	struct pollfd *pfd;	int npfd;	int vt;	int r; 	if(fstat(0, &ts) < 0)		panic("fstat current tty: %r");	vt = MINOR(ts.st_rdev); 	kproc("unblank", unblank, nil);	kproc("ignore0", ignore0, nil); 	consinit(); 	npfd = 1;	pfd = malloc(sizeof(struct pollfd));	if(!pfd)		panic("malloc pollfd: %r");	pfd->fd = open("/proc/bus/input/devices", O_RDONLY|O_CLOEXEC); // input hotplug	if (pfd->fd < 0)		panic("cannot open /proc/bus/input/devices: %r");	pfd->events = POLLIN; 	qlock(&flushlock);TOP:	while(ioctl(0, VT_WAITACTIVE, vt) < 0)		if (errno != EINTR)			panic("ioctl VT_WAITACTIVE: %r");	qunlock(&flushlock); 	qlock(&drawlock);	flushmemscreen(gscreen->clipr);	qunlock(&drawlock); 	eventattach(&pfd, &npfd); 	for (;;) {		if(poll(pfd, npfd, -1) < 0)			oserror();		if(atomic_load(&switchaway)) {			if(altdown){	// Kalt used to switch vt				kbdsc(0x1d);	// Send Kctl to break the compose sequence				kbdsc(0x1d|0x80);				altdown = 0;			}			eventdetach(pfd + 1, npfd - 1);			npfd = 1;			qlock(&flushlock);			if(ioctl(0, VT_RELDISP, 1) < 0)				panic("ioctl VT_RELDISP: %r");			atomic_store(&switchaway, 0);			goto TOP;		}		if (pfd->revents & POLLIN) {			eventdetach(pfd + 1, npfd - 1);			npfd = 1;			qlock(&flushlock);			goto TOP;		}		for(r = 1; r < npfd; r++)			if (pfd[r].revents & POLLIN) {				if (read(pfd[r].fd, &data, sizeof(data)) != sizeof(data))					panic("eventfd read: %r");				onevent(&data);			}	} 	eventdetach(pfd, npfd);	free(pfd);} voidscreensize(Rectangle r, ulong chan){	USED(r); USED(chan);	gscreen = backbuf;	gscreen->clipr = ZR;} voidscreeninit(void){	memimageinit(); 	if(fbattach(0) == nil) {		panic("cannot open framebuffer: %r");	} 	screensize(screenr, chan);	if (gscreen == nil)		panic("screensize failed"); 	gscreen->clipr = screenr;	kproc("fbflush", fbflush, nil);	kproc("fbdev", fbproc, nil); 	qlock(&drawlock);	terminit();	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++;

Lines 1–500 of 757.