1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1999-2008 Robert Osfield
2 *
3 * This software is open source and may be redistributed and/or modified under
4 * the terms of the GNU General Public License (GPL) version 2.0.
5 * The full license is in LICENSE.txt file included with this distribution,.
6 *
7 * This software is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * include LICENSE.txt for more details.
11 */
12
13 #include <osg/Timer>
14
15 #include <osgDB/ReaderWriter>
16 #include <osgDB/FileNameUtils>
17 #include <osgDB/Registry>
18
19 #include <osgWidget/VncClient>
20
21 extern "C" {
22 #include <rfb/rfbclient.h>
23 }
24
25 class LibVncImage : public osgWidget::VncImage
26 {
27 public:
28
29 LibVncImage();
30
31 bool connect(const std::string& hostname);
32
33 void close();
34
35 virtual bool sendPointerEvent(int x, int y, int buttonMask);
36
getTimeOfLastRender() const37 double getTimeOfLastRender() const { return _timeOfLastRender; }
38
time() const39 double time() const { return osg::Timer::instance()->time_s(); }
40
41 virtual bool sendKeyEvent(int key, bool keyDown);
42
43 virtual void setFrameLastRendered(const osg::FrameStamp* frameStamp);
44
45 static rfbBool resizeImage(rfbClient* client);
46
47 static void updateImage(rfbClient* client,int x,int y,int w,int h);
48
49 static void passwordCheck(rfbClient* client,const char* encryptedPassWord,int len);
50 static char* getPassword(rfbClient* client);
51
52 std::string _optionString;
53 std::string _username;
54 std::string _password;
55
56 double _timeOfLastRender;
57
58 osg::ref_ptr<osg::RefBlock> _inactiveBlock;
59
60 protected:
61
62 virtual ~LibVncImage();
63
64 class RfbThread : public osg::Referenced, public OpenThreads::Thread
65 {
66 public:
67
RfbThread(rfbClient * client,LibVncImage * image)68 RfbThread(rfbClient* client, LibVncImage* image):
69 _client(client),
70 _image(image),
71 _done(false) {}
72
~RfbThread()73 virtual ~RfbThread()
74 {
75 _done = true;
76 if (isRunning())
77 {
78 cancel();
79 join();
80 }
81 }
82
run()83 virtual void run()
84 {
85 do
86 {
87 // OSG_NOTICE<<"RfThread::run()"<<std::endl;
88 int i=WaitForMessage(_client,1000000);
89 if (i)
90 {
91 if(!HandleRFBServerMessage(_client))
92 {
93 OSG_NOTICE<<"HandleRFBServerMessage returned non zero value."<<std::endl;
94 }
95 // _image->updated();
96 }
97 else
98 {
99 // OSG_NOTICE<<"Timed out"<<std::endl;
100 }
101
102
103
104 double currentTime = _image->time();
105 double timeBeforeIdle = 0.1;
106
107 if (currentTime > _image->getTimeOfLastRender()+timeBeforeIdle)
108 {
109 //OSG_NOTICE<<"New: Time to idle"<<std::endl;
110 _image->_inactiveBlock->reset();
111 _image->_inactiveBlock->block();
112 //OSG_NOTICE<<" Finished block."<<std::endl;
113 }
114 else
115 {
116 //OSG_NOTICE<<"New: Should still be active"<<std::endl;
117 }
118
119
120 } while (!_done && !testCancel());
121 }
122
123 rfbClient* _client;
124 osg::observer_ptr<LibVncImage> _image;
125 bool _done;
126
127 };
128
129 public:
130
131 rfbClient* _client;
132
133 osg::ref_ptr<RfbThread> _rfbThread;
134
135 };
136
LibVncImage()137 LibVncImage::LibVncImage():
138 _client(0)
139 {
140 // setPixelBufferObject(new osg::PixelBufferObject(this);
141
142 _inactiveBlock = new osg::RefBlock;
143 }
144
~LibVncImage()145 LibVncImage::~LibVncImage()
146 {
147 close();
148 }
149
rfbInitConnection(rfbClient * client)150 static rfbBool rfbInitConnection(rfbClient* client)
151 {
152 /* Unless we accepted an incoming connection, make a TCP connection to the
153 given VNC server */
154
155 if (!client->listenSpecified) {
156 if (!client->serverHost || !ConnectToRFBServer(client,client->serverHost,client->serverPort))
157 return FALSE;
158 }
159
160 /* Initialise the VNC connection, including reading the password */
161
162 if (!InitialiseRFBConnection(client))
163 return FALSE;
164
165 if (!SetFormatAndEncodings(client))
166 return FALSE;
167
168 client->width=client->si.framebufferWidth;
169 client->height=client->si.framebufferHeight;
170 client->MallocFrameBuffer(client);
171
172 if (client->updateRect.x < 0) {
173 client->updateRect.x = client->updateRect.y = 0;
174 client->updateRect.w = client->width;
175 client->updateRect.h = client->height;
176 }
177
178 if (client->appData.scaleSetting>1)
179 {
180 if (!SendScaleSetting(client, client->appData.scaleSetting))
181 return FALSE;
182 if (!SendFramebufferUpdateRequest(client,
183 client->updateRect.x / client->appData.scaleSetting,
184 client->updateRect.y / client->appData.scaleSetting,
185 client->updateRect.w / client->appData.scaleSetting,
186 client->updateRect.h / client->appData.scaleSetting,
187 FALSE))
188 return FALSE;
189 }
190 else
191 {
192 if (!SendFramebufferUpdateRequest(client,
193 client->updateRect.x, client->updateRect.y,
194 client->updateRect.w, client->updateRect.h,
195 FALSE))
196 return FALSE;
197 }
198
199 return TRUE;
200 }
201
passwordCheck(rfbClient *,const char *,int)202 void LibVncImage::passwordCheck(rfbClient* /*client*/,const char* /*encryptedPassWord*/,int /*len*/)
203 {
204 OSG_NOTICE<<"LibVncImage::passwordCheck"<<std::endl;
205 }
206
getPassword(rfbClient * client)207 char* LibVncImage::getPassword(rfbClient* client)
208 {
209 LibVncImage* image = (LibVncImage*)(rfbClientGetClientData(client, 0));
210 OSG_NOTICE<<"LibVncImage::getPassword "<<image->_password<<std::endl;
211 return strdup(image->_password.c_str());
212 }
213
214
connect(const std::string & hostname)215 bool LibVncImage::connect(const std::string& hostname)
216 {
217 if (hostname.empty()) return false;
218
219 if (_client) close();
220
221 _client = rfbGetClient(8,3,4);
222 _client->canHandleNewFBSize = TRUE;
223 _client->MallocFrameBuffer = resizeImage;
224 _client->GotFrameBufferUpdate = updateImage;
225 _client->HandleKeyboardLedState = 0;
226 _client->HandleTextChat = 0;
227
228 // provide the password if we have one assigned
229 if (!_password.empty()) _client->GetPassword = getPassword;
230
231 rfbClientSetClientData(_client, 0, this);
232
233 size_t pos = hostname.find(":");
234 if (pos == std::string::npos)
235 {
236 _client->serverHost = strdup(hostname.c_str());
237 }
238 else
239 {
240 _client->serverHost = strdup(hostname.substr(0, pos).c_str());
241 _client->serverPort = atoi(hostname.substr(pos+1).c_str());
242 }
243
244 // _client->appData.qualityLevel = ;
245 // _client->appData.encodings = ;
246 // _client->appData.compressLevel = ;
247 // _client->appData.scaleSetting = ;
248
249 if(rfbInitConnection(_client))
250 {
251 _rfbThread = new RfbThread(_client, this);
252 _rfbThread->startThread();
253
254 return true;
255 }
256 else
257 {
258 close();
259
260 return false;
261 }
262 }
263
264
close()265 void LibVncImage::close()
266 {
267 if (_rfbThread.valid())
268 {
269 _inactiveBlock->release();
270
271 // stop the client thread
272 _rfbThread = 0;
273 }
274
275 if (_client)
276 {
277 // close the client
278 rfbClientCleanup(_client);
279 _client = 0;
280 }
281 }
282
283
resizeImage(rfbClient * client)284 rfbBool LibVncImage::resizeImage(rfbClient* client)
285 {
286 LibVncImage* image = (LibVncImage*)(rfbClientGetClientData(client, 0));
287
288 int width = client->width;
289 int height = client->height;
290 int depth = client->format.bitsPerPixel;
291
292 OSG_NOTICE<<"resize "<<width<<", "<<height<<", "<<depth<<" image = "<<image<<std::endl;
293 PrintPixelFormat(&(client->format));
294
295 bool swap = client->format.redShift!=0;
296
297 if (!image->_optionString.empty())
298 {
299 if (image->_optionString.find("swap")!=std::string::npos || image->_optionString.find("swop")!=std::string::npos) swap = true;
300 }
301
302 GLenum gl_pixelFormat = swap ? GL_BGRA : GL_RGBA;
303
304 if (!image->_optionString.empty())
305 {
306 if (image->_optionString.find("RGB")!=std::string::npos) gl_pixelFormat = GL_RGBA;
307 if (image->_optionString.find("RGBA")!=std::string::npos) gl_pixelFormat = GL_RGBA;
308 if (image->_optionString.find("BGR")!=std::string::npos) gl_pixelFormat = GL_BGRA;
309 if (image->_optionString.find("BGRA")!=std::string::npos) gl_pixelFormat = GL_BGRA;
310 }
311
312 image->allocateImage(width, height, 1, gl_pixelFormat, GL_UNSIGNED_BYTE);
313 image->setInternalTextureFormat(GL_RGBA);
314
315
316
317 client->frameBuffer= (uint8_t*)(image->data());
318
319 return TRUE;
320 }
321
updateImage(rfbClient * client,int,int,int,int)322 void LibVncImage::updateImage(rfbClient* client, int /*x*/, int /*y*/, int /*w*/, int /*h*/)
323 {
324 LibVncImage* image = (LibVncImage*)(rfbClientGetClientData(client, 0));
325
326 image->dirty();
327 }
328
sendPointerEvent(int x,int y,int buttonMask)329 bool LibVncImage::sendPointerEvent(int x, int y, int buttonMask)
330 {
331 if (_client)
332 {
333 SendPointerEvent(_client ,x, y, buttonMask);
334 return true;
335 }
336 return false;
337 }
338
sendKeyEvent(int key,bool keyDown)339 bool LibVncImage::sendKeyEvent(int key, bool keyDown)
340 {
341 if (_client)
342 {
343 SendKeyEvent(_client, key, keyDown ? TRUE : FALSE);
344 return true;
345 }
346 return false;
347 }
348
349
setFrameLastRendered(const osg::FrameStamp *)350 void LibVncImage::setFrameLastRendered(const osg::FrameStamp*)
351 {
352
353 _timeOfLastRender = time();
354
355 // release block
356 _inactiveBlock->release();
357 }
358
359 class ReaderWriterVNC : public osgDB::ReaderWriter
360 {
361 public:
362
ReaderWriterVNC()363 ReaderWriterVNC()
364 {
365 supportsExtension("vnc","VNC plugin");
366
367 supportsOption("swap","Swaps the pixel format order, exchanging the red and blue channels.");
368 supportsOption("swop","American spelling, same effect as swap.");
369 supportsOption("RGB","Use RGBA pixel format for the vnc image");
370 supportsOption("RGBA","Use RGBA pixel format for the vnc image");
371 supportsOption("BGR","Use BGRA pixel format for the vnc image");
372 supportsOption("BGRA","Use BGRA pixel format for the vnc image");
373 }
374
className() const375 virtual const char* className() const { return "VNC plugin"; }
376
readObject(const std::string & file,const osgDB::ReaderWriter::Options * options) const377 virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
378 {
379 return readImage(file,options);
380 }
381
readImage(const std::string & fileName,const osgDB::ReaderWriter::Options * options) const382 virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
383 {
384 if (!osgDB::equalCaseInsensitive(osgDB::getFileExtension(fileName),"vnc"))
385 {
386 return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
387 }
388
389 std::string hostname = osgDB::getNameLessExtension(fileName);
390
391 OSG_NOTICE<<"Hostname = "<<hostname<<std::endl;
392
393 osg::ref_ptr<LibVncImage> image = new LibVncImage;
394 image->setDataVariance(osg::Object::DYNAMIC);
395 image->setOrigin(osg::Image::TOP_LEFT);
396
397 const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ?
398 options->getAuthenticationMap() :
399 osgDB::Registry::instance()->getAuthenticationMap();
400
401 if (authenticationMap != NULL)
402 {
403 const osgDB::AuthenticationDetails* details = authenticationMap->getAuthenticationDetails(hostname);
404 if (details == NULL)
405 {
406 size_t pos = hostname.find(":");
407 if (pos != std::string::npos)
408 {
409 details = authenticationMap->getAuthenticationDetails(hostname.substr(0, pos));
410 }
411 }
412
413 // configure authentication if required.
414 if (details != NULL)
415 {
416 OSG_NOTICE << "Passing in password = " << details->password << std::endl;
417
418 image->_username = details->username;
419 image->_password = details->password;
420 }
421 }
422
423 if (options && !options->getOptionString().empty())
424 {
425 image->_optionString = options->getOptionString();
426 }
427
428 if (!image->connect(hostname))
429 {
430 return "Could not connect to "+hostname;
431 }
432
433 return image.get();
434 }
435
readNode(const std::string & fileName,const osgDB::ReaderWriter::Options * options) const436 virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
437 {
438 osgDB::ReaderWriter::ReadResult result = readImage(fileName, options);
439 if (!result.validImage()) return result;
440
441 osg::ref_ptr<osgWidget::VncClient> vncClient = new osgWidget::VncClient();
442 if (vncClient->assign(dynamic_cast<osgWidget::VncImage*>(result.getImage())))
443 {
444 return vncClient.release();
445 }
446 else
447 {
448 return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
449 }
450 }
451 };
452
453 // now register with Registry to instantiate the above
454 // reader/writer.
455 REGISTER_OSGPLUGIN(vnc, ReaderWriterVNC)
456
457