root/OpenSceneGraph/trunk/src/osgPlugins/vnc/ReaderWriterVNC.cpp @ 12850

Revision 12850, 10.1 kB (checked in by robert, 2 years ago)

Implement a simply attempt at detecting when the pixel format needs to be swapped.

  • Property svn:eol-style set to native
Line 
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
21extern "C" {
22#include <rfb/rfbclient.h>
23}
24
25class 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
37        double getTimeOfLastUpdate() const { return _timeOfLastUpdate; }
38        double getTimeOfLastRender() const { return _timeOfLastRender; }
39
40        double time() const { return osg::Timer::instance()->time_s(); }
41
42        virtual bool sendKeyEvent(int key, bool keyDown);
43
44        virtual void setFrameLastRendered(const osg::FrameStamp* frameStamp);
45
46        void updated();
47
48        static rfbBool resizeImage(rfbClient* client);
49       
50        static void updateImage(rfbClient* client,int x,int y,int w,int h);
51       
52        double                      _timeOfLastUpdate;
53        double                      _timeOfLastRender;
54
55        bool                        _active;
56        osg::ref_ptr<osg::RefBlock> _inactiveBlock;
57
58    protected:
59   
60        virtual ~LibVncImage();
61
62        class RfbThread : public osg::Referenced, public OpenThreads::Thread
63        {
64        public:
65
66            RfbThread(rfbClient* client, LibVncImage* image):
67                _client(client),
68                _image(image),
69                _done(false) {}
70
71            virtual ~RfbThread()
72            {
73                _done = true;
74                while(isRunning())
75                {
76                    OpenThreads::Thread::YieldCurrentThread();
77                }
78            }
79
80            virtual void run()
81            {
82                do
83                {
84                    if (_image->_active)
85                    {               
86                        int i=WaitForMessage(_client,5000);
87                        if(i<0)
88                            return;
89
90                        if(i)
91                        {
92                            OSG_INFO<<"VNC Handling "<<i<<" messages"<<std::endl;
93                       
94                            if(!HandleRFBServerMessage(_client))
95                            return;
96
97                            _image->updated();
98                        }
99                    }
100                    else
101                    {
102                        _image->_inactiveBlock->block();
103                    }
104                   
105                   
106                    double deltaTime = _image->getTimeOfLastRender() - _image->getTimeOfLastUpdate();
107                    if (deltaTime<-0.01)
108                    {
109                        //OSG_NOTICE<<"Inactive"<<std::endl;
110                        //_image->_active = false;
111                    }
112                    else
113                    {
114                        _image->_active = true;
115                    }
116
117                } while (!_done && !testCancel());
118            }
119
120            rfbClient*                      _client;
121            osg::observer_ptr<LibVncImage>  _image;
122            bool                            _done;
123
124        };
125
126    public:
127
128        rfbClient* _client;
129
130        osg::ref_ptr<RfbThread>     _rfbThread;
131     
132};
133
134LibVncImage::LibVncImage():
135    _client(0)
136{
137    // setPixelBufferObject(new osg::PixelBufferObject(this);
138
139    _inactiveBlock = new osg::RefBlock;
140
141}
142
143LibVncImage::~LibVncImage()
144{
145    close();
146}
147
148static rfbBool rfbInitConnection(rfbClient* client)
149{
150  /* Unless we accepted an incoming connection, make a TCP connection to the
151     given VNC server */
152
153  if (!client->listenSpecified) {
154    if (!client->serverHost || !ConnectToRFBServer(client,client->serverHost,client->serverPort))
155      return FALSE;
156  }
157
158  /* Initialise the VNC connection, including reading the password */
159
160  if (!InitialiseRFBConnection(client))
161    return FALSE;
162
163  if (!SetFormatAndEncodings(client))
164    return FALSE;
165
166  client->width=client->si.framebufferWidth;
167  client->height=client->si.framebufferHeight;
168  client->MallocFrameBuffer(client);
169
170  if (client->updateRect.x < 0) {
171    client->updateRect.x = client->updateRect.y = 0;
172    client->updateRect.w = client->width;
173    client->updateRect.h = client->height;
174  }
175
176  if (client->appData.scaleSetting>1)
177  {
178      if (!SendScaleSetting(client, client->appData.scaleSetting))
179          return FALSE;
180      if (!SendFramebufferUpdateRequest(client,
181                  client->updateRect.x / client->appData.scaleSetting,
182                  client->updateRect.y / client->appData.scaleSetting,
183                  client->updateRect.w / client->appData.scaleSetting,
184                  client->updateRect.h / client->appData.scaleSetting,
185                  FALSE))
186          return FALSE;
187  }
188  else
189  {
190      if (!SendFramebufferUpdateRequest(client,
191                  client->updateRect.x, client->updateRect.y,
192                  client->updateRect.w, client->updateRect.h,
193                  FALSE))
194      return FALSE;
195  }
196
197  return TRUE;
198}
199
200
201bool LibVncImage::connect(const std::string& hostname)
202{
203    if (hostname.empty()) return false;
204
205    if (_client) close();
206
207    _client = rfbGetClient(8,3,4);
208    _client->canHandleNewFBSize = TRUE;
209    _client->MallocFrameBuffer = resizeImage;
210    _client->GotFrameBufferUpdate = updateImage;
211    _client->HandleKeyboardLedState = 0;
212    _client->HandleTextChat = 0;
213
214    rfbClientSetClientData(_client, 0, this);
215   
216    _client->serverHost = strdup(hostname.c_str());
217
218    // _client->serverPort = ;
219    // _client->appData.qualityLevel = ;
220    // _client->appData.encodings = ;
221    // _client->appData.compressLevel = ;
222    // _client->appData.scaleSetting = ;
223
224    if(rfbInitConnection(_client))
225    {
226        _rfbThread = new RfbThread(_client, this);
227        _rfbThread->startThread();
228       
229        return true;
230    }
231    else
232    {
233        close();
234       
235        return false;
236    }
237}
238
239
240void LibVncImage::close()
241{
242    if (_rfbThread.valid())
243    {
244        _inactiveBlock->release();
245
246        // stop the client thread
247        _rfbThread = 0;
248    }
249
250    if (_client)
251    {
252        // close the client
253        rfbClientCleanup(_client);
254        _client = 0;
255    }
256}
257
258
259rfbBool LibVncImage::resizeImage(rfbClient* client)
260{
261    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
262   
263    int width = client->width;
264    int height = client->height;
265    int depth = client->format.bitsPerPixel;
266
267    OSG_NOTICE<<"resize "<<width<<", "<<height<<", "<<depth<<" image = "<<image<<std::endl;
268    PrintPixelFormat(&(client->format));
269
270    bool swap = client->format.redShift!=0;
271    GLenum gl_pixelFormat = swap ? GL_BGRA : GL_RGBA;
272
273    image->allocateImage(width, height, 1, gl_pixelFormat, GL_UNSIGNED_BYTE);
274    image->setInternalTextureFormat(GL_RGBA);
275
276
277   
278    client->frameBuffer= (uint8_t*)(image->data());
279   
280    return TRUE;
281}
282
283void LibVncImage::updateImage(rfbClient* client,int x,int y,int w,int h)
284{
285    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
286    image->dirty();
287}
288
289bool LibVncImage::sendPointerEvent(int x, int y, int buttonMask)
290{
291    if (_client)
292    {
293        SendPointerEvent(_client ,x, y, buttonMask);
294        return true;
295    }
296    return false;
297}
298
299bool LibVncImage::sendKeyEvent(int key, bool keyDown)
300{
301    if (_client)
302    {
303        SendKeyEvent(_client, key, keyDown ? TRUE : FALSE);
304        return true;
305    }
306    return false;
307}
308
309
310void LibVncImage::setFrameLastRendered(const osg::FrameStamp*)
311{
312    _timeOfLastRender = time();
313
314    if (!_active) _inactiveBlock->release();
315    _active = true;
316}
317
318void LibVncImage::updated()
319{
320    _timeOfLastUpdate = time();
321}
322
323class ReaderWriterVNC : public osgDB::ReaderWriter
324{
325    public:
326   
327        ReaderWriterVNC()
328        {
329            supportsExtension("vnc","VNC plugin");
330        }
331       
332        virtual const char* className() const { return "VNC plugin"; }
333
334        virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
335        {
336            return readImage(file,options);
337        }
338
339        virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
340        {
341            if (!osgDB::equalCaseInsensitive(osgDB::getFileExtension(fileName),"vnc"))
342            {
343                return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
344            }
345
346            std::string hostname = osgDB::getNameLessExtension(fileName);
347           
348            OSG_NOTICE<<"Hostname = "<<hostname<<std::endl;
349
350            osg::ref_ptr<LibVncImage> image = new LibVncImage;
351            image->setDataVariance(osg::Object::DYNAMIC);
352           
353            image->setOrigin(osg::Image::TOP_LEFT);
354
355            if (!image->connect(hostname))
356            {
357                return "Could not connect to "+hostname;
358            }
359           
360            return image.get();
361        }
362       
363        virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
364        {
365            osgDB::ReaderWriter::ReadResult result = readImage(fileName, options);
366            if (!result.validImage()) return result;
367           
368            osg::ref_ptr<osgWidget::VncClient> vncClient = new osgWidget::VncClient();
369            if (vncClient->assign(dynamic_cast<osgWidget::VncImage*>(result.getImage())))
370            {
371                return vncClient.release();
372            }
373            else
374            {
375                return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
376            }
377        }
378};
379
380// now register with Registry to instantiate the above
381// reader/writer.
382REGISTER_OSGPLUGIN(vnc, ReaderWriterVNC)
Note: See TracBrowser for help on using the browser.