From 745845f79987e4b4ab7f5728746a0eda8898930f Mon Sep 17 00:00:00 2001 From: Monroe Williams Date: Thu, 27 Aug 2009 19:00:18 +0000 Subject: svn merge -r 129841:129910 svn+ssh://svn.lindenlab.com/svn/linden/branches/moss/pluginapi_05-merge@129910 svn merge -r 129913:131718 svn+ssh://svn.lindenlab.com/svn/linden/branches/pluginapi/pluginapi_05 Some branch shenannigans in the pluginapi_05 branch caused this to become a two-part merge. --- indra/test_apps/llplugintest/llmediaplugintest.cpp | 2168 ++++++++++++++++++++ 1 file changed, 2168 insertions(+) create mode 100644 indra/test_apps/llplugintest/llmediaplugintest.cpp (limited to 'indra/test_apps/llplugintest/llmediaplugintest.cpp') diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp new file mode 100644 index 0000000000..7a84ef83cb --- /dev/null +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -0,0 +1,2168 @@ +/** + * @file LLMediaPluginTest2.cpp + * @brief Primary test application for LLMedia (Separate Process) Plugin system + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llapr.h" +#include "llerrorcontrol.h" + +#include +#include +#include +#include + +#include "llmediaplugintest.h" + +#if __APPLE__ + #include +#else + #define FREEGLUT_STATIC + #include "GL/freeglut.h" + #define GLUI_FREEGLUT +#endif + +#if LL_WINDOWS +#pragma warning(disable: 4263) +#pragma warning(disable: 4264) +#endif +#include "glui.h" + + +LLMediaPluginTest* gApplication = 0; +static void gluiCallbackWrapper( int control_id ); + +//////////////////////////////////////////////////////////////////////////////// +// +static bool isTexture( GLuint texture ) +{ + bool result = false; + + // glIsTexture will sometimes return false for real textures... do this instead. + if(texture != 0) + result = true; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::mediaPanel() +{ + mMediaTextureHandle = 0; + mPickTextureHandle = 0; + mMediaSource = NULL; + mPickTexturePixels = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::~mediaPanel() +{ + // delete OpenGL texture handles + if ( isTexture( mPickTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting pick texture " << mPickTextureHandle << std::endl; + glDeleteTextures( 1, &mPickTextureHandle ); + mPickTextureHandle = 0; + } + + if ( isTexture( mMediaTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting media texture " << mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &mMediaTextureHandle ); + mMediaTextureHandle = 0; + } + + if(mPickTexturePixels) + { + delete mPickTexturePixels; + } + + if(mMediaSource) + { + delete mMediaSource; + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMediaPluginTest::LLMediaPluginTest( int app_window, int window_width, int window_height ) : + mVersionMajor( 2 ), + mVersionMinor( 0 ), + mVersionPatch( 0 ), + mMaxPanels( 16 ), + mViewportAspect( 0 ), + mAppWindow( app_window ), + mCurMouseX( 0 ), + mCurMouseY( 0 ), + mFuzzyMedia( true ), + mSelectedPanel( 0 ), + mMediaBrowserControlEnableCookies( 0 ), + mMediaBrowserControlBackButton( 0 ), + mMediaBrowserControlForwardButton( 0 ), + mMediaTimeControlVolume( 100 ), + mMediaTimeControlSeekSeconds( 0 ), + mGluiMediaTimeControlWindowFlag( true ), + mGluiMediaBrowserControlWindowFlag( true ), + mMediaBrowserControlBackButtonFlag( true ), + mMediaBrowserControlForwardButtonFlag( true ), + mHomeWebUrl( "http://www.google.com/" ) +{ + // debugging spam + std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT + std::cout << std::endl << " GLUI version: " << GLUI_Master.get_version() << std::endl; + std::cout << std::endl << "Media Plugin Test version: " << mVersionMajor << "." << mVersionMinor << "." << mVersionPatch << std::endl; + + // bookmark title + mBookmarks.push_back( std::pair< std::string, std::string >( "--- Bookmarks ---", "" ) ); + + // insert hardcoded URLs here as required for testing + //mBookmarks.push_back( std::pair< std::string, std::string >( "description", "url" ) ); + + // read bookmarks from file. + // note: uses command in ./CmakeLists.txt which copies bookmmarks file from source directory + // to app directory (WITHOUT build configuration dir) (this is cwd in Windows within MSVC) + // For example, test_apps\llplugintest and not test_apps\llplugintest\Release + // This may need to be changed for Mac/Linux builds. + // See https://jira.lindenlab.com/browse/DEV-31350 for large list of media URLs from AGNI + const std::string bookmarks_filename( "bookmarks.txt" ); + std::ifstream file_handle( bookmarks_filename.c_str() ); + if ( file_handle.is_open() ) + { + std::cout << "Reading bookmarks for test" << std::endl; + while( ! file_handle.eof() ) + { + std::string line; + std::getline( file_handle, line ); + if ( file_handle.eof() ) + break; + + if ( line.substr( 0, 1 ) != "#" ) + { + size_t comma_pos = line.find_first_of( ',' ); + if ( comma_pos != std::string::npos ) + { + std::string description = line.substr( 0, comma_pos ); + std::string url = line.substr( comma_pos + 1 ); +#if LL_DARWIN || LL_LINUX + // Don't load flash movies on mac or linux yet. + if ( url.find( ".swf" ) != std::string::npos ) + { + continue; + } +#endif + mBookmarks.push_back( std::pair< std::string, std::string >( description, url ) ); + } + else + { + mBookmarks.push_back( std::pair< std::string, std::string >( line, line ) ); + }; + }; + }; + std::cout << "Read " << mBookmarks.size() << " bookmarks" << std::endl; + } + else + { + std::cout << "Unable to read bookmarks from file: " << bookmarks_filename << std::endl; + }; + + // initialize linden lab APR module + ll_init_apr(); + + // Set up llerror logging + { + LLError::initForApplication("."); + LLError::setDefaultLevel(LLError::LEVEL_INFO); +// LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG); + } + + // lots of randomness in this app + srand( ( unsigned int )time( 0 ) ); + + // build GUI + makeChrome(); + + // OpenGL initialilzation + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClearDepth( 1.0f ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); + glDepthFunc( GL_LEQUAL ); + glEnable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + glColor3f( 1.0f, 1.0f, 1.0f ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + + // start with a sane view + resetView(); + + // initial media panel + const int num_initial_panels = 4; + for( int i = 0; i < num_initial_panels; ++i ) + { + //addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + }; + + // always add a Web panel for testing + addMediaPanel( "http://www.google.com" ); + //addMediaPanel( "http://www.wiicade.com/Data/72/game.swf" ); + //addMediaPanel( "http://movies.apple.com/movies/wb/watchmen/watchmen-tlr2_480p.mov" ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMediaPluginTest::~LLMediaPluginTest() +{ + // delete all media panels + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + remMediaPanel( mMediaPanels[ i ] ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::reshape( int width, int height ) +{ + // update viewport (the active window inside the chrome) + int viewport_x, viewport_y; + int viewport_height, viewport_width; + GLUI_Master.get_viewport_area( &viewport_x, &viewport_y, &viewport_width, &viewport_height ); + mViewportAspect = (float)( viewport_width ) / (float)( viewport_height ); + glViewport( viewport_x, viewport_y, viewport_width, viewport_height ); + + // save these as we'll need them later + mWindowWidth = width; + mWindowHeight = height; + + // adjust size of URL bar so it doesn't get clipped + mUrlEdit->set_w( mWindowWidth - 360 ); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::bindTexture(GLuint texture, GLint row_length, GLint alignment) +{ + glEnable( GL_TEXTURE_2D ); + +// std::cerr << "binding texture " << texture << std::endl; + + glBindTexture( GL_TEXTURE_2D, texture ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, row_length ); + glPixelStorei( GL_UNPACK_ALIGNMENT, alignment ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMediaPluginTest::checkGLError(char *name) +{ + bool result = false; + GLenum error = glGetError(); + + if(error != GL_NO_ERROR) + { + // For some reason, glGenTextures is returning GL_INVALID_VALUE... + std::cout << name << " ERROR 0x" << std::hex << error << std::dec << std::endl; + result = true; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::drawGeometry( int panel ) +{ + // texture coordinates for each panel + GLfloat non_opengl_texture_coords[ 8 ] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; + GLfloat opengl_texture_coords[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + GLfloat *texture_coords = mMediaPanels[ panel ]->mAppTextureCoordsOpenGL?opengl_texture_coords:non_opengl_texture_coords; + + // base coordinates for each panel + GLfloat base_vertex_pos[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + // calculate posiitons + const int num_panels = (int)mMediaPanels.size(); + const int num_rows = (int)sqrt( (float)num_panels ); + const int num_cols = num_panels / num_rows; + const int panel_x = ( panel / num_rows ); + const int panel_y = ( panel % num_rows ); + + const float spacing = 0.1f; + const GLfloat offset_x = num_cols * ( 1.0 + spacing ) / 2; + const GLfloat offset_y = num_rows * ( 1.0 + spacing ) / 2; + + // Adjust for media aspect ratios + { + float aspect = 1.0f; + + if(mMediaPanels[ panel ]->mMediaHeight != 0) + { + aspect = (float)mMediaPanels[ panel ]->mMediaWidth / (float)mMediaPanels[ panel ]->mMediaHeight; + } + + if(aspect > 1.0f) + { + // media is wider than it is high -- adjust the top and bottom in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2 + 1]; + + if(temp < 0.5f) + temp += 0.5 - (0.5f / aspect); + else + temp -= 0.5 - (0.5f / aspect); + + base_vertex_pos[corner * 2 + 1] = temp; + } + } + else if(aspect < 1.0f) + { + // media is higher than it is wide -- adjust the left and right sides in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2]; + + if(temp < 0.5f) + temp += 0.5f - (0.5f * aspect); + else + temp -= 0.5f - (0.5f * aspect); + + base_vertex_pos[corner * 2] = temp; + } + } + } + + glBegin( GL_QUADS ); + for( int corner = 0; corner < 4; ++corner ) + { + glTexCoord2f( texture_coords[ corner * 2 ], texture_coords[ corner * 2 + 1 ] ); + GLfloat x = base_vertex_pos[ corner * 2 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; + GLfloat y = base_vertex_pos[ corner * 2 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; + + glVertex3f( x, y, 0.0f ); + }; + glEnd(); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::startPanelHighlight( float red, float green, float blue, float line_width ) +{ + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( -2.5f, -2.5f ); + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + glLineWidth( line_width ); + glColor3f( red, green, blue ); + glDisable( GL_TEXTURE_2D ); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::endPanelHighlight() +{ + glPopAttrib(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::draw( int draw_type ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + // drawing pick texture + if ( draw_type == DrawTypePickTexture ) + { + // only bother with pick if we have something to render + // Actually, we need to pick even if we're not ready to render. + // Otherwise you can't select and remove a panel which has gone bad. +// if ( mMediaPanels[ panel ]->mReadyToRender ) + { + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // pick texture is a power of 2 so no need to scale + glLoadIdentity(); + + // bind to media texture + glLoadIdentity(); + bindTexture( mMediaPanels[ panel ]->mPickTextureHandle ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // draw geometry using pick texture + drawGeometry( panel ); + + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + } + else + if ( draw_type == DrawTypeMediaTexture ) + { + bool texture_valid = false; + bool plugin_exited = false; + + if(mMediaPanels[ panel ]->mMediaSource) + { + texture_valid = mMediaPanels[ panel ]->mMediaSource->textureValid(); + plugin_exited = mMediaPanels[ panel ]->mMediaSource->isPluginExited(); + } + + // save texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // only process texture if the media is ready to draw + // (we still want to draw the geometry) + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // bind to media texture + bindTexture( mMediaPanels[ panel ]->mMediaTextureHandle ); + + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + } + + // scale to fit panel + glScalef( mMediaPanels[ panel ]->mTextureScaleX, + mMediaPanels[ panel ]->mTextureScaleY, + 1.0f ); + }; + + float intensity = plugin_exited?0.25f:1.0f; + + // highlight the selected panel + if ( mSelectedPanel && ( mMediaPanels[ panel ]->mId == mSelectedPanel->mId ) ) + { + startPanelHighlight( intensity, intensity, 0.0f, 5.0f ); + drawGeometry( panel ); + endPanelHighlight(); + } + else + // this panel not able to render yet since it + // doesn't have enough information + if ( !mMediaPanels[ panel ]->mReadyToRender ) + { + startPanelHighlight( intensity, 0.0f, 0.0f, 2.0f ); + drawGeometry( panel ); + endPanelHighlight(); + } + else + // just display a border around the media + { + startPanelHighlight( 0.0f, intensity, 0.0f, 2.0f ); + drawGeometry( panel ); + endPanelHighlight(); + }; + + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // draw visual geometry + drawGeometry( panel ); + } + + // restore texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::display() +{ + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // start with a clean slate + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // set up OpenGL view + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -mViewportAspect * 0.04f, mViewportAspect * 0.04f, -0.04f, 0.04f, 0.1f, 50.0f ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, 0.0f ); + glTranslatef( mViewPos[ 0 ], mViewPos[ 1 ], -mViewPos[ 2 ] ); + glMultMatrixf( mViewRotation ); + + // draw pick texture + draw( DrawTypePickTexture ); + + // read colors and get coordinate values + glReadPixels( mCurMouseX, mCurMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, mPixelReadColor ); + + // clear the pick render (otherwise it may depth-fight with the textures rendered later) + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // draw visible geometry + draw( DrawTypeMediaTexture ); + + glutSwapBuffers(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::idle() +{ +// checkGLError("LLMediaPluginTest::idle"); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // random creation/destruction of panels enabled? + const time_t panel_timeout_time = 5; + if ( mRandomPanelCount ) + { + // time for a change + static time_t last_panel_time = 0; + if ( time( NULL ) - last_panel_time > panel_timeout_time ) + { + if ( rand() % 2 == 0 ) + { + if ( mMediaPanels.size() < 16 ) + { + std::cout << "Randomly adding new panel" << std::endl; + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + }; + } + else + { + if ( mMediaPanels.size() > 0 ) + { + std::cout << "Deleting selected panel" << std::endl; + remMediaPanel( mSelectedPanel ); + }; + }; + time( &last_panel_time ); + }; + }; + + // random selection of bookmarks enabled? + const time_t bookmark_timeout_time = 5; + if ( mRandomBookmarks ) + { + // time for a change + static time_t last_bookmark_time = 0; + if ( time( NULL ) - last_bookmark_time > bookmark_timeout_time ) + { + // go to a different random bookmark on each panel + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + std::string uri = mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second; + + std::cout << "Random: navigating to : " << uri << std::endl; + + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( mime_type != mMediaPanels[ panel ]->mMimeType ) + { + replaceMediaPanel( mMediaPanels[ panel ], uri ); + } + else + { + mMediaPanels[ panel ]->mMediaSource->loadURI( uri ); + mMediaPanels[ panel ]->mMediaSource->start(); + }; + }; + + time( &last_bookmark_time ); + }; + }; + + // update UI + if ( mSelectedPanel ) + { + // set volume based on slider if we have time media +// if ( mGluiMediaTimeControlWindowFlag ) +// { +// mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); +// }; + + // NOTE: it is absurd that we need cache the state of GLUI controls + // but enabling/disabling controls drags framerate from 500+ + // down to 15. Not a problem for plugin system - only this test + // enable/disable time based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + if ( ! mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->enable(); + mGluiMediaTimeControlWindowFlag = true; + }; + } + else + { + if ( mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->disable(); + mGluiMediaTimeControlWindowFlag = false; + }; + }; + + // enable/disable browser based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + { + if ( ! mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->enable(); + mGluiMediaBrowserControlWindowFlag = true; + }; + } + else + { + if ( mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->disable(); + mGluiMediaBrowserControlWindowFlag = false; + }; + }; + + // enable/disable browser back button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryBackAvailable() ) + { + if ( ! mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->enable(); + mMediaBrowserControlBackButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->disable(); + mMediaBrowserControlBackButtonFlag = false; + }; + }; + + // enable/disable browser forward button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryForwardAvailable() ) + { + if ( ! mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->enable(); + mMediaBrowserControlForwardButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->disable(); + mMediaBrowserControlForwardButtonFlag = false; + }; + }; + + // NOTE: This is *very* slow and not worth optimising + updateStatusBar(); + }; + + // update all the panels + for( int panel_index = 0; panel_index < (int)mMediaPanels.size(); ++panel_index ) + { + mediaPanel *panel = mMediaPanels[ panel_index ]; + + // call plugins idle function so it can potentially update itself + panel->mMediaSource->idle(); + + // update each media panel + updateMediaPanel( panel ); + + LLRect dirty_rect; + if ( ! panel->mMediaSource->textureValid() ) + { + //std::cout << "texture invalid, skipping update..." << std::endl; + } + else + if ( panel && + ( panel->mMediaWidth != panel->mMediaSource->getWidth() || + panel->mMediaHeight != panel->mMediaSource->getHeight() ) ) + { + //std::cout << "Resize in progress, skipping update..." << std::endl; + } + else + if ( panel->mMediaSource->getDirty( &dirty_rect ) ) + { + const unsigned char* pixels = panel->mMediaSource->getBitsData(); + if ( pixels && isTexture(panel->mMediaTextureHandle)) + { + int x_offset = dirty_rect.mLeft; + int y_offset = dirty_rect.mBottom; + int width = dirty_rect.mRight - dirty_rect.mLeft; + int height = dirty_rect.mTop - dirty_rect.mBottom; + + if((dirty_rect.mRight <= panel->mTextureWidth) && (dirty_rect.mTop <= panel->mTextureHeight)) + { + // Offset the pixels pointer properly + pixels += ( y_offset * panel->mMediaSource->getTextureDepth() * panel->mMediaSource->getBitsWidth() ); + pixels += ( x_offset * panel->mMediaSource->getTextureDepth() ); + + // set up texture + bindTexture( panel->mMediaTextureHandle, panel->mMediaSource->getBitsWidth() ); + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + }; + + checkGLError("glTexParameteri"); + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + checkGLError("glPixelStorei"); + } + + // draw portion that changes into texture + glTexSubImage2D( GL_TEXTURE_2D, 0, + x_offset, + y_offset, + width, + height, + panel->mMediaSource->getTextureFormatPrimary(), + panel->mMediaSource->getTextureFormatType(), + pixels ); + + if(checkGLError("glTexSubImage2D")) + { + std::cerr << " panel ID=" << panel->mId << std::endl; + std::cerr << " texture size = " << panel->mTextureWidth << " x " << panel->mTextureHeight << std::endl; + std::cerr << " media size = " << panel->mMediaWidth << " x " << panel->mMediaHeight << std::endl; + std::cerr << " dirty rect = " << dirty_rect.mLeft << ", " << dirty_rect.mBottom << ", " << dirty_rect.mRight << ", " << dirty_rect.mTop << std::endl; + std::cerr << " texture width = " << panel->mMediaSource->getBitsWidth() << std::endl; + std::cerr << " format primary = 0x" << std::hex << panel->mMediaSource->getTextureFormatPrimary() << std::dec << std::endl; + std::cerr << " format type = 0x" << std::hex << panel->mMediaSource->getTextureFormatType() << std::dec << std::endl; + std::cerr << " pixels = " << (void*)pixels << std::endl; + } + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); + checkGLError("glPixelStorei"); + } + + panel->mMediaSource->resetDirty(); + + panel->mReadyToRender = true; + } + else + { + std::cerr << "dirty rect is outside current media size, skipping update" << std::endl; + } + }; + }; + }; + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::windowPosToTexturePos( int window_x, int window_y, + int& media_x, int& media_y, + int& id ) +{ + if ( ! mSelectedPanel ) + { + media_x = 0; + media_y = 0; + id = 0; + return; + }; + + // record cursor poisiton for a readback next frame + mCurMouseX = window_x; + // OpenGL app == coordinate system this way + // NOTE: unrelated to settings in plugin - this + // is just for this app + mCurMouseY = mWindowHeight - window_y; + + // extract x (0..1023, y (0..1023) and id (0..15) from RGB components + unsigned long pixel_read_color_bits = ( mPixelReadColor[ 0 ] << 16 ) | ( mPixelReadColor[ 1 ] << 8 ) | mPixelReadColor[ 2 ]; + int texture_x = pixel_read_color_bits & 0x3ff; + int texture_y = ( pixel_read_color_bits >> 10 ) & 0x3ff; + id = ( pixel_read_color_bits >> 20 ) & 0x0f; + + // scale to size of media (1024 because we use 10 bits for X and Y from 24) + media_x = (int)( ( (float)mSelectedPanel->mMediaWidth * (float)texture_x ) / 1024.0f ); + media_y = (int)( ( (float)mSelectedPanel->mMediaHeight * (float)texture_y ) / 1024.0f ); + + // we assume the plugin uses an inverted coordinate scheme like OpenGL + // if not, the plugin code inverts the Y coordinate for us - we don't need to + media_y = mSelectedPanel->mMediaHeight - media_y; + + if ( media_x > 0 && media_y > 0 ) + { + //std::cout << " mouse coords: " << mCurMouseX << " x " << mCurMouseY << " and id = " << id << std::endl; + //std::cout << "raw texture coords: " << texture_x << " x " << texture_y << " and id = " << id << std::endl; + //std::cout << " media coords: " << media_x << " x " << media_y << " and id = " << id << std::endl; + //std::cout << std::endl; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::selectPanelById( int id ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mId == id ) + { + selectPanel(mMediaPanels[ panel ]); + return; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::selectPanel( mediaPanel* panel ) +{ + if( mSelectedPanel == panel ) + return; + + // turn off volume before we delete it + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + mSelectedPanel->mMediaSource->setVolume( 0.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_LOW ); + }; + + mSelectedPanel = panel; + + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_NORMAL ); + + if(!mSelectedPanel->mStartUrl.empty()) + { + mUrlEdit->set_text(const_cast(mSelectedPanel->mStartUrl.c_str()) ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLMediaPluginTest::findMediaPanel( LLPluginClassMedia* source ) +{ + mediaPanel *result = NULL; + + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mMediaSource == source ) + { + result = mMediaPanels[ panel ]; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::navigateToNewURI( std::string uri ) +{ + if ( uri.length() ) + { + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( !mSelectedPanel->mMediaSource->isPluginExited() && (mime_type == mSelectedPanel->mMimeType) ) + { + std::cout << "MIME type is the same" << std::endl; + mSelectedPanel->mMediaSource->loadURI( uri ); + mSelectedPanel->mMediaSource->start(); + mBookmarkList->do_selection( 0 ); + } + else + { + std::cout << "MIME type changed or plugin had exited" << std::endl; + replaceMediaPanel( mSelectedPanel, uri ); + mBookmarkList->do_selection( 0 ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::initUrlHistory( std::string uris ) +{ + if ( uris.length() > 0 ) + { + std::cout << "init URL : " << uris << std::endl; + LLSD historySD; + + char *cstr, *p; + cstr = new char[uris.size()+1]; + strcpy(cstr, uris.c_str()); + const char *DELIMS = " ,;"; + p = strtok(cstr, DELIMS); + while (p != NULL) { + historySD.insert(0, p); + p = strtok(NULL, DELIMS); + } + mSelectedPanel->mMediaSource->initializeUrlHistory(historySD); + delete[] cstr; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::gluiCallback( int control_id ) +{ + if ( control_id == mIdBookmarks ) + { + std::string uri = mBookmarks[ mSelBookmark ].second; + + navigateToNewURI( uri ); + } + else + if ( control_id == mIdUrlEdit) + { + std::string uri = mUrlEdit->get_text(); + + navigateToNewURI( uri ); + } + else + if ( control_id == mIdUrlInitHistoryEdit ) + { + std::string uri = mUrlInitHistoryEdit->get_text(); + + initUrlHistory( uri ); + } + else + if ( control_id == mIdControlAddPanel ) + { + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + } + else + if ( control_id == mIdControlRemPanel ) + { + remMediaPanel( mSelectedPanel ); + } + else + if ( control_id == mIdDisableTimeout ) + { + // Set the "disable timeout" flag for all active plugins. + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + mMediaPanels[ i ]->mMediaSource->setDisableTimeout(mDisableTimeout); + } + } + else + if ( control_id == mIdControlCrashPlugin ) + { + // send message to plugin and ask it to crash + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->crashPlugin(); + } + } + else + if ( control_id == mIdControlHangPlugin ) + { + // send message to plugin and ask it to hang + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->hangPlugin(); + } + } + else + if ( control_id == mIdControlExitApp ) + { + // text for exiting plugin system cleanly + delete this; // clean up + exit( 0 ); + } + else + if ( control_id == mIdMediaTimeControlPlay ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlLoop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( true ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlPause ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->pause(); + } + else + if ( control_id == mIdMediaTimeControlStop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->stop(); + }; + } + else + if ( control_id == mIdMediaTimeControlSeek ) + { + if ( mSelectedPanel ) + { + // get value from spinner + float seconds_to_seek = mMediaTimeControlSeekSeconds; + mSelectedPanel->mMediaSource->seek( seconds_to_seek ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlRewind ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(-2.0f); + }; + } + else + if ( control_id == mIdMediaTimeControlFastForward ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(2.0f); + }; + } + else + if ( control_id == mIdMediaBrowserControlBack ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_back(); + } + else + if ( control_id == mIdMediaBrowserControlStop ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_stop(); + } + else + if ( control_id == mIdMediaBrowserControlForward ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_forward(); + } + else + if ( control_id == mIdMediaBrowserControlHome ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->loadURI( mHomeWebUrl ); + } + else + if ( control_id == mIdMediaBrowserControlReload ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_reload( true ); + } + else + if ( control_id == mIdMediaBrowserControlClearCache ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cache(); + } + else + if ( control_id == mIdMediaBrowserControlClearCookies ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cookies(); + } + else + if ( control_id == mIdMediaBrowserControlEnableCookies ) + { + if ( mSelectedPanel ) + { + if ( mMediaBrowserControlEnableCookies ) + { + mSelectedPanel->mMediaSource->enable_cookies( true ); + } + else + { + mSelectedPanel->mMediaSource->enable_cookies( false ); + } + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::keyboard( int key ) +{ + //if ( key == 'a' || key == 'A' ) + // addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + //else + //if ( key == 'r' || key == 'R' ) + // remMediaPanel( mSelectedPanel ); + //else + //if ( key == 'd' || key == 'D' ) + // dumpPanelInfo(); + //else + if ( key == 27 ) + { + std::cout << "Application finished - exiting..." << std::endl; + delete this; + exit( 0 ); + }; + + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_DOWN, key, 0 ); + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_UP, key, 0 ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mouseButton( int button, int state, int x, int y ) +{ + if ( button == GLUT_LEFT_BUTTON ) + { + if ( state == GLUT_DOWN ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_DOWN, media_x, media_y, 0 ); + } + else + if ( state == GLUT_UP ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + // only select a panel if we're on a panel + // (HACK: strictly speaking this rules out clicking on + // the origin of a panel but that's very unlikely) + if ( media_x > 0 && media_y > 0 ) + { + selectPanelById( id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_UP, media_x, media_y, 0 ); + }; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mousePassive( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mouseMove( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::makeChrome() +{ + // IDs used by GLUI + int start_id = 0x1000; + + // right side window - geometry manipulators +#if __APPLE__ + // the Apple GLUT implementation doesn't seem to set the graphic offset of subwindows correctly when they overlap in certain ways. + // Use a separate controls window in this case. + // GLUI window at right containing manipulation controls and other buttons + int x = glutGet(GLUT_WINDOW_X) + glutGet(GLUT_WINDOW_WIDTH) + 4; + int y = glutGet(GLUT_WINDOW_Y); + GLUI* right_glui_window = GLUI_Master.create_glui( "", 0, x, y ); +#else + GLUI* right_glui_window = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_RIGHT ); +#endif + mViewRotationCtrl = right_glui_window->add_rotation( "Rotation", mViewRotation ); + mViewTranslationCtrl = right_glui_window->add_translation( "Translate", GLUI_TRANSLATION_XY, mViewPos ); + mViewTranslationCtrl->set_speed( 0.01f ); + mViewScaleCtrl = right_glui_window->add_translation( "Scale", GLUI_TRANSLATION_Z, &mViewPos[ 2 ] ); + mViewScaleCtrl->set_speed( 0.05f ); + right_glui_window->set_main_gfx_window( mAppWindow ); + + // right side window - app controls + mIdControlAddPanel = start_id++; + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + right_glui_window->add_button( "Add panel", mIdControlAddPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + mIdControlRemPanel = start_id++; + right_glui_window->add_button( "Rem panel", mIdControlRemPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlCrashPlugin = start_id++; + right_glui_window->add_button( "Crash plugin", mIdControlCrashPlugin, gluiCallbackWrapper ); + mIdControlHangPlugin = start_id++; + right_glui_window->add_button( "Hang plugin", mIdControlHangPlugin, gluiCallbackWrapper ); + + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlExitApp = start_id++; + right_glui_window->add_button( "Exit app", mIdControlExitApp, gluiCallbackWrapper ); + + //// top window - holds bookmark UI + mIdBookmarks = start_id++; + mSelBookmark = 0; + GLUI* glui_window_top = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mBookmarkList = glui_window_top->add_listbox( "", &mSelBookmark, mIdBookmarks, gluiCallbackWrapper ); + // only add the first 50 bookmarks - list can be very long sometimes (30,000+) + // when testing list of media URLs from AGNI for example + for( unsigned int each = 0; each < mBookmarks.size() && each < 50; ++each ) + mBookmarkList->add_item( each, const_cast< char* >( mBookmarks[ each ].first.c_str() ) ); + glui_window_top->set_main_gfx_window( mAppWindow ); + + glui_window_top->add_column( false ); + mIdUrlEdit = start_id++; + mUrlEdit = glui_window_top->add_edittext( "Url:", GLUI_EDITTEXT_TEXT, 0, mIdUrlEdit, gluiCallbackWrapper ); + mUrlEdit->set_w( 600 ); + GLUI* glui_window_top2 = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mIdUrlInitHistoryEdit = start_id++; + mUrlInitHistoryEdit = glui_window_top2->add_edittext( "Init History (separate by commas or semicolons):", + GLUI_EDITTEXT_TEXT, 0, mIdUrlInitHistoryEdit, gluiCallbackWrapper ); + mUrlInitHistoryEdit->set_w( 800 ); + + // top window - media controls for "time" media types (e.g. movies) + mGluiMediaTimeControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaTimeControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaTimeControlPlay = start_id++; + mGluiMediaTimeControlWindow->add_button( "PLAY", mIdMediaTimeControlPlay, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlLoop = start_id++; + mGluiMediaTimeControlWindow->add_button( "LOOP", mIdMediaTimeControlLoop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlPause = start_id++; + mGluiMediaTimeControlWindow->add_button( "PAUSE", mIdMediaTimeControlPause, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + + GLUI_Button *button; + mIdMediaTimeControlRewind = start_id++; + button = mGluiMediaTimeControlWindow->add_button( "<<", mIdMediaTimeControlRewind, gluiCallbackWrapper ); + button->set_w(30); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlFastForward = start_id++; + button = mGluiMediaTimeControlWindow->add_button( ">>", mIdMediaTimeControlFastForward, gluiCallbackWrapper ); + button->set_w(30); + + mGluiMediaTimeControlWindow->add_column( true ); + + mIdMediaTimeControlStop = start_id++; + mGluiMediaTimeControlWindow->add_button( "STOP", mIdMediaTimeControlStop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlVolume = start_id++; + GLUI_Spinner* spinner = mGluiMediaTimeControlWindow->add_spinner( "Volume", 2, &mMediaTimeControlVolume, mIdMediaTimeControlVolume, gluiCallbackWrapper); + spinner->set_float_limits( 0, 100 ); + mGluiMediaTimeControlWindow->add_column( true ); + mIdMediaTimeControlSeekSeconds = start_id++; + spinner = mGluiMediaTimeControlWindow->add_spinner( "", 2, &mMediaTimeControlSeekSeconds, mIdMediaTimeControlSeekSeconds, gluiCallbackWrapper); + spinner->set_float_limits( 0, 200 ); + spinner->set_w( 32 ); + spinner->set_speed( 0.025f ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlSeek = start_id++; + mGluiMediaTimeControlWindow->add_button( "SEEK", mIdMediaTimeControlSeek, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + + + // top window - media controls for "browser" media types (e.g. web browser) + mGluiMediaBrowserControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaBrowserControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaBrowserControlBack = start_id++; + mMediaBrowserControlBackButton = mGluiMediaBrowserControlWindow->add_button( "BACK", mIdMediaBrowserControlBack, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlStop = start_id++; + mGluiMediaBrowserControlWindow->add_button( "STOP", mIdMediaBrowserControlStop, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlForward = start_id++; + mMediaBrowserControlForwardButton = mGluiMediaBrowserControlWindow->add_button( "FORWARD", mIdMediaBrowserControlForward, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlHome = start_id++; + mGluiMediaBrowserControlWindow->add_button( "HOME", mIdMediaBrowserControlHome, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlReload = start_id++; + mGluiMediaBrowserControlWindow->add_button( "RELOAD", mIdMediaBrowserControlReload, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlClearCache = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR CACHE", mIdMediaBrowserControlClearCache, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlClearCookies = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR COOKIES", mIdMediaBrowserControlClearCookies, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlEnableCookies = start_id++; + mMediaBrowserControlEnableCookies = 0; + mGluiMediaBrowserControlWindow->add_checkbox( "Enable Cookies", &mMediaBrowserControlEnableCookies, mIdMediaBrowserControlEnableCookies, gluiCallbackWrapper ); + + // top window - misc controls + GLUI* glui_window_misc_control = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mIdRandomPanelCount = start_id++; + mRandomPanelCount = 0; + glui_window_misc_control->add_checkbox( "Randomize panel count", &mRandomPanelCount, mIdRandomPanelCount, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + mIdRandomBookmarks = start_id++; + mRandomBookmarks = 0; + glui_window_misc_control->add_checkbox( "Randomize bookmarks", &mRandomBookmarks, mIdRandomBookmarks, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + + mIdDisableTimeout = start_id++; + mDisableTimeout = 0; + glui_window_misc_control->add_checkbox( "Disable plugin timeout", &mDisableTimeout, mIdDisableTimeout, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + + // bottom window - status + mBottomGLUIWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_BOTTOM ); + mStatusText = mBottomGLUIWindow->add_statictext( "" ); + mBottomGLUIWindow->set_main_gfx_window( mAppWindow ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::resetView() +{ + mViewRotationCtrl->reset(); + + mViewScaleCtrl->set_x( 0.0f ); + mViewScaleCtrl->set_y( 0.0f ); + mViewScaleCtrl->set_z( 3.0f ); + + mViewTranslationCtrl->set_x( 0.0f ); + mViewTranslationCtrl->set_y( 0.0f ); + mViewTranslationCtrl->set_z( 0.0f ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ) +{ + int pick_texture_width = 1024; + int pick_texture_height = 1024; + int pick_texture_depth = 3; + unsigned char* ptr = new unsigned char[ pick_texture_width * pick_texture_height * pick_texture_depth ]; + for( int y = 0; y < pick_texture_height; ++y ) + { + for( int x = 0; x < pick_texture_width * pick_texture_depth ; x += pick_texture_depth ) + { + unsigned long bits = 0L; + bits |= ( id << 20 ) | ( y << 10 ) | ( x / 3 ); + unsigned char r_component = ( bits >> 16 ) & 0xff; + unsigned char g_component = ( bits >> 8 ) & 0xff; + unsigned char b_component = bits & 0xff; + + ptr[ y * pick_texture_width * pick_texture_depth + x + 0 ] = r_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 1 ] = g_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 2 ] = b_component; + }; + }; + + glGenTextures( 1, texture_handle ); + + checkGLError("glGenTextures"); + std::cout << "glGenTextures returned " << *texture_handle << std::endl; + + bindTexture( *texture_handle ); + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + pick_texture_width, pick_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, ptr ); + + *texture_pixels = ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaPluginTest::mimeTypeFromUrl( std::string& url ) +{ + // default to web + std::string mime_type = "text/html"; + + // we may need a more advanced MIME type accessor later :-) + if ( url.find( ".swf" ) != std::string::npos ) // Flash movies + mime_type = "application/x-shockwave-flash"; + else + if ( url.find( ".mov" ) != std::string::npos ) // Movies + mime_type = "video/quicktime"; + else + if ( url.find( ".txt" ) != std::string::npos ) // Apple Text descriptors + mime_type = "video/quicktime"; + + return mime_type; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaPluginTest::pluginNameFromMimeType( std::string& mime_type ) +{ +#if LL_DARWIN + std::string plugin_name( "media_plugin_null.dylib" ); + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dylib"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dylib"; + +#elif LL_WINDOWS + std::string plugin_name( "media_plugin_null.dll" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dll"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dll"; + else + if ( mime_type == "application/x-shockwave-flash" ) + plugin_name = "media_plugin_flash_activex.dll"; + +#elif LL_LINUX + std::string plugin_name( "libmedia_plugin_null.so" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "libmedia_plugin_quicktime.so"; + else + if ( mime_type == "text/html" ) + plugin_name = "libmedia_plugin_webkit.so"; +#endif + return plugin_name; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::addMediaPanel( std::string url ) +{ + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + media_source->init( launcher_name, plugin_name ); + media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + mediaPanel* panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + // look through current media panels to find an unused index number + bool id_exists = true; + for( int nid = 0; nid < mMaxPanels; ++nid ) + { + // does this id exist already? + id_exists = false; + for( int pid = 0; pid < (int)mMediaPanels.size(); ++pid ) + { + if ( nid == mMediaPanels[ pid ]->mId ) + { + id_exists = true; + break; + }; + }; + + // id wasn't found so we can use it + if ( ! id_exists ) + { + panel->mId = nid; + break; + }; + }; + + // if we get here and this flag is set, there is no room for any more panels + if ( id_exists ) + { + std::cout << "No room for any more panels" << std::endl; + } + else + { + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // save this in the list of panels + mMediaPanels.push_back( panel ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); + + std::cout << "Adding new media panel for " << url << "(" << media_width << "x" << media_height << ") with index " << panel->mId << " - total panels = " << mMediaPanels.size() << std::endl; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::updateMediaPanel( mediaPanel* panel ) +{ +// checkGLError("LLMediaPluginTest::updateMediaPanel"); + + if ( ! panel ) + return; + + if(!panel->mMediaSource || !panel->mMediaSource->textureValid()) + { + panel->mReadyToRender = false; + return; + } + + // take a reference copy of the plugin values since they + // might change during this lifetime of this function + int plugin_media_width = panel->mMediaSource->getWidth(); + int plugin_media_height = panel->mMediaSource->getHeight(); + int plugin_texture_width = panel->mMediaSource->getBitsWidth(); + int plugin_texture_height = panel->mMediaSource->getBitsHeight(); + + // If the texture isn't created or the media or texture dimensions changed AND + // the sizes are valid then we need to delete the old media texture (if necessary) + // then make a new one. + if ((panel->mMediaTextureHandle == 0 || + panel->mMediaWidth != plugin_media_width || + panel->mMediaHeight != plugin_media_height || + panel->mTextureWidth != plugin_texture_width || + panel->mTextureHeight != plugin_texture_height) && + ( plugin_media_width > 0 && plugin_media_height > 0 && + plugin_texture_width > 0 && plugin_texture_height > 0 ) ) + { + std::cout << "Valid media size (" << plugin_media_width << " x " << plugin_media_height + << ") and texture size (" << plugin_texture_width << " x " << plugin_texture_height + << ") for panel with ID=" << panel->mId << " - making texture" << std::endl; + + // delete old GL texture + if ( isTexture( panel->mMediaTextureHandle ) ) + { + std::cerr << "updateMediaPanel: deleting texture " << panel->mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &panel->mMediaTextureHandle ); + panel->mMediaTextureHandle = 0; + } + + std::cerr << "before: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + + // make a GL texture based on the dimensions the plugin told us + GLuint new_texture = 0; + glGenTextures( 1, &new_texture ); + + checkGLError("glGenTextures"); + + std::cout << "glGenTextures returned " << new_texture << std::endl; + + panel->mMediaTextureHandle = new_texture; + + bindTexture( panel->mMediaTextureHandle ); + + std::cout << "Setting texture size to " << plugin_texture_width << " x " << plugin_texture_height << std::endl; + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + plugin_texture_width, plugin_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, + 0 ); + + + std::cerr << "after: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + }; + + // update our record of the media and texture dimensions + // NOTE: do this after we we check for sizes changes + panel->mMediaWidth = plugin_media_width; + panel->mMediaHeight = plugin_media_height; + panel->mTextureWidth = plugin_texture_width; + panel->mTextureHeight = plugin_texture_height; + if ( plugin_texture_width > 0 ) + { + panel->mTextureScaleX = (double)panel->mMediaWidth / (double)panel->mTextureWidth; + }; + if ( plugin_texture_height > 0 ) + { + panel->mTextureScaleY = (double)panel->mMediaHeight / (double)panel->mTextureHeight; + }; + + // update the flag which tells us if the media source uses OprnGL coords or not. + panel->mAppTextureCoordsOpenGL = panel->mMediaSource->getTextureCoordsOpenGL(); + + // Check to see if we have enough to render this panel. + // If we do, set a flag that the display functions use so + // they only render a panel with media if it's ready. + if ( panel->mMediaWidth < 0 || + panel->mMediaHeight < 0 || + panel->mTextureWidth < 1 || + panel->mTextureHeight < 1 || + panel->mMediaTextureHandle == 0 ) + { + panel->mReadyToRender = false; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::replaceMediaPanel( mediaPanel* panel, std::string url ) +{ + // no media panels so we can't change anything - have to add + if ( mMediaPanels.size() == 0 ) + return; + + // sanity check + if ( ! panel ) + return; + + int index; + for(index = 0; index < (int)mMediaPanels.size(); index++) + { + if(mMediaPanels[index] == panel) + break; + } + + if(index >= (int)mMediaPanels.size()) + { + // panel isn't in mMediaPanels + return; + } + + std::cout << "Replacing media panel with index " << panel->mId << std::endl; + + int panel_id = panel->mId; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + media_source->init( launcher_name, plugin_name ); + media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + panel->mId = panel_id; + + // Replace the entry in the panels array + mMediaPanels[index] = panel; + + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::getRandomMediaSize( int& width, int& height, std::string mime_type ) +{ + // Make a new media source with a random size which we'll either + // directly or the media plugin will tell us what it wants later. + // Use a random size so we can test support for weird media sizes. + // (Almost everything else will get filled in later once the + // plugin responds) + // NB. Do we need to enforce that width is on 4 pixel boundary? + width = ( ( rand() % 170 ) + 30 ) * 4; + height = ( ( rand() % 170 ) + 30 ) * 4; + + // adjust this random size if it's a browser so we get + // a more useful size for testing.. + if ( mime_type == "text/html" ) + { + width = ( ( rand() % 100 ) + 100 ) * 4; + height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; + } + else + // adjust this random size if it's Flash so we get + // a more useful size for testing.. + if ( mime_type == "application/x-shockwave-flash" ) + { + width = ( ( rand() % 100 ) + 100 ) * 4; + height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::remMediaPanel( mediaPanel* panel ) +{ + // always leave one panel + if ( mMediaPanels.size() == 1 ) + return; + + // sanity check - don't think this can happen but see above for a case where it might... + if ( ! panel ) + return; + + std::cout << "Removing media panel with index " << panel->mId << " - total panels = " << mMediaPanels.size() - 1 << std::endl; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // remove from storage list + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + if ( mMediaPanels[ i ] == panel ) + { + mMediaPanels.erase( mMediaPanels.begin() + i ); + break; + }; + }; + + // select the first panel + selectPanel( mMediaPanels[ 0 ] ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::updateStatusBar() +{ + if ( ! mSelectedPanel ) + return; + + // cache results - this is a very slow function + static int cached_id = -1; + static int cached_media_width = -1; + static int cached_media_height = -1; + static int cached_texture_width = -1; + static int cached_texture_height = -1; + static bool cached_supports_browser_media = true; + static bool cached_supports_time_media = false; + static int cached_movie_time = -1; + + static std::string cached_plugin_version = ""; + if ( + cached_id == mSelectedPanel->mId && + cached_media_width == mSelectedPanel->mMediaWidth && + cached_media_height == mSelectedPanel->mMediaHeight && + cached_texture_width == mSelectedPanel->mTextureWidth && + cached_texture_height == mSelectedPanel->mTextureHeight && + cached_supports_browser_media == mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() && + cached_supports_time_media == mSelectedPanel->mMediaSource->pluginSupportsMediaTime() && + cached_plugin_version == mSelectedPanel->mMediaSource->getPluginVersion() && + cached_movie_time == (int)mSelectedPanel->mMediaSource->getCurrentTime() + ) + { + // nothing changed so don't spend time in this shitty function + return; + }; + + std::ostringstream stream( "" ); + + stream.str( "" ); + stream.clear(); + + stream << "Id: "; + stream << std::setw( 2 ) << std::setfill( '0' ); + stream << mSelectedPanel->mId; + stream << " | "; + stream << "Media: "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaWidth; + stream << " x "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaHeight; + stream << " | "; + stream << "Texture: "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureWidth; + stream << " x "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureHeight; + stream << " | "; + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + stream << "BROWSER"; + else + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + stream << "TIME "; + stream << " | "; + stream << mSelectedPanel->mMediaSource->getPluginVersion(); + stream << " | "; + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getCurrentTime(); + stream << " / "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getDuration(); + stream << " @ "; + stream << (int)mSelectedPanel->mMediaSource->getCurrentPlayRate(); + stream << " | "; + }; + + glutSetWindow( mBottomGLUIWindow->get_glut_window_id() ); + mStatusText->set_text( const_cast< char*>( stream.str().c_str() ) ); + glutSetWindow( mAppWindow ); + + // caching + cached_id = mSelectedPanel->mId; + cached_media_width = mSelectedPanel->mMediaWidth; + cached_media_height = mSelectedPanel->mMediaHeight; + cached_texture_width = mSelectedPanel->mTextureWidth; + cached_texture_height = mSelectedPanel->mTextureHeight; + cached_supports_browser_media = mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser(); + cached_supports_time_media = mSelectedPanel->mMediaSource->pluginSupportsMediaTime(); + cached_plugin_version = mSelectedPanel->mMediaSource->getPluginVersion(); + cached_movie_time = (int)mSelectedPanel->mMediaSource->getCurrentTime(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::dumpPanelInfo() +{ + std::cout << std::endl << "===== Media Panels =====" << std::endl; + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << i + 1 << "> "; + std::cout << "Id: "; + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mId; + std::cout << " | "; + std::cout << "Media: "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaWidth; + std::cout << " x "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaHeight; + std::cout << " | "; + std::cout << "Texture: "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureWidth; + std::cout << " x "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureHeight; + std::cout << " | "; + if ( mMediaPanels[ i ] == mSelectedPanel ) + std::cout << "(selected)"; + + std::cout << std::endl; + }; + std::cout << "========================" << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + // Uncomment this to make things much, much quieter. +// return; + + switch(event) + { + case MEDIA_EVENT_CONTENT_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << std::endl; + break; + + case MEDIA_EVENT_TIME_DURATION_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << std::endl; + break; + + case MEDIA_EVENT_SIZE_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_SIZE_CHANGED " << std::endl; + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN " << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << std::endl; + break; + + case MEDIA_EVENT_PROGRESS_UPDATED: + std::cerr << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << std::endl; + break; + + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; + break; + + case MEDIA_EVENT_LOCATION_CHANGED: + { + std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; + mediaPanel* panel = findMediaPanel(self); + if(panel != NULL) + { + panel->mStartUrl = self->getLocation(); + if(panel == mSelectedPanel) + { + mUrlEdit->set_text(const_cast(panel->mStartUrl.c_str()) ); + } + } + } + break; + + case MEDIA_EVENT_CLICK_LINK_HREF: + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, uri is " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_PLUGIN_FAILED: + std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << std::endl; + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +static void gluiCallbackWrapper( int control_id ) +{ + if ( gApplication ) + gApplication->gluiCallback( control_id ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutReshape( int width, int height ) +{ + if ( gApplication ) + gApplication->reshape( width, height ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutDisplay() +{ + if ( gApplication ) + gApplication->display(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutIdle(int update_ms) +{ + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + if ( gApplication ) + gApplication->idle(); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboard( unsigned char key, int x, int y ) +{ + if ( gApplication ) + gApplication->keyboard( key ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMousePassive( int x, int y ) +{ + if ( gApplication ) + gApplication->mousePassive( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseMove( int x , int y ) +{ + if ( gApplication ) + gApplication->mouseMove( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseButton( int button, int state, int x, int y ) +{ + if ( gApplication ) + gApplication->mouseButton( button, state, x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int main( int argc, char* argv[] ) +{ + glutInit( &argc, argv ); + glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); + + const int app_window_x = 80; + const int app_window_y = 0; + const int app_window_width = 964; + const int app_window_height = 964; + + glutInitWindowPosition( app_window_x, app_window_y ); + glutInitWindowSize( app_window_width, app_window_height ); + + int app_window_handle = glutCreateWindow( "LLMediaPluginTest2" ); + + glutDisplayFunc( glutDisplay ); + + GLUI_Master.set_glutReshapeFunc( glutReshape ); + GLUI_Master.set_glutKeyboardFunc( glutKeyboard ); + GLUI_Master.set_glutMouseFunc( glutMouseButton ); + + glutPassiveMotionFunc( glutMousePassive ); + glutMotionFunc( glutMouseMove ); + + glutSetWindow( app_window_handle ); + + gApplication = new LLMediaPluginTest( app_window_handle, app_window_width, app_window_height ); + + // update at approximately 60hz + int update_ms = 1000 / 60; + + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + glutMainLoop(); + + delete gApplication; +} -- cgit v1.2.3