diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 34a08603fabdb165486892b21c9cc69b2faf6923..aa6684e7a80eaabd4522394e6f79cb75b56f88a1 100755
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -128,6 +128,7 @@ set(llui_SOURCE_FILES
     llviewmodel.cpp
     llview.cpp
     llviewquery.cpp
+    llviewereventrecorder.cpp
     llwindowshade.cpp
     llxuiparser.cpp
     )
@@ -240,6 +241,7 @@ set(llui_HEADER_FILES
     llviewinject.h
     llviewmodel.h
     llview.h
+    llviewereventrecorder.h
     llviewquery.h
     llwindowshade.h
     llxuiparser.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index a8149a9a1dae6372f63f1d644ae28d8554e318f7..4ccb01910670decbf15ca994941e5b5add2770ce 100755
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -49,6 +49,7 @@
 #include "lluictrlfactory.h"
 #include "llhelp.h"
 #include "lldockablefloater.h"
+#include "llviewereventrecorder.h"
 
 static LLDefaultChildRegistry::Register<LLButton> r("button");
 
@@ -443,6 +444,8 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
 		 */
 		LLUICtrl::handleMouseDown(x, y, mask);
 
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
 		if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
 
 		mMouseDownTimer.start();
@@ -473,6 +476,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
 		 * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
 		 */
 		LLUICtrl::handleMouseUp(x, y, mask);
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); 
 
 		// Regardless of where mouseup occurs, handle callback
 		if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 09e27a264aa151399208526cf86d02ee21d7ba2d..913de49d6389e72ead50dbcb124893319131007d 100755
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -29,7 +29,7 @@
 // mini-map floater, etc.
 
 #include "linden_common.h"
-
+#include "llviewereventrecorder.h"
 #include "llfloater.h"
 
 #include "llfocusmgr.h"
@@ -653,7 +653,10 @@ void LLFloater::handleVisibilityChange ( BOOL new_visibility )
 
 void LLFloater::openFloater(const LLSD& key)
 {
-	llinfos << "Opening floater " << getName() << llendl;
+    llinfos << "Opening floater " << getName() << " full path: " << getPathname() << llendl;
+
+	LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), true,"floater"); // Last param is event subtype or empty string
+
 	mKey = key; // in case we need to open ourselves again
 
 	if (getSoundFlags() != SILENT 
@@ -707,6 +710,7 @@ void LLFloater::openFloater(const LLSD& key)
 void LLFloater::closeFloater(bool app_quitting)
 {
 	llinfos << "Closing floater " << getName() << llendl;
+	LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), false,"floater"); // Last param is event subtype or empty string
 	if (app_quitting)
 	{
 		LLFloater::sQuitting = true;
@@ -1552,6 +1556,17 @@ BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
 	return TRUE;//always
 }
 
+// virtual
+BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+	lldebugs << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+	BOOL handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView
+	if (handled) {
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+	}
+	return handled;
+}
+
 // virtual
 BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
 {
@@ -1572,7 +1587,11 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
 	else
 	{
 		bringToFront( x, y );
-		return LLPanel::handleMouseDown( x, y, mask );
+		BOOL handled = LLPanel::handleMouseDown( x, y, mask ); 
+		if (handled) {
+			LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); 
+		}
+		return handled;
 	}
 }
 
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 4dba1e645f5bdcf3d6319180c24efa89879c8170..09fe2219c0517171dae5cd9dd04782385babdcd8 100755
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -285,6 +285,7 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	S32				getHeaderHeight() const { return mHeaderHeight; }
 
 	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
+	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleRightMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleDoubleClick(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleMiddleMouseDown(S32 x, S32 y, MASK mask);
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index fd981557047697b291af17e4791b04de1d723234..76ba53ec329e600084424a110695f7924edc709b 100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -27,7 +27,7 @@
 #include "linden_common.h"
 
 #include "lltabcontainer.h"
-
+#include "llviewereventrecorder.h"
 #include "llfocusmgr.h"
 #include "lllocalcliprect.h"
 #include "llrect.h"
@@ -578,6 +578,11 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
 			tab_button->setFocus(TRUE);
 		}
 	}
+	if (handled) {
+		// Note: May need to also capture local coords right here ?
+		LLViewerEventRecorder::instance().update_xui(getPathname( ));
+	}
+
 	return handled;
 }
 
@@ -629,30 +634,33 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
 	BOOL handled = FALSE;
 	BOOL has_scroll_arrows = (getMaxScrollPos() > 0)  && !getTabsHidden();
 
