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