void MediaPluginCEF::receiveMessage(const char* message_string)
{
	//  std::cerr << "MediaPluginCEF::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 = "CEF plugin 1.1.3";
				message.setValue("plugin_version", plugin_version);
				sendMessage(message);
			}
			else if (message_name == "idle")
			{
				mLLCEFLib->update();

                mVolumeCatcher.pump();
				// this seems bad but unless the state changes (it won't until we figure out
				// how to get CEF to tell us if copy/cut/paste is available) then this function
				// will return immediately
				checkEditState();
			}
			else if (message_name == "cleanup")
			{
				mLLCEFLib->requestExit();
			}
			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)
					{
						mPixels = NULL;
						mTextureSegmentName.clear();
					}
					mSharedSegments.erase(iter);
				}
				else
				{
				}

				LLPluginMessage message("base", "shm_remove_response");
				message.setValue("name", name);
				sendMessage(message);
			}
			else
			{
			}
		}
		else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
		{
			if (message_name == "init")
			{
				// event callbacks from LLCefLib
				mLLCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
				mLLCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
				mLLCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this));
				mLLCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnAddressChangeCallback(std::bind(&MediaPluginCEF::onAddressChangeCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnNavigateURLCallback(std::bind(&MediaPluginCEF::onNavigateURLCallback, this, std::placeholders::_1, std::placeholders::_2));
				mLLCEFLib->setOnHTTPAuthCallback(std::bind(&MediaPluginCEF::onHTTPAuthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
				mLLCEFLib->setOnFileDownloadCallback(std::bind(&MediaPluginCEF::onFileDownloadCallback, this, std::placeholders::_1));
				mLLCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1, std::placeholders::_2));
				mLLCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this));

				LLCEFLib::LLCEFLibSettings settings;
				settings.initial_width = 1024;
				settings.initial_height = 1024;
				settings.plugins_enabled = mPluginsEnabled;
				settings.media_stream_enabled = false; // MAINT-6060 - WebRTC media removed until we can add granualrity/query UI
				settings.javascript_enabled = mJavascriptEnabled;
				settings.cookies_enabled = mCookiesEnabled;
				settings.cookie_store_path = mCookiePath;
				settings.cache_enabled = true;
				settings.cache_path = mCachePath;
				settings.locale = generate_cef_locale(mHostLanguage);
				settings.accept_language_list = mHostLanguage;
				settings.user_agent_substring = mLLCEFLib->makeCompatibleUserAgentString(mUserAgentSubtring);
				settings.debug_output = mEnableMediaPluginDebugging;
				settings.log_file = mLogFile;

				bool result = mLLCEFLib->init(settings);
				if (!result)
				{
					// if this fails, the media system in viewer will put up a message
				}

				// 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_BGRA);
				message.setValueU32("type", GL_UNSIGNED_BYTE);
				message.setValueBoolean("coords_opengl", true);
				sendMessage(message);
			}
			else if (message_name == "set_user_data_path")
			{
				std::string user_data_path_cache = message_in.getValue("cache_path");
				std::string user_data_path_cookies = message_in.getValue("cookies_path");
				std::string user_data_path_logs = message_in.getValue("logs_path");
				mCachePath = user_data_path_cache + "cef_cache";
				mCookiePath = user_data_path_cookies + "cef_cookies";
				mLogFile = user_data_path_logs + "cef.log";
			}
			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;
					};
				};

				mLLCEFLib->setSize(mWidth, mHeight);

				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 == "set_language_code")
			{
				mHostLanguage = message_in.getValue("language");
			}
			else if (message_name == "load_uri")
			{
				std::string uri = message_in.getValue("uri");
				mLLCEFLib->navigate(uri);
			}
			else if (message_name == "set_cookie")
			{
				std::string uri = message_in.getValue("uri");
				std::string name = message_in.getValue("name");
				std::string value = message_in.getValue("value");
				std::string domain = message_in.getValue("domain");
				std::string path = message_in.getValue("path");
				bool httponly = message_in.getValueBoolean("httponly");
				bool secure = message_in.getValueBoolean("secure");
				mLLCEFLib->setCookie(uri, name, value, domain, path, httponly, secure);
			}
			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");

				// only even send left mouse button events to LLCEFLib
				// (partially prompted by crash in OS X CEF when sending right button events)
				// we catch the right click in viewer and display our own context menu anyway
				S32 button = message_in.getValueS32("button");
				LLCEFLib::EMouseButton btn = LLCEFLib::MB_MOUSE_BUTTON_LEFT;

				if (event == "down" && button == 0)
				{
					mLLCEFLib->mouseButton(btn, LLCEFLib::ME_MOUSE_DOWN, x, y);
					mLLCEFLib->setFocus(true);

					std::stringstream str;
					str << "Mouse down at = " << x << ", " << y;
					postDebugMessage(str.str());
				}
				else if (event == "up" && button == 0)
				{
					mLLCEFLib->mouseButton(btn, LLCEFLib::ME_MOUSE_UP, x, y);

					std::stringstream str;
					str << "Mouse up at = " << x << ", " << y;
					postDebugMessage(str.str());
				}
				else if (event == "double_click")
				{
					mLLCEFLib->mouseButton(btn, LLCEFLib::ME_MOUSE_DOUBLE_CLICK, x, y);
				}
				else
				{
					mLLCEFLib->mouseMove(x, y);
				}
			}
			else if (message_name == "scroll_event")
			{
				S32 x = message_in.getValueS32("x");
				S32 y = message_in.getValueS32("y");
				const int scaling_factor = 40;
				y *= -scaling_factor;

				mLLCEFLib->mouseWheel(x, y);
			}
			else if (message_name == "text_event")
			{
				std::string text = message_in.getValue("text");
				std::string modifiers = message_in.getValue("modifiers");
				LLSD native_key_data = message_in.getValueLLSD("native_key_data");

				unicodeInput(text, decodeModifiers(modifiers), native_key_data);
			}
			else if (message_name == "key_event")
			{
#if LL_DARWIN
				std::string event = message_in.getValue("event");
				S32 key = message_in.getValueS32("key");
                LLSD native_key_data = message_in.getValueLLSD("native_key_data");

#if 0
				if (event == "down")
				{
					//mLLCEFLib->keyPress(key, true);
					mLLCEFLib->keyboardEvent(LLCEFLib::KE_KEY_DOWN, (uint32_t)key, 0, LLCEFLib::KM_MODIFIER_NONE, 0, 0, 0);

				}
				else if (event == "up")
				{
					//mLLCEFLib->keyPress(key, false);
					mLLCEFLib->keyboardEvent(LLCEFLib::KE_KEY_UP, (uint32_t)key, 0, LLCEFLib::KM_MODIFIER_NONE, 0, 0, 0);
				}
#else
                // Treat unknown events as key-up for safety.
                LLCEFLib::EKeyEvent key_event = LLCEFLib::KE_KEY_UP;
                if (event == "down")
                {
                    key_event = LLCEFLib::KE_KEY_DOWN;
                }
                else if (event == "repeat")
                {
                    key_event = LLCEFLib::KE_KEY_REPEAT;
                }

                keyEvent(key_event, key, LLCEFLib::KM_MODIFIER_NONE, native_key_data);

#endif
#else
				std::string event = message_in.getValue("event");
				S32 key = message_in.getValueS32("key");
				std::string modifiers = message_in.getValue("modifiers");
				LLSD native_key_data = message_in.getValueLLSD("native_key_data");

				// Treat unknown events as key-up for safety.
				LLCEFLib::EKeyEvent key_event = LLCEFLib::KE_KEY_UP;
				if (event == "down")
				{
					key_event = LLCEFLib::KE_KEY_DOWN;
				}
				else if (event == "repeat")
				{
					key_event = LLCEFLib::KE_KEY_REPEAT;
				}

				keyEvent(key_event, key, decodeModifiers(modifiers), native_key_data);
#endif
			}
			else if (message_name == "enable_media_plugin_debugging")
			{
				mEnableMediaPluginDebugging = message_in.getValueBoolean("enable");
			}
			if (message_name == "auth_response")
			{
				authResponse(message_in);
			}
			if (message_name == "edit_cut")
			{
				mLLCEFLib->editCut();
			}
			if (message_name == "edit_copy")
			{
				mLLCEFLib->editCopy();
			}
			if (message_name == "edit_paste")
			{
				mLLCEFLib->editPaste();
			}
		}
		else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
		{
			if (message_name == "set_page_zoom_factor")
			{
				F32 factor = (F32)message_in.getValueReal("factor");
				mLLCEFLib->setPageZoom(factor);
			}
			if (message_name == "browse_stop")
			{
				mLLCEFLib->stop();
			}
			else if (message_name == "browse_reload")
			{
				bool ignore_cache = true;
				mLLCEFLib->reload(ignore_cache);
			}
			else if (message_name == "browse_forward")
			{
				mLLCEFLib->goForward();
			}
			else if (message_name == "browse_back")
			{
				mLLCEFLib->goBack();
			}
			else if (message_name == "cookies_enabled")
			{
				mCookiesEnabled = message_in.getValueBoolean("enable");
			}
			else if (message_name == "set_user_agent")
			{
				mUserAgentSubtring = message_in.getValue("user_agent");
			}
			else if (message_name == "show_web_inspector")
			{
				mLLCEFLib->showDevTools(true);
			}
			else if (message_name == "plugins_enabled")
			{
				mPluginsEnabled = message_in.getValueBoolean("enable");
			}
			else if (message_name == "javascript_enabled")
			{
				mJavascriptEnabled = message_in.getValueBoolean("enable");
			}
		}
        else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
        {
            if (message_name == "set_volume")
            {
                F32 volume = (F32)message_in.getValueReal("volume");
                setVolume(volume);
            }
        }
        else
		{
		};
	}
}
void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
{
    bool motion = false, buttonEvents = false;
    boost::optional<TOOL_EVENT> evt;

    int type = aEvent.GetEventType();

    // Mouse handling
    if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
        type == wxEVT_MAGNIFY ||
#endif
        type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP ||
        type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP ||
        type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ||
        type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK ||
        // Event issued whem mouse retains position in screen coordinates,
        // but changes in world coordinates (e.g. autopanning)
        type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE )
    {
        wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
        int mods = decodeModifiers( me );

        VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetMousePosition();
        VECTOR2D pos = getView()->ToWorld( screenPos );

        if( pos != m_lastMousePos )
        {
            motion = true;
            m_lastMousePos = pos;
        }

        for( unsigned int i = 0; i < m_buttons.size(); i++ )
            buttonEvents |= handleMouseButton( aEvent, i, motion );

        if( !buttonEvents && motion )
        {
            evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
            evt->SetMousePosition( pos );
        }

#ifdef __APPLE__
        // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus
        // after second LMB click and currently I have no means to do better debugging
        if( type == wxEVT_LEFT_UP )
            static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus();
#endif /* __APPLE__ */
    }

    // Keyboard handling
    else if( type == wxEVT_CHAR )
    {
        wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
        int key = ke->GetKeyCode();
        int mods = decodeModifiers( ke );

        if( mods & MD_CTRL )
        {
#if !wxCHECK_VERSION( 2, 9, 0 )
            // I really look forward to the day when we will use only one version of wxWidgets..
            const int WXK_CONTROL_A = 1;
            const int WXK_CONTROL_Z = 26;
#endif

            // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT
            // http://docs.wxwidgets.org/trunk/classwx_key_event.html:
            // "char events for ASCII letters in this case carry codes corresponding to the ASCII
            // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z."
            if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
                key += 'A' - 1;
        }

        if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
            evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL );
        else
            evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
    }

    if( evt )
        m_toolMgr->ProcessEvent( *evt );

    // pass the event to the GUI, it might still be interested in it