+	S32 local_x = x - getRect().mLeft;
+	S32 local_y = y - getRect().mBottom;
+
 	if (has_scroll_arrows)
 	{
 		if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
 		{
-			S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
-			S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
+			local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
+			local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
 			handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
 		}
 		else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x,	y))
 		{
-			S32	local_x	= x	- mJumpNextArrowBtn->getRect().mLeft;
-			S32	local_y	= y	- mJumpNextArrowBtn->getRect().mBottom;
+			local_x	= x	- mJumpNextArrowBtn->getRect().mLeft;
+			local_y	= y	- mJumpNextArrowBtn->getRect().mBottom;
 			handled = mJumpNextArrowBtn->handleMouseUp(local_x,	local_y, mask);
 		}
 		else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
 		{
-			S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
-			S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
+			local_x = x - mPrevArrowBtn->getRect().mLeft;
+			local_y = y - mPrevArrowBtn->getRect().mBottom;
 			handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
 		}
 		else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
 		{
-			S32 local_x = x - mNextArrowBtn->getRect().mLeft;
-			S32 local_y = y - mNextArrowBtn->getRect().mBottom;
+			local_x = x - mNextArrowBtn->getRect().mLeft;
+			local_y = y - mNextArrowBtn->getRect().mBottom;
 			handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
 		}
 	}
@@ -676,6 +684,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
 		}
 		gFocusMgr.setMouseCapture(NULL);
 	}
+	if (handled) {
+		// Note: may need to capture local coords here
+		LLViewerEventRecorder::instance().update_xui(getPathname( ));
+	}
 	return handled;
 }
 
@@ -1059,21 +1071,21 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
 		
 		if (mIsVertical)
 		{
-			p.name(std::string("vert tab button"));
-			p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
-			p.image_selected(mMiddleTabParams.tab_left_image_selected);
-			p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
+		  p.name("vtab_"+std::string(child->getName()));
+		  p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
+		  p.image_selected(mMiddleTabParams.tab_left_image_selected);
+		  p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
 		}
 		else
-		{
-			p.name(std::string(child->getName()) + " tab");
-			p.visible(false);
-			p.image_unselected(tab_img);
-			p.image_selected(tab_selected_img);
-			p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
-			// Try to squeeze in a bit more text
-			p.pad_left( mLabelPadLeft );
-			p.pad_right(2);
+		  { 
+		    p.name("htab_"+std::string(child->getName()));
+		    p.visible(false);
+		    p.image_unselected(tab_img);
+		    p.image_selected(tab_selected_img);
+		    p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
+		    // Try to squeeze in a bit more text
+		    p.pad_left( mLabelPadLeft );
+		    p.pad_right(2);
 		}
 		
 		// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index b9c843e931c4aa657eec7a3f3bed2d127870b230..1722bf27bddcda5937d681f7ebff1d22b1bb2383 100755
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -29,7 +29,7 @@
 
 #define LLUICTRL_CPP
 #include "lluictrl.h"
-
+#include "llviewereventrecorder.h"
 #include "llfocusmgr.h"
 #include "llpanel.h"
 #include "lluictrlfactory.h"
@@ -308,22 +308,40 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
 //virtual 
 BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
 {
+
+	lldebugs << "LLUICtrl::handleMouseDown calling	LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+  
 	BOOL handled  = LLView::handleMouseDown(x,y,mask);
+	
 	if (mMouseDownSignal)
 	{
 		(*mMouseDownSignal)(this,x,y,mask);
 	}
+	lldebugs << "LLUICtrl::handleMousedown - handled is returning as: " << handled << "	  " << llendl;
+	
+	if (handled) {
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+	}
 	return handled;
 }
 
 //virtual
 BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
 {
+
+	lldebugs << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+
 	BOOL handled  = LLView::handleMouseUp(x,y,mask);
+	if (handled) {
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname()); 
+	}
 	if (mMouseUpSignal)
 	{
 		(*mMouseUpSignal)(this,x,y,mask);
 	}
+
+	lldebugs << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << "  -  is returning as: " << handled << "   " << llendl;
+
 	return handled;
 }
 
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 3613a40e2c6c8021fce9c8fb8997f31eef03eaf0..9a42fc637b3a9d8018dd27b42eac15180bf56cbb 100755
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -48,7 +48,9 @@
 #include "lluictrlfactory.h"
 #include "lltooltip.h"
 #include "llsdutil.h"
-
+#include "llsdserialize.h"
+#include "llviewereventrecorder.h"
+#include "llkeyboard.h"
 // for ui edit hack
 #include "llbutton.h"
 #include "lllineeditor.h"
