diff options
| author | Callum Prentice <callum@gmail.com> | 2017-04-21 13:37:24 -0700 | 
|---|---|---|
| committer | Callum Prentice <callum@gmail.com> | 2017-04-21 13:37:24 -0700 | 
| commit | 362ffb213108ba7c5e582aa9d72a44a82f5455a3 (patch) | |
| tree | e4a7ce43240831750d072cccfd8754000a9752e6 | |
| parent | b8cf14c51fe7350e8dd045679a147b9d9e0cebe2 (diff) | |
Fix windows line endings because it's 2017 and our tools can't deal with it
| -rw-r--r-- | indra/llplugin/llpluginprocesschild.cpp | 1178 | ||||
| -rw-r--r-- | indra/media_plugins/example/media_plugin_example.cpp | 783 | 
2 files changed, 980 insertions, 981 deletions
| diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index 3804ffadbb..e24d222cb6 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -1,589 +1,589 @@ -/**
 -* @file llpluginprocesschild.cpp
 -* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
 -*
 -* @cond
 -* $LicenseInfo:firstyear=2008&license=viewerlgpl$
 -* Second Life Viewer Source Code
 -* Copyright (C) 2010, Linden Research, Inc.
 -*
 -* This library is free software; you can redistribute it and/or
 -* modify it under the terms of the GNU Lesser General Public
 -* License as published by the Free Software Foundation;
 -* version 2.1 of the License only.
 -*
 -* This library is distributed in the hope that it will be useful,
 -* but WITHOUT ANY WARRANTY; without even the implied warranty of
 -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 -* Lesser General Public License for more details.
 -*
 -* You should have received a copy of the GNU Lesser General Public
 -* License along with this library; if not, write to the Free Software
 -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 -*
 -* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 -* $/LicenseInfo$
 -* @endcond
 -*/
 -
 -#include "linden_common.h"
 -
 -#include "llpluginprocesschild.h"
 -#include "llplugininstance.h"
 -#include "llpluginmessagepipe.h"
 -#include "llpluginmessageclasses.h"
 -
 -static const F32 GOODBYE_SECONDS = 20.0f;
 -static const F32 HEARTBEAT_SECONDS = 1.0f;
 -static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f;  // Each call to idle will give the plugin this much time.
 -
 -LLPluginProcessChild::LLPluginProcessChild()
 -{
 -	mState = STATE_UNINITIALIZED;
 -	mInstance = NULL;
 -	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
 -	mSleepTime = PLUGIN_IDLE_SECONDS;	// default: send idle messages at 100Hz
 -	mCPUElapsed = 0.0f;
 -	mBlockingRequest = false;
 -	mBlockingResponseReceived = false;
 -}
 -
 -LLPluginProcessChild::~LLPluginProcessChild()
 -{
 -	if (mInstance != NULL)
 -	{
 -		sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
 -
 -		// IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted 
 -		// appears to fail and lock up which means that a given instance of the slplugin process never exits. 
 -		// This is bad, especially when users try to update their version of SL - it fails because the slplugin 
 -		// process as well as a bunch of plugin specific files are locked and cannot be overwritten.
 -		exit(0);
 -		//delete mInstance;
 -		//mInstance = NULL;
 -	}
 -}
 -
 -void LLPluginProcessChild::killSockets(void)
 -{
 -	killMessagePipe();
 -	mSocket.reset();
 -}
 -
 -void LLPluginProcessChild::init(U32 launcher_port)
 -{
 -	mLauncherHost = LLHost("127.0.0.1", launcher_port);
 -	setState(STATE_INITIALIZED);
 -}
 -
 -void LLPluginProcessChild::idle(void)
 -{
 -	bool idle_again;
 -	do
 -	{
 -		if (mState < STATE_SHUTDOWNREQ)
 -		{   // Once we have hit the shutdown request state checking for errors might put us in a spurious 
 -			// error state... don't do that.
 -
 -			if (APR_STATUS_IS_EOF(mSocketError))
 -			{
 -				// Plugin socket was closed.  This covers both normal plugin termination and host crashes.
 -				setState(STATE_ERROR);
 -			}
 -			else if (mSocketError != APR_SUCCESS)
 -			{
 -				LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR" << LL_ENDL;
 -				setState(STATE_ERROR);
 -			}
 -
 -			if ((mState > STATE_INITIALIZED) && (mMessagePipe == NULL))
 -			{
 -				// The pipe has been closed -- we're done.
 -				// TODO: This could be slightly more subtle, but I'm not sure it needs to be.
 -				LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR" << LL_ENDL;
 -				setState(STATE_ERROR);
 -			}
 -		}
 -
 -		// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
 -		// USE THIS CAREFULLY, since it can starve other code.  Specifically make sure there's no way to get into a closed cycle and never return.
 -		// When in doubt, don't do it.
 -		idle_again = false;
 -
 -		if (mInstance != NULL)
 -		{
 -			// Provide some time to the plugin
 -			mInstance->idle();
 -		}
 -
 -		switch (mState)
 -		{
 -		case STATE_UNINITIALIZED:
 -			break;
 -
 -		case STATE_INITIALIZED:
 -			if (mSocket->blockingConnect(mLauncherHost))
 -			{
 -				// This automatically sets mMessagePipe
 -				new LLPluginMessagePipe(this, mSocket);
 -
 -				setState(STATE_CONNECTED);
 -			}
 -			else
 -			{
 -				// connect failed
 -				setState(STATE_ERROR);
 -			}
 -			break;
 -
 -		case STATE_CONNECTED:
 -			sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello"));
 -			setState(STATE_PLUGIN_LOADING);
 -			break;
 -
 -		case STATE_PLUGIN_LOADING:
 -			if (!mPluginFile.empty())
 -			{
 -				mInstance = new LLPluginInstance(this);
 -				if (mInstance->load(mPluginDir, mPluginFile) == 0)
 -				{
 -					mHeartbeat.start();
 -					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
 -					mCPUElapsed = 0.0f;
 -					setState(STATE_PLUGIN_LOADED);
 -				}
 -				else
 -				{
 -					setState(STATE_ERROR);
 -				}
 -			}
 -			break;
 -
 -		case STATE_PLUGIN_LOADED:
 -		{
 -			setState(STATE_PLUGIN_INITIALIZING);
 -			LLPluginMessage message("base", "init");
 -			sendMessageToPlugin(message);
 -		}
 -		break;
 -
 -		case STATE_PLUGIN_INITIALIZING:
 -			// waiting for init_response...
 -			break;
 -
 -		case STATE_RUNNING:
 -			if (mInstance != NULL)
 -			{
 -				// Provide some time to the plugin
 -				LLPluginMessage message("base", "idle");
 -				message.setValueReal("time", PLUGIN_IDLE_SECONDS);
 -				sendMessageToPlugin(message);
 -
 -				mInstance->idle();
 -
 -				if (mHeartbeat.hasExpired())
 -				{
 -
 -					// This just proves that we're not stuck down inside the plugin code.
 -					LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat");
 -
 -					// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle.
 -					// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation.
 -					// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation.
 -					heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64());
 -
 -					sendMessageToParent(heartbeat);
 -
 -					mHeartbeat.reset();
 -					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
 -					mCPUElapsed = 0.0f;
 -				}
 -			}
 -			// receivePluginMessage will transition to STATE_UNLOADING
 -			break;
 -
 -		case STATE_SHUTDOWNREQ:
 -			// set next state first thing in case "cleanup" message advances state.
 -			setState(STATE_UNLOADING);
 -			mWaitGoodbye.setTimerExpirySec(GOODBYE_SECONDS);
 -
 -			if (mInstance != NULL)
 -			{
 -				sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
 -			}
 -			break;
 -
 -		case STATE_UNLOADING:
 -			// waiting for goodbye from plugin.
 -			if (mWaitGoodbye.hasExpired())
 -			{
 -				LL_WARNS() << "Wait for goodbye expired.  Advancing to UNLOADED" << LL_ENDL;
 -				setState(STATE_UNLOADED);
 -			}
 -			break;
 -
 -		case STATE_UNLOADED:
 -			killSockets();
 -			delete mInstance;
 -			mInstance = NULL;
 -			setState(STATE_DONE);
 -			break;
 -
 -		case STATE_ERROR:
 -			// Close the socket to the launcher
 -			killSockets();
 -			// TODO: Where do we go from here?  Just exit()?
 -			setState(STATE_DONE);
 -			break;
 -
 -		case STATE_DONE:
 -			// just sit here.
 -			break;
 -		}
 -
 -	} while (idle_again);
 -}
 -
 -void LLPluginProcessChild::sleep(F64 seconds)
 -{
 -	deliverQueuedMessages();
 -	if (mMessagePipe)
 -	{
 -		mMessagePipe->pump(seconds);
 -	}
 -	else
 -	{
 -		ms_sleep((int)(seconds * 1000.0f));
 -	}
 -}
 -
 -void LLPluginProcessChild::pump(void)
 -{
 -	deliverQueuedMessages();
 -	if (mMessagePipe)
 -	{
 -		mMessagePipe->pump(0.0f);
 -	}
 -	else
 -	{
 -		// Should we warn here?
 -	}
 -}
 -
 -
 -bool LLPluginProcessChild::isRunning(void)
 -{
 -	bool result = false;
 -
 -	if (mState == STATE_RUNNING)
 -		result = true;
 -
 -	return result;
 -}
 -
 -bool LLPluginProcessChild::isDone(void)
 -{
 -	bool result = false;
 -
 -	switch (mState)
 -	{
 -	case STATE_DONE:
 -		result = true;
 -		break;
 -	default:
 -		break;
 -	}
 -
 -	return result;
 -}
 -
 -void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message)
 -{
 -	if (mInstance)
 -	{
 -		std::string buffer = message.generate();
 -
 -		LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
 -		LLTimer elapsed;
 -
 -		mInstance->sendMessage(buffer);
 -
 -		mCPUElapsed += elapsed.getElapsedTimeF64();
 -	}
 -	else
 -	{
 -		LL_WARNS("Plugin") << "mInstance == NULL" << LL_ENDL;
 -	}
 -}
 -
 -void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message)
 -{
 -	std::string buffer = message.generate();
 -
 -	LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL;
 -
 -	writeMessageRaw(buffer);
 -}
 -
 -void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
 -{
 -	// Incoming message from the TCP Socket
 -
 -	LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
 -
 -	// Decode this message
 -	LLPluginMessage parsed;
 -	parsed.parse(message);
 -
 -	if (mBlockingRequest)
 -	{
 -		// We're blocking the plugin waiting for a response.
 -
 -		if (parsed.hasValue("blocking_response"))
 -		{
 -			// This is the message we've been waiting for -- fall through and send it immediately. 
 -			mBlockingResponseReceived = true;
 -		}
 -		else
 -		{
 -			// Still waiting.  Queue this message and don't process it yet.
 -			mMessageQueue.push(message);
 -			return;
 -		}
 -	}
 -
 -	bool passMessage = true;
 -
 -	// FIXME: how should we handle queueing here?
 -
 -	{
 -		std::string message_class = parsed.getClass();
 -		if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
 -		{
 -			passMessage = false;
 -
 -			std::string message_name = parsed.getName();
 -			if (message_name == "load_plugin")
 -			{
 -				mPluginFile = parsed.getValue("file");
 -				mPluginDir = parsed.getValue("dir");
 -			}
 -			else if (message_name == "shutdown_plugin")
 -			{
 -				setState(STATE_SHUTDOWNREQ);
 -			}
 -			else if (message_name == "shm_add")
 -			{
 -				std::string name = parsed.getValue("name");
 -				size_t size = (size_t)parsed.getValueS32("size");
 -
 -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 -				if (iter != mSharedMemoryRegions.end())
 -				{
 -					// Need to remove the old region first
 -					LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL;
 -				}
 -				else
 -				{
 -					// This is a new region
 -					LLPluginSharedMemory *region = new LLPluginSharedMemory;
 -					if (region->attach(name, size))
 -					{
 -						mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region));
 -
 -						std::stringstream addr;
 -						addr << region->getMappedAddress();
 -
 -						// Send the add notification to the plugin
 -						LLPluginMessage message("base", "shm_added");
 -						message.setValue("name", name);
 -						message.setValueS32("size", (S32)size);
 -						message.setValuePointer("address", region->getMappedAddress());
 -						sendMessageToPlugin(message);
 -
 -						// and send the response to the parent
 -						message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response");
 -						message.setValue("name", name);
 -						sendMessageToParent(message);
 -					}
 -					else
 -					{
 -						LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL;
 -						delete region;
 -					}
 -				}
 -
 -			}
 -			else if (message_name == "shm_remove")
 -			{
 -				std::string name = parsed.getValue("name");
 -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 -				if (iter != mSharedMemoryRegions.end())
 -				{
 -					// forward the remove request to the plugin -- its response will trigger us to detach the segment.
 -					LLPluginMessage message("base", "shm_remove");
 -					message.setValue("name", name);
 -					sendMessageToPlugin(message);
 -				}
 -				else
 -				{
 -					LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL;
 -				}
 -			}
 -			else if (message_name == "sleep_time")
 -			{
 -				mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz
 -			}
 -			else if (message_name == "crash")
 -			{
 -				// Crash the plugin
 -				LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL;
 -			}
 -			else if (message_name == "hang")
 -			{
 -				// Hang the plugin
 -				LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL;
 -				while (1)
 -				{
 -					// wheeeeeeeee......
 -				}
 -			}
 -			else
 -			{
 -				LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL;
 -			}
 -		}
 -	}
 -
 -	if (passMessage && mInstance != NULL)
 -	{
 -		LLTimer elapsed;
 -
 -		mInstance->sendMessage(message);
 -
 -		mCPUElapsed += elapsed.getElapsedTimeF64();
 -	}
 -}
 -
 -/* virtual */
 -void LLPluginProcessChild::receivePluginMessage(const std::string &message)
 -{
 -	LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
 -
 -	if (mBlockingRequest)
 -	{
 -		// 
 -		LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
 -	}
 -
 -	// Incoming message from the plugin instance
 -	bool passMessage = true;
 -
 -	// FIXME: how should we handle queueing here?
 -
 -	// Intercept certain base messages (responses to ones sent by this class)
 -	{
 -		// Decode this message
 -		LLPluginMessage parsed;
 -		parsed.parse(message);
 -
 -		if (parsed.hasValue("blocking_request"))
 -		{
 -			mBlockingRequest = true;
 -		}
 -
 -		std::string message_class = parsed.getClass();
 -		if (message_class == "base")
 -		{
 -			std::string message_name = parsed.getName();
 -			if (message_name == "init_response")
 -			{
 -				// The plugin has finished initializing.
 -				setState(STATE_RUNNING);
 -
 -				// Don't pass this message up to the parent
 -				passMessage = false;
 -
 -				LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response");
 -				LLSD versions = parsed.getValueLLSD("versions");
 -				new_message.setValueLLSD("versions", versions);
 -
 -				if (parsed.hasValue("plugin_version"))
 -				{
 -					std::string plugin_version = parsed.getValue("plugin_version");
 -					new_message.setValueLLSD("plugin_version", plugin_version);
 -				}
 -
 -				// Let the parent know it's loaded and initialized.
 -				sendMessageToParent(new_message);
 -			}
 -			else if (message_name == "goodbye")
 -			{
 -				setState(STATE_UNLOADED);
 -			}
 -			else if (message_name == "shm_remove_response")
 -			{
 -				// Don't pass this message up to the parent
 -				passMessage = false;
 -
 -				std::string name = parsed.getValue("name");
 -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 -				if (iter != mSharedMemoryRegions.end())
 -				{
 -					// detach the shared memory region
 -					iter->second->detach();
 -
 -					// and remove it from our map
 -					mSharedMemoryRegions.erase(iter);
 -
 -					// Finally, send the response to the parent.
 -					LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response");
 -					message.setValue("name", name);
 -					sendMessageToParent(message);
 -				}
 -				else
 -				{
 -					LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL;
 -				}
 -			}
 -		}
 -	}
 -
 -	if (passMessage)
 -	{
 -		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
 -		writeMessageRaw(message);
 -	}
 -
 -	while (mBlockingRequest)
 -	{
 -		// The plugin wants to block and wait for a response to this message.
 -		sleep(mSleepTime);	// this will pump the message pipe and process messages
 -
 -		if (mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
 -		{
 -			// Response has been received, or we've hit an error state.  Stop waiting.
 -			mBlockingRequest = false;
 -			mBlockingResponseReceived = false;
 -		}
 -	}
 -}
 -
 -
 -void LLPluginProcessChild::setState(EState state)
 -{
 -	LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
 -	mState = state;
 -};
 -
 -void LLPluginProcessChild::deliverQueuedMessages()
 -{
 -	if (!mBlockingRequest)
 -	{
 -		while (!mMessageQueue.empty())
 -		{
 -			receiveMessageRaw(mMessageQueue.front());
 -			mMessageQueue.pop();
 -		}
 -	}
 -}
 +/** +* @file llpluginprocesschild.cpp +* @brief LLPluginProcessChild handles the child side of the external-process plugin API. +* +* @cond +* $LicenseInfo:firstyear=2008&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +* @endcond +*/ + +#include "linden_common.h" + +#include "llpluginprocesschild.h" +#include "llplugininstance.h" +#include "llpluginmessagepipe.h" +#include "llpluginmessageclasses.h" + +static const F32 GOODBYE_SECONDS = 20.0f; +static const F32 HEARTBEAT_SECONDS = 1.0f; +static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f;  // Each call to idle will give the plugin this much time. + +LLPluginProcessChild::LLPluginProcessChild() +{ +	mState = STATE_UNINITIALIZED; +	mInstance = NULL; +	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); +	mSleepTime = PLUGIN_IDLE_SECONDS;	// default: send idle messages at 100Hz +	mCPUElapsed = 0.0f; +	mBlockingRequest = false; +	mBlockingResponseReceived = false; +} + +LLPluginProcessChild::~LLPluginProcessChild() +{ +	if (mInstance != NULL) +	{ +		sendMessageToPlugin(LLPluginMessage("base", "cleanup")); + +		// IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted  +		// appears to fail and lock up which means that a given instance of the slplugin process never exits.  +		// This is bad, especially when users try to update their version of SL - it fails because the slplugin  +		// process as well as a bunch of plugin specific files are locked and cannot be overwritten. +		exit(0); +		//delete mInstance; +		//mInstance = NULL; +	} +} + +void LLPluginProcessChild::killSockets(void) +{ +	killMessagePipe(); +	mSocket.reset(); +} + +void LLPluginProcessChild::init(U32 launcher_port) +{ +	mLauncherHost = LLHost("127.0.0.1", launcher_port); +	setState(STATE_INITIALIZED); +} + +void LLPluginProcessChild::idle(void) +{ +	bool idle_again; +	do +	{ +		if (mState < STATE_SHUTDOWNREQ) +		{   // Once we have hit the shutdown request state checking for errors might put us in a spurious  +			// error state... don't do that. + +			if (APR_STATUS_IS_EOF(mSocketError)) +			{ +				// Plugin socket was closed.  This covers both normal plugin termination and host crashes. +				setState(STATE_ERROR); +			} +			else if (mSocketError != APR_SUCCESS) +			{ +				LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR" << LL_ENDL; +				setState(STATE_ERROR); +			} + +			if ((mState > STATE_INITIALIZED) && (mMessagePipe == NULL)) +			{ +				// The pipe has been closed -- we're done. +				// TODO: This could be slightly more subtle, but I'm not sure it needs to be. +				LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR" << LL_ENDL; +				setState(STATE_ERROR); +			} +		} + +		// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). +		// USE THIS CAREFULLY, since it can starve other code.  Specifically make sure there's no way to get into a closed cycle and never return. +		// When in doubt, don't do it. +		idle_again = false; + +		if (mInstance != NULL) +		{ +			// Provide some time to the plugin +			mInstance->idle(); +		} + +		switch (mState) +		{ +		case STATE_UNINITIALIZED: +			break; + +		case STATE_INITIALIZED: +			if (mSocket->blockingConnect(mLauncherHost)) +			{ +				// This automatically sets mMessagePipe +				new LLPluginMessagePipe(this, mSocket); + +				setState(STATE_CONNECTED); +			} +			else +			{ +				// connect failed +				setState(STATE_ERROR); +			} +			break; + +		case STATE_CONNECTED: +			sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello")); +			setState(STATE_PLUGIN_LOADING); +			break; + +		case STATE_PLUGIN_LOADING: +			if (!mPluginFile.empty()) +			{ +				mInstance = new LLPluginInstance(this); +				if (mInstance->load(mPluginDir, mPluginFile) == 0) +				{ +					mHeartbeat.start(); +					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS); +					mCPUElapsed = 0.0f; +					setState(STATE_PLUGIN_LOADED); +				} +				else +				{ +					setState(STATE_ERROR); +				} +			} +			break; + +		case STATE_PLUGIN_LOADED: +		{ +			setState(STATE_PLUGIN_INITIALIZING); +			LLPluginMessage message("base", "init"); +			sendMessageToPlugin(message); +		} +		break; + +		case STATE_PLUGIN_INITIALIZING: +			// waiting for init_response... +			break; + +		case STATE_RUNNING: +			if (mInstance != NULL) +			{ +				// Provide some time to the plugin +				LLPluginMessage message("base", "idle"); +				message.setValueReal("time", PLUGIN_IDLE_SECONDS); +				sendMessageToPlugin(message); + +				mInstance->idle(); + +				if (mHeartbeat.hasExpired()) +				{ + +					// This just proves that we're not stuck down inside the plugin code. +					LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat"); + +					// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle. +					// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation. +					// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation. +					heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64()); + +					sendMessageToParent(heartbeat); + +					mHeartbeat.reset(); +					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS); +					mCPUElapsed = 0.0f; +				} +			} +			// receivePluginMessage will transition to STATE_UNLOADING +			break; + +		case STATE_SHUTDOWNREQ: +			// set next state first thing in case "cleanup" message advances state. +			setState(STATE_UNLOADING); +			mWaitGoodbye.setTimerExpirySec(GOODBYE_SECONDS); + +			if (mInstance != NULL) +			{ +				sendMessageToPlugin(LLPluginMessage("base", "cleanup")); +			} +			break; + +		case STATE_UNLOADING: +			// waiting for goodbye from plugin. +			if (mWaitGoodbye.hasExpired()) +			{ +				LL_WARNS() << "Wait for goodbye expired.  Advancing to UNLOADED" << LL_ENDL; +				setState(STATE_UNLOADED); +			} +			break; + +		case STATE_UNLOADED: +			killSockets(); +			delete mInstance; +			mInstance = NULL; +			setState(STATE_DONE); +			break; + +		case STATE_ERROR: +			// Close the socket to the launcher +			killSockets(); +			// TODO: Where do we go from here?  Just exit()? +			setState(STATE_DONE); +			break; + +		case STATE_DONE: +			// just sit here. +			break; +		} + +	} while (idle_again); +} + +void LLPluginProcessChild::sleep(F64 seconds) +{ +	deliverQueuedMessages(); +	if (mMessagePipe) +	{ +		mMessagePipe->pump(seconds); +	} +	else +	{ +		ms_sleep((int)(seconds * 1000.0f)); +	} +} + +void LLPluginProcessChild::pump(void) +{ +	deliverQueuedMessages(); +	if (mMessagePipe) +	{ +		mMessagePipe->pump(0.0f); +	} +	else +	{ +		// Should we warn here? +	} +} + + +bool LLPluginProcessChild::isRunning(void) +{ +	bool result = false; + +	if (mState == STATE_RUNNING) +		result = true; + +	return result; +} + +bool LLPluginProcessChild::isDone(void) +{ +	bool result = false; + +	switch (mState) +	{ +	case STATE_DONE: +		result = true; +		break; +	default: +		break; +	} + +	return result; +} + +void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message) +{ +	if (mInstance) +	{ +		std::string buffer = message.generate(); + +		LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL; +		LLTimer elapsed; + +		mInstance->sendMessage(buffer); + +		mCPUElapsed += elapsed.getElapsedTimeF64(); +	} +	else +	{ +		LL_WARNS("Plugin") << "mInstance == NULL" << LL_ENDL; +	} +} + +void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message) +{ +	std::string buffer = message.generate(); + +	LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL; + +	writeMessageRaw(buffer); +} + +void LLPluginProcessChild::receiveMessageRaw(const std::string &message) +{ +	// Incoming message from the TCP Socket + +	LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL; + +	// Decode this message +	LLPluginMessage parsed; +	parsed.parse(message); + +	if (mBlockingRequest) +	{ +		// We're blocking the plugin waiting for a response. + +		if (parsed.hasValue("blocking_response")) +		{ +			// This is the message we've been waiting for -- fall through and send it immediately.  +			mBlockingResponseReceived = true; +		} +		else +		{ +			// Still waiting.  Queue this message and don't process it yet. +			mMessageQueue.push(message); +			return; +		} +	} + +	bool passMessage = true; + +	// FIXME: how should we handle queueing here? + +	{ +		std::string message_class = parsed.getClass(); +		if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) +		{ +			passMessage = false; + +			std::string message_name = parsed.getName(); +			if (message_name == "load_plugin") +			{ +				mPluginFile = parsed.getValue("file"); +				mPluginDir = parsed.getValue("dir"); +			} +			else if (message_name == "shutdown_plugin") +			{ +				setState(STATE_SHUTDOWNREQ); +			} +			else if (message_name == "shm_add") +			{ +				std::string name = parsed.getValue("name"); +				size_t size = (size_t)parsed.getValueS32("size"); + +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); +				if (iter != mSharedMemoryRegions.end()) +				{ +					// Need to remove the old region first +					LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL; +				} +				else +				{ +					// This is a new region +					LLPluginSharedMemory *region = new LLPluginSharedMemory; +					if (region->attach(name, size)) +					{ +						mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); + +						std::stringstream addr; +						addr << region->getMappedAddress(); + +						// Send the add notification to the plugin +						LLPluginMessage message("base", "shm_added"); +						message.setValue("name", name); +						message.setValueS32("size", (S32)size); +						message.setValuePointer("address", region->getMappedAddress()); +						sendMessageToPlugin(message); + +						// and send the response to the parent +						message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response"); +						message.setValue("name", name); +						sendMessageToParent(message); +					} +					else +					{ +						LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; +						delete region; +					} +				} + +			} +			else if (message_name == "shm_remove") +			{ +				std::string name = parsed.getValue("name"); +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); +				if (iter != mSharedMemoryRegions.end()) +				{ +					// forward the remove request to the plugin -- its response will trigger us to detach the segment. +					LLPluginMessage message("base", "shm_remove"); +					message.setValue("name", name); +					sendMessageToPlugin(message); +				} +				else +				{ +					LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL; +				} +			} +			else if (message_name == "sleep_time") +			{ +				mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz +			} +			else if (message_name == "crash") +			{ +				// Crash the plugin +				LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL; +			} +			else if (message_name == "hang") +			{ +				// Hang the plugin +				LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL; +				while (1) +				{ +					// wheeeeeeeee...... +				} +			} +			else +			{ +				LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL; +			} +		} +	} + +	if (passMessage && mInstance != NULL) +	{ +		LLTimer elapsed; + +		mInstance->sendMessage(message); + +		mCPUElapsed += elapsed.getElapsedTimeF64(); +	} +} + +/* virtual */ +void LLPluginProcessChild::receivePluginMessage(const std::string &message) +{ +	LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL; + +	if (mBlockingRequest) +	{ +		//  +		LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL; +	} + +	// Incoming message from the plugin instance +	bool passMessage = true; + +	// FIXME: how should we handle queueing here? + +	// Intercept certain base messages (responses to ones sent by this class) +	{ +		// Decode this message +		LLPluginMessage parsed; +		parsed.parse(message); + +		if (parsed.hasValue("blocking_request")) +		{ +			mBlockingRequest = true; +		} + +		std::string message_class = parsed.getClass(); +		if (message_class == "base") +		{ +			std::string message_name = parsed.getName(); +			if (message_name == "init_response") +			{ +				// The plugin has finished initializing. +				setState(STATE_RUNNING); + +				// Don't pass this message up to the parent +				passMessage = false; + +				LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response"); +				LLSD versions = parsed.getValueLLSD("versions"); +				new_message.setValueLLSD("versions", versions); + +				if (parsed.hasValue("plugin_version")) +				{ +					std::string plugin_version = parsed.getValue("plugin_version"); +					new_message.setValueLLSD("plugin_version", plugin_version); +				} + +				// Let the parent know it's loaded and initialized. +				sendMessageToParent(new_message); +			} +			else if (message_name == "goodbye") +			{ +				setState(STATE_UNLOADED); +			} +			else if (message_name == "shm_remove_response") +			{ +				// Don't pass this message up to the parent +				passMessage = false; + +				std::string name = parsed.getValue("name"); +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); +				if (iter != mSharedMemoryRegions.end()) +				{ +					// detach the shared memory region +					iter->second->detach(); + +					// and remove it from our map +					mSharedMemoryRegions.erase(iter); + +					// Finally, send the response to the parent. +					LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response"); +					message.setValue("name", name); +					sendMessageToParent(message); +				} +				else +				{ +					LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL; +				} +			} +		} +	} + +	if (passMessage) +	{ +		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL; +		writeMessageRaw(message); +	} + +	while (mBlockingRequest) +	{ +		// The plugin wants to block and wait for a response to this message. +		sleep(mSleepTime);	// this will pump the message pipe and process messages + +		if (mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL)) +		{ +			// Response has been received, or we've hit an error state.  Stop waiting. +			mBlockingRequest = false; +			mBlockingResponseReceived = false; +		} +	} +} + + +void LLPluginProcessChild::setState(EState state) +{ +	LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; +	mState = state; +}; + +void LLPluginProcessChild::deliverQueuedMessages() +{ +	if (!mBlockingRequest) +	{ +		while (!mMessageQueue.empty()) +		{ +			receiveMessageRaw(mMessageQueue.front()); +			mMessageQueue.pop(); +		} +	} +} diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp index e8474ceddc..c296a0413d 100644 --- a/indra/media_plugins/example/media_plugin_example.cpp +++ b/indra/media_plugins/example/media_plugin_example.cpp @@ -1,392 +1,391 @@ -/**
 -* @file media_plugin_example.cpp
 -* @brief Example plugin for LLMedia API plugin system
 -*
 -* @cond
 -* $LicenseInfo:firstyear=2008&license=viewerlgpl$
 -* Second Life Viewer Source Code
 -* Copyright (C) 2010, Linden Research, Inc.
 -*
 -* This library is free software; you can redistribute it and/or
 -* modify it under the terms of the GNU Lesser General Public
 -* License as published by the Free Software Foundation;
 -* version 2.1 of the License only.
 -*
 -* This library is distributed in the hope that it will be useful,
 -* but WITHOUT ANY WARRANTY; without even the implied warranty of
 -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 -* Lesser General Public License for more details.
 -*
 -* You should have received a copy of the GNU Lesser General Public
 -* License along with this library; if not, write to the Free Software
 -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 -*
 -* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 -* $/LicenseInfo$
 -* @endcond
 -*/
 -
 -#include "linden_common.h"
 -
 -#include "llgl.h"
 -#include "llplugininstance.h"
 -#include "llpluginmessage.h"
 -#include "llpluginmessageclasses.h"
 -#include "media_plugin_base.h"
 -
 -#include <time.h>
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -class mediaPluginExample :
 -	public MediaPluginBase
 -{
 -public:
 -	mediaPluginExample(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
 -	~mediaPluginExample();
 -
 -	/*virtual*/ void receiveMessage(const char* message_string);
 -
 -private:
 -	bool init();
 -	void update(F64 milliseconds);
 -	bool mFirstTime;
 -
 -	time_t mLastUpdateTime;
 -	enum Constants { ENumObjects = 64 };
 -	unsigned char* mBackgroundPixels;
 -	int mColorR[ENumObjects];
 -	int mColorG[ENumObjects];
 -	int mColorB[ENumObjects];
 -	int mXpos[ENumObjects];
 -	int mYpos[ENumObjects];
 -	int mXInc[ENumObjects];
 -	int mYInc[ENumObjects];
 -	int mBlockSize[ENumObjects];
 -};
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -mediaPluginExample::mediaPluginExample(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) :
 -MediaPluginBase(host_send_func, host_user_data)
 -{
 -	mFirstTime = true;
 -	mTextureWidth = 0;
 -	mTextureHeight = 0;
 -	mWidth = 0;
 -	mHeight = 0;
 -	mDepth = 4;
 -	mPixels = 0;
 -	mLastUpdateTime = 0;
 -	mBackgroundPixels = 0;
 -}
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -mediaPluginExample::~mediaPluginExample()
 -{
 -}
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -void mediaPluginExample::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 = "Example plugin 0.0.0";
 -				message.setValue("plugin_version", plugin_version);
 -				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")
 -			{
 -				LLPluginMessage message("base", "goodbye");
 -				sendMessage(message);
 -
 -				mDeleteMe = true;
 -			}
 -			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");
 -
 -				mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
 -
 -			}
 -			else if (message_name == "shm_remove")
 -			{
 -				std::string name = message_in.getValue("name");
 -
 -				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 == "init")
 -			{
 -				// Plugin gets to decide the texture parameters to use.
 -				mDepth = 4;
 -				LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
 -				message.setValueS32("default_width", 1024);
 -				message.setValueS32("default_height", 1024);
 -				message.setValueS32("depth", mDepth);
 -				message.setValueU32("internalformat", GL_RGB);
 -				message.setValueU32("format", GL_RGBA);
 -				message.setValueU32("type", GL_UNSIGNED_BYTE);
 -				message.setValueBoolean("coords_opengl", true);
 -				sendMessage(message);
 -			}
 -			else 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;
 -
 -						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);
 -
 -				mFirstTime = true;
 -				mLastUpdateTime = 0;
 -
 -			}
 -			else if (message_name == "load_uri")
 -			{
 -			}
 -			else if (message_name == "mouse_event")
 -			{
 -				std::string event = message_in.getValue("event");
 -				if (event == "down")
 -				{
 -
 -				}
 -				else if (event == "up")
 -				{
 -				}
 -				else if (event == "double_click")
 -				{
 -				}
 -			}
 -		}
 -		else
 -		{
 -		};
 -	}
 -}
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -void mediaPluginExample::update(F64 milliseconds)
 -{
 -	if (mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048)
 -		return;
 -
 -	if (mPixels == 0)
 -		return;
 -
 -	if (mFirstTime)
 -	{
 -		for (int n = 0; n < ENumObjects; ++n)
 -		{
 -			mXpos[n] = (mWidth / 2) + rand() % (mWidth / 16) - (mWidth / 32);
 -			mYpos[n] = (mHeight / 2) + rand() % (mHeight / 16) - (mHeight / 32);
 -
 -			mColorR[n] = rand() % 0x60 + 0x60;
 -			mColorG[n] = rand() % 0x60 + 0x60;
 -			mColorB[n] = rand() % 0x60 + 0x60;
 -
 -			mXInc[n] = 0;
 -			while (mXInc[n] == 0)
 -				mXInc[n] = rand() % 7 - 3;
 -
 -			mYInc[n] = 0;
 -			while (mYInc[n] == 0)
 -				mYInc[n] = rand() % 9 - 4;
 -
 -			mBlockSize[n] = rand() % 0x30 + 0x10;
 -		};
 -
 -		delete[] mBackgroundPixels;
 -
 -		mBackgroundPixels = new unsigned char[mWidth * mHeight * mDepth];
 -
 -		mFirstTime = false;
 -	};
 -
 -	if (time(NULL) > mLastUpdateTime + 3)
 -	{
 -		const int num_squares = rand() % 20 + 4;
 -		int sqr1_r = rand() % 0x80 + 0x20;
 -		int sqr1_g = rand() % 0x80 + 0x20;
 -		int sqr1_b = rand() % 0x80 + 0x20;
 -		int sqr2_r = rand() % 0x80 + 0x20;
 -		int sqr2_g = rand() % 0x80 + 0x20;
 -		int sqr2_b = rand() % 0x80 + 0x20;
 -
 -		for (int y1 = 0; y1 < num_squares; ++y1)
 -		{
 -			for (int x1 = 0; x1 < num_squares; ++x1)
 -			{
 -				int px_start = mWidth * x1 / num_squares;
 -				int px_end = (mWidth * (x1 + 1)) / num_squares;
 -				int py_start = mHeight * y1 / num_squares;
 -				int py_end = (mHeight * (y1 + 1)) / num_squares;
 -
 -				for (int y2 = py_start; y2 < py_end; ++y2)
 -				{
 -					for (int x2 = px_start; x2 < px_end; ++x2)
 -					{
 -						int rowspan = mWidth * mDepth;
 -
 -						if ((y1 % 2) ^ (x1 % 2))
 -						{
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 0] = sqr1_r;
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 1] = sqr1_g;
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 2] = sqr1_b;
 -						}
 -						else
 -						{
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 0] = sqr2_r;
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 1] = sqr2_g;
 -							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 2] = sqr2_b;
 -						};
 -					};
 -				};
 -			};
 -		};
 -
 -		time(&mLastUpdateTime);
 -	};
 -
 -	memcpy(mPixels, mBackgroundPixels, mWidth * mHeight * mDepth);
 -
 -	for (int n = 0; n < ENumObjects; ++n)
 -	{
 -		if (rand() % 50 == 0)
 -		{
 -			mXInc[n] = 0;
 -			while (mXInc[n] == 0)
 -				mXInc[n] = rand() % 7 - 3;
 -
 -			mYInc[n] = 0;
 -			while (mYInc[n] == 0)
 -				mYInc[n] = rand() % 9 - 4;
 -		};
 -
 -		if (mXpos[n] + mXInc[n] < 0 || mXpos[n] + mXInc[n] >= mWidth - mBlockSize[n])
 -			mXInc[n] = -mXInc[n];
 -
 -		if (mYpos[n] + mYInc[n] < 0 || mYpos[n] + mYInc[n] >= mHeight - mBlockSize[n])
 -			mYInc[n] = -mYInc[n];
 -
 -		mXpos[n] += mXInc[n];
 -		mYpos[n] += mYInc[n];
 -
 -		for (int y = 0; y < mBlockSize[n]; ++y)
 -		{
 -			for (int x = 0; x < mBlockSize[n]; ++x)
 -			{
 -				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 0] = mColorR[n];
 -				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 1] = mColorG[n];
 -				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 2] = mColorB[n];
 -			};
 -		};
 -	};
 -
 -	setDirty(0, 0, mWidth, mHeight);
 -};
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -bool mediaPluginExample::init()
 -{
 -	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
 -	message.setValue("name", "Example Plugin");
 -	sendMessage(message);
 -
 -	return true;
 -};
 -
 -////////////////////////////////////////////////////////////////////////////////
 -//
 -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func,
 -	void* host_user_data,
 -	LLPluginInstance::sendMessageFunction *plugin_send_func,
 -	void **plugin_user_data)
 -{
 -	mediaPluginExample* self = new mediaPluginExample(host_send_func, host_user_data);
 -	*plugin_send_func = mediaPluginExample::staticReceiveMessage;
 -	*plugin_user_data = (void*)self;
 -
 -	return 0;
 -}
 -
 +/** +* @file media_plugin_example.cpp +* @brief Example plugin for LLMedia API plugin system +* +* @cond +* $LicenseInfo:firstyear=2008&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +* @endcond +*/ + +#include "linden_common.h" + +#include "llgl.h" +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#include <time.h> + +//////////////////////////////////////////////////////////////////////////////// +// +class mediaPluginExample : +	public MediaPluginBase +{ +public: +	mediaPluginExample(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); +	~mediaPluginExample(); + +	/*virtual*/ void receiveMessage(const char* message_string); + +private: +	bool init(); +	void update(F64 milliseconds); +	bool mFirstTime; + +	time_t mLastUpdateTime; +	enum Constants { ENumObjects = 64 }; +	unsigned char* mBackgroundPixels; +	int mColorR[ENumObjects]; +	int mColorG[ENumObjects]; +	int mColorB[ENumObjects]; +	int mXpos[ENumObjects]; +	int mYpos[ENumObjects]; +	int mXInc[ENumObjects]; +	int mYInc[ENumObjects]; +	int mBlockSize[ENumObjects]; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPluginExample::mediaPluginExample(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : +MediaPluginBase(host_send_func, host_user_data) +{ +	mFirstTime = true; +	mTextureWidth = 0; +	mTextureHeight = 0; +	mWidth = 0; +	mHeight = 0; +	mDepth = 4; +	mPixels = 0; +	mLastUpdateTime = 0; +	mBackgroundPixels = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPluginExample::~mediaPluginExample() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void mediaPluginExample::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 = "Example plugin 0.0.0"; +				message.setValue("plugin_version", plugin_version); +				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") +			{ +				LLPluginMessage message("base", "goodbye"); +				sendMessage(message); + +				mDeleteMe = true; +			} +			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"); + +				mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + +			} +			else if (message_name == "shm_remove") +			{ +				std::string name = message_in.getValue("name"); + +				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 == "init") +			{ +				// Plugin gets to decide the texture parameters to use. +				mDepth = 4; +				LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); +				message.setValueS32("default_width", 1024); +				message.setValueS32("default_height", 1024); +				message.setValueS32("depth", mDepth); +				message.setValueU32("internalformat", GL_RGB); +				message.setValueU32("format", GL_RGBA); +				message.setValueU32("type", GL_UNSIGNED_BYTE); +				message.setValueBoolean("coords_opengl", true); +				sendMessage(message); +			} +			else 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; + +						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); + +				mFirstTime = true; +				mLastUpdateTime = 0; + +			} +			else if (message_name == "load_uri") +			{ +			} +			else if (message_name == "mouse_event") +			{ +				std::string event = message_in.getValue("event"); +				if (event == "down") +				{ + +				} +				else if (event == "up") +				{ +				} +				else if (event == "double_click") +				{ +				} +			} +		} +		else +		{ +		}; +	} +} + +//////////////////////////////////////////////////////////////////////////////// +// +void mediaPluginExample::update(F64 milliseconds) +{ +	if (mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048) +		return; + +	if (mPixels == 0) +		return; + +	if (mFirstTime) +	{ +		for (int n = 0; n < ENumObjects; ++n) +		{ +			mXpos[n] = (mWidth / 2) + rand() % (mWidth / 16) - (mWidth / 32); +			mYpos[n] = (mHeight / 2) + rand() % (mHeight / 16) - (mHeight / 32); + +			mColorR[n] = rand() % 0x60 + 0x60; +			mColorG[n] = rand() % 0x60 + 0x60; +			mColorB[n] = rand() % 0x60 + 0x60; + +			mXInc[n] = 0; +			while (mXInc[n] == 0) +				mXInc[n] = rand() % 7 - 3; + +			mYInc[n] = 0; +			while (mYInc[n] == 0) +				mYInc[n] = rand() % 9 - 4; + +			mBlockSize[n] = rand() % 0x30 + 0x10; +		}; + +		delete[] mBackgroundPixels; + +		mBackgroundPixels = new unsigned char[mWidth * mHeight * mDepth]; + +		mFirstTime = false; +	}; + +	if (time(NULL) > mLastUpdateTime + 3) +	{ +		const int num_squares = rand() % 20 + 4; +		int sqr1_r = rand() % 0x80 + 0x20; +		int sqr1_g = rand() % 0x80 + 0x20; +		int sqr1_b = rand() % 0x80 + 0x20; +		int sqr2_r = rand() % 0x80 + 0x20; +		int sqr2_g = rand() % 0x80 + 0x20; +		int sqr2_b = rand() % 0x80 + 0x20; + +		for (int y1 = 0; y1 < num_squares; ++y1) +		{ +			for (int x1 = 0; x1 < num_squares; ++x1) +			{ +				int px_start = mWidth * x1 / num_squares; +				int px_end = (mWidth * (x1 + 1)) / num_squares; +				int py_start = mHeight * y1 / num_squares; +				int py_end = (mHeight * (y1 + 1)) / num_squares; + +				for (int y2 = py_start; y2 < py_end; ++y2) +				{ +					for (int x2 = px_start; x2 < px_end; ++x2) +					{ +						int rowspan = mWidth * mDepth; + +						if ((y1 % 2) ^ (x1 % 2)) +						{ +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 0] = sqr1_r; +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 1] = sqr1_g; +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 2] = sqr1_b; +						} +						else +						{ +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 0] = sqr2_r; +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 1] = sqr2_g; +							mBackgroundPixels[y2 * rowspan + x2 * mDepth + 2] = sqr2_b; +						}; +					}; +				}; +			}; +		}; + +		time(&mLastUpdateTime); +	}; + +	memcpy(mPixels, mBackgroundPixels, mWidth * mHeight * mDepth); + +	for (int n = 0; n < ENumObjects; ++n) +	{ +		if (rand() % 50 == 0) +		{ +			mXInc[n] = 0; +			while (mXInc[n] == 0) +				mXInc[n] = rand() % 7 - 3; + +			mYInc[n] = 0; +			while (mYInc[n] == 0) +				mYInc[n] = rand() % 9 - 4; +		}; + +		if (mXpos[n] + mXInc[n] < 0 || mXpos[n] + mXInc[n] >= mWidth - mBlockSize[n]) +			mXInc[n] = -mXInc[n]; + +		if (mYpos[n] + mYInc[n] < 0 || mYpos[n] + mYInc[n] >= mHeight - mBlockSize[n]) +			mYInc[n] = -mYInc[n]; + +		mXpos[n] += mXInc[n]; +		mYpos[n] += mYInc[n]; + +		for (int y = 0; y < mBlockSize[n]; ++y) +		{ +			for (int x = 0; x < mBlockSize[n]; ++x) +			{ +				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 0] = mColorR[n]; +				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 1] = mColorG[n]; +				mPixels[(mXpos[n] + x) * mDepth + (mYpos[n] + y) * mDepth * mWidth + 2] = mColorB[n]; +			}; +		}; +	}; + +	setDirty(0, 0, mWidth, mHeight); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +bool mediaPluginExample::init() +{ +	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); +	message.setValue("name", "Example Plugin"); +	sendMessage(message); + +	return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, +	void* host_user_data, +	LLPluginInstance::sendMessageFunction *plugin_send_func, +	void **plugin_user_data) +{ +	mediaPluginExample* self = new mediaPluginExample(host_send_func, host_user_data); +	*plugin_send_func = mediaPluginExample::staticReceiveMessage; +	*plugin_user_data = (void*)self; + +	return 0; +} | 
