From 17f9fd5f9653077b6059b34147a447310a30daec Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Fri, 25 Sep 2009 19:23:24 -0700 Subject: Changes force no crt manifest linking in windows plugin files. --- indra/media_plugins/quicktime/CMakeLists.txt | 8 ++++++++ indra/media_plugins/webkit/CMakeLists.txt | 8 ++++++++ 2 files changed, 16 insertions(+) (limited to 'indra/media_plugins') diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt index db11c9ae21..f0b8f0d167 100644 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -56,6 +56,14 @@ add_dependencies(media_plugin_quicktime ${LLCOMMON_LIBRARIES} ) +if (WINDOWS) + set_target_properties( + media_plugin_quicktime + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + if (QUICKTIME) add_definitions(-DLL_QUICKTIME_ENABLED=1) diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt index d96477279d..5bccd589d8 100644 --- a/indra/media_plugins/webkit/CMakeLists.txt +++ b/indra/media_plugins/webkit/CMakeLists.txt @@ -52,6 +52,14 @@ add_dependencies(media_plugin_webkit ${LLCOMMON_LIBRARIES} ) +if (WINDOWS) + set_target_properties( + media_plugin_webkit + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + if (DARWIN) # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name set_target_properties( -- cgit v1.2.3 From 65d8a5a11b3b6aa2a7d0e972dfaa90a3aaf02e07 Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Mon, 28 Sep 2009 17:57:51 -0700 Subject: Changed win32 generation of initial 'component_path' for webkit plugin. DllMain() captures module handle. Call to GetModuleFilePathA() gets the dll filepath, Filename is trimmed off. --- indra/media_plugins/webkit/media_plugin_webkit.cpp | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'indra/media_plugins') diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 2928b7e6b3..b1bb5f23b9 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -48,6 +48,18 @@ #include #endif +#if LL_WINDOWS + // *NOTE:Mani - This captures the module handle fo rthe dll. This is used below + // to get the path to this dll for webkit initialization. + // I don't know how/if this can be done with apr... + namespace { HMODULE gModuleHandle;}; + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) + { + gModuleHandle = (HMODULE) hinstDLL; + return TRUE; + } +#endif + //////////////////////////////////////////////////////////////////////////////// // class MediaPluginWebKit : @@ -126,7 +138,31 @@ private: return false; } std::string application_dir = std::string( cwd ); + +#if LL_WINDOWS + //*NOTE:Mani - On windows, at least, the component path is the + // location of this dll's image file. + std::string component_dir; + char dll_path[_MAX_PATH]; + DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH); + while(len && dll_path[ len ] != ('\\') ) + { + len--; + } + if(len >= 0) + { + dll_path[len] = 0; + component_dir = dll_path; + } + else + { + // *NOTE:Mani - This case should be an rare exception. + // GetModuleFileNameA should always give you a full path, no? + component_dir = application_dir; + } +#else std::string component_dir = application_dir; +#endif std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? // window handle - needed on Windows and must be app window. -- cgit v1.2.3 From d02906b12ee335676e11ac883981e75f4df2cacc Mon Sep 17 00:00:00 2001 From: Monroe Linden Date: Mon, 12 Oct 2009 17:21:26 -0700 Subject: Added an optional "loaded_duration" parameter to the 'updated' message. This is the duration through which the time-based media has loaded, and should be between zero and the value of the "duration" parameter. If the parameter is not supplied, it will be implicitly set to the same value as the "duration" parameter, so the movie will appear fully loaded. This can be queried with LLPluginClassMedia::getLoadedDuration(). Made the "loaded_duration" parameter also implicitly set the progress percentage and send out MEDIA_EVENT_PROGRESS_UPDATED messages when it changes. Made the quicktime plugin set the "loaded_duration" parameter in its update messages. --- indra/media_plugins/quicktime/media_plugin_quicktime.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'indra/media_plugins') diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index fbda65120d..556865f771 100644 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -103,6 +103,7 @@ private: message.setValueReal("current_time", getCurrentTime()); message.setValueReal("duration", getDuration()); message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); + message.setValueReal("loaded_duration", getLoadedDuration()); } sendMessage(message); @@ -593,6 +594,19 @@ private: 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 ); -- cgit v1.2.3 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 --- .../quicktime/media_plugin_quicktime.cpp | 2052 ++++++++++---------- indra/media_plugins/webkit/media_plugin_webkit.cpp | 13 +- 2 files changed, 1068 insertions(+), 997 deletions(-) (limited to 'indra/media_plugins') 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) -- 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/media_plugins') 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 318e4784d2585be31ce9c07d87319e08e5e6e8d7 Mon Sep 17 00:00:00 2001 From: callum <none@none> Date: Thu, 22 Oct 2009 18:09:38 -0700 Subject: No Jira - added some new bookmarks to include Flash based sites for testing purposes. --- indra/media_plugins/webkit/media_plugin_webkit.cpp | 1666 ++++++++++---------- 1 file changed, 833 insertions(+), 833 deletions(-) (limited to 'indra/media_plugins') diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 65872e1596..6c54f63859 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -1,842 +1,842 @@ -/** - * @file media_plugin_webkit.cpp - * @brief Webkit 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 "llqtwebkit.h" - -#include "linden_common.h" -#include "indra_constants.h" // for indra keyboard codes - -#include "llgl.h" - -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#if LL_WINDOWS -#include <direct.h> -#else -#include <unistd.h> -#include <stdlib.h> -#endif - -#if LL_WINDOWS - // *NOTE:Mani - This captures the module handle fo rthe dll. This is used below - // to get the path to this dll for webkit initialization. - // I don't know how/if this can be done with apr... - namespace { HMODULE gModuleHandle;}; +/** + * @file media_plugin_webkit.cpp + * @brief Webkit 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 "llqtwebkit.h" + +#include "linden_common.h" +#include "indra_constants.h" // for indra keyboard codes + +#include "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#if LL_WINDOWS +#include <direct.h> +#else +#include <unistd.h> +#include <stdlib.h> +#endif + +#if LL_WINDOWS + // *NOTE:Mani - This captures the module handle fo rthe dll. This is used below + // to get the path to this dll for webkit initialization. + // I don't know how/if this can be done with apr... + namespace { HMODULE gModuleHandle;}; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { gModuleHandle = (HMODULE) hinstDLL; return TRUE; } -#endif - -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginWebKit : - public MediaPluginBase, - public LLEmbeddedBrowserWindowObserver -{ -public: - MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginWebKit(); - - /*virtual*/ void receiveMessage(const char *message_string); - -private: - - int mBrowserWindowId; - bool mBrowserInitialized; - bool mNeedsUpdate; - - bool mCanCut; - bool mCanCopy; - bool mCanPaste; - - //////////////////////////////////////////////////////////////////////////////// - // - void update(int milliseconds) - { - LLQtWebKit::getInstance()->pump( milliseconds ); - - checkEditState(); - - if ( mNeedsUpdate ) - { - const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); - - unsigned int buffer_size = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId ); - -// std::cerr << "webkit plugin: updating" << std::endl; - - // TODO: should get rid of this memcpy if possible - if ( mPixels && browser_pixels ) - { -// std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; - memcpy( mPixels, browser_pixels, buffer_size ); - } - - if ( mWidth > 0 && mHeight > 0 ) - { -// std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl; - setDirty( 0, 0, mWidth, mHeight ); - } - - mNeedsUpdate = false; - }; - }; - - //////////////////////////////////////////////////////////////////////////////// - // - bool initBrowser() - { - // already initialized - if ( mBrowserInitialized ) - return true; - - // not enough information to initialize the browser yet. - if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || - mTextureWidth < 0 || mTextureHeight < 0 ) - { - return false; - }; - - // set up directories - char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use - if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) - { - llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl; - return false; - } - std::string application_dir = std::string( cwd ); - -#if LL_WINDOWS - //*NOTE:Mani - On windows, at least, the component path is the - // location of this dll's image file. - std::string component_dir; - char dll_path[_MAX_PATH]; - DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH); +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginWebKit : + public MediaPluginBase, + public LLEmbeddedBrowserWindowObserver +{ +public: + MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginWebKit(); + + /*virtual*/ void receiveMessage(const char *message_string); + +private: + + int mBrowserWindowId; + bool mBrowserInitialized; + bool mNeedsUpdate; + + bool mCanCut; + bool mCanCopy; + bool mCanPaste; + + //////////////////////////////////////////////////////////////////////////////// + // + void update(int milliseconds) + { + LLQtWebKit::getInstance()->pump( milliseconds ); + + checkEditState(); + + if ( mNeedsUpdate ) + { + const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); + + unsigned int buffer_size = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId ); + +// std::cerr << "webkit plugin: updating" << std::endl; + + // TODO: should get rid of this memcpy if possible + if ( mPixels && browser_pixels ) + { +// std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; + memcpy( mPixels, browser_pixels, buffer_size ); + } + + if ( mWidth > 0 && mHeight > 0 ) + { +// std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl; + setDirty( 0, 0, mWidth, mHeight ); + } + + mNeedsUpdate = false; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + bool initBrowser() + { + // already initialized + if ( mBrowserInitialized ) + return true; + + // not enough information to initialize the browser yet. + if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || + mTextureWidth < 0 || mTextureHeight < 0 ) + { + return false; + }; + + // set up directories + char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use + if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) + { + llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl; + return false; + } + std::string application_dir = std::string( cwd ); + +#if LL_WINDOWS + //*NOTE:Mani - On windows, at least, the component path is the + // location of this dll's image file. + std::string component_dir; + char dll_path[_MAX_PATH]; + DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH); while(len && dll_path[ len ] != ('\\') ) { len--; } - if(len >= 0) - { - dll_path[len] = 0; - component_dir = dll_path; - } - else - { - // *NOTE:Mani - This case should be an rare exception. - // GetModuleFileNameA should always give you a full path, no? - component_dir = application_dir; - } -#else - std::string component_dir = application_dir; -#endif - std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? - - // window handle - needed on Windows and must be app window. -#if LL_WINDOWS - char window_title[ MAX_PATH ]; - GetConsoleTitleA( window_title, MAX_PATH ); - void* native_window_handle = (void*)FindWindowA( NULL, window_title ); -#else - void* native_window_handle = 0; -#endif - - // main browser initialization - bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, profileDir, native_window_handle ); - if ( result ) - { - // create single browser window - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); - -#if LL_WINDOWS - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#elif LL_DARWIN - // Disable plugins - LLQtWebKit::getInstance()->enablePlugins(false); -#elif LL_LINUX - // Disable plugins - LLQtWebKit::getInstance()->enablePlugins(false); -#endif - - // tell LLQtWebKit about the size of the browser window - LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); - - // observer events that LLQtWebKit emits - LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); - - // append details to agent string - LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); - - // don't flip bitmap - LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); - - // Set the background color to black - LLQtWebKit::getInstance()-> - // set background color to be black - mostly for initial login page - LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, 0x00, 0x00, 0x00 ); - - // go to the "home page" - // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. -// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); - - // set flag so we don't do this again - mBrowserInitialized = true; - - return true; - }; - - return false; - }; - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onCursorChanged(const EventType& event) - { - LLQtWebKit::ECursor llqt_cursor = (LLQtWebKit::ECursor)event.getIntValue(); - std::string name; - - switch(llqt_cursor) - { - case LLQtWebKit::C_ARROW: - name = "arrow"; - break; - case LLQtWebKit::C_IBEAM: - name = "ibeam"; - break; - case LLQtWebKit::C_SPLITV: - name = "splitv"; - break; - case LLQtWebKit::C_SPLITH: - name = "splith"; - break; - case LLQtWebKit::C_POINTINGHAND: - name = "hand"; - break; - - default: - llwarns << "Unknown cursor ID: " << (int)llqt_cursor << llendl; - break; - } - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed"); - message.setValue("name", name); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onPageChanged( const EventType& event ) - { - // flag that an update is required - mNeedsUpdate = true; - }; - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onNavigateBegin(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); - message.setValue("uri", event.getEventUri()); - sendMessage(message); - - setStatus(STATUS_LOADING); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onNavigateComplete(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); - message.setValue("uri", event.getEventUri()); - message.setValueS32("result_code", event.getIntValue()); - message.setValue("result_string", event.getStringValue()); - message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK)); - message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD)); - sendMessage(message); - - setStatus(STATUS_LOADED); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onUpdateProgress(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress"); - message.setValueS32("percent", event.getIntValue()); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onStatusTextChange(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text"); - 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) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); - message.setValue("uri", event.getEventUri()); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onClickLinkHref(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); - message.setValue("uri", event.getStringValue()); - message.setValue("target", event.getStringValue2()); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onClickLinkNoFollow(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); - message.setValue("uri", event.getStringValue()); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseDown( int x, int y ) - { - LLQtWebKit::getInstance()->mouseDown( mBrowserWindowId, x, y ); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseUp( int x, int y ) - { - LLQtWebKit::getInstance()->mouseUp( mBrowserWindowId, x, y ); - LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, true ); - checkEditState(); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseMove( int x, int y ) - { - LLQtWebKit::getInstance()->mouseMove( mBrowserWindowId, x, y ); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void keyPress( int key ) - { - int llqt_key; - - // The incoming values for 'key' will be the ones from indra_constants.h - // the outgoing values are the ones from llqtwebkit.h - - switch((KEY)key) - { - // This is the list that the llqtwebkit implementation actually maps into Qt keys. -// case KEY_XXX: llqt_key = LL_DOM_VK_CANCEL; break; -// case KEY_XXX: llqt_key = LL_DOM_VK_HELP; break; - case KEY_BACKSPACE: llqt_key = LL_DOM_VK_BACK_SPACE; break; - case KEY_TAB: llqt_key = LL_DOM_VK_TAB; break; -// case KEY_XXX: llqt_key = LL_DOM_VK_CLEAR; break; - case KEY_RETURN: llqt_key = LL_DOM_VK_RETURN; break; - case KEY_PAD_RETURN: llqt_key = LL_DOM_VK_ENTER; break; - case KEY_SHIFT: llqt_key = LL_DOM_VK_SHIFT; break; - case KEY_CONTROL: llqt_key = LL_DOM_VK_CONTROL; break; - case KEY_ALT: llqt_key = LL_DOM_VK_ALT; break; -// case KEY_XXX: llqt_key = LL_DOM_VK_PAUSE; break; - case KEY_CAPSLOCK: llqt_key = LL_DOM_VK_CAPS_LOCK; break; - case KEY_ESCAPE: llqt_key = LL_DOM_VK_ESCAPE; break; - case KEY_PAGE_UP: llqt_key = LL_DOM_VK_PAGE_UP; break; - case KEY_PAGE_DOWN: llqt_key = LL_DOM_VK_PAGE_DOWN; break; - case KEY_END: llqt_key = LL_DOM_VK_END; break; - case KEY_HOME: llqt_key = LL_DOM_VK_HOME; break; - case KEY_LEFT: llqt_key = LL_DOM_VK_LEFT; break; - case KEY_UP: llqt_key = LL_DOM_VK_UP; break; - case KEY_RIGHT: llqt_key = LL_DOM_VK_RIGHT; break; - case KEY_DOWN: llqt_key = LL_DOM_VK_DOWN; break; -// case KEY_XXX: llqt_key = LL_DOM_VK_PRINTSCREEN; break; - case KEY_INSERT: llqt_key = LL_DOM_VK_INSERT; break; - case KEY_DELETE: llqt_key = LL_DOM_VK_DELETE; break; -// case KEY_XXX: llqt_key = LL_DOM_VK_CONTEXT_MENU; break; - - default: - if(key < KEY_SPECIAL) - { - // Pass the incoming key through -- it should be regular ASCII, which should be correct for webkit. - llqt_key = key; - } - else - { - // Don't pass through untranslated special keys -- they'll be all wrong. - llqt_key = 0; - } - break; - } - -// std::cerr << "keypress, original code = 0x" << std::hex << key << ", converted code = 0x" << std::hex << llqt_key << std::dec << std::endl; - - if(llqt_key != 0) - { - LLQtWebKit::getInstance()->keyPress( mBrowserWindowId, llqt_key ); - } - - checkEditState(); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void unicodeInput( const std::string &utf8str ) - { - LLWString wstr = utf8str_to_wstring(utf8str); - - unsigned int i; - for(i=0; i < wstr.size(); i++) - { -// std::cerr << "unicode input, code = 0x" << std::hex << (unsigned long)(wstr[i]) << std::dec << std::endl; - - LLQtWebKit::getInstance()->unicodeInput(mBrowserWindowId, wstr[i]); - } - - checkEditState(); - }; - - void checkEditState(void) - { - bool can_cut = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT); - bool can_copy = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY); - bool can_paste = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE); - - if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste)) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state"); - - if(can_cut != mCanCut) - { - mCanCut = can_cut; - message.setValueBoolean("cut", can_cut); - } - - if(can_copy != mCanCopy) - { - mCanCopy = can_copy; - message.setValueBoolean("copy", can_copy); - } - - if(can_paste != mCanPaste) - { - mCanPaste = can_paste; - message.setValueBoolean("paste", can_paste); - } - - sendMessage(message); - - } - } - -}; - -MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : - MediaPluginBase(host_send_func, host_user_data) -{ -// std::cerr << "MediaPluginWebKit constructor" << std::endl; - - mBrowserWindowId = 0; - mBrowserInitialized = false; - mNeedsUpdate = true; - mCanCut = false; - mCanCopy = false; - mCanPaste = false; -} - -MediaPluginWebKit::~MediaPluginWebKit() -{ - // unhook observer - LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this ); - - // clean up - LLQtWebKit::getInstance()->reset(); - -// std::cerr << "MediaPluginWebKit destructor" << std::endl; -} - -void MediaPluginWebKit::receiveMessage(const char *message_string) -{ -// std::cerr << "MediaPluginWebKit::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; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - message.setValueLLSD("versions", versions); - - std::string plugin_version = "Webkit media plugin, Webkit version "; - plugin_version += LLQtWebKit::getInstance()->getVersion(); - message.setValue("plugin_version", plugin_version); - sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - mDepth = 4; - - message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - message.setValueS32("default_width", 1024); - message.setValueS32("default_height", 1024); - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGBA); - message.setValueU32("format", GL_RGBA); - message.setValueU32("type", GL_UNSIGNED_BYTE); - message.setValueBoolean("coords_opengl", 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 << "MediaPluginWebKit::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 << "MediaPluginWebKit::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(); - } - mSharedSegments.erase(iter); - } - else - { -// std::cerr << "MediaPluginWebKit::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 << "MediaPluginWebKit::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"); - - if(!name.empty()) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - mPixels = (unsigned char*)iter->second.mAddress; - mWidth = width; - mHeight = height; - - // initialize (only gets called once) - initBrowser(); - - // size changed so tell the browser - LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); - -// std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight -// << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; - - S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); - - // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. - if(real_width <= texture_width) - { - texture_width = real_width; - } - else - { - // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. -// std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; - mDeleteMe = true; - return; - } - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - - }; - }; - - 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); - - } - else if(message_name == "load_uri") - { - std::string uri = message_in.getValue("uri"); - -// std::cout << "loading URI: " << uri << std::endl; - - if(!uri.empty()) - { - LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri ); - } - } - 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"); - // std::string modifiers = message.getValue("modifiers"); - - if(event == "down") - { - mouseDown(x, y); - //std::cout << "Mouse down at " << x << " x " << y << std::endl; - } - else if(event == "up") - { - mouseUp(x, y); - //std::cout << "Mouse up at " << x << " x " << y << std::endl; - } - else if(event == "move") - { - mouseMove(x, y); - //std::cout << ">>>>>>>>>>>>>>>>>>>> Mouse move at " << x << " x " << y << std::endl; - } - } - else if(message_name == "scroll_event") - { - // S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - // std::string modifiers = message.getValue("modifiers"); - - // We currently ignore horizontal scrolling. - // The scroll values are roughly 1 per wheel click, so we need to magnify them by some factor. - // Arbitrarily, I choose 16. - y *= 16; - LLQtWebKit::getInstance()->scrollByLines(mBrowserWindowId, y); - } - else if(message_name == "key_event") - { - std::string event = message_in.getValue("event"); - - // act on "key down" or "key repeat" - if ( (event == "down") || (event == "repeat") ) - { - S32 key = message_in.getValueS32("key"); - keyPress( key ); - }; - } - else if(message_name == "text_event") - { - std::string text = message_in.getValue("text"); - - unicodeInput(text); - } - if(message_name == "edit_cut") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT ); - } - if(message_name == "edit_copy") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY ); - } - if(message_name == "edit_paste") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE ); - } - else - { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; - }; - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) - { - if(message_name == "focus") - { - bool val = message_in.getValueBoolean("focused"); - LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, val ); - } - else if(message_name == "clear_cache") - { - LLQtWebKit::getInstance()->clearCache(); - } - else if(message_name == "clear_cookies") - { - LLQtWebKit::getInstance()->clearAllCookies(); - } - else if(message_name == "enable_cookies") - { - bool val = message_in.getValueBoolean("enable"); - LLQtWebKit::getInstance()->enableCookies( val ); - } - else if(message_name == "proxy_setup") - { - bool val = message_in.getValueBoolean("enable"); - std::string host = message_in.getValue("host"); - int port = message_in.getValueS32("port"); - LLQtWebKit::getInstance()->enableProxy( val, host, port ); - } - else if(message_name == "browse_stop") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_STOP ); - } - else if(message_name == "browse_reload") - { - // foo = message_in.getValueBoolean("ignore_cache"); - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD ); - } - else if(message_name == "browse_forward") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD ); - } - else if(message_name == "browse_back") - { - LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK ); - } - else if(message_name == "set_status_redirect") - { - int code = message_in.getValueS32("code"); - std::string url = message_in.getValue("url"); - if ( 404 == code ) // browser lib only supports 404 right now - { - LLQtWebKit::getInstance()->set404RedirectUrl( mBrowserWindowId, url ); - }; - } - else if(message_name == "set_user_agent") - { - std::string user_agent = message_in.getValue("user_agent"); - LLQtWebKit::getInstance()->setBrowserAgentId( user_agent ); - } - else if(message_name == "init_history") - { - // Initialize browser history - LLSD history = message_in.getValueLLSD("history"); - // First, clear the URL history - LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId); - // Then, add the history items in order - LLSD::array_iterator iter_history = history.beginArray(); - LLSD::array_iterator end_history = history.endArray(); - for(; iter_history != end_history; ++iter_history) - { - std::string url = (*iter_history).asString(); - if(! url.empty()) { - LLQtWebKit::getInstance()->prependHistoryUrl(mBrowserWindowId, url); - } - } - } - else - { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; - }; - } - else - { -// std::cerr << "MediaPluginWebKit::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) -{ - MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); - *plugin_send_func = MediaPluginWebKit::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; -} - + if(len >= 0) + { + dll_path[len] = 0; + component_dir = dll_path; + } + else + { + // *NOTE:Mani - This case should be an rare exception. + // GetModuleFileNameA should always give you a full path, no? + component_dir = application_dir; + } +#else + std::string component_dir = application_dir; +#endif + std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? + + // window handle - needed on Windows and must be app window. +#if LL_WINDOWS + char window_title[ MAX_PATH ]; + GetConsoleTitleA( window_title, MAX_PATH ); + void* native_window_handle = (void*)FindWindowA( NULL, window_title ); +#else + void* native_window_handle = 0; +#endif + + // main browser initialization + bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, profileDir, native_window_handle ); + if ( result ) + { + // create single browser window + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); + +#if LL_WINDOWS + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); +#elif LL_DARWIN + // Disable plugins + LLQtWebKit::getInstance()->enablePlugins(false); +#elif LL_LINUX + // Disable plugins + LLQtWebKit::getInstance()->enablePlugins(false); +#endif + + // tell LLQtWebKit about the size of the browser window + LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + + // observer events that LLQtWebKit emits + LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); + + // append details to agent string + LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); + + // don't flip bitmap + LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); + + // Set the background color to black + LLQtWebKit::getInstance()-> + // set background color to be black - mostly for initial login page + LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, 0x00, 0x00, 0x00 ); + + // go to the "home page" + // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. +// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); + + // set flag so we don't do this again + mBrowserInitialized = true; + + return true; + }; + + return false; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onCursorChanged(const EventType& event) + { + LLQtWebKit::ECursor llqt_cursor = (LLQtWebKit::ECursor)event.getIntValue(); + std::string name; + + switch(llqt_cursor) + { + case LLQtWebKit::C_ARROW: + name = "arrow"; + break; + case LLQtWebKit::C_IBEAM: + name = "ibeam"; + break; + case LLQtWebKit::C_SPLITV: + name = "splitv"; + break; + case LLQtWebKit::C_SPLITH: + name = "splith"; + break; + case LLQtWebKit::C_POINTINGHAND: + name = "hand"; + break; + + default: + llwarns << "Unknown cursor ID: " << (int)llqt_cursor << llendl; + break; + } + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed"); + message.setValue("name", name); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onPageChanged( const EventType& event ) + { + // flag that an update is required + mNeedsUpdate = true; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateBegin(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message.setValue("uri", event.getEventUri()); + sendMessage(message); + + setStatus(STATUS_LOADING); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateComplete(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message.setValue("uri", event.getEventUri()); + message.setValueS32("result_code", event.getIntValue()); + message.setValue("result_string", event.getStringValue()); + message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK)); + message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD)); + sendMessage(message); + + setStatus(STATUS_LOADED); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onUpdateProgress(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress"); + message.setValueS32("percent", event.getIntValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onStatusTextChange(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text"); + 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) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); + message.setValue("uri", event.getEventUri()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onClickLinkHref(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); + message.setValue("uri", event.getStringValue()); + message.setValue("target", event.getStringValue2()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onClickLinkNoFollow(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); + message.setValue("uri", event.getStringValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseDown( int x, int y ) + { + LLQtWebKit::getInstance()->mouseDown( mBrowserWindowId, x, y ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseUp( int x, int y ) + { + LLQtWebKit::getInstance()->mouseUp( mBrowserWindowId, x, y ); + LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, true ); + checkEditState(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseMove( int x, int y ) + { + LLQtWebKit::getInstance()->mouseMove( mBrowserWindowId, x, y ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyPress( int key ) + { + int llqt_key; + + // The incoming values for 'key' will be the ones from indra_constants.h + // the outgoing values are the ones from llqtwebkit.h + + switch((KEY)key) + { + // This is the list that the llqtwebkit implementation actually maps into Qt keys. +// case KEY_XXX: llqt_key = LL_DOM_VK_CANCEL; break; +// case KEY_XXX: llqt_key = LL_DOM_VK_HELP; break; + case KEY_BACKSPACE: llqt_key = LL_DOM_VK_BACK_SPACE; break; + case KEY_TAB: llqt_key = LL_DOM_VK_TAB; break; +// case KEY_XXX: llqt_key = LL_DOM_VK_CLEAR; break; + case KEY_RETURN: llqt_key = LL_DOM_VK_RETURN; break; + case KEY_PAD_RETURN: llqt_key = LL_DOM_VK_ENTER; break; + case KEY_SHIFT: llqt_key = LL_DOM_VK_SHIFT; break; + case KEY_CONTROL: llqt_key = LL_DOM_VK_CONTROL; break; + case KEY_ALT: llqt_key = LL_DOM_VK_ALT; break; +// case KEY_XXX: llqt_key = LL_DOM_VK_PAUSE; break; + case KEY_CAPSLOCK: llqt_key = LL_DOM_VK_CAPS_LOCK; break; + case KEY_ESCAPE: llqt_key = LL_DOM_VK_ESCAPE; break; + case KEY_PAGE_UP: llqt_key = LL_DOM_VK_PAGE_UP; break; + case KEY_PAGE_DOWN: llqt_key = LL_DOM_VK_PAGE_DOWN; break; + case KEY_END: llqt_key = LL_DOM_VK_END; break; + case KEY_HOME: llqt_key = LL_DOM_VK_HOME; break; + case KEY_LEFT: llqt_key = LL_DOM_VK_LEFT; break; + case KEY_UP: llqt_key = LL_DOM_VK_UP; break; + case KEY_RIGHT: llqt_key = LL_DOM_VK_RIGHT; break; + case KEY_DOWN: llqt_key = LL_DOM_VK_DOWN; break; +// case KEY_XXX: llqt_key = LL_DOM_VK_PRINTSCREEN; break; + case KEY_INSERT: llqt_key = LL_DOM_VK_INSERT; break; + case KEY_DELETE: llqt_key = LL_DOM_VK_DELETE; break; +// case KEY_XXX: llqt_key = LL_DOM_VK_CONTEXT_MENU; break; + + default: + if(key < KEY_SPECIAL) + { + // Pass the incoming key through -- it should be regular ASCII, which should be correct for webkit. + llqt_key = key; + } + else + { + // Don't pass through untranslated special keys -- they'll be all wrong. + llqt_key = 0; + } + break; + } + +// std::cerr << "keypress, original code = 0x" << std::hex << key << ", converted code = 0x" << std::hex << llqt_key << std::dec << std::endl; + + if(llqt_key != 0) + { + LLQtWebKit::getInstance()->keyPress( mBrowserWindowId, llqt_key ); + } + + checkEditState(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void unicodeInput( const std::string &utf8str ) + { + LLWString wstr = utf8str_to_wstring(utf8str); + + unsigned int i; + for(i=0; i < wstr.size(); i++) + { +// std::cerr << "unicode input, code = 0x" << std::hex << (unsigned long)(wstr[i]) << std::dec << std::endl; + + LLQtWebKit::getInstance()->unicodeInput(mBrowserWindowId, wstr[i]); + } + + checkEditState(); + }; + + void checkEditState(void) + { + bool can_cut = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT); + bool can_copy = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY); + bool can_paste = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE); + + if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste)) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state"); + + if(can_cut != mCanCut) + { + mCanCut = can_cut; + message.setValueBoolean("cut", can_cut); + } + + if(can_copy != mCanCopy) + { + mCanCopy = can_copy; + message.setValueBoolean("copy", can_copy); + } + + if(can_paste != mCanPaste) + { + mCanPaste = can_paste; + message.setValueBoolean("paste", can_paste); + } + + sendMessage(message); + + } + } + +}; + +MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : + MediaPluginBase(host_send_func, host_user_data) +{ +// std::cerr << "MediaPluginWebKit constructor" << std::endl; + + mBrowserWindowId = 0; + mBrowserInitialized = false; + mNeedsUpdate = true; + mCanCut = false; + mCanCopy = false; + mCanPaste = false; +} + +MediaPluginWebKit::~MediaPluginWebKit() +{ + // unhook observer + LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this ); + + // clean up + LLQtWebKit::getInstance()->reset(); + +// std::cerr << "MediaPluginWebKit destructor" << std::endl; +} + +void MediaPluginWebKit::receiveMessage(const char *message_string) +{ +// std::cerr << "MediaPluginWebKit::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; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; + message.setValueLLSD("versions", versions); + + std::string plugin_version = "Webkit media plugin, Webkit version "; + plugin_version += LLQtWebKit::getInstance()->getVersion(); + message.setValue("plugin_version", plugin_version); + sendMessage(message); + + // Plugin gets to decide the texture parameters to use. + mDepth = 4; + + message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGBA); + message.setValueU32("format", GL_RGBA); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", 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 << "MediaPluginWebKit::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 << "MediaPluginWebKit::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(); + } + mSharedSegments.erase(iter); + } + else + { +// std::cerr << "MediaPluginWebKit::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 << "MediaPluginWebKit::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"); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + + // initialize (only gets called once) + initBrowser(); + + // size changed so tell the browser + LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + +// std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight +// << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; + + S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); + + // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. + if(real_width <= texture_width) + { + texture_width = real_width; + } + else + { + // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. +// std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; + mDeleteMe = true; + return; + } + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + }; + }; + + 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); + + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + +// std::cout << "loading URI: " << uri << std::endl; + + if(!uri.empty()) + { + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri ); + } + } + 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"); + // std::string modifiers = message.getValue("modifiers"); + + if(event == "down") + { + mouseDown(x, y); + //std::cout << "Mouse down at " << x << " x " << y << std::endl; + } + else if(event == "up") + { + mouseUp(x, y); + //std::cout << "Mouse up at " << x << " x " << y << std::endl; + } + else if(event == "move") + { + mouseMove(x, y); + //std::cout << ">>>>>>>>>>>>>>>>>>>> Mouse move at " << x << " x " << y << std::endl; + } + } + else if(message_name == "scroll_event") + { + // S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + // std::string modifiers = message.getValue("modifiers"); + + // We currently ignore horizontal scrolling. + // The scroll values are roughly 1 per wheel click, so we need to magnify them by some factor. + // Arbitrarily, I choose 16. + y *= 16; + LLQtWebKit::getInstance()->scrollByLines(mBrowserWindowId, y); + } + else if(message_name == "key_event") + { + std::string event = message_in.getValue("event"); + + // act on "key down" or "key repeat" + if ( (event == "down") || (event == "repeat") ) + { + S32 key = message_in.getValueS32("key"); + keyPress( key ); + }; + } + else if(message_name == "text_event") + { + std::string text = message_in.getValue("text"); + + unicodeInput(text); + } + if(message_name == "edit_cut") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT ); + } + if(message_name == "edit_copy") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY ); + } + if(message_name == "edit_paste") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE ); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; + }; + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) + { + if(message_name == "focus") + { + bool val = message_in.getValueBoolean("focused"); + LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, val ); + } + else if(message_name == "clear_cache") + { + LLQtWebKit::getInstance()->clearCache(); + } + else if(message_name == "clear_cookies") + { + LLQtWebKit::getInstance()->clearAllCookies(); + } + else if(message_name == "enable_cookies") + { + bool val = message_in.getValueBoolean("enable"); + LLQtWebKit::getInstance()->enableCookies( val ); + } + else if(message_name == "proxy_setup") + { + bool val = message_in.getValueBoolean("enable"); + std::string host = message_in.getValue("host"); + int port = message_in.getValueS32("port"); + LLQtWebKit::getInstance()->enableProxy( val, host, port ); + } + else if(message_name == "browse_stop") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_STOP ); + } + else if(message_name == "browse_reload") + { + // foo = message_in.getValueBoolean("ignore_cache"); + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD ); + } + else if(message_name == "browse_forward") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD ); + } + else if(message_name == "browse_back") + { + LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK ); + } + else if(message_name == "set_status_redirect") + { + int code = message_in.getValueS32("code"); + std::string url = message_in.getValue("url"); + if ( 404 == code ) // browser lib only supports 404 right now + { + LLQtWebKit::getInstance()->set404RedirectUrl( mBrowserWindowId, url ); + }; + } + else if(message_name == "set_user_agent") + { + std::string user_agent = message_in.getValue("user_agent"); + LLQtWebKit::getInstance()->setBrowserAgentId( user_agent ); + } + else if(message_name == "init_history") + { + // Initialize browser history + LLSD history = message_in.getValueLLSD("history"); + // First, clear the URL history + LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId); + // Then, add the history items in order + LLSD::array_iterator iter_history = history.beginArray(); + LLSD::array_iterator end_history = history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) { + LLQtWebKit::getInstance()->prependHistoryUrl(mBrowserWindowId, url); + } + } + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; + }; + } + else + { +// std::cerr << "MediaPluginWebKit::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) +{ + MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); + *plugin_send_func = MediaPluginWebKit::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} + -- cgit v1.2.3