<yoshiki@xemacs.org>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <assert.h>
#include <vncviewer.h>
void PrintInHex(char *buf, int len);
Bool errorMessageOnReadFailure = True;
#define BUF_SIZE 8192
static char buf[BUF_SIZE];
static char *bufoutptr = buf;
static int buffered = 0;
Bool vncLogTimeStamp = False;
unsigned red_shift, green_shift, blue_shift;
static Bool rfbsockReady = False;
static void
rfbsockReadyCallback(XtPointer clientData, int *fd, XtInputId *id)
{
rfbsockReady = True;
XtRemoveInput(*id);
}
static void
ProcessXtEvents()
{
rfbsockReady = False;
XtAppAddInput(appContext, rfbsock, (XtPointer)XtInputReadMask,
rfbsockReadyCallback, NULL);
while (!rfbsockReady) {
XtAppProcessEvent(appContext, XtIMAll);
}
}
void my_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if (fwrite(ptr, size, nmemb, stream)
!=nmemb){
fprintf(stderr,"vncrec: Error occured writing %u bytes"
"into a file stream: ", (unsigned)(size*nmemb));
perror(NULL);
exit(1);
}
}
static void
writeLogHeader (void)
{
struct timeval tv;
static unsigned int frame = 0;
if (vncLogTimeStamp)
{
long tell = ftell(vncLog);
unsigned int wframe = Swap32IfLE(frame);
my_fwrite (&wframe, sizeof(wframe), 1, vncLog);
gettimeofday (&tv, NULL);
if (appData.debugFrames) {
fprintf(stderr, "write frame %u at time %.3f @ offset %ld\n",
frame, tv.tv_sec + tv.tv_usec / 1e6, tell);
}
tv.tv_sec = Swap32IfLE (tv.tv_sec);
tv.tv_usec = Swap32IfLE (tv.tv_usec);
my_fwrite (&tv, sizeof (struct timeval), 1, vncLog);
frame++;
}
}
static struct timeval
timeval_subtract (struct timeval x,
struct timeval y)
{
struct timeval result;
result.tv_sec = x.tv_sec - y.tv_sec;
result.tv_usec = x.tv_usec - y.tv_usec;
if (result.tv_usec < 0)
{
result.tv_sec--;
result.tv_usec += 1000000;
}
assert (result.tv_usec >= 0);
return result;
}
void scanline(unsigned char *r, unsigned char *g, unsigned char *b, unsigned
char *src, unsigned cycles)
{
unsigned long v;
for (;cycles;cycles--){
v=*(unsigned long *)(void *)src;
*r++=v>>red_shift;
*g++=v>>green_shift;
*b++=v>>blue_shift;
src+=4;
}
}
#define CLIP(x) if (x<0) x=0; else if (x>255) x=255;
void rgb2yuv(unsigned char *d, unsigned plane)
{
unsigned char *r, *g, *b;
int y,u,v;
r=d;
g=r+plane;
b=g+plane;
for (;plane;plane--){
y=((77**r+150**g+29**b+0x80)>>8);
u=0x80+((-43**r-85**g+128**b+0x7f)>>8);
v=0x80+((128**r-107**g-21**b+0x7f)>>8);
CLIP(y);
CLIP(u);
CLIP(v);
*r++=y;
*g++=u;
*b++=v;
}
}
void dump_image(XImage *i, unsigned times)
{
unsigned char frame_header[]="FRAME\n";
unsigned w=si.framebufferWidth;
unsigned h=si.framebufferHeight;
unsigned char *d;
unsigned x, y;
unsigned char *rptr, *gptr, *bptr;
unsigned char *wptr;
if (i->bitmap_unit!=32) {
fprintf(stderr,"Sorry, only >= 8 bits per channel colour "
"is supported. bitmap_unit=%u\n",
i->bitmap_unit);
exit(1);
}
d = malloc(w*h*3);
if (!appData.writeYUV)
{
rptr = i->data;
wptr = d;
for (y=0; y<h; y++){
for (x=0; x<w; ++x){
int v;
v = *(unsigned long *)(void *)rptr;
rptr += 4;
*wptr++ = v >> red_shift;
*wptr++ = v >> green_shift;
*wptr++ = v >> blue_shift;
}
}
for (;times;times--){
my_fwrite(d, w*h*3, 1, stdout);
}
}
else
{
for (y=0; y<h; y++){
rptr=d+y*w;
gptr=rptr+w*h;
bptr=gptr+w*h;
scanline(rptr, gptr, bptr, i->data+i->bytes_per_line*y, w);
}
rgb2yuv(d, w*h);
for (y=0;y<h-1;y+=2){
bptr=d+(h+y)*w;
rptr=bptr+h*w;
for (x=0;x<w-1;x+=2, bptr+=2, rptr+=2){
bptr[0]=(bptr[0]+bptr[1]+bptr[w]+bptr[w+1])>>2;
rptr[0]=(rptr[0]+rptr[1]+rptr[w]+rptr[w+1])>>2;
}
}
bptr=d+w*h;
rptr=bptr+w*h;
wptr=bptr;
for (y=0;y<h-1;y+=2){
gptr=bptr+y*w;
for (x=0;x<w-1;x+=2, gptr+=2)
*wptr++=*gptr;
}
for (y=0;y<h-1;y+=2){
gptr=rptr+y*w;
for (x=0;x<w-1;x+=2, gptr+=2)
*wptr++=*gptr;
}
for (;times;times--){
my_fwrite(frame_header, sizeof(frame_header)-1, 1, stdout);
my_fwrite(d, wptr-d, 1, stdout);
}
}
free(d);
}
int test_endian(void)
{
long a=1;
*((unsigned char *)(void *)&a)=0;
return a?MSBFirst:LSBFirst;
}
unsigned mask2shift(unsigned long in)
{
unsigned shift=0;
if (!in) return 0;
while(!(in&1)){
in>>=1;
shift++;
}
return shift;
}
void examine_layout(void)
{
XWindowAttributes attr;
Window rootwin;
int dummy_i;
unsigned dummy_u;
XGetGeometry(dpy, desktopWin, &rootwin,
&dummy_i, &dummy_i, &dummy_u, &dummy_u, &dummy_u, &dummy_u);
XGetWindowAttributes(dpy, rootwin, &attr);
fprintf(stderr,"red_mask=%lx, green_mask=%lx, blue_mask=%lx, "
"CPU endian=%u, dpy endian=%u\n",
attr.visual->red_mask,
attr.visual->green_mask,
attr.visual->blue_mask,
test_endian(),
ImageByteOrder(dpy));
red_shift=mask2shift(attr.visual->red_mask);
green_shift=mask2shift(attr.visual->green_mask);
blue_shift=mask2shift(attr.visual->blue_mask);
if (test_endian()!=ImageByteOrder(dpy)){
red_shift=24-red_shift;
green_shift=24-green_shift;
blue_shift=24-blue_shift;
}
fprintf(stderr,"Image dump color channel shifts: R=%u, G=%u, B=%u\n",
red_shift, green_shift, blue_shift);
}
void print_movie_frames_up_to_time(struct timeval tv)
{
static double framerate;
static double start_time = 0;
double now = tv.tv_sec + tv.tv_usec / 1e6;
static unsigned last_frame = 0, this_frame = 0;
static unsigned times;
XImage *image;
if (start_time == 0) {
framerate = getenv("VNCREC_MOVIE_FRAMERATE") ? atoi(getenv("VNCREC_MOVIE_FRAMERATE")) : 25;
if (appData.ffInfo) {
if (appData.writeYUV) {
fprintf(stderr,
"Error: -ffinfo and -writeYUV are mutually exclusive, "
"use -f yuv4mpegpipe.\n");
}
else {
printf("-pixel_format rgb24 -video_size %ux%u -framerate %u\n",
si.framebufferWidth,
si.framebufferHeight,
(unsigned)framerate);
}
exit(0);
}
if (appData.writeYUV) {
printf("YUV4MPEG2 W%u H%u F%u:1 Ip A0:0\n",
si.framebufferWidth,
si.framebufferHeight,
(unsigned)framerate);
}
else {
fprintf(stderr,"RGB8 W%u H%u F%u:1 Ip A0:0\n",
si.framebufferWidth,
si.framebufferHeight,
(unsigned)framerate);
}
examine_layout();
start_time = now;
}
this_frame = (unsigned)((now - start_time) * framerate);
assert(this_frame >= last_frame);
if (this_frame == last_frame) return;
times = this_frame - last_frame;
last_frame = this_frame;
image = XGetImage(dpy, desktopWin,0, 0, si.framebufferWidth,
si.framebufferHeight, 0xffffffff,
ZPixmap);
assert(image);
if (0) {
if (times == 1) {
fprintf(stderr,"Dumping frame for time %.2f sec - %.6f.\n",
times / framerate, now);
}
else {
fprintf(stderr,"Dumping %u frames for time %.2f sec - %.6f.\n",
times, times / framerate, now);
}
}
dump_image(image, times);
XDestroyImage(image);
}
Bool
ReadFromRFBServer(char *out, unsigned int n)
{
if (appData.play || appData.movie)
{
int i;
char buf[1];
if (vncLogTimeStamp)
{
long tell = ftell(vncLog);
static unsigned long rframe_curr = 0;
unsigned int rframe;
static struct timeval prev;
struct timeval tv;
i = fread (&rframe, sizeof (rframe), 1, vncLog);
if (i < 1)
return False;
rframe = Swap32IfLE(rframe);
if (rframe != rframe_curr) {
fprintf(stderr, "Frame number does not match! File desynced or corrupt!.\n");
abort();
}
++rframe_curr;
i = fread (&tv, sizeof (struct timeval), 1, vncLog);
if (i < 1)
return False;
tv.tv_sec = Swap32IfLE (tv.tv_sec);
tv.tv_usec = Swap32IfLE (tv.tv_usec);
if (!appData.movie &&
prev.tv_sec)
{
struct timeval diff = timeval_subtract (tv, prev);
sleep (diff.tv_sec);
usleep (diff.tv_usec);
}
prev = tv;
if (appData.debugFrames) {
fprintf(stderr, "read frame %u at time %.3f @ offset %ld\n",
rframe, tv.tv_sec + tv.tv_usec / 1e6, tell);
}
if(appData.movie)
print_movie_frames_up_to_time(tv);
}
i = fread (out, 1, n, vncLog);
if (i < 0)
return False;
else
return True;
}
if (n <= buffered) {
memcpy(out, bufoutptr, n);
if (appData.record)
{
writeLogHeader ();
fwrite (bufoutptr, 1, n, vncLog);
}
bufoutptr += n;
buffered -= n;
return True;
}
memcpy(out, bufoutptr, buffered);
if (appData.record)
{
writeLogHeader ();
fwrite (bufoutptr, 1, buffered, vncLog);
}
out += buffered;
n -= buffered;
bufoutptr = buf;
buffered = 0;
if (n <= BUF_SIZE) {
while (buffered < n) {
int i = read(rfbsock, buf + buffered, BUF_SIZE - buffered);
if (i <= 0) {
if (i < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
ProcessXtEvents();
i = 0;
} else {
fprintf(stderr,"%s",programName);
perror(": read");
return False;
}
} else {
if (errorMessageOnReadFailure) {
fprintf(stderr,"%s: VNC server closed connection\n",programName);
}
return False;
}
}
buffered += i;
}
memcpy(out, bufoutptr, n);
if (appData.record)
fwrite (bufoutptr, 1, n, vncLog);
bufoutptr += n;
buffered -= n;
return True;
} else {
while (n > 0) {
int i = read(rfbsock, out, n);
if (i <= 0) {
if (i < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
ProcessXtEvents();
i = 0;
} else {
fprintf(stderr,"%s",programName);
perror(": read");
return False;
}
} else {
if (errorMessageOnReadFailure) {
fprintf(stderr,"%s: VNC server closed connection\n",programName);
}
return False;
}
}
else
{
if (appData.record)
fwrite (out, 1, i, vncLog);
}
out += i;
n -= i;
}
return True;
}
}
Bool
WriteExact(int sock, char *buf, int n)
{
fd_set fds;
int i = 0;
int j;
if (appData.play || appData.movie)
return True;
while (i < n) {
j = write(sock, buf + i, (n - i));
if (j <= 0) {
if (j < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
FD_ZERO(&fds);
FD_SET(rfbsock,&fds);
if (select(rfbsock+1, NULL, &fds, NULL, NULL) <= 0) {
fprintf(stderr,"%s",programName);
perror(": select");
return False;
}
j = 0;
} else {
fprintf(stderr,"%s",programName);
perror(": write");
return False;
}
} else {
fprintf(stderr,"%s: write failed\n",programName);
return False;
}
}
i += j;
}
return True;
}
int
ConnectToTcpAddr(unsigned int host, int port)
{
int sock;
struct sockaddr_in addr;
int one = 1;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = host;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
fprintf(stderr,"%s",programName);
perror(": ConnectToTcpAddr: socket");
return -1;
}
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr,"%s",programName);
perror(": ConnectToTcpAddr: connect");
close(sock);
return -1;
}
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one)) < 0) {
fprintf(stderr,"%s",programName);
perror(": ConnectToTcpAddr: setsockopt");
close(sock);
return -1;
}
return sock;
}
int
FindFreeTcpPort(void)
{
int sock, port;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
fprintf(stderr,"%s",programName);
perror(": FindFreeTcpPort: socket");
return 0;
}
for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
addr.sin_port = htons((unsigned short)port);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
close(sock);
return port;
}
}
close(sock);
return 0;
}
int
ListenAtTcpPort(int port)
{
int sock;
struct sockaddr_in addr;
int one = 1;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
fprintf(stderr,"%s",programName);
perror(": ListenAtTcpPort: socket");
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(const char *)&one, sizeof(one)) < 0) {
fprintf(stderr,"%s",programName);
perror(": ListenAtTcpPort: setsockopt");
close(sock);
return -1;
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr,"%s",programName);
perror(": ListenAtTcpPort: bind");
close(sock);
return -1;
}
if (listen(sock, 5) < 0) {
fprintf(stderr,"%s",programName);
perror(": ListenAtTcpPort: listen");
close(sock);
return -1;
}
return sock;
}
int
AcceptTcpConnection(int listenSock)
{
int sock;
struct sockaddr_in addr;
int addrlen = sizeof(addr);
int one = 1;
sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen);
if (sock < 0) {
fprintf(stderr,"%s",programName);
perror(": AcceptTcpConnection: accept");
return -1;
}
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one)) < 0) {
fprintf(stderr,"%s",programName);
perror(": AcceptTcpConnection: setsockopt");
close(sock);
return -1;
}
return sock;
}
Bool
SetNonBlocking(int sock)
{
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
fprintf(stderr,"%s",programName);
perror(": AcceptTcpConnection: fcntl");
return False;
}
return True;
}
Bool
StringToIPAddr(const char *str, unsigned int *addr)
{
struct hostent *hp;
if (strcmp(str,"") == 0) {
*addr = 0;
return True;
}
*addr = inet_addr(str);
if (*addr != -1)
return True;
hp = gethostbyname(str);
if (hp) {
*addr = *(unsigned int *)hp->h_addr;
return True;
}
return False;
}
Bool
SameMachine(int sock)
{
struct sockaddr_in peeraddr, myaddr;
int addrlen = sizeof(struct sockaddr_in);
if (appData.play || appData.record || appData.movie)
return False;
getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen);
getsockname(sock, (struct sockaddr *)&myaddr, &addrlen);
return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
}
void
PrintInHex(char *buf, int len)
{
int i, j;
char c, str[17];
str[16] = 0;
fprintf(stderr,"ReadExact: ");
for (i = 0; i < len; i++)
{
if ((i % 16 == 0) && (i != 0)) {
fprintf(stderr," ");
}
c = buf[i];
str[i % 16] = (((c > 31) && (c < 127)) ? c : '.');
fprintf(stderr,"%02x ",(unsigned char)c);
if ((i % 4) == 3)
fprintf(stderr," ");
if ((i % 16) == 15)
{
fprintf(stderr,"%s\n",str);
}
}
if ((i % 16) != 0)
{
for (j = i % 16; j < 16; j++)
{
fprintf(stderr," ");
if ((j % 4) == 3) fprintf(stderr," ");
}
str[i % 16] = 0;
fprintf(stderr,"%s\n",str);
}
fflush(stderr);
}