From 434709680326aa634805afdcf900b7445ef4aac4 Mon Sep 17 00:00:00 2001 From: callum Date: Thu, 15 Oct 2009 11:26:52 -0700 Subject: https://jira.lindenlab.com/jira/browse/DEV-40711 Implement name fetching capabilities for Webkit and Quicktime plugins Adds support for new PluginAPI message (MEDIA_EVENT_NAME_CHANGED) that updates the "title" of the media. In WebKit plugin this is the contents of the tag. In The QuickTime plugin it is the "display name" from the movie meta data --- indra/llplugin/llpluginclassmedia.cpp | 5 + indra/llplugin/llpluginclassmediaowner.h | 1 + .../quicktime/media_plugin_quicktime.cpp | 2052 ++++++++++---------- indra/media_plugins/webkit/media_plugin_webkit.cpp | 13 +- indra/test_apps/llplugintest/llmediaplugintest.cpp | 7 + 5 files changed, 1081 insertions(+), 997 deletions(-) (limited to 'indra') diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 2a343fd0c9..fc58b48a7b 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -835,6 +835,11 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mCanPaste = message.getValueBoolean("paste"); } } + else if(message_name == "name_text") + { + mMediaName = message.getValue("name"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAME_CHANGED); + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index cfee847080..4690f09172 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -52,6 +52,7 @@ public: MEDIA_EVENT_NAVIGATE_COMPLETE, // browser has finished navigation MEDIA_EVENT_PROGRESS_UPDATED, // browser has updated loading progress MEDIA_EVENT_STATUS_TEXT_CHANGED, // browser has updated the status text + MEDIA_EVENT_NAME_CHANGED, // browser has updated the name of the media (typically <title> tag) MEDIA_EVENT_LOCATION_CHANGED, // browser location (URL) has changed (maybe due to internal navagation/frames/etc) MEDIA_EVENT_CLICK_LINK_HREF, // I'm not entirely sure what the semantics of these two are MEDIA_EVENT_CLICK_LINK_NOFOLLOW, diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 556865f771..9b378fcd92 100644 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -1,995 +1,1057 @@ -/** - * @file media_plugin_quicktime.cpp - * @brief QuickTime plugin for LLMedia API plugin system - * - * $LicenseInfo:firstyear=2008&license=viewergpl$ - * - * Copyright (c) 2008, 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 "llgl.h" - -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#if LL_QUICKTIME_ENABLED - -#if defined(LL_DARWIN) - #include <QuickTime/QuickTime.h> -#elif defined(LL_WINDOWS) - #include "MacTypes.h" - #include "QTML.h" - #include "Movies.h" - #include "QDoffscreen.h" - #include "FixMath.h" -#endif - -// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginQuickTime(); - - /* virtual */ void receiveMessage(const char *message_string); - -private: - - int mNaturalWidth; - int mNaturalHeight; - Movie mMovieHandle; - GWorldPtr mGWorldHandle; - ComponentInstance mMovieController; - int mCurVolume; - bool mMediaSizeChanging; - bool mIsLooping; - const int mMinWidth; - const int mMaxWidth; - const int mMinHeight; - const int mMaxHeight; - F64 mPlayRate; - - enum ECommand { - COMMAND_NONE, - COMMAND_STOP, - COMMAND_PLAY, - COMMAND_FAST_FORWARD, - COMMAND_FAST_REWIND, - COMMAND_PAUSE, - COMMAND_SEEK, - }; - ECommand mCommand; - - // Override this to add current time and duration to the message - /*virtual*/ void setDirty(int left, int top, int right, int bottom) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); - - message.setValueS32("left", left); - message.setValueS32("top", top); - message.setValueS32("right", right); - message.setValueS32("bottom", bottom); - - if(mMovieHandle) - { - message.setValueReal("current_time", getCurrentTime()); - message.setValueReal("duration", getDuration()); - message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); - message.setValueReal("loaded_duration", getLoadedDuration()); - } - - sendMessage(message); - } - - - static Rect rectFromSize(int width, int height) - { - Rect result; - - - result.left = 0; - result.top = 0; - result.right = width; - result.bottom = height; - - return result; - } - - Fixed getPlayRate(void) - { - Fixed result; - if(mPlayRate == 0.0f) - { - // Default to the movie's preferred rate - result = GetMoviePreferredRate(mMovieHandle); - if(result == 0) - { - // Don't return a 0 play rate, ever. - std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl; - result = X2Fix(1.0f); - } - } - else - { - result = X2Fix(mPlayRate); - } - - return result; - } - - void load( const std::string url ) - { - if ( url.empty() ) - return; - - // Stop and unload any existing movie before starting another one. - unload(); - - setStatus(STATUS_LOADING); - - //In case std::string::c_str() makes a copy of the url data, - //make sure there is memory to hold it before allocating memory for handle. - //if fails, NewHandleClear(...) should return NULL. - const char* url_string = url.c_str() ; - Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) ); - if ( NULL == handle || noErr != MemError() || NULL == *handle ) - { - setStatus(STATUS_ERROR); - return; - } - - BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) ); - - OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType ); - DisposeHandle( handle ); - if ( noErr != err ) - { - setStatus(STATUS_ERROR); - return; - }; - - // do pre-roll actions (typically fired for streaming movies but not always) - PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); - - Rect movie_rect = rectFromSize(mWidth, mHeight); - - // make a new movie controller - mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie ); - - // movie controller - MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this ); - - SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize ); - - // function that gets called when a frame is drawn - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this ); - - setStatus(STATUS_LOADED); - - sizeChanged(); - }; - - bool unload() - { - if ( mMovieHandle ) - { - StopMovie( mMovieHandle ); - if ( mMovieController ) - { - MCMovieChanged( mMovieController, mMovieHandle ); - }; - }; - - if ( mMovieController ) - { - MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this ); - DisposeMovieController( mMovieController ); - mMovieController = NULL; - }; - - if ( mMovieHandle ) - { - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this ); - DisposeMovie( mMovieHandle ); - mMovieHandle = NULL; - }; - - if ( mGWorldHandle ) - { - DisposeGWorld( mGWorldHandle ); - mGWorldHandle = NULL; - }; - - setStatus(STATUS_NONE); - - return true; - } - - bool navigateTo( const std::string url ) - { - unload(); - load( url ); - - return true; - }; - - bool sizeChanged() - { - if ( ! mMovieHandle ) - return false; - - // Check to see whether the movie's natural size has updated - { - int width, height; - getMovieNaturalSize(&width, &height); - if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight))) - { - mNaturalWidth = width; - mNaturalHeight = height; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); - message.setValue("name", mTextureSegmentName); - message.setValueS32("width", width); - message.setValueS32("height", height); - sendMessage(message); - //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl; - } - } - - // sanitize destination size - Rect dest_rect = rectFromSize(mWidth, mHeight); - - // media depth won't change - int depth_bits = mDepth * 8; - long rowbytes = mDepth * mTextureWidth; - - GWorldPtr old_gworld_handle = mGWorldHandle; - - if(mPixels != NULL) - { - // We have pixels. Set up a GWorld pointing at the texture. - OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - else - { - // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally. - Rect tempRect = rectFromSize(1, 1); - OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - - SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) ); - - // If the GWorld was already set up, delete it. - if(old_gworld_handle != NULL) - { - DisposeGWorld( old_gworld_handle ); - } - - // Set up the movie display matrix - { - // scale movie to fit rect and invert vertically to match opengl image format - MatrixRecord transform; - SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix - double scaleX = (double) mWidth / mNaturalWidth; - double scaleY = -1.0 * (double) mHeight / mNaturalHeight; - double centerX = mWidth / 2.0; - double centerY = mHeight / 2.0; - ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) ); - SetMovieMatrix( mMovieHandle, &transform ); - } - - // update movie controller - if ( mMovieController ) - { - MCSetControllerPort( mMovieController, mGWorldHandle ); - MCPositionController( mMovieController, &dest_rect, &dest_rect, - mcTopLeftMovie | mcPositionDontInvalidate ); - MCMovieChanged( mMovieController, mMovieHandle ); - } - - - // Emit event with size change so the calling app knows about it too - // TODO: - //LLMediaEvent event( this ); - //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event ); - - return true; - } - - static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref ) - { - Boolean result = false; - - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - switch( action ) - { - // handle window resizing - case mcActionControllerSizeChanged: - // Ensure that the movie draws correctly at the new size - self->sizeChanged(); - break; - - // Block any movie controller actions that open URLs. - case mcActionLinkToURL: - case mcActionGetNextURL: - case mcActionLinkToURLExtended: - // Prevent the movie controller from handling the message - result = true; - break; - - default: - break; - }; - - return result; - }; - - static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref ) - { - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // IMPORTANT: typically, a consumer who is observing this event will set a flag - // when this event is fired then render later. Be aware that the media stream - // can change during this period - dimensions, depth, format etc. - //LLMediaEvent event( self ); -// self->updateQuickTime(); - // TODO ^^^ - - if ( self->mWidth > 0 && self->mHeight > 0 ) - self->setDirty( 0, 0, self->mWidth, self->mHeight ); - - return noErr; - }; - - static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) - { - //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // TODO: - //LLMediaEvent event( self ); - //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); - }; - - - void rewind() - { - GoToBeginningOfMovie( mMovieHandle ); - MCMovieChanged( mMovieController, mMovieHandle ); - }; - - bool processState() - { - if ( mCommand == COMMAND_PLAY ) - { - if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING ) - { - long state = GetMovieLoadState( mMovieHandle ); - - if ( state >= kMovieLoadStatePlaythroughOK ) - { - // if the movie is at the end (generally because it reached it naturally) - // and we play is requested, jump back to the start of the movie. - // note: this is different from having loop flag set. - if ( IsMovieDone( mMovieHandle ) ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - }; - - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - setStatus(STATUS_PLAYING); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_STOP ) - { - if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - - setStatus(STATUS_LOADED); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_PAUSE ) - { - if ( mStatus == STATUS_PLAYING ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - setStatus(STATUS_PAUSED); - mCommand = COMMAND_NONE; - }; - }; - }; - - return true; - }; - - void play(F64 rate) - { - mPlayRate = rate; - mCommand = COMMAND_PLAY; - }; - - void stop() - { - mCommand = COMMAND_STOP; - }; - - void pause() - { - mCommand = COMMAND_PAUSE; - }; - - void getMovieNaturalSize(int *movie_width, int *movie_height) - { - Rect rect; - - GetMovieNaturalBoundsRect( mMovieHandle, &rect ); - - int width = ( rect.right - rect.left ); - int height = ( rect.bottom - rect.top ); - - // make sure width and height fall in valid range - if ( width < mMinWidth ) - width = mMinWidth; - - if ( width > mMaxWidth ) - width = mMaxWidth; - - if ( height < mMinHeight ) - height = mMinHeight; - - if ( height > mMaxHeight ) - height = mMaxHeight; - - // return the new rect - *movie_width = width; - *movie_height = height; - } - - void updateQuickTime(int milliseconds) - { - if ( ! mMovieHandle ) - return; - - if ( ! mMovieController ) - return; - - // service QuickTime - // Calling it this way doesn't have good behavior on Windows... -// MoviesTask( mMovieHandle, milliseconds ); - // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle. -// MoviesTask( mMovieHandle, 0 ); - - MCIdle( mMovieController ); - - if ( ! mGWorldHandle ) - return; - - if ( mMediaSizeChanging ) - return; - - // update state machine - processState(); - - // special code for looping - need to rewind at the end of the movie - if ( mIsLooping ) - { - // QT call to see if we are at the end - can't do with controller - if ( IsMovieDone( mMovieHandle ) ) - { - // go back to start - rewind(); - - if ( mMovieController ) - { - // kick off new play - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - - // set the volume - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - }; - }; - }; - - int getDataWidth() const - { - if ( mGWorldHandle ) - { - int depth = mDepth; - - if (depth < 1) - depth = 1; - - // ALWAYS use the row bytes from the PixMap if we have a GWorld because - // sometimes it's not the same as mMediaDepth * mMediaWidth ! - PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle ); - return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth; - } - else - { - // TODO : return LLMediaImplCommon::getaDataWidth(); - return 0; - } - }; - - void seek( F64 time ) - { - if ( mMovieController ) - { - TimeRecord when; - when.scale = GetMovieTimeScale( mMovieHandle ); - when.base = 0; - - // 'time' is in (floating point) seconds. The timebase time will be in 'units', where - // there are 'scale' units per second. - SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) ); - - when.value.hi = ( SInt32 )( raw_time >> 32 ); - when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) ); - - MCDoAction( mMovieController, mcActionGoToTime, &when ); - }; - }; - - F64 getDuration() - { - TimeValue duration = GetMovieDuration( mMovieHandle ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)duration / (F64)scale; - }; - - F64 getLoadedDuration() - { - TimeValue duration; - if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr) - { - // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie. - duration = GetMovieDuration( mMovieHandle ); - } - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)duration / (F64)scale; - }; - - F64 getCurrentTime() - { - TimeValue curr_time = GetMovieTime( mMovieHandle, 0 ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)curr_time / (F64)scale; - }; - - void setVolume( F64 volume ) - { - mCurVolume = (short)(volume * ( double ) 0x100 ); - - if ( mMovieController ) - { - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void update(int milliseconds = 0) - { - updateQuickTime(milliseconds); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseDown( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseUp( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseMove( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void keyPress( unsigned char key ) - { - }; - -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data), - mMinWidth( 0 ), - mMaxWidth( 2048 ), - mMinHeight( 0 ), - mMaxHeight( 2048 ) -{ -// std::cerr << "MediaPluginQuickTime constructor" << std::endl; - - mNaturalWidth = -1; - mNaturalHeight = -1; - mMovieHandle = 0; - mGWorldHandle = 0; - mMovieController = 0; - mCurVolume = 0x99; - mMediaSizeChanging = false; - mIsLooping = false; - mCommand = COMMAND_NONE; - mPlayRate = 0.0f; - mStatus = STATUS_NONE; -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ -// std::cerr << "MediaPluginQuickTime destructor" << std::endl; - - ExitMovies(); - -#ifdef LL_WINDOWS - TerminateQTML(); -// std::cerr << "QuickTime closing down" << std::endl; -#endif -} - - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ -// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - LLPluginMessage message_in; - - if(message_in.parse(message_string) >= 0) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) - { - if(message_name == "init") - { - LLPluginMessage message("base", "init_response"); - LLSD versions = LLSD::emptyMap(); - versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - // Normally a plugin would only specify one of these two subclasses, but this is a demo... -// versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; - message.setValueLLSD("versions", versions); - - #ifdef LL_WINDOWS - if ( InitializeQTML( 0L ) != noErr ) - { - //TODO: If no QT on Windows, this fails - respond accordingly. - //return false; - } - else - { -// std::cerr << "QuickTime initialized" << std::endl; - }; - #endif - - EnterMovies(); - - std::string plugin_version = "QuickTime media plugin, QuickTime version "; - - long version = 0; - Gestalt( gestaltQuickTimeVersion, &version ); - std::ostringstream codec( "" ); - codec << std::hex << version << std::dec; - plugin_version += codec.str(); - message.setValue("plugin_version", plugin_version); - sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - #if defined(LL_WINDOWS) - // Values for Windows - mDepth = 3; - message.setValueU32("format", GL_RGB); - message.setValueU32("type", GL_UNSIGNED_BYTE); - - // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even. - // Padding to a multiple of 3*32 guarantees it'll divide out properly. - message.setValueU32("padding", 32 * 3); - #else - // Values for Mac - mDepth = 4; - message.setValueU32("format", GL_BGRA_EXT); - #ifdef __BIG_ENDIAN__ - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV ); - #else - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8); - #endif - - // Pad texture width to a multiple of 32 bytes, to line up with cache lines. - message.setValueU32("padding", 32); - #endif - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGB); - message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. - message.setValueBoolean("allow_downsample", true); - sendMessage(message); - } - else if(message_name == "idle") - { - // no response is necessary here. - F64 time = message_in.getValueReal("time"); - - // Convert time to milliseconds for update() - update((int)(time * 1000.0f)); - } - else if(message_name == "cleanup") - { - // TODO: clean up here - } - else if(message_name == "shm_added") - { - SharedSegmentInfo info; - info.mAddress = message_in.getValuePointer("address"); - info.mSize = (size_t)message_in.getValueS32("size"); - std::string name = message_in.getValue("name"); - - -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name -// << ", size: " << info.mSize -// << ", address: " << info.mAddress -// << std::endl; - - mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - - } - else if(message_name == "shm_remove") - { - std::string name = message_in.getValue("name"); - -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl; - - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - if(mPixels == iter->second.mAddress) - { - // This is the currently active pixel buffer. Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - - // Make sure the movie GWorld is no longer pointed at the shared segment. - sizeChanged(); - } - mSharedSegments.erase(iter); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl; - } - - // Send the response so it can be cleaned up. - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl; - } - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) - { - if(message_name == "size_change") - { - std::string name = message_in.getValue("name"); - S32 width = message_in.getValueS32("width"); - S32 height = message_in.getValueS32("height"); - S32 texture_width = message_in.getValueS32("texture_width"); - S32 texture_height = message_in.getValueS32("texture_height"); - - //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); - message.setValue("name", name); - message.setValueS32("width", width); - message.setValueS32("height", height); - message.setValueS32("texture_width", texture_width); - message.setValueS32("texture_height", texture_height); - sendMessage(message); - - if(!name.empty()) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { -// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl; -// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl; - - mPixels = (unsigned char*)iter->second.mAddress; - mTextureSegmentName = name; - mWidth = width; - mHeight = height; - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - - mMediaSizeChanging = false; - - sizeChanged(); - - update(); - }; - }; - } - else if(message_name == "load_uri") - { - std::string uri = message_in.getValue("uri"); - load( uri ); - sendStatus(); - } - else if(message_name == "mouse_event") - { - std::string event = message_in.getValue("event"); - S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - - if(event == "down") - { - mouseDown(x, y); - } - else if(event == "up") - { - mouseUp(x, y); - } - else if(event == "move") - { - mouseMove(x, y); - }; - }; - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) - { - if(message_name == "stop") - { - stop(); - } - else if(message_name == "start") - { - F64 rate = 0.0; - if(message_in.hasValue("rate")) - { - rate = message_in.getValueReal("rate"); - } - play(rate); - } - else if(message_name == "pause") - { - pause(); - } - else if(message_name == "seek") - { - F64 time = message_in.getValueReal("time"); - seek(time); - } - else if(message_name == "set_loop") - { - bool loop = message_in.getValueBoolean("loop"); - mIsLooping = loop; - } - else if(message_name == "set_volume") - { - F64 volume = message_in.getValueReal("volume"); - setVolume(volume); - } - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl; - }; - }; -} - -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data); - *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; -} - -#else // LL_QUICKTIME_ENABLED - -// Stubbed-out class with constructor/destructor (necessary or windows linker -// will just think its dead code and optimize it all out) -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginQuickTime(); - /* virtual */ void receiveMessage(const char *message_string); -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) -{ - // no-op -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ - // no-op -} - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ - // no-op -} - -// We're building without quicktime enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - return -1; -} - -#endif // LL_QUICKTIME_ENABLED +/** + * @file media_plugin_quicktime.cpp + * @brief QuickTime plugin for LLMedia API plugin system + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, 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 "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#if LL_QUICKTIME_ENABLED + +#if defined(LL_DARWIN) + #include <QuickTime/QuickTime.h> +#elif defined(LL_WINDOWS) + #include "MacTypes.h" + #include "QTML.h" + #include "Movies.h" + #include "QDoffscreen.h" + #include "FixMath.h" + #include "QTLoadLibraryUtils.h" +#endif + +// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginQuickTime : public MediaPluginBase +{ +public: + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginQuickTime(); + + /* virtual */ void receiveMessage(const char *message_string); + +private: + + int mNaturalWidth; + int mNaturalHeight; + Movie mMovieHandle; + GWorldPtr mGWorldHandle; + ComponentInstance mMovieController; + int mCurVolume; + bool mMediaSizeChanging; + bool mIsLooping; + std::string mMovieTitle; + bool mReceivedTitle; + const int mMinWidth; + const int mMaxWidth; + const int mMinHeight; + const int mMaxHeight; + F64 mPlayRate; + + enum ECommand { + COMMAND_NONE, + COMMAND_STOP, + COMMAND_PLAY, + COMMAND_FAST_FORWARD, + COMMAND_FAST_REWIND, + COMMAND_PAUSE, + COMMAND_SEEK, + }; + ECommand mCommand; + + // Override this to add current time and duration to the message + /*virtual*/ void setDirty(int left, int top, int right, int bottom) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + if(mMovieHandle) + { + message.setValueReal("current_time", getCurrentTime()); + message.setValueReal("duration", getDuration()); + message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); + } + + sendMessage(message); + } + + + static Rect rectFromSize(int width, int height) + { + Rect result; + + + result.left = 0; + result.top = 0; + result.right = width; + result.bottom = height; + + return result; + } + + Fixed getPlayRate(void) + { + Fixed result; + if(mPlayRate == 0.0f) + { + // Default to the movie's preferred rate + result = GetMoviePreferredRate(mMovieHandle); + if(result == 0) + { + // Don't return a 0 play rate, ever. + std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl; + result = X2Fix(1.0f); + } + } + else + { + result = X2Fix(mPlayRate); + } + + return result; + } + + void load( const std::string url ) + { + + if ( url.empty() ) + return; + + // Stop and unload any existing movie before starting another one. + unload(); + + setStatus(STATUS_LOADING); + + //In case std::string::c_str() makes a copy of the url data, + //make sure there is memory to hold it before allocating memory for handle. + //if fails, NewHandleClear(...) should return NULL. + const char* url_string = url.c_str() ; + Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) ); + + if ( NULL == handle || noErr != MemError() || NULL == *handle ) + { + setStatus(STATUS_ERROR); + return; + } + + BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) ); + + OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType ); + DisposeHandle( handle ); + if ( noErr != err ) + { + setStatus(STATUS_ERROR); + return; + }; + + // do pre-roll actions (typically fired for streaming movies but not always) + PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); + + Rect movie_rect = rectFromSize(mWidth, mHeight); + + // make a new movie controller + mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie ); + + // movie controller + MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this ); + + SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize ); + + // function that gets called when a frame is drawn + SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this ); + + setStatus(STATUS_LOADED); + + sizeChanged(); + }; + + bool unload() + { + // new movie and have to get title again + mReceivedTitle = false; + + if ( mMovieHandle ) + { + StopMovie( mMovieHandle ); + if ( mMovieController ) + { + MCMovieChanged( mMovieController, mMovieHandle ); + }; + }; + + if ( mMovieController ) + { + MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this ); + DisposeMovieController( mMovieController ); + mMovieController = NULL; + }; + + if ( mMovieHandle ) + { + SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this ); + DisposeMovie( mMovieHandle ); + mMovieHandle = NULL; + }; + + if ( mGWorldHandle ) + { + DisposeGWorld( mGWorldHandle ); + mGWorldHandle = NULL; + }; + + setStatus(STATUS_NONE); + + return true; + } + + bool navigateTo( const std::string url ) + { + unload(); + load( url ); + + return true; + }; + + bool sizeChanged() + { + if ( ! mMovieHandle ) + return false; + + // Check to see whether the movie's natural size has updated + { + int width, height; + getMovieNaturalSize(&width, &height); + if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight))) + { + mNaturalWidth = width; + mNaturalHeight = height; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); + message.setValue("name", mTextureSegmentName); + message.setValueS32("width", width); + message.setValueS32("height", height); + sendMessage(message); + //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl; + } + } + + // sanitize destination size + Rect dest_rect = rectFromSize(mWidth, mHeight); + + // media depth won't change + int depth_bits = mDepth * 8; + long rowbytes = mDepth * mTextureWidth; + + GWorldPtr old_gworld_handle = mGWorldHandle; + + if(mPixels != NULL) + { + // We have pixels. Set up a GWorld pointing at the texture. + OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes); + if ( noErr != result ) + { + // TODO: unrecoverable?? throw exception? return something? + return false; + } + } + else + { + // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally. + Rect tempRect = rectFromSize(1, 1); + OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0); + if ( noErr != result ) + { + // TODO: unrecoverable?? throw exception? return something? + return false; + } + } + + SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) ); + + // If the GWorld was already set up, delete it. + if(old_gworld_handle != NULL) + { + DisposeGWorld( old_gworld_handle ); + } + + // Set up the movie display matrix + { + // scale movie to fit rect and invert vertically to match opengl image format + MatrixRecord transform; + SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix + double scaleX = (double) mWidth / mNaturalWidth; + double scaleY = -1.0 * (double) mHeight / mNaturalHeight; + double centerX = mWidth / 2.0; + double centerY = mHeight / 2.0; + ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) ); + SetMovieMatrix( mMovieHandle, &transform ); + } + + // update movie controller + if ( mMovieController ) + { + MCSetControllerPort( mMovieController, mGWorldHandle ); + MCPositionController( mMovieController, &dest_rect, &dest_rect, + mcTopLeftMovie | mcPositionDontInvalidate ); + MCMovieChanged( mMovieController, mMovieHandle ); + } + + + // Emit event with size change so the calling app knows about it too + // TODO: + //LLMediaEvent event( this ); + //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event ); + + return true; + } + static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref ) + { + Boolean result = false; + + MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + switch( action ) + { + // handle window resizing + case mcActionControllerSizeChanged: + // Ensure that the movie draws correctly at the new size + self->sizeChanged(); + break; + + // Block any movie controller actions that open URLs. + case mcActionLinkToURL: + case mcActionGetNextURL: + case mcActionLinkToURLExtended: + // Prevent the movie controller from handling the message + result = true; + break; + + default: + break; + }; + + return result; + }; + + static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref ) + { + MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + // IMPORTANT: typically, a consumer who is observing this event will set a flag + // when this event is fired then render later. Be aware that the media stream + // can change during this period - dimensions, depth, format etc. + //LLMediaEvent event( self ); +// self->updateQuickTime(); + // TODO ^^^ + + + if ( self->mWidth > 0 && self->mHeight > 0 ) + self->setDirty( 0, 0, self->mWidth, self->mHeight ); + + return noErr; + }; + + static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) + { + //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + // TODO: + //LLMediaEvent event( self ); + //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); + }; + + + void rewind() + { + GoToBeginningOfMovie( mMovieHandle ); + MCMovieChanged( mMovieController, mMovieHandle ); + }; + + bool processState() + { + if ( mCommand == COMMAND_PLAY ) + { + if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING ) + { + long state = GetMovieLoadState( mMovieHandle ); + + if ( state >= kMovieLoadStatePlaythroughOK ) + { + // if the movie is at the end (generally because it reached it naturally) + // and we play is requested, jump back to the start of the movie. + // note: this is different from having loop flag set. + if ( IsMovieDone( mMovieHandle ) ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + rewind(); + }; + + MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + setStatus(STATUS_PLAYING); + mCommand = COMMAND_NONE; + }; + }; + } + else + if ( mCommand == COMMAND_STOP ) + { + if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED ) + { + if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + rewind(); + + setStatus(STATUS_LOADED); + mCommand = COMMAND_NONE; + }; + }; + } + else + if ( mCommand == COMMAND_PAUSE ) + { + if ( mStatus == STATUS_PLAYING ) + { + if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + setStatus(STATUS_PAUSED); + mCommand = COMMAND_NONE; + }; + }; + }; + + return true; + }; + + void play(F64 rate) + { + mPlayRate = rate; + mCommand = COMMAND_PLAY; + }; + + void stop() + { + mCommand = COMMAND_STOP; + }; + + void pause() + { + mCommand = COMMAND_PAUSE; + }; + + void getMovieNaturalSize(int *movie_width, int *movie_height) + { + Rect rect; + + GetMovieNaturalBoundsRect( mMovieHandle, &rect ); + + int width = ( rect.right - rect.left ); + int height = ( rect.bottom - rect.top ); + + // make sure width and height fall in valid range + if ( width < mMinWidth ) + width = mMinWidth; + + if ( width > mMaxWidth ) + width = mMaxWidth; + + if ( height < mMinHeight ) + height = mMinHeight; + + if ( height > mMaxHeight ) + height = mMaxHeight; + + // return the new rect + *movie_width = width; + *movie_height = height; + } + + void updateQuickTime(int milliseconds) + { + if ( ! mMovieHandle ) + return; + + if ( ! mMovieController ) + return; + + // service QuickTime + // Calling it this way doesn't have good behavior on Windows... +// MoviesTask( mMovieHandle, milliseconds ); + // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle. +// MoviesTask( mMovieHandle, 0 ); + + MCIdle( mMovieController ); + + if ( ! mGWorldHandle ) + return; + + if ( mMediaSizeChanging ) + return; + + // update state machine + processState(); + +titleChanged( "" ); + + // special code for looping - need to rewind at the end of the movie + if ( mIsLooping ) + { + // QT call to see if we are at the end - can't do with controller + if ( IsMovieDone( mMovieHandle ) ) + { + // go back to start + rewind(); + + if ( mMovieController ) + { + // kick off new play + MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); + + // set the volume + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + }; + }; + }; + }; + + int getDataWidth() const + { + if ( mGWorldHandle ) + { + int depth = mDepth; + + if (depth < 1) + depth = 1; + + // ALWAYS use the row bytes from the PixMap if we have a GWorld because + // sometimes it's not the same as mMediaDepth * mMediaWidth ! + PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle ); + return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth; + } + else + { + // TODO : return LLMediaImplCommon::getaDataWidth(); + return 0; + } + }; + + void seek( F64 time ) + { + if ( mMovieController ) + { + TimeRecord when; + when.scale = GetMovieTimeScale( mMovieHandle ); + when.base = 0; + + // 'time' is in (floating point) seconds. The timebase time will be in 'units', where + // there are 'scale' units per second. + SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) ); + + when.value.hi = ( SInt32 )( raw_time >> 32 ); + when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) ); + + MCDoAction( mMovieController, mcActionGoToTime, &when ); + }; + }; + + F64 getDuration() + { + TimeValue duration = GetMovieDuration( mMovieHandle ); + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)duration / (F64)scale; + }; + + F64 getCurrentTime() + { + TimeValue curr_time = GetMovieTime( mMovieHandle, 0 ); + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)curr_time / (F64)scale; + }; + + void setVolume( F64 volume ) + { + mCurVolume = (short)(volume * ( double ) 0x100 ); + + if ( mMovieController ) + { + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void update(int milliseconds = 0) + { + updateQuickTime(milliseconds); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseDown( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseUp( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseMove( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyPress( unsigned char key ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // Grab movie title into mMovieTitle - should be called repeatedly + // until it returns true since movie title takes a while to become + // available. + const bool getMovieTitle() + { + // grab meta data from movie + QTMetaDataRef media_data_ref; + OSErr result = QTCopyMovieMetaData( mMovieHandle, &media_data_ref ); + if ( noErr != result ) + return false; + + // look up "Display Name" in meta data + OSType meta_data_key = kQTMetaDataCommonKeyDisplayName; + QTMetaDataItem item = kQTMetaDataItemUninitialized; + result = QTMetaDataGetNextItem( media_data_ref, kQTMetaDataStorageFormatWildcard, + 0, kQTMetaDataKeyFormatCommon, + (const UInt8 *)&meta_data_key, + sizeof( meta_data_key ), &item ); + if ( noErr != result ) + return false; + + // find the size of the title + ByteCount size; + result = QTMetaDataGetItemValue( media_data_ref, item, NULL, 0, &size ); + if ( noErr != result || size <= 0 ) + return false; + + // allocate some space and grab it + UInt8* item_data = new UInt8( size ); + memset( item_data, 0, size * sizeof( UInt8* ) ); + result = QTMetaDataGetItemValue( media_data_ref, item, item_data, size, NULL ); + if ( noErr != result ) + return false; + + // save it + mMovieTitle = std::string( (char* )item_data ); + + // clean up + delete [] item_data; + + return true; + }; + + // called by this plugin when the title changes + // this creates the message and sends it back to the host app + void titleChanged( std::string title ) + { + if ( ! mReceivedTitle ) + { + if ( getMovieTitle() ) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", mMovieTitle ); + sendMessage( message ); + mReceivedTitle = true; + }; + }; + }; +}; + +MediaPluginQuickTime::MediaPluginQuickTime( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data), + mMinWidth( 0 ), + mMaxWidth( 2048 ), + mMinHeight( 0 ), + mMaxHeight( 2048 ) +{ +// std::cerr << "MediaPluginQuickTime constructor" << std::endl; + + mNaturalWidth = -1; + mNaturalHeight = -1; + mMovieHandle = 0; + mGWorldHandle = 0; + mMovieController = 0; + mCurVolume = 0x99; + mMediaSizeChanging = false; + mIsLooping = false; + mMovieTitle = std::string(); + mReceivedTitle = false; + mCommand = COMMAND_NONE; + mPlayRate = 0.0f; + mStatus = STATUS_NONE; +} + +MediaPluginQuickTime::~MediaPluginQuickTime() +{ +// std::cerr << "MediaPluginQuickTime destructor" << std::endl; + + ExitMovies(); + +#ifdef LL_WINDOWS + TerminateQTML(); +// std::cerr << "QuickTime closing down" << std::endl; +#endif +} + + +void MediaPluginQuickTime::receiveMessage(const char *message_string) +{ +// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + LLPluginMessage message_in; + + if(message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if(message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + // Normally a plugin would only specify one of these two subclasses, but this is a demo... + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + #ifdef LL_WINDOWS + + // QuickTime 7.6.4 has an issue (that was not present in 7.6.2) with initializing QuickTime + // according to this article: http://lists.apple.com/archives/QuickTime-API/2009/Sep/msg00097.html + // The solution presented there appears to work. + QTLoadLibrary("qtcf.dll"); + + // main initialization for QuickTime - only required on Windows + OSErr result = InitializeQTML( 0L ); + if ( result != noErr ) + { + //TODO: If no QT on Windows, this fails - respond accordingly. + } + else + { + //std::cerr << "QuickTime initialized" << std::endl; + }; + #endif + + // required for both Windows and Mac + EnterMovies(); + + std::string plugin_version = "QuickTime media plugin, QuickTime version "; + + long version = 0; + Gestalt( gestaltQuickTimeVersion, &version ); + std::ostringstream codec( "" ); + codec << std::hex << version << std::dec; + plugin_version += codec.str(); + message.setValue("plugin_version", plugin_version); + sendMessage(message); + + // Plugin gets to decide the texture parameters to use. + message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + #if defined(LL_WINDOWS) + // Values for Windows + mDepth = 3; + message.setValueU32("format", GL_RGB); + message.setValueU32("type", GL_UNSIGNED_BYTE); + + // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even. + // Padding to a multiple of 3*32 guarantees it'll divide out properly. + message.setValueU32("padding", 32 * 3); + #else + // Values for Mac + mDepth = 4; + message.setValueU32("format", GL_BGRA_EXT); + #ifdef __BIG_ENDIAN__ + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV ); + #else + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8); + #endif + + // Pad texture width to a multiple of 32 bytes, to line up with cache lines. + message.setValueU32("padding", 32); + #endif + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGB); + message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. + message.setValueBoolean("allow_downsample", true); + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + F64 time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + // TODO: clean up here + } + else if(message_name == "shm_added") + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer("address"); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); +// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name +// << ", size: " << info.mSize +// << ", address: " << info.mAddress +// << std::endl; + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + +// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl; + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + if(mPixels == iter->second.mAddress) + { + // This is the currently active pixel buffer. Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + + // Make sure the movie GWorld is no longer pointed at the shared segment. + sizeChanged(); + } + mSharedSegments.erase(iter); + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { +// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl; +// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl; + + mPixels = (unsigned char*)iter->second.mAddress; + mTextureSegmentName = name; + mWidth = width; + mHeight = height; + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + mMediaSizeChanging = false; + + sizeChanged(); + + update(); + }; + }; + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + load( uri ); + sendStatus(); + } + else if(message_name == "mouse_event") + { + std::string event = message_in.getValue("event"); + S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + + if(event == "down") + { + mouseDown(x, y); + } + else if(event == "up") + { + mouseUp(x, y); + } + else if(event == "move") + { + mouseMove(x, y); + }; + }; + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if(message_name == "stop") + { + stop(); + } + else if(message_name == "start") + { + F64 rate = 0.0; + if(message_in.hasValue("rate")) + { + rate = message_in.getValueReal("rate"); + } + play(rate); + } + else if(message_name == "pause") + { + pause(); + } + else if(message_name == "seek") + { + F64 time = message_in.getValueReal("time"); + seek(time); + } + else if(message_name == "set_loop") + { + bool loop = message_in.getValueBoolean("loop"); + mIsLooping = loop; + } + else if(message_name == "set_volume") + { + F64 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl; + }; + }; +} + +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data); + *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} + +#else // LL_QUICKTIME_ENABLED + +// Stubbed-out class with constructor/destructor (necessary or windows linker +// will just think its dead code and optimize it all out) +class MediaPluginQuickTime : public MediaPluginBase +{ +public: + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginQuickTime(); + /* virtual */ void receiveMessage(const char *message_string); +}; + +MediaPluginQuickTime::MediaPluginQuickTime( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data) +{ + // no-op +} + +MediaPluginQuickTime::~MediaPluginQuickTime() +{ + // no-op +} + +void MediaPluginQuickTime::receiveMessage(const char *message_string) +{ + // no-op +} + +// We're building without quicktime enabled. Just refuse to initialize. +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + return -1; +} + +#endif // LL_QUICKTIME_ENABLED diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 1b71ba1769..65872e1596 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -183,7 +183,7 @@ private: #if LL_WINDOWS // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(false); + LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_DARWIN // Disable plugins LLQtWebKit::getInstance()->enablePlugins(false); @@ -308,7 +308,16 @@ private: message.setValue("status", event.getStringValue()); sendMessage(message); } - + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onTitleChange(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", event.getStringValue()); + sendMessage(message); + } + //////////////////////////////////////////////////////////////////////////////// // virtual void onLocationChange(const EventType& event) diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp index ba66b449f3..234422b68a 100644 --- a/indra/test_apps/llplugintest/llmediaplugintest.cpp +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -138,6 +138,8 @@ LLMediaPluginTest::LLMediaPluginTest( int app_window, int window_width, int wind mMediaBrowserControlBackButtonFlag( true ), mMediaBrowserControlForwardButtonFlag( true ), mHomeWebUrl( "http://www.google.com/" ) + //mHomeWebUrl( "file:///C|/Program Files/QuickTime/Sample.mov" ) + //mHomeWebUrl( "http://movies.apple.com/movies/wb/watchmen/watchmen-tlr2_480p.mov" ) { // debugging spam std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT @@ -2008,6 +2010,11 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; break; + case MEDIA_EVENT_NAME_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_NAME_CHANGED, new name is: " << self->getMediaName() << std::endl; + glutSetWindowTitle( self->getMediaName().c_str() ); + break; + case MEDIA_EVENT_LOCATION_CHANGED: { std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; -- cgit v1.2.3 From 3831ca912eb8b78ccdcfac7d37c015ca45351c83 Mon Sep 17 00:00:00 2001 From: callum <none@none> Date: Thu, 15 Oct 2009 11:29:14 -0700 Subject: https://jira.lindenlab.com/jira/browse/DEV-40649 Media auto refreshes after Whitelist set This checkin fixes a related issue to this task and prohibits user from (A) adding an item to the whitelist that would invalidate the home URL and (b) adding a home URL that would not pass the whitelist. --- indra/newview/llfloatermediasettings.cpp | 26 + indra/newview/llfloatermediasettings.h | 2 + indra/newview/llfloatertools.cpp | 8 +- indra/newview/llpanelmediasettingsgeneral.cpp | 17 + indra/newview/llpanelmediasettingsgeneral.h | 3 +- indra/newview/llpanelmediasettingssecurity.cpp | 586 ++++++++++++--------- indra/newview/llpanelmediasettingssecurity.h | 7 + .../newview/skins/default/xui/en/notifications.xml | 13 + 8 files changed, 406 insertions(+), 256 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp index aa457de2d8..d941f24f49 100644 --- a/indra/newview/llfloatermediasettings.cpp +++ b/indra/newview/llfloatermediasettings.cpp @@ -118,6 +118,7 @@ BOOL LLFloaterMediaSettings::postBuild() mTabContainer->addTabPanel( LLTabContainer::TabPanelParams(). panel(mPanelMediaSettingsSecurity)); + mPanelMediaSettingsSecurity->setParent( this ); // restore the last tab viewed from persistance variable storage if (!mTabContainer->selectTab(gSavedSettings.getS32("LastMediaSettingsTab"))) @@ -248,3 +249,28 @@ void LLFloaterMediaSettings::enableOkApplyBtns( bool enable ) childSetEnabled( "OK", enable ); childSetEnabled( "Apply", enable ); } + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLFloaterMediaSettings::getHomeUrl() +{ + if ( mPanelMediaSettingsGeneral ) + return mPanelMediaSettingsGeneral->getHomeUrl(); + else + return std::string( "" ); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLFloaterMediaSettings::passesWhiteList( const std::string& test_url ) +{ + // sanity check - don't think this can happen + if ( mPanelMediaSettingsSecurity ) + // version in security dialog code is specialized so we pass in + // empty string for first parameter since it's not used + return mPanelMediaSettingsSecurity->passesWhiteList( "", test_url ); + else + // this is all we can do + return false; +} diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h index e2683039cc..17a47cb0f5 100644 --- a/indra/newview/llfloatermediasettings.h +++ b/indra/newview/llfloatermediasettings.h @@ -56,6 +56,8 @@ public: static void clearValues( bool editable); void enableOkApplyBtns( bool enable ); LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;}; + const std::string getHomeUrl(); + bool passesWhiteList( const std::string& test_url ); bool mIdenticalHasMediaInfo; bool mMultipleMedia; diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 320647ff1a..f2385307db 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -1114,8 +1114,8 @@ void LLFloaterTools::getMediaState() // Media data is valid if(media_data_get!=default_media_data) { - //TODO: get Meida title - //media_title = media_data_get->getTile(); + //TODO: get media title + //media_title = media_data_get->getTitle(); //LLFloaterMediaSettings::getInstance()->mIdenticalValidMedia = true; media_title = media_data_get.getHomeURL(); } @@ -1149,8 +1149,8 @@ void LLFloaterTools::getMediaState() // Media data is valid if(media_data_get!=default_media_data) { - //TODO: get Meida title - //media_title = media_data_get->getTile(); + //TODO: get media title + //media_title = media_data_get->getTitle(); media_title = media_data_get.getHomeURL(); } diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index a33d9604fe..4b25b47fa3 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -350,6 +350,16 @@ void LLPanelMediaSettingsGeneral::onClose(bool app_quitting) void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata ) { LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; + + // check url user is trying to enter for home URL will pass whitelist + // and decline to accept it if it doesn't. + std::string home_url = self->mHomeURL->getValue().asString(); + if ( ! self->mParent->passesWhiteList( home_url ) ) + { + LLNotifications::instance().add("WhiteListInvalidatesHomeUrl"); + return; + }; + self->updateMediaPreview(); } @@ -398,3 +408,10 @@ void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent ) { mParent = parent; }; + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLPanelMediaSettingsGeneral::getHomeUrl() +{ + return mHomeURL->getValue().asString(); +} diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h index 5acfa39181..c404ad127f 100644 --- a/indra/newview/llpanelmediasettingsgeneral.h +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -63,6 +63,8 @@ public: void updateMediaPreview(); void updateCurrentURL(); + + const std::string getHomeUrl(); protected: LLFloaterMediaSettings* mParent; @@ -74,7 +76,6 @@ private: LLComboBox* mControls; LLCheckBoxCtrl* mAutoLoop; LLCheckBoxCtrl* mFirstClick; -// LLTextureCtrl* mMediaPreview; LLCheckBoxCtrl* mAutoZoom; LLCheckBoxCtrl* mAutoPlay; LLCheckBoxCtrl* mAutoScale; diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp index cea105d7de..bdbc64e4b0 100644 --- a/indra/newview/llpanelmediasettingssecurity.cpp +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -1,255 +1,339 @@ -/** - * @file llpanelmediasettingssecurity.cpp - * @brief LLPanelMediaSettingsSecurity class implementation - * - * $LicenseInfo:firstyear=2009&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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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 "llviewerprecompiledheaders.h" -#include "llfloaterreg.h" -#include "llpanelmediasettingssecurity.h" -#include "llpanelcontents.h" -#include "llcheckboxctrl.h" -#include "llscrolllistctrl.h" -#include "llscrolllistitem.h" -#include "lluictrlfactory.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llsdutil.h" -#include "llselectmgr.h" -#include "llmediaentry.h" -#include "llfloaterwhitelistentry.h" -#include "llfloatermediasettings.h" -//////////////////////////////////////////////////////////////////////////////// -// -LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() -{ - // build dialog from XML - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_security.xml"); - mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this)); - mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this)); -} - -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLPanelMediaSettingsSecurity::postBuild() -{ - mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY ); - mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY ); - - childSetAction("whitelist_add", onBtnAdd, this); - childSetAction("whitelist_del", onBtnDel, this); - - setDefaultBtn("whitelist_add"); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// virtual -LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity() -{ -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLPanelMediaSettingsSecurity::draw() -{ - // housekeeping - LLPanel::draw(); - - // if list is empty, disable DEL button and checkbox to enable use of list - if ( mWhiteListList->isEmpty() ) - { - childSetEnabled( "whitelist_del", false ); - childSetEnabled( LLMediaEntry::WHITELIST_KEY, false ); - childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, false ); - } - else - { - childSetEnabled( "whitelist_del", true ); - childSetEnabled( LLMediaEntry::WHITELIST_KEY, true ); - childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, true ); - }; - - // if nothing is selected, disable DEL button - if ( mWhiteListList->getSelectedValue().asString().empty() ) - { - childSetEnabled( "whitelist_del", false ); - } - else - { - childSetEnabled( "whitelist_del", true ); - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - - if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) - { - if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) - { - self->clearValues(self, editable); - // only show multiple - return; - } - - } - else - { - if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) - { - self->clearValues(self, editable); - // only show multiple - return; - } - - } - std::string base_key( "" ); - std::string tentative_key( "" ); - - struct - { - std::string key_name; - LLUICtrl* ctrl_ptr; - std::string ctrl_type; - - } data_set [] = - { - { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" }, - { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" }, - { "", NULL , "" } - }; - - for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) - { - base_key = std::string( data_set[ i ].key_name ); - tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); - - // TODO: CP - I bet there is a better way to do this using Boost - if ( media_settings[ base_key ].isDefined() ) - { - if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) - { - static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> - setValue( media_settings[ base_key ].asBoolean() ); - } - else - if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" ) - { - // get control - LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr ); - list->deleteAllItems(); - - // points to list of white list URLs - LLSD url_list = media_settings[ base_key ]; - - // iterate over them and add to scroll list - LLSD::array_iterator iter = url_list.beginArray(); - while( iter != url_list.endArray() ) - { - // TODO: is iter guaranteed to be valid here? - std::string url = *iter; - list->addSimpleElement( url ); - ++iter; - }; - }; - data_set[ i ].ctrl_ptr->setEnabled(editable); - data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); - }; - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - self->mEnableWhiteList->clear(); - self->mWhiteListList->deleteAllItems(); - self->mEnableWhiteList->setEnabled(editable); - self->mWhiteListList->setEnabled(editable); -} -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::apply( void* userdata ) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - - // build LLSD Fragment - LLSD media_data_security; - self->getValues(media_data_security); - // this merges contents of LLSD passed in with what's there so this is ok - LLSelectMgr::getInstance()->selectionSetMediaData( media_data_security ); -} - +/** + * @file llpanelmediasettingssecurity.cpp + * @brief LLPanelMediaSettingsSecurity class implementation + * + * $LicenseInfo:firstyear=2009&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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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 "llviewerprecompiledheaders.h" +#include "llfloaterreg.h" +#include "llpanelmediasettingssecurity.h" +#include "llpanelcontents.h" +#include "llcheckboxctrl.h" +#include "llscrolllistctrl.h" +#include "llscrolllistitem.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llfloaterwhitelistentry.h" +#include "llfloatermediasettings.h" +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() : + mParent( NULL ) +{ + // build dialog from XML + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_security.xml"); + mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this)); + mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this)); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsSecurity::postBuild() +{ + mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY ); + mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY ); + + childSetAction("whitelist_add", onBtnAdd, this); + childSetAction("whitelist_del", onBtnDel, this); + + setDefaultBtn("whitelist_add"); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::draw() +{ + // housekeeping + LLPanel::draw(); + + // if list is empty, disable DEL button and checkbox to enable use of list + if ( mWhiteListList->isEmpty() ) + { + childSetEnabled( "whitelist_del", false ); + childSetEnabled( LLMediaEntry::WHITELIST_KEY, false ); + childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, false ); + } + else + { + childSetEnabled( "whitelist_del", true ); + childSetEnabled( LLMediaEntry::WHITELIST_KEY, true ); + childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, true ); + }; + + // if nothing is selected, disable DEL button + if ( mWhiteListList->getSelectedValue().asString().empty() ) + { + childSetEnabled( "whitelist_del", false ); + } + else + { + childSetEnabled( "whitelist_del", true ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + + if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) + { + if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) + { + self->clearValues(self, editable); + // only show multiple + return; + } + + } + else + { + if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) + { + self->clearValues(self, editable); + // only show multiple + return; + } + + } + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" }, + { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + { + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + } + else + if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" ) + { + // get control + LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr ); + list->deleteAllItems(); + + // points to list of white list URLs + LLSD url_list = media_settings[ base_key ]; + + // iterate over them and add to scroll list + LLSD::array_iterator iter = url_list.beginArray(); + while( iter != url_list.endArray() ) + { + // TODO: is iter guaranteed to be valid here? + std::string url = *iter; + list->addSimpleElement( url ); + ++iter; + }; + }; + data_set[ i ].ctrl_ptr->setEnabled(editable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + self->mEnableWhiteList->clear(); + self->mWhiteListList->deleteAllItems(); + self->mEnableWhiteList->setEnabled(editable); + self->mWhiteListList->setEnabled(editable); +} +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::apply( void* userdata ) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + + // build LLSD Fragment + LLSD media_data_security; + self->getValues(media_data_security); + // this merges contents of LLSD passed in with what's there so this is ok + LLSelectMgr::getInstance()->selectionSetMediaData( media_data_security ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in ) +{ + fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = mEnableWhiteList->getValue(); + + // iterate over white list and extract items + std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin(); + fill_me_in[LLMediaEntry::WHITELIST_KEY].clear(); + while( iter != white_list_items.end() ) + { + std::string white_list_url = (*iter)->getValue().asString(); + fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( white_list_url ); + ++iter; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// Try to make a valid URL if a fragment ( +// white list list box widget and build a list to test against. Can also +const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& src_url ) +{ + // make sure the fragment has a scheme + const std::string default_scheme( "http://" ); + if ( src_url.find( default_scheme ) == std::string::npos ) + { + return default_scheme + src_url; + }; + + // TODO: probably other checks and defaults we can do here.. + + // this is now a "valid" URL + return src_url; +} + +/////////////////////////////////////////////////////////////////////////////// +// wrapper for testing a URL against the whitelist. We grab entries from +// white list list box widget and build a list to test against. Can also +// optionally pass the URL that you are trying to add to the widget since +// it won't be added until this call returns. +bool LLPanelMediaSettingsSecurity::passesWhiteList( const std::string& added_url, + const std::string& test_url ) +{ + // the checkUrlAgainstWhitelist(..) function works on a vector + // of strings for the white list entries - in this panel, the white list + // is stored in the widgets themselves so we need to build something compatible. + std::vector< std::string > whitelist_strings; + whitelist_strings.clear(); // may not be required - I forget what the spec says. + + // step through whitelist widget entries and grab them as strings + std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin(); + while( iter != white_list_items.end() ) + { + const std::string whitelist_url = (*iter)->getValue().asString(); + whitelist_strings.push_back( whitelist_url ); + + ++iter; + }; + + // add in the URL that might be added to the whitelist so we can test that too + if ( added_url.length() ) + whitelist_strings.push_back( added_url ); + + // possible the URL is just a fragment so we validize it + const std::string valid_url = makeValidUrl( test_url ); + + // indicate if the URL passes whitelist + return LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_strings ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::addWhiteListItem(const std::string& url) +{ + // grab home URL from the general panel (via the parent floater) + std::string home_url( "" ); + if ( mParent ) + home_url = mParent->getHomeUrl(); + + // if the home URL is blank (user hasn't entered it yet) then + // don't bother to check if it passes the white list + if ( home_url.empty() ) + { + mWhiteListList->addSimpleElement( url ); + return; + }; + + // if the URL passes the white list, add it + if ( passesWhiteList( url, home_url ) ) + { + mWhiteListList->addSimpleElement( url ); + } + else + // display a message indicating you can't do that + { + LLNotifications::instance().add("WhiteListInvalidatesHomeUrl"); + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata ) +{ + LLFloaterReg::showInstance("whitelist_entry"); +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata ) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + + self->mWhiteListList->deleteSelectedItems(); +} + //////////////////////////////////////////////////////////////////////////////// // -void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in ) -{ - fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = mEnableWhiteList->getValue(); - - // iterate over white list and extract items - std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData(); - std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin(); - fill_me_in[LLMediaEntry::WHITELIST_KEY].clear(); - while( iter != white_list_items.end() ) - { - std::string white_list_url = (*iter)->getValue().asString(); - fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( white_list_url ); - ++iter; - }; -} - - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::addWhiteListItem(const std::string& url) -{ - mWhiteListList->addSimpleElement( url ); -} - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata ) -{ - LLFloaterReg::showInstance("whitelist_entry"); -} - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata ) +void LLPanelMediaSettingsSecurity::setParent( LLFloaterMediaSettings* parent ) { - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + mParent = parent; +}; - self->mWhiteListList->deleteSelectedItems(); -} diff --git a/indra/newview/llpanelmediasettingssecurity.h b/indra/newview/llpanelmediasettingssecurity.h index b7cf67c039..b78ee92193 100644 --- a/indra/newview/llpanelmediasettingssecurity.h +++ b/indra/newview/llpanelmediasettingssecurity.h @@ -37,6 +37,7 @@ class LLCheckBoxCtrl; class LLScrollListCtrl; +class LLFloaterMediaSettings; class LLPanelMediaSettingsSecurity : public LLPanel { @@ -52,6 +53,12 @@ class LLPanelMediaSettingsSecurity : public LLPanel static void initValues( void* userdata, const LLSD& media_settings,bool editable ); static void clearValues( void* userdata, bool editable); void addWhiteListItem(const std::string& url); + void setParent( LLFloaterMediaSettings* parent ); + const std::string makeValidUrl( const std::string& src_url ); + bool passesWhiteList( const std::string& added_url, const std::string& test_url ); + + protected: + LLFloaterMediaSettings* mParent; private: LLCheckBoxCtrl* mEnableWhiteList; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index f141a909a8..4c90d9ee8a 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -659,6 +659,19 @@ To place the media on only one face, choose Select Texture and click on the desi yestext="OK"/> </notification> + <notification + icon="alertmodal.tga" + name="WhiteListInvalidatesHomeUrl" + type="alertmodal"> +Adding this entry to the whitelist will invalidate the home URL you +specified for this instance of media. You are not allowed to do this +so the entry cannot be added to the whitelist. + <usetemplate + name="okbutton" + yestext="Ok"/> + </notification> + + <notification icon="alertmodal.tga" name="MustBeInParcel" -- cgit v1.2.3 From 590d3b9e3a2abdd37270ca64689a64c4e879fbe6 Mon Sep 17 00:00:00 2001 From: Steve Bennetts <steve@lindenlab.com> Date: Thu, 15 Oct 2009 14:14:46 -0700 Subject: EXT-1279 Crash if font file not found. Added getFontDefault() to llfontgl.h --HG-- branch : branch-test --- indra/llrender/llfontgl.cpp | 7 +++++++ indra/llrender/llfontgl.h | 1 + indra/llrender/llfontregistry.cpp | 5 ++++- indra/llui/llconsole.cpp | 5 +++++ indra/llui/llui.cpp | 7 ++++++- indra/newview/llfolderviewitem.cpp | 4 ++++ 6 files changed, 27 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index f7bab3de67..16c99599cf 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -910,6 +910,13 @@ LLFontGL* LLFontGL::getFontByName(const std::string& name) } } +//static +LLFontGL* LLFontGL::getFontDefault() +{ + return getFontSansSerif(); // Fallback to sans serif as default font +} + + // static std::string LLFontGL::getFontPathSystem() { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index ad84b6d641..d6d6436aee 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -167,6 +167,7 @@ public: static LLFontGL* getFont(const LLFontDescriptor& desc); // Use with legacy names like "SANSSERIF_SMALL" or "OCRA" static LLFontGL* getFontByName(const std::string& name); + static LLFontGL* getFontDefault(); // default fallback font static std::string getFontPathLocal(); static std::string getFontPathSystem(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 45573cd817..7a3d6ec4f2 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -380,7 +380,10 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) LLFontDescriptor nearest_exact_desc = *match_desc; nearest_exact_desc.setSize(norm_desc.getSize()); font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc); - if (it != mFontMap.end()) + // If we fail to find a font in the fonts directory, it->second might be NULL. + // We shouldn't construcnt a font with a NULL mFontFreetype. + // This may not be the best solution, but it at least prevents a crash. + if (it != mFontMap.end() && it->second != NULL) { llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl; diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 285ce82d2d..e0053b4cc7 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -120,6 +120,11 @@ void LLConsole::setFontSize(S32 size_index) { mFont = LLFontGL::getFontSansSerifHuge(); } + // Make sure the font exists + if (mFont == NULL) + { + mFont = LLFontGL::getFontDefault(); + } for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) { diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index c89e5944fa..eb1b60244d 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1954,7 +1954,12 @@ namespace LLInitParam return fontp; } } - + + if (mData.mValue == NULL) + { + mData.mValue = LLFontGL::getFontDefault(); + } + // default to current value return mData.mValue; } diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index ee5fba5ace..ea1275aad1 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -70,6 +70,10 @@ LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) { LLFontDescriptor labelfontdesc("SansSerif", "Small", style); rtn = LLFontGL::getFont(labelfontdesc); + if (!rtn) + { + rtn = LLFontGL::getFontDefault(); + } sFonts[style] = rtn; } return rtn; -- cgit v1.2.3 From 12628429d8553cf242a38f820187552b6fe93628 Mon Sep 17 00:00:00 2001 From: callum <none@none> Date: Thu, 15 Oct 2009 14:49:27 -0700 Subject: Fixed defect for URLs that already have a scheme See http://10.1.19.90:8080/go?page=ReviewDisplay&reviewid=18 --- indra/newview/llpanelmediasettingssecurity.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp index bdbc64e4b0..f5607aa287 100644 --- a/indra/newview/llpanelmediasettingssecurity.cpp +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -236,16 +236,20 @@ void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in ) // white list list box widget and build a list to test against. Can also const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& src_url ) { - // make sure the fragment has a scheme - const std::string default_scheme( "http://" ); - if ( src_url.find( default_scheme ) == std::string::npos ) - { + // use LLURI to determine if we have a valid scheme + LLURI candidate_url( src_url ); + if ( candidate_url.scheme().empty() ) + { + // build a URL comprised of default scheme and the original fragment + const std::string default_scheme( "http://" ); return default_scheme + src_url; - }; - - // TODO: probably other checks and defaults we can do here.. + }; + + // we *could* test the "default scheme" + "original fragment" URL again + // using LLURI to see if it's valid but I think the outcome is the same + // in either case - our only option is to return the original URL - // this is now a "valid" URL + // we *think* the original url passed in was valid return src_url; } -- cgit v1.2.3 From 113053b504971cb36c7f67f4d0cd56983910a523 Mon Sep 17 00:00:00 2001 From: callum <none@none> Date: Thu, 15 Oct 2009 15:08:10 -0700 Subject: Fixed defects - added back getLoadedDuration() code that mysteriously vanished and fix name of function that checks the title. See http://10.1.19.90:8080/go?page=ReviewDisplay&reviewid=19 --- .../quicktime/media_plugin_quicktime.cpp | 28 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 9b378fcd92..7100d03f05 100644 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -532,7 +532,8 @@ private: // update state machine processState(); -titleChanged( "" ); + // see if title arrived and if so, update member variable with contents + checkTitle(); // special code for looping - need to rewind at the end of the movie if ( mIsLooping ) @@ -595,6 +596,19 @@ titleChanged( "" ); }; }; + F64 getLoadedDuration() + { + TimeValue duration; + if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr) + { + // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie. + duration = GetMovieDuration( mMovieHandle ); + } + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)duration / (F64)scale; + }; + F64 getDuration() { TimeValue duration = GetMovieDuration( mMovieHandle ); @@ -696,17 +710,23 @@ titleChanged( "" ); return true; }; - // called by this plugin when the title changes - // this creates the message and sends it back to the host app - void titleChanged( std::string title ) + // called regularly to see if title changed + void checkTitle() { + // we did already receive title so keep checking if ( ! mReceivedTitle ) { + // grab title from movie meta data if ( getMovieTitle() ) { + // pass back to host application LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); message.setValue("name", mMovieTitle ); sendMessage( message ); + + // stop looking once we find a title for this movie. + // TODO: this may to be reset if movie title changes + // during playback but this is okay for now mReceivedTitle = true; }; }; -- cgit v1.2.3 From 2cb043a6262e61809c26496a6ec197179806e0d4 Mon Sep 17 00:00:00 2001 From: Rick Pasetto <rick@lindenlab.com> Date: Thu, 15 Oct 2009 16:30:48 -0700 Subject: FIX DEV-39513: Tweak target camera position to avoid a flip when target normal is vertical Review #21 This attempts to tweak the target camera position slightly towards where the camera started from, so as to avoid the flip noted in this JIRA. --- indra/newview/llviewermediafocus.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index db31714f16..74ed6eea7b 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -227,8 +227,27 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) distance += depth * 0.5; // Finally animate the camera to this new position and focal point - gAgent.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), - LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + LLVector3d camera_pos, target_pos; + // Target lookat position is the center of the selection (in global coords) + target_pos = LLSelectMgr::getInstance()->getSelectionCenterGlobal(); + // Target look-from (camera) position is "distance" away from the target along the normal + LLVector3d pickNormal = LLVector3d(pick.mNormal); + pickNormal.normalize(); + camera_pos = target_pos + pickNormal * distance; + if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) + { + // If the normal points directly up, the camera will "flip" around. + // We try to avoid this by adjusting the target camera position a + // smidge towards current camera position + LLVector3d cur_camera_pos = LLVector3d(gAgent.getCameraPositionGlobal()); + LLVector3d delta = (cur_camera_pos - camera_pos); + F64 len = delta.length(); + delta.normalize(); + // Move 1% of the distance towards original camera location + camera_pos += 0.01 * len * delta; + } + + gAgent.setCameraPosAndFocusGlobal(camera_pos, target_pos, LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); } } void LLViewerMediaFocus::onFocusReceived() -- cgit v1.2.3 From 665bac16cef36e265829d6692959daacc9811451 Mon Sep 17 00:00:00 2001 From: Rick Pasetto <rick@lindenlab.com> Date: Thu, 15 Oct 2009 17:33:42 -0700 Subject: Clarify comments a little --- indra/newview/llviewermediafocus.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index 74ed6eea7b..d81c332d54 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -228,7 +228,7 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) // Finally animate the camera to this new position and focal point LLVector3d camera_pos, target_pos; - // Target lookat position is the center of the selection (in global coords) + // The target lookat position is the center of the selection (in global coords) target_pos = LLSelectMgr::getInstance()->getSelectionCenterGlobal(); // Target look-from (camera) position is "distance" away from the target along the normal LLVector3d pickNormal = LLVector3d(pick.mNormal); @@ -239,6 +239,14 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) // If the normal points directly up, the camera will "flip" around. // We try to avoid this by adjusting the target camera position a // smidge towards current camera position + // *NOTE: this solution is not perfect. All it attempts to solve is the + // "looking down" problem where the camera flips around when it animates + // to that position. You still are not guaranteed to be looking at the + // media in the correct orientation. What this solution does is it will + // put the camera into position keeping as best it can the current + // orientation with respect to the face. In other words, if before zoom + // the media appears "upside down" from the camera, after zooming it will + // still be upside down, but at least it will not flip. LLVector3d cur_camera_pos = LLVector3d(gAgent.getCameraPositionGlobal()); LLVector3d delta = (cur_camera_pos - camera_pos); F64 len = delta.length(); -- cgit v1.2.3 From 90378004089e35f54301ee9b348a39d87e7b6f40 Mon Sep 17 00:00:00 2001 From: Steve Bennetts <steve@lindenlab.com> Date: Thu, 15 Oct 2009 18:07:39 -0700 Subject: EXT-651 - Added Upload Image shortcut to Advanced > Keyboard Shortcuts. Also cleaned up that menu a little. --HG-- branch : branch-test --- indra/newview/skins/default/xui/en/menu_viewer.xml | 78 +++++++++++----------- 1 file changed, 38 insertions(+), 40 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b0839ce255..52a0877b6e 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1065,34 +1065,46 @@ layout="topleft" name="Search" shortcut="control|F"> - <menu_item_check.on_check - function="Floater.Visible" - parameter="search" /> - <menu_item_check.on_click - function="Floater.Toggle" - parameter="search" /> + <menu_item_check.on_check + function="Floater.Visible" + parameter="search" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="search" /> </menu_item_check> <menu_item_call - enabled="false" - label="Release Keys" + label="Upload Image (L$[COST])..." + layout="topleft" + name="Upload Image" + shortcut="control|U"> + <menu_item_call.on_click + function="File.UploadImage" + parameter="" /> + <menu_item_call.on_enable + function="File.EnableUpload" /> + </menu_item_call> + <menu_item_separator/> + <menu_item_call + label="Close Window" layout="topleft" - name="Release Keys"> + name="Close Window" + shortcut="control|W"> <menu_item_call.on_click - function="Tools.ReleaseKeys" - parameter="" /> + function="File.CloseWindow" /> <menu_item_call.on_enable - function="Tools.EnableReleaseKeys" - parameter="" /> + function="File.EnableCloseWindow" /> </menu_item_call> <menu_item_call - label="Set UI Size to Default" + label="Close All Windows" layout="topleft" - name="Set UI Size to Default"> + name="Close All Windows" + shortcut="control|shift|W"> <menu_item_call.on_click - function="View.DefaultUISize" /> + function="File.CloseAllWindows" /> + <menu_item_call.on_enable + function="File.EnableCloseAllWindows" /> </menu_item_call> - <menu_item_separator - layout="topleft" /> + <menu_item_separator/> <menu_item_check label="Always Run" layout="topleft" @@ -1113,28 +1125,6 @@ <menu_item_check.on_enable function="Agent.enableFlying" /> </menu_item_check> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="Close Window" - layout="topleft" - name="Close Window" - shortcut="control|W"> - <menu_item_call.on_click - function="File.CloseWindow" /> - <menu_item_call.on_enable - function="File.EnableCloseWindow" /> - </menu_item_call> - <menu_item_call - label="Close All Windows" - layout="topleft" - name="Close All Windows" - shortcut="control|shift|W"> - <menu_item_call.on_click - function="File.CloseAllWindows" /> - <menu_item_call.on_enable - function="File.EnableCloseAllWindows" /> - </menu_item_call> <menu_item_separator layout="topleft" /> <menu_item_call @@ -1305,6 +1295,14 @@ <menu_item_call.on_click function="Advanced.RebakeTextures" /> </menu_item_call> + <menu_item_call + label="Set UI Size to Default" + layout="topleft" + name="Set UI Size to Default"> + <menu_item_call.on_click + function="View.DefaultUISize" /> + </menu_item_call> + <menu_item_separator/> <menu_item_check label="Limit Select Distance" layout="topleft" -- cgit v1.2.3 From 86787b58edf59997b68dca6a0927d24b2a24a2b5 Mon Sep 17 00:00:00 2001 From: Monroe Linden <monroe@lindenlab.com> Date: Thu, 15 Oct 2009 18:42:30 -0700 Subject: Major refactor of LLViewerMediaFocus and LLPanelMediaHUD. LLViewerMediaFocus now tracks two separate objects: the currently focused media object, and the media object that's currently being hovered over. It no longer stores smart pointers to either the LLViewerObject or the LLViewerMediaImpl -- it now looks up both by UUID every time they're needed, and fails gracefully if either goes away. This will prevent it from keeping objects from being deleted. The poorly-understood "mouseOverFlag" has been expunged. LLViewerMediaFocus no longer uses LLSelectMgr at all. The object to focus on is explicitly passed between LLViewerMediaFocus and LLPanelMediaHUD instead of going indirectly through the selection manager. LLViewerMediaFocus also no longer interacts with the pick from LLToolPie -- the data it needs from the pick (the object and normal vector) is passed explicitly. LLViewerMediaFocus::setCameraZoom and LLViewerMediaFocus::getBBoxAspectRatio now have no dependencies on the LLViewerMediaFocus object -- all the data they need is passed in when they're called by the LLPanelMediaHUD. I made them static member functions, but they could be moved to LLPanelMediaHUD or even made into file-scoped static functions. The only reason I didn't do either of those is that it seems like they belong with the LLViewerMediaFocus code as opposed to the HUD. --- indra/newview/llagent.cpp | 2 +- indra/newview/llappviewer.cpp | 1 - indra/newview/llselectmgr.cpp | 20 ++- indra/newview/lltoolpie.cpp | 72 ++++----- indra/newview/llviewermedia.cpp | 49 +++++- indra/newview/llviewermedia.h | 5 +- indra/newview/llviewermediafocus.cpp | 303 ++++++++++++++++++----------------- indra/newview/llviewermediafocus.h | 57 ++++--- 8 files changed, 289 insertions(+), 220 deletions(-) (limited to 'indra') diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ab9db303b5..d6fe9a20ed 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -5975,7 +5975,7 @@ bool LLAgent::teleportCore(bool is_local) LLFloaterReg::hideInstance("about_land"); LLViewerParcelMgr::getInstance()->deselectLand(); - LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL); + LLViewerMediaFocus::getInstance()->clearFocus(); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a631314d5b..8544d6fb29 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1518,7 +1518,6 @@ bool LLAppViewer::cleanup() //Note: //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() //because some new image might be generated during cleaning up media. --bao - LLViewerMediaFocus::cleanupClass(); LLViewerMedia::cleanupClass(); LLViewerParcelMedia::cleanupClass(); gTextureList.shutdown(); // shutdown again in case a callback added something diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index d163ceb30e..61a203f442 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -4903,7 +4903,6 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { inspect_item_id = inspect_instance->getSelectedUUID(); } - LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getSelectedUUID(); for (S32 pass = 0; pass < 2; pass++) { for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); @@ -4917,11 +4916,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { continue; } - if (objectp->getID() == focus_item_id) - { - node->renderOneSilhouette(gFocusMgr.getFocusColor()); - } - else if(objectp->getID() == inspect_item_id) + if(objectp->getID() == inspect_item_id) { node->renderOneSilhouette(sHighlightInspectColor); } @@ -4975,6 +4970,19 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) } } +#if 0 + // Hilight focused media object + { + LLViewerObject* objectp = LLViewerMediaFocus::getInstance()->getFocusedObject(); + if(objectp) + { + // FIXME: how do I construct a silhouette for an object that's not selected? + // Would we need to add another LLObjectSelectionHandle for this purpose? + node->renderOneSilhouette(gFocusMgr.getFocusColor()); + } + } +#endif + if (for_hud && avatar) { glMatrixMode(GL_PROJECTION); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index beb16c267e..d472626fab 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -518,11 +518,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) if(!object) { - // We need to clear media hover flag - if (LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); - } + LLViewerMediaFocus::getInstance()->clearHover(); } } @@ -1027,7 +1023,6 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs()) { - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -1035,11 +1030,7 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); LLMediaEntry* mep = (tep->hasMedia()) ? tep->getMediaData() : NULL; viewer_media_t media_impl = mep ? LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()) : NULL; @@ -1049,11 +1040,9 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) && gSavedSettings.getBOOL("MediaOnAPrimUI") && media_impl.notNull()) { - // LLObjectSelectionHandle selection = /*LLViewerMediaFocus::getInstance()->getSelection()*/ LLSelectMgr::getInstance()->getSelection(); - if (/*! selection->contains(pick.getObject(), pick.mObjectFace)*/ - ! LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) ) + if (!LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) ) { - LLViewerMediaFocus::getInstance()->setFocusFace(TRUE, pick.getObject(), pick.mObjectFace, media_impl); + LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal); } else { @@ -1065,7 +1054,6 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) return true; } - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -1079,50 +1067,50 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick) LLPointer<LLViewerObject> objectp = pick.getObject(); - // Early out cases. Must clear mouse over media focus flag + // Early out cases. Must clear media hover. // did not hit an object or did not hit a valid face if ( objectp.isNull() || pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs() ) { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + LLViewerMediaFocus::getInstance()->clearHover(); return false; } - - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; if (mep && gSavedSettings.getBOOL("MediaOnAPrimUI")) { viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); - if(LLViewerMediaFocus::getInstance()->getFocus() && media_impl.notNull()) - { - media_impl->mouseMove(pick.mUVCoords); - - gViewerWindow->setCursor(media_impl->getLastSetCursor()); - } - else + + if(media_impl.notNull()) { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } + // Update media hover object + if (!LLViewerMediaFocus::getInstance()->isHoveringOverFace(objectp, pick.mObjectFace)) + { + LLViewerMediaFocus::getInstance()->setHoverFace(objectp, pick.mObjectFace, media_impl, pick.mNormal); + } + + // If this is the focused media face, send mouse move events. + if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace)) + { + media_impl->mouseMove(pick.mUVCoords); + gViewerWindow->setCursor(media_impl->getLastSetCursor()); + } + else + { + // This is not the focused face -- set the default cursor. + gViewerWindow->setCursor(UI_CURSOR_ARROW); + } - // Set mouse over flag if unset - if (! LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLSelectMgr::getInstance()->setHoverObject(objectp, pick.mObjectFace); - LLViewerMediaFocus::getInstance()->setMouseOverFlag(true, media_impl); - LLViewerMediaFocus::getInstance()->setPickInfo(pick); + return true; } - - return true; } - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + + // In all other cases, clear media hover. + LLViewerMediaFocus::getInstance()->clearHover(); return false; } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 6a40c76757..b8cf3e667e 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -836,7 +836,15 @@ void LLViewerMediaImpl::stop() { if(mMediaSource) { - mMediaSource->stop(); + if(mMediaSource->pluginSupportsMediaBrowser()) + { + mMediaSource->browse_stop(); + } + else + { + mMediaSource->stop(); + } + // destroyMediaSource(); } } @@ -1008,6 +1016,45 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) return TRUE; } + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateBack() +{ + if (mMediaSource) + { + if(mMediaSource->pluginSupportsMediaTime()) + { + mMediaSource->start(-2.0); + } + else + { + mMediaSource->browse_back(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateForward() +{ + if (mMediaSource) + { + if(mMediaSource->pluginSupportsMediaTime()) + { + mMediaSource->start(2.0); + } + else + { + mMediaSource->browse_forward(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateReload() +{ + navigateTo(mMediaURL, "", true, false); +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateHome() { diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 37aabcf2d6..05c67eda47 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -141,7 +141,10 @@ public: void mouseMove(const LLVector2& texture_coords); void mouseLeftDoubleClick(S32 x,S32 y ); void mouseCapture(); - + + void navigateBack(); + void navigateForward(); + void navigateReload(); void navigateHome(); void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false); void navigateStop(); diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index d81c332d54..cad8b5f0ce 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -53,7 +53,8 @@ // LLViewerMediaFocus::LLViewerMediaFocus() -: mMouseOverFlag(false) +: mFocusedObjectFace(0), + mHoverObjectFace(0) { } @@ -63,110 +64,100 @@ LLViewerMediaFocus::~LLViewerMediaFocus() // Clean up in cleanupClass() instead. } -void LLViewerMediaFocus::cleanupClass() -{ - LLViewerMediaFocus *self = LLViewerMediaFocus::getInstance(); - - if(self) - { - // mMediaHUD will have been deleted by this point -- don't try to delete it. - - /* Richard says: - all widgets are supposed to be destroyed at the same time - you shouldn't hold on to pointer to them outside of ui code - you can use the LLHandle approach - if you want to be type safe, you'll need to add a LLRootHandle to whatever derived class you are pointing to - look at llview::gethandle - its our version of a weak pointer - */ - if(self->mMediaHUD.get()) - { - self->mMediaHUD.get()->setMediaImpl(NULL); - } - self->mMediaImpl = NULL; - } - -} - - -void LLViewerMediaFocus::setFocusFace( BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl ) -{ +void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(mMediaImpl.notNull()) + + LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl(); + if(old_media_impl) { - mMediaImpl->focus(false); + old_media_impl->focus(false); } - if (b && media_impl.notNull()) + if (media_impl.notNull() && objectp.notNull()) { bool face_auto_zoom = false; - mMediaImpl = media_impl; - mMediaImpl->focus(true); + media_impl->focus(true); - LLSelectMgr::getInstance()->deselectAll(); - LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + mFocusedImplID = media_impl->getMediaTextureID(); + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + mFocusedObjectNormal = pick_normal; - if(objectp.notNull()) + LLTextureEntry* tep = objectp->getTE(face); + if(tep->hasMedia()) { - LLTextureEntry* tep = objectp->getTE(face); - if(! tep->hasMedia()) - { - // Error condition - } LLMediaEntry* mep = tep->getMediaData(); face_auto_zoom = mep->getAutoZoom(); - if(! mep->getAutoPlay()) + if(!media_impl->hasMedia()) { std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL(); media_impl->navigateTo(url, "", true); } } - mFocus = LLSelectMgr::getInstance()->getSelection(); + else + { + // This should never happen. + llwarns << "Can't find media entry for focused face" << llendl; + } + + gFocusMgr.setKeyboardFocus(this); + + // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. + update(); + if(mMediaHUD.get() && face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) { mMediaHUD.get()->resetZoomLevel(); mMediaHUD.get()->nextZoomLevel(); } - if (!mFocus->isEmpty()) - { - gFocusMgr.setKeyboardFocus(this); - } - mObjectID = objectp->getID(); - mObjectFace = face; - // LLViewerMedia::addObserver(this, mObjectID); - - } else { - gFocusMgr.setKeyboardFocus(NULL); - if(! parcel->getMediaPreventCameraZoom()) + if(hasFocus()) { - if (!mFocus->isEmpty()) + if(mMediaHUD.get()) { - gAgent.setFocusOnAvatar(TRUE, ANIMATE); + mMediaHUD.get()->resetZoomLevel(); } + + gFocusMgr.setKeyboardFocus(NULL); } - mFocus = NULL; - // LLViewerMedia::remObserver(this, mObjectID); - // Null out the media hud media pointer - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaImpl(NULL); - } + mFocusedImplID = LLUUID::null; + mFocusedObjectID = LLUUID::null; + mFocusedObjectFace = 0; + } +} - // and null out the media impl - mMediaImpl = NULL; - mObjectID = LLUUID::null; - mObjectFace = 0; +void LLViewerMediaFocus::clearFocus() +{ + setFocusFace(NULL, 0, NULL); +} + +void LLViewerMediaFocus::setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull()) + { + mHoverImplID = media_impl->getMediaTextureID(); + mHoverObjectID = objectp->getID(); + mHoverObjectFace = face; + mHoverObjectNormal = pick_normal; } - if(mMediaHUD.get()) + else { - mMediaHUD.get()->setMediaFocus(b); + mHoverObjectID = LLUUID::null; + mHoverObjectFace = 0; + mHoverImplID = LLUUID::null; } } + +void LLViewerMediaFocus::clearHover() +{ + setHoverFace(NULL, 0, NULL); +} + + bool LLViewerMediaFocus::getFocus() { if (gFocusMgr.getKeyboardFocus() == this) @@ -176,22 +167,15 @@ bool LLViewerMediaFocus::getFocus() return false; } -// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value -void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) +// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value +void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor) { - LLPickInfo& pick = LLToolPie::getInstance()->getPick(); - - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - pick = mPickInfo; - setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); - } - - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + if (object) { gAgent.setFocusOnAvatar(FALSE, ANIMATE); - LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + LLBBox bbox = object->getBoundingBoxAgent(); + LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); F32 height; F32 width; F32 depth; @@ -199,7 +183,7 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. - F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); + F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for @@ -229,9 +213,9 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) // Finally animate the camera to this new position and focal point LLVector3d camera_pos, target_pos; // The target lookat position is the center of the selection (in global coords) - target_pos = LLSelectMgr::getInstance()->getSelectionCenterGlobal(); + target_pos = center; // Target look-from (camera) position is "distance" away from the target along the normal - LLVector3d pickNormal = LLVector3d(pick.mNormal); + LLVector3d pickNormal = LLVector3d(normal); pickNormal.normalize(); camera_pos = target_pos + pickNormal * distance; if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) @@ -255,92 +239,69 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) camera_pos += 0.01 * len * delta; } - gAgent.setCameraPosAndFocusGlobal(camera_pos, target_pos, LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + gAgent.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); + } + else + { + // If we have no object, focus back on the avatar. + gAgent.setFocusOnAvatar(TRUE, ANIMATE); } } void LLViewerMediaFocus::onFocusReceived() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(true); + // Don't do this here -- this doesn't change "inworld media focus", it just changes whether the viewer's input is focused on the media. +// LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); +// if(media_impl.notNull()) +// media_impl->focus(true); LLFocusableElement::onFocusReceived(); } void LLViewerMediaFocus::onFocusLost() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(false); + // Don't do this here -- this doesn't change "inworld media focus", it just changes whether the viewer's input is focused on the media. +// LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); +// if(media_impl.notNull()) +// media_impl->focus(false); + gViewerWindow->focusClient(); - mFocus = NULL; LLFocusableElement::onFocusLost(); } -void LLViewerMediaFocus::setMouseOverFlag(bool b, viewer_media_t media_impl) + +BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if (b && media_impl.notNull()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) { - if(! mMediaHUD.get()) - { - LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(mMediaImpl); - mMediaHUD = media_hud->getHandle(); - gHUDView->addChild(media_hud); - } - mMediaHUD.get()->setMediaImpl(media_impl); + media_impl->handleKeyHere(key, mask); - if(mMediaImpl.notNull() && (mMediaImpl != media_impl)) + if (key == KEY_ESCAPE) { - mMediaImpl->focus(false); + clearFocus(); } - - mMediaImpl = media_impl; - } - mMouseOverFlag = b; -} -LLUUID LLViewerMediaFocus::getSelectedUUID() -{ - LLViewerObject* object = mFocus->getFirstObject(); - return object ? object->getID() : LLUUID::null; -} -#if 0 // Must re-implement when the new media api event system is ready -void LLViewerMediaFocus::onNavigateComplete( const EventType& event_in ) -{ - if (hasFocus() && mLastURL != event_in.getStringValue()) - { - LLViewerMedia::focus(true, mObjectID); - // spoof mouse event to reassert focus - LLViewerMedia::mouseDown(1,1, mObjectID); - LLViewerMedia::mouseUp(1,1, mObjectID); - } - mLastURL = event_in.getStringValue(); -} -#endif -BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) -{ - if(mMediaImpl.notNull()) - mMediaImpl->handleKeyHere(key, mask); - - if (key == KEY_ESCAPE && mMediaHUD.get()) - { - mMediaHUD.get()->close(); } + return true; } BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleUnicodeCharHere(uni_char); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->handleUnicodeCharHere(uni_char); return true; } BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) { BOOL retval = FALSE; - if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl && media_impl->hasMedia()) { // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y. // The latter is the position of the mouse at the time of the event // The former is the 'scroll amount' in x and y, respectively. // All we have for 'scroll amount' here is 'clicks', and no mask. - mMediaImpl->getMediaPlugin()->scrollEvent(0, clicks, /*mask*/0); + media_impl->getMediaPlugin()->scrollEvent(0, clicks, /*mask*/0); retval = TRUE; } return retval; @@ -348,19 +309,45 @@ BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLViewerMediaFocus::update() { - if (mMediaHUD.get()) + LLViewerMediaImpl *media_impl = getFocusedMediaImpl(); + LLViewerObject *viewer_object = getFocusedObject(); + S32 face = mFocusedObjectFace; + LLVector3 normal = mFocusedObjectNormal; + bool focus = true; + + if(!media_impl || !viewer_object) + { + focus = false; + media_impl = getHoverMediaImpl(); + viewer_object = getHoverObject(); + face = mHoverObjectFace; + normal = mHoverObjectNormal; + } + + if(media_impl && viewer_object) { - if(mFocus.notNull() || mMouseOverFlag || mMediaHUD.get()->isMouseOver()) + // We have an object and impl to point at. + + // Make sure the media HUD object exists. + if(! mMediaHUD.get()) { - // mMediaHUD.get()->setVisible(true); - mMediaHUD.get()->updateShape(); + LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(); + mMediaHUD = media_hud->getHandle(); + gHUDView->addChild(media_hud); } - else + mMediaHUD.get()->setMediaFace(viewer_object, face, media_impl, normal); + } + else + { + // The media HUD is no longer needed. + if(mMediaHUD.get()) { - mMediaHUD.get()->setVisible(false); + mMediaHUD.get()->setMediaFace(NULL, 0, NULL); } } } + + // This function calculates the aspect ratio and the world aligned components of a selection bounding box. F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) { @@ -429,5 +416,31 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& bool LLViewerMediaFocus::isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face) { - return objectp->getID() == mObjectID && face == mObjectFace; + return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace; +} + +bool LLViewerMediaFocus::isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face) +{ + return objectp->getID() == mHoverObjectID && face == mHoverObjectFace; +} + + +LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mFocusedImplID); +} + +LLViewerObject* LLViewerMediaFocus::getFocusedObject() +{ + return gObjectList.findObject(mFocusedObjectID); +} + +LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mHoverImplID); +} + +LLViewerObject* LLViewerMediaFocus::getHoverObject() +{ + return gObjectList.findObject(mHoverObjectID); } diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index 2688a8b708..d5e3e6019c 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -50,44 +50,55 @@ public: LLViewerMediaFocus(); ~LLViewerMediaFocus(); - static void cleanupClass(); - - void setFocusFace(BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl); - void clearFocus() { setFocusFace(false, NULL, 0, NULL); } + // Set/clear the face that has media focus (takes keyboard input and has the full set of controls) + void setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3d::zero); + void clearFocus(); + + // Set/clear the face that has "media hover" (has the mimimal set of controls to zoom in or pop out into a media browser). + // If a media face has focus, the media hover will be ignored. + void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3d::zero); + void clearHover(); + /*virtual*/ bool getFocus(); - /*virtual*/ // void onNavigateComplete( const EventType& event_in ); - /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - LLUUID getSelectedUUID(); - LLObjectSelectionHandle getSelection() { return mFocus; } - void update(); + + static void setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor); + static F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - void setCameraZoom(F32 padding_factor); - void setMouseOverFlag(bool b, viewer_media_t media_impl = NULL); - bool getMouseOverFlag() { return mMouseOverFlag; } - void setPickInfo(LLPickInfo pick_info) { mPickInfo = pick_info; } - F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - - // TODO: figure out why selection mgr hates me bool isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face); + bool isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face); + + // These look up (by uuid) and return the values that were set with setFocusFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getFocusedMediaImpl(); + LLViewerObject* getFocusedObject(); + S32 getFocusedFace() { return mFocusedObjectFace; } + + // These look up (by uuid) and return the values that were set with setHoverFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getHoverMediaImpl(); + LLViewerObject* getHoverObject(); + S32 getHoverFace() { return mHoverObjectFace; } protected: /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); private: - LLObjectSelectionHandle mFocus; - std::string mLastURL; - bool mMouseOverFlag; - LLPickInfo mPickInfo; + LLHandle<LLPanelMediaHUD> mMediaHUD; - LLUUID mObjectID; - S32 mObjectFace; - viewer_media_t mMediaImpl; + + LLUUID mFocusedObjectID; + S32 mFocusedObjectFace; + LLUUID mFocusedImplID; + LLVector3 mFocusedObjectNormal; + + LLUUID mHoverObjectID; + S32 mHoverObjectFace; + LLUUID mHoverImplID; + LLVector3 mHoverObjectNormal; }; -- cgit v1.2.3 From 451e5b7174aed801f0dc536a93347b5c6ed73ca2 Mon Sep 17 00:00:00 2001 From: Monroe Linden <monroe@lindenlab.com> Date: Thu, 15 Oct 2009 18:58:03 -0700 Subject: Fix for a "case not handled in switch" due to the addition of MEDIA_EVENT_NAME_CHANGED. --- indra/newview/llmediactrl.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra') diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 26ff219437..8005cd1180 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -911,6 +911,12 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL; }; break; + + case MEDIA_EVENT_NAME_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL; + }; + break; }; // chain all events to any potential observers of this object. -- cgit v1.2.3 From ac91bc0115d0f8d34f03819e25bb278f559e9f28 Mon Sep 17 00:00:00 2001 From: Monroe Linden <monroe@lindenlab.com> Date: Thu, 15 Oct 2009 19:01:54 -0700 Subject: Fix for another "case not handled in switch" due to the addition of MEDIA_EVENT_NAME_CHANGED. --- indra/newview/llviewerparcelmedia.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra') diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 9bcdcbf9ad..6233a337a6 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -552,6 +552,12 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL; }; break; + + case MEDIA_EVENT_NAME_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL; + }; + break; }; } -- cgit v1.2.3 From cb1b361b49c9ae6a76eaeeb4476a341a024a71f1 Mon Sep 17 00:00:00 2001 From: Rick Pasetto <rick@lindenlab.com> Date: Fri, 16 Oct 2009 10:34:16 -0700 Subject: FIX windows build: no automatic conversoin between LLVector3 and LLVector3d?? --- indra/newview/llviewermediafocus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index d5e3e6019c..c77533ba5a 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -51,12 +51,12 @@ public: ~LLViewerMediaFocus(); // Set/clear the face that has media focus (takes keyboard input and has the full set of controls) - void setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3d::zero); + void setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); void clearFocus(); // Set/clear the face that has "media hover" (has the mimimal set of controls to zoom in or pop out into a media browser). // If a media face has focus, the media hover will be ignored. - void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3d::zero); + void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); void clearHover(); /*virtual*/ bool getFocus(); -- cgit v1.2.3