377 lines
9.9 KiB
C++
377 lines
9.9 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1999-2008 Robert Osfield
|
|
*
|
|
* This software is open source and may be redistributed and/or modified under
|
|
* the terms of the GNU General Public License (GPL) version 2.0.
|
|
* The full license is in LICENSE.txt file included with this distribution,.
|
|
*
|
|
* This software is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* include LICENSE.txt for more details.
|
|
*/
|
|
|
|
#include <osg/Timer>
|
|
|
|
#include <osgDB/ReaderWriter>
|
|
#include <osgDB/FileNameUtils>
|
|
#include <osgDB/Registry>
|
|
|
|
#include <osgWidget/VncClient>
|
|
|
|
extern "C" {
|
|
#include <rfb/rfbclient.h>
|
|
}
|
|
|
|
class LibVncImage : public osgWidget::VncImage
|
|
{
|
|
public:
|
|
|
|
LibVncImage();
|
|
|
|
bool connect(const std::string& hostname);
|
|
|
|
void close();
|
|
|
|
virtual bool sendPointerEvent(int x, int y, int buttonMask);
|
|
|
|
double getTimeOfLastUpdate() const { return _timeOfLastUpdate; }
|
|
double getTimeOfLastRender() const { return _timeOfLastRender; }
|
|
|
|
double time() const { return osg::Timer::instance()->time_s(); }
|
|
|
|
virtual bool sendKeyEvent(int key, bool keyDown);
|
|
|
|
virtual void setFrameLastRendered(const osg::FrameStamp* frameStamp);
|
|
|
|
void updated();
|
|
|
|
static rfbBool resizeImage(rfbClient* client);
|
|
|
|
static void updateImage(rfbClient* client,int x,int y,int w,int h);
|
|
|
|
double _timeOfLastUpdate;
|
|
double _timeOfLastRender;
|
|
|
|
bool _active;
|
|
osg::ref_ptr<osg::RefBlock> _inactiveBlock;
|
|
|
|
protected:
|
|
|
|
virtual ~LibVncImage();
|
|
|
|
class RfbThread : public osg::Referenced, public OpenThreads::Thread
|
|
{
|
|
public:
|
|
|
|
RfbThread(rfbClient* client, LibVncImage* image):
|
|
_client(client),
|
|
_image(image),
|
|
_done(false) {}
|
|
|
|
virtual ~RfbThread()
|
|
{
|
|
_done = true;
|
|
while(isRunning())
|
|
{
|
|
OpenThreads::Thread::YieldCurrentThread();
|
|
}
|
|
}
|
|
|
|
virtual void run()
|
|
{
|
|
do
|
|
{
|
|
if (_image->_active)
|
|
{
|
|
int i=WaitForMessage(_client,5000);
|
|
if(i<0)
|
|
return;
|
|
|
|
if(i)
|
|
{
|
|
OSG_NOTICE<<"Handling "<<i<<" messages"<<std::endl;
|
|
|
|
if(!HandleRFBServerMessage(_client))
|
|
return;
|
|
|
|
_image->updated();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_image->_inactiveBlock->block();
|
|
}
|
|
|
|
|
|
double deltaTime = _image->getTimeOfLastRender() - _image->getTimeOfLastUpdate();
|
|
if (deltaTime<-0.01)
|
|
{
|
|
//OSG_NOTICE<<"Inactive"<<std::endl;
|
|
//_image->_active = false;
|
|
}
|
|
else
|
|
{
|
|
_image->_active = true;
|
|
}
|
|
|
|
} while (!_done && !testCancel());
|
|
}
|
|
|
|
rfbClient* _client;
|
|
osg::observer_ptr<LibVncImage> _image;
|
|
bool _done;
|
|
|
|
};
|
|
|
|
public:
|
|
|
|
rfbClient* _client;
|
|
|
|
osg::ref_ptr<RfbThread> _rfbThread;
|
|
|
|
};
|
|
|
|
LibVncImage::LibVncImage():
|
|
_client(0)
|
|
{
|
|
// setPixelBufferObject(new osg::PixelBufferObject(this);
|
|
|
|
_inactiveBlock = new osg::RefBlock;
|
|
|
|
}
|
|
|
|
LibVncImage::~LibVncImage()
|
|
{
|
|
close();
|
|
}
|
|
|
|
static rfbBool rfbInitConnection(rfbClient* client)
|
|
{
|
|
/* Unless we accepted an incoming connection, make a TCP connection to the
|
|
given VNC server */
|
|
|
|
if (!client->listenSpecified) {
|
|
if (!client->serverHost || !ConnectToRFBServer(client,client->serverHost,client->serverPort))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialise the VNC connection, including reading the password */
|
|
|
|
if (!InitialiseRFBConnection(client))
|
|
return FALSE;
|
|
|
|
if (!SetFormatAndEncodings(client))
|
|
return FALSE;
|
|
|
|
client->width=client->si.framebufferWidth;
|
|
client->height=client->si.framebufferHeight;
|
|
client->MallocFrameBuffer(client);
|
|
|
|
if (client->updateRect.x < 0) {
|
|
client->updateRect.x = client->updateRect.y = 0;
|
|
client->updateRect.w = client->width;
|
|
client->updateRect.h = client->height;
|
|
}
|
|
|
|
if (client->appData.scaleSetting>1)
|
|
{
|
|
if (!SendScaleSetting(client, client->appData.scaleSetting))
|
|
return FALSE;
|
|
if (!SendFramebufferUpdateRequest(client,
|
|
client->updateRect.x / client->appData.scaleSetting,
|
|
client->updateRect.y / client->appData.scaleSetting,
|
|
client->updateRect.w / client->appData.scaleSetting,
|
|
client->updateRect.h / client->appData.scaleSetting,
|
|
FALSE))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!SendFramebufferUpdateRequest(client,
|
|
client->updateRect.x, client->updateRect.y,
|
|
client->updateRect.w, client->updateRect.h,
|
|
FALSE))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
bool LibVncImage::connect(const std::string& hostname)
|
|
{
|
|
if (hostname.empty()) return false;
|
|
|
|
if (_client) close();
|
|
|
|
_client = rfbGetClient(8,3,4);
|
|
_client->canHandleNewFBSize = TRUE;
|
|
_client->MallocFrameBuffer = resizeImage;
|
|
_client->GotFrameBufferUpdate = updateImage;
|
|
_client->HandleKeyboardLedState = 0;
|
|
_client->HandleTextChat = 0;
|
|
|
|
rfbClientSetClientData(_client, 0, this);
|
|
|
|
_client->serverHost = strdup(hostname.c_str());
|
|
|
|
// _client->serverPort = ;
|
|
// _client->appData.qualityLevel = ;
|
|
// _client->appData.encodings = ;
|
|
// _client->appData.compressLevel = ;
|
|
// _client->appData.scaleSetting = ;
|
|
|
|
if(rfbInitConnection(_client))
|
|
{
|
|
_rfbThread = new RfbThread(_client, this);
|
|
_rfbThread->startThread();
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
close();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void LibVncImage::close()
|
|
{
|
|
if (_rfbThread.valid())
|
|
{
|
|
_inactiveBlock->release();
|
|
|
|
// stop the client thread
|
|
_rfbThread = 0;
|
|
}
|
|
|
|
if (_client)
|
|
{
|
|
// close the client
|
|
rfbClientCleanup(_client);
|
|
_client = 0;
|
|
}
|
|
}
|
|
|
|
|
|
rfbBool LibVncImage::resizeImage(rfbClient* client)
|
|
{
|
|
osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
|
|
|
|
int width=client->width;
|
|
int height=client->height;
|
|
int depth=client->format.bitsPerPixel;
|
|
|
|
OSG_NOTICE<<"resize "<<width<<", "<<height<<", "<<depth<<" image = "<<image<<std::endl;
|
|
|
|
image->allocateImage(width,height,1,GL_RGBA,GL_UNSIGNED_BYTE);
|
|
|
|
client->frameBuffer= (uint8_t*)(image->data());
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LibVncImage::updateImage(rfbClient* client,int x,int y,int w,int h)
|
|
{
|
|
osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
|
|
image->dirty();
|
|
}
|
|
|
|
bool LibVncImage::sendPointerEvent(int x, int y, int buttonMask)
|
|
{
|
|
if (_client)
|
|
{
|
|
SendPointerEvent(_client ,x, y, buttonMask);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LibVncImage::sendKeyEvent(int key, bool keyDown)
|
|
{
|
|
if (_client)
|
|
{
|
|
SendKeyEvent(_client, key, keyDown ? TRUE : FALSE);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void LibVncImage::setFrameLastRendered(const osg::FrameStamp*)
|
|
{
|
|
_timeOfLastRender = time();
|
|
|
|
if (!_active) _inactiveBlock->release();
|
|
_active = true;
|
|
}
|
|
|
|
void LibVncImage::updated()
|
|
{
|
|
_timeOfLastUpdate = time();
|
|
}
|
|
|
|
class ReaderWriterVNC : public osgDB::ReaderWriter
|
|
{
|
|
public:
|
|
|
|
ReaderWriterVNC()
|
|
{
|
|
supportsExtension("vnc","VNC plugin");
|
|
}
|
|
|
|
virtual const char* className() const { return "VNC plugin"; }
|
|
|
|
virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
|
{
|
|
return readImage(file,options);
|
|
}
|
|
|
|
virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
|
|
{
|
|
if (!osgDB::equalCaseInsensitive(osgDB::getFileExtension(fileName),"vnc"))
|
|
{
|
|
return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
|
|
}
|
|
|
|
std::string hostname = osgDB::getNameLessExtension(fileName);
|
|
|
|
OSG_NOTICE<<"Hostname = "<<hostname<<std::endl;
|
|
|
|
osg::ref_ptr<LibVncImage> image = new LibVncImage;
|
|
image->setDataVariance(osg::Object::DYNAMIC);
|
|
|
|
image->setOrigin(osg::Image::TOP_LEFT);
|
|
|
|
if (!image->connect(hostname))
|
|
{
|
|
return "Could not connect to "+hostname;
|
|
}
|
|
|
|
return image.get();
|
|
}
|
|
|
|
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
|
|
{
|
|
osgDB::ReaderWriter::ReadResult result = readImage(fileName, options);
|
|
if (!result.validImage()) return result;
|
|
|
|
osg::ref_ptr<osgWidget::VncClient> vncClient = new osgWidget::VncClient();
|
|
if (vncClient->assign(dynamic_cast<osgWidget::VncImage*>(result.getImage())))
|
|
{
|
|
return vncClient.release();
|
|
}
|
|
else
|
|
{
|
|
return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
|
|
}
|
|
}
|
|
};
|
|
|
|
// now register with Registry to instantiate the above
|
|
// reader/writer.
|
|
REGISTER_OSGPLUGIN(vnc, ReaderWriterVNC)
|
|
|