diff options
Diffstat (limited to 'indra')
29 files changed, 1317 insertions, 907 deletions
| diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp index 4270c8b511..ff5459c1eb 100644 --- a/indra/llcommon/lleventapi.cpp +++ b/indra/llcommon/lleventapi.cpp @@ -34,6 +34,7 @@  // std headers  // external library headers  // other Linden headers +#include "llerror.h"  LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):      lbase(name, field), @@ -45,3 +46,32 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s  LLEventAPI::~LLEventAPI()  {  } + +LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey): +    mResp(seed), +    mReq(request), +    mKey(replyKey) +{} + +LLEventAPI::Response::~Response() +{ +    // When you instantiate a stack Response object, if the original +    // request requested a reply, send it when we leave this block, no +    // matter how. +    sendReply(mResp, mReq, mKey); +} + +void LLEventAPI::Response::warn(const std::string& warning) +{ +    LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL; +    mResp["warnings"].append(warning); +} + +void LLEventAPI::Response::error(const std::string& error) +{ +    // Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut +    // down altogether. +    LL_WARNS("LLEventAPI::Response") << error << LL_ENDL; + +    mResp["error"] = error; +} diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h index d75d521e8e..1a37d780b6 100644 --- a/indra/llcommon/lleventapi.h +++ b/indra/llcommon/lleventapi.h @@ -76,6 +76,89 @@ public:          LLEventDispatcher::add(name, desc, callable, required);      } +    /** +     * Instantiate a Response object in any LLEventAPI subclass method that +     * wants to guarantee a reply (if requested) will be sent on exit from the +     * method. The reply will be sent if request.has(@a replyKey), default +     * "reply". If specified, the value of request[replyKey] is the name of +     * the LLEventPump on which to send the reply. Conventionally you might +     * code something like: +     * +     * @code +     * void MyEventAPI::someMethod(const LLSD& request) +     * { +     *     // Send a reply event as long as request.has("reply") +     *     Response response(LLSD(), request); +     *     // ... +     *     // will be sent in reply event +     *     response["somekey"] = some_data; +     * } +     * @endcode +     */ +    class LL_COMMON_API Response +    { +    public: +        /** +         * Instantiating a Response object in an LLEventAPI subclass method +         * ensures that, if desired, a reply event will be sent. +         * +         * @a seed is the initial reply LLSD that will be further decorated before +         * being sent as the reply +         * +         * @a request is the incoming request LLSD; we particularly care about +         * [replyKey] and ["reqid"] +         * +         * @a replyKey [default "reply"] is the string name of the LLEventPump +         * on which the caller wants a reply. If <tt>(! +         * request.has(replyKey))</tt>, no reply will be sent. +         */ +        Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply"); +        ~Response(); + +        /** +         * @code +         * if (some condition) +         * { +         *     response.warn("warnings are logged and collected in [\"warnings\"]"); +         * } +         * @endcode +         */ +        void warn(const std::string& warning); +        /** +         * @code +         * if (some condition isn't met) +         * { +         *     // In a function returning void, you can validly 'return +         *     // expression' if the expression is itself of type void. But +         *     // returning is up to you; response.error() has no effect on +         *     // flow of control. +         *     return response.error("error message, logged and also sent as [\"error\"]"); +         * } +         * @endcode +         */ +        void error(const std::string& error); + +        /** +         * set other keys... +         * +         * @code +         * // set any attributes you want to be sent in the reply +         * response["info"] = some_value; +         * // ... +         * response["ok"] = went_well; +         * @endcode +         */ +        LLSD& operator[](const LLSD::String& key) { return mResp[key]; } +		 +		 /** +		 * set the response to the given data +		 */ +		void setResponse(LLSD const & response){ mResp = response; } + +        LLSD mResp, mReq; +        LLSD::String mKey; +    }; +  private:      std::string mDesc;  }; diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index ff03506e84..db1ea4792b 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -591,6 +591,17 @@ void LLReqID::stamp(LLSD& response) const  bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)  { +    // If the original request has no value for replyKey, it's pointless to +    // construct or send a reply event: on which LLEventPump should we send +    // it? Allow that to be optional: if the caller wants to require replyKey, +    // it can so specify when registering the operation method. +    if (! request.has(replyKey)) +    { +        return false; +    } + +    // Here the request definitely contains replyKey; reasonable to proceed. +      // Copy 'reply' to modify it.      LLSD newreply(reply);      // Get the ["reqid"] element from request diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 8c02ad8290..3b27a1639a 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -165,33 +165,60 @@ void LLMemory::logMemoryInfo(BOOL update)  	llinfos << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << llendl ;  	llinfos << "Current availabe physical memory(KB): " << sAvailPhysicalMemInKB << llendl ;  	llinfos << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << llendl ; + +	llinfos << "--- private pool information -- " << llendl ; +	llinfos << "Total reserved (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalReservedSize / 1024 << llendl ; +	llinfos << "Total allocated (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalAllocatedSize / 1024 << llendl ;  }  //return 0: everything is normal;  //return 1: the memory pool is low, but not in danger;  //return -1: the memory pool is in danger, is about to crash.  //static  -S32 LLMemory::isMemoryPoolLow() +bool LLMemory::isMemoryPoolLow()  {  	static const U32 LOW_MEMEOY_POOL_THRESHOLD_KB = 64 * 1024 ; //64 MB for emergency use +	const static U32 MAX_SIZE_CHECKED_MEMORY_BLOCK = 64 * 1024 * 1024 ; //64 MB +	static void* last_reserved_address = NULL ;  	if(!sEnableMemoryFailurePrevention)  	{ -		return 0 ; //no memory failure prevention. +		return false ; //no memory failure prevention.  	}  	if(sAvailPhysicalMemInKB < (LOW_MEMEOY_POOL_THRESHOLD_KB >> 2)) //out of physical memory  	{ -		return -1 ; +		return true ;  	}  	if(sAllocatedPageSizeInKB + (LOW_MEMEOY_POOL_THRESHOLD_KB >> 2) > sMaxHeapSizeInKB) //out of virtual address space.  	{ -		return -1 ; +		return true ;  	} -	return (S32)(sAvailPhysicalMemInKB < LOW_MEMEOY_POOL_THRESHOLD_KB ||  +	bool is_low = (S32)(sAvailPhysicalMemInKB < LOW_MEMEOY_POOL_THRESHOLD_KB ||   		sAllocatedPageSizeInKB + LOW_MEMEOY_POOL_THRESHOLD_KB > sMaxHeapSizeInKB) ; + +	//check the virtual address space fragmentation +	if(!is_low) +	{ +		if(!last_reserved_address) +		{ +			last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; +		} +		else +		{ +			last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; +			if(!last_reserved_address) //failed, try once more +			{ +				last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; +			} +		} + +		is_low = !last_reserved_address ; //allocation failed +	} + +	return is_low ;  }  //static  @@ -1289,15 +1316,13 @@ U16 LLPrivateMemoryPool::LLMemoryChunk::getPageLevel(U32 size)  //--------------------------------------------------------------------  const U32 CHUNK_SIZE = 4 << 20 ; //4 MB  const U32 LARGE_CHUNK_SIZE = 4 * CHUNK_SIZE ; //16 MB -LLPrivateMemoryPool::LLPrivateMemoryPool(S32 type) : +LLPrivateMemoryPool::LLPrivateMemoryPool(S32 type, U32 max_pool_size) :  	mMutexp(NULL),	  	mReservedPoolSize(0),  	mHashFactor(1), -	mType(type) +	mType(type), +	mMaxPoolSize(max_pool_size)  { -	const U32 MAX_POOL_SIZE = 256 * 1024 * 1024 ; //256 MB - -	mMaxPoolSize = MAX_POOL_SIZE ;  	if(type == STATIC_THREADED || type == VOLATILE_THREADED)  	{  		mMutexp = new LLMutex ; @@ -1362,16 +1387,31 @@ char* LLPrivateMemoryPool::allocate(U32 size)  				chunk = chunk->mNext ;  			}  		} - -		chunk = addChunk(chunk_idx) ; -		if(chunk) +		else  		{ -			p = chunk->allocate(size) ; +			chunk = addChunk(chunk_idx) ; +			if(chunk) +			{ +				p = chunk->allocate(size) ; +			}  		}  	}  	unlock() ; +	if(!p) //to get memory from the private pool failed, try the heap directly +	{ +		static bool to_log = true ; +		 +		if(to_log) +		{ +			llwarns << "The memory pool overflows, now using heap directly!" << llendl ; +			to_log = false ; +		} + +		return (char*)malloc(size) ; +	} +  	return p ;  } @@ -1472,7 +1512,7 @@ void  LLPrivateMemoryPool::destroyPool()  	unlock() ;  } -void  LLPrivateMemoryPool::checkSize(U32 asked_size) +bool LLPrivateMemoryPool::checkSize(U32 asked_size)  {  	if(mReservedPoolSize + asked_size > mMaxPoolSize)  	{ @@ -1480,8 +1520,12 @@ void  LLPrivateMemoryPool::checkSize(U32 asked_size)  		llinfos << "Total reserved size: " << mReservedPoolSize + asked_size << llendl ;  		llinfos << "Total_allocated Size: " << getTotalAllocatedSize() << llendl ; -		llerrs << "The pool is overflowing..." << llendl ; +		//llerrs << "The pool is overflowing..." << llendl ; + +		return false ;  	} + +	return true ;  }  LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_index) @@ -1501,7 +1545,11 @@ LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_inde  			MAX_SLOT_SIZES[chunk_index], MIN_BLOCK_SIZES[chunk_index], MAX_BLOCK_SIZES[chunk_index]) ;  	} -	checkSize(preferred_size + overhead) ; +	if(!checkSize(preferred_size + overhead)) +	{ +		return NULL ; +	} +  	mReservedPoolSize += preferred_size + overhead ;  	char* buffer = (char*)malloc(preferred_size + overhead) ; @@ -1593,7 +1641,7 @@ LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* a  void LLPrivateMemoryPool::addToHashTable(LLMemoryChunk* chunk)   { -	static const U16 HASH_FACTORS[] = {41, 83, 193, 317, 419, 523, 0xFFFF};  +	static const U16 HASH_FACTORS[] = {41, 83, 193, 317, 419, 523, 719, 997, 1523, 0xFFFF};   	U16 i ;  	if(mChunkHashList.empty()) @@ -1774,7 +1822,7 @@ void LLPrivateMemoryPool::LLChunkHashElement::remove(LLPrivateMemoryPool::LLMemo  //--------------------------------------------------------------------  LLPrivateMemoryPoolManager* LLPrivateMemoryPoolManager::sInstance = NULL ; -LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled)  +LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size)   {  	mPoolList.resize(LLPrivateMemoryPool::MAX_TYPES) ; @@ -1784,6 +1832,9 @@ LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled)  	}  	mPrivatePoolEnabled = enabled ; + +	const U32 MAX_POOL_SIZE = 256 * 1024 * 1024 ; //256 MB +	mMaxPrivatePoolSize = llmax(max_pool_size, MAX_POOL_SIZE) ;  }  LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()  @@ -1826,11 +1877,11 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()  }  //static  -void LLPrivateMemoryPoolManager::initClass(BOOL enabled)  +void LLPrivateMemoryPoolManager::initClass(BOOL enabled, U32 max_pool_size)   {  	llassert_always(!sInstance) ; -	sInstance = new LLPrivateMemoryPoolManager(enabled) ; +	sInstance = new LLPrivateMemoryPoolManager(enabled, max_pool_size) ;  }  //static  @@ -1862,7 +1913,7 @@ LLPrivateMemoryPool* LLPrivateMemoryPoolManager::newPool(S32 type)  	if(!mPoolList[type])  	{ -		mPoolList[type] = new LLPrivateMemoryPool(type) ; +		mPoolList[type] = new LLPrivateMemoryPool(type, mMaxPrivatePoolSize) ;  	}  	return mPoolList[type] ; diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index db753f0d8b..6967edd7e7 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -122,7 +122,7 @@ public:  	static void initMaxHeapSizeGB(F32 max_heap_size_gb, BOOL prevent_heap_failure);  	static void updateMemoryInfo() ;  	static void logMemoryInfo(BOOL update = FALSE); -	static S32  isMemoryPoolLow(); +	static bool isMemoryPoolLow();  	static U32 getAvailableMemKB() ;  	static U32 getMaxMemKB() ; @@ -303,7 +303,7 @@ public:  	} ;  private: -	LLPrivateMemoryPool(S32 type) ; +	LLPrivateMemoryPool(S32 type, U32 max_pool_size) ;  	~LLPrivateMemoryPool() ;  	char *allocate(U32 size) ; @@ -320,7 +320,7 @@ private:  	void unlock() ;	  	S32 getChunkIndex(U32 size) ;  	LLMemoryChunk*  addChunk(S32 chunk_index) ; -	void checkSize(U32 asked_size) ; +	bool checkSize(U32 asked_size) ;  	void removeChunk(LLMemoryChunk* chunk) ;  	U16  findHashKey(const char* addr);  	void addToHashTable(LLMemoryChunk* chunk) ; @@ -383,12 +383,12 @@ private:  class LL_COMMON_API LLPrivateMemoryPoolManager  {  private: -	LLPrivateMemoryPoolManager(BOOL enabled) ; +	LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size) ;  	~LLPrivateMemoryPoolManager() ;  public:	  	static LLPrivateMemoryPoolManager* getInstance() ; -	static void initClass(BOOL enabled) ; +	static void initClass(BOOL enabled, U32 pool_size) ;  	static void destroyClass() ;  	LLPrivateMemoryPool* newPool(S32 type) ; @@ -398,6 +398,7 @@ private:  	static LLPrivateMemoryPoolManager* sInstance ;  	std::vector<LLPrivateMemoryPool*> mPoolList ;  	BOOL mPrivatePoolEnabled; +	U32  mMaxPrivatePoolSize;  public:  	//debug and statistics info. diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 3377465bb6..e4ad7f4f54 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -29,7 +29,7 @@  const S32 LL_VERSION_MAJOR = 3;  const S32 LL_VERSION_MINOR = 1; -const S32 LL_VERSION_PATCH = 0; +const S32 LL_VERSION_PATCH = 1;  const S32 LL_VERSION_BUILD = 0;  const char * const LL_CHANNEL = "Second Life Developer"; diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index b3b2f4ae56..0ab883cb70 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -112,6 +112,7 @@ set(llui_SOURCE_FILES      llurlmatch.cpp      llurlregistry.cpp      llviewborder.cpp +    llviewinject.cpp      llviewmodel.cpp      llview.cpp      llviewquery.cpp @@ -216,6 +217,7 @@ set(llui_HEADER_FILES      llurlmatch.h      llurlregistry.h      llviewborder.h +    llviewinject.h      llviewmodel.h      llview.h      llviewquery.h diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index d58df5801b..9b9e2ddb55 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -1045,3 +1045,9 @@ boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal  	if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t();  	return mDoubleClickSignal->connect(cb);   } + +void LLUICtrl::addInfo(LLSD & info) +{ +	LLView::addInfo(info); +	info["value"] = getValue(); +} diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 09bed9b958..8a8b589e9c 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -301,7 +301,9 @@ protected:  	static F32 sActiveControlTransparency;  	static F32 sInactiveControlTransparency; - +	 +	virtual void addInfo(LLSD & info); +	  private:  	BOOL			mIsChrome; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 60452b9ae4..3ddfb090c8 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -31,7 +31,10 @@  #include "llview.h"  #include <cassert> +#include <sstream>  #include <boost/tokenizer.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp>  #include "llrender.h"  #include "llevent.h" @@ -44,6 +47,7 @@  #include "v3color.h"  #include "lluictrlfactory.h"  #include "lltooltip.h" +#include "llsdutil.h"  // for ui edit hack  #include "llbutton.h" @@ -66,6 +70,8 @@ S32		LLView::sLastLeftXML = S32_MIN;  S32		LLView::sLastBottomXML = S32_MIN;  std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack; +LLView::DrilldownFunc LLView::sDrilldown = +	boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);  //#if LL_DEBUG  BOOL LLView::sIsDrawing = FALSE; @@ -346,13 +352,11 @@ void LLView::removeChild(LLView* child)  LLView::ctrl_list_t LLView::getCtrlList() const  {  	ctrl_list_t controls; -	for(child_list_const_iter_t iter = mChildList.begin(); -		iter != mChildList.end(); -		iter++) +	BOOST_FOREACH(LLView* viewp, mChildList)  	{ -		if((*iter)->isCtrl()) +		if(viewp->isCtrl())  		{ -			controls.push_back(static_cast<LLUICtrl*>(*iter)); +			controls.push_back(static_cast<LLUICtrl*>(viewp));  		}  	}  	return controls; @@ -428,6 +432,36 @@ BOOL LLView::isInEnabledChain() const  	return enabled;  } +static void buildPathname(std::ostream& out, const LLView* view) +{ +	if (! (view && view->getParent())) +	{ +		return; // Don't include root in the path. +	} + +	buildPathname(out, view->getParent()); + +	// Build pathname into ostream on the way back from recursion. +	out << '/' << view->getName(); +} + +std::string LLView::getPathname() const +{ +	std::ostringstream out; +	buildPathname(out, this); +	return out.str(); +} + +//static +std::string LLView::getPathname(const LLView* view) +{ +    if (! view) +    { +        return "NULL"; +    } +    return view->getPathname(); +} +  // virtual  BOOL LLView::canFocusChildren() const  { @@ -574,9 +608,8 @@ void LLView::deleteAllChildren()  void LLView::setAllChildrenEnabled(BOOL b)  { -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* viewp, mChildList)  	{ -		LLView* viewp = *child_it;  		viewp->setEnabled(b);  	}  } @@ -602,9 +635,8 @@ void LLView::setVisible(BOOL visible)  // virtual  void LLView::handleVisibilityChange ( BOOL new_visibility )  { -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* viewp, mChildList)  	{ -		LLView* viewp = *child_it;  		// only views that are themselves visible will have their overall visibility affected by their ancestors  		if (viewp->getVisible())  		{ @@ -646,56 +678,178 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask)  	//llinfos << "Mouse left " << getName() << llendl;  } +bool LLView::visibleAndContains(S32 local_x, S32 local_y) +{ +	return sDrilldown(this, local_x, local_y) +		&& getVisible(); +} + +bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y) +{ +	return visibleAndContains(local_x, local_y) +		&& getEnabled(); +} + +void LLView::logMouseEvent() +{ +	if (sDebugMouseHandling) +	{ +		sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage; +	} +} + +template <typename METHOD, typename CHARTYPE> +LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method, +										CHARTYPE c, MASK mask) +{ +	if ( getVisible() && getEnabled() ) +	{ +		BOOST_FOREACH(LLView* viewp, mChildList) +		{ +			if ((viewp->*method)(c, mask, TRUE)) +			{ +				if (LLView::sDebugKeys) +				{ +					llinfos << desc << " handled by " << viewp->getName() << llendl; +				} +				return viewp; +			} +		} +	} +    return NULL; +} + +// XDATA might be MASK, or S32 clicks +template <typename METHOD, typename XDATA> +LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra) +{ +	BOOST_FOREACH(LLView* viewp, mChildList) +	{ +		S32 local_x = x - viewp->getRect().mLeft; +		S32 local_y = y - viewp->getRect().mBottom; + +		if (!viewp->visibleEnabledAndContains(local_x, local_y)) +		{ +			continue; +		} + +		if ((viewp->*method)( local_x, local_y, extra ) +			|| viewp->blockMouseEvent( local_x, local_y )) +		{ +			viewp->logMouseEvent(); +			return viewp; +		} +	} +	return NULL; +}  LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* viewp, mChildList)  	{ -		LLView* viewp = *child_it;  		S32 local_x = x - viewp->getRect().mLeft;  		S32 local_y = y - viewp->getRect().mBottom; -		if(!viewp->pointInView(local_x, local_y)  -			|| !viewp->getVisible()) +		// Differs from childrenHandleMouseEvent() in that we want to offer +		// tooltips even for disabled widgets. +		if(!viewp->visibleAndContains(local_x, local_y))  		{  			continue;  		} -		if (viewp->handleToolTip(local_x, local_y, mask) ) +		if (viewp->handleToolTip(local_x, local_y, mask)  +			|| viewp->blockMouseEvent(local_x, local_y))  		{ -			if (sDebugMouseHandling) -			{ -				sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -			} +			viewp->logMouseEvent(); +			return viewp; +		} +	} +	return NULL; +} -			handled_view = viewp; -			break; +LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, +									   BOOL drop, +									   EDragAndDropType cargo_type, +									   void* cargo_data, +									   EAcceptance* accept, +									   std::string& tooltip_msg) +{ +	// default to not accepting drag and drop, will be overridden by handler +	*accept = ACCEPT_NO; + +	BOOST_FOREACH(LLView* viewp, mChildList) +	{ +		S32 local_x = x - viewp->getRect().mLeft; +		S32 local_y = y - viewp->getRect().mBottom; +		if( !viewp->visibleEnabledAndContains(local_x, local_y)) +		{ +			continue;  		} -		if (viewp->blockMouseEvent(local_x, local_y)) +		// Differs from childrenHandleMouseEvent() simply in that this virtual +		// method call diverges pretty radically from the usual (x, y, int). +		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop, +									 cargo_type, +									 cargo_data, +									 accept, +									 tooltip_msg) +			|| viewp->blockMouseEvent(local_x, local_y))  		{ -			handled_view = viewp; -			break; +			return viewp;  		}  	} -	return handled_view; +	return NULL;  } +LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) +{ +	BOOST_FOREACH(LLView* viewp, mChildList) +	{ +		S32 local_x = x - viewp->getRect().mLeft; +		S32 local_y = y - viewp->getRect().mBottom; +		if(!viewp->visibleEnabledAndContains(local_x, local_y)) +		{ +			continue; +		} + +		// This call differentiates this method from childrenHandleMouseEvent(). +		LLUI::sWindow->setCursor(viewp->getHoverCursor()); -LLView*	LLView::childFromPoint(S32 x, S32 y) +		if (viewp->handleHover(local_x, local_y, mask) +			|| viewp->blockMouseEvent(local_x, local_y)) +		{ +			viewp->logMouseEvent(); +			return viewp; +		} +	} +	return NULL; +} + +LLView*	LLView::childFromPoint(S32 x, S32 y, bool recur)  { -	if (!getVisible()  ) +	if (!getVisible())  		return false; -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + +	BOOST_FOREACH(LLView* viewp, mChildList)  	{ -		LLView* viewp = *child_it;  		S32 local_x = x - viewp->getRect().mLeft;  		S32 local_y = y - viewp->getRect().mBottom; -		if (!viewp->pointInView(local_x, local_y)  -			|| !viewp->getVisible() ) +		if (!viewp->visibleAndContains(local_x, local_y))  		{  			continue;  		} +		// Here we've found the first (frontmost) visible child at this level +		// containing the specified point. Is the caller asking us to drill +		// down and return the innermost leaf child at this point, or just the +		// top-level child? +		if (recur) +		{ +			LLView* leaf(viewp->childFromPoint(local_x, local_y, recur)); +			// Maybe viewp is already a leaf LLView, or maybe it has children +			// but this particular (x, y) point falls between them. If the +			// recursive call returns non-NULL, great, use that; else just use +			// viewp. +			return leaf? leaf : viewp; +		}  		return viewp;  	} @@ -815,45 +969,6 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,  	return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;  } -LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, -									   BOOL drop, -									   EDragAndDropType cargo_type, -									   void* cargo_data, -									   EAcceptance* accept, -									   std::string& tooltip_msg) -{ -	LLView* handled_view = NULL; -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -	{ -		LLView* viewp = *child_it; -		S32 local_x = x - viewp->getRect().mLeft; -		S32 local_y = y - viewp->getRect().mBottom; -		if( !viewp->pointInView(local_x, local_y) || -			!viewp->getVisible() || -			!viewp->getEnabled()) -		{ -			continue; -		} -		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop, -									 cargo_type, -									 cargo_data, -									 accept, -									 tooltip_msg)) -		{ -			handled_view = viewp; -			break; -		} - -		if (viewp->blockMouseEvent(x, y)) -		{ -			*accept = ACCEPT_NO; -			handled_view = viewp; -			break; -		} -	} -	return handled_view; -} -  void LLView::onMouseCaptureLost()  {  } @@ -903,391 +1018,57 @@ BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)  	return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;  } -  LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)  { -	LLView* handled_view = NULL; -	if (getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if (!viewp->pointInView(local_x, local_y)  -				|| !viewp->getVisible() -				|| !viewp->getEnabled()) -			{ -				continue; -			} - -			if (viewp->handleScrollWheel( local_x, local_y, clicks )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} - -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; -} - -LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) -{ -	LLView* handled_view = NULL; -	if (getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if(!viewp->pointInView(local_x, local_y)  -				|| !viewp->getVisible()  -				|| !viewp->getEnabled()) -			{ -				continue; -			} - -			if (viewp->handleHover(local_x, local_y, mask) ) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} - -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				LLUI::sWindow->setCursor(viewp->getHoverCursor()); - -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks);  }  // Called during downward traversal  LLView* LLView::childrenHandleKey(KEY key, MASK mask)  { -	LLView* handled_view = NULL; - -	if ( getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			if (viewp->handleKey(key, mask, TRUE)) -			{ -				if (LLView::sDebugKeys) -				{ -					llinfos << "Key handled by " << viewp->getName() << llendl; -				} -				handled_view = viewp; -				break; -			} -		} -	} - -	return handled_view; +	return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);  }  // Called during downward traversal  LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)  { -	LLView* handled_view = NULL; - -	if ( getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			if (viewp->handleUnicodeChar(uni_char, TRUE)) -			{ -				if (LLView::sDebugKeys) -				{ -					llinfos << "Unicode character handled by " << viewp->getName() << llendl; -				} -				handled_view = viewp; -				break; -			} -		} -	} - -	return handled_view; +	return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask, +								   uni_char, MASK_NONE);  }  LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; - -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -	{ -		LLView* viewp = *child_it; -		S32 local_x = x - viewp->getRect().mLeft; -		S32 local_y = y - viewp->getRect().mBottom; - -		if (!viewp->pointInView(local_x, local_y)  -			|| !viewp->getVisible()  -			|| !viewp->getEnabled()) -		{ -			continue; -		} - -		if(viewp->handleMouseDown( local_x, local_y, mask )) -		{ -			if (sDebugMouseHandling) -			{ -				sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -			} -			handled_view = viewp; -			break; -		} - -		if(viewp->blockMouseEvent(local_x, local_y)) -		{ -			handled_view = viewp; -			break; -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);  }  LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; - -	if (getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; - -			if (!viewp->pointInView(local_x, local_y) -				|| !viewp->getVisible()  -				|| !viewp->getEnabled()) -			{ -				continue; -			} - -			if (viewp->handleRightMouseDown( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} - -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);  }  LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; - -	if (getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if (!viewp->pointInView(local_x, local_y) -				|| !viewp->getVisible()  -				|| !viewp->getEnabled()) -			{ -				continue; -			} - -			if(viewp->handleMiddleMouseDown( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);  }  LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; - -	if (getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; - -			if (!viewp->pointInView(local_x, local_y)  -				|| !viewp->getVisible()  -				|| !viewp->getEnabled()) -			{ -				continue; -			} - -			if (viewp->handleDoubleClick( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);  }  LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; -	if( getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if (!viewp->pointInView(local_x, local_y) -				|| !viewp->getVisible() -				|| !viewp->getEnabled()) -			{ -				continue; -			} -			 -			if (viewp->handleMouseUp( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);  }  LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; -	if( getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if (!viewp->pointInView(local_x, local_y)  -				|| !viewp->getVisible()  -				|| !viewp->getEnabled() ) -			{ -				continue; -			} - -			if(viewp->handleRightMouseUp( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} -				handled_view = viewp; -				break; -			} - -			if(viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);  }  LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)  { -	LLView* handled_view = NULL; -	if( getVisible() && getEnabled() ) -	{ -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) -		{ -			LLView* viewp = *child_it; -			S32 local_x = x - viewp->getRect().mLeft; -			S32 local_y = y - viewp->getRect().mBottom; -			if (!viewp->pointInView(local_x, local_y)  -				|| !viewp->getVisible()  -				|| !viewp->getEnabled()) -			{ -				continue; -			} -				 -			if(viewp->handleMiddleMouseUp( local_x, local_y, mask )) -			{ -				if (sDebugMouseHandling) -				{ -					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; -				} -				handled_view = viewp; -				break; -			} - -			if (viewp->blockMouseEvent(local_x, local_y)) -			{ -				handled_view = viewp; -				break; -			} -		} -	} -	return handled_view; +	return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);  }  void LLView::draw() @@ -1460,9 +1241,8 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)  		mRect.mTop = getRect().mBottom + height;  		// move child views according to reshape flags -		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +		BOOST_FOREACH(LLView* viewp, mChildList)  		{ -			LLView* viewp = *child_it;  			LLRect child_rect( viewp->mRect );  			if (viewp->followsRight() && viewp->followsLeft()) @@ -1525,10 +1305,8 @@ LLRect LLView::calcBoundingRect()  {  	LLRect local_bounding_rect = LLRect::null; -	child_list_const_iter_t child_it; -	for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* childp, mChildList)  	{ -		LLView* childp = *child_it;  		// ignore invisible and "top" children when calculating bounding rect  		// such as combobox popups  		if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())  @@ -1693,11 +1471,9 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const  	//richard: should we allow empty names?  	//if(name.empty())  	//	return NULL; -	child_list_const_iter_t child_it;  	// Look for direct children *first* -	for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* childp, mChildList)  	{ -		LLView* childp = *child_it;  		llassert(childp);  		if (childp->getName() == name)  		{ @@ -1707,9 +1483,8 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const  	if (recurse)  	{  		// Look inside each child as well. -		for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +		BOOST_FOREACH(LLView* childp, mChildList)  		{ -			LLView* childp = *child_it;  			llassert(childp);  			LLView* viewp = childp->findChildView(name, recurse);  			if ( viewp ) @@ -2850,9 +2625,9 @@ S32	LLView::notifyParent(const LLSD& info)  bool	LLView::notifyChildren(const LLSD& info)  {  	bool ret = false; -	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +	BOOST_FOREACH(LLView* childp, mChildList)  	{ -		ret |= (*child_it)->notifyChildren(info); +		ret = ret || childp->notifyChildren(info);  	}  	return ret;  } @@ -2872,3 +2647,24 @@ const LLViewDrawContext& LLViewDrawContext::getCurrentContext()  	return *sDrawContextStack.back();  } + +LLSD LLView::getInfo(void) +{ +	LLSD info; +	addInfo(info); +	return info; +} + +void LLView::addInfo(LLSD & info) +{ +	info["path"] = getPathname(); +	info["class"] = typeid(*this).name(); +	info["visible"] = getVisible(); +	info["visible_chain"] = isInVisibleChain(); +	info["enabled"] = getEnabled(); +	info["enabled_chain"] = isInEnabledChain(); +	info["available"] = isAvailable(); +	LLRect rect(calcScreenRect()); +	info["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop) +				("right", rect.mRight)("bottom", rect.mBottom); +} diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 594a5eec6b..fe15307a5d 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -50,6 +50,8 @@  #include "llfocusmgr.h"  #include <list> +#include <boost/function.hpp> +#include <boost/noncopyable.hpp>  class LLSD; @@ -437,12 +439,15 @@ public:  	/*virtual*/ void	screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;  	/*virtual*/ void	localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; -	virtual		LLView*	childFromPoint(S32 x, S32 y); +	virtual		LLView*	childFromPoint(S32 x, S32 y, bool recur=false);  	// view-specific handlers   	virtual void	onMouseEnter(S32 x, S32 y, MASK mask);  	virtual void	onMouseLeave(S32 x, S32 y, MASK mask); +	std::string getPathname() const; +	// static method handles NULL pointer too +	static std::string getPathname(const LLView*);  	template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const  	{ @@ -511,11 +516,17 @@ public:  	virtual S32	notify(const LLSD& info) { return 0;};  	static const LLViewDrawContext& getDrawContext(); +	 +	// Returns useful information about this ui widget. +	LLSD getInfo(void);  protected:  	void			drawDebugRect();  	void			drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE);  	void			drawChildren(); +	bool			visibleAndContains(S32 local_x, S32 local_Y); +	bool			visibleEnabledAndContains(S32 local_x, S32 local_y); +	void			logMouseEvent();  	LLView*	childrenHandleKey(KEY key, MASK mask);  	LLView* childrenHandleUnicodeChar(llwchar uni_char); @@ -538,9 +549,24 @@ protected:  	LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);  	ECursorType mHoverCursor; -	 + +	virtual void addInfo(LLSD & info);  private: +	template <typename METHOD, typename XDATA> +	LLView* childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra); + +	template <typename METHOD, typename CHARTYPE> +	LLView* childrenHandleCharEvent(const std::string& desc, const METHOD& method, +									CHARTYPE c, MASK mask); + +	// adapter to blur distinction between handleKey() and handleUnicodeChar() +	// for childrenHandleCharEvent() +	BOOL	handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, BOOL from_parent) +	{ +		return handleUnicodeChar(uni_char, from_parent); +	} +  	LLView*		mParentView;  	child_list_t mChildList; @@ -582,7 +608,35 @@ private:  	LLView& getDefaultWidgetContainer() const; +	// This allows special mouse-event targeting logic for testing. +	typedef boost::function<bool(const LLView*, S32 x, S32 y)> DrilldownFunc; +	static DrilldownFunc sDrilldown; +  public: +	// This is the only public accessor to alter sDrilldown. This is not +	// an accident. The intended usage pattern is like: +	// { +	//     LLView::TemporaryDrilldownFunc scoped_func(myfunctor); +	//     // ... test with myfunctor ... +	// } // exiting block restores original LLView::sDrilldown +	class TemporaryDrilldownFunc: public boost::noncopyable +	{ +	public: +		TemporaryDrilldownFunc(const DrilldownFunc& func): +			mOldDrilldown(sDrilldown) +		{ +			sDrilldown = func; +		} + +		~TemporaryDrilldownFunc() +		{ +			sDrilldown = mOldDrilldown; +		} + +	private: +		DrilldownFunc mOldDrilldown; +	}; +  	// Depth in view hierarchy during rendering  	static S32	sDepth; diff --git a/indra/llui/llviewinject.cpp b/indra/llui/llviewinject.cpp new file mode 100644 index 0000000000..46c5839f8e --- /dev/null +++ b/indra/llui/llviewinject.cpp @@ -0,0 +1,49 @@ +/** + * @file   llviewinject.cpp + * @author Nat Goodspeed + * @date   2011-08-16 + * @brief  Implementation for llviewinject. + *  + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Copyright (c) 2011, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llviewinject.h" +// STL headers +// std headers +// external library headers +// other Linden headers + +llview::TargetEvent::TargetEvent(LLView* view) +{ +    // Walk up the view tree from target LLView to the root (NULL). If +    // passed NULL, iterate 0 times. +    for (; view; view = view->getParent()) +    { +        // At each level, operator() is going to ask: for a particular parent +        // LLView*, which of its children should I select? So for this view's +        // parent, select this view. +        mChildMap[view->getParent()] = view; +    } +} + +bool llview::TargetEvent::operator()(const LLView* view, S32 /*x*/, S32 /*y*/) const +{ +    // We are being called to decide whether to direct an incoming mouse event +    // to this child view. (Normal LLView processing is to check whether the +    // incoming (x, y) is within the view.) Look up the parent to decide +    // whether, for that parent, this is the previously-selected child. +    ChildMap::const_iterator found(mChildMap.find(view->getParent())); +    // If we're looking at a child whose parent isn't even in the map, never +    // mind. +    if (found == mChildMap.end()) +    { +        return false; +    } +    // So, is this the predestined child for this parent? +    return (view == found->second); +} diff --git a/indra/llui/llviewinject.h b/indra/llui/llviewinject.h new file mode 100644 index 0000000000..0de3d155c4 --- /dev/null +++ b/indra/llui/llviewinject.h @@ -0,0 +1,56 @@ +/** + * @file   llviewinject.h + * @author Nat Goodspeed + * @date   2011-08-16 + * @brief  Supplemental LLView functionality used for simulating UI events. + *  + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Copyright (c) 2011, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLVIEWINJECT_H) +#define LL_LLVIEWINJECT_H + +#include "llview.h" +#include <map> + +namespace llview +{ + +    /** +     * TargetEvent is a callable with state, specifically intended for use as +     * an LLView::TemporaryDrilldownFunc. Instantiate it with the desired +     * target LLView*; pass it to a TemporaryDrilldownFunc instance; +     * TargetEvent::operator() will then attempt to direct subsequent mouse +     * events to the desired target LLView*. (This is an "attempt" because +     * LLView will still balk unless the target LLView and every parent are +     * visible and enabled.) +     */ +    class TargetEvent +    { +    public: +        /** +         * Construct TargetEvent with the desired target LLView*. (See +         * LLUI::resolvePath() to obtain an LLView* given a string pathname.) +         * This sets up for operator(). +         */ +        TargetEvent(LLView* view); + +        /** +         * This signature must match LLView::DrilldownFunc. When you install +         * this TargetEvent instance using LLView::TemporaryDrilldownFunc, +         * LLView will call this method to decide whether to propagate an +         * incoming mouse event to the passed child LLView*. +         */ +        bool operator()(const LLView*, S32 x, S32 y) const; + +    private: +        // For a given parent LLView, identify which child to select. +        typedef std::map<LLView*, LLView*> ChildMap; +        ChildMap mChildMap; +    }; + +} // llview namespace + +#endif /* ! defined(LL_LLVIEWINJECT_H) */ diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 3d89867bc1..341bddfffd 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -38,7 +38,6 @@ set(llwindow_SOURCE_FILES      llkeyboardheadless.cpp      llwindowheadless.cpp      llwindowcallbacks.cpp -    llwindowlistener.cpp      )  set(llwindow_HEADER_FILES @@ -48,7 +47,6 @@ set(llwindow_HEADER_FILES      llkeyboardheadless.h      llwindowheadless.h      llwindowcallbacks.h -    llwindowlistener.h      )  set(viewer_SOURCE_FILES diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 71a5df910d..dc3a1099b1 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -41,8 +41,6 @@  #include "llkeyboard.h"  #include "linked_lists.h"  #include "llwindowcallbacks.h" -#include "llwindowlistener.h" -#include <boost/lambda/core.hpp>  // @@ -118,17 +116,10 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags)  	  mFlags(flags),  	  mHighSurrogate(0)  { -	// gKeyboard is still NULL, so it doesn't do LLWindowListener any good to -	// pass its value right now. Instead, pass it a nullary function that -	// will, when we later need it, return the value of gKeyboard. -	// boost::lambda::var() constructs such a functor on the fly. -	mListener = new LLWindowListener(callbacks, boost::lambda::var(gKeyboard));  }  LLWindow::~LLWindow()  { -	delete mListener; -	mListener = NULL;  }  //virtual diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 6bdc01ae88..e8a86a1880 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -36,7 +36,6 @@  class LLSplashScreen;  class LLPreeditor;  class LLWindowCallbacks; -class LLWindowListener;  // Refer to llwindow_test in test/common/llwindow for usage example @@ -189,7 +188,6 @@ protected:  	BOOL		mHideCursorPermanent;  	U32			mFlags;  	U16			mHighSurrogate; -	LLWindowListener* mListener;   	// Handle a UTF-16 encoding unit received from keyboard.   	// Converting the series of UTF-16 encoding units to UTF-32 data, diff --git a/indra/llwindow/llwindowlistener.cpp b/indra/llwindow/llwindowlistener.cpp deleted file mode 100644 index 91b99d83c6..0000000000 --- a/indra/llwindow/llwindowlistener.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/**  - * @file llwindowlistener.cpp - * @brief EventAPI interface for injecting input into LLWindow - * - * $LicenseInfo:firstyear=2001&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$ - */ - -#include "linden_common.h" - -#include "llwindowlistener.h" - -#include "llcoord.h" -#include "llkeyboard.h" -#include "llwindowcallbacks.h" -#include <map> - -LLWindowListener::LLWindowListener(LLWindowCallbacks *window, const KeyboardGetter& kbgetter) -	: LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"), -	  mWindow(window), -	  mKbGetter(kbgetter) -{ -	std::string keySomething = -		"Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified "; -	std::string keyExplain = -		"(integer keycode values, or keysym \"XXXX\" from any KEY_XXXX, in\n" -		"http://hg.secondlife.com/viewer-development/src/tip/indra/llcommon/indra_constants.h )"; -	std::string mask = -		"Specify optional [\"mask\"] as an array containing any of \"CONTROL\", \"ALT\",\n" -		"\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n" -		"to form the mask used with the event."; - -	std::string mouseSomething = -		"Given [\"button\"], [\"x\"] and [\"y\"], inject the given mouse "; -	std::string mouseExplain = -		"(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")"; - -	add("keyDown", -		keySomething + "keypress event.\n" + keyExplain + '\n' + mask, -		&LLWindowListener::keyDown); -	add("keyUp", -		keySomething + "key release event.\n" + keyExplain + '\n' + mask, -		&LLWindowListener::keyUp); -	add("mouseDown", -		mouseSomething + "click event.\n" + mouseExplain + '\n' + mask, -		&LLWindowListener::mouseDown); -	add("mouseUp", -		mouseSomething + "release event.\n" + mouseExplain + '\n' + mask, -		&LLWindowListener::mouseUp); -	add("mouseMove", -		std::string("Given [\"x\"] and [\"y\"], inject the given mouse movement event.\n") + -		mask, -		&LLWindowListener::mouseMove); -	add("mouseScroll", -		"Given an integer number of [\"clicks\"], inject the given mouse scroll event.\n" -		"(positive clicks moves downward through typical content)", -		&LLWindowListener::mouseScroll); -} - -template <typename MAPPED> -class StringLookup -{ -private: -	std::string mDesc; -	typedef std::map<std::string, MAPPED> Map; -	Map mMap; - -public: -	StringLookup(const std::string& desc): mDesc(desc) {} - -	MAPPED lookup(const typename Map::key_type& key) const -	{ -		typename Map::const_iterator found = mMap.find(key); -		if (found == mMap.end()) -		{ -			LL_WARNS("LLWindowListener") << "Unknown " << mDesc << " '" << key << "'" << LL_ENDL; -			return MAPPED(); -		} -		return found->second; -	} - -protected: -	void add(const typename Map::key_type& key, const typename Map::mapped_type& value) -	{ -		mMap.insert(typename Map::value_type(key, value)); -	} -}; - -// for WhichKeysym. KeyProxy is like the typedef KEY, except that KeyProxy() -// (default-constructed) is guaranteed to have the value KEY_NONE. -class KeyProxy -{ -public: -	KeyProxy(KEY k): mKey(k) {} -	KeyProxy(): mKey(KEY_NONE) {} -	operator KEY() const { return mKey; } - -private: -	KEY mKey; -}; - -struct WhichKeysym: public StringLookup<KeyProxy> -{ -	WhichKeysym(): StringLookup<KeyProxy>("keysym") -	{ -		add("RETURN",		KEY_RETURN); -		add("LEFT",			KEY_LEFT); -		add("RIGHT",		KEY_RIGHT); -		add("UP",			KEY_UP); -		add("DOWN",			KEY_DOWN); -		add("ESCAPE",		KEY_ESCAPE); -		add("BACKSPACE",	KEY_BACKSPACE); -		add("DELETE",		KEY_DELETE); -		add("SHIFT",		KEY_SHIFT); -		add("CONTROL",		KEY_CONTROL); -		add("ALT",			KEY_ALT); -		add("HOME",			KEY_HOME); -		add("END",			KEY_END); -		add("PAGE_UP",		KEY_PAGE_UP); -		add("PAGE_DOWN",	KEY_PAGE_DOWN); -		add("HYPHEN",		KEY_HYPHEN); -		add("EQUALS",		KEY_EQUALS); -		add("INSERT",		KEY_INSERT); -		add("CAPSLOCK",		KEY_CAPSLOCK); -		add("TAB",			KEY_TAB); -		add("ADD",			KEY_ADD); -		add("SUBTRACT",		KEY_SUBTRACT); -		add("MULTIPLY",		KEY_MULTIPLY); -		add("DIVIDE",		KEY_DIVIDE); -		add("F1",			KEY_F1); -		add("F2",			KEY_F2); -		add("F3",			KEY_F3); -		add("F4",			KEY_F4); -		add("F5",			KEY_F5); -		add("F6",			KEY_F6); -		add("F7",			KEY_F7); -		add("F8",			KEY_F8); -		add("F9",			KEY_F9); -		add("F10",			KEY_F10); -		add("F11",			KEY_F11); -		add("F12",			KEY_F12); - -		add("PAD_UP",		KEY_PAD_UP); -		add("PAD_DOWN",		KEY_PAD_DOWN); -		add("PAD_LEFT",		KEY_PAD_LEFT); -		add("PAD_RIGHT",	KEY_PAD_RIGHT); -		add("PAD_HOME",		KEY_PAD_HOME); -		add("PAD_END",		KEY_PAD_END); -		add("PAD_PGUP",		KEY_PAD_PGUP); -		add("PAD_PGDN",		KEY_PAD_PGDN); -		add("PAD_CENTER",	KEY_PAD_CENTER); // the 5 in the middle -		add("PAD_INS",		KEY_PAD_INS); -		add("PAD_DEL",		KEY_PAD_DEL); -		add("PAD_RETURN",	KEY_PAD_RETURN); -		add("PAD_ADD",		KEY_PAD_ADD); // not used -		add("PAD_SUBTRACT", KEY_PAD_SUBTRACT); // not used -		add("PAD_MULTIPLY", KEY_PAD_MULTIPLY); // not used -		add("PAD_DIVIDE",	KEY_PAD_DIVIDE); // not used - -		add("BUTTON0",		KEY_BUTTON0); -		add("BUTTON1",		KEY_BUTTON1); -		add("BUTTON2",		KEY_BUTTON2); -		add("BUTTON3",		KEY_BUTTON3); -		add("BUTTON4",		KEY_BUTTON4); -		add("BUTTON5",		KEY_BUTTON5); -		add("BUTTON6",		KEY_BUTTON6); -		add("BUTTON7",		KEY_BUTTON7); -		add("BUTTON8",		KEY_BUTTON8); -		add("BUTTON9",		KEY_BUTTON9); -		add("BUTTON10",		KEY_BUTTON10); -		add("BUTTON11",		KEY_BUTTON11); -		add("BUTTON12",		KEY_BUTTON12); -		add("BUTTON13",		KEY_BUTTON13); -		add("BUTTON14",		KEY_BUTTON14); -		add("BUTTON15",		KEY_BUTTON15); -	} -}; -static WhichKeysym keysyms; - -struct WhichMask: public StringLookup<MASK> -{ -	WhichMask(): StringLookup<MASK>("shift mask") -	{ -		add("NONE",			MASK_NONE); -		add("CONTROL",		MASK_CONTROL); // Mapped to cmd on Macs -		add("ALT",			MASK_ALT); -		add("SHIFT",		MASK_SHIFT); -		add("MAC_CONTROL",	MASK_MAC_CONTROL); // Un-mapped Ctrl key on Macs, not used on Windows -	} -}; -static WhichMask masks; - -static MASK getMask(const LLSD& event) -{ -	MASK mask(MASK_NONE); -	LLSD masknames(event["mask"]); -	for (LLSD::array_const_iterator ai(masknames.beginArray()), aend(masknames.endArray()); -		 ai != aend; ++ai) -	{ -		mask |= masks.lookup(*ai); -	} -	return mask; -} - -static KEY getKEY(const LLSD& event) -{ -    if (event.has("keysym")) -	{ -		return keysyms.lookup(event["keysym"]); -	} -	else if (event.has("keycode")) -	{ -		return KEY(event["keycode"].asInteger()); -	} -	else -	{ -		return KEY(event["char"].asString()[0]); -	} -} - -void LLWindowListener::keyDown(LLSD const & evt) -{ -	mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt)); -} - -void LLWindowListener::keyUp(LLSD const & evt) -{ -	mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt)); -} - -// for WhichButton -typedef BOOL (LLWindowCallbacks::*MouseFunc)(LLWindow *, LLCoordGL, MASK); -struct Actions -{ -	Actions(const MouseFunc& d, const MouseFunc& u): down(d), up(u), valid(true) {} -	Actions(): valid(false) {} -	MouseFunc down, up; -	bool valid; -}; - -struct WhichButton: public StringLookup<Actions> -{ -	WhichButton(): StringLookup<Actions>("mouse button") -	{ -		add("LEFT",		Actions(&LLWindowCallbacks::handleMouseDown, -								&LLWindowCallbacks::handleMouseUp)); -		add("RIGHT",	Actions(&LLWindowCallbacks::handleRightMouseDown, -								&LLWindowCallbacks::handleRightMouseUp)); -		add("MIDDLE",	Actions(&LLWindowCallbacks::handleMiddleMouseDown, -								&LLWindowCallbacks::handleMiddleMouseUp)); -	} -}; -static WhichButton buttons; - -static LLCoordGL getPos(const LLSD& event) -{ -	return LLCoordGL(event["x"].asInteger(), event["y"].asInteger()); -} - -void LLWindowListener::mouseDown(LLSD const & evt) -{ -	Actions actions(buttons.lookup(evt["button"])); -	if (actions.valid) -	{ -		(mWindow->*(actions.down))(NULL, getPos(evt), getMask(evt)); -	} -} - -void LLWindowListener::mouseUp(LLSD const & evt) -{ -	Actions actions(buttons.lookup(evt["button"])); -	if (actions.valid) -	{ -		(mWindow->*(actions.up))(NULL, getPos(evt), getMask(evt)); -	} -} - -void LLWindowListener::mouseMove(LLSD const & evt) -{ -	mWindow->handleMouseMove(NULL, getPos(evt), getMask(evt)); -} - -void LLWindowListener::mouseScroll(LLSD const & evt) -{ -	S32 clicks = evt["clicks"].asInteger(); - -	mWindow->handleScrollWheel(NULL, clicks); -} - diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 050d4b729f..bf38a8b062 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -185,9 +185,10 @@ protected:  	ctrl_name_table_t mNameTable;  	std::string mTypeString[TYPE_COUNT]; +public:  	eControlType typeStringToEnum(const std::string& typestr);  	std::string typeEnumToString(eControlType typeenum);	 -public: +  	LLControlGroup(const std::string& name);  	~LLControlGroup();  	void cleanup(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fe80a47ca4..cb0f630aa0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -603,6 +603,7 @@ set(viewer_SOURCE_FILES      llweb.cpp      llwebsharing.cpp      llwind.cpp +    llwindowlistener.cpp      llwlanimator.cpp      llwldaycycle.cpp      llwlhandlers.cpp @@ -1161,6 +1162,7 @@ set(viewer_HEADER_FILES      llweb.h      llwebsharing.h      llwind.h +    llwindowlistener.h      llwlanimator.h      llwldaycycle.h      llwlhandlers.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index be4ec93946..15f1bbd1b1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5657,6 +5657,17 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>MemoryPrivatePoolSize</key> +    <map> +      <key>Comment</key> +      <string>Size of the private memory pool in MB (min. value is 256)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>512</integer> +    </map>      <key>MemProfiling</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 11e2e1e607..a4640f5504 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -724,7 +724,7 @@ bool LLAppViewer::init()  	//set the max heap size.  	initMaxHeapSize() ; -	LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled")) ; +	LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ;  	// write Google Breakpad minidump files to our log directory  	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); @@ -1124,63 +1124,25 @@ void LLAppViewer::checkMemory()  {  	const static F32 MEMORY_CHECK_INTERVAL = 1.0f ; //second  	//const static F32 MAX_QUIT_WAIT_TIME = 30.0f ; //seconds -	const static U32 MAX_SIZE_CHECKED_MEMORY_BLOCK = 64 * 1024 * 1024 ; //64 MB -	//static F32 force_quit_timer = MAX_QUIT_WAIT_TIME + MEMORY_CHECK_INTERVAL ; -	static void* last_reserved_address = NULL ; +	//static F32 force_quit_timer = MAX_QUIT_WAIT_TIME + MEMORY_CHECK_INTERVAL ;	 -	if(MEMORY_CHECK_INTERVAL > mMemCheckTimer.getElapsedTimeF32()) +	if(!gGLManager.mDebugGPU)  	{  		return ;  	} -	mMemCheckTimer.reset() ; - -	if(gGLManager.mDebugGPU) -	{ -		//update the availability of memory -		LLMemory::updateMemoryInfo() ; -	} -	//check the virtual address space fragmentation -	if(!last_reserved_address) -	{ -		last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; -	} -	else +	if(MEMORY_CHECK_INTERVAL > mMemCheckTimer.getElapsedTimeF32())  	{ -		last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; -		if(!last_reserved_address) //failed, try once more -		{ -			last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ; -		} +		return ;  	} +	mMemCheckTimer.reset() ; -	S32 is_low = !last_reserved_address || LLMemory::isMemoryPoolLow() ; - -	//if(is_low < 0) //to force quit -	//{ -	//	if(force_quit_timer > MAX_QUIT_WAIT_TIME) //just hit the limit for the first time -	//	{ -	//		//send out the notification to tell the viewer is about to quit in 30 seconds. -	//		LLNotification::Params params("ForceQuitDueToLowMemory"); -	//		LLNotifications::instance().add(params); +	//update the availability of memory +	LLMemory::updateMemoryInfo() ; -	//		force_quit_timer = MAX_QUIT_WAIT_TIME - MEMORY_CHECK_INTERVAL ; -	//	} -	//	else -	//	{ -	//		force_quit_timer -= MEMORY_CHECK_INTERVAL ; -	//		if(force_quit_timer < 0.f) -	//		{ -	//			forceQuit() ; //quit -	//		} -	//	} -	//} -	//else -	//{ -	//	force_quit_timer = MAX_QUIT_WAIT_TIME + MEMORY_CHECK_INTERVAL ; -	//} +	bool is_low = LLMemory::isMemoryPoolLow() ; -	LLPipeline::throttleNewMemoryAllocation(!is_low ? FALSE : TRUE) ;		 +	LLPipeline::throttleNewMemoryAllocation(is_low) ;		  	if(is_low)  	{ diff --git a/indra/newview/llviewercontrollistener.cpp b/indra/newview/llviewercontrollistener.cpp index 8bc25fa281..361b96221c 100644 --- a/indra/newview/llviewercontrollistener.cpp +++ b/indra/newview/llviewercontrollistener.cpp @@ -31,99 +31,196 @@  #include "llviewercontrollistener.h"  #include "llviewercontrol.h" +#include "llcontrol.h" +#include "llerror.h" +#include "llsdutil.h" +#include "stringize.h" +#include <sstream> -LLViewerControlListener gSavedSettingsListener; +namespace { + +LLViewerControlListener sSavedSettingsListener; + +} // unnamed namespace  LLViewerControlListener::LLViewerControlListener()  	: LLEventAPI("LLViewerControl", -                 "LLViewerControl listener: set, toggle or set default for various controls", -                 "group") +				 "LLViewerControl listener: set, toggle or set default for various controls")  { -	add("Global", -        "Set gSavedSettings control [\"key\"] to value [\"value\"]", -        boost::bind(&LLViewerControlListener::set, &gSavedSettings, _1)); -	add("PerAccount", -        "Set gSavedPerAccountSettings control [\"key\"] to value [\"value\"]", -        boost::bind(&LLViewerControlListener::set, &gSavedPerAccountSettings, _1)); -	add("Warning", -        "Set gWarningSettings control [\"key\"] to value [\"value\"]", -        boost::bind(&LLViewerControlListener::set, &gWarningSettings, _1)); -	add("Crash", -        "Set gCrashSettings control [\"key\"] to value [\"value\"]", -        boost::bind(&LLViewerControlListener::set, &gCrashSettings, _1)); - -#if 0 -	add(/*"toggleControl",*/ "Global", boost::bind(&LLViewerControlListener::toggleControl, &gSavedSettings, _1)); -	add(/*"toggleControl",*/ "PerAccount", boost::bind(&LLViewerControlListener::toggleControl, &gSavedPerAccountSettings, _1)); -	add(/*"toggleControl",*/ "Warning", boost::bind(&LLViewerControlListener::toggleControl, &gWarningSettings, _1)); -	add(/*"toggleControl",*/ "Crash", boost::bind(&LLViewerControlListener::toggleControl, &gCrashSettings, _1)); - -	add(/*"setDefault",*/ "Global", boost::bind(&LLViewerControlListener::setDefault, &gSavedSettings, _1)); -	add(/*"setDefault",*/ "PerAccount", boost::bind(&LLViewerControlListener::setDefault, &gSavedPerAccountSettings, _1)); -	add(/*"setDefault",*/ "Warning", boost::bind(&LLViewerControlListener::setDefault, &gWarningSettings, _1)); -	add(/*"setDefault",*/ "Crash", boost::bind(&LLViewerControlListener::setDefault, &gCrashSettings, _1)); -#endif // 0 +	std::ostringstream groupnames; +	groupnames << "[\"group\"] is one of "; +	const char* delim = ""; +	for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), +								  cgkend(LLControlGroup::endKeys()); +		 cgki != cgkend; ++cgki) +	{ +		groupnames << delim << '"' << *cgki << '"'; +		delim = ", "; +	} +	groupnames << '\n'; +	std::string grouphelp(groupnames.str()); +	std::string replyhelp("If [\"reply\"] requested, send new [\"value\"] on specified LLEventPump\n"); + +	add("set", +		std::string("Set [\"group\"] control [\"key\"] to optional value [\"value\"]\n" +					"If [\"value\"] omitted, set to control's defined default value\n") + +		grouphelp + replyhelp, +		&LLViewerControlListener::set, +		LLSDMap("group", LLSD())("key", LLSD())); +	add("toggle", +		std::string("Toggle [\"group\"] control [\"key\"], if boolean\n") + grouphelp + replyhelp, +		&LLViewerControlListener::toggle, +		LLSDMap("group", LLSD())("key", LLSD())); +	add("get", +		std::string("Query [\"group\"] control [\"key\"], replying on LLEventPump [\"reply\"]\n") + +		grouphelp, +		&LLViewerControlListener::get, +		LLSDMap("group", LLSD())("key", LLSD())("reply", LLSD())); +	add("groups", +		"Send on LLEventPump [\"reply\"] an array [\"groups\"] of valid group names", +		&LLViewerControlListener::groups, +		LLSDMap("reply", LLSD())); +	add("vars", +		std::string("For [\"group\"], send on LLEventPump [\"reply\"] an array [\"vars\"],\n" +					"each of whose entries looks like:\n" +					"  [\"name\"], [\"type\"], [\"value\"], [\"comment\"]\n") + grouphelp, +		&LLViewerControlListener::vars, +		LLSDMap("group", LLSD())("reply", LLSD()));  } -//static -void LLViewerControlListener::set(LLControlGroup * controls, LLSD const & event_data) +struct Info  { -	if(event_data.has("key")) +	Info(const LLSD& request): +		response(LLSD(), request), +		groupname(request["group"]), +		group(LLControlGroup::getInstance(groupname)), +		key(request["key"]), +		control(NULL)  	{ -		std::string key(event_data["key"]); +		if (! group) +		{ +			response.error(STRINGIZE("Unrecognized group '" << groupname << "'")); +			return; +		} -		if(controls->controlExists(key)) +		control = group->getControl(key); +		if (! control)  		{ -			controls->setUntypedValue(key, event_data["value"]); +			response.error(STRINGIZE("In group '" << groupname +									 << "', unrecognized control key '" << key << "'"));  		} -		else +	} + +	~Info() +	{ +		// If in fact the request passed to our constructor names a valid +		// group and key, grab the final value of the indicated control and +		// stuff it in our response. Since this outer destructor runs before +		// the contained Response destructor, this data will go into the +		// response we send. +		if (control)  		{ -			llwarns << "requested unknown control: \"" << key << '\"' << llendl; +			response["name"]	= control->getName(); +			response["type"]	= group->typeEnumToString(control->type()); +			response["value"]	= control->get(); +			response["comment"] = control->getComment();  		}  	} + +	LLEventAPI::Response response; +	std::string groupname; +	LLControlGroup* group; +	std::string key; +	LLControlVariable* control; +}; + +//static +void LLViewerControlListener::set(LLSD const & request) +{ +	Info info(request); +	if (! info.control) +		return; + +	if (request.has("value")) +	{ +		info.control->setValue(request["value"]); +	} +	else +	{ +		info.control->resetToDefault(); +	}  }  //static -void LLViewerControlListener::toggleControl(LLControlGroup * controls, LLSD const & event_data) +void LLViewerControlListener::toggle(LLSD const & request)  { -	if(event_data.has("key")) +	Info info(request); +	if (! info.control) +		return; + +	if (info.control->isType(TYPE_BOOLEAN)) +	{ +		info.control->set(! info.control->get().asBoolean()); +	} +	else  	{ -		std::string key(event_data["key"]); +		info.response.error(STRINGIZE("toggle of non-boolean '" << info.groupname +									  << "' control '" << info.key +									  << "', type is " +									  << info.group->typeEnumToString(info.control->type()))); +	} +} -		if(controls->controlExists(key)) -		{ -			LLControlVariable * control = controls->getControl(key); -			if(control->isType(TYPE_BOOLEAN)) -			{ -				control->set(!control->get().asBoolean()); -			} -			else -			{ -				llwarns << "requested toggle of non-boolean control: \"" << key << "\", type is " << control->type() << llendl; -			} -		} -		else -		{ -			llwarns << "requested unknown control: \"" << key << '\"' << llendl; -		} +void LLViewerControlListener::get(LLSD const & request) +{ +	// The Info constructor and destructor actually do all the work here. +	Info info(request); +} + +void LLViewerControlListener::groups(LLSD const & request) +{ +	// No Info, we're not looking up either a group or a control name. +	Response response(LLSD(), request); +	for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), +								  cgkend(LLControlGroup::endKeys()); +		 cgki != cgkend; ++cgki) +	{ +		response["groups"].append(*cgki);  	}  } -//static -void LLViewerControlListener::setDefault(LLControlGroup * controls, LLSD const & event_data) +struct CollectVars: public LLControlGroup::ApplyFunctor  { -	if(event_data.has("key")) +	CollectVars(LLControlGroup* g): +		mGroup(g) +	{} + +	virtual void apply(const std::string& name, LLControlVariable* control)  	{ -		std::string key(event_data["key"]); +		vars.append(LLSDMap +					("name", name) +					("type", mGroup->typeEnumToString(control->type())) +					("value", control->get()) +					("comment", control->getComment())); +	} -		if(controls->controlExists(key)) -		{ -			LLControlVariable * control = controls->getControl(key); -			control->resetToDefault(); -		} -		else -		{ -			llwarns << "requested unknown control: \"" << key << '\"' << llendl; -		} +	LLControlGroup* mGroup; +	LLSD vars; +}; + +void LLViewerControlListener::vars(LLSD const & request) +{ +	// This method doesn't use Info, because we're not looking up a specific +	// control name. +	Response response(LLSD(), request); +	std::string groupname(request["group"]); +	LLControlGroup* group(LLControlGroup::getInstance(groupname)); +	if (! group) +	{ +		return response.error(STRINGIZE("Unrecognized group '" << groupname << "'"));  	} + +	CollectVars collector(group); +	group->applyToAll(&collector); +	response["vars"] = collector.vars;  } diff --git a/indra/newview/llviewercontrollistener.h b/indra/newview/llviewercontrollistener.h index fd211b97af..2e72046924 100644 --- a/indra/newview/llviewercontrollistener.h +++ b/indra/newview/llviewercontrollistener.h @@ -40,11 +40,11 @@ public:  	LLViewerControlListener();  private: -	static void set(LLControlGroup *controls, LLSD const & event_data); -	static void toggleControl(LLControlGroup *controls, LLSD const & event_data); -	static void setDefault(LLControlGroup *controls, LLSD const & event_data); +	static void set(LLSD const & event_data); +	static void toggle(LLSD const & event_data); +	static void get(LLSD const & event_data); +	static void groups(LLSD const & event_data); +	static void vars(LLSD const & event_data);  }; -extern LLViewerControlListener gSavedSettingsListener; -  #endif // LL_LLVIEWERCONTROLLISTENER_H diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 6142ee0dd6..19326c4e30 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -202,7 +202,7 @@ void display_stats()  		gMemoryAllocated = LLMemory::getCurrentRSS();  		U32 memory = (U32)(gMemoryAllocated / (1024*1024));  		llinfos << llformat("MEMORY: %d MB", memory) << llendl; -		LLMemory::logMemoryInfo() ; +		LLMemory::logMemoryInfo(TRUE) ;  		gRecentMemoryTime.reset();  	}  } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 6435904fee..74ee918bfe 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2836,8 +2836,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)  			else  			{  				LLVector3 pos, look_at; -				U64 region_handle; -				U8 region_access; +				U64 region_handle(0); +				U8 region_access(0);  				std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size);  				std::string region_access_str = LLStringUtil::null;  				std::string region_access_icn = LLStringUtil::null; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index a7f4209e69..98ae746ca7 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -36,6 +36,7 @@  #include <iostream>  #include <fstream>  #include <algorithm> +#include <boost/lambda/core.hpp>  #include "llagent.h"  #include "llagentcamera.h" @@ -198,6 +199,7 @@  #include "llfloaternotificationsconsole.h"  #include "llnearbychat.h" +#include "llwindowlistener.h"  #include "llviewerwindowlistener.h"  #include "llpaneltopinfobar.h" @@ -1547,7 +1549,12 @@ LLViewerWindow::LLViewerWindow(  	mResDirty(false),  	mStatesDirty(false),  	mCurrResolutionIndex(0), -    mViewerWindowListener(new LLViewerWindowListener(this)), +	// gKeyboard is still NULL, so it doesn't do LLWindowListener any good to +	// pass its value right now. Instead, pass it a nullary function that +	// will, when we later need it, return the value of gKeyboard. +	// boost::lambda::var() constructs such a functor on the fly. +	mWindowListener(new LLWindowListener(this, boost::lambda::var(gKeyboard))), +	mViewerWindowListener(new LLViewerWindowListener(this)),  	mProgressView(NULL)  {  	LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index edd241a742..d35feb4667 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -62,6 +62,7 @@ class LLImageFormatted;  class LLHUDIcon;  class LLWindow;  class LLRootView; +class LLWindowListener;  class LLViewerWindowListener;  class LLPopupView; @@ -456,7 +457,8 @@ protected:  	bool			mStatesDirty;  	U32			mCurrResolutionIndex; -    boost::scoped_ptr<LLViewerWindowListener> mViewerWindowListener; +	boost::scoped_ptr<LLWindowListener> mWindowListener; +	boost::scoped_ptr<LLViewerWindowListener> mViewerWindowListener;  protected:  	static std::string sSnapshotBaseName; diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp new file mode 100644 index 0000000000..28f959eb71 --- /dev/null +++ b/indra/newview/llwindowlistener.cpp @@ -0,0 +1,505 @@ +/**  + * @file llwindowlistener.cpp + * @brief EventAPI interface for injecting input into LLWindow + * + * $LicenseInfo:firstyear=2001&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$ + */ + +#include "llviewerprecompiledheaders.h" +#include "linden_common.h" + +#include "llwindowlistener.h" + +#include "llcoord.h" +#include "llfocusmgr.h" +#include "llkeyboard.h" +#include "llwindowcallbacks.h" +#include "llui.h" +#include "llview.h" +#include "llviewinject.h" +#include "llviewerwindow.h" +#include "llviewerkeyboard.h" +#include "llrootview.h" +#include "llsdutil.h" +#include "stringize.h" +#include <typeinfo> +#include <map> +#include <boost/scoped_ptr.hpp> +#include <boost/lambda/core.hpp> +#include <boost/lambda/bind.hpp> + +namespace bll = boost::lambda; + +LLWindowListener::LLWindowListener(LLViewerWindow *window, const KeyboardGetter& kbgetter) +	: LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"), +	  mWindow(window), +	  mKbGetter(kbgetter) +{ +	std::string keySomething = +		"Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified "; +	std::string keyExplain = +		"(integer keycode values, or keysym string from any addKeyName() call in\n" +		"http://hg.secondlife.com/viewer-development/src/tip/indra/llwindow/llkeyboard.cpp )\n"; +	std::string mask = +		"Specify optional [\"mask\"] as an array containing any of \"CTL\", \"ALT\",\n" +		"\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n" +		"to form the mask used with the event."; + +	std::string given = "Given "; +	std::string mouseParams = +		"optional [\"path\"], optional [\"x\"] and [\"y\"], inject the requested mouse "; +	std::string buttonParams = +		std::string("[\"button\"], ") + mouseParams; +	std::string buttonExplain = +		"(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")\n"; +	std::string paramsExplain = +		"[\"path\"] is as for LLUI::resolvePath(), described in\n" +		"http://hg.secondlife.com/viewer-development/src/tip/indra/llui/llui.h\n" +		"If you omit [\"path\"], you must specify both [\"x\"] and [\"y\"].\n" +		"If you specify [\"path\"] without both [\"x\"] and [\"y\"], will synthesize (x, y)\n" +		"in the center of the LLView selected by [\"path\"].\n" +		"You may specify [\"path\"] with both [\"x\"] and [\"y\"], will use your (x, y).\n" +		"This may cause the LLView selected by [\"path\"] to reject the event.\n" +		"Optional [\"reply\"] requests a reply event on the named LLEventPump.\n" +		"reply[\"error\"] isUndefined (None) on success, else an explanatory message.\n"; + +	add("getInfo", +		"Get information about the ui element specified by [\"path\"]", +		&LLWindowListener::getInfo, +		LLSDMap("reply", LLSD())); +	add("getPaths", +		"Send on [\"reply\"] an event in which [\"paths\"] is an array of valid LLView\n" +		"pathnames. Optional [\"under\"] pathname specifies the base node under which\n" +		"to list; all nodes from root if no [\"under\"].", +		&LLWindowListener::getPaths, +		LLSDMap("reply", LLSD())); +	add("keyDown", +		keySomething + "keypress event.\n" + keyExplain + mask, +		&LLWindowListener::keyDown); +	add("keyUp", +		keySomething + "key release event.\n" + keyExplain + mask, +		&LLWindowListener::keyUp); +	add("mouseDown", +		given + buttonParams + "click event.\n" + buttonExplain + paramsExplain + mask, +		&LLWindowListener::mouseDown); +	add("mouseUp", +		given + buttonParams + "release event.\n" + buttonExplain + paramsExplain + mask, +		&LLWindowListener::mouseUp); +	add("mouseMove", +		given + mouseParams + "movement event.\n" + paramsExplain + mask, +		&LLWindowListener::mouseMove); +	add("mouseScroll", +		"Given an integer number of [\"clicks\"], inject the requested mouse scroll event.\n" +		"(positive clicks moves downward through typical content)", +		&LLWindowListener::mouseScroll); +} + +template <typename MAPPED> +class StringLookup +{ +private: +	std::string mDesc; +	typedef std::map<std::string, MAPPED> Map; +	Map mMap; + +public: +	StringLookup(const std::string& desc): mDesc(desc) {} + +	MAPPED lookup(const typename Map::key_type& key) const +	{ +		typename Map::const_iterator found = mMap.find(key); +		if (found == mMap.end()) +		{ +			LL_WARNS("LLWindowListener") << "Unknown " << mDesc << " '" << key << "'" << LL_ENDL; +			return MAPPED(); +		} +		return found->second; +	} + +protected: +	void add(const typename Map::key_type& key, const typename Map::mapped_type& value) +	{ +		mMap.insert(typename Map::value_type(key, value)); +	} +}; + +namespace { + +// helper for getMask() +MASK lookupMask_(const std::string& maskname) +{ +	// It's unclear to me whether MASK_MAC_CONTROL is important, but it's not +	// supported by maskFromString(). Handle that specially. +	if (maskname == "MAC_CONTROL") +	{ +		return MASK_MAC_CONTROL; +	} +	else +	{ +		// In case of lookup failure, return MASK_NONE, which won't affect our +		// caller's OR. +		MASK mask(MASK_NONE); +		LLKeyboard::maskFromString(maskname, &mask); +		return mask; +	} +} + +MASK getMask(const LLSD& event) +{ +	LLSD masknames(event["mask"]); +	if (! masknames.isArray()) +	{ +		// If event["mask"] is a single string, perform normal lookup on it. +		return lookupMask_(masknames); +	} + +	// Here event["mask"] is an array of mask-name strings. OR together their +	// corresponding bits. +	MASK mask(MASK_NONE); +	for (LLSD::array_const_iterator ai(masknames.beginArray()), aend(masknames.endArray()); +		 ai != aend; ++ai) +	{ +		mask |= lookupMask_(*ai); +	} +	return mask; +} + +KEY getKEY(const LLSD& event) +{ +    if (event.has("keysym")) +	{ +		// Initialize to KEY_NONE; that way we can ignore the bool return from +		// keyFromString() and, in the lookup-fail case, simply return KEY_NONE. +		KEY key(KEY_NONE); +		LLKeyboard::keyFromString(event["keysym"], &key); +		return key; +	} +	else if (event.has("keycode")) +	{ +		return KEY(event["keycode"].asInteger()); +	} +	else +	{ +		return KEY(event["char"].asString()[0]); +	} +} + +} // namespace + +void LLWindowListener::getInfo(LLSD const & evt) +{ +	Response response(LLSD(), evt); +	 +	if (evt.has("path")) +	{ +		std::string path(evt["path"]); +		LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), path); +		if (target_view != 0) +		{ +			response.setResponse(target_view->getInfo()); +		} +		else  +		{ +			response.error(STRINGIZE(evt["op"].asString() << " request " +											"specified invalid \"path\": '" << path << "'")); +		} +	} +	else  +	{ +		response.error( +			STRINGIZE(evt["op"].asString() << "request did not provide a path" )); +	} +} + +void LLWindowListener::getPaths(LLSD const & request) +{ +	Response response(LLSD(), request); +	LLView *root(LLUI::getRootView()), *base(NULL); +	// Capturing request["under"] as string means we conflate the case in +	// which there is no ["under"] key with the case in which its value is the +	// empty string. That seems to make sense to me. +	std::string under(request["under"]); + +	// Deal with optional "under" parameter +	if (under.empty()) +	{ +		base = root; +	} +	else +	{ +		base = LLUI::resolvePath(root, under); +		if (! base) +		{ +			return response.error(STRINGIZE(request["op"].asString() << " request " +											"specified invalid \"under\" path: '" << under << "'")); +		} +	} + +	// Traverse the entire subtree under 'base', collecting pathnames +	for (LLView::tree_iterator_t ti(base->beginTreeDFS()), tend(base->endTreeDFS()); +		 ti != tend; ++ti) +	{ +		response["paths"].append((*ti)->getPathname()); +	} +} + +void LLWindowListener::keyDown(LLSD const & evt) +{ +	Response response(LLSD(), evt); +	 +	if (evt.has("path")) +	{ +		std::string path(evt["path"]); +		LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), path); +		if (target_view == 0)  +		{ +			response.error(STRINGIZE(evt["op"].asString() << " request " +											"specified invalid \"path\": '" << path << "'")); +		} +		else if(target_view->isAvailable()) +		{ +			response.setResponse(target_view->getInfo()); +			 +			gFocusMgr.setKeyboardFocus(target_view); +			KEY key = getKEY(evt); +			MASK mask = getMask(evt); +			gViewerKeyboard.handleKey(key, mask, false); +			if(key < 0x80) mWindow->handleUnicodeChar(key, mask); +		} +		else  +		{ +			response.error(STRINGIZE(evt["op"].asString() << " request " +											"element specified by \"path\": '" << path << "'"  +											<< " is not visible")); +		} +	} +	else  +	{ +		mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt)); +	} +} + +void LLWindowListener::keyUp(LLSD const & evt) +{ +	Response response(LLSD(), evt); + +	if (evt.has("path")) +	{ +		std::string path(evt["path"]); +		LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), path); +		if (target_view == 0 ) +		{ +			response.error(STRINGIZE(evt["op"].asString() << " request " +											"specified invalid \"path\": '" << path << "'")); +		} +		else if (target_view->isAvailable()) +		{ +			response.setResponse(target_view->getInfo()); + +			gFocusMgr.setKeyboardFocus(target_view); +			mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt)); +		} +		else  +		{ +			response.error(STRINGIZE(evt["op"].asString() << " request " +											"element specified byt \"path\": '" << path << "'"  +											<< " is not visible")); +		} +	} +	else  +	{ +		mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt)); +	} +} + +// for WhichButton +typedef BOOL (LLWindowCallbacks::*MouseMethod)(LLWindow *, LLCoordGL, MASK); +struct Actions +{ +	Actions(const MouseMethod& d, const MouseMethod& u): down(d), up(u), valid(true) {} +	Actions(): valid(false) {} +	MouseMethod down, up; +	bool valid; +}; + +struct WhichButton: public StringLookup<Actions> +{ +	WhichButton(): StringLookup<Actions>("mouse button") +	{ +		add("LEFT",		Actions(&LLWindowCallbacks::handleMouseDown, +								&LLWindowCallbacks::handleMouseUp)); +		add("RIGHT",	Actions(&LLWindowCallbacks::handleRightMouseDown, +								&LLWindowCallbacks::handleRightMouseUp)); +		add("MIDDLE",	Actions(&LLWindowCallbacks::handleMiddleMouseDown, +								&LLWindowCallbacks::handleMiddleMouseUp)); +	} +}; +static WhichButton buttons; + +typedef boost::function<bool(LLCoordGL, MASK)> MouseFunc; + +static void mouseEvent(const MouseFunc& func, const LLSD& request) +{ +	// Ensure we send response +	LLEventAPI::Response response(LLSD(), request); +	// We haven't yet established whether the incoming request has "x" and "y", +	// but capture this anyway, with 0 for omitted values. +	LLCoordGL pos(request["x"].asInteger(), request["y"].asInteger()); +	bool has_pos(request.has("x") && request.has("y")); + +	boost::scoped_ptr<LLView::TemporaryDrilldownFunc> tempfunc; + +	// Documentation for mouseDown(), mouseUp() and mouseMove() claims you +	// must either specify ["path"], or both of ["x"] and ["y"]. You MAY +	// specify all. Let's say that passing "path" as an empty string is +	// equivalent to not passing it at all. +	std::string path(request["path"]); +	if (path.empty()) +	{ +		// Without "path", you must specify both "x" and "y". +		if (! has_pos) +		{ +			return response.error(STRINGIZE(request["op"].asString() << " request " +											"without \"path\" must specify both \"x\" and \"y\": " +											<< request)); +		} +	} +	else // ! path.empty() +	{ +		LLView* root   = LLUI::getRootView(); +		LLView* target = LLUI::resolvePath(root, path); +		if (! target) +		{ +			return response.error(STRINGIZE(request["op"].asString() << " request " +											"specified invalid \"path\": '" << path << "'")); +		} + +		response.setResponse(target->getInfo()); + +		// The intent of this test is to prevent trying to drill down to a +		// widget in a hidden floater, or on a tab that's not current, etc. +		if (! target->isInVisibleChain()) +		{ +			return response.error(STRINGIZE(request["op"].asString() << " request " +											"specified \"path\" not currently visible: '" +											<< path << "'")); +		} + +		// This test isn't folded in with the above error case since you can +		// (e.g.) pop up a tooltip even for a disabled widget. +		if (! target->isInEnabledChain()) +		{ +			response.warn(STRINGIZE(request["op"].asString() << " request " +									"specified \"path\" not currently enabled: '" +									<< path << "'")); +		} + +		if (! has_pos) +		{ +			LLRect rect(target->calcScreenRect()); +			pos.set(rect.getCenterX(), rect.getCenterY()); +			// nonstandard warning tactic: probably usual case; we want event +			// sender to know synthesized (x, y), but maybe don't need to log? +			response["warnings"].append(STRINGIZE("using center point (" +												  << pos.mX << ", " << pos.mY << ")")); +		} + +/*==========================================================================*| +		// NEVER MIND: the LLView tree defines priority handler layers in +		// front of the normal widget set, so this has never yet produced +		// anything but spam warnings. (sigh) + +		// recursive childFromPoint() should give us the frontmost, leafmost +		// widget at the specified (x, y). +		LLView* frontmost = root->childFromPoint(pos.mX, pos.mY, true); +		if (frontmost != target) +		{ +			response.warn(STRINGIZE(request["op"].asString() << " request " +									"specified \"path\" = '" << path +									<< "', but frontmost LLView at (" << pos.mX << ", " << pos.mY +									<< ") is '" << LLView::getPathname(frontmost) << "'")); +		} +|*==========================================================================*/ + +		// Instantiate a TemporaryDrilldownFunc to route incoming mouse events +		// to the target LLView*. But put it on the heap since "path" is +		// optional. Nonetheless, manage it with a boost::scoped_ptr so it +		// will be destroyed when we leave. +		tempfunc.reset(new LLView::TemporaryDrilldownFunc(llview::TargetEvent(target))); +	} + +	// The question of whether the requested LLView actually handled the +	// specified event is important enough, and its handling unclear enough, +	// to warrant a separate response attribute. Instead of deciding here to +	// make it a warning, or an error, let caller decide. +	response["handled"] = func(pos, getMask(request)); + +	// On exiting this scope, response will send, tempfunc will restore the +	// normal pointInView(x, y) containment logic, etc. +} + +void LLWindowListener::mouseDown(LLSD const & request) +{ +	Actions actions(buttons.lookup(request["button"])); +	if (actions.valid) +	{ +		// Normally you can pass NULL to an LLWindow* without compiler +		// complaint, but going through boost::lambda::bind() evidently +		// bypasses that special case: it only knows you're trying to pass an +		// int to a pointer. Explicitly cast NULL to the desired pointer type. +		mouseEvent(bll::bind(actions.down, mWindow, +							 static_cast<LLWindow*>(NULL), bll::_1, bll::_2), +				   request); +	} +} + +void LLWindowListener::mouseUp(LLSD const & request) +{ +	Actions actions(buttons.lookup(request["button"])); +	if (actions.valid) +	{ +		mouseEvent(bll::bind(actions.up, mWindow, +							 static_cast<LLWindow*>(NULL), bll::_1, bll::_2), +				   request); +	} +} + +void LLWindowListener::mouseMove(LLSD const & request) +{ +	// We want to call the same central mouseEvent() routine for +	// handleMouseMove() as for button clicks. But handleMouseMove() returns +	// void, whereas mouseEvent() accepts a function returning bool -- and +	// uses that bool return. Use (void-lambda-expression, true) to construct +	// a callable that returns bool anyway. Pass 'true' because we expect that +	// our caller will usually treat 'false' as a problem. +	mouseEvent((bll::bind(&LLWindowCallbacks::handleMouseMove, mWindow, +						  static_cast<LLWindow*>(NULL), bll::_1, bll::_2), +				true), +			   request); +} + +void LLWindowListener::mouseScroll(LLSD const & request) +{ +	S32 clicks = request["clicks"].asInteger(); + +	mWindow->handleScrollWheel(NULL, clicks); +} diff --git a/indra/llwindow/llwindowlistener.h b/indra/newview/llwindowlistener.h index 74e577ff93..7af5ab3b9f 100644 --- a/indra/llwindow/llwindowlistener.h +++ b/indra/newview/llwindowlistener.h @@ -31,14 +31,16 @@  #include <boost/function.hpp>  class LLKeyboard; -class LLWindowCallbacks; +class LLViewerWindow;  class LLWindowListener : public LLEventAPI  {  public:  	typedef boost::function<LLKeyboard*()> KeyboardGetter; -	LLWindowListener(LLWindowCallbacks * window, const KeyboardGetter& kbgetter); +	LLWindowListener(LLViewerWindow * window, const KeyboardGetter& kbgetter); +	void getInfo(LLSD const & evt); +	void getPaths(LLSD const & evt);  	void keyDown(LLSD const & evt);  	void keyUp(LLSD const & evt);  	void mouseDown(LLSD const & evt); @@ -47,7 +49,7 @@ public:  	void mouseScroll(LLSD const & evt);  private: -	LLWindowCallbacks * mWindow; +	LLViewerWindow * mWindow;  	KeyboardGetter mKbGetter;  }; | 