@@ -642,13 +644,27 @@ void LLView::setVisible(BOOL visible)
 // virtual
 void LLView::handleVisibilityChange ( BOOL new_visibility )
 {
+	BOOL old_visibility;
 	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
 		// only views that are themselves visible will have their overall visibility affected by their ancestors
-		if (viewp->getVisible())
+		old_visibility=viewp->getVisible();
+
+		if (old_visibility!=new_visibility)
+		{
+			LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget");
+		}
+
+		if (old_visibility)
 		{
 			viewp->handleVisibilityChange ( new_visibility );
 		}
+
+		// Consider changing returns to confirm success and know which widget grabbed it
+		// For now assume success and log at highest xui possible 
+		// NOTE we log actual state - which may differ if it somehow failed to set visibility
+		lldebugs << "LLView::handleVisibilityChange	 - now: " << getVisible()  << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << llendl;
+		
 	}
 }
 
@@ -697,6 +713,7 @@ bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
 		&& getEnabled();
 }
 
+// This is NOT event recording related
 void LLView::logMouseEvent()
 {
 	if (sDebugMouseHandling)
@@ -743,8 +760,15 @@ LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDA
 		if ((viewp->*method)( local_x, local_y, extra )
 			|| (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y )))
 		{
-			viewp->logMouseEvent();
-			return viewp;
+		lldebugs << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x  "<< local_x << " " << x	<< "local/global y " << local_y << " " << y << llendl;
+		lldebugs << "LLView::childrenHandleMouseEvent  getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << llendl;
+
+		LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); 
+
+		// This is NOT event recording related
+		viewp->logMouseEvent();
+
+		return viewp;
 		}
 	}
 	return NULL;
@@ -766,6 +790,7 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
 		if (viewp->handleToolTip(local_x, local_y, mask) 
 			|| viewp->blockMouseEvent(local_x, local_y))
 		{
+			// This is NOT event recording related
 			viewp->logMouseEvent();
 			return viewp;
 		}
@@ -824,6 +849,7 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
 		if (viewp->handleHover(local_x, local_y, mask)
 			|| viewp->blockMouseEvent(local_x, local_y))
 		{
+			// This is NOT event recording related
 			viewp->logMouseEvent();
 			return viewp;
 		}
@@ -907,10 +933,11 @@ BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 
 		if (!handled)
 		{
+			// For event logging we don't care which widget handles it
+			// So we capture the key at the end of this function once we know if it was handled
 			handled = handleKeyHere( key, mask );
-			if (handled && LLView::sDebugKeys)
-			{
-				llinfos << "Key handled by " << getName() << llendl;
+			if (handled) {
+				llwarns << "Key handled by " << getName() << llendl;
 			}
 		}
 	}
@@ -958,12 +985,17 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
 		handled = mParentView->handleUnicodeChar(uni_char, FALSE);
 	}
 
+	if (handled) {
+		LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
+	}
+	
 	return handled;
 }
 
 
 BOOL LLView::handleUnicodeCharHere(llwchar uni_char )
 {
+	llwarns << "LLView::handleUnicodeCharHere - about to return false - key is not being handled - a class somewhere should implement this method" << llendl;
 	return FALSE;
 }
 
@@ -987,12 +1019,21 @@ BOOL LLView::hasMouseCapture()
 
 BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
 {
-	return childrenHandleMouseUp( x, y, mask ) != NULL;
+
+
+	LLView* r = childrenHandleMouseUp( x, y, mask );
+
+	return (r!=NULL);
+
 }
 
 BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	return childrenHandleMouseDown( x, y, mask ) != NULL;
+
+	LLView* r= childrenHandleMouseDown(x, y, mask );
+
+	return (r!=NULL);
+
 }
 
 BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
