1 /* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 #include <sys/types.h>
23 #include <rfb/Logger_stdio.h>
24 #include <rfb/LogWriter.h>
25 #include <rfb/VNCServerST.h>
26 #include <rfb/Configuration.h>
27 #include <rfb/SSecurityFactoryStandard.h>
29 #include <network/TcpSocket.h>
35 #include <X11/Xutil.h>
36 #include <X11/extensions/XTest.h>
39 #include <rfb/Encoder.h>
44 using namespace network;
50 #define NO_KEY { 0, 0 }
58 static const struct key_code ascii_to_key_code[] = { // ASCII Value Serves as Index
59 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x00 - 0x03
60 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x04 - 0x07
61 { 0x0E, 0 }, { 0x0F, 0 }, { 0x1C, 0 }, NO_KEY, // 0x08 - 0x0B
62 NO_KEY, { 0x1C, 0 }, NO_KEY, NO_KEY, // 0x0C - 0x0F
63 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x10 - 0x13
64 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x14 - 0x17
65 NO_KEY, NO_KEY, NO_KEY, { 0x01, 0 }, // 0x18 - 0x1B
66 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x1C - 0x1F
67 { 0x39, 0 }, { 0x02, 1 }, { 0x28, 1 }, { 0x04, 1 }, // 0x20 - 0x23
68 { 0x05, 1 }, { 0x06, 1 }, { 0x08, 1 }, { 0x28, 0 }, // 0x24 - 0x27
69 { 0x0A, 1 }, { 0x0B, 1 }, { 0x09, 1 }, { 0x0D, 1 }, // 0x28 - 0x2B
70 { 0x33, 0 }, { 0x0C, 0 }, { 0x34, 0 }, { 0x35, 0 }, // 0x2C - 0x2F
71 { 0x0B, 0 }, { 0x02, 0 }, { 0x03, 0 }, { 0x04, 0 }, // 0x30 - 0x33
72 { 0x05, 0 }, { 0x06, 0 }, { 0x07, 0 }, { 0x08, 0 }, // 0x34 - 0x37
73 { 0x09, 0 }, { 0x0A, 0 }, { 0x27, 1 }, { 0x27, 0 }, // 0x38 - 0x3B
74 { 0x33, 1 }, { 0x0D, 0 }, { 0x34, 1 }, { 0x35, 1 }, // 0x3C - 0x3F
75 { 0x03, 1 }, { 0x1E, 1 }, { 0x30, 1 }, { 0x2E, 1 }, // 0x40 - 0x43
76 { 0x20, 1 }, { 0x12, 1 }, { 0x21, 1 }, { 0x22, 1 }, // 0x44 - 0x47
77 { 0x23, 1 }, { 0x17, 1 }, { 0x24, 1 }, { 0x25, 1 }, // 0x48 - 0x4B
78 { 0x26, 1 }, { 0x32, 1 }, { 0x31, 1 }, { 0x18, 1 }, // 0x4C - 0x4F
79 { 0x19, 1 }, { 0x10, 1 }, { 0x13, 1 }, { 0x1F, 1 }, // 0x50 - 0x53
80 { 0x14, 1 }, { 0x16, 1 }, { 0x2F, 1 }, { 0x11, 1 }, // 0x54 - 0x57
81 { 0x2D, 1 }, { 0x15, 1 }, { 0x2C, 1 }, { 0x1A, 0 }, // 0x58 - 0x5B
82 { 0x2B, 0 }, { 0x1B, 0 }, { 0x07, 1 }, { 0x0C, 1 }, // 0x5C - 0x5F
83 { 0x29, 0 }, { 0x1E, 0 }, { 0x30, 0 }, { 0x2E, 0 }, // 0x60 - 0x63
84 { 0x20, 0 }, { 0x12, 0 }, { 0x21, 0 }, { 0x22, 0 }, // 0x64 - 0x67
85 { 0x23, 0 }, { 0x17, 0 }, { 0x24, 0 }, { 0x25, 0 }, // 0x68 - 0x6B
86 { 0x26, 0 }, { 0x32, 0 }, { 0x31, 0 }, { 0x18, 0 }, // 0x6C - 0x6F
87 { 0x19, 0 }, { 0x10, 0 }, { 0x13, 0 }, { 0x1F, 0 }, // 0x70 - 0x73
88 { 0x14, 0 }, { 0x16, 0 }, { 0x2F, 0 }, { 0x11, 0 }, // 0x74 - 0x77
89 { 0x2D, 0 }, { 0x15, 0 }, { 0x2C, 0 }, { 0x1A, 1 }, // 0x78 - 0x7B
90 { 0x2B, 1 }, { 0x1B, 1 }, { 0x29, 1 }, { 0x0E, 0 } // 0x7C - 0x7F
93 static uint8_t convert_to_scancode(rdr::U32 key, bool down)
97 unsigned char scancode=0;
100 struct key_code c = ascii_to_key_code[key];
101 scancode=c.scan_code;
104 case 0xffe1: //left shift
108 case 0xffe2: //right shift
112 case 0xffe3: //left ctrl
113 case 0xffe4: //right ctrl
114 scancode = 0x1d; // translated as left ctrl
117 case 0xffe7: //left meta
118 case 0xffe8: //right meta
119 case 0xffe9: //left alt
120 case 0xffea: //right alt
121 scancode = 0x38; // translated as a left alt
124 case 0xff08: // backspace
132 case 0xff0d: // return
136 case 0xff1b: // escape
140 case 0xff63: // insert
144 case 0xffff: // delete
156 case 0xff55: // pageup
160 case 0xff56: // pagedown
172 case 0xff53: // right
220 fprintf(stderr,"Ignoring key 0x%x (down=%d)\n", key, down);
242 LogWriter vlog("main");
244 StringParameter displayname("display", "The X display", "");
245 IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
246 StringParameter geometry("geometry", "Height and width", "1024x768");
248 VncAuthPasswdFileParameter vncAuthPasswdFile;
250 static void CleanupSignalHandler(int sig)
252 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
253 // exit() rather than the default which is to abort.
254 fprintf(stderr,"CleanupSignalHandler called\n");
259 inline int sign(int x) { return x<0; }
261 inline int abs(int x) { if (x<0) { return -x; } else {return x;}}
263 class V3Desktop : public SDesktop, public rfb::ColourMap
266 V3Desktop(int fd) // fd is the open fd of the VM's device
267 : v3_fd(fd), pb(0), server(0), oldButtonMask(0)
270 mouse_inited=false; // not yet
272 // Let's get the needed resolution
273 if (v3_get_fb_spec(v3_fd,&v3_spec)) {
274 fprintf(stderr, "Can't get spec from VM\n");
279 fprintf(stderr,"VM's spec is %u x %u x %u with %u bits per channel and rgb offsets (%u,%u,%u)\n",
280 v3_spec.width, v3_spec.height, v3_spec.bytes_per_pixel,
281 v3_spec.bits_per_channel, v3_spec.red_offset, v3_spec.green_offset, v3_spec.blue_offset);
283 if (! (v3_spec.bytes_per_pixel==4 &&
284 v3_spec.bits_per_channel==8)) {
285 fprintf(stderr,"Error in forma compatabiliity\n");
289 dpyWidth=v3_spec.width;
290 dpyHeight=v3_spec.height;
292 // Now we can build our image to match
293 image = new Image(dpyWidth, dpyHeight);
295 // Convert to the internal pixel format a
296 pf.bpp = v3_spec.bytes_per_pixel*8;
297 pf.depth = v3_spec.bits_per_channel*3;
301 //default pixel formats dont work!
302 pf.redShift = v3_spec.red_offset*8;
303 pf.greenShift = v3_spec.green_offset*8;
304 pf.blueShift = v3_spec.blue_offset*8;
309 // assigns new pixelbuffer to xdesktop object
310 pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
311 (rdr::U8*)image->data, this);
314 virtual ~V3Desktop() {
319 void setVNCServer(VNCServer* s) {
321 server->setPixelBuffer(pb);
324 // -=- SDesktop interface, worry about the pointer and key events later..
326 // Mouse events in VNC give absolute pixel coordinates of the mouse pointer
327 // the PS/2 mouse spec means we provide relative movements with +/- 256
328 // these relative movements can then be scaled by the mouse or by the software
329 // therefore, we will need to trans back from absolute to relative, perhaps
330 // converting one VNC event to multiple PS/2 events
331 // we assume here that the translation is 1:1
333 virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
334 //vlog.info("Pointer event occurred, x position: %d, y position: %d; button mask: %d.", pos.x, pos.y, buttonMask);
345 // delta from current position
346 dx = pos.x - mouse_lastpos.x ;
347 dy = pos.y - mouse_lastpos.y ;
350 // update last position
355 // dx and dy are now +/- 2^16
356 // we can generate increments of up to +/- MAXINC;
359 incx = min(MAXINC, abs(dx));
360 incy = min(MAXINC, abs(dy));
362 if (v3_send_mouse(v3_fd, sign(dx), incx, sign(dy), incy, buttonMask)) {
363 fprintf(stderr, "Error in sending mouse event\n");
367 dx += (dx>=0) ? -incx : +incx;
368 dy += (dy>=0) ? -incy : +incy;
372 virtual void keyEvent(rdr::U32 key, bool down) {
373 //vlog.info("Key event received (key=%d, down=%d.",key,down);
375 uint8_t scan_code = convert_to_scancode(key,down);
377 if (scan_code && v3_send_key(v3_fd,scan_code)) {
378 fprintf(stderr, "Error in sending key event\n");
384 virtual void clientCutText(const char* str, int len) {
387 virtual Point getFbSize() {
388 return Point(pb->width(), pb->height());
391 virtual void lookup(int index, int* r, int* g, int* b) {
393 // Probably not important since we will use true-color
395 /* X implementation..
398 if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
399 XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
401 xc.red = xc.green = xc.blue = 0;
409 virtual void poll() {
411 server->clientsReadyForUpdate() &&
412 v3_have_update(v3_fd)) {
414 struct v3_frame_buffer_spec newspec = v3_spec;
416 if (v3_get_fb_data(v3_fd,&newspec,image->data)<0) {
417 fprintf(stderr,"Failed to get fb data from VM\n");
421 if (memcmp(&newspec,&v3_spec,sizeof(struct v3_frame_buffer_spec))) {
422 fprintf(stderr,"Uh oh - screen spec has changed\n");
426 //fprintf(stderr,"render!\n");
428 server->add_changed(pb->getRect());
435 struct v3_frame_buffer_spec v3_spec;
453 fprintf(stderr, "\nusage: %s [<parameters>] /dev/v3-vmN \n", programName);
455 "Parameters can be turned on with -<param> or off with -<param>=0\n"
456 "Parameters which take a value can be specified as "
458 "Other valid forms are <param>=<value> -<param>=<value> "
459 "--<param>=<value>\n"
460 "Parameter names are case-insensitive. The parameters are:\n\n");
461 Configuration::listParams(79, 14);
465 int main(int argc, char** argv)
471 LogWriter::setLogParams("*:stderr:30");
473 programName = argv[0];
475 // Grab the v3 device before all the OO stuff goes inscrutable on us
479 v3_dev = argv[argc-1];
481 fprintf(stderr,"Will attempt to connect to VM %s\n",v3_dev);
488 for (int i = 1; i < argc; i++) {
489 if (Configuration::setParam(argv[i]))
492 if (argv[i][0] == '-') {
494 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
505 CharArray dpyStr(displayname.getData());
508 signal(SIGHUP, CleanupSignalHandler);
509 signal(SIGINT, CleanupSignalHandler);
510 signal(SIGTERM, CleanupSignalHandler);
513 v3_fd = open(v3_dev, O_RDONLY);
516 perror("Cannot open VM");
520 V3Desktop desktop(v3_fd);
521 VNCServerST server("v3x0vncserver", &desktop);
522 desktop.setVNCServer(&server);
524 TcpSocket::initTcpSockets();
525 TcpListener listener((int)rfbport);
526 vlog.info("Listening on port %d", (int)rfbport);
533 tv.tv_usec = 50*1000;
536 FD_SET(listener.getFd(), &rfds);
538 std::list<Socket*> sockets;
539 server.getSockets(&sockets);
540 std::list<Socket*>::iterator i;
541 for (i = sockets.begin(); i != sockets.end(); i++) {
542 FD_SET((*i)->getFd(), &rfds);
545 int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
546 if (n < 0) throw rdr::SystemException("select",errno);
548 if (FD_ISSET(listener.getFd(), &rfds)) {
549 Socket* sock = listener.accept();
550 server.addClient(sock);
553 server.getSockets(&sockets);
554 for (i = sockets.begin(); i != sockets.end(); i++) {
555 if (FD_ISSET((*i)->getFd(), &rfds)) {
556 server.processSocketEvent(*i);
560 server.checkTimeouts();
564 } catch (rdr::Exception &e) {