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 class V3Desktop : public SDesktop, public rfb::ColourMap
262 V3Desktop(int fd) // fd is the open fd of the VM's device
263 : v3_fd(fd), pb(0), server(0), oldButtonMask(0)
266 // Let's get the needed resolution
267 if (v3_get_fb_spec(v3_fd,&v3_spec)) {
268 fprintf(stderr, "Can't get spec from VM\n");
273 fprintf(stderr,"VM's spec is %u x %u x %u with %u bits per channel and rgb offsets (%u,%u,%u)\n",
274 v3_spec.width, v3_spec.height, v3_spec.bytes_per_pixel,
275 v3_spec.bits_per_channel, v3_spec.red_offset, v3_spec.green_offset, v3_spec.blue_offset);
277 if (! (v3_spec.bytes_per_pixel==4 &&
278 v3_spec.bits_per_channel==8)) {
279 fprintf(stderr,"Error in forma compatabiliity\n");
283 dpyWidth=v3_spec.width;
284 dpyHeight=v3_spec.height;
286 // Now we can build our image to match
287 image = new Image(dpyWidth, dpyHeight);
289 // Convert to the internal pixel format a
290 pf.bpp = v3_spec.bytes_per_pixel*8;
291 pf.depth = v3_spec.bits_per_channel*3;
295 //default pixel formats dont work!
296 pf.redShift = v3_spec.red_offset*8;
297 pf.greenShift = v3_spec.green_offset*8;
298 pf.blueShift = v3_spec.blue_offset*8;
303 // assigns new pixelbuffer to xdesktop object
304 pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
305 (rdr::U8*)image->data, this);
308 virtual ~V3Desktop() {
313 void setVNCServer(VNCServer* s) {
315 server->setPixelBuffer(pb);
318 // -=- SDesktop interface, worry about the pointer and key events later..
319 virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
320 vlog.info("Pointer event occurred, x position: %d, y position: %d; button mask: %d.", pos.x, pos.y, buttonMask);
321 if (v3_send_mouse(v3_fd,pos.x,pos.y,buttonMask)) {
322 fprintf(stderr, "Error in sending mouse event\n");
327 virtual void keyEvent(rdr::U32 key, bool down) {
328 vlog.info("Key event received (key=%d, down=%d.",key,down);
330 //STILL NEED TO FIND MAPPING FROM KEYSYM TO ACTUAL KEYSTROKES
331 uint8_t scan_code = convert_to_scancode(key,down);
333 if (scan_code && v3_send_key(v3_fd,scan_code)) {
334 fprintf(stderr, "Error in sending key event\n");
340 virtual void clientCutText(const char* str, int len) {
343 virtual Point getFbSize() {
344 return Point(pb->width(), pb->height());
347 virtual void lookup(int index, int* r, int* g, int* b) {
349 // Probably not important since we will use true-color
351 /* X implementation..
354 if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
355 XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
357 xc.red = xc.green = xc.blue = 0;
365 virtual void poll() {
367 server->clientsReadyForUpdate() &&
368 v3_have_update(v3_fd)) {
370 struct v3_frame_buffer_spec newspec = v3_spec;
372 if (v3_get_fb_data(v3_fd,&newspec,image->data)<0) {
373 fprintf(stderr,"Failed to get fb data from VM\n");
377 if (memcmp(&newspec,&v3_spec,sizeof(struct v3_frame_buffer_spec))) {
378 fprintf(stderr,"Uh oh - screen spec has changed\n");
382 fprintf(stderr,"render!\n");
384 server->add_changed(pb->getRect());
391 struct v3_frame_buffer_spec v3_spec;
407 fprintf(stderr, "\nusage: %s [<parameters>] /dev/v3-vmN \n", programName);
409 "Parameters can be turned on with -<param> or off with -<param>=0\n"
410 "Parameters which take a value can be specified as "
412 "Other valid forms are <param>=<value> -<param>=<value> "
413 "--<param>=<value>\n"
414 "Parameter names are case-insensitive. The parameters are:\n\n");
415 Configuration::listParams(79, 14);
419 int main(int argc, char** argv)
425 LogWriter::setLogParams("*:stderr:30");
427 programName = argv[0];
429 // Grab the v3 device before all the OO stuff goes inscrutable on us
433 v3_dev = argv[argc-1];
435 fprintf(stderr,"Will attempt to connect to VM %s\n",v3_dev);
442 for (int i = 1; i < argc; i++) {
443 if (Configuration::setParam(argv[i]))
446 if (argv[i][0] == '-') {
448 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
459 CharArray dpyStr(displayname.getData());
462 signal(SIGHUP, CleanupSignalHandler);
463 signal(SIGINT, CleanupSignalHandler);
464 signal(SIGTERM, CleanupSignalHandler);
467 v3_fd = open(v3_dev, O_RDONLY);
470 perror("Cannot open VM");
474 V3Desktop desktop(v3_fd);
475 VNCServerST server("v3x0vncserver", &desktop);
476 desktop.setVNCServer(&server);
478 TcpSocket::initTcpSockets();
479 TcpListener listener((int)rfbport);
480 vlog.info("Listening on port %d", (int)rfbport);
487 tv.tv_usec = 50*1000;
490 FD_SET(listener.getFd(), &rfds);
492 std::list<Socket*> sockets;
493 server.getSockets(&sockets);
494 std::list<Socket*>::iterator i;
495 for (i = sockets.begin(); i != sockets.end(); i++) {
496 FD_SET((*i)->getFd(), &rfds);
499 int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
500 if (n < 0) throw rdr::SystemException("select",errno);
502 if (FD_ISSET(listener.getFd(), &rfds)) {
503 Socket* sock = listener.accept();
504 server.addClient(sock);
507 server.getSockets(&sockets);
508 for (i = sockets.begin(); i != sockets.end(); i++) {
509 if (FD_ISSET((*i)->getFd(), &rfds)) {
510 server.processSocketEvent(*i);
514 server.checkTimeouts();
518 } catch (rdr::Exception &e) {