@@ -1065,7 +1106,7 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
 
 LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
 {
-	return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
+	return	childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
 }
 
 LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..36df1f6ac14adb9c6ccb3f68e1912f034e6e342a
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -0,0 +1,270 @@
+#include "llviewereventrecorder.h"
+#include "llui.h"
+#include "llleap.h"
+
+LLViewerEventRecorder::LLViewerEventRecorder() {
+
+  clear(UNDEFINED);
+
+  // Remove any previous event log file
+  std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
+  LLFile::remove(old_log_ui_events_to_llsd_file);
+  
+
+  mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
+  LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file);
+
+}
+
+
+bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
+  return LLUI::sSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems");
+}
+
+
+void LLViewerEventRecorder::setEventLoggingOn() {
+  if (! mLog.is_open()) {
+    mLog.open(mLogFilename, llofstream::out);
+  }
+  logEvents=true; 
+  lldebugs << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << llendl;
+}
+
+void LLViewerEventRecorder::setEventLoggingOff() {
+  logEvents=false;
+  mLog.flush();
+  mLog.close();
+  lldebugs << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << llendl;
+}
+
+
+ LLViewerEventRecorder::~LLViewerEventRecorder() {
+  if (mLog.is_open()) {
+      mLog.close();
+    }
+}
+
+void LLViewerEventRecorder::clear_xui() {
+  xui.clear();
+}
+
+void LLViewerEventRecorder::clear(S32 r) {
+
+  xui.clear();
+
+  local_x=r;
+  local_y=r;
+
+  global_x=r;
+  global_y=r;
+    
+
+}
+
+void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) {
+  local_x=x;
+  local_y=y;
+}
+
+void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) {
+  global_x=x;
+  global_y=y;
+}
+
+void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) {
+
+  LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), xui);
+  if (! target_view) {
+    lldebugs << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << llendl;
+    return;
+  }
+  lldebugs << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x   "<< this->local_x << " " << this->global_x  << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+
+
+  if (this->local_x < 1 && this->local_y<1 && local_x && local_y) {
+    this->local_x=local_x;
+    this->local_y=local_y;
+  }
+  this->global_x=global_x;
+  this->global_y=global_y;
+
+  // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler
+  if (mName!="" &&  mName!="/" && xui=="") { 
+    //	xui=std::string("/")+mName+xui; 
+    //xui=mName+xui; 
+    xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS
+  }
+
+  lldebugs << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x   "<< this->local_x << " " << this->global_x  << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+}
+
+void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype) {
+
+  LLSD  event=LLSD::emptyMap();
+
+  event.insert("event",LLSD(std::string("visibility")));
+
+  if (visibility) {
+    event.insert("visibility",LLSD(true));
+  } else {
+    event.insert("visibility",LLSD(false));
+  }
+
+  if (event_subtype!="") {
+    event.insert("event_subtype", LLSD(event_subtype));
+  }
+
+  if(name!="") {
+    event.insert("name",LLSD(name));
+  }
+
+  if (xui!="") {
+    event.insert("path",LLSD(xui));
+  }
+
+  event.insert("timestamp",LLSD(LLDate::now().asString())); 
+  recordEvent(event);
+}
+
+
+std::string LLViewerEventRecorder::get_xui() {
+  return xui;
+}
+void LLViewerEventRecorder::update_xui(std::string xui) {
+  if (xui!="" && this->xui=="" ) {
+    lldebugs << "LLViewerEventRecorder::update_xui to " << xui << llendl;
+    this->xui=xui;
+  } else {
+    lldebugs << "LLViewerEventRecorder::update_xui called with empty string" << llendl;
+  }
+}
+
+void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) {
+
+  // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere
+
+  LLSD event = LLSD::emptyMap();
+
+  event.insert("event",LLSD("type"));
+
+  // keysym ...or
+  // keycode...or
+  // char
+  event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key)));
+
+  // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+  // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+  // break the test script and it would be useful to have more context to make these sorts of edits safer
+ 
+  // TODO  replace this with a call which extracts to an array of names of masks (just like vita expects during playback)
+  // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-)
+  // call the func - llkeyboard::llsdStringarrayFromMask
+
+  LLSD key_mask=LLSD::emptyArray();
+
+  if (mask & MASK_CONTROL)     { key_mask.append(LLSD("CTL")); }  // Mac command key - has code of 0x1  in llcommon/indra_contstants
+  if (mask & MASK_ALT)         { key_mask.append(LLSD("ALT")); }
+  if (mask & MASK_SHIFT)       { key_mask.append(LLSD("SHIFT")); }
+  if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); }
+
+  event.insert("mask",key_mask); 
+  event.insert("timestamp",LLSD(LLDate::now().asString())); 
+
+  // Although vita has keyDown and keyUp requests it does not have type as a high-level concept 
+  // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events 
+  // Here  we will use  "type" as  our event type
+
+  lldebugs << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << llendl;
+
+
+  //lldebugs  << "[VITA] key_name: "  << LLKeyboard::stringFromKey(key) << "mask: "<< mask  << "handled by " << getName() << llendl;
+  lldebugs  << "LLVIewerEventRecorder::logKeyEvent  key_name: "  << LLKeyboard::stringFromKey(key) << "mask: "<< mask  << llendl;
+
+
+  recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::playbackRecording() {
+
+  LLSD LeapCommand;
+
+  // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer
+  LeapCommand =LLUI::sSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand");
+  
+  lldebugs << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << llendl;
+  LLLeap::create("", LeapCommand, false); // exception=false
+  
+}
+
+
+void LLViewerEventRecorder::recordEvent(LLSD event) {
+  lldebugs << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << llendl;
+  mLog << event << std::endl;
+  
+}
+void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) {
+  if (! logEvents) return;
+
+  // Note: keyUp is not captured since the viewer seems to not care about keyUp events
+
+  LLSD event=LLSD::emptyMap();
+
+  event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+  
+  // keysym ...or
+  // keycode...or
+  // char
+
+  lldebugs << "Wrapped in conversion to wstring " <<  wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << llendl;
+  
+  event.insert("char",
+	       LLSD(  wstring_to_utf8str(LLWString( 1,uni_char))  )
+	       ); 
+
+  // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+  // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+  // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+  // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point
+
+  event.insert("event",LLSD("keyDown")); 
+
+  lldebugs  << "[VITA] unicode key: " << uni_char   << llendl;
+  lldebugs  << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << llendl;
+
+
+  recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name)
+{
+  if (! logEvents) return; 
+
+  LLSD  event=LLSD::emptyMap();
+
+  event.insert("event",LLSD(std::string("mouse"+ button_state)));
+  event.insert("button",LLSD(button_name));
+  if (xui!="") {
+    event.insert("path",LLSD(xui));
+  }
+
+  if (local_x>0 && local_y>0) {
+    event.insert("local_x",LLSD(local_x));
+    event.insert("local_y",LLSD(local_y));
+  }
+
+  if (global_x>0 && global_y>0) {
+    event.insert("global_x",LLSD(global_x));
+    event.insert("global_y",LLSD(global_y));
+  }
+  event.insert("timestamp",LLSD(LLDate::now().asString())); 
+  recordEvent(event);
+
+
+  clear(UNDEFINED);
+   
+
+}
diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h
new file mode 100644
index 0000000000000000000000000000000000000000..190490b51f147523c5059bd98bac0ae510352f3b
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.h
@@ -0,0 +1,78 @@
+#ifndef LL_VIEWER_EVENT_RECORDER
+#define LL_VIEWER_EVENT_RECORDER
+
+
+#include "linden_common.h" 
+
+#include "lldir.h" 
+#include "llsd.h"  
+#include "llfile.h"
+#include "llvfile.h"
+#include "lldate.h"
+#include "llsdserialize.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+
+#include <sstream>
+
+#include "llsingleton.h" // includes llerror which we need here so we can skip the include here
+
+class LLViewerEventRecorder : public LLSingleton<LLViewerEventRecorder>
+{
+
+ public:
+
+  LLViewerEventRecorder(); // TODO Protect constructor better if we can (not happy in private section) - could add a factory... - we are singleton
+  ~LLViewerEventRecorder();
+
+
+  void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y,  std::string mName);
+  void setMouseLocalCoords(S32 x,S32 y);
+  void setMouseGlobalCoords(S32 x,S32 y);
+
+  void logMouseEvent(std::string button_state, std::string button_name );
+  void logKeyEvent(KEY key, MASK mask);
+  void logKeyUnicodeEvent(llwchar uni_char);
+
+  void logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype);
+
+  void clear_xui();
+  std::string get_xui();
+  void update_xui(std::string xui);
+
+  bool getLoggingStatus();
+  void setEventLoggingOn();
+  void setEventLoggingOff();
+
+  void playbackRecording();
+
+  bool displayViewerEventRecorderMenuItems();
+
+
+ protected:
+  // On if we wish to log events at the moment - toggle via Develop/Recorder submenu
+  bool logEvents;
+
+  std::string mLogFilename;
+  llofstream  mLog; 
+
+
+ private:
+
+  // Mouse event info 
+  S32 global_x;
+  S32 global_y;
+  S32 local_x;
+  S32 local_y;
+
+  // XUI path of UI element
+  std::string xui;
+
+  // Actually write the event out to llsd log file
+  void recordEvent(LLSD event);
+
+  void clear(S32 r); 
+
+  static const S32 UNDEFINED=-1;
+};
+#endif
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 97637c937fe6efe6e236dab40183113e6e8b177e..53c7b4ff24b9461a46fd527ca3f72671e2434c52 100755
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -2656,6 +2656,8 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e
 			break;
 
 		case kEventWindowClose:
+			// Note on event recording - QUIT is a known special case and we are choosing NOT to record it for the record and playback feature 
+			// it is handled at a very low-level
 			if(mCallbacks->handleCloseRequest(this))
 			{
 				// Get the app to initiate cleanup.
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 7ab7787d77c874b1b3d075001e3e621023a45664..a6e93edc7978d5d4289c1ab47c88d59112a5ffae 100755
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -164,6 +164,14 @@
       <string>UserLoginInfo</string>
     </map>
 
+    <key>logevents</key>
+    <map>
+      <key>desc</key>
+      <string>Log ui events for later playback</string>
+      <key>map-to</key>
+      <string>LogEvents</string>
+    </map>
+
     <key>logmetrics</key>
     <map>
       <key>desc</key>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 344079b640c7daac52dd476717171f42d6d601de..54624e3f1d6bd48d0bcb898f1418e4c65a90dfd9 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4842,6 +4842,16 @@
       <key>Value</key>
       <array />
     </map>
+    <key>LeapPlaybackEventsCommand</key>
+    <map>
+      <key>Comment</key>
+      <string>Command line to use leap to launch playback of event recordings</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>LLSD</string>
+      <key>Value</key>
+    </map>
     <key>LSLFindCaseInsensitivity</key>
         <map>
         <key>Comment</key>
@@ -10105,6 +10115,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>ShowEventRecorderMenuItems</key>
+    <map>
+      <key>Comment</key>
+      <string>Whether or not Event Recorder menu choices - Start / Stop event recording should appear in the (currently) Develop menu</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>ShowGestureButton</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f92274dbbd84d0ed7b80bab78b745b4355ef61ac..209e91d713f5c635fef1632b6c515305113cdbb4 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -222,6 +222,10 @@
 #include "llmachineid.h"
 #include "llmainlooprepeater.h"
 
+
+#include "llviewereventrecorder.h"
+
+
 // *FIX: These extern globals should be cleaned up.
 // The globals either represent state/config/resource-storage of either 
 // this app, or another 'component' of the viewer. App globals should be 
@@ -696,6 +700,7 @@ LLAppViewer::LLAppViewer() :
 LLAppViewer::~LLAppViewer()
 {
 	delete mSettingsLocationList;
+	LLViewerEventRecorder::instance().~LLViewerEventRecorder();
 
 	LLLoginInstance::instance().setUpdaterService(0);
 	
@@ -2545,6 +2550,10 @@ bool LLAppViewer::initConfiguration()
         }
     }
 
+    if  (clp.hasOption("logevents")) {
+	LLViewerEventRecorder::instance().setEventLoggingOn();
+    }
+
     if(clp.hasOption("channel"))
     {
 		LLVersionInfo::resetChannel(clp.getOption("channel")[0]);
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index 4ecdc31e21e15c481585977da3ab601aad95cc5d..a8e82c6153a15a70622002d83459c09b9edf65d9 100755
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -681,7 +681,10 @@ BOOL LLViewerKeyboard::handleKey(KEY translated_key,  MASK translated_mask, BOOL
 	{
 		// it is sufficient to set this value once per call to handlekey
 		// without clearing it, as it is only used in the subsequent call to scanKey
-		mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
+		mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); 
+		// mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress)
+		// NOT whether some UI shortcut wishes to handle the keypress
+	  
 	}
 	return mKeyHandledByUI[translated_key];
 }
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 5e2f05f4687297dabc2e9dd8156f1ae925d28fab..7bde5d388ed0552f7cbbea0b67986931a7fca257 100755
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -40,6 +40,7 @@
 #include "llinventorypanel.h"
 #include "llnotifications.h"
 #include "llnotificationsutil.h"
+#include "llviewereventrecorder.h"
 
 // newview includes
 #include "llagent.h"
@@ -1950,6 +1951,43 @@ class LLAdvancedDropPacket : public view_listener_t
 };
 
 
+////////////////////
+// EVENT Recorder //
+///////////////////
+
+
+class LLAdvancedViewerEventRecorder : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		std::string command = userdata.asString();
+		if ("start playback" == command)
+		{
+			llinfos << "Event Playback starting" << llendl;
+			LLViewerEventRecorder::instance().playbackRecording();
+			llinfos << "Event Playback completed" << llendl;
+		}
+		else if ("stop playback" == command)
+		{
+			// Future
+		}
+		else if ("start recording" == command)
+		{
+			LLViewerEventRecorder::instance().setEventLoggingOn();
+			llinfos << "Event recording started" << llendl;
+		}
+		else if ("stop recording" == command)
+		{
+			LLViewerEventRecorder::instance().setEventLoggingOff();
+			llinfos << "Event recording stopped" << llendl;
+		} 
+
+		return true;
+	}		
+};
+
+
+
 
 /////////////////
 // AGENT PILOT //
@@ -8320,6 +8358,8 @@ void initialize_menus()
 	// Don't prepend MenuName.Foo because these can be used in any menu.
 	enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service));
 
+	enable.add("displayViewerEventRecorderMenuItems",boost::bind(&LLViewerEventRecorder::displayViewerEventRecorderMenuItems,&LLViewerEventRecorder::instance()));
+
 	view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts");
 
 	enable.add("Conversation.IsConversationLoggingAllowed", boost::bind(&LLFloaterIMContainer::isConversationLoggingAllowed));
@@ -8578,6 +8618,7 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot");
 	view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop");
 	view_listener_t::addMenu(new LLAdvancedCheckAgentPilotLoop(), "Advanced.CheckAgentPilotLoop");