#ifdef __APPLE__
    // On OS X, key events are always meant to be caught.  An uncaught key event is assumed
    // to be a user input error by OS X (as they are pressing keys in a context where nothing
    // is there to catch the event).  This annoyingly makes OS X beep and/or flash the screen
    // in pcbnew and the footprint editor any time a hotkey is used.  The correct procedure is
    // to NOT pass key events to the GUI under OS X.

    if( type != wxEVT_CHAR )
        aEvent.Skip();
#else
    aEvent.Skip();
#endif

    updateUI();
}
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);
            }
            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")
            {
                // DTOR most likely won't be called but the recent change to the way this process
                // is (not) killed means we see this message and can do what we need to here.
                // Note: this cleanup is ultimately what writes cookies to the disk
                LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
                LLQtWebKit::getInstance()->reset();
            }
            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_TIME)
        {
            if(message_name == "set_volume")
            {
                F32 volume = message_in.getValueReal("volume");
                setVolume(volume);
            }
        }
        else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
        {
            if(message_name == "init")
            {
                mTarget = message_in.getValue("target");

                // This is the media init message -- all necessary data for initialization should have been received.
                if(initBrowser())
                {

                    // 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_RGBA);
#if LL_QTWEBKIT_USES_PIXMAPS
                    message.setValueU32("format", GL_BGRA_EXT); // I hope this isn't system-dependant... is it?  If so, we'll have to check the root window's pixel layout or something... yuck.
#else
                    message.setValueU32("format", GL_RGBA);
#endif // LL_QTWEBKIT_USES_PIXMAPS
                    message.setValueU32("type", GL_UNSIGNED_BYTE);
                    message.setValueBoolean("coords_opengl", true);
                    sendMessage(message);
                }
                else
                {
                    // if initialization failed, we're done.
                    mDeleteMe = true;
                }

            }
            else if(message_name == "set_user_data_path")
            {
                std::string user_data_path = message_in.getValue("path"); // n.b. always has trailing platform-specific dir-delimiter
                mProfileDir = user_data_path + "browser_profile";

                // FIXME: Should we do anything with this if it comes in after the browser has been initialized?
            }
            else if(message_name == "set_language_code")
            {
                mHostLanguage = message_in.getValue("language");

                // FIXME: Should we do anything with this if it comes in after the browser has been initialized?
            }
            else if(message_name == "plugins_enabled")
            {
                mPluginsEnabled = message_in.getValueBoolean("enable");
            }
            else if(message_name == "javascript_enabled")
            {
                mJavascriptEnabled = message_in.getValueBoolean("enable");
            }
            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");
                mBackgroundR = message_in.getValueReal("background_r");
                mBackgroundG = message_in.getValueReal("background_g");
                mBackgroundB = message_in.getValueReal("background_b");
//				mBackgroundA = message_in.setValueReal("background_a");		// Ignore any alpha

                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;

                        if(initBrowserWindow())
                        {

                            // 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;
                            }
                        }
                        else
                        {
                            // Setting up the browser window failed.  This is a fatal error.
                            mDeleteMe = true;
                        }


                        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())
                {
                    if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
                    {
                        LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri );
                    }
                    else
                    {
                        mInitialNavigateURL = uri;
                    }
                }
            }
            else if(message_name == "mouse_event")
            {
                std::string event = message_in.getValue("event");
                S32 button = message_in.getValueS32("button");
                mLastMouseX = message_in.getValueS32("x");
                mLastMouseY = message_in.getValueS32("y");
                std::string modifiers = message_in.getValue("modifiers");

                // Treat unknown mouse events as mouse-moves.
                LLQtWebKit::EMouseEvent mouse_event = LLQtWebKit::ME_MOUSE_MOVE;
                if(event == "down")
                {
                    mouse_event = LLQtWebKit::ME_MOUSE_DOWN;
                }
                else if(event == "up")
                {
                    mouse_event = LLQtWebKit::ME_MOUSE_UP;
                }
                else if(event == "double_click")
                {
                    mouse_event = LLQtWebKit::ME_MOUSE_DOUBLE_CLICK;
                }

                LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, mouse_event, button, mLastMouseX, mLastMouseY, decodeModifiers(modifiers));
                checkEditState();
            }
            else if(message_name == "scroll_event")
            {
                S32 x = message_in.getValueS32("x");
                S32 y = message_in.getValueS32("y");
                std::string modifiers = message_in.getValue("modifiers");

                // Incoming scroll events are adjusted so that 1 detent is approximately 1 unit.
                // Qt expects 1 detent to be 120 units.
                // It also seems that our y scroll direction is inverted vs. what Qt expects.

                x *= 120;
                y *= -120;

                LLQtWebKit::getInstance()->scrollWheelEvent(mBrowserWindowId, mLastMouseX, mLastMouseY, x, y, decodeModifiers(modifiers));
            }
            else if(message_name == "key_event")
            {
                std::string event = message_in.getValue("event");
                S32 key = message_in.getValueS32("key");
                std::string modifiers = message_in.getValue("modifiers");
                LLSD native_key_data = message_in.getValueLLSD("native_key_data");

                // Treat unknown events as key-up for safety.
                LLQtWebKit::EKeyEvent key_event = LLQtWebKit::KE_KEY_UP;
                if(event == "down")
                {
                    key_event = LLQtWebKit::KE_KEY_DOWN;
                }
                else if(event == "repeat")
                {
                    key_event = LLQtWebKit::KE_KEY_REPEAT;
                }

                keyEvent(key_event, key, decodeModifiers(modifiers), native_key_data);
            }
            else if(message_name == "text_event")
            {
                std::string text = message_in.getValue("text");
                std::string modifiers = message_in.getValue("modifiers");
                LLSD native_key_data = message_in.getValueLLSD("native_key_data");

                unicodeInput(text, decodeModifiers(modifiers), native_key_data);
            }
            if(message_name == "edit_cut")
            {
                LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT );
                checkEditState();
            }
            if(message_name == "edit_copy")
            {
                LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY );
                checkEditState();
            }
            if(message_name == "edit_paste")
            {
                LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE );
                checkEditState();
            }
            if(message_name == "pick_file_response")
            {
                onPickFileResponse(message_in.getValue("file"));
            }
            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 );

                if(mFirstFocus && val)
                {
                    // On the first focus, post a tab key event.  This fixes a problem with initial focus.
                    std::string empty;
                    keyEvent(LLQtWebKit::KE_KEY_DOWN, KEY_TAB, decodeModifiers(empty));
                    keyEvent(LLQtWebKit::KE_KEY_UP, KEY_TAB, decodeModifiers(empty));
                    mFirstFocus = false;
                }
            }
            else if(message_name == "clear_cache")
            {
                LLQtWebKit::getInstance()->clearCache();
            }
            else if(message_name == "clear_cookies")
            {
                LLQtWebKit::getInstance()->clearAllCookies();
            }
            else if(message_name == "enable_cookies")
            {
                mCookiesEnabled = message_in.getValueBoolean("enable");
                LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled );
            }
            else if(message_name == "enable_plugins")
            {
                mPluginsEnabled = message_in.getValueBoolean("enable");
                LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled );
            }
            else if(message_name == "enable_javascript")
            {
                mJavascriptEnabled = message_in.getValueBoolean("enable");
                //LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
            }
            else if(message_name == "set_cookies")
            {
                LLQtWebKit::getInstance()->setCookies(message_in.getValue("cookies"));
            }
            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")
            {
                mUserAgent = message_in.getValue("user_agent");
                LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
            }
            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 if(message_name == "proxy_window_opened")
            {
                std::string target = message_in.getValue("target");
                std::string uuid = message_in.getValue("uuid");
                LLQtWebKit::getInstance()->proxyWindowOpened(mBrowserWindowId, target, uuid);
            }
            else if(message_name == "proxy_window_closed")
            {
                std::string uuid = message_in.getValue("uuid");
                LLQtWebKit::getInstance()->proxyWindowClosed(mBrowserWindowId, uuid);
            }
            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;
        };
    }
}
bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
{
    BUTTON_STATE* st = m_buttons[aIndex];
    wxEventType type = aEvent.GetEventType();
    boost::optional<TOOL_EVENT> evt;
    bool isClick = false;

//    bool up = type == st->upEvent;
//    bool down = type == st->downEvent;
    bool up = false, down = false;
    bool dblClick = type == st->dblClickEvent;
    bool state = st->GetState();

    if( !dblClick )
    {
        // Sometimes the dispatcher does not receive mouse button up event, so it stays
        // in the dragging mode even if the mouse button is not held anymore
        if( st->pressed && !state )
            up = true;
        else if( !st->pressed && state )
            down = true;
    }

    int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
    int args = st->button | mods;

    if( down )      // Handle mouse button press
    {
        st->downTimestamp = wxGetLocalTimeMillis();
        st->dragOrigin = m_lastMousePos;
        st->downPosition = m_lastMousePos;
        st->dragMaxDelta = 0;
        st->pressed = true;
        evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
    }
    else if( up )   // Handle mouse button release
    {
        st->pressed = false;

        if( st->dragging )
        {
            wxLongLong t = wxGetLocalTimeMillis();

            // Determine if it was just a single click or beginning of dragging
            if( t - st->downTimestamp < DragTimeThreshold &&
                    st->dragMaxDelta < DragDistanceThreshold )
                isClick = true;
            else
                evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
        }
        else
            isClick = true;

        if( isClick )
            evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );

        st->dragging = false;
    }
    else if( dblClick )
    {
        evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
    }

    if( st->pressed && aMotion )
    {
        st->dragging = true;
        double dragPixelDistance =
            getView()->ToScreen( m_lastMousePos - st->dragOrigin, false ).EuclideanNorm();
        st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance );

        wxLongLong t = wxGetLocalTimeMillis();

        if( t - st->downTimestamp > DragTimeThreshold || st->dragMaxDelta > DragDistanceThreshold )
        {
            evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
            evt->setMouseDragOrigin( st->dragOrigin );
            evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
        }
    }

    if( evt )
    {
        evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
        m_toolMgr->ProcessEvent( *evt );

        return true;
    }

    return false;
}