From 02ca44eea58b3311f5879309c1e310e893a41b1e Mon Sep 17 00:00:00 2001
From: Kyler Eastridge <felix.wolfz@gmail.com>
Date: Sat, 7 Oct 2023 16:54:30 -0400
Subject: [BUG-234493] Add "Until key release" to the gesture "Wait"

---
 indra/llcharacter/llmultigesture.cpp               |  2 +
 indra/llcharacter/llmultigesture.h                 | 10 +++
 indra/newview/llgesturemgr.cpp                     | 76 ++++++++++++++++++++--
 indra/newview/llgesturemgr.h                       | 11 +++-
 indra/newview/llpreviewgesture.cpp                 | 11 ++++
 indra/newview/llpreviewgesture.h                   |  1 +
 indra/newview/llviewerwindow.cpp                   |  9 +++
 .../default/xui/en/floater_preview_gesture.xml     | 15 ++++-
 8 files changed, 126 insertions(+), 9 deletions(-)

(limited to 'indra')

diff --git a/indra/llcharacter/llmultigesture.cpp b/indra/llcharacter/llmultigesture.cpp
index 7ed242f90a..511d931569 100644
--- a/indra/llcharacter/llmultigesture.cpp
+++ b/indra/llcharacter/llmultigesture.cpp
@@ -68,6 +68,8 @@ void LLMultiGesture::reset()
 	mCurrentStep = 0;
 	mWaitTimer.reset();
 	mWaitingTimer = FALSE;
+	mTriggeredByKey = FALSE;
+	mKeyReleased = FALSE;
 	mWaitingAnimations = FALSE;
 	mWaitingAtEnd = FALSE;
 	mRequestedAnimIDs.clear();
diff --git a/indra/llcharacter/llmultigesture.h b/indra/llcharacter/llmultigesture.h
index 92820159d4..1865ec089c 100644
--- a/indra/llcharacter/llmultigesture.h
+++ b/indra/llcharacter/llmultigesture.h
@@ -83,9 +83,18 @@ public:
 	// We're waiting for triggered animations to stop playing
 	BOOL mWaitingAnimations;
 
+	// We're waiting for key release
+	BOOL mWaitingKeyRelease;
+
 	// We're waiting a fixed amount of time
 	BOOL mWaitingTimer;
 
+	// We're waiting for triggered animations to stop playing
+	BOOL mTriggeredByKey;
+
+	// Has the key been released?
+	BOOL mKeyReleased;
+
 	// Waiting after the last step played for all animations to complete
 	BOOL mWaitingAtEnd;
 
@@ -210,6 +219,7 @@ public:
 
 const U32 WAIT_FLAG_TIME		= 0x01;
 const U32 WAIT_FLAG_ALL_ANIM	= 0x02;
+const U32 WAIT_FLAG_KEY_RELEASE	= 0x04;
 
 class LLGestureStepWait : public LLGestureStep
 {
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index c0f773968d..ce00bf3a54 100644
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -58,6 +58,9 @@
 
 // Longest time, in seconds, to wait for all animations to stop playing
 const F32 MAX_WAIT_ANIM_SECS = 30.f;
+// Longest time, in seconds, to wait for a key release.
+// This should be relatively long, but not too long. 10 minutes is enough
+const F32 MAX_WAIT_KEY_SECS = 60.f * 10.f;
 
 // Lightweight constructor.
 // init() does the heavy lifting.
@@ -528,12 +531,13 @@ void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset
 	LLGestureMgr::instance().replaceGesture(base_item_id, gesture, new_asset_id);
 }
 
-void LLGestureMgr::playGesture(LLMultiGesture* gesture)
+void LLGestureMgr::playGesture(LLMultiGesture* gesture, bool fromKeyPress)
 {
 	if (!gesture) return;
 
 	// Reset gesture to first step
 	gesture->mCurrentStep = 0;
+	gesture->mTriggeredByKey = fromKeyPress;
 
 	// Add to list of playing
 	gesture->mPlaying = TRUE;
@@ -731,7 +735,8 @@ BOOL LLGestureMgr::triggerGesture(KEY key, MASK mask)
 		if (!gesture) continue;
 
 		if (gesture->mKey == key
-			&& gesture->mMask == mask)
+			&& gesture->mMask == mask
+			&& gesture->mWaitingKeyRelease == FALSE)
 		{
 			matching.push_back(gesture);
 		}
@@ -744,13 +749,38 @@ BOOL LLGestureMgr::triggerGesture(KEY key, MASK mask)
 		
 		LLMultiGesture* gesture = matching[random];
 			
-		playGesture(gesture);
+		playGesture(gesture, TRUE);
 		return TRUE;
 	}
 	return FALSE;
 }
 
 
