blob: df40c33a062423ad8813a5799661b66cd57890a8 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
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.
7 *
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.
12 *
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,
16 * USA.
17 */
18#include <strings.h>
19#include <sys/time.h>
20#include <sys/types.h>
21#include <unistd.h>
22#include <errno.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>
28
29#include <network/TcpSocket.h>
30
31#include "Image.h"
32#include <signal.h>
33#include <X11/X.h>
34#include <X11/Xlib.h>
35#include <X11/Xutil.h>
36#include <X11/extensions/XTest.h>
37
38
39#include <rfb/Encoder.h>
40
41using namespace rfb;
42using namespace rdr;
43using namespace network;
44
45LogWriter vlog("main");
46
47StringParameter displayname("display", "The X display", "");
48IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
49VncAuthPasswdFileParameter vncAuthPasswdFile;
50
51static void CleanupSignalHandler(int sig)
52{
53 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
54 // exit() rather than the default which is to abort.
55 fprintf(stderr,"CleanupSignalHandler called\n");
56 exit(1);
57}
58
59
60class XDesktop : public SDesktop, public rfb::ColourMap
61{
62public:
63 XDesktop(Display* dpy_)
64 : dpy(dpy_), pb(0), server(0), oldButtonMask(0), haveXtest(false)
65 {
66 int xtestEventBase;
67 int xtestErrorBase;
68 int major, minor;
69
70 if (XTestQueryExtension(dpy, &xtestEventBase,
71 &xtestErrorBase, &major, &minor)) {
72 XTestGrabControl(dpy, True);
73 vlog.info("XTest extension present - version %d.%d",major,minor);
74 haveXtest = true;
75 } else {
76 vlog.info("XTest extension not present");
77 vlog.info("unable to inject events or display while server is grabbed");
78 }
79
80 int dpyWidth = DisplayWidth(dpy, DefaultScreen(dpy));
81 int dpyHeight = DisplayHeight(dpy, DefaultScreen(dpy));
82 Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
83
84 image = new Image(dpy, dpyWidth, dpyHeight);
85 image->get(DefaultRootWindow(dpy));
86
87 pf.bpp = image->xim->bits_per_pixel;
88 pf.depth = image->xim->depth;
89 pf.bigEndian = (image->xim->byte_order == MSBFirst);
90 pf.trueColour = (vis->c_class == TrueColor);
91 pf.redShift = ffs(vis->red_mask) - 1;
92 pf.greenShift = ffs(vis->green_mask) - 1;
93 pf.blueShift = ffs(vis->blue_mask) - 1;
94 pf.redMax = vis->red_mask >> pf.redShift;
95 pf.greenMax = vis->green_mask >> pf.greenShift;
96 pf.blueMax = vis->blue_mask >> pf.blueShift;
97
98 pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
99 (rdr::U8*)image->xim->data, this);
100 }
101 virtual ~XDesktop() {
102 delete pb;
103 }
104
105 void setVNCServer(VNCServer* s) {
106 server = s;
107 server->setPixelBuffer(pb);
108 }
109
110 // -=- SDesktop interface
111
112 virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
113 if (!haveXtest) return;
114 XTestFakeMotionEvent(dpy, DefaultScreen(dpy), pos.x, pos.y, CurrentTime);
115 if (buttonMask != oldButtonMask) {
116 for (int i = 0; i < 5; i++) {
117 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
118 if (buttonMask & (1<<i)) {
119 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
120 } else {
121 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
122 }
123 }
124 }
125 }
126 oldButtonMask = buttonMask;
127 }
128
129 virtual void keyEvent(rdr::U32 key, bool down) {
130 if (!haveXtest) return;
131 int keycode = XKeysymToKeycode(dpy, key);
132 if (keycode)
133 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
134 }
135
136 virtual void clientCutText(const char* str, int len) {
137 }
138
139 virtual Point getFbSize() {
140 return Point(pb->width(), pb->height());
141 }
142
143 // rfb::ColourMap callbacks
144 virtual void lookup(int index, int* r, int* g, int* b) {
145 XColor xc;
146 xc.pixel = index;
147 if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
148 XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
149 } else {
150 xc.red = xc.green = xc.blue = 0;
151 }
152 *r = xc.red;
153 *g = xc.green;
154 *b = xc.blue;
155 }
156
157 virtual void poll() {
158 if (server && server->clientsReadyForUpdate()) {
159 image->get(DefaultRootWindow(dpy));
160 server->add_changed(pb->getRect());
161 server->tryUpdate();
162 }
163 }
164
165protected:
166 Display* dpy;
167 PixelFormat pf;
168 PixelBuffer* pb;
169 VNCServer* server;
170 Image* image;
171 int oldButtonMask;
172 bool haveXtest;
173};
174
175char* programName;
176
177static void usage()
178{
179 fprintf(stderr, "\nusage: %s [<parameters>]\n", programName);
180 fprintf(stderr,"\n"
181 "Parameters can be turned on with -<param> or off with -<param>=0\n"
182 "Parameters which take a value can be specified as "
183 "-<param> <value>\n"
184 "Other valid forms are <param>=<value> -<param>=<value> "
185 "--<param>=<value>\n"
186 "Parameter names are case-insensitive. The parameters are:\n\n");
187 Configuration::listParams(79, 14);
188 exit(1);
189}
190
191int main(int argc, char** argv)
192{
193 initStdIOLoggers();
194 LogWriter::setLogParams("*:stderr:30");
195
196 programName = argv[0];
197 Display* dpy;
198
199 for (int i = 1; i < argc; i++) {
200 if (Configuration::setParam(argv[i]))
201 continue;
202
203 if (argv[i][0] == '-') {
204 if (i+1 < argc) {
205 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
206 i++;
207 continue;
208 }
209 }
210 usage();
211 }
212
213 usage();
214 }
215
216 CharArray dpyStr(displayname.getData());
217 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
218 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
219 programName, XDisplayName(displayname.getData()));
220 exit(1);
221 }
222
223 signal(SIGHUP, CleanupSignalHandler);
224 signal(SIGINT, CleanupSignalHandler);
225 signal(SIGTERM, CleanupSignalHandler);
226
227 try {
228 XDesktop desktop(dpy);
229 VNCServerST server("x0vncserver", &desktop);
230 desktop.setVNCServer(&server);
231
232 TcpSocket::initTcpSockets();
233 TcpListener listener((int)rfbport);
234 vlog.info("Listening on port %d", (int)rfbport);
235
236 while (true) {
237 fd_set rfds;
238 struct timeval tv;
239
240 tv.tv_sec = 0;
241 tv.tv_usec = 50*1000;
242
243 FD_ZERO(&rfds);
244 FD_SET(listener.getFd(), &rfds);
245
246 std::list<Socket*> sockets;
247 server.getSockets(&sockets);
248 std::list<Socket*>::iterator i;
249 for (i = sockets.begin(); i != sockets.end(); i++) {
250 FD_SET((*i)->getFd(), &rfds);
251 }
252
253 int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
254 if (n < 0) throw rdr::SystemException("select",errno);
255
256 if (FD_ISSET(listener.getFd(), &rfds)) {
257 Socket* sock = listener.accept();
258 server.addClient(sock);
259 }
260
261 server.getSockets(&sockets);
262 for (i = sockets.begin(); i != sockets.end(); i++) {
263 if (FD_ISSET((*i)->getFd(), &rfds)) {
264 server.processSocketEvent(*i);
265 }
266 }
267
268 server.checkTimeouts();
269 desktop.poll();
270 }
271
272 } catch (rdr::Exception &e) {
273 vlog.error(e.str());
274 };
275
276 return 0;
277}