Skip to content

Instantly share code, notes, and snippets.

@linnaea
Created April 24, 2014 03:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save linnaea/11240923 to your computer and use it in GitHub Desktop.
Save linnaea/11240923 to your computer and use it in GitHub Desktop.
VNC Server exporting framebuffer device content, I wrote it for use with fbterm. It doesn't handle input. Updates at 5fps, very CPU hungry. Code released into the public domain.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <errno.h>
#include <string.h>
#include <rfb/rfb.h>
#include <semaphore.h>
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
//pthread_mutex_t *fb_lock = 0;
sem_t *trigger_update = 0;
void(* fb_convert)(char *buffer, int pixels) = NULL;
void fatal(char *str, int line, uintmax_t p1, uintmax_t p2, uintmax_t p3, uintmax_t p4) {
fprintf(stderr, "fatal(0x%016"PRIXMAX", 0x%016"PRIXMAX", 0x%016"PRIXMAX", 0x%016"PRIXMAX")@%d: %s, errno=%d: %s\n", p1, p2, p3, p4, line, str, errno, strerror(errno));
exit(EXIT_FAILURE);
}
void convert_bgrx_rgbx(char *buffer, int pixels) {
int i = 0;
char t;
for(i = 0; i < pixels; i++, buffer+=4) {
t = *(buffer+2);
*(buffer+2) = *buffer;
*buffer = t;
}
}
void keyproc(rfbBool down, rfbKeySym keySym, struct _rfbClientRec *cl) {
printf("%"PRIX8" %08"PRIX32"\n", down, keySym);
//sem_post(trigger_update);
}
void fb_proc_update(rfbScreenInfoPtr server, char *fb_prev, int resx, int resy/*, struct timespec* time_round*/) {
int blkx, blky, bx_s, by_s, bx_e, by_e, bresx, bresy, bx, by, gbx, gby, bdiff;
uint32_t *pfb, *pfb_prev;
pfb = (uint32_t*)server->frameBuffer;
pfb_prev = (uint32_t*)fb_prev;
bresx = resx / 8;
bresy = resy / 8;
for(blky = 0; blky < 8; blky++) {
gby = blky * bresy;
for(blkx = 0; blkx < 8; blkx++) {
gbx = blkx * bresx;
bx_s = bresx; bx_e = 0;
by_s = bresy; by_e = 0;
bdiff = 0;
for(by = 0; by < bresy; by++) {
if(!(memcmp(pfb + (gby+by)*resx + gbx, pfb_prev + (gby+by)*resx + gbx, bresx*4))) continue;
bdiff = 1;
for(bx = 0; bx < bx_s; bx++) {
if((*(pfb + (gby+by)*resx + gbx+bx)) != (*(pfb_prev + (gby+by)*resx + gbx+bx))) {
bx_s = bx;
if(bx_e < bx_s) bx_e = bx_s;
break;
}
}
for(bx = bx_e; bx < bresx; bx++) {
if((*(pfb + (gby+by)*resx + gbx+bx)) != (*(pfb_prev + (gby+by)*resx + gbx+bx))) {
bx_e = bx;
}
}
if(by < by_s) by_s = by;
if(by > by_e) by_e = by;
}
if(bdiff) {
//printf("[%3ld]blksearch[%d, %d]: [%4d, %-4d] -> [%4d, %-4d]\n", time_round->tv_nsec/1000000, blkx, blky, gbx+bx_s,gby+by_s,gbx+bx_e+1,gby+by_e+1);
rfbMarkRectAsModified(server,gbx+bx_s,gby+by_s,gbx+bx_e+1,gby+by_e+1);
}
}
}
}
int main(int argc, char **argv) {
char *fbdev = "/dev/fb0";
struct fb_fix_screeninfo fb_fixinfo;
struct fb_var_screeninfo fb_varinfo;
struct timespec sem_timeout;
//struct timespec ts_action;
rfbScreenInfoPtr server;
//pthread_mutexattr_t lock_attr;
int ret, fbsize;
int fd = -1;
char *fb_prev = NULL;
char cursor[] = " ";
trigger_update = (sem_t*) malloc(sizeof(sem_t));
if(!trigger_update)
fatal("malloc", __LINE__, sizeof(sem_t), 0, 0, 0);
/*fb_lock = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
if(!fb_lock)
fatal("malloc", __LINE__, sizeof(pthread_mutex_t), 0, 0, 0);*/
if((ret = sem_init(trigger_update, 0, 0)) != 0)
fatal("sem_init", __LINE__, ret, (uintmax_t)trigger_update, 0, 0);
/*if((ret = pthread_mutexattr_init(&lock_attr)) != 0)
fatal("pthread_mutexattr_init", __LINE__, ret, (uintmax_t)&lock_attr, 0, 0);
if((ret = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK)) != 0)
fatal("pthread_mutexattr_settype", __LINE__, ret, (uintmax_t)&lock_attr, PTHREAD_MUTEX_ERRORCHECK, 0);
if((ret = pthread_mutex_init(fb_lock, &lock_attr)) != 0)
fatal("pthread_mutex_init", __LINE__, ret, (uintmax_t)fb_lock, (uintmax_t)&lock_attr, 0);*/
fd = open(fbdev, O_RDONLY);
if(fd == -1) fatal("Open device", __LINE__, fd, 0, 0, 0);
if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_varinfo) != 0)
fatal("FBIOGET_VSCREENINFO", __LINE__, fd, 0, 0, 0);
if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_fixinfo) != 0)
fatal("FBIOGET_FSCREENINFO", __LINE__, fd, 0, 0, 0);
close(fd); fd = -1;
printf("DEV=%-16.16s ID=%-16.16s res=%5dx%-5d\n", fbdev, fb_fixinfo.id, fb_varinfo.xres, fb_varinfo.yres);
printf("bpp=%2d grayscale=%08X nonstd=%d\n", fb_varinfo.bits_per_pixel, fb_varinfo.grayscale, fb_varinfo.nonstd);
printf("r.offset=%2d r.length=%2d r.msb_right=%d\n", fb_varinfo.red.offset, fb_varinfo.red.length, fb_varinfo.red.msb_right);
printf("g.offset=%2d g.length=%2d g.msb_right=%d\n", fb_varinfo.green.offset, fb_varinfo.green.length, fb_varinfo.green.msb_right);
printf("b.offset=%2d b.length=%2d b.msb_right=%d\n", fb_varinfo.blue.offset, fb_varinfo.blue.length, fb_varinfo.blue.msb_right);
printf("a.offset=%2d a.length=%2d a.msb_right=%d\n", fb_varinfo.transp.offset, fb_varinfo.transp.length, fb_varinfo.transp.msb_right);
if(
fb_varinfo.nonstd == 0 && fb_varinfo.grayscale == 0 && fb_varinfo.bits_per_pixel == 32
&& fb_varinfo.transp.msb_right == 0 && fb_varinfo.blue.msb_right == 0 && fb_varinfo.green.msb_right == 0 && fb_varinfo.red.msb_right == 0
&& fb_varinfo.red.offset == 16 && fb_varinfo.green.offset == 8 && fb_varinfo.blue.offset == 0
&& fb_varinfo.red.length == 8 && fb_varinfo.green.length == 8 && fb_varinfo.blue.length == 8
) {
fb_convert = convert_bgrx_rgbx;
}
if(fb_convert == NULL)
fatal("fb_convert", __LINE__, 0, 0, 0, 0);
server = rfbGetScreen(&argc, argv, fb_varinfo.xres, fb_varinfo.yres, 8, 3, 4);
if(server == 0)
fatal("rfbGetScreen", __LINE__, fb_varinfo.xres, fb_varinfo.yres, 0, 0);
fbsize = fb_varinfo.xres*fb_varinfo.yres*4;
server->desktopName = fbdev;
server->frameBuffer = (char*) malloc(fb_varinfo.xres*fb_varinfo.yres*4);
if(!server->frameBuffer)
fatal("malloc", __LINE__, fbsize, 0, 0, 0);
fb_prev = (char*) malloc(fb_varinfo.xres*fb_varinfo.yres*4);
if(!fb_prev)
fatal("malloc", __LINE__, fbsize, 0, 0, 0);
server->kbdAddEvent = keyproc;
server->cursor = rfbMakeXCursor(1, 1, cursor, cursor);
rfbInitServer(server);
rfbRunEventLoop(server,-1,TRUE);
clock_gettime(CLOCK_REALTIME, &sem_timeout);
while(1) {
sem_timeout.tv_nsec += 200000000; //minimum 5fps
if(sem_timeout.tv_nsec > 999999999) {
sem_timeout.tv_nsec -= 1000000000;
sem_timeout.tv_sec++;
}
ret = sem_timedwait(trigger_update, &sem_timeout);
if((ret != 0) && (errno != ETIMEDOUT))
fatal("sem_timedwait", __LINE__, (uintmax_t)trigger_update, sem_timeout.tv_sec, sem_timeout.tv_nsec, 0);
memcpy(fb_prev, server->frameBuffer, fbsize);
fd = open(fbdev, O_RDONLY);
if(fd == -1) fatal("open", __LINE__, (uintmax_t)fbdev, O_RDONLY, 0, 0);
if((ret = read(fd, server->frameBuffer, fbsize)) != fbsize)
fatal("read", __LINE__, fd, (uintmax_t)server->frameBuffer, fbsize, ret);
close(fd); fd = -1;
//printf("SEM TS=%11"PRId64".%09"PRId32"\n", (int64_t)sem_timeout.tv_sec, (int32_t)sem_timeout.tv_nsec);
//clock_gettime(CLOCK_REALTIME, &ts_action);
//printf("READ TS=%11"PRId64".%09"PRId32"\n", (int64_t)ts_action.tv_sec, (int32_t)ts_action.tv_nsec);
fb_convert(server->frameBuffer, fbsize/4);
//clock_gettime(CLOCK_REALTIME, &ts_action);
//printf("CONV TS=%11"PRId64".%09"PRId32"\n", (int64_t)ts_action.tv_sec, (int32_t)ts_action.tv_nsec);
fb_proc_update(server, fb_prev, fb_varinfo.xres, fb_varinfo.yres/*, &ts_action*/);
//clock_gettime(CLOCK_REALTIME, &ts_action);
//printf("PROC TS=%11"PRId64".%09"PRId32"\n\n", (int64_t)ts_action.tv_sec, (int32_t)ts_action.tv_nsec);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment