#include #include #include #include #include #include #include #include #include #include #include #include #include "llmozlib2.h" class UBrowserImage; #ifdef _WINDOWS #include #elif defined(__APPLE__) #include #else extern "C" { #include } #endif ////////////////////////////////////////////////////////////////////////// // // UBrowserManager interface // class UBrowserManager : public osg::Object { public: static osg::ref_ptr& instance(); void init(const std::string& application); void* getNativeWindowHandle(); const std::string& getApplication() const { return _application; } void registerUBrowserImage(UBrowserImage* image) { OpenThreads::ScopedLock lock(_ubrowserImageListMutex); _ubrowserImageList.push_back(image); } void unregisterUBrowserImage(UBrowserImage* image) { OpenThreads::ScopedLock lock(_ubrowserImageListMutex); UBrowserImageList::iterator itr = std::find(_ubrowserImageList.begin(), _ubrowserImageList.end(), image); if (itr != _ubrowserImageList.end()) _ubrowserImageList.erase(itr); } void sendKeyEvent(UBrowserImage* image, int key, bool keyDown); void sendPointerEvent(UBrowserImage* image, int x, int y, int buttonMask); void navigateTo(UBrowserImage* image, const std::string& page); typedef std::list< UBrowserImage* > UBrowserImageList; OpenThreads::Mutex _ubrowserImageListMutex; UBrowserImageList _ubrowserImageList; protected: UBrowserManager(); UBrowserManager(const UBrowserManager& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) {} virtual ~UBrowserManager(); META_Object(osgWidget,UBrowserManager) void setUpKeyMap(); int convertToXULKey(int key) const; bool _initialized; bool _done; std::string _application; void* _nativeWindowHandle; typedef std::map KeyMap; KeyMap _keyMap; int _previousButtonMask; osg::ref_ptr _thread; }; //////////////////////////////////////////////////////////////////////////////////// // // UBrowser interface class UBrowserImage : public osg::Image, public LLEmbeddedBrowserWindowObserver { public: UBrowserImage(const std::string& homeURL, int width, int height); const std::string& getHomeURL() const { return _homeURL; } virtual void sendPointerEvent(int x, int y, int buttonMask); virtual void sendKeyEvent(int key, bool keyDown); void navigateTo(const std::string& page); //////////////////////////////////////////////////////////////////////////////// // virtual void onPageChanged( const EventType& eventIn ) { // flag that an update is required - page grab happens in idle() so we don't stall osg::notify(osg::NOTICE) << "Event: onPageChanged " << eventIn.getEventUri() << std::endl; _needsUpdate = true; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onNavigateBegin( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: begin navigation to " << eventIn.getEventUri() << std::endl; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onNavigateComplete( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: end navigation to " << eventIn.getEventUri() << " with response status of " << eventIn.getIntValue() << std::endl; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onUpdateProgress( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: progress value updated to " << eventIn.getIntValue() << std::endl; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onStatusTextChange( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: status updated to " << eventIn.getStringValue() << std::endl; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onLocationChange( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: location changed to " << eventIn.getStringValue() << std::endl; }; //////////////////////////////////////////////////////////////////////////////// // virtual void onClickLinkHref( const EventType& eventIn ) { osg::notify(osg::NOTICE) << "Event: clicked on link to " << eventIn.getStringValue() << std::endl; }; void setBrowserWindowId(int id) { _browserWindowId = id; } int getBrowserWindowId() const { return _browserWindowId; } protected: virtual ~UBrowserImage(); int _browserWindowId; bool _needsUpdate; std::string _homeURL; }; ////////////////////////////////////////////////////////////////////////// // // UBrowserManager implementation // UBrowserManager::UBrowserManager(): _initialized(false), _previousButtonMask(0) { } UBrowserManager::~UBrowserManager() { _thread->setDone(true); while(_thread->isRunning()) { OpenThreads::Thread::YieldCurrentThread(); } _thread = 0; } osg::ref_ptr& UBrowserManager::instance() { static osg::ref_ptr s_UBrowserManager = new UBrowserManager; return s_UBrowserManager; } #if defined(_WINDOWS) void* UBrowserManager::getNativeWindowHandle() { if (_nativeWindowHandle) return _nativeWindowHandle; // My implementation of the embedded browser needs a native window handle // Can't get this via GLUT so had to use this hack _nativeWindowHandle = FindWindow( NULL, _appWindowName.c_str() ); return _nativeWindowHandle; } #elif defined(__APPLE__) void* UBrowserManager::getNativeWindowHandle() { if (_nativeWindowHandle) return _nativeWindowHandle; // Create a window just for this purpose. Rect window_rect = {100, 100, 200, 200}; _nativeWindowHandle = (void*) NewCWindow( NULL, &window_rect, "\p", false, // Create the window invisible. zoomDocProc, // Window with a grow box and a zoom box kLastWindowOfClass, // create it behind other windows false, // no close box 0); } return _nativeWindowHandle; } #else void* UBrowserManager::getNativeWindowHandle() { if (_nativeWindowHandle) return _nativeWindowHandle; gtk_disable_setlocale(); gtk_init(NULL, NULL); GtkWidget *win = gtk_window_new(GTK_WINDOW_POPUP); // Why a layout widget? A MozContainer would be ideal, but // it involves exposing Mozilla headers to mozlib-using apps. // A layout widget with a GtkWindow parent has the desired // properties of being plain GTK, having a window, and being // derived from a GtkContainer. GtkWidget *rtnw = gtk_layout_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(win), rtnw); gtk_widget_realize(rtnw); GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(rtnw), GTK_NO_WINDOW); _nativeWindowHandle = rtnw; return _nativeWindowHandle; } #endif struct InitOperation : public osg::Operation { InitOperation(): Operation("init",false) {} /** Override the standard Operation operator and dynamic cast object to a GraphicsContext, * on success call operation()(GraphicsContext*).*/ virtual void operator () (osg::Object* object) { UBrowserManager* ubrowserManager = dynamic_cast(object); // create a single browser window and set things up. std::string applicationDir = osgDB::getFilePath(ubrowserManager->getApplication()); if (applicationDir.empty()) applicationDir = osgDB::getRealPath("."); else applicationDir = osgDB::getRealPath(applicationDir); std::string componentDir = "/usr/lib/xulrunner"; std::string profileDir = applicationDir + "/" + "testGL_profile"; LLMozLib::getInstance()->init( applicationDir, componentDir, profileDir, ubrowserManager->getNativeWindowHandle() ); // append details to agent string LLMozLib::getInstance()->setBrowserAgentId( ubrowserManager->getApplication() ); } }; struct UpdateOperation : public osg::Operation { UpdateOperation(): osg::Operation("update",true) {} virtual void operator () (osg::Object* object) { UBrowserManager* ubrowserManager = dynamic_cast(object); // osg::notify(osg::NOTICE)<<"Update"<_ubrowserImageList.empty()) { // osg::notify(osg::NOTICE)<<"Nothing to do"< > RefImageList; RefImageList images; { OpenThreads::ScopedLock lock(ubrowserManager->_ubrowserImageListMutex); std::copy(ubrowserManager->_ubrowserImageList.begin(), ubrowserManager->_ubrowserImageList.end(), std::back_inserter(images)); } for(RefImageList::iterator itr = images.begin(); itr != images.end(); ++itr) { update(itr->get()); } // osg::notify(osg::NOTICE)<<"complted Update"<getBrowserWindowId(); if (id==0) { int width = image->s(); int height = image->t(); osg::notify(osg::INFO)<<"Constructing browser window for first time, width = "<getBrowserWindowId(); // send event to LLMozLib if (_buttonDelta>0) { LLMozLib::getInstance()->mouseDown( id, _x, _y ); } else if (_buttonDelta<0) { LLMozLib::getInstance()->mouseUp( id, _x, _y ); // this seems better than sending focus on mouse down (still need to improve this) LLMozLib::getInstance()->focusBrowser( id, true ); } else { // send event to LLMozLib LLMozLib::getInstance()->mouseMove( id, _x, _y ); } } osg::ref_ptr _image; int _x; int _y; int _buttonDelta; }; void UBrowserManager::sendPointerEvent(UBrowserImage* image, int x, int y, int buttonMask) { int deltaButton = (buttonMask&1) - (_previousButtonMask&1); _previousButtonMask = buttonMask; _thread->add(new PointerEventOperation(image, x, y, deltaButton)); } struct KeyEventOperation : public osg::Operation { KeyEventOperation(UBrowserImage* image, int key, bool isUnicode): osg::Operation("key event",false), _image(image), _key(key), _isUnicode(isUnicode) {} virtual void operator () (osg::Object* object) { int id = _image->getBrowserWindowId(); if (_isUnicode) LLMozLib::getInstance()->unicodeInput( id, _key ); else LLMozLib::getInstance()->keyPress( id, _key ); } osg::ref_ptr _image; int _key; bool _isUnicode; }; void UBrowserManager::sendKeyEvent(UBrowserImage* image, int key, bool keyDown) { if (!keyDown) return; KeyMap::const_iterator itr = _keyMap.find(key); if (_keyMap.find(key)==_keyMap.end()) _thread->add(new KeyEventOperation(image, key, true)); else _thread->add(new KeyEventOperation(image, itr->second, false)); } struct NavigateToOperation : public osg::Operation { NavigateToOperation(UBrowserImage* image, const std::string& url): osg::Operation("key event",false), _image(image), _url(url) {} virtual void operator () (osg::Object* object) { int id = _image->getBrowserWindowId(); LLMozLib::getInstance()->navigateTo( id, _url ); } osg::ref_ptr _image; std::string _url; }; void UBrowserManager::navigateTo(UBrowserImage* image, const std::string& url) { _thread->add(new NavigateToOperation(image, url)); } //////////////////////////////////////////////////////////////////////////////////// // // UBrowser implementation UBrowserImage::UBrowserImage(const std::string& homeURL, int width, int height): _browserWindowId(0), _needsUpdate(true) { GLint internalFormat = GL_RGB; GLenum pixelFormat = GL_BGR_EXT; setImage(width,height,1, internalFormat, pixelFormat, GL_UNSIGNED_BYTE, 0, osg::Image::NO_DELETE); setDataVariance(osg::Object::DYNAMIC); setOrigin(osg::Image::TOP_LEFT); _homeURL = homeURL; UBrowserManager::instance()->registerUBrowserImage(this); } UBrowserImage::~UBrowserImage() { UBrowserManager::instance()->unregisterUBrowserImage(this); } void UBrowserImage::sendPointerEvent(int x, int y, int buttonMask) { UBrowserManager::instance()->sendPointerEvent(this, x, y, buttonMask); } void UBrowserImage::sendKeyEvent(int key, bool keyDown) { UBrowserManager::instance()->sendKeyEvent(this, key, keyDown); } void UBrowserImage::navigateTo(const std::string& url) { UBrowserManager::instance()->navigateTo(this, url); } osg::Node* createInteractiveQuad(const osg::Vec3& origin, osg::Vec3& widthAxis, osg::Vec3& heightAxis, osg::Image* image) { bool flip = image->getOrigin()==osg::Image::TOP_LEFT; osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(origin, widthAxis, heightAxis, 0.0f, flip ? 1.0f : 0.0f , 1.0f, flip ? 0.0f : 1.0f); osg::Texture2D* texture = new osg::Texture2D(image); texture->setResizeNonPowerOfTwoHint(false); texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); pictureQuad->setEventCallback(new osgViewer::InteractiveImageHandler(image)); osg::Geode* geode = new osg::Geode; geode->addDrawable(pictureQuad); return geode; } int main( int argc, char* argv[] ) { osg::ArgumentParser arguments(&argc, argv); UBrowserManager::instance()->init(arguments[0]); osgViewer::Viewer viewer(arguments); typedef std::list< osg::ref_ptr > Images; Images images; for(int i=1; iget(); float width = 1.0; float height = float(image->t())/float(image->s()); osg::Vec3 widthAxis = osg::Vec3(width,0.0f,0.0f); osg::Vec3 heightAxis = xyPlane ? osg::Vec3(0.0f,height,0.0f) : osg::Vec3(0.0f,0.0f,height); group->addChild(createInteractiveQuad(origin, widthAxis, heightAxis, image)); origin += widthAxis*1.1f; } viewer.setSceneData(group); viewer.addEventHandler(new osgViewer::StatsHandler); return viewer.run(); }