+BOOL LLGestureMgr::triggerGestureRelease(KEY key, MASK mask)
+{
+	std::vector <LLMultiGesture *> matching;
+	item_map_t::iterator it;
+
+	// collect matching gestures
+	for (it = mActive.begin(); it != mActive.end(); ++it)
+	{
+		LLMultiGesture* gesture = (*it).second;
+
+		// asset data might not have arrived yet
+		if (!gesture) continue;
+
+		if (gesture->mKey == key
+			&& gesture->mMask == mask)
+		{
+			gesture->mKeyReleased = TRUE;
+		}
+	}
+	
+	//If we found one, block. Otherwise tell them it's free to go.
+	return matching.size() > 0;
+}
+
+
 S32 LLGestureMgr::getPlayingCount() const
 {
 	return mPlaying.size();
@@ -899,6 +929,33 @@ void LLGestureMgr::stepGesture(LLMultiGesture* gesture)
 			continue;
 		}
 
+		// If we're waiting a fixed amount of time, check for timer
+		// expiration.
+		if (gesture->mWaitingKeyRelease)
+		{
+			// We're waiting for a certain amount of time to pass
+			LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+			if (gesture->mKeyReleased)
+			{
+				// wait is done, continue execution
+				gesture->mWaitingKeyRelease = FALSE;
+				gesture->mCurrentStep++;
+			}
+			else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_KEY_SECS)
+			{
+				LL_INFOS("GestureMgr") << "Waited too long for key release, continuing gesture."
+					<< LL_ENDL;
+				gesture->mWaitingAnimations = FALSE;
+				gesture->mCurrentStep++;
+			}
+			else
+			{
+				// we're waiting, so execution is done for now
+				waiting = TRUE;
+			}
+			continue;
+		}
+
 		// If we're waiting on our animations to stop, poll for
 		// completion.
 		if (gesture->mWaitingAnimations)