+	view_listener_t::addMenu(new LLAdvancedViewerEventRecorder(), "Advanced.EventRecorder");
 
 	// Advanced > Debugging
 	view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint");
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 65a906d3c0bd2a5f39a5da3bb273d591379863c3..3f2ff7eb82a54a9ca68cc9d139c971851533deed 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -197,6 +197,8 @@
 #include "llagentui.h"
 #include "llwearablelist.h"
 
+#include "llviewereventrecorder.h"
+
 #include "llnotifications.h"
 #include "llnotificationsutil.h"
 #include "llnotificationmanager.h"
@@ -914,27 +916,18 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window,  LLCoordGL pos, MASK
 			{
 				llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl;
 			}
-			return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down);
-		}
 
-		// Topmost view gets a chance before the hierarchy
-		//LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
-		//if (top_ctrl)
-		//{
-		//	S32 local_x, local_y;
-		//	top_ctrl->screenPointToLocal( x, y, &local_x, &local_y );
-		//		if (top_ctrl->pointInView(local_x, local_y))
-		//		{
-		//			return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down)	;
-		//		}
-		//		else
-		//		{
-		//		if (down)
-		//		{
-		//			gFocusMgr.setTopCtrl(NULL);
-		//		}
-		//	}
-		//}
+			BOOL r = mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down); 
+			if (r) {
+
+				lldebugs << "LLViewerWindow::handleAnyMouseClick viewer with mousecaptor calling updatemouseeventinfo - local_x|global x  "<< local_x << " " << x  << "local/global y " << local_y << " " << y << llendl;
+
+				LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y);
+				LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname)); 
+
+			}
+			return r;
+		}
 
 		// Mark the click as handled and return if we aren't within the root view to avoid spurious bugs
 		if( !mRootView->pointInView(x, y) )
@@ -942,27 +935,44 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window,  LLCoordGL pos, MASK
 			return TRUE;
 		}
 		// Give the UI views a chance to process the click
-		if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) )
+
+		BOOL r= mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ;
+		if (r) 
 		{
+
+			lldebugs << "LLViewerWindow::handleAnyMouseClick calling updatemouseeventinfo - global x  "<< " " << x	<< "global y " << y	 << "buttonstate: " << buttonstatestr << " buttonname " << buttonname << llendl;
+
+			LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y);
+
+			// Clear local coords - this was a click on root window so these are not needed
+			// By not including them, this allows the test skeleton generation tool to be smarter when generating code
+			// the code generator can be smarter because when local coords are present it can try the xui path with local coords
+			// and fallback to global coordinates only if needed. 
+			// The drawback to this approach is sometimes a valid xui path will appear to work fine, but NOT interact with the UI element
+			// (VITA support not implemented yet or not visible to VITA due to widget further up xui path not being visible to VITA)
+			// For this reason it's best to provide hints where possible here by leaving out local coordinates
+			LLViewerEventRecorder::instance().setMouseLocalCoords(-1,-1);
+			LLViewerEventRecorder::instance().logMouseEvent(buttonstatestr,buttonname); 
+
 			if (LLView::sDebugMouseHandling)
 			{
-				llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl;
-			}
+				llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLViewerEventRecorder::instance().get_xui()	<< llendl;
+			} 
 			return TRUE;
-		}
-		else if (LLView::sDebugMouseHandling)
-		{
-			llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl;
-		}
+		} else if (LLView::sDebugMouseHandling)
+			{
+				llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl;
+			}
 	}
 
 	// Do not allow tool manager to handle mouseclicks if we have disconnected	
 	if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) )
 	{
+		LLViewerEventRecorder::instance().clear_xui(); 
 		return TRUE;
 	}
-	
 
+	
 	// If we got this far on a down-click, it wasn't handled.
 	// Up-clicks, though, are always handled as far as the OS is concerned.
 	BOOL default_rtn = !down;
@@ -1333,7 +1343,8 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key,  MASK mask)
 void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
 {
 	LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true);
-	return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+	gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+	return; // Be clear this function returns nothing
 }
 
 
@@ -2477,6 +2488,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 		||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE))
 		||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE)))
 	{
+		lldebugs << "LLviewerWindow::handleKey handle nav keys for nav" << llendl;
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 
@@ -2491,12 +2504,14 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 			&& keyboard_focus 
 			&& keyboard_focus->handleKey(key,mask,FALSE))
 		{
+			LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 			return TRUE;
 		}
 
 		if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
 			||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))
 		{
+			LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 			return TRUE;
 		}
 	}
@@ -2506,6 +2521,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 	// if nothing has focus, go to first or last UI element as appropriate
 	if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL))
 	{
+		llwarns << "LLviewerWindow::handleKey give floaters first chance at tab key " << llendl;
 		if (gMenuHolder) gMenuHolder->hideMenus();
 
 		// if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
@@ -2520,11 +2536,13 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 		{
 			mRootView->focusNextRoot();
 		}
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 	// hidden edit menu for cut/copy/paste
 	if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask))
 	{
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 
@@ -2564,18 +2582,27 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 
 		if (keyboard_focus->handleKey(key, mask, FALSE))
 		{
+
+			lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned true" << llendl;
+			LLViewerEventRecorder::instance().logKeyEvent(key,mask); 
 			return TRUE;
+		} else {
+			lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned FALSE" << llendl;
 		}
 	}
 
 	if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) )
 	{
+		lldebugs << "LLviewerWindow::handleKey toolbar handling?" << llendl;
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 
 	// Try for a new-format gesture
 	if (LLGestureMgr::instance().triggerGesture(key, mask))
 	{
+		lldebugs << "LLviewerWindow::handleKey new gesture feature" << llendl;
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 
@@ -2583,6 +2610,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 	// don't pass it down to the menus.
 	if (gGestureList.trigger(key, mask))
 	{
+		lldebugs << "LLviewerWindow::handleKey check gesture trigger" << llendl;
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
 		return TRUE;
 	}
 
@@ -2631,7 +2660,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
 	// HACK: Numeric keypad <enter> on Mac is Unicode 3
 	// HACK: Control-M on Windows is Unicode 13
 	if ((uni_char == 13 && mask != MASK_CONTROL)
-		|| (uni_char == 3 && mask == MASK_NONE))
+	    || (uni_char == 3 && mask == MASK_NONE) )
 	{
 		if (mask != MASK_ALT)
 		{
@@ -2654,14 +2683,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
 			return TRUE;
 		}
 
-		//// Topmost view gets a chance before the hierarchy
-		//LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
-		//if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) )
-		//{
-		//	return TRUE;
-		//}
-
-		return TRUE;
+        return TRUE;
 	}
 
 	return FALSE;
@@ -2670,8 +2692,6 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
 
 void LLViewerWindow::handleScrollWheel(S32 clicks)
 {
-	LLView::sMouseHandlerMessage.clear();
-
 	LLUI::resetMouseIdleTimer();
 	
 	LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp
index 28f959eb7190eda9641da4cd38b3c71ec72691f3..a8e06511d725d64d1b865e476322516df3c00490 100755
--- a/indra/newview/llwindowlistener.cpp
+++ b/indra/newview/llwindowlistener.cpp
@@ -265,7 +265,9 @@ void LLWindowListener::getPaths(LLSD const & request)
 void LLWindowListener::keyDown(LLSD const & evt)
 {
 	Response response(LLSD(), evt);
-	
+	KEY key = getKEY(evt);
+	MASK mask = getMask(evt);
+
 	if (evt.has("path"))
 	{
 		std::string path(evt["path"]);
@@ -280,8 +282,6 @@ void LLWindowListener::keyDown(LLSD const & evt)
 			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);
 		}
@@ -294,7 +294,8 @@ void LLWindowListener::keyDown(LLSD const & evt)
 	}
 	else 
 	{
-		mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt));
+		gViewerKeyboard.handleKey(key, mask, false); 
+		if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
 	}
 }
 
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index b01c3067ff577179ba54528b9c7db7350bcf0978..b71faa2d3e9176dd92e53e65f4638d402a625ae6 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2919,6 +2919,34 @@
          label="Recorder"
          name="Recorder"
          tear_off="true">
+            <menu_item_call visible="false"
+             label="Start event recording"
+             name="Start event recording">
+	      <menu_item_call.on_visible
+		 function="displayViewerEventRecorderMenuItems" />
+                <menu_item_call.on_click
+                 function="Advanced.EventRecorder"
+                 parameter="start recording" />
+            </menu_item_call>
+            <menu_item_call visible="false"
+             label="Stop event recording"
+             name="Stop event recording">
+	      <menu_item_call.on_visible
+		 function="displayViewerEventRecorderMenuItems" />
+                <menu_item_call.on_click
+                 function="Advanced.EventRecorder"
+                 parameter="stop recording" />
+            </menu_item_call>
+            <menu_item_call visible="false"
+             label="Playback event recording"
+             name="Playback event recording">
+	      <menu_item_call.on_visible
+		 function="displayViewerEventRecorderMenuItems" />
+                <menu_item_call.on_click
+                 function="Advanced.EventRecorder"
+                 parameter="start playback" />
+            </menu_item_call>
+
             <menu_item_call
              label="Start Playback"
              name="Start Playback">