@@ -1015,7 +1072,18 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step)
 	case STEP_WAIT:
 		{
 			LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
-			if (wait_step->mFlags & WAIT_FLAG_TIME)
+			if (gesture->mTriggeredByKey // Only wait here IF we were triggered by a key!
+				&& gesture->mKeyReleased == FALSE // We can only do this once! Prevent gestures infinitely running
+				&& wait_step->mFlags & WAIT_FLAG_KEY_RELEASE)
+			{
+				// Lets wait for the key release first so we don't hold up re-presses
+				
+				gesture->mWaitingKeyRelease = TRUE;
+				// Use the wait timer as a deadlock breaker for key release
+				// waits.
+				gesture->mWaitTimer.reset();
+			}
+			else if (wait_step->mFlags & WAIT_FLAG_TIME)
 			{
 				gesture->mWaitingTimer = TRUE;
 				gesture->mWaitTimer.reset();
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index 7c8e8279c2..e805c91145 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -102,7 +102,10 @@ public:
 	const item_map_t& getActiveGestures() const { return mActive; }
 	// Force a gesture to be played, for example, if it is being
 	// previewed.
-	void playGesture(LLMultiGesture* gesture);
+	void playGesture(LLMultiGesture* gesture, bool fromKeyPress);
+	void playGesture(LLMultiGesture* gesture) {
+		playGesture(gesture, FALSE);
+	}
 	void playGesture(const LLUUID& item_id);
 
 	// Stop all requested or playing anims for this gesture
@@ -118,10 +121,14 @@ public:
 	{
 		mCallbackMap[inv_item_id] = cb;
 	}
-	// Trigger the first gesture that matches this key.
+	// Trigger a random gesture that matches this key.
 	// Returns TRUE if it finds a gesture bound to that key.
 	BOOL triggerGesture(KEY key, MASK mask);
 
+	// Trigger release wait on all gestures that matches this key.
+	// Returns TRUE if it finds a gesture bound to that key.
+	BOOL triggerGestureRelease(KEY key, MASK mask);
+
 	// Trigger all gestures referenced as substrings in this string
 	BOOL triggerAndReviseString(const std::string &str, std::string *revised_string = NULL);
 
diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp
index 759e7859f2..5d5010e27e 100644
--- a/indra/newview/llpreviewgesture.cpp
+++ b/indra/newview/llpreviewgesture.cpp
@@ -433,6 +433,11 @@ BOOL LLPreviewGesture::postBuild()
 	edit->setIgnoreTab(TRUE);
 	mChatEditor = edit;
 
+	check = getChild<LLCheckBoxCtrl>( "wait_key_release_check");
+	check->setVisible(FALSE);
+	check->setCommitCallback(onCommitWait, this);
+	mWaitKeyReleaseCheck = check;
+
 	check = getChild<LLCheckBoxCtrl>( "wait_anim_check");
 	check->setVisible(FALSE);
 	check->setCommitCallback(onCommitWait, this);
@@ -638,6 +643,7 @@ void LLPreviewGesture::refresh()
 		mAnimationRadio->setEnabled(FALSE);
 		mSoundCombo->setEnabled(FALSE);
 		mChatEditor->setEnabled(FALSE);
+		mWaitKeyReleaseCheck->setEnabled(FALSE);
 		mWaitAnimCheck->setEnabled(FALSE);
 		mWaitTimeCheck->setEnabled(FALSE);
 		mWaitTimeEditor->setEnabled(FALSE);
@@ -660,6 +666,7 @@ void LLPreviewGesture::refresh()
 	mAnimationRadio->setEnabled(modifiable);
 	mSoundCombo->setEnabled(modifiable);
 	mChatEditor->setEnabled(modifiable);
+	mWaitKeyReleaseCheck->setEnabled(modifiable);
 	mWaitAnimCheck->setEnabled(modifiable);
 	mWaitTimeCheck->setEnabled(modifiable);
 	mWaitTimeEditor->setEnabled(modifiable);
@@ -695,6 +702,7 @@ void LLPreviewGesture::refresh()
 	mAnimationRadio->setVisible(FALSE);
 	mSoundCombo->setVisible(FALSE);
 	mChatEditor->setVisible(FALSE);
+	mWaitKeyReleaseCheck->setVisible(FALSE);
 	mWaitAnimCheck->setVisible(FALSE);
 	mWaitTimeCheck->setVisible(FALSE);
 	mWaitTimeEditor->setVisible(FALSE);
@@ -739,6 +747,8 @@ void LLPreviewGesture::refresh()
 			{
 				LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
 				optionstext = getString("step_wait");
+				mWaitKeyReleaseCheck->setVisible(TRUE);
+				mWaitKeyReleaseCheck->set(wait_step->mFlags & WAIT_FLAG_KEY_RELEASE);
 				mWaitAnimCheck->setVisible(TRUE);
 				mWaitAnimCheck->set(wait_step->mFlags & WAIT_FLAG_ALL_ANIM);
 				mWaitTimeCheck->setVisible(TRUE);
@@ -1516,6 +1526,7 @@ void LLPreviewGesture::onCommitWait(LLUICtrl* ctrl, void* data)
 
 	LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
 	U32 flags = 0x0;
+	if (self->mWaitKeyReleaseCheck->get()) flags |= WAIT_FLAG_KEY_RELEASE;
 	if (self->mWaitAnimCheck->get()) flags |= WAIT_FLAG_ALL_ANIM;
 	if (self->mWaitTimeCheck->get()) flags |= WAIT_FLAG_TIME;
 	wait_step->mFlags = flags;
diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h
index f5c47d71b8..d0fddaf49a 100644
--- a/indra/newview/llpreviewgesture.h
+++ b/indra/newview/llpreviewgesture.h
@@ -154,6 +154,7 @@ private:
 	LLComboBox*			mAnimationCombo;
 	LLComboBox*			mSoundCombo;
 	LLLineEditor*		mChatEditor;
+	LLCheckBoxCtrl*		mWaitKeyReleaseCheck;
 	LLCheckBoxCtrl*		mWaitAnimCheck;
 	LLCheckBoxCtrl*		mWaitTimeCheck;
 	LLLineEditor*		mWaitTimeEditor;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index d68d0cd92c..8012a934c9 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -2867,6 +2867,15 @@ BOOL LLViewerWindow::handleKeyUp(KEY key, MASK mask)
 		}
 	}
 
+	// Try for a new-format gesture
+	if (LLGestureMgr::instance().triggerGestureRelease(key, mask))
+	{
+		LL_DEBUGS() << "LLviewerWindow::handleKey new gesture release feature" << LL_ENDL;
+		LLViewerEventRecorder::instance().logKeyEvent(key,mask);
+		return TRUE;
+	}
+	//Old format gestures do not support this, so no need to implement it.
+
 	// don't pass keys on to world when something in ui has focus
 	return gFocusMgr.childHasKeyboardFocus(mRootView)
 		|| LLMenuGL::getKeyboardMode()
diff --git a/indra/newview/skins/default/xui/en/floater_preview_gesture.xml b/indra/newview/skins/default/xui/en/floater_preview_gesture.xml
index c4ac936334..28b735d297 100644
--- a/indra/newview/skins/default/xui/en/floater_preview_gesture.xml
+++ b/indra/newview/skins/default/xui/en/floater_preview_gesture.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
  legacy_header_height="18"
- height="460"
- min_height="460"
+ height="475"
+ min_height="475"
  layout="topleft"
  name="gesture_preview"
  help_topic="gesture_preview"
@@ -294,6 +294,15 @@
          top_pad="3"
          width="80" />
     </radio_group>
+    <check_box
+     follows="top|left"
+     height="20"
+     label="until key is released"
+     layout="topleft"
+     left="28"
+     name="wait_key_release_check"
+     top="330"
+     width="100" />
     <check_box
      follows="top|left"
      height="20"
@@ -301,7 +310,7 @@
      layout="topleft"
      left="28"
      name="wait_anim_check"
-     top="330"
+     top_delta="20"
      width="100" />
     <check_box
      follows="top|left"
-- 
cgit v1.2.3