From 31e0cefb6c1eabfe931d3b95b585947cce3b21ff Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Thu, 19 Sep 2019 16:57:22 +0300 Subject: [PATCH 001/121] SL-6109 New cell type with icon and text (and 1 pixel offset for all text cells) --- indra/llui/llscrolllistcell.cpp | 146 +++++++++++++++++++++++++++++++- indra/llui/llscrolllistcell.h | 29 ++++++- 2 files changed, 169 insertions(+), 6 deletions(-) diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 8000efad0e..63762ab8b8 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -50,6 +50,10 @@ LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_ { cell = new LLScrollListDate(cell_p); } + else if (cell_p.type() == "icontext") + { + cell = new LLScrollListIconText(cell_p); + } else // default is "text" { cell = new LLScrollListText(cell_p); @@ -168,7 +172,7 @@ U32 LLScrollListText::sCount = 0; LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) : LLScrollListCell(p), - mText(p.value().asString()), + mText(p.text.isProvided() ? p.text() : p.value().asString()), mFont(p.font), mColor(p.color), mUseColor(p.color.isProvided()), @@ -296,7 +300,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col switch(mFontAlignment) { case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); + left = mFont->getWidth(mText.getString(), 1, mHighlightOffset); break; case LLFontGL::RIGHT: left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); @@ -319,7 +323,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col switch(mFontAlignment) { case LLFontGL::LEFT: - start_x = 0.f; + start_x = 1.f; break; case LLFontGL::RIGHT: start_x = (F32)getWidth(); @@ -435,3 +439,139 @@ const LLSD LLScrollListDate::getValue() const { return mDate; } + +// +// LLScrollListIconText +// +LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p) + : LLScrollListText(p), + mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())), + mPad(4) +{ + mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight(); +} + +LLScrollListIconText::~LLScrollListIconText() +{ +} + +const LLSD LLScrollListIconText::getValue() const +{ + if (mIcon.isNull()) + { + return LLStringUtil::null; + } + return mIcon->getName(); +} + +void LLScrollListIconText::setValue(const LLSD& value) +{ + if (value.isUUID()) + { + // don't use default image specified by LLUUID::null, use no image in that case + LLUUID image_id = value.asUUID(); + mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); + } + else + { + std::string value_string = value.asString(); + if (LLUUID::validate(value_string)) + { + setValue(LLUUID(value_string)); + } + else if (!value_string.empty()) + { + mIcon = LLUI::getUIImage(value.asString()); + } + else + { + mIcon = NULL; + } + } +} + +void LLScrollListIconText::setWidth(S32 width) +{ + LLScrollListCell::setWidth(width); + // Assume that iamge height and width is identical to font height and width + mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight(); +} + + +void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + LLColor4 display_color; + if (mUseColor) + { + display_color = mColor; + } + else + { + display_color = color; + } + + S32 icon_height = mFont->getLineHeight(); + S32 icon_space = mIcon ? (icon_height + mPad) : 0; + + if (mHighlightCount > 0) + { + S32 left = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset); + break; + case LLFontGL::RIGHT: + left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space; + break; + case LLFontGL::HCENTER: + left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2; + break; + } + LLRect highlight_rect(left - 2, + mFont->getLineHeight() + 1, + left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, + 1); + mRoundedRectImage->draw(highlight_rect, highlight_color); + } + + // Try to draw the entire string + F32 right_x; + U32 string_chars = mText.length(); + F32 start_text_x = 0.f; + S32 start_icon_x = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + start_text_x = icon_space + 1; + start_icon_x = 1; + break; + case LLFontGL::RIGHT: + start_text_x = (F32)getWidth(); + start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space; + break; + case LLFontGL::HCENTER: + F32 center = (F32)getWidth()* 0.5f; + start_text_x = center + ((F32)icon_space * 0.5f); + start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f); + break; + } + mFont->render(mText.getWString(), 0, + start_text_x, 0.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getTextWidth(), + &right_x, + TRUE); + + if (mIcon) + { + mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor); + } +} + + diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index d625ebddcc..1604a9b1dc 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -59,7 +59,8 @@ public: visible; Optional userdata; - Optional value; + Optional value; // state of checkbox, icon id/name, date + Optional text; // description or text Optional tool_tip; Optional font; @@ -152,7 +153,7 @@ public: void setText(const LLStringExplicit& text); void setFontStyle(const U8 font_style); -private: +protected: LLUIString mText; S32 mTextWidth; const LLFontGL* mFont; @@ -169,7 +170,7 @@ private: }; /* - * Cell displaying an image. + * Cell displaying an image. AT the moment, this is specifically UI image */ class LLScrollListIcon : public LLScrollListCell { @@ -223,4 +224,26 @@ private: LLDate mDate; }; +/* +* Cell displaying icon and text. +*/ + +class LLScrollListIconText : public LLScrollListText +{ +public: + LLScrollListIconText(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListIconText(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setValue(const LLSD& value); + + + S32 getIconWidth() const; + /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/ + +private: + LLPointer mIcon; + S32 mPad; +}; + #endif From 5322f41250851e210ad130cbf7b5fa1c04efb6ce Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Thu, 19 Sep 2019 16:59:23 +0300 Subject: [PATCH 002/121] SL-6109 Extended Key-to-string functionality --- indra/llui/llmenugl.cpp | 10 ++--- indra/llui/llmenugl.h | 13 ++++-- indra/llwindow/llkeyboard.cpp | 77 +++++++++++++++++++---------------- indra/llwindow/llkeyboard.h | 14 ++----- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 5568a84494..c266bec777 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -263,13 +263,13 @@ BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) +BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) { - LLKeyBinding *accelerator = NULL; + LLMenuKeyboardBinding *accelerator = NULL; if (mAcceleratorKey != KEY_NONE) { - std::list::iterator list_it; + std::list::iterator list_it; for (list_it = listp->begin(); list_it != listp->end(); ++list_it) { accelerator = *list_it; @@ -293,7 +293,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) } if (!accelerator) { - accelerator = new LLKeyBinding; + accelerator = new LLMenuKeyboardBinding; if (accelerator) { accelerator->mKey = mAcceleratorKey; @@ -1024,7 +1024,7 @@ BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) +BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) { LLMenuGL* branch = getBranch(); if (!branch) diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 1f11f26192..b47b6a5214 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -42,6 +42,13 @@ extern S32 MENU_BAR_HEIGHT; extern S32 MENU_BAR_WIDTH; +class LLMenuKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemGL // @@ -109,7 +116,7 @@ public: virtual void setBriefItem(BOOL brief); virtual BOOL isBriefItem() const; - virtual BOOL addToAcceleratorList(std::list *listp); + virtual BOOL addToAcceleratorList(std::list *listp); void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } BOOL getAllowKeyRepeat() const { return mAllowKeyRepeat; } @@ -631,7 +638,7 @@ public: virtual BOOL handleAcceleratorKey(KEY key, MASK mask); // check if we've used these accelerators already - virtual BOOL addToAcceleratorList(std::list *listp); + virtual BOOL addToAcceleratorList(std::list *listp); // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -797,7 +804,7 @@ private: void checkMenuTrigger(); - std::list mAccelerators; + std::list mAccelerators; BOOL mAltKeyTrigger; }; diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index f6f6c3931c..8e75325859 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -347,7 +347,48 @@ std::string LLKeyboard::stringFromKey(KEY key) return res; } +//static +std::string LLKeyboard::stringFromAccelerator(MASK accel_mask) +{ + std::string res; + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + + if (trans == NULL) + { + LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; + return res; + } + + // Append any masks +#ifdef LL_DARWIN + // Standard Mac names for modifier keys in menu equivalents + // We could use the symbol characters, but they only exist in certain fonts. + if (accel_mask & MASK_CONTROL) + { + if (accel_mask & MASK_MAC_CONTROL) + { + res.append(trans("accel-mac-control")); + } + else + { + res.append(trans("accel-mac-command")); // Symbol would be "\xE2\x8C\x98" + } + } + if (accel_mask & MASK_ALT) + res.append(trans("accel-mac-option")); // Symbol would be "\xE2\x8C\xA5" + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-mac-shift")); // Symbol would be "\xE2\x8C\xA7" +#else + if (accel_mask & MASK_CONTROL) + res.append(trans("accel-win-control")); + if (accel_mask & MASK_ALT) + res.append(trans("accel-win-alt")); + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-win-shift")); +#endif + return res; +} //static std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) { @@ -359,41 +400,7 @@ std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) return res; } - LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; - - if( trans == NULL ) - { - LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; - return res; - } - - // Append any masks -#ifdef LL_DARWIN - // Standard Mac names for modifier keys in menu equivalents - // We could use the symbol characters, but they only exist in certain fonts. - if( accel_mask & MASK_CONTROL ) - { - if ( accel_mask & MASK_MAC_CONTROL ) - { - res.append( trans("accel-mac-control") ); - } - else - { - res.append( trans("accel-mac-command") ); // Symbol would be "\xE2\x8C\x98" - } - } - if( accel_mask & MASK_ALT ) - res.append( trans("accel-mac-option") ); // Symbol would be "\xE2\x8C\xA5" - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-mac-shift") ); // Symbol would be "\xE2\x8C\xA7" -#else - if( accel_mask & MASK_CONTROL ) - res.append( trans("accel-win-control") ); - if( accel_mask & MASK_ALT ) - res.append( trans("accel-win-alt") ); - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-win-shift") ); -#endif + res.append(stringFromAccelerator(accel_mask)); std::string key_string = LLKeyboard::stringFromKey(key); if ((accel_mask & MASK_NORMALKEYS) && (key_string[0] == '-' || key_string[0] == '=' || key_string[0] == '+')) diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 6f2dc87317..f6404164e7 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -38,10 +38,10 @@ enum EKeystate { KEYSTATE_DOWN, KEYSTATE_LEVEL, - KEYSTATE_UP + KEYSTATE_UP }; -typedef boost::function LLKeyFunc; +typedef boost::function LLKeyFunc; typedef std::string (LLKeyStringTranslatorFunc)(const char *label); enum EKeyboardInsertMode @@ -50,15 +50,6 @@ enum EKeyboardInsertMode LL_KIM_OVERWRITE }; -class LLKeyBinding -{ -public: - KEY mKey; - MASK mMask; -// const char *mName; // unused - LLKeyFunc mFunction; -}; - class LLWindowCallbacks; class LLKeyboard @@ -104,6 +95,7 @@ public: static BOOL maskFromString(const std::string& str, MASK *mask); // False on failure static BOOL keyFromString(const std::string& str, KEY *key); // False on failure static std::string stringFromKey(KEY key); + static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"... static std::string stringFromAccelerator( MASK accel_mask, KEY key ); void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; } From b9294516fc65f7a172ae119e20865b70c43c19c0 Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Thu, 19 Sep 2019 16:55:28 +0300 Subject: [PATCH 003/121] SL-6109 Implement keybindings --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/indra_constants.h | 10 ++ indra/llcommon/llkeybind.cpp | 147 ++++++++++++++++++++++++++ indra/llcommon/llkeybind.h | 70 ++++++++++++ indra/llwindow/llmousehandler.cpp | 2 +- indra/llwindow/llmousehandler.h | 12 +-- indra/llxml/CMakeLists.txt | 1 - indra/llxml/llcontrol.h | 2 - indra/newview/llfloaterpreference.cpp | 14 +-- indra/newview/llfloaterpreference.h | 2 +- indra/newview/lltool.cpp | 2 +- indra/newview/lltool.h | 2 +- indra/newview/lltoolpie.cpp | 2 +- indra/newview/lltoolpie.h | 2 +- indra/newview/llviewerwindow.cpp | 39 ++++--- indra/newview/llviewerwindow.h | 2 +- indra/newview/llvoiceclient.cpp | 8 +- indra/newview/llvoiceclient.h | 2 +- 18 files changed, 268 insertions(+), 53 deletions(-) create mode 100644 indra/llcommon/llkeybind.cpp create mode 100644 indra/llcommon/llkeybind.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index af41b9e460..7e52a620db 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -73,6 +73,7 @@ set(llcommon_SOURCE_FILES llinitparam.cpp llinitdestroyclass.cpp llinstancetracker.cpp + llkeybind.cpp llleap.cpp llleaplistener.cpp llliveappconfig.cpp @@ -183,6 +184,7 @@ set(llcommon_HEADER_FILES llinitdestroyclass.h llinitparam.h llinstancetracker.h + llkeybind.h llkeythrottle.h llleap.h llleaplistener.h diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index e7b0e0ef8e..f8c0232660 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -54,6 +54,16 @@ enum ETerrainBrushType E_LANDBRUSH_INVALID = 6 }; +enum EMouseClickType{ + CLICK_NONE = -1, + CLICK_LEFT = 0, + CLICK_MIDDLE, + CLICK_RIGHT, + CLICK_BUTTON4, + CLICK_BUTTON5, + CLICK_DOUBLELEFT +}; + // keys // Bit masks for various keyboard modifier keys. const MASK MASK_NONE = 0x0000; diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp new file mode 100644 index 0000000000..f227c0a1a5 --- /dev/null +++ b/indra/llcommon/llkeybind.cpp @@ -0,0 +1,147 @@ +/** + * @file llkeybind.cpp + * @brief Information about key combinations. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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 "llkeybind.h" + +#include "llsd.h" +#include "llsdutil.h" + +LLKeyData::LLKeyData() + : mMouse(CLICK_NONE), mKey(KEY_NONE), mMask(MASK_NONE) +{ +} + +LLKeyData::LLKeyData(const LLSD &key_data) +{ + mMouse = (EMouseClickType)key_data["mouse"].asInteger(); + mKey = key_data["key"].asInteger(); + mMask = key_data["mask"].asInteger(); +} + +LLSD LLKeyData::asLLSD() const +{ + LLSD data; + data["mouse"] = (LLSD::Integer)mMouse; + data["key"] = (LLSD::Integer)mKey; + data["mask"] = (LLSD::Integer)mMask; + return data; +} + +bool LLKeyData::isEmpty() const +{ + return mMouse == CLICK_NONE && mKey == KEY_NONE && mMask == MASK_NONE; +} + +void LLKeyData::reset() +{ + mMouse = CLICK_NONE; + mKey = KEY_NONE; + mMask = MASK_NONE; +} + +LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) +{ + mMouse = rhs.mMouse; + mKey = rhs.mKey; + mMask = rhs.mMask; + return *this; +} + +// LLKeyBind + +LLKeyBind::LLKeyBind(const LLSD &key_bind) +{ + if (key_bind.has("DataPrimary")) + { + mDataPrimary = LLKeyData(key_bind["DataPrimary"]); + } + if (key_bind.has("DataSecondary")) + { + mDataSecondary = LLKeyData(key_bind["DataSecondary"]); + } +} + +bool LLKeyBind::operator==(const LLKeyBind& rhs) +{ + if (mDataPrimary.mMouse != rhs.mDataPrimary.mMouse) return false; + if (mDataPrimary.mKey != rhs.mDataPrimary.mKey) return false; + if (mDataPrimary.mMask != rhs.mDataPrimary.mMask) return false; + if (mDataSecondary.mMouse != rhs.mDataSecondary.mMouse) return false; + if (mDataSecondary.mKey != rhs.mDataSecondary.mKey) return false; + if (mDataSecondary.mMask != rhs.mDataSecondary.mMask) return false; + return true; +} + +bool LLKeyBind::empty() +{ + if (mDataPrimary.mMouse != CLICK_NONE) return false; + if (mDataPrimary.mKey != KEY_NONE) return false; + if (mDataPrimary.mMask != MASK_NONE) return false; + if (mDataSecondary.mMouse != CLICK_NONE) return false; + if (mDataSecondary.mKey != KEY_NONE) return false; + if (mDataSecondary.mMask != MASK_NONE) return false; + return false; +} + +LLSD LLKeyBind::asLLSD() const +{ + LLSD data; + if (!mDataPrimary.isEmpty()) + { + data["DataPrimary"] = mDataPrimary.asLLSD(); + } + if (!mDataSecondary.isEmpty()) + { + data["DataSecondary"] = mDataSecondary.asLLSD(); + } + return data; +} + +bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const +{ + if (mDataPrimary.mKey == key && mDataPrimary.mMask == mask && mDataPrimary.mMouse == mouse) + { + return true; + } + if (mDataSecondary.mKey == key && mDataSecondary.mMask == mask && mDataSecondary.mMouse == mouse) + { + return true; + } + return false; +} + +bool LLKeyBind::canHandleKey(KEY key, MASK mask) const +{ + return canHandle(CLICK_NONE, key, mask); +} + +bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const +{ + return canHandle(mouse, KEY_NONE, mask); +} + diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h new file mode 100644 index 0000000000..4fe622fb79 --- /dev/null +++ b/indra/llcommon/llkeybind.h @@ -0,0 +1,70 @@ +/** + * @file llkeybind.h + * @brief Information about key combinations. + * + * $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$ + */ + +#ifndef LL_KEYBIND_H +#define LL_KEYBIND_H + +#include "indra_constants.h" + +// KeyData - single key combination (mouse/mask/keyboard) +class LL_COMMON_API LLKeyData +{ +public: + LLKeyData(); + LLKeyData(const LLSD &key_data); + + LLSD asLLSD() const; + bool isEmpty() const; + void reset(); + LLKeyData& operator=(const LLKeyData& rhs); + + EMouseClickType mMouse; + KEY mKey; + MASK mMask; +}; + +// One function can bind to multiple Key options +class LLKeyBind +{ +public: + LLKeyBind() {} + LLKeyBind(const LLSD &key_bind); + + bool operator==(const LLKeyBind& rhs); + bool empty(); + + LLSD asLLSD() const; + + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; + bool canHandleKey(KEY key, MASK mask) const; + bool canHandleMouse(EMouseClickType mouse, MASK mask) const; + + LLKeyData mDataPrimary; + LLKeyData mDataSecondary; +}; + + +#endif // LL_KEYBIND_H diff --git a/indra/llwindow/llmousehandler.cpp b/indra/llwindow/llmousehandler.cpp index d5fa65fe4b..e41ebd42f3 100644 --- a/indra/llwindow/llmousehandler.cpp +++ b/indra/llwindow/llmousehandler.cpp @@ -27,7 +27,7 @@ #include "llmousehandler.h" //virtual -BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL handled = FALSE; if (down) diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h index 1dcd0348d8..d221dd117c 100644 --- a/indra/llwindow/llmousehandler.h +++ b/indra/llwindow/llmousehandler.h @@ -29,6 +29,7 @@ #include "linden_common.h" #include "llrect.h" +#include "indra_constants.h" // Mostly-abstract interface. // Intended for use via multiple inheritance. @@ -46,16 +47,7 @@ public: SHOW_ALWAYS, } EShowToolTip; - typedef enum { - CLICK_LEFT, - CLICK_MIDDLE, - CLICK_RIGHT, - CLICK_BUTTON4, - CLICK_BUTTON5, - CLICK_DOUBLELEFT - } EClickType; - - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask) = 0; diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index 17400a203e..013a422d35 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -28,7 +28,6 @@ set(llxml_HEADER_FILES CMakeLists.txt llcontrol.h - llcontrolgroupreader.h llxmlnode.h llxmlparser.h llxmltree.h diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index f136918896..e2dac5a1de 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -34,8 +34,6 @@ #include "llrefcount.h" #include "llinstancetracker.h" -#include "llcontrolgroupreader.h" - #include // *NOTE: boost::visit_each<> generates warning 4675 on .net 2003 diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 951d11bbe5..95c255d697 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -170,7 +170,7 @@ public: void setParent(LLFloaterPreference* parent) { mParent = parent; } BOOL handleKeyHere(KEY key, MASK mask); - BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); static void onCancel(void* user_data); private: @@ -214,11 +214,11 @@ BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) return result; } -BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = FALSE; if (down - && (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) + && (clicktype == CLICK_MIDDLE || clicktype == CLICK_BUTTON4 || clicktype == CLICK_BUTTON5) && mask == 0) { mParent->setMouse(clicktype); @@ -1674,21 +1674,21 @@ void LLFloaterPreference::setKey(KEY key) getChild("modifier_combo")->onCommit(); } -void LLFloaterPreference::setMouse(LLMouseHandler::EClickType click) +void LLFloaterPreference::setMouse(EMouseClickType click) { std::string bt_name; std::string ctrl_value; switch (click) { - case LLMouseHandler::CLICK_MIDDLE: + case CLICK_MIDDLE: bt_name = "middle_mouse"; ctrl_value = MIDDLE_MOUSE_CV; break; - case LLMouseHandler::CLICK_BUTTON4: + case CLICK_BUTTON4: bt_name = "button4_mouse"; ctrl_value = MOUSE_BUTTON_4_CV; break; - case LLMouseHandler::CLICK_BUTTON5: + case CLICK_BUTTON5: bt_name = "button5_mouse"; ctrl_value = MOUSE_BUTTON_5_CV; break; diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 526214a617..0bbfdc7c17 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -148,7 +148,7 @@ public: void onSelectSkin(); void onClickSetKey(); void setKey(KEY key); - void setMouse(LLMouseHandler::EClickType click); + void setMouse(EMouseClickType click); void onClickSetMiddleMouse(); void onClickSetSounds(); void onClickEnablePopup(); diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index c5e31ff8e6..0038138078 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -60,7 +60,7 @@ LLTool::~LLTool() } } -BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index 308983afda..41a38804ce 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -49,7 +49,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index aeb8bdc496..3879acefa6 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -91,7 +91,7 @@ LLToolPie::LLToolPie() { } -BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index fe0acfe473..6d0e25eaeb 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -42,7 +42,7 @@ class LLToolPie : public LLTool, public LLSingleton public: // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 44d02b4224..822dc20692 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -914,7 +914,7 @@ LLViewerWindow::Params::Params() {} -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) { const char* buttonname = ""; const char* buttonstatestr = ""; @@ -923,6 +923,8 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK x = ll_round((F32)x / mDisplayScale.mV[VX]); y = ll_round((F32)y / mDisplayScale.mV[VY]); + LLVoiceClient::getInstance()->updateMouseState(clicktype, mask, down); + // only send mouse clicks to UI if UI is visible if(gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) { @@ -938,26 +940,26 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK switch (clicktype) { - case LLMouseHandler::CLICK_LEFT: + case CLICK_LEFT: mLeftMouseDown = down; buttonname = "Left"; break; - case LLMouseHandler::CLICK_RIGHT: + case CLICK_RIGHT: mRightMouseDown = down; buttonname = "Right"; break; - case LLMouseHandler::CLICK_MIDDLE: + case CLICK_MIDDLE: mMiddleMouseDown = down; buttonname = "Middle"; break; - case LLMouseHandler::CLICK_DOUBLELEFT: + case CLICK_DOUBLELEFT: mLeftMouseDown = down; buttonname = "Left Double Click"; break; - case LLMouseHandler::CLICK_BUTTON4: + case CLICK_BUTTON4: buttonname = "Button 4"; break; - case LLMouseHandler::CLICK_BUTTON5: + case CLICK_BUTTON5: buttonname = "Button 5"; break; } @@ -1075,7 +1077,7 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask mMouseDownTimer.reset(); } BOOL down = TRUE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + return handleAnyMouseClick(window, pos, mask, CLICK_LEFT, down); } BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1083,8 +1085,7 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // try handling as a double-click first, then a single-click if that // wasn't handled. BOOL down = TRUE; - if (handleAnyMouseClick(window, pos, mask, - LLMouseHandler::CLICK_DOUBLELEFT, down)) + if (handleAnyMouseClick(window, pos, mask, CLICK_DOUBLELEFT, down)) { return TRUE; } @@ -1098,7 +1099,7 @@ BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) mMouseDownTimer.stop(); } BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + return handleAnyMouseClick(window, pos, mask, CLICK_LEFT, down); } @@ -1110,7 +1111,7 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK y = ll_round((F32)y / mDisplayScale.mV[VY]); BOOL down = TRUE; - BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + BOOL handle = handleAnyMouseClick(window, pos, mask, CLICK_RIGHT, down); if (handle) return handle; @@ -1131,14 +1132,13 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + return handleAnyMouseClick(window, pos, mask, CLICK_RIGHT, down); } BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, true); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + handleAnyMouseClick(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1293,8 +1293,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, false); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + handleAnyMouseClick(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1305,12 +1304,10 @@ BOOL LLViewerWindow::handleOtherMouse(LLWindow *window, LLCoordGL pos, MASK mask switch (button) { case 4: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON4, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON4, down); + handleAnyMouseClick(window, pos, mask, CLICK_BUTTON4, down); break; case 5: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON5, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON5, down); + handleAnyMouseClick(window, pos, mask, CLICK_BUTTON5, down); break; default: break; diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 44c1fbd066..2e1a17596f 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -176,7 +176,7 @@ public: void setUIVisibility(bool); bool getUIVisibility(); - BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); // // LLWindowCallback interface implementation diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index cc590fc947..3fc41ddacc 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -642,15 +642,15 @@ void LLVoiceClient::setPTTKey(std::string &key) // Value is stored as text for readability if(key == "MiddleMouse") { - mPTTMouseButton = LLMouseHandler::CLICK_MIDDLE; + mPTTMouseButton = CLICK_MIDDLE; } else if(key == "MouseButton4") { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON4; + mPTTMouseButton = CLICK_BUTTON4; } else if (key == "MouseButton5") { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON5; + mPTTMouseButton = CLICK_BUTTON5; } else { @@ -712,7 +712,7 @@ void LLVoiceClient::keyUp(KEY key, MASK mask) } } } -void LLVoiceClient::updateMouseState(S32 click, bool down) +void LLVoiceClient::updateMouseState(S32 click, MASK mask, bool down) { if(mPTTMouseButton == click && LLAgent::isActionAllowed("speak")) { diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 3d04e1f0db..12916ab71b 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -417,7 +417,7 @@ public: // PTT key triggering void keyDown(KEY key, MASK mask); void keyUp(KEY key, MASK mask); - void updateMouseState(S32 click, bool down); + void updateMouseState(S32 click, MASK mask, bool down); boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } From 3633ccf1a1077d3e53e7f125d1dd5ed2aaa98906 Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Mon, 19 Aug 2019 23:16:29 +0300 Subject: [PATCH 004/121] SL_6109 Rebinding --- indra/newview/llfloaterpreference.cpp | 330 +++++++++++++++++- indra/newview/llfloaterpreference.h | 21 ++ indra/newview/llspatialpartition.cpp | 2 +- indra/newview/llvoiceclient.cpp | 7 +- .../default/xui/en/floater_preferences.xml | 7 + .../xui/en/panel_preferences_controls.xml | 196 +++++++++++ 6 files changed, 553 insertions(+), 10 deletions(-) create mode 100644 indra/newview/skins/default/xui/en/panel_preferences_controls.xml diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 95c255d697..38ec8805a1 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -52,6 +52,7 @@ #include "llfavoritesbar.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterimsession.h" +#include "llkeybindings.h" #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" @@ -168,6 +169,7 @@ public: /*virtual*/ BOOL postBuild(); void setParent(LLFloaterPreference* parent) { mParent = parent; } + void setTmpParent(LLPanelPreferenceControls* parent) { mTmpParent = parent; } // todo: voice key will be removed, class renamved, so it will have only one parent BOOL handleKeyHere(KEY key, MASK mask); BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); @@ -175,11 +177,13 @@ public: private: LLFloaterPreference* mParent; + LLPanelPreferenceControls* mTmpParent;// todo: voice key will be removed, class renamved, so it will have only one parent }; LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key) : LLModalDialog(key), - mParent(NULL) + mParent(NULL), + mTmpParent(NULL) { } @@ -201,15 +205,30 @@ LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog() BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) { BOOL result = TRUE; + + if (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT || key == KEY_NONE) + { + // temp + return false; + } + // todo, same for escape if (key == 'Q' && mask == MASK_CONTROL) { result = FALSE; - } + if (mTmpParent) + { + mTmpParent->onSetKey(KEY_NONE, MASK_NONE); + } + } else if (mParent) { mParent->setKey(key); } + else if (mTmpParent) + { + mTmpParent->onSetKey(key, mask); + } closeFloater(); return result; } @@ -217,11 +236,38 @@ BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = FALSE; - if (down - && (clicktype == CLICK_MIDDLE || clicktype == CLICK_BUTTON4 || clicktype == CLICK_BUTTON5) - && mask == 0) + + if (clicktype == CLICK_LEFT) { - mParent->setMouse(clicktype); + if (down) + { + result = LLView::handleMouseDown(x, y, mask); + } + else + { + result = LLView::handleMouseUp(x, y, mask); + } + } + if (clicktype == LLMouseHandler::CLICK_LEFT) + { + result = LLView::handleDoubleClick(x, y, mask); + } + if (result) + { + return TRUE; + } + if (down && clicktype != LLMouseHandler::CLICK_RIGHT) //tmp + //&& (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) + //&& mask == 0) + { + if (mParent) + { + mParent->setMouse(clicktype); + } + else if (mTmpParent) + { + mTmpParent->onSetMouse(clicktype, mask); + } result = TRUE; closeFloater(); } @@ -237,6 +283,11 @@ BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseCli void LLVoiceSetKeyDialog::onCancel(void* user_data) { LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data; + // tmp needs 'no key' button + if (self->mTmpParent) + { + self->mTmpParent->onSetKey(KEY_NONE, MASK_NONE); + } self->closeFloater(); } @@ -2863,6 +2914,273 @@ void LLPanelPreferenceGraphics::setHardwareDefaults() resetDirtyChilds(); } + +//-------------------For LLPanelPreferenceControls' list--------------------------- +class LLGroupControlsListItem : public LLScrollListItem, public LLHandleProvider +{ +public: + LLGroupControlsListItem(const LLScrollListItem::Params& p) + : LLScrollListItem(p) + { + } + + LLGroupControlsListItem(const LLScrollListItem::Params& p, const LLUUID& icon_id) + : LLScrollListItem(p), mIconId(icon_id) + { + } + + void draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) + { + // todo: insert image and adjust rect + LLScrollListItem::draw(rect, fg_color, bg_color, highlight_color, column_padding); + } +private: + LLUUID mIconId; +}; + +static const std::string tmp_typetostring[LLControlBindings::CONTROL_NUM_INDICES] = { + "control_view_actions", + "control_about", + "control_orbit", + "control_pan", + "control_world_map", + "control_zoom", + "control_interactions", + "control_build", + "control_drag", + "control_edit", + "control_menu", + "control_open", + "control_touch", + "control_wear", + "control_movements", + "control_moveto", + "control_sit", + "control_teleportto", + "control_forward", + "control_backward", + "control_left", + "control_right", + "control_lstrafe", + "control_rstrafe", + "control_jump", + "control_down", + "control_run", + "control_toggle_run", + "control_fly", + "control_mediacontent", + "control_parcel", + "control_media", + "control_voice", + "control_toggle_voice", + "control_reserved", + "control_menu", + "control_reserved_select", + "control_shift_select", + "control_cntrl_select" +}; + +//------------------------LLPanelPreferenceControls-------------------------------- +static LLPanelInjector t_pref_contrls("panel_preference_controls"); + +BOOL LLPanelPreferenceControls::postBuild() +{ + //todo: on open instead of on the go + //todo: add pitch/yaw? + //todo: Scroll? + //todo: should be auto-expandable with menu items and should pull names from menu when possible + + + // populate list of controls + pControlsTable = getChild("controls_list"); + populateControlTable(); + + pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); + + return TRUE; +} + +void LLPanelPreferenceControls::populateControlTable() +{ + pControlsTable->clearRows(); + + // todo: subsections need sorting? + std::string label; + LLScrollListCell::Params cell_params; + // init basic cell params + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.font_halign = LLFontGL::LEFT; + cell_params.column = ""; + cell_params.value = label; + + LLScrollListItem::Params item_params_blank; + cell_params.enabled = false; + item_params_blank.value = LLSD::Integer(-1); + item_params_blank.columns.add(cell_params); + cell_params.enabled = true; + + for (U32 i = LLControlBindings::CONTROL_VIEW_ACTIONS; i < LLControlBindings::CONTROL_NUM_INDICES; i++) + { + switch (i) + { + case LLControlBindings::CONTROL_VIEW_ACTIONS: + { + // same as below, but without separator + LLScrollListItem::Params item_params; + item_params.value = LLSD::Integer(i); + + label = getString(tmp_typetostring[i]); + cell_params.column = "lst_action"; + cell_params.value = label; + //dummy cells + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl1"; + cell_params.value = ""; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl2"; + cell_params.value = ""; + item_params.columns.add(cell_params); + LLUUID id; + LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id); + pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM); + break; + } + case LLControlBindings::CONTROL_INTERACTIONS: + case LLControlBindings::CONTROL_MOVEMENTS: + case LLControlBindings::CONTROL_MEDIACONTENT: + case LLControlBindings::CONTROL_RESERVED: + { + // insert blank + pControlsTable->addRow(item_params_blank, EAddPosition::ADD_BOTTOM); + // inster with icon + LLScrollListItem::Params item_params; + item_params.value = LLSD::Integer(i); + + label = getString(tmp_typetostring[i]); + cell_params.column = "lst_action"; + cell_params.value = label; + //dummy cells + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl1"; + cell_params.value = ""; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl2"; + cell_params.value = ""; + item_params.columns.add(cell_params); + LLUUID id; + LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id); + pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM); + break; + } + default: + { + //default insert + LLScrollListItem::Params item_params; + item_params.value = LLSD::Integer(i); + + // todo oddset + cell_params.column = "lst_action"; + label = getString(tmp_typetostring[i]); + cell_params.value = label; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl1"; + cell_params.value = gControlBindings.getPrimaryControl((LLControlBindings::EControlTypes)i).asString(); + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl2"; + cell_params.value = gControlBindings.getSecondaryControl((LLControlBindings::EControlTypes)i).asString(); + item_params.columns.add(cell_params); + + pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); + break; + } + } + } +} + +void LLPanelPreferenceControls::cancel() +{ +} + +void LLPanelPreferenceControls::saveSettings() +{ +} + +void LLPanelPreferenceControls::resetDirtyChilds() +{ +} + +bool LLPanelPreferenceControls::hasDirtyChilds() +{ + return false; +} + +void LLPanelPreferenceControls::onListCommit() +{ + LLScrollListItem* item = pControlsTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + S32 control = item->getValue().asInteger(); + + if (!gControlBindings.canAssignControl((LLControlBindings::EControlTypes)control)) + { + return; + } + + // todo: add code to determine what cell was clicked, probably cells themself should be clickable + + LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance("voice_set_key", LLSD(), TRUE); + if (dialog) + { + dialog->setTmpParent(this); // will be remade from being voice later + } +} + +void LLPanelPreferenceControls::onSetKey(KEY key, MASK mask) +{ + LLScrollListItem* item = pControlsTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger(); + + if (!gControlBindings.canAssignControl(control)) + { + return; + } + + gControlBindings.registerPrimaryControl(control, LLMouseHandler::CLICK_NONE, key, mask); + + // instead of populating, update single element + populateControlTable(); +} + +void LLPanelPreferenceControls::onSetMouse(LLMouseHandler::EClickType click, MASK mask) +{ + + LLScrollListItem* item = pControlsTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger(); + + if (!gControlBindings.canAssignControl(control)) + { + return; + } + + gControlBindings.registerPrimaryControl(control, click, KEY_NONE, mask); + + // instead of populating, update single element + populateControlTable(); +} + LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key) : LLFloater(key) { diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 0bbfdc7c17..bfccd64624 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -294,6 +294,27 @@ private: LOG_CLASS(LLPanelPreferenceGraphics); }; +class LLPanelPreferenceControls : public LLPanelPreference +{ + LOG_CLASS(LLPanelPreferenceControls); +public: + BOOL postBuild(); + void populateControlTable(); + void cancel(); + void saveSettings(); + void resetDirtyChilds(); + + void onListCommit(); + void onSetKey(KEY key, MASK mask); + void onSetMouse(LLMouseHandler::EClickType click, MASK mask); + +protected: + bool hasDirtyChilds(); + +private: + LLScrollListCtrl* pControlsTable; +}; + class LLFloaterPreferenceGraphicsAdvanced : public LLFloater { public: diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 77bbcdada6..be8a9a5d2d 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -994,7 +994,7 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible) } LLSpatialGroup* group = drawablep->getSpatialGroup(); - llassert(group != NULL); + //llassert(group != NULL); if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING)) { diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 3fc41ddacc..8951fcf368 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -35,6 +35,7 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llui.h" +#include "llkeybindings.h" #include "llkeyboard.h" #include "llagent.h" @@ -691,7 +692,7 @@ void LLVoiceClient::keyDown(KEY key, MASK mask) return; } - if (mPTTMouseButton == 0 && LLAgent::isActionAllowed("speak") && (key == mPTTKey)) + if (LLAgent::isActionAllowed("speak") && gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask)) { bool down = gKeyboard->getKeyDown(mPTTKey); if (down) @@ -703,7 +704,7 @@ void LLVoiceClient::keyDown(KEY key, MASK mask) } void LLVoiceClient::keyUp(KEY key, MASK mask) { - if (mPTTMouseButton == 0 && (key == mPTTKey)) + if (gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask)) { bool down = gKeyboard->getKeyDown(mPTTKey); if (!down) @@ -714,7 +715,7 @@ void LLVoiceClient::keyUp(KEY key, MASK mask) } void LLVoiceClient::updateMouseState(S32 click, MASK mask, bool down) { - if(mPTTMouseButton == click && LLAgent::isActionAllowed("speak")) + if(LLAgent::isActionAllowed("speak") && gControlBindings.canHandleMouse(LLControlBindings::CONTROL_VOICE, click, mask)) { inputUserControlState(down); } diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index 0e62d50072..ee730dcb01 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -169,6 +169,13 @@ https://accounts.secondlife.com/change_email/ layout="topleft" help_topic="preferences_uploads_tab" name="uploads" /> + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml new file mode 100644 index 0000000000..a1a2fd0598 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml @@ -0,0 +1,196 @@ + + + + View Actions + + + About/Profile + + + Orbit + + + Pan + + + World Map + + + Zoom + + + Interactions + + + Build + + + Drag + + + Edit + + + Menu + + + Open + + + Touch + + + Wear + + + Move Actions + + + Move To + + + Sit/Stand + + + Teleport To + + + Move Forward + + + Move Backward + + + Left + + + Right + + + + Strafe left + + + Strafe right + + + Strafe right + + + Strafe right + + + Run + + + Toggle Run + + + Fly/Stop flying + + + Sound and Media + + + Play/Pause Parcel Media + + + Play/Stop All Media + + + Voice + + + Toggle Voice + + + Reserved Controls + + + + Select + + + Multi-Select + + + Add to Selection + + + + + + + + + + From 4df05c5a8995158922c7b7aacfef442ac8ae6fdd Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Tue, 17 Sep 2019 21:36:59 +0300 Subject: [PATCH 005/121] SL-6109 Keyaboard support ready --- indra/llcommon/llkeybind.cpp | 180 +++- indra/llcommon/llkeybind.h | 23 +- indra/llui/llscrolllistctrl.cpp | 4 + indra/llui/llscrolllistctrl.h | 4 +- indra/newview/CMakeLists.txt | 2 + indra/newview/llfloaterpreference.cpp | 696 ++++++++------- indra/newview/llfloaterpreference.h | 31 +- indra/newview/llkeyconflict.cpp | 789 ++++++++++++++++++ indra/newview/llkeyconflict.h | 214 +++++ indra/newview/llviewerkeyboard.cpp | 56 +- indra/newview/llviewerkeyboard.h | 6 +- indra/newview/llvoiceclient.cpp | 18 +- .../default/xui/en/floater_select_key.xml | 42 +- .../xui/en/panel_preferences_controls.xml | 161 +++- 14 files changed, 1807 insertions(+), 419 deletions(-) create mode 100644 indra/newview/llkeyconflict.cpp create mode 100644 indra/newview/llkeyconflict.h diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp index f227c0a1a5..765084bbf6 100644 --- a/indra/llcommon/llkeybind.cpp +++ b/indra/llcommon/llkeybind.cpp @@ -36,6 +36,11 @@ LLKeyData::LLKeyData() { } +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask) +: mMouse(mouse), mKey(key), mMask(mask) +{ +} + LLKeyData::LLKeyData(const LLSD &key_data) { mMouse = (EMouseClickType)key_data["mouse"].asInteger(); @@ -72,65 +77,100 @@ LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) return *this; } +bool LLKeyData::operator==(const LLKeyData& rhs) +{ + if (mMouse != rhs.mMouse) return false; + if (mKey != rhs.mKey) return false; + if (mMask != rhs.mMask) return false; + return true; +} + +bool LLKeyData::operator!=(const LLKeyData& rhs) +{ + if (mMouse != rhs.mMouse) return true; + if (mKey != rhs.mKey) return true; + if (mMask != rhs.mMask) return true; + return false; +} + // LLKeyBind LLKeyBind::LLKeyBind(const LLSD &key_bind) { - if (key_bind.has("DataPrimary")) + if (key_bind.isArray()) { - mDataPrimary = LLKeyData(key_bind["DataPrimary"]); - } - if (key_bind.has("DataSecondary")) - { - mDataSecondary = LLKeyData(key_bind["DataSecondary"]); + for (LLSD::array_const_iterator data = key_bind.beginArray(), endLists = key_bind.endArray(); + data != endLists; + data++ + ) + { + mData.push_back(LLKeyData(*data)); + } } } bool LLKeyBind::operator==(const LLKeyBind& rhs) { - if (mDataPrimary.mMouse != rhs.mDataPrimary.mMouse) return false; - if (mDataPrimary.mKey != rhs.mDataPrimary.mKey) return false; - if (mDataPrimary.mMask != rhs.mDataPrimary.mMask) return false; - if (mDataSecondary.mMouse != rhs.mDataSecondary.mMouse) return false; - if (mDataSecondary.mKey != rhs.mDataSecondary.mKey) return false; - if (mDataSecondary.mMask != rhs.mDataSecondary.mMask) return false; + U32 size = mData.size(); + if (size != rhs.mData.size()) return false; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return false; + } + return true; } -bool LLKeyBind::empty() +bool LLKeyBind::operator!=(const LLKeyBind& rhs) { - if (mDataPrimary.mMouse != CLICK_NONE) return false; - if (mDataPrimary.mKey != KEY_NONE) return false; - if (mDataPrimary.mMask != MASK_NONE) return false; - if (mDataSecondary.mMouse != CLICK_NONE) return false; - if (mDataSecondary.mKey != KEY_NONE) return false; - if (mDataSecondary.mMask != MASK_NONE) return false; + U32 size = mData.size(); + if (size != rhs.mData.size()) return true; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return true; + } + return false; } +bool LLKeyBind::isEmpty() const +{ + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (!iter->isEmpty()) return false; + } + return true; +} + LLSD LLKeyBind::asLLSD() const { LLSD data; - if (!mDataPrimary.isEmpty()) + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) { - data["DataPrimary"] = mDataPrimary.asLLSD(); - } - if (!mDataSecondary.isEmpty()) - { - data["DataSecondary"] = mDataSecondary.asLLSD(); + if (!iter->isEmpty()) + { + data.append(iter->asLLSD()); + } } return data; } bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const { - if (mDataPrimary.mKey == key && mDataPrimary.mMask == mask && mDataPrimary.mMouse == mouse) + if (mouse == CLICK_NONE && key == KEY_NONE) { - return true; + // assume placeholder + return false; } - if (mDataSecondary.mKey == key && mDataSecondary.mMask == mask && mDataSecondary.mMouse == mouse) + + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) { - return true; + if (iter->mKey == key && iter->mMask == mask && iter->mMouse == mouse) + { + return true; + } } return false; } @@ -145,3 +185,85 @@ bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const return canHandle(mouse, KEY_NONE, mask); } +bool LLKeyBind::addKeyData(EMouseClickType mouse, KEY key, MASK mask) +{ + if (!canHandle(mouse, key, mask)) + { + mData.push_back(LLKeyData(mouse, key, mask)); + return true; + } + return false; +} + +bool LLKeyBind::addKeyData(const LLKeyData& data) +{ + if (!canHandle(data.mMouse, data.mKey, data.mMask)) + { + mData.push_back(data); + return true; + } + return false; +} + +void LLKeyBind::replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, U32 index) +{ + if (mouse != CLICK_NONE && key != KEY_NONE && mask != MASK_NONE) + { + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == key && iter->mMask == mask && iter->mMouse == mouse) + { + mData.erase(iter); + break; + } + } + } + if (mData.size() > index) + { + mData[index] = LLKeyData(mouse, key, mask); + } + else + { + mData.push_back(LLKeyData(mouse, key, mask)); + } +} + +void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index) +{ + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == data.mKey && iter->mMask == data.mMask && iter->mMouse == data.mMouse) + { + mData.erase(iter); + break; + } + } + if (mData.size() > index) + { + mData[index] = data; + } + else + { + mData.push_back(data); + } +} + +bool LLKeyBind::hasKeyData(U32 index) const +{ + return mData.size() > index; +} + +LLKeyData LLKeyBind::getKeyData(U32 index) const +{ + if (mData.size() > index) + { + return mData[index]; + } + return LLKeyData(); +} + +U32 LLKeyBind::getDataCount() +{ + return mData.size(); +} + diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h index 4fe622fb79..481949f275 100644 --- a/indra/llcommon/llkeybind.h +++ b/indra/llcommon/llkeybind.h @@ -34,12 +34,16 @@ class LL_COMMON_API LLKeyData { public: LLKeyData(); + LLKeyData(EMouseClickType mouse, KEY key, MASK mask); LLKeyData(const LLSD &key_data); LLSD asLLSD() const; bool isEmpty() const; + bool empty() const { return isEmpty(); }; void reset(); LLKeyData& operator=(const LLKeyData& rhs); + bool operator==(const LLKeyData& rhs); + bool operator!=(const LLKeyData& rhs); EMouseClickType mMouse; KEY mKey; @@ -54,7 +58,9 @@ public: LLKeyBind(const LLSD &key_bind); bool operator==(const LLKeyBind& rhs); - bool empty(); + bool operator!=(const LLKeyBind& rhs); + bool isEmpty() const; + bool empty() const { return isEmpty(); }; LLSD asLLSD() const; @@ -62,8 +68,19 @@ public: bool canHandleKey(KEY key, MASK mask) const; bool canHandleMouse(EMouseClickType mouse, MASK mask) const; - LLKeyData mDataPrimary; - LLKeyData mDataSecondary; + // these methods enshure there will be no repeats + bool addKeyData(EMouseClickType mouse, KEY key, MASK mask); + bool addKeyData(const LLKeyData& data); + void replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, U32 index); + void replaceKeyData(const LLKeyData& data, U32 index); + bool hasKeyData(U32 index) const; + void clear() { mData.clear(); }; + LLKeyData getKeyData(U32 index) const; + U32 getDataCount(); + +private: + typedef std::vector data_vector_t; + data_vector_t mData; }; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 763c3aeb81..6c30bdde17 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -130,6 +130,7 @@ LLScrollListCtrl::Params::Params() search_column("search_column", 0), sort_column("sort_column", -1), sort_ascending("sort_ascending", true), + can_sort("can_sort", true), mouse_wheel_opaque("mouse_wheel_opaque", false), commit_on_keyboard_movement("commit_on_keyboard_movement", true), heading_height("heading_height"), @@ -166,6 +167,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mSelectionChanged(false), mNeedsScroll(false), mCanSelect(true), + mCanSort(p.can_sort), mColumnsDirty(false), mMaxItemCount(INT_MAX), mBorderThickness( 2 ), @@ -2801,6 +2803,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata) LLScrollListCtrl *parent = info->mParentCtrl; if (!parent) return; + if (!parent->mCanSort) return; + S32 column_index = info->mIndex; LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 43e1c0d707..edfbaa6548 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -115,7 +115,8 @@ public: // sort and search behavior Optional search_column, sort_column; - Optional sort_ascending; + Optional sort_ascending, + can_sort; // whether user is allowed to sort // colors Optional fg_unselected_color, @@ -460,6 +461,7 @@ private: bool mNeedsScroll; bool mMouseWheelOpaque; bool mCanSelect; + bool mCanSort; // Whether user is allowed to sort bool mDisplayColumnHeaders; bool mColumnsDirty; bool mColumnWidthsDirty; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 33fa186a2e..340af8ece0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -376,6 +376,7 @@ set(viewer_SOURCE_FILES llinventoryobserver.cpp llinventorypanel.cpp lljoystickbutton.cpp + llkeyconflict.cpp lllandmarkactions.cpp lllandmarklist.cpp lllegacyatmospherics.cpp @@ -1007,6 +1008,7 @@ set(viewer_HEADER_FILES llinventoryobserver.h llinventorypanel.h lljoystickbutton.h + llkeyconflict.h lllandmarkactions.h lllandmarklist.h lllightconstants.h diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 38ec8805a1..34cfe6ebb4 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -52,7 +52,6 @@ #include "llfavoritesbar.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterimsession.h" -#include "llkeybindings.h" #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" @@ -72,8 +71,9 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llviewercamera.h" -#include "llviewerwindow.h" +#include "llviewereventrecorder.h" #include "llviewermessage.h" +#include "llviewerwindow.h" #include "llviewershadermgr.h" #include "llviewerthrottle.h" #include "llvoavatarself.h" @@ -160,36 +160,49 @@ struct LabelTable : public LLInitParam::Block {} }; -class LLVoiceSetKeyDialog : public LLModalDialog +// Filters for LLSetKeyBindDialog +static const U32 ALLOW_MOUSE = 1; +static const U32 ALLOW_MASK_MOUSE = 2; +static const U32 ALLOW_KEYS = 4; //keyboard +static const U32 ALLOW_MASK_KEYS = 8; +static const U32 ALLOW_MASKS = 16; +static const U32 IGNORE_MASKS = 32; // For example W (aka Forward) should work regardless of SHIFT being pressed +static const U32 DEFAULT_KEY_FILTER = ALLOW_MOUSE | ALLOW_MASK_MOUSE | ALLOW_KEYS | ALLOW_MASK_KEYS; + +class LLSetKeyBindDialog : public LLModalDialog { public: - LLVoiceSetKeyDialog(const LLSD& key); - ~LLVoiceSetKeyDialog(); - + LLSetKeyBindDialog(const LLSD& key); + ~LLSetKeyBindDialog(); + /*virtual*/ BOOL postBuild(); - - void setParent(LLFloaterPreference* parent) { mParent = parent; } - void setTmpParent(LLPanelPreferenceControls* parent) { mTmpParent = parent; } // todo: voice key will be removed, class renamved, so it will have only one parent - + + void setParent(LLPanelPreferenceControls* parent, U32 key_mask = DEFAULT_KEY_FILTER); + BOOL handleKeyHere(KEY key, MASK mask); BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); static void onCancel(void* user_data); - + static void onBlank(void* user_data); + static void onDefault(void* user_data); + private: - LLFloaterPreference* mParent; - LLPanelPreferenceControls* mTmpParent;// todo: voice key will be removed, class renamved, so it will have only one parent + LLPanelPreferenceControls* mParent; + + U32 mKeyMask; }; -LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key) +LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key) : LLModalDialog(key), mParent(NULL), - mTmpParent(NULL) + mKeyMask(DEFAULT_KEY_FILTER) { } //virtual -BOOL LLVoiceSetKeyDialog::postBuild() +BOOL LLSetKeyBindDialog::postBuild() { + childSetAction("SetEmpty", onBlank, this); + childSetAction("Default", onDefault, this); childSetAction("Cancel", onCancel, this); getChild("Cancel")->setFocus(TRUE); @@ -198,47 +211,85 @@ BOOL LLVoiceSetKeyDialog::postBuild() return TRUE; } -LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog() +void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_mask) +{ + mParent = parent; + mKeyMask = key_mask; + + LLTextBase *text_ctrl = getChild("descritption"); + + std::string input; + if ((key_mask & ALLOW_MOUSE) != 0) + { + input = getString("mouse"); + } + if ((key_mask & ALLOW_KEYS) != 0) + { + if (!input.empty()) + { + input += ", "; + } + input += getString("keyboard"); + } + text_ctrl->setTextArg("[INPUT]", input); +} + +LLSetKeyBindDialog::~LLSetKeyBindDialog() { } -BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) +BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask) { BOOL result = TRUE; - if (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT || key == KEY_NONE) + if ((key == 'Q' && mask == MASK_CONTROL) + || key == KEY_ESCAPE) + { + closeFloater(); + return true; + } + + // forbidden keys + if (key == KEY_NONE + || key == KEY_RETURN + || key == KEY_DELETE + || key == KEY_BACKSPACE) { - // temp return false; } - - // todo, same for escape - if (key == 'Q' && mask == MASK_CONTROL) - { - result = FALSE; - if (mTmpParent) - { - mTmpParent->onSetKey(KEY_NONE, MASK_NONE); - } + + if ((mKeyMask & ALLOW_MASKS) == 0 + && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT)) + { + // mask by themself are not allowed + return false; } + else if ((mKeyMask & ALLOW_KEYS) == 0) + { + // basic keys not allowed + return false; + } + else if ((mKeyMask & ALLOW_MASK_KEYS) == 0 && mask != 0) + { + // masked keys not allowed + return false; + } + else if (mParent) { - mParent->setKey(key); - } - else if (mTmpParent) - { - mTmpParent->onSetKey(key, mask); + mParent->onSetKeyBind(CLICK_NONE, key, mask); } closeFloater(); return result; } -BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) +BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = FALSE; if (clicktype == CLICK_LEFT) { + // try handling buttons first if (down) { result = LLView::handleMouseDown(x, y, mask); @@ -248,30 +299,21 @@ BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseCli result = LLView::handleMouseUp(x, y, mask); } } - if (clicktype == LLMouseHandler::CLICK_LEFT) - { - result = LLView::handleDoubleClick(x, y, mask); - } - if (result) - { - return TRUE; - } - if (down && clicktype != LLMouseHandler::CLICK_RIGHT) //tmp - //&& (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) - //&& mask == 0) + + if (!result + && ((mKeyMask & ALLOW_MOUSE) != 0) + && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported + && ((mKeyMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) { if (mParent) { - mParent->setMouse(clicktype); - } - else if (mTmpParent) - { - mTmpParent->onSetMouse(clicktype, mask); + mParent->onSetKeyBind(clicktype, KEY_NONE, mask); } result = TRUE; closeFloater(); } - else + + if (!result) { result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); } @@ -280,17 +322,36 @@ BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseCli } //static -void LLVoiceSetKeyDialog::onCancel(void* user_data) +void LLSetKeyBindDialog::onCancel(void* user_data) { - LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data; - // tmp needs 'no key' button - if (self->mTmpParent) - { - self->mTmpParent->onSetKey(KEY_NONE, MASK_NONE); - } + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; self->closeFloater(); } +//static +void LLSetKeyBindDialog::onBlank(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + // tmp needs 'no key' button + if (self->mParent) + { + self->mParent->onSetKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE); + } + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onDefault(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + // tmp needs 'no key' button + if (self->mParent) + { + self->mParent->onDefaultKeyBind(); + } + self->closeFloater(); +} + // global functions @@ -370,37 +431,6 @@ void handleAppearanceCameraMovementChanged(const LLSD& newvalue) } } -/*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option && floater ) - { - if ( floater ) - { - floater->setAllIgnored(); - // LLFirstUse::disableFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} - -bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if ( 0 == option && floater ) - { - if ( floater ) - { - floater->resetAllIgnored(); - //LLFirstUse::resetFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} -*/ - void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator) { numerator = 0; @@ -435,7 +465,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) static bool registered_dialog = false; if (!registered_dialog) { - LLFloaterReg::add("voice_set_key", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); registered_dialog = true; } @@ -1711,53 +1741,6 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data) void LLFloaterPreference::onClickSetKey() { - LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance("voice_set_key", LLSD(), TRUE); - if (dialog) - { - dialog->setParent(this); - } -} - -void LLFloaterPreference::setKey(KEY key) -{ - getChild("modifier_combo")->setValue(LLKeyboard::stringFromKey(key)); - // update the control right away since we no longer wait for apply - getChild("modifier_combo")->onCommit(); -} - -void LLFloaterPreference::setMouse(EMouseClickType click) -{ - std::string bt_name; - std::string ctrl_value; - switch (click) - { - case CLICK_MIDDLE: - bt_name = "middle_mouse"; - ctrl_value = MIDDLE_MOUSE_CV; - break; - case CLICK_BUTTON4: - bt_name = "button4_mouse"; - ctrl_value = MOUSE_BUTTON_4_CV; - break; - case CLICK_BUTTON5: - bt_name = "button5_mouse"; - ctrl_value = MOUSE_BUTTON_5_CV; - break; - default: - break; - } - - if (!ctrl_value.empty()) - { - LLUICtrl* p2t_line_editor = getChild("modifier_combo"); - // We are using text control names for readability and compatibility with voice - p2t_line_editor->setControlValue(ctrl_value); - LLPanel* advanced_preferences = dynamic_cast(p2t_line_editor->getParent()); - if (advanced_preferences) - { - p2t_line_editor->setValue(advanced_preferences->getString(bt_name)); - } - } } void LLFloaterPreference::onClickSetMiddleMouse() @@ -1782,18 +1765,6 @@ void LLFloaterPreference::onClickSetSounds() getChild("gesture_audio_play_btn")->setEnabled(!gSavedSettings.getBOOL("MuteSounds")); } -/* -void LLFloaterPreference::onClickSkipDialogs() -{ - LLNotificationsUtil::add("SkipShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_skip_dialogs, _1, _2, this)); -} - -void LLFloaterPreference::onClickResetDialogs() -{ - LLNotificationsUtil::add("ResetShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_reset_dialogs, _1, _2, this)); -} - */ - void LLFloaterPreference::onClickEnablePopup() { LLScrollListCtrl& disabled_popups = getChildRef("disabled_popups"); @@ -2794,7 +2765,7 @@ void LLPanelPreferenceGraphics::setPresetText() } } - if (hasDirtyChilds() && !preset_graphic_active.empty()) + if (hasDirtyChilds() && !preset_graphic_active.empty()) { gSavedSettings.setString("PresetGraphicActive", ""); preset_graphic_active.clear(); @@ -2914,98 +2885,124 @@ void LLPanelPreferenceGraphics::setHardwareDefaults() resetDirtyChilds(); } - -//-------------------For LLPanelPreferenceControls' list--------------------------- -class LLGroupControlsListItem : public LLScrollListItem, public LLHandleProvider -{ -public: - LLGroupControlsListItem(const LLScrollListItem::Params& p) - : LLScrollListItem(p) - { - } - - LLGroupControlsListItem(const LLScrollListItem::Params& p, const LLUUID& icon_id) - : LLScrollListItem(p), mIconId(icon_id) - { - } - - void draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) - { - // todo: insert image and adjust rect - LLScrollListItem::draw(rect, fg_color, bg_color, highlight_color, column_padding); - } -private: - LLUUID mIconId; -}; - -static const std::string tmp_typetostring[LLControlBindings::CONTROL_NUM_INDICES] = { - "control_view_actions", - "control_about", - "control_orbit", - "control_pan", - "control_world_map", - "control_zoom", - "control_interactions", - "control_build", - "control_drag", - "control_edit", - "control_menu", - "control_open", - "control_touch", - "control_wear", - "control_movements", - "control_moveto", - "control_sit", - "control_teleportto", - "control_forward", - "control_backward", - "control_left", - "control_right", - "control_lstrafe", - "control_rstrafe", - "control_jump", - "control_down", - "control_run", - "control_toggle_run", - "control_fly", - "control_mediacontent", - "control_parcel", - "control_media", - "control_voice", - "control_toggle_voice", - "control_reserved", - "control_menu", - "control_reserved_select", - "control_shift_select", - "control_cntrl_select" -}; - //------------------------LLPanelPreferenceControls-------------------------------- static LLPanelInjector t_pref_contrls("panel_preference_controls"); +LLPanelPreferenceControls::LLPanelPreferenceControls() + :LLPanelPreference(), + mEditingIndex(-1), + mEditingColumn(-1), + mEditingMode(0), + mShowKeyDialog(false) +{ +} + +LLPanelPreferenceControls::~LLPanelPreferenceControls() +{ +} + BOOL LLPanelPreferenceControls::postBuild() { - //todo: on open instead of on the go //todo: add pitch/yaw? - //todo: Scroll? //todo: should be auto-expandable with menu items and should pull names from menu when possible // populate list of controls pControlsTable = getChild("controls_list"); - populateControlTable(); + pKeyModeBox = getChild("key_mode"); pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); + pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this)); + getChild("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaults, this)); return TRUE; } +// Something of a workaround: cells don't handle clicks, so we catch a click, then process it on hover. +BOOL LLPanelPreferenceControls::handleHover(S32 x, S32 y, MASK mask) +{ + if (mShowKeyDialog) + { + if (mEditingIndex > 0 && mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)mEditingIndex)) + { + mEditingColumn = pControlsTable->getColumnIndexFromOffset(x); + + if (mEditingColumn >0) + { + LLSetKeyBindDialog* dialog = LLFloaterReg::showTypedInstance("keybind_dialog", LLSD(), TRUE); + if (dialog) + { + if (mConflictHandler[mEditingMode].getLoadedMode() == LLKeyConflictHandler::MODE_GENERAL) + { + dialog->setParent(this, DEFAULT_KEY_FILTER); + } + else + { + dialog->setParent(this, ALLOW_KEYS | ALLOW_MASK_KEYS); + } + } + } + } + mShowKeyDialog = false; + } + return LLPanelPreference::handleHover(x, y, mask); +} + +void LLPanelPreferenceControls::addGroupRow(const std::string &icon, S32 index) +{ + LLScrollListItem::Params item_params; + item_params.value = LLSD::Integer(-1); + + LLScrollListCell::Params icon_cell_params; + icon_cell_params.font = LLFontGL::getFontSansSerif(); + icon_cell_params.font_halign = LLFontGL::LEFT; + icon_cell_params.type = "icontext"; + + LLScrollListCell::Params cell_params; + // init basic cell params + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.font_halign = LLFontGL::LEFT; + + std::string control_name = LLKeyConflictHandler::getControlName((LLKeyConflictHandler::EControlTypes)index); + std::string label; + if (hasString(control_name)) + { + label = getString(control_name); + } + else + { + label = control_name; + } + icon_cell_params.column = "lst_action"; + icon_cell_params.text = label; + icon_cell_params.value = icon; + item_params.columns.add(icon_cell_params); + //dummy cells + cell_params.column = "lst_ctrl1"; + cell_params.value = ""; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl2"; + cell_params.value = ""; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl3"; + cell_params.value = ""; + item_params.columns.add(cell_params); + pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); +} + +void LLPanelPreferenceControls::regenerateControls() +{ + mEditingMode = pKeyModeBox->getValue().asInteger(); + mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::EModes)mEditingMode); + populateControlTable(); +} + void LLPanelPreferenceControls::populateControlTable() { pControlsTable->clearRows(); // todo: subsections need sorting? - std::string label; + std::string label, control_name; LLScrollListCell::Params cell_params; // init basic cell params cell_params.font = LLFontGL::getFontSansSerif(); @@ -3013,81 +3010,71 @@ void LLPanelPreferenceControls::populateControlTable() cell_params.column = ""; cell_params.value = label; - LLScrollListItem::Params item_params_blank; - cell_params.enabled = false; - item_params_blank.value = LLSD::Integer(-1); - item_params_blank.columns.add(cell_params); - cell_params.enabled = true; - - for (U32 i = LLControlBindings::CONTROL_VIEW_ACTIONS; i < LLControlBindings::CONTROL_NUM_INDICES; i++) + S32 start = mEditingMode == LLKeyConflictHandler::MODE_GENERAL ? LLKeyConflictHandler::CONTROL_VIEW_ACTIONS : LLKeyConflictHandler::CONTROL_MOVEMENTS; + S32 end = mEditingMode == LLKeyConflictHandler::MODE_GENERAL ? LLKeyConflictHandler::CONTROL_NUM_INDICES : LLKeyConflictHandler::CONTROL_RESERVED; + for (S32 i = start; i < end; i++) { - switch (i) + LLKeyConflictHandler::EControlTypes type = (LLKeyConflictHandler::EControlTypes)i; + switch (type) { - case LLControlBindings::CONTROL_VIEW_ACTIONS: - { - // same as below, but without separator - LLScrollListItem::Params item_params; - item_params.value = LLSD::Integer(i); - - label = getString(tmp_typetostring[i]); - cell_params.column = "lst_action"; - cell_params.value = label; - //dummy cells - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl1"; - cell_params.value = ""; - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl2"; - cell_params.value = ""; - item_params.columns.add(cell_params); - LLUUID id; - LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id); - pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM); - break; - } - case LLControlBindings::CONTROL_INTERACTIONS: - case LLControlBindings::CONTROL_MOVEMENTS: - case LLControlBindings::CONTROL_MEDIACONTENT: - case LLControlBindings::CONTROL_RESERVED: - { - // insert blank - pControlsTable->addRow(item_params_blank, EAddPosition::ADD_BOTTOM); - // inster with icon - LLScrollListItem::Params item_params; - item_params.value = LLSD::Integer(i); - - label = getString(tmp_typetostring[i]); - cell_params.column = "lst_action"; - cell_params.value = label; - //dummy cells - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl1"; - cell_params.value = ""; - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl2"; - cell_params.value = ""; - item_params.columns.add(cell_params); - LLUUID id; - LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id); - pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM); - break; - } - default: + case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS: + addSeparator(); + addGroupRow("Search_Icon", i); + break; + case LLKeyConflictHandler::CONTROL_INTERACTIONS: + addSeparator(); + addGroupRow("Command_Gestures_Icon", i); + break; + case LLKeyConflictHandler::CONTROL_MOVEMENTS: + addSeparator(); + addGroupRow("Move_Walk_Off", i); + break; + case LLKeyConflictHandler::CONTROL_MEDIACONTENT: + addSeparator(); + addGroupRow("Audio_Press", i); + break; + case LLKeyConflictHandler::CONTROL_CAMERA: + addSeparator(); + addGroupRow("Cam_FreeCam_Off", i); + break; + case LLKeyConflictHandler::CONTROL_EDIT_TITLE: + addSeparator(); + addGroupRow("Tool_Dozer", i); + break; + case LLKeyConflictHandler::CONTROL_RESERVED: + addSeparator(); + addGroupRow("Info_Small", i); + break; + default: { //default insert LLScrollListItem::Params item_params; item_params.value = LLSD::Integer(i); - // todo oddset cell_params.column = "lst_action"; - label = getString(tmp_typetostring[i]); + bool enabled = mConflictHandler[mEditingMode].canAssignControl(type); + control_name = LLKeyConflictHandler::getControlName(type); + if (hasString(control_name)) + { + label = getString(control_name); + } + else + { + label = control_name; + } cell_params.value = label; item_params.columns.add(cell_params); cell_params.column = "lst_ctrl1"; - cell_params.value = gControlBindings.getPrimaryControl((LLControlBindings::EControlTypes)i).asString(); + cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 0); + cell_params.enabled = enabled; item_params.columns.add(cell_params); cell_params.column = "lst_ctrl2"; - cell_params.value = gControlBindings.getSecondaryControl((LLControlBindings::EControlTypes)i).asString(); + cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 1); + cell_params.enabled = enabled; + item_params.columns.add(cell_params); + cell_params.column = "lst_ctrl3"; + cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 2); + cell_params.enabled = enabled; item_params.columns.add(cell_params); pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); @@ -3095,23 +3082,64 @@ void LLPanelPreferenceControls::populateControlTable() } } } + + //temp + if (mEditingMode == LLKeyConflictHandler::MODE_GENERAL) + pControlsTable->setEnabled(false); +} + +// Just a workaround to not care about first separator before headers (we can start from random header) +void LLPanelPreferenceControls::addSeparator() +{ + if (pControlsTable->getItemCount() > 0) + { + pControlsTable->addSeparator(EAddPosition::ADD_BOTTOM); + } +} + +void LLPanelPreferenceControls::apply() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + } + } } void LLPanelPreferenceControls::cancel() { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].clear(); + } + } + pControlsTable->clear(); } void LLPanelPreferenceControls::saveSettings() { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + } + } + + S32 mode = pKeyModeBox->getValue().asInteger(); + if (mConflictHandler[mode].empty()) + { + regenerateControls(); + } } void LLPanelPreferenceControls::resetDirtyChilds() { -} - -bool LLPanelPreferenceControls::hasDirtyChilds() -{ - return false; + regenerateControls(); } void LLPanelPreferenceControls::onListCommit() @@ -3124,63 +3152,87 @@ void LLPanelPreferenceControls::onListCommit() S32 control = item->getValue().asInteger(); - if (!gControlBindings.canAssignControl((LLControlBindings::EControlTypes)control)) + if (control <= 0) { return; } - - // todo: add code to determine what cell was clicked, probably cells themself should be clickable - LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance("voice_set_key", LLSD(), TRUE); - if (dialog) + if (!mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)control)) { - dialog->setTmpParent(this); // will be remade from being voice later + return; } + + // List does not tell us what cell was clicked, so we have to figure it out manually, but + // fresh mouse coordinates are not yet accessible during onCommit() and there are other issues, + // so we cheat: remember item user clicked at, trigger 'key dialog' on hover that comes next, + // use coordinates from hover to calculate cell + mEditingIndex = control; + mShowKeyDialog = true; } -void LLPanelPreferenceControls::onSetKey(KEY key, MASK mask) +void LLPanelPreferenceControls::onModeCommit() { - LLScrollListItem* item = pControlsTable->getFirstSelected(); - if (item == NULL) + regenerateControls(); +} + +void LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask) +{ + LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex; + + if (!mConflictHandler[mEditingMode].canAssignControl(control)) { return; } - LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger(); - - if (!gControlBindings.canAssignControl(control)) + pControlsTable->deselectAllItems(); + pControlsTable->selectByValue(mEditingIndex); + LLScrollListItem *item = pControlsTable->getFirstSelected(); + if (item && mEditingColumn > 0) { - return; + + mConflictHandler[mEditingMode].registerControl(control, mEditingColumn - 1, click, key, mask); + + LLScrollListCell *cell = item->getColumn(1); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 0)); + cell = item->getColumn(2); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 1)); + cell = item->getColumn(3); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 2)); } - gControlBindings.registerPrimaryControl(control, LLMouseHandler::CLICK_NONE, key, mask); - - // instead of populating, update single element populateControlTable(); } -void LLPanelPreferenceControls::onSetMouse(LLMouseHandler::EClickType click, MASK mask) +void LLPanelPreferenceControls::onRestoreDefaults() { - - LLScrollListItem* item = pControlsTable->getFirstSelected(); - if (item == NULL) - { - return; - } - - LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger(); - - if (!gControlBindings.canAssignControl(control)) - { - return; - } - - gControlBindings.registerPrimaryControl(control, click, KEY_NONE, mask); - - // instead of populating, update single element + mConflictHandler[mEditingMode].resetToDefaults(); populateControlTable(); } +void LLPanelPreferenceControls::onDefaultKeyBind() +{ + LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex; + + if (!mConflictHandler[mEditingMode].canAssignControl(control)) + { + return; + } + + pControlsTable->deselectAllItems(); + pControlsTable->selectByValue(mEditingIndex); + LLScrollListItem *item = pControlsTable->getFirstSelected(); + if (item) + { + LLScrollListCell *cell = item->getColumn(mEditingColumn); + + if (mEditingColumn > 0) + { + mConflictHandler[mEditingMode].resetToDefault(control, mEditingColumn - 1); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, mEditingColumn - 1)); + } + } +} + LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key) : LLFloater(key) { diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index bfccd64624..c36dee114c 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -37,12 +37,14 @@ #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" #include "llsearcheditor.h" +#include "llkeyconflict.h" class LLConversationLogObserver; class LLPanelPreference; class LLPanelLCD; class LLPanelDebug; class LLMessageSystem; +class LLComboBox; class LLScrollListCtrl; class LLSliderCtrl; class LLSD; @@ -147,8 +149,6 @@ public: void onClickSkin(LLUICtrl* ctrl,const LLSD& userdata); void onSelectSkin(); void onClickSetKey(); - void setKey(KEY key); - void setMouse(EMouseClickType click); void onClickSetMiddleMouse(); void onClickSetSounds(); void onClickEnablePopup(); @@ -298,21 +298,36 @@ class LLPanelPreferenceControls : public LLPanelPreference { LOG_CLASS(LLPanelPreferenceControls); public: + LLPanelPreferenceControls(); + ~LLPanelPreferenceControls(); + BOOL postBuild(); - void populateControlTable(); + BOOL handleHover(S32 x, S32 y, MASK mask); + + void apply(); void cancel(); void saveSettings(); void resetDirtyChilds(); void onListCommit(); - void onSetKey(KEY key, MASK mask); - void onSetMouse(LLMouseHandler::EClickType click, MASK mask); - -protected: - bool hasDirtyChilds(); + void onModeCommit(); + void onSetKeyBind(EMouseClickType click, KEY key, MASK mask); + void onRestoreDefaults(); + void onDefaultKeyBind(); private: + void addGroupRow(const std::string &icon, S32 index); + void regenerateControls(); + void populateControlTable(); + void addSeparator(); + LLScrollListCtrl* pControlsTable; + LLComboBox *pKeyModeBox; + LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT]; + S32 mEditingIndex; + S32 mEditingColumn; + S32 mEditingMode; + bool mShowKeyDialog; }; class LLFloaterPreferenceGraphicsAdvanced : public LLFloater diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp new file mode 100644 index 0000000000..0f0129bf68 --- /dev/null +++ b/indra/newview/llkeyconflict.cpp @@ -0,0 +1,789 @@ +/** + * @file llkeyconflict.cpp + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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$ + */ + +/* + * App-wide preferences. Note that these are not per-user, + * because we need to load many preferences before we have + * a login name. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llkeyconflict.h" + +#include "llinitparam.h" +#include "llkeyboard.h" +#include "llviewercontrol.h" +#include "llviewerkeyboard.h" +#include "llxuiparser.h" +//#include "llstring.h" + +static const std::string typetostring[LLKeyConflictHandler::CONTROL_NUM_INDICES] = { + "control_view_actions", + "control_about", + "control_orbit", + "control_pan", + "control_world_map", + "control_zoom", + "control_interactions", + "control_build", + //"control_drag", + "control_edit", + //"control_menu", + "control_open", + "control_touch", + "control_wear", + "control_movements", + "control_moveto", + "control_sit", + "control_teleportto", + "push_forward", + "push_backward", + "turn_left", + "turn_right", + "slide_left", + "slide_right", + "jump", + "push_down", + //"control_run", + "control_toggle_run", + "toggle_fly", + "stop_moving", + "control_camera", + "look_up", + "look_down", + "move_forward", + "move_backward", + "move_forward_fast", + "move_backward_fast", + "move_forward_sitting", + "move_backward_sitting", + "spin_over", + "spin_under", + "spin_over_sitting", + "spin_under_sitting", + "pan_up", + "pan_down", + "pan_left", + "pan_right", + "pan_in", + "pan_out", + "spin_around_ccw", + "spin_around_cw", + "spin_around_ccw_sitting", + "spin_around_cw_sitting", + "control_edit_title", + "edit_avatar_spin_ccw", + "edit_avatar_spin_cw", + "edit_avatar_spin_over", + "edit_avatar_spin_under", + "edit_avatar_move_forward", + "edit_avatar_move_backward", + "control_mediacontent", + "control_parcel", + "control_media", + "control_voice", + "control_toggle_voice", + "start_chat", + "start_gesture", + "control_reserved", + "control_delete", + "control_menu", + "control_reserved_select", + "control_shift_select", + "control_cntrl_select" +}; + +// note, a solution is needed that will keep this up to date with llviewerkeyboard +typedef std::map control_enum_t; +static const control_enum_t command_to_key = +{ + { "jump", LLKeyConflictHandler::CONTROL_JUMP }, + { "push_down", LLKeyConflictHandler::CONTROL_DOWN }, + { "push_forward", LLKeyConflictHandler::CONTROL_FORWARD }, + { "push_backward", LLKeyConflictHandler::CONTROL_BACKWARD }, + { "look_up", LLKeyConflictHandler::CONTROL_LOOK_UP }, + { "look_down", LLKeyConflictHandler::CONTROL_LOOK_DOWN }, + { "toggle_fly", LLKeyConflictHandler::CONTROL_TOGGLE_FLY }, + { "turn_left", LLKeyConflictHandler::CONTROL_LEFT }, + { "turn_right", LLKeyConflictHandler::CONTROL_RIGHT }, + { "slide_left", LLKeyConflictHandler::CONTROL_LSTRAFE }, + { "slide_right", LLKeyConflictHandler::CONTROL_RSTRAFE }, + { "spin_around_ccw", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW }, // todo, no idea what these spins are + { "spin_around_cw", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CW }, + { "spin_around_ccw_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW_SITTING }, + { "spin_around_cw_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW_SITTING }, + { "spin_over", LLKeyConflictHandler::CONTROL_CAMERA_SOVER }, + { "spin_under", LLKeyConflictHandler::CONTROL_CAMERA_SUNDER }, + { "spin_over_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SOVER_SITTING }, + { "spin_under_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SUNDER_SITTING }, + { "move_forward", LLKeyConflictHandler::CONTROL_CAMERA_FORWARD }, + { "move_backward", LLKeyConflictHandler::CONTROL_CAMERA_BACKWARD }, + { "move_forward_sitting", LLKeyConflictHandler::CONTROL_CAMERA_FSITTING }, + { "move_backward_sitting", LLKeyConflictHandler::CONTROL_CAMERA_BSITTING }, + { "pan_up", LLKeyConflictHandler::CONTROL_CAMERA_PANUP }, + { "pan_down", LLKeyConflictHandler::CONTROL_CAMERA_PANDOWN }, + { "pan_left", LLKeyConflictHandler::CONTROL_CAMERA_PANLEFT }, + { "pan_right", LLKeyConflictHandler::CONTROL_CAMERA_PANRIGHT }, + { "pan_in", LLKeyConflictHandler::CONTROL_CAMERA_PANIN }, + { "pan_out", LLKeyConflictHandler::CONTROL_CAMERA_PANOUT }, + { "move_forward_fast", LLKeyConflictHandler::CONTROL_CAMERA_FFORWARD }, + { "move_backward_fast", LLKeyConflictHandler::CONTROL_CAMERA_FBACKWARD }, + { "edit_avatar_spin_ccw", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_CCW }, + { "edit_avatar_spin_cw", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_CW }, + { "edit_avatar_spin_over", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_OVER }, + { "edit_avatar_spin_under", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_UNDER }, + { "edit_avatar_move_forward", LLKeyConflictHandler::CONTROL_EDIT_AV_MV_FORWARD }, + { "edit_avatar_move_backward", LLKeyConflictHandler::CONTROL_EDIT_AV_MV_BACKWARD }, + { "stop_moving", LLKeyConflictHandler::CONTROL_STOP }, + { "start_chat", LLKeyConflictHandler::CONTROL_START_CHAT }, + { "start_gesture", LLKeyConflictHandler::CONTROL_START_GESTURE }, +}; + + +// LLKeyboard::stringFromMask is meant for UI and is OS dependent, +// so this class uses it's own version +std::string string_from_mask(MASK mask) +{ + std::string res; + if ((mask & MASK_CONTROL) != 0) + { + res = "CTL"; + } + if ((mask & MASK_ALT) != 0) + { + if (!res.empty()) res += "_"; + res += "ALT"; + } + if ((mask & MASK_SHIFT) != 0) + { + if (!res.empty()) res += "_"; + res += "SHIFT"; + } + + if (mask == MASK_NONE) + { + res = "NONE"; + } + return res; +} + +// LLKeyConflictHandler + +LLKeyConflictHandler::LLKeyConflictHandler() + : mHasUnsavedChanges(false) +{ + // todo: assign conflic priorities + // todo: load from keys.xml? + + // Thise controls are meant to cause conflicts when user tries to assign same control somewhere else + /*registerTemporaryControl(CONTROL_RESERVED_MENU, CLICK_RIGHT, KEY_NONE, MASK_NONE, 0); + registerTemporaryControl(CONTROL_SHIFT_SELECT, CLICK_LEFT, KEY_NONE, MASK_SHIFT, 0); + registerTemporaryControl(CONTROL_CNTRL_SELECT, CLICK_LEFT, KEY_NONE, MASK_CONTROL, 0); + registerTemporaryControl(CONTROL_DELETE, CLICK_NONE, KEY_DELETE, MASK_NONE, 0); + + loadFromSettings();*/ +} + +LLKeyConflictHandler::LLKeyConflictHandler(EModes mode) + : mHasUnsavedChanges(false), + mLoadedMode(mode) +{ + loadFromSettings(mode); +} + +bool LLKeyConflictHandler::canHandleControl(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask) +{ + return mControlsMap[control_type].canHandle(mouse_ind, key, mask); +} + +bool LLKeyConflictHandler::canHandleKey(EControlTypes control_type, KEY key, MASK mask) +{ + return canHandleControl(control_type, CLICK_NONE, key, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, MASK mask) +{ + return canHandleControl(control_type, mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(EControlTypes control_type, S32 mouse_ind, MASK mask) +{ + return canHandleControl(control_type, (EMouseClickType)mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canAssignControl(EControlTypes control_type) +{ + std::map::iterator iter = mControlsMap.find(control_type); + if (iter != mControlsMap.end()) + { + return iter->second.mAssignable; + } + return false; +} + +void LLKeyConflictHandler::registerControl(EControlTypes control_type, U32 index, EMouseClickType mouse, KEY key, MASK mask) +{ + LLKeyConflict &type_data = mControlsMap[control_type]; + if (!type_data.mAssignable) + { + LL_ERRS() << "Error in code, user or system should not be able to change certain controls" << LL_ENDL; + } + type_data.mKeyBind.replaceKeyData(mouse, key, mask, index); + + mHasUnsavedChanges = true; +} + +LLKeyData LLKeyConflictHandler::getControl(EControlTypes control_type, U32 index) +{ + return mControlsMap[control_type].getKeyData(index); +} + +// static +std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata) +{ + std::string result; + + if (keydata.mMask != MASK_NONE && keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromAccelerator(keydata.mMask, keydata.mKey); + } + else if (keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromKey(keydata.mKey); + } + else if (keydata.mMask != MASK_NONE) + { + LL_ERRS() << "Masks binding without keys is not supported yet" << LL_ENDL; + } + + #ifdef LL_DARWIN + // darwin uses special symbols and doesn't need '+' for masks + if (mMouse != CLICK_NONE && mKey != KEY_NONE) + { + result += " + "; + } + #else + if (keydata.mMouse != CLICK_NONE && !result.empty()) + { + result += " + "; + } + #endif + + switch (keydata.mMouse) + { + case CLICK_LEFT: + result += "LMB"; + break; + case CLICK_MIDDLE: + result += "MMB"; + break; + case CLICK_RIGHT: + result += "RMB"; + break; + case CLICK_BUTTON4: + result += "MB4"; + break; + case CLICK_BUTTON5: + result += "MB5"; + break; + case CLICK_DOUBLELEFT: + result += "Double LMB"; + break; + default: + break; + } + return result; +} + +// static +std::string LLKeyConflictHandler::getControlName(EControlTypes control_type) +{ + return typetostring[control_type]; +} + +std::string LLKeyConflictHandler::getControlString(EControlTypes control_type, U32 index) +{ + return getStringFromKeyData(mControlsMap[control_type].getKeyData(index)); +} + +void LLKeyConflictHandler::loadFromSettings(const LLViewerKeyboard::KeyMode& keymode, control_map_t *destination) +{ + for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + KEY key; + MASK mask; + if (it->key.getValue().empty()) + { + key = KEY_NONE; + } + else + { + LLKeyboard::keyFromString(it->key, &key); + } + LLKeyboard::maskFromString(it->mask, &mask); + std::string command_name = it->command; + // it->command + // It might be better to have map, but at the moment enum is easier to iterate through. + // Besides keys.xml might not contain all commands + control_enum_t::const_iterator iter = command_to_key.find(command_name); + if (iter != command_to_key.end()) + { + LLKeyConflict &type_data = (*destination)[iter->second]; + type_data.mAssignable = true; + // Don't care about conflict level, all movement and view commands already account for it + type_data.mKeyBind.addKeyData(CLICK_NONE, key, mask); + } + } +} + +void LLKeyConflictHandler::loadFromSettings(const EModes &load_mode, const std::string &filename, control_map_t *destination) +{ + if (filename.empty()) + { + return; + } + + LLViewerKeyboard::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + switch (load_mode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + loadFromSettings(keys.first_person, destination); + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + loadFromSettings(keys.third_person, destination); + } + break; + case MODE_EDIT: + if (keys.edit.isProvided()) + { + loadFromSettings(keys.edit, destination); + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + loadFromSettings(keys.edit_avatar, destination); + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + loadFromSettings(keys.sitting, destination); + } + break; + default: + break; + } + } +} + +void LLKeyConflictHandler::loadFromSettings(EModes load_mode) +{ + mControlsMap.clear(); + mDefaultsMap.clear(); + if (load_mode == MODE_GENERAL) + { + for (U32 i = 0; i < CONTROL_NUM_INDICES; i++) + { + EControlTypes type = (EControlTypes)i; + switch (type) + { + case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS: + case LLKeyConflictHandler::CONTROL_INTERACTIONS: + case LLKeyConflictHandler::CONTROL_MOVEMENTS: + case LLKeyConflictHandler::CONTROL_MEDIACONTENT: + case LLKeyConflictHandler::CONTROL_CAMERA: + case LLKeyConflictHandler::CONTROL_EDIT_TITLE: + case LLKeyConflictHandler::CONTROL_RESERVED: + // ignore 'headers', they are for representation and organization purposes + break; + default: + { + std::string name = getControlName(type); + LLControlVariablePtr var = gSavedSettings.getControl(name); + if (var) + { + LLKeyBind bind(var->getValue()); + LLKeyConflict key(bind, true, 0); + mControlsMap[type] = key; + } + break; + } + } + } + } + else + { + // load defaults + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keys.xml"); + loadFromSettings(load_mode, filename, &mDefaultsMap); + + // load user's + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml"); + if (gDirUtilp->fileExists(filename)) + { + loadFromSettings(load_mode, filename, &mControlsMap); + } + else + { + mControlsMap = mDefaultsMap; + } + } + mLoadedMode = load_mode; + + generatePlaceholders(); +} + +void LLKeyConflictHandler::saveToSettings() +{ + if (mControlsMap.empty()) + { + return; + } + + if (mLoadedMode == MODE_GENERAL) + { + for (U32 i = 0; i < CONTROL_NUM_INDICES; i++) + { + EControlTypes type = (EControlTypes)i; + switch (type) + { + case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS: + case LLKeyConflictHandler::CONTROL_INTERACTIONS: + case LLKeyConflictHandler::CONTROL_MOVEMENTS: + case LLKeyConflictHandler::CONTROL_MEDIACONTENT: + case LLKeyConflictHandler::CONTROL_CAMERA: + case LLKeyConflictHandler::CONTROL_EDIT_TITLE: + case LLKeyConflictHandler::CONTROL_RESERVED: + // ignore 'headers', they are for representation and organization purposes + break; + default: + { + if (mControlsMap[type].mAssignable) + { + std::string name = getControlName(type); + if (gSavedSettings.controlExists(name)) + { + gSavedSettings.setLLSD(name, mControlsMap[type].mKeyBind.asLLSD()); + } + else if (!mControlsMap[type].mKeyBind.empty()) + { + // shouldn't happen user side since all settings are supposed to be declared already, but handy when creating new ones + // (just don't forget to change comment and to copy them from user's folder) + LL_INFOS() << "Creating new keybinding " << name << LL_ENDL; + gSavedSettings.declareLLSD(name, mControlsMap[type].mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS); + } + } + break; + } + } + } + } + else + { + // loaded full copy of original file + std::string filename = gDirUtilp->findFile("keys.xml", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + + LLViewerKeyboard::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + // replace category we edited + + // todo: fix this + // workaround to avoid doing own param container + LLViewerKeyboard::KeyMode mode; + LLViewerKeyboard::KeyBinding binding; + + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + for (; iter != end; ++iter) + { + U32 size = iter->second.mKeyBind.getDataCount(); + for (U32 i = 0; i < size; ++i) + { + // Still write empty keys to make sure we will maintain UI position + LLKeyData data = iter->second.mKeyBind.getKeyData(i); + if (data.mKey == KEY_NONE) + { + binding.key = ""; + } + else + { + // Note: this is UI string, we might want to hardcode our own for 'fixed' use in keys.xml + binding.key = LLKeyboard::stringFromKey(data.mKey); + } + binding.mask = string_from_mask(data.mMask); + binding.command = getControlName(iter->first); + mode.bindings.add(binding); + } + } + + switch (mLoadedMode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + keys.first_person.bindings.set(mode.bindings, true); + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + keys.third_person.bindings.set(mode.bindings, true); + } + break; + case MODE_EDIT: + if (keys.edit.isProvided()) + { + keys.edit.bindings.set(mode.bindings, true); + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + keys.edit_avatar.bindings.set(mode.bindings, true); + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + keys.sitting.bindings.set(mode.bindings, true); + } + break; + default: + break; + } + + // write back to user's xml; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml"); + + LLXMLNodePtr output_node = new LLXMLNode("keys", false); + LLXUIParser parser; + parser.writeXUI(output_node, keys); + + // Write the resulting XML to file + if (!output_node->isNull()) + { + LLFILE *fp = LLFile::fopen(filename, "w"); + if (fp != NULL) + { + LLXMLNode::writeHeaderToFile(fp); + output_node->writeToFile(fp); + fclose(fp); + } + } + // Now force a rebind for keyboard + if (gDirUtilp->fileExists(filename)) + { + gViewerKeyboard.loadBindingsXML(filename); + } + } + } + mHasUnsavedChanges = false; +} + +LLKeyData LLKeyConflictHandler::getDefaultControl(EControlTypes control_type, U32 index) +{ + if (mLoadedMode == MODE_GENERAL) + { + std::string name = getControlName(control_type); + LLControlVariablePtr var = gSavedSettings.getControl(name); + if (var) + { + return LLKeyBind(var->getDefault()).getKeyData(index); + } + return LLKeyData(); + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_type); + if (iter != mDefaultsMap.end()) + { + return iter->second.mKeyBind.getKeyData(index); + } + return LLKeyData(); + } +} + +void LLKeyConflictHandler::resetToDefault(EControlTypes control_type, U32 index) +{ + LLKeyData data = getDefaultControl(control_type, index); + mControlsMap[control_type].setKeyData(data, index); +} + +void LLKeyConflictHandler::resetToDefault(EControlTypes control_type) +{ + if (mLoadedMode == MODE_GENERAL) + { + std::string name = getControlName(control_type); + LLControlVariablePtr var = gSavedSettings.getControl(name); + if (var) + { + mControlsMap[control_type].mKeyBind = LLKeyBind(var->getDefault()); + } + else + { + mControlsMap[control_type].mKeyBind.clear(); + } + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_type); + if (iter != mDefaultsMap.end()) + { + mControlsMap[control_type].mKeyBind = iter->second.mKeyBind; + } + else + { + mControlsMap[control_type].mKeyBind.clear(); + } + } +} + +void LLKeyConflictHandler::resetToDefaults(EModes mode) +{ + if (mode == MODE_GENERAL) + { + for (U32 i = 0; i < CONTROL_NUM_INDICES; i++) + { + EControlTypes type = (EControlTypes)i; + switch (type) + { + case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS: + case LLKeyConflictHandler::CONTROL_INTERACTIONS: + case LLKeyConflictHandler::CONTROL_MOVEMENTS: + case LLKeyConflictHandler::CONTROL_MEDIACONTENT: + case LLKeyConflictHandler::CONTROL_CAMERA: + case LLKeyConflictHandler::CONTROL_EDIT_TITLE: + case LLKeyConflictHandler::CONTROL_RESERVED: + // ignore 'headers', they are for representation and organization purposes + break; + default: + { + resetToDefault(type); + break; + } + } + } + } + else + { + mControlsMap.clear(); + mControlsMap = mDefaultsMap; + generatePlaceholders(); + } + + mHasUnsavedChanges = true; +} + +void LLKeyConflictHandler::resetToDefaults() +{ + if (!empty()) + { + resetToDefaults(mLoadedMode); + } +} + +void LLKeyConflictHandler::resetAllToDefaults() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml"); + if (gDirUtilp->fileExists(filename)) + { + LLFile::remove(filename); + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keys.xml"); + gViewerKeyboard.loadBindingsXML(filename); + } + + for (U32 i = 0; i < CONTROL_NUM_INDICES; i++) + { + EControlTypes type = (EControlTypes)i; + switch (type) + { + case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS: + case LLKeyConflictHandler::CONTROL_INTERACTIONS: + case LLKeyConflictHandler::CONTROL_MOVEMENTS: + case LLKeyConflictHandler::CONTROL_MEDIACONTENT: + case LLKeyConflictHandler::CONTROL_RESERVED: + // ignore 'headers', they are for representation and organization purposes + break; + default: + { + resetToDefault(type); + break; + } + } + } + mHasUnsavedChanges = false; +} + +void LLKeyConflictHandler::clear() +{ + mHasUnsavedChanges = false; + mControlsMap.clear(); + mDefaultsMap.clear(); +} + +void LLKeyConflictHandler::resetKeyboardBindings() +{ + std::string filename = gDirUtilp->findFile("keys.xml", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + + gViewerKeyboard.loadBindingsXML(filename); +} + +void LLKeyConflictHandler::generatePlaceholders() +{ + +} + +void LLKeyConflictHandler::registerTemporaryControl(EControlTypes control_type, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask) +{ + LLKeyConflict *type_data = &mControlsMap[control_type]; + type_data->mAssignable = false; + type_data->mConflictMask = conflict_mask; + type_data->mKeyBind.addKeyData(mouse, key, mask); +} + diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h new file mode 100644 index 0000000000..79bd9b8438 --- /dev/null +++ b/indra/newview/llkeyconflict.h @@ -0,0 +1,214 @@ +/** + * @file llkeyconflict.h + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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$ + */ + +#ifndef LL_LLKEYCONFLICT_H +#define LL_LLKEYCONFLICT_H + +#include "llkeybind.h" +#include "llviewerkeyboard.h" + + +class LLKeyConflict +{ +public: + LLKeyConflict() : mAssignable(true), mConflictMask(0) {} //temporary assignable, don't forget to change once all keys are recorded + LLKeyConflict(bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask) {} + LLKeyConflict(const LLKeyBind &bind, bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask), mKeyBind(bind) {} + + LLKeyData getPrimaryKeyData() { return mKeyBind.getKeyData(0); } + LLKeyData getKeyData(U32 index) { return mKeyBind.getKeyData(index); } + void setPrimaryKeyData(const LLKeyData& data) { mKeyBind.replaceKeyData(data, 0); } + void setKeyData(const LLKeyData& data, U32 index) { mKeyBind.replaceKeyData(data, index); } + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) { return mKeyBind.canHandle(mouse, key, mask); } + + LLKeyBind mKeyBind; + bool mAssignable; // whether user can change key or key simply acts as placeholder + U32 mConflictMask; +}; + +class LLKeyConflictHandler +{ +public: + + enum EModes // partially repeats e_keyboard_mode + { + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_GENERAL, + MODE_COUNT + }; + + enum EConflictTypes // priority higherst to lowest + { + CONFLICT_LAND = 1, + CONFLICT_OBJECT = 2, + CONFLICT_TOUCH = 4, + CONFLICT_INTERACTIBLE = 8, + CONFLICT_AVATAR = 16, + CONFLICT_ANY = 511 + }; + + // todo, unfortunately will have to remove this and use map/array of strings + enum EControlTypes + { + CONTROL_VIEW_ACTIONS = 0, // Group control, for visual representation in view, not for use + CONTROL_ABOUT, + CONTROL_ORBIT, + CONTROL_PAN, + CONTROL_WORLD_MAP, + CONTROL_ZOOM, + CONTROL_INTERACTIONS, // Group control, for visual representation + CONTROL_BUILD, + //CONTROL_DRAG, + CONTROL_EDIT, + //CONTROL_MENU, + CONTROL_OPEN, + CONTROL_TOUCH, + CONTROL_WEAR, + CONTROL_MOVEMENTS, // Group control, for visual representation + CONTROL_MOVETO, + CONTROL_SIT, + CONTROL_TELEPORTTO, + CONTROL_FORWARD, + CONTROL_BACKWARD, + CONTROL_LEFT, // Check and sinc name with real movement names + CONTROL_RIGHT, + CONTROL_LSTRAFE, + CONTROL_RSTRAFE, + CONTROL_JUMP, + CONTROL_DOWN, + //CONTROL_RUN, + CONTROL_TOGGLE_RUN, + CONTROL_TOGGLE_FLY, + CONTROL_STOP, + CONTROL_CAMERA, // Group control, for visual representation + CONTROL_LOOK_UP, + CONTROL_LOOK_DOWN, + CONTROL_CAMERA_FORWARD, + CONTROL_CAMERA_BACKWARD, + CONTROL_CAMERA_FFORWARD, + CONTROL_CAMERA_FBACKWARD, + CONTROL_CAMERA_FSITTING, + CONTROL_CAMERA_BSITTING, + CONTROL_CAMERA_SOVER, + CONTROL_CAMERA_SUNDER, + CONTROL_CAMERA_SOVER_SITTING, + CONTROL_CAMERA_SUNDER_SITTING, + CONTROL_CAMERA_PANUP, + CONTROL_CAMERA_PANDOWN, + CONTROL_CAMERA_PANLEFT, + CONTROL_CAMERA_PANRIGHT, + CONTROL_CAMERA_PANIN, + CONTROL_CAMERA_PANOUT, + CONTROL_CAMERA_SPIN_CCW, + CONTROL_CAMERA_SPIN_CW, + CONTROL_CAMERA_SPIN_CCW_SITTING, + CONTROL_CAMERA_SPIN_CW_SITTING, + CONTROL_EDIT_TITLE, // Group control, for visual representation + CONTROL_EDIT_AV_SPIN_CCW, + CONTROL_EDIT_AV_SPIN_CW, + CONTROL_EDIT_AV_SPIN_OVER, + CONTROL_EDIT_AV_SPIN_UNDER, + CONTROL_EDIT_AV_MV_FORWARD, + CONTROL_EDIT_AV_MV_BACKWARD, + CONTROL_MEDIACONTENT, // Group control, for visual representation + CONTROL_PARCEL, // Play pause + CONTROL_MEDIA, // Play stop + CONTROL_VOICE, // Keep pressing for it to be ON + CONTROL_TOGGLE_VOICE, // Press once to ON/OFF + CONTROL_START_CHAT, // Press once to ON/OFF + CONTROL_START_GESTURE, // Press once to ON/OFF + CONTROL_RESERVED, // Special group control, controls that are disabled by default and not meant to be changed + CONTROL_DELETE, + CONTROL_RESERVED_MENU, + CONTROL_RESERVED_SELECT, + CONTROL_SHIFT_SELECT, + CONTROL_CNTRL_SELECT, + CONTROL_NUM_INDICES // Size, always should be last + }; + + // Note: missed selection and edition commands (would be really nice to go through selection via MB4/5 or wheel) + + LLKeyConflictHandler(); + LLKeyConflictHandler(EModes mode); + + bool canHandleControl(EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask); + bool canHandleKey(EControlTypes control_type, KEY key, MASK mask); + bool canHandleMouse(EControlTypes control_type, EMouseClickType mouse_ind, MASK mask); + bool canHandleMouse(EControlTypes control_type, S32 mouse_ind, MASK mask); //Just for convinience + bool canAssignControl(EControlTypes control_type); + void registerControl(EControlTypes control_type, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask); //todo: return conflicts? + + LLKeyData getControl(EControlTypes control_type, U32 data_index); + + static std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata); + static std::string getControlName(EControlTypes control_type); + std::string getControlString(EControlTypes control_type, U32 data_index); + + + // Drops any changes loads controls with ones from 'saved settings' or from xml + void loadFromSettings(EModes load_mode); + // Saves settings to 'saved settings' or to xml + void saveToSettings(); + + LLKeyData getDefaultControl(EControlTypes control_type, U32 data_index); + // Resets keybinding to default variant from 'saved settings' or xml + void resetToDefault(EControlTypes control_type, U32 index); + void resetToDefault(EControlTypes control_type); + void resetToDefaults(EModes mode); + void resetToDefaults(); + void resetAllToDefaults(); + + bool empty() { return mControlsMap.empty(); } + void clear(); + + bool hasUnsavedChanges() { return mHasUnsavedChanges; } + EModes getLoadedMode() { return mLoadedMode; } + // todo: conflict search + +private: + // at the moment these kind of control is not savable, but takes part will take part in conflict resolution + void registerTemporaryControl(EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask); + + typedef std::map control_map_t; + void loadFromSettings(const LLViewerKeyboard::KeyMode& keymode, control_map_t *destination); + void loadFromSettings(const EModes &load_mode, const std::string &filename, control_map_t *destination); + void resetKeyboardBindings(); + void generatePlaceholders(); //'headers' for ui and non-assignable values + + control_map_t mControlsMap; + control_map_t mDefaultsMap; + bool mHasUnsavedChanges; + EModes mLoadedMode; +}; + + +#endif // LL_LLKEYCONFLICT_H diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 6914e0fc2b..1c93a2e954 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -605,6 +605,12 @@ void start_gesture( EKeystate s ) } } +void toggle_parcel_media(EKeystate s) +{ + bool pause = LLViewerMedia::isAnyMediaPlaying(); + LLViewerMedia::setAllMediaPaused(pause); +} + #define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); REGISTER_KEYBOARD_ACTION("jump", agent_jump); REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); @@ -646,6 +652,7 @@ REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward) REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); REGISTER_KEYBOARD_ACTION("start_chat", start_chat); REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); +REGISTER_KEYBOARD_ACTION("toggle_parcel_media", toggle_parcel_media); #undef REGISTER_KEYBOARD_ACTION LLViewerKeyboard::LLViewerKeyboard() @@ -807,7 +814,7 @@ BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, c mBindings[mode][index].mFunction = function; if (index == mBindingCount[mode]) - mBindingCount[mode]++; + mBindingCount[mode]++; return TRUE; } @@ -818,21 +825,25 @@ LLViewerKeyboard::KeyBinding::KeyBinding() command("command") {} -LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode) -: bindings("binding"), - mode(_mode) +LLViewerKeyboard::KeyMode::KeyMode() +: bindings("binding") {} LLViewerKeyboard::Keys::Keys() -: first_person("first_person", KeyMode(MODE_FIRST_PERSON)), - third_person("third_person", KeyMode(MODE_THIRD_PERSON)), - edit("edit", KeyMode(MODE_EDIT)), - sitting("sitting", KeyMode(MODE_SITTING)), - edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR)) +: first_person("first_person"), + third_person("third_person"), + edit("edit"), + sitting("sitting"), + edit_avatar("edit_avatar") {} S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) { + for (S32 i = 0; i < MODE_COUNT; i++) + { + mBindingCount[i] = 0; + } + S32 binding_count = 0; Keys keys; LLSimpleXUIParser parser; @@ -840,16 +851,16 @@ S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) if (parser.readXUI(filename, keys) && keys.validateBlock()) { - binding_count += loadBindingMode(keys.first_person); - binding_count += loadBindingMode(keys.third_person); - binding_count += loadBindingMode(keys.edit); - binding_count += loadBindingMode(keys.sitting); - binding_count += loadBindingMode(keys.edit_avatar); + binding_count += loadBindingMode(keys.first_person, MODE_FIRST_PERSON); + binding_count += loadBindingMode(keys.third_person, MODE_THIRD_PERSON); + binding_count += loadBindingMode(keys.edit, MODE_EDIT); + binding_count += loadBindingMode(keys.sitting, MODE_SITTING); + binding_count += loadBindingMode(keys.edit_avatar, MODE_EDIT_AVATAR); } return binding_count; } -S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) +S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode, S32 mode) { S32 binding_count = 0; for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), @@ -857,12 +868,15 @@ S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) it != end_it; ++it) { - KEY key; - MASK mask; - LLKeyboard::keyFromString(it->key, &key); - LLKeyboard::maskFromString(it->mask, &mask); - bindKey(keymode.mode, key, mask, it->command); - binding_count++; + if (!it->key.getValue().empty()) + { + KEY key; + MASK mask; + LLKeyboard::keyFromString(it->key, &key); + LLKeyboard::maskFromString(it->mask, &mask); + bindKey(mode, key, mask, it->command); + binding_count++; + } } return binding_count; diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h index 110dc89d28..2bfe285be4 100644 --- a/indra/newview/llviewerkeyboard.h +++ b/indra/newview/llviewerkeyboard.h @@ -71,8 +71,8 @@ public: struct KeyMode : public LLInitParam::Block { Multiple bindings; - EKeyboardMode mode; - KeyMode(EKeyboardMode mode); + + KeyMode(); }; struct Keys : public LLInitParam::Block @@ -100,7 +100,7 @@ public: void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level); private: - S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode); + S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode, S32 mode); BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 8951fcf368..f8588156c1 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -35,7 +35,6 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llui.h" -#include "llkeybindings.h" #include "llkeyboard.h" #include "llagent.h" @@ -692,33 +691,38 @@ void LLVoiceClient::keyDown(KEY key, MASK mask) return; } - if (LLAgent::isActionAllowed("speak") && gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask)) + // + /*static LLCachedControl key_bind(gSavedSettings, "control_toggle_voice"); + LLKeyBind bind(key_bind); + if (LLAgent::isActionAllowed("speak") && bind().canHandleKey(key, mask)) { bool down = gKeyboard->getKeyDown(mPTTKey); if (down) { inputUserControlState(down); } - } + }*/ } void LLVoiceClient::keyUp(KEY key, MASK mask) { - if (gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask)) + /*static LLCachedControl key_bind(gSavedSettings, "control_toggle_voice"); + if (key_bind().canHandleKey(key, mask)) { bool down = gKeyboard->getKeyDown(mPTTKey); if (!down) { inputUserControlState(down); } - } + }*/ } void LLVoiceClient::updateMouseState(S32 click, MASK mask, bool down) { - if(LLAgent::isActionAllowed("speak") && gControlBindings.canHandleMouse(LLControlBindings::CONTROL_VOICE, click, mask)) + /*static LLCachedControl mouse_bind(gSavedSettings, "control_toggle_voice"); + if (mouse_bind().canHandleMouse((EMouseClickType)click, mask)) { inputUserControlState(down); - } + }*/ } diff --git a/indra/newview/skins/default/xui/en/floater_select_key.xml b/indra/newview/skins/default/xui/en/floater_select_key.xml index 4e89df5a73..c00b805954 100644 --- a/indra/newview/skins/default/xui/en/floater_select_key.xml +++ b/indra/newview/skins/default/xui/en/floater_select_key.xml @@ -7,7 +7,15 @@ height="90" layout="topleft" name="modal container" - width="240"> + width="272"> + + Keyboard + + + Mouse Buttons + - Press a key to set your Speak button trigger. + width="212"> +Press a key to set your trigger. +Allowed input: [INPUT]. - - - Never show: - - + name="all_popups" + right="-10"> + + + + + + From 09d571d460959efd4df793e748c5e00f8d2e32d9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Mar 2021 09:10:42 +0100 Subject: [PATCH 074/121] Remove filter editor limitation on 255 bytes and attribute default --- .../skins/default/xui/en/panel_preferences_alerts.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml index 0041634bf9..48cb019f63 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml @@ -414,12 +414,10 @@ height="22" right="-10" top_pad="8" - clear_button_visible="true" - label="Search Here" - max_length_bytes="255" + label="Filter Alerts" name="popup_filter" text_pad_left="6" - tool_tip="Type the search term you are interested in here. Results will be displayed for partial fulltext matches within the popup's name."> + tool_tip="Type the search term you are interested in here. Results will be displayed for partial fulltext matches."> From 9e93ff1e0b7ea2c003763cbc69f10135d8614546 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Mar 2021 09:10:57 +0100 Subject: [PATCH 075/121] Update German translation --- .../default/xui/de/panel_preferences_alerts.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml index ba5551ac3a..89c938994c 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml @@ -54,12 +54,14 @@ - - Immer anzeigen: - - - Niemals anzeigen: + + Hinweise, die immer oder niemals angezeigt werden können: + + + + + From a353b6923c45d565cae6137142a220b3f4e64cc2 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Fri, 19 Mar 2021 16:38:04 +0100 Subject: [PATCH 076/121] Updated Polish translation --- .../skins/default/xui/pl/floater_texture_ctrl.xml | 4 ++-- .../default/xui/pl/panel_preferences_alerts.xml | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml index dfa7925037..c25cf80e07 100644 --- a/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml @@ -12,7 +12,7 @@ - + Rozmiar: @@ -31,7 +31,7 @@ - + diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/pl/panel_preferences_alerts.xml index 95b7f69ae6..77e246fd64 100644 --- a/indra/newview/skins/default/xui/pl/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/pl/panel_preferences_alerts.xml @@ -45,12 +45,14 @@ - - Zawsze pokazuj: - - - Nigdy nie pokazuj: + + Ostrzeżenia przeglądarki, które można pokazać lub ukryć: + + + + + From 31b72e6bf43dca5786bf8145d8f8d9f07c14ca44 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Mar 2021 19:54:10 +0100 Subject: [PATCH 077/121] FIRE-30852: Add Deja Vu Sans as fallback font for Linux --- indra/newview/fonts/fonts.xml | 3 +++ indra/newview/fonts/fonts_celestia_medium_redux.xml | 3 +++ indra/newview/fonts/fonts_deja_vu_all_caps.xml | 3 +++ indra/newview/fonts/fonts_droid.xml | 3 +++ indra/newview/fonts/fonts_dyslexia.xml | 3 +++ indra/newview/fonts/fonts_liberation.xml | 3 +++ indra/newview/fonts/fonts_mobi.xml | 3 +++ indra/newview/fonts/fonts_noto.xml | 3 +++ indra/newview/fonts/fonts_roboto.xml | 3 +++ indra/newview/fonts/fonts_ubuntu.xml | 3 +++ 10 files changed, 30 insertions(+) diff --git a/indra/newview/fonts/fonts.xml b/indra/newview/fonts/fonts.xml index 00ce0e8b17..63b3bfe38a 100644 --- a/indra/newview/fonts/fonts.xml +++ b/indra/newview/fonts/fonts.xml @@ -32,6 +32,9 @@ STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + PingFang.ttc STIXGeneral.otf + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + Date: Fri, 19 Mar 2021 21:58:00 +0100 Subject: [PATCH 078/121] Fix layout issue with people floater options list on Vintage skin when the floater is very small, add background color settings to container_view to allow optional blank scroll_view containers --- indra/llui/llcontainerview.cpp | 10 ++++++- indra/llui/llcontainerview.h | 12 ++++++++ .../skins/vintage/xui/en/panel_people.xml | 28 ++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 727fbe850e..4b057fa62c 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -47,6 +47,10 @@ LLContainerView::LLContainerView(const LLContainerView::Params& p) : LLView(p), mShowLabel(p.show_label), mLabel(p.label), + // Add background visible flag and color to container_view so we can have blank scrollview containers + mBackgroundVisible(p.background_visible), + mBackgroundColor(p.bg_color), + // mDisplayChildren(p.display_children) { mScrollContainer = NULL; @@ -111,10 +115,14 @@ BOOL LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) void LLContainerView::draw() { + // Add background visible flag and color to container_view so we can have blank scrollview containers + if (mBackgroundVisible) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + // Add background visible flag and color to container_view so we can have blank scrollview containers + // gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor); } // Draw the label diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index 99267d978a..48ec36a861 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -47,9 +47,17 @@ public: Optional label; Optional show_label; Optional display_children; + // Add background visible flag and color to container_view so we can have blank scrollview containers + Optional background_visible; + Optional bg_color; + // Params() : label("label"), show_label("show_label", FALSE), + // Add background visible flag and color to container_view so we can have blank scrollview containers + background_visible("background_visible", true), + bg_color("bg_color", LLColor4(0.f, 0.f, 0.f, 0.25f)), + // display_children("display_children", TRUE) { changeDefault(mouse_opaque, false); @@ -90,5 +98,9 @@ public: protected: BOOL mDisplayChildren; std::string mLabel; + // Add background visible flag and color to container_view so we can have blank scrollview containers + bool mBackgroundVisible; + LLUIColor mBackgroundColor; + // }; #endif // LL_CONTAINERVIEW_ diff --git a/indra/newview/skins/vintage/xui/en/panel_people.xml b/indra/newview/skins/vintage/xui/en/panel_people.xml index 3c0e9de6a1..1325dc94c4 100644 --- a/indra/newview/skins/vintage/xui/en/panel_people.xml +++ b/indra/newview/skins/vintage/xui/en/panel_people.xml @@ -130,9 +130,31 @@ Looking for people to hang out with? Use the search box to find topics or conten name="radarmap_options_panel" label="Options" layout="topleft" + background_visible="false" + follows="all" + left="3" + height="375"> + + + height="370"> + + + + + + + + From ed0a854a51a052167907f92dcf5039af99eb3f39 Mon Sep 17 00:00:00 2001 From: Zi Ree Date: Sat, 20 Mar 2021 01:57:36 +0100 Subject: [PATCH 079/121] FIRE-30846 - FIRE-14344 - Attempt to fix Linux/Mac joystick selection, joystick graph display and button display --- indra/newview/llfloaterjoystick.cpp | 47 +++++++++++++++++-- indra/newview/llfloaterjoystick.h | 3 ++ indra/newview/llviewerjoystick.cpp | 2 + .../skins/default/xui/en/floater_joystick.xml | 7 +++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp index 3d5af0149e..48913cc91e 100644 --- a/indra/newview/llfloaterjoystick.cpp +++ b/indra/newview/llfloaterjoystick.cpp @@ -52,6 +52,11 @@ #include #endif +// FIRE-14344 - show joystick buttons +const std::string JOYSTICK_BUTTON_ON ( "\xE2\xAC\xA4" ); // U+2B24 BLACK LARGE CIRCLE +const std::string JOYSTICK_BUTTON_OFF( "\xE2\x97\xAF" ); // U+25EF WHITE LARGE CIRCLE +// + static LLTrace::SampleStatHandle<> sJoystickAxis0("Joystick axis 0"), sJoystickAxis1("Joystick axis 1"), sJoystickAxis2("Joystick axis 2"), @@ -68,7 +73,6 @@ static LLTrace::SampleStatHandle<>* sJoystickAxes[6] = &sJoystickAxis5 }; - #if LL_WINDOWS && !LL_MESA_HEADLESS BOOL CALLBACK di8_list_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef) @@ -113,10 +117,20 @@ void LLFloaterJoystick::draw() refreshListOfDevices(); } + // FIRE-14344 - If we are on the login screen, this value will be 0.0f and we know we + // have to update the joystick status by ourselves + if (gFrameIntervalSeconds.value() == 0.0f) + { + joystick->updateStatus(); + } + // + for (U32 i = 0; i < 6; i++) { F32 value = joystick->getJoystickAxis(i); - sample(*sJoystickAxes[i], value * gFrameIntervalSeconds.value()); + // FIRE-14344 - using the frame interval seems to break the graphs + // sample(*sJoystickAxes[i], value * gFrameIntervalSeconds.value()); + sample(*sJoystickAxes[i], value); if (mAxisStatsBar[i]) { F32 minbar, maxbar; @@ -129,6 +143,23 @@ void LLFloaterJoystick::draw() } } + // FIRE-14344 - show joystick buttons + std::string buttons; + for (U32 i = 0; i < 16; i++) + { + U32 value = joystick->getJoystickButton(i); + if (value == 0) + { + buttons += JOYSTICK_BUTTON_OFF; + } + else + { + buttons += JOYSTICK_BUTTON_ON; + } + } + mJoystickButtons->setText(buttons); + // + LLFloater::draw(); } @@ -137,7 +168,10 @@ BOOL LLFloaterJoystick::postBuild() center(); // Micro Save on calls to gSavedSettings //F32 range = gSavedSettings.getBOOL("Cursor3D") ? 128.f : 2.f; - F32 range = m3DCursor ? 128.f : 2.f; + // FIRE-14344 - use 1.f for the graph ranges instead of 128.f : 2.f to get better resolution - + // needs testing with an actual absolute pointer device if the range scales in a useful way when + // not starting at 128.f + F32 range = 1.f; for (U32 i = 0; i < 6; i++) { @@ -160,6 +194,9 @@ BOOL LLFloaterJoystick::postBuild() childSetAction("cancel_btn", onClickCancel, this); childSetAction("ok_btn", onClickOK, this); + // FIRE-14344 - show joystick buttons + mJoystickButtons = getChild("joystick_buttons"); + refresh(); refreshListOfDevices(); return TRUE; @@ -294,7 +331,9 @@ void LLFloaterJoystick::refreshListOfDevices() std::string desc = LLViewerJoystick::getInstance()->getDescription(); if (!desc.empty()) { - LLSD value = LLSD::Integer(0); + // FIRE-30846 - Select the first detected device, don't fall back to 0 which will select "None" + // LLSD value = LLSD::Integer(0); + LLSD value = LLSD::Integer(1); addDevice(desc, value); mHasDeviceList = true; } diff --git a/indra/newview/llfloaterjoystick.h b/indra/newview/llfloaterjoystick.h index 1d46efd3f6..5bda3a0b1e 100644 --- a/indra/newview/llfloaterjoystick.h +++ b/indra/newview/llfloaterjoystick.h @@ -97,6 +97,9 @@ private: // stats view LLStatBar* mAxisStatsBar[6]; + + // FIRE-14344 - show joystick buttons + LLTextBase* mJoystickButtons; }; #endif diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index d8bb7c1960..030f0b6cd2 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -1427,6 +1427,8 @@ bool LLViewerJoystick::isLikeSpaceNavigator() const #if LIB_NDOF return (isJoystickInitialized() && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 + // FIRE-30846 - Add combined product string that is returned by some devices + || strncmp(mNdofDev->product, "3Dconnexion SpaceNavigator", 26) == 0 || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0 || strncmp(mNdofDev->product, "SpacePilot", 10) == 0)); diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml index 64cae4f4d7..a9759c7d4d 100644 --- a/indra/newview/skins/default/xui/en/floater_joystick.xml +++ b/indra/newview/skins/default/xui/en/floater_joystick.xml @@ -268,6 +268,13 @@ name="axis5" tick_spacing="0.5" /> + Date: Sat, 20 Mar 2021 13:45:54 +0100 Subject: [PATCH 080/121] FIRE-30833: Add Cascadia Code font both as option for the UI as for the script editor --- indra/llrender/llfontgl.cpp | 11 +++++++++++ indra/llrender/llfontgl.h | 1 + indra/llrender/llfontregistry.cpp | 2 ++ indra/newview/fonts/fonts.xml | 9 +++++++++ indra/newview/fonts/fonts_celestia_medium_redux.xml | 9 +++++++++ indra/newview/fonts/fonts_deja_vu_all_caps.xml | 9 +++++++++ indra/newview/fonts/fonts_droid.xml | 11 ++++++++++- indra/newview/fonts/fonts_dyslexia.xml | 9 +++++++++ indra/newview/fonts/fonts_liberation.xml | 9 +++++++++ indra/newview/fonts/fonts_mobi.xml | 9 +++++++++ indra/newview/fonts/fonts_noto.xml | 9 +++++++++ indra/newview/fonts/fonts_roboto.xml | 9 +++++++++ indra/newview/fonts/fonts_ubuntu.xml | 9 +++++++++ .../default/xui/de/panel_preferences_firestorm.xml | 2 ++ .../default/xui/en/panel_preferences_firestorm.xml | 2 ++ indra/newview/skins/default/xui/en/strings.xml | 1 + 16 files changed, 110 insertions(+), 1 deletion(-) diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index fd6787a660..e113c08eae 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -1100,6 +1100,13 @@ LLFontGL* LLFontGL::getFontOCRA() static LLFontGL* fontp = getFont(LLFontDescriptor("OCRA","Monospace",0)); return fontp; } + +//static +LLFontGL* LLFontGL::getFontCascadia() +{ + static LLFontGL* fontp = getFont(LLFontDescriptor("Cascadia", "Cascadia", 0)); + return fontp; +} // //static @@ -1152,6 +1159,10 @@ LLFontGL* LLFontGL::getFontByName(const std::string& name) { return getFontMonospace(); } + else if (name == "Cascadia") + { + return getFontCascadia(); + } // else { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index acc922b752..5b03fc67b1 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -193,6 +193,7 @@ public: // Advanced script editor static LLFontGL* getFontScripting(); static LLFontGL* getFontOCRA(); + static LLFontGL* getFontCascadia(); // static LLFontGL* getFontExtChar(); static LLFontGL* getFont(const LLFontDescriptor& desc); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index a8cdce95e9..18f4fc4271 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -169,6 +169,8 @@ LLFontDescriptor LLFontDescriptor::normalize() const // Advanced script editor if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Scripting")) new_size = "Scripting"; + if (new_size != s_template_string && new_size.empty() && findSubString(new_name, "Cascadia")) + new_size = "Cascadia"; // if (new_size.empty()) new_size = "Medium"; diff --git a/indra/newview/fonts/fonts.xml b/indra/newview/fonts/fonts.xml index 63b3bfe38a..4d921b7a5d 100644 --- a/indra/newview/fonts/fonts.xml +++ b/indra/newview/fonts/fonts.xml @@ -91,6 +91,11 @@ ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSansAllCaps.ttf @@ -173,6 +178,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf - + + + CascadiaCode-Light.ttf + + DroidSans.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + opendyslexic.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="9.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + MobiSans.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + NotoSansCombined-Regular.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + Roboto-Regular.ttf @@ -175,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -179,6 +184,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + + + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml index 8a37d12c16..c3b8bf37b6 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml @@ -1295,6 +1295,7 @@ tool_tip="Name of the font used in the LSL script editor"> + + diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 32e647e166..b17b350868 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3193,6 +3193,7 @@ Your current position: [AVATAR_POS] Deja Vu All Caps Noto Sans Celestia Medium Redux + Cascadia Code Unknown Mode [APP_NAME] Phoenix From 3c40d98558b85fb3824737c3cdfaedea8d4fda39 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 20 Mar 2021 13:46:42 +0100 Subject: [PATCH 081/121] FIRE-30833: Maybe add the new files as well... --- indra/newview/fonts/CascadiaCode-Light.ttf | Bin 0 -> 285648 bytes indra/newview/fonts/CascadiaCodeFONTLOG.txt | 80 ++++++++ indra/newview/fonts/CascadiaCodeLICENSE.txt | 94 +++++++++ indra/newview/fonts/fonts_cascadia_code.xml | 207 ++++++++++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 indra/newview/fonts/CascadiaCode-Light.ttf create mode 100644 indra/newview/fonts/CascadiaCodeFONTLOG.txt create mode 100644 indra/newview/fonts/CascadiaCodeLICENSE.txt create mode 100644 indra/newview/fonts/fonts_cascadia_code.xml diff --git a/indra/newview/fonts/CascadiaCode-Light.ttf b/indra/newview/fonts/CascadiaCode-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0abd90a372c60fa99e30acf6a90265a18a86b345 GIT binary patch literal 285648 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%0A5vRwE^jl~LcoG} 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�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 Ofji}diVO@yBL4%HqipB^ literal 0 HcmV?d00001 diff --git a/indra/newview/fonts/CascadiaCodeFONTLOG.txt b/indra/newview/fonts/CascadiaCodeFONTLOG.txt new file mode 100644 index 0000000000..ed62cf14d2 --- /dev/null +++ b/indra/newview/fonts/CascadiaCodeFONTLOG.txt @@ -0,0 +1,80 @@ +FONTLOG for Cascadia Code +=================================== + +This file provides detailed information on the Cascadia Code font. This information should be distributed along with the Cascadia Code font and any derivative works. + +Basic Font Information +----------------------------------- + +Cascadia Code is a monospaced font shipped by Microsoft. It includes programming ligatures and is intended for use in terminal applications as well as text editors. + +Contribution Information +----------------------------------- + +Documentation and contribution guidelines can be found at: https://github.com/microsoft/cascadia-code + +ChangeLog +----------------------------------- + +09-25-2019 (Aaron Bell) Bug Fixes and Improvements + +09-18-2019 (Aaron Bell) Cascadia Code Version 1909.16 +- Initial release + +10-04-2019 (Aaron Bell) Cascadia Code Version 1910.04 +- Addition of Latin-1 characters +- Addition of box drawing glyphs +11-20-2019 (Aaron Bell) Cascadia Code Version 1911.20 +- Cyrillic, Greek, Vietnamese, and many other symbols added +- Font converted to UFO with a build workflow +- font rehinted +- many other fixes! + +04-29-2020 (Aaron Bell) April bug fixes +- Many small bug fixes from the last few months + +05-05-2020 Font vertical metrics locked + +05-15-2020 (Aaron Bell, Dustin Howett) [Version 2005.15] +Fixed WinDescent value to improve line spacing + +06-29-2020 (Aaron Bell) Variable font conversion +Converted Cascadia Code to a Variable font with a weight axis + + +06-29-2020 (Aaron Bell) [Version 2007.01] +Reworked Cascadia Code to be a variable font with 6 named variations ranging +from ExtraLight to Bold. +Split the long equals ligature (==) for earier reading +Removed the hexadeciaml 'x' alternate +Corrected malformed IJacute dioacritics +Introduced a compatibility layout for PowerLine glyphs that will +be used in GDI + +08-25-2020 (Aaron Bell) [Version 2008.25] +Started generating static TTFs and hinting them with ttfautohint +Aligned the powerline glyphs to the font metrics exactly +Realigned the box drawing glyphs to match the letters +Added anchors to all alphabetic characters (and brevecomb-cy) +added fi/fl ligatures (decomposed!) +Fixed the weight of horncomb in bold + +9-14-2020 +1) Added support for Salishan language groups +2) Remastered mark positioning for glyphs with 2 diacritics (needed for Salishan) +3) Rebuild Build Script to simplify code +4) Fixed minor other hinting issues +5) Fixed RVRN substitutions for heavy weight + +9-21-2020 +1) Correcting mark positioning for salishan + +Acknowledgements +----------------------------------- + +If you make modifications, be sure to add your name (N), email (E), web-address (if you have one) (W), and description (D). This list is in alphabetical order. + +N: Aaron Bell +E: aaron@sajatypeworks.com +W: http://sajatypeworks.com +D: Original font designer diff --git a/indra/newview/fonts/CascadiaCodeLICENSE.txt b/indra/newview/fonts/CascadiaCodeLICENSE.txt new file mode 100644 index 0000000000..40f856f6ec --- /dev/null +++ b/indra/newview/fonts/CascadiaCodeLICENSE.txt @@ -0,0 +1,94 @@ +Copyright (c) 2019 - Present, Microsoft Corporation, +with Reserved Font Name Cascadia Code. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/indra/newview/fonts/fonts_cascadia_code.xml b/indra/newview/fonts/fonts_cascadia_code.xml new file mode 100644 index 0000000000..7467601597 --- /dev/null +++ b/indra/newview/fonts/fonts_cascadia_code.xml @@ -0,0 +1,207 @@ + + + + + CascadiaCode-Light.ttf + + meiryo.TTC + YuGothR.ttc + MSGOTHIC.TTC + gulim.ttc + simhei.ttf + ArialUni.ttf + msyh.ttc + seguisym.ttf + nirmala.ttf + tahoma.ttf + Cambria.ttc + micross.ttf + malgun.ttf + + + ヒラギノ角ゴシック W3.ttc + ヒラギノ角ゴ Pro W3.otf + ヒラギノ角ゴ ProN W3.otf + ヒラギノ明朝 ProN W3.ttc + AppleGothic.dfont + AppleGothic.ttf + AppleSDGothicNeo-Regular.otf + AppleSDGothicNeo.ttc + 华文细黑.ttf + PingFang.ttc + STIXGeneral.otf + Thonburi.ttc + + + DejaVuSans.ttf + + + + + CascadiaCode-Light.ttf + + arialbd.ttf + + + Helvetica.dfont + + + + + CascadiaCode-Light.ttf + + arial.ttf + + + Helvetica.dfont + + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + SourceCodePro-Regular.ttf + + + + ocra.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + arial.ttf + + + arial.ttf + + + + + CascadiaCode-Light.ttf + + arialbd.ttf + + + arialbd.ttf + + + + + CascadiaCode-Light.ttf + + ariali.ttf + + + ariali.ttf + + + + + CascadiaCode-Light.ttf + + arialbi.ttf + + + arialbi.ttf + + + + + times.ttf + CascadiaCode-Light.ttf + + + + + + + + + + + From 3e404be2d1cda47058eacc2128a1c8c4ebbadb70 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Sat, 20 Mar 2021 15:15:05 +0100 Subject: [PATCH 082/121] Updated Polish translation --- .../skins/vintage/xui/pl/panel_people.xml | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/indra/newview/skins/vintage/xui/pl/panel_people.xml b/indra/newview/skins/vintage/xui/pl/panel_people.xml index 108ef0f203..690a84462e 100644 --- a/indra/newview/skins/vintage/xui/pl/panel_people.xml +++ b/indra/newview/skins/vintage/xui/pl/panel_people.xml @@ -38,35 +38,41 @@ Chcesz spotkać ludzi? Spróbuj użyć [secondlife:///app/worldmap Mapy Świata] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2933c664414521da95d954bde59a21306ceda74b Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 20 Mar 2021 16:17:13 +0100 Subject: [PATCH 083/121] Fix outfit wearables ordering never getting updated when updating an existing outfit, which could lead to displaying incorrect outfit state --- indra/newview/llappearancemgr.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6b3699de30..5611f6adfb 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -3456,7 +3456,7 @@ void LLAppearanceMgr::updateIsDirty() { LLViewerInventoryItem *item = *i; - if (item->getName() == FSLSLBridge::instance().currentFullName()) + if (FSLSLBridge::instance().isBridgeValid() && item && item->getLinkedUUID() == FSLSLBridge::instance().getBridge()->getUUID()) { cof_items.erase( i ); break; @@ -3688,6 +3688,19 @@ void update_base_outfit_after_ordering() } } + // Exclude LSL bridge or the following size check will always fail! + for (LLInventoryModel::item_array_t::iterator i = cof_item_array.begin(); i != cof_item_array.end(); ++i) + { + LLViewerInventoryItem *item = *i; + + if (FSLSLBridge::instance().isBridgeValid() && item && item->getLinkedUUID() == FSLSLBridge::instance().getBridge()->getUUID()) + { + cof_item_array.erase(i); + break; + } + } + // + if (outfit_item_array.size() != cof_item_array.size()) { return; From 52abd2d9a5b0b6323424756ba7eac70f69567f39 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Sun, 21 Mar 2021 13:03:35 +0100 Subject: [PATCH 084/121] FIRE-30857 Russian translation update, by Romka Swallowtail --- .../xui/ru/panel_preferences_alerts.xml | 12 ++-- .../skins/vintage/xui/ru/panel_people.xml | 67 ++++++++++--------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/indra/newview/skins/default/xui/ru/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/ru/panel_preferences_alerts.xml index 60614cc91b..6731aa5556 100644 --- a/indra/newview/skins/default/xui/ru/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/ru/panel_preferences_alerts.xml @@ -45,12 +45,14 @@ - - Всегда показывать: - - - Никогда не показывать: + + Оповещения которые можно показать или скрыть: + + + + + diff --git a/indra/newview/skins/vintage/xui/ru/panel_people.xml b/indra/newview/skins/vintage/xui/ru/panel_people.xml index f591b48ca7..4b6e575b8e 100644 --- a/indra/newview/skins/vintage/xui/ru/panel_people.xml +++ b/indra/newview/skins/vintage/xui/ru/panel_people.xml @@ -37,41 +37,46 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - From d6401237816c668d472004aa46db1e0849a667a6 Mon Sep 17 00:00:00 2001 From: Beq Date: Fri, 12 Mar 2021 18:23:53 +0000 Subject: [PATCH 085/121] Incorporate Tracy profiler as 3p --- indra/CMakeLists.txt | 9 ++++ indra/cmake/00-Common.cmake | 6 ++- indra/cmake/CMakeLists.txt | 1 + indra/cmake/LLCommon.cmake | 9 ++++ indra/cmake/Tracy.cmake | 15 +++++++ indra/llcommon/CMakeLists.txt | 4 ++ indra/llcommon/FSTracyClient.cpp | 58 +++++++++++++++++++++++++ indra/llcommon/llfasttimer.cpp | 9 +++- indra/llcommon/llfasttimer.h | 26 ++++++++++- indra/newview/app_settings/settings.xml | 11 +++++ indra/newview/llappviewer.cpp | 16 ++++++- indra/newview/llviewerdisplay.cpp | 7 +-- indra/newview/llvovolume.cpp | 3 +- indra/newview/pipeline.cpp | 2 +- scripts/configure_firestorm.sh | 13 +++++- 15 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 indra/cmake/Tracy.cmake create mode 100644 indra/llcommon/FSTracyClient.cpp diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 1beedb4961..b49e5b1668 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -76,6 +76,15 @@ endif (USE_AVX_OPTIMIZATION) add_subdirectory(cmake) +# Tracy Profiler support +option(USE_TRACY_PROFILER "Tracy Profiler support" OFF) +if (USE_TRACY_PROFILER) + message(STATUS "Compiling with Tracy profiler") +else (USE_TRACY_PROFILER) + message(STATUS "Compiling without Tracy profiler") +endif (USE_TRACY_PROFILER) +# Tracy Profiler support + add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index ce8b396cc1..97f8e8690e 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -139,7 +139,11 @@ if (WINDOWS) # /arch:SSE2 /fp:fast ) - +# Add Tracy profiler support + if (USE_TRACY_PROFILER) + add_definitions( /DTRACY_ENABLE /DTRACY_NO_FASTTIMERS ) + endif() +# # Nicky: x64 implies SSE2 if( ADDRESS_SIZE EQUAL 32 ) add_definitions( /arch:SSE2 ) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 55e24df9b5..e8dcc56287 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -88,6 +88,7 @@ set(cmake_SOURCE_FILES PulseAudio.cmake Python.cmake TemplateCheck.cmake + Tracy.cmake Tut.cmake UI.cmake UnixInstall.cmake diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 8900419f9b..8c209fdb7e 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -4,12 +4,21 @@ include(APR) include(Boost) include(EXPAT) include(ZLIB) +include(Tracy) # Tracy profiler +# Add Tracy profiler support +#set(LLCOMMON_INCLUDE_DIRS +# ${LIBS_OPEN_DIR}/llcommon +# ${APRUTIL_INCLUDE_DIR} +# ${APR_INCLUDE_DIR} +# ) set(LLCOMMON_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llcommon ${APRUTIL_INCLUDE_DIR} ${APR_INCLUDE_DIR} + ${TRACY_INCLUDE_DIR} ) +# set(LLCOMMON_SYSTEM_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) diff --git a/indra/cmake/Tracy.cmake b/indra/cmake/Tracy.cmake new file mode 100644 index 0000000000..db1e609fb8 --- /dev/null +++ b/indra/cmake/Tracy.cmake @@ -0,0 +1,15 @@ +# Tracy Profiler support. +if (USE_TRACY_PROFILER) + include(Prebuilt) + use_prebuilt_binary(Tracy) + if (WINDOWS) + # set(TRACY_LIBRARIES + # ${ARCH_PREBUILT_DIRS_RELEASE}/Tracy.lib + add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS) + set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy) + elseif (DARWIN) + message(FATAL_ERROR "Tracy is not yet implemented in FS for OSX.") + else (WINDOWS) + message(FATAL_ERROR "Tracy is not yet implemented in FS for Linux.") + endif (WINDOWS) +endif (USE_TRACY_PROFILER) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index cd59780a0f..879a7c4a42 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -256,6 +256,10 @@ set(llcommon_HEADER_FILES ) # Add all nd* files. memory pool, intrinsics, ... +# Tracy Profiler support +if (USE_TRACY_PROFILER) + list(APPEND llcommon_SOURCE_FILES FSTracyClient.cpp) +endif() SET( llcommon_ND_SOURCE_FILES nd/ndexceptions.cpp diff --git a/indra/llcommon/FSTracyClient.cpp b/indra/llcommon/FSTracyClient.cpp new file mode 100644 index 0000000000..1b9a86eb5d --- /dev/null +++ b/indra/llcommon/FSTracyClient.cpp @@ -0,0 +1,58 @@ +// +// Tracy profiler +// ---------------- +// +// For fast integration, compile and +// link with this source file (and none +// other) in your executable (or in the +// main DLL / shared object on multi-DLL +// projects). +// + +// Define TRACY_ENABLE to enable profiler. +// #define __CYGWIN__ +#include "common/TracySystem.cpp" + +#ifdef TRACY_ENABLE + +#ifdef _MSC_VER +# pragma warning(push, 0) +#endif + +#include "common/tracy_lz4.cpp" +#include "client/TracyProfiler.cpp" +#include "client/TracyCallstack.cpp" +#include "client/TracySysTime.cpp" +#ifndef __CYGWIN__ +#define __CYGWIN__ +#include "client/TracySysTrace.cpp" +#undef __CYGWIN__ +#else +#include "client/TracySysTrace.cpp" +#endif +#include "common/TracySocket.cpp" +#include "client/tracy_rpmalloc.cpp" +#include "client/TracyDxt1.cpp" + +#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +# include "libbacktrace/alloc.cpp" +# include "libbacktrace/dwarf.cpp" +# include "libbacktrace/fileline.cpp" +# include "libbacktrace/mmapio.cpp" +# include "libbacktrace/posix.cpp" +# include "libbacktrace/sort.cpp" +# include "libbacktrace/state.cpp" +# if TRACY_HAS_CALLSTACK == 4 +# include "libbacktrace/macho.cpp" +# else +# include "libbacktrace/elf.cpp" +# endif +#endif + +#ifdef _MSC_VER +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "dbghelp.lib") +# pragma warning(pop) +#endif + +#endif \ No newline at end of file diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index ab93713b44..ac3e260fb1 100644 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -54,13 +54,20 @@ #else #error "architecture not supported" #endif +// are we actively profiling? +#ifdef TRACY_ENABLE +namespace LLTrace +{ + bool active{false}; +} +// +#endif namespace LLTrace { ////////////////////////////////////////////////////////////////////////////// // statics - bool BlockTimer::sLog = false; std::string BlockTimer::sLogName = ""; bool BlockTimer::sMetricLog = false; diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index f3405ea993..e0df4f5277 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -38,7 +38,31 @@ #define LL_FAST_TIMER_ON 1 #define LL_FASTTIMER_USE_RDTSC 1 -#define LL_RECORD_BLOCK_TIME(timer_stat) const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +// Add Tracy profiler support +// #define LL_RECORD_BLOCK_TIME(timer_stat) \ +// const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#ifdef TRACY_ENABLE + +#include "Tracy.hpp" + +namespace LLTrace +{ + extern bool active; +} +// #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well. +#ifdef TRACY_NO_FASTTIMERS +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +ZoneNamedN( ___tracy_scoped_zone, #timer_stat , LLTrace::active); +#else // TRACY_NO_FASTTIMERS +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +ZoneNamedN( ___tracy_scoped_zone, #timer_stat , LLTrace::active); \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#endif // TRACY_NO_FASTTIMERS +#else // TRACY_ENABLE +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#endif // TRACY_ENABLE +// namespace LLTrace { diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4c4cc72d53..9983a79b54 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -24628,6 +24628,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + FSTracyEnableWhenConnected + + Comment + Enable profiling as soon as a server connects + Persist + 1 + Type + Boolean + Value + 1 + FSFilterGrowlKeywordDuplicateIMs Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 67ebdb43e2..8b42cc7035 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1652,7 +1652,19 @@ bool LLAppViewer::doFrame() { LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); LLSD newFrame; - +// Tracy enabling + static bool one_time{false}; + static LLCachedControl tracy_enable_when_connected(gSavedSettings, "FSTracyEnableWhenConnected"); + if( !one_time && (gFrameCount % 10 == 0) ) + { + if(!LLTrace::active && tracy_enable_when_connected && TracyIsConnected) + { + LLTrace::active = true; + one_time=true; // prevent reset race if we disable manually. + LL_INFOS() << "Tracy profiler or collector connected" << LL_ENDL; + } + } +// // MaxFPS Viewer-Chui merge error LLTimer periodicRenderingTimer; BOOL restore_rendering_masks = FALSE; @@ -1953,7 +1965,7 @@ bool LLAppViewer::doFrame() LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - + FrameMark; // Tracy support delineate Frame return ! LLApp::isRunning(); } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index e955166827..bd5a3e718e 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1427,8 +1427,9 @@ void render_ui(F32 zoom_factor, int subfield) // Finalize scene gPipeline.renderFinalize(); - LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); - render_hud_elements(); + {// give unique scope + LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); + render_hud_elements(); // [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) if (RlvActions::hasBehaviour(RLV_BHVR_SETOVERLAY)) { @@ -1436,7 +1437,7 @@ void render_ui(F32 zoom_factor, int subfield) } // [/RLVa:KB] render_hud_attachments(); - + } // unique scope LLGLSDefault gls_default; LLGLSUIDefault gls_ui; { diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 3167f7cce5..d848810eeb 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -6349,9 +6349,10 @@ static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh"); void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) { llassert(group); + LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB);// move out one scope (but are these even useful as dupes?) if (group && group->hasState(LLSpatialGroup::MESH_DIRTY) && !group->hasState(LLSpatialGroup::GEOM_DIRTY)) { - LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB); + // LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB);// move out one scope (but are these even useful as dupes?) LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_GEN_DRAW_INFO); //make sure getgeometryvolume shows up in the right place in timers group->mBuilt = 1.f; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 6b9f27a101..0a5e59418d 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4753,7 +4753,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera) { LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomDeferred"); - LL_RECORD_BLOCK_TIME(FTM_RENDER_GEOMETRY); + // LL_RECORD_BLOCK_TIME(FTM_RENDER_GEOMETRY); remove duplicate zone scope LL_RECORD_BLOCK_TIME(FTM_DEFERRED_POOLS); diff --git a/scripts/configure_firestorm.sh b/scripts/configure_firestorm.sh index 1f18503aca..4ba88357f3 100755 --- a/scripts/configure_firestorm.sh +++ b/scripts/configure_firestorm.sh @@ -39,6 +39,7 @@ WANTS_SINGLEGRID=$FALSE WANTS_AVX=$FALSE WANTS_AVX2=$FALSE WANTS_TESTBUILD=$FALSE +WANTS_TRACY=$FALSE WANTS_BUILD=$FALSE WANTS_CRASHREPORTING=$FALSE TARGET_PLATFORM="darwin" # darwin, windows, linux @@ -77,6 +78,7 @@ showUsage() echo " --singlegrid : Build for single grid usage (Requires --opensim)" echo " --avx : Build with Advanced Vector Extensions" echo " --avx2 : Build with Advanced Vector Extensions 2" + echo " --tracy : Build with Tracy Profiler support" echo " --crashreporting : Build with crash reporting enabled" echo " --testbuild : Create time-limited test build (build date + )" echo " --platform : Build for specified platform (darwin | windows | linux)" @@ -92,7 +94,7 @@ getArgs() # $* = the options passed in from main { if [ $# -gt 0 ]; then - while getoptex "clean build config version package no-package fmodstudio openal ninja vscode jobs: platform: kdu opensim no-opensim singlegrid: avx avx2 crashreporting testbuild: help chan: btype:" "$@" ; do + while getoptex "clean build config version package no-package fmodstudio openal ninja vscode jobs: platform: kdu opensim no-opensim singlegrid: avx avx2 tracy crashreporting testbuild: help chan: btype:" "$@" ; do #ensure options are valid if [ -z "$OPTOPT" ] ; then @@ -119,6 +121,7 @@ getArgs() ;; avx) WANTS_AVX=$TRUE;; avx2) WANTS_AVX2=$TRUE;; + tracy) WANTS_TRACY=$TRUE;; crashreporting) WANTS_CRASHREPORTING=$TRUE;; testbuild) WANTS_TESTBUILD=$TRUE TESTBUILD_PERIOD="$OPTARG" @@ -308,6 +311,7 @@ else fi echo -e " AVX: `b2a $WANTS_AVX`" | tee -a $LOG echo -e " AVX2: `b2a $WANTS_AVX2`" | tee -a $LOG +echo -e " TRACY: `b2a $WANTS_TRACY`" | tee -a $LOG echo -e " CRASHREPORTING: `b2a $WANTS_CRASHREPORTING`" | tee -a $LOG if [ $WANTS_TESTBUILD -eq $TRUE ] ; then echo -e " TESTBUILD: `b2a $WANTS_TESTBUILD` ($TESTBUILD_PERIOD days)" | tee -a $LOG @@ -463,6 +467,11 @@ if [ $WANTS_CONFIG -eq $TRUE ] ; then else AVX2_OPTIMIZATION="-DUSE_AVX2_OPTIMIZATION:BOOL=OFF" fi + if [ $WANTS_TRACY -eq $TRUE ] ; then + TRACY_PROFILER="-DUSE_TRACY_PROFILER:BOOL=ON" + else + TRACY_PROFILER="-DUSE_TRACY_PROFILER:BOOL=OFF" + fi if [ $WANTS_TESTBUILD -eq $TRUE ] ; then TESTBUILD="-DTESTBUILD:BOOL=ON -DTESTBUILDPERIOD:STRING=$TESTBUILD_PERIOD" else @@ -531,7 +540,7 @@ if [ $WANTS_CONFIG -eq $TRUE ] ; then UNATTENDED="-DUNATTENDED=ON" fi - cmake -G "$TARGET" ../indra $CHANNEL ${GITHASH} $FMODSTUDIO $OPENAL $KDU $OPENSIM $SINGLEGRID $AVX_OPTIMIZATION $AVX2_OPTIMIZATION $TESTBUILD $PACKAGE \ + cmake -G "$TARGET" ../indra $CHANNEL ${GITHASH} $FMODSTUDIO $OPENAL $KDU $OPENSIM $SINGLEGRID $AVX_OPTIMIZATION $AVX2_OPTIMIZATION $TRACY_PROFILER $TESTBUILD $PACKAGE \ $UNATTENDED -DLL_TESTS:BOOL=OFF -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE -DCMAKE_BUILD_TYPE:STRING=$BTYPE \ $CRASH_REPORTING -DVIEWER_SYMBOL_FILE:STRING="${VIEWER_SYMBOL_FILE:-}" -DROOT_PROJECT_NAME:STRING=Firestorm $LL_ARGS_PASSTHRU ${VSCODE_FLAGS:-} | tee $LOG From 07d2b1b6fddd796a53f8866797c5068012aa6dcb Mon Sep 17 00:00:00 2001 From: Beq Date: Fri, 12 Mar 2021 18:24:45 +0000 Subject: [PATCH 086/121] benchmark for FileSystem cache changes --- indra/llfilesystem/llfilesystem.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index c952cb2f1a..8e5bc6406d 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -34,6 +34,8 @@ #include "llfasttimer.h" #include "lldiskcache.h" +#define FSZoneScoped ZoneNamedC( ___tracy_scoped_zone, tracy::Color::Gold, LLTrace::active); + const S32 LLFileSystem::READ = 0x00000001; const S32 LLFileSystem::WRITE = 0x00000002; const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE @@ -57,6 +59,7 @@ LLFileSystem::~LLFileSystem() // static bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { + FSZoneScoped; std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -85,6 +88,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) // { + FSZoneScoped; std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -102,6 +106,7 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, const LLUUID& new_file_id, const LLAssetType::EType new_file_type) { + FSZoneScoped; std::string old_id_str; old_file_id.toString(old_id_str); const std::string extra_info = ""; @@ -132,6 +137,7 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp // static S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { + FSZoneScoped; std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -157,6 +163,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi BOOL LLFileSystem::read(U8* buffer, S32 bytes) { + FSZoneScoped; BOOL success = TRUE; std::string id; @@ -215,16 +222,19 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes) S32 LLFileSystem::getLastBytesRead() { + FSZoneScoped; return mBytesRead; } BOOL LLFileSystem::eof() { + FSZoneScoped; return mPosition >= getSize(); } BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { + FSZoneScoped; std::string id_str; mFileID.toString(id_str); const std::string extra_info = ""; @@ -334,6 +344,7 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) BOOL LLFileSystem::seek(S32 offset, S32 origin) { + FSZoneScoped; if (-1 == origin) { origin = mPosition; @@ -364,22 +375,26 @@ BOOL LLFileSystem::seek(S32 offset, S32 origin) S32 LLFileSystem::tell() const { + FSZoneScoped; return mPosition; } S32 LLFileSystem::getSize() { + FSZoneScoped; return LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() { + FSZoneScoped; // offer up a huge size since we don't care what the max is return INT_MAX; } BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) { + FSZoneScoped; LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; @@ -390,6 +405,7 @@ BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_typ BOOL LLFileSystem::remove() { + FSZoneScoped; LLFileSystem::removeFile(mFileID, mFileType); return TRUE; From bd20cfad1d4184756b56d88d669bf1ed208c8f7b Mon Sep 17 00:00:00 2001 From: Beq Date: Thu, 18 Mar 2021 14:47:12 +0000 Subject: [PATCH 087/121] Ensure clean compile when 3p-Tracy not installed --- indra/llcommon/FSTracyClient.cpp | 9 + indra/llcommon/fsprofiler.h | 32 ++ indra/llcommon/llfasttimer.cpp | 8 - indra/llcommon/llfasttimer.h | 12 +- indra/llfilesystem/llfilesystem.cpp | 43 +- indra/newview/llappviewer.cpp | 10 +- indra/tools/vstool/DispatchUtility.cs | 271 ---------- indra/tools/vstool/README.txt | 9 - indra/tools/vstool/VSTool.csproj | 98 ---- indra/tools/vstool/VSTool.exe | Bin 24576 -> 0 bytes indra/tools/vstool/VSTool.sln | 19 - indra/tools/vstool/app.config | 3 - indra/tools/vstool/main.cs | 733 -------------------------- 13 files changed, 78 insertions(+), 1169 deletions(-) create mode 100644 indra/llcommon/fsprofiler.h delete mode 100644 indra/tools/vstool/DispatchUtility.cs delete mode 100644 indra/tools/vstool/README.txt delete mode 100755 indra/tools/vstool/VSTool.csproj delete mode 100755 indra/tools/vstool/VSTool.exe delete mode 100755 indra/tools/vstool/VSTool.sln delete mode 100644 indra/tools/vstool/app.config delete mode 100755 indra/tools/vstool/main.cs diff --git a/indra/llcommon/FSTracyClient.cpp b/indra/llcommon/FSTracyClient.cpp index 1b9a86eb5d..d40c042113 100644 --- a/indra/llcommon/FSTracyClient.cpp +++ b/indra/llcommon/FSTracyClient.cpp @@ -14,6 +14,15 @@ #include "common/TracySystem.cpp" #ifdef TRACY_ENABLE +// are we actively profiling? +// At some point this should move to fsprofiler.cpp to correspond with the headerfile +#ifdef TRACY_ENABLE +namespace FSProfiler +{ + bool active{false}; +} +#endif +// #ifdef _MSC_VER # pragma warning(push, 0) diff --git a/indra/llcommon/fsprofiler.h b/indra/llcommon/fsprofiler.h new file mode 100644 index 0000000000..41ffa6a17f --- /dev/null +++ b/indra/llcommon/fsprofiler.h @@ -0,0 +1,32 @@ +#pragma once +#ifndef FS_PROFILER_H_INCLUDED +#define FS_PROFILER_H_INCLUDED + +// define a simple set of empty macros that allow us to build without the Tracy profiler installed in 3p +// this is similar to the profiler abstraction used by LL but as they have no plans to release that any time soon we'll replace it +// Just a minimal set at the moment will add locks/gpu/memory and other stuff later + +#ifdef TRACY_ENABLE +#include "Tracy.hpp" +namespace FSProfiler +{ + extern bool active; +} + +#define FSZone ZoneNamed( ___tracy_scoped_zone, FSProfiler::active) +#define FSZoneN( name ) ZoneNamedN( ___tracy_scoped_zone, name, FSProfiler::active) +#define FSZoneC(color) ZoneNamedC( ___tracy_scoped_zone, color, FSProfiler::active) +#define FSZoneNC(name, color) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSProfiler::active) +#define FSPlot( name, value ) TracyPlot( name, value) +#define FSFrameMark FrameMark + +#else + +#define FSZone +#define FSZoneN( name ) +#define FSZoneC(color) +#define FSZoneNC(name, color) +#define FSPlot( name, value ) +#define FSFrameMark +#endif // TRACY_ENABLE +#endif \ No newline at end of file diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index ac3e260fb1..4f6afd17fd 100644 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -54,15 +54,7 @@ #else #error "architecture not supported" #endif -// are we actively profiling? -#ifdef TRACY_ENABLE -namespace LLTrace -{ - bool active{false}; -} -// -#endif namespace LLTrace { diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index e0df4f5277..70c0735f35 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -41,21 +41,15 @@ // Add Tracy profiler support // #define LL_RECORD_BLOCK_TIME(timer_stat) \ // const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#include "fsprofiler.h" #ifdef TRACY_ENABLE - -#include "Tracy.hpp" - -namespace LLTrace -{ - extern bool active; -} // #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well. #ifdef TRACY_NO_FASTTIMERS #define LL_RECORD_BLOCK_TIME(timer_stat) \ -ZoneNamedN( ___tracy_scoped_zone, #timer_stat , LLTrace::active); +FSZoneN( #timer_stat ); #else // TRACY_NO_FASTTIMERS #define LL_RECORD_BLOCK_TIME(timer_stat) \ -ZoneNamedN( ___tracy_scoped_zone, #timer_stat , LLTrace::active); \ +FSZoneN( #timer_stat ); \ const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); #endif // TRACY_NO_FASTTIMERS #else // TRACY_ENABLE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 8e5bc6406d..4b2b28af80 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -33,8 +33,7 @@ #include "llfilesystem.h" #include "llfasttimer.h" #include "lldiskcache.h" - -#define FSZoneScoped ZoneNamedC( ___tracy_scoped_zone, tracy::Color::Gold, LLTrace::active); +#include "fsprofiler.h" const S32 LLFileSystem::READ = 0x00000001; const S32 LLFileSystem::WRITE = 0x00000002; @@ -59,7 +58,7 @@ LLFileSystem::~LLFileSystem() // static bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -88,7 +87,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) // { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -106,7 +105,8 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, const LLUUID& new_file_id, const LLAssetType::EType new_file_type) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; std::string old_id_str; old_file_id.toString(old_id_str); const std::string extra_info = ""; @@ -137,7 +137,8 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp // static S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -163,7 +164,8 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi BOOL LLFileSystem::read(U8* buffer, S32 bytes) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; BOOL success = TRUE; std::string id; @@ -222,19 +224,22 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes) S32 LLFileSystem::getLastBytesRead() { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; return mBytesRead; } BOOL LLFileSystem::eof() { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; return mPosition >= getSize(); } BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; std::string id_str; mFileID.toString(id_str); const std::string extra_info = ""; @@ -344,7 +349,8 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) BOOL LLFileSystem::seek(S32 offset, S32 origin) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; if (-1 == origin) { origin = mPosition; @@ -375,26 +381,30 @@ BOOL LLFileSystem::seek(S32 offset, S32 origin) S32 LLFileSystem::tell() const { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; return mPosition; } S32 LLFileSystem::getSize() { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; return LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; // offer up a huge size since we don't care what the max is return INT_MAX; } BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; @@ -405,7 +415,8 @@ BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_typ BOOL LLFileSystem::remove() { - FSZoneScoped; + FSZoneC(tracy::Color::Gold); +; LLFileSystem::removeFile(mFileID, mFileType); return TRUE; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8b42cc7035..f16570c2de 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -282,6 +282,8 @@ #include "fsradar.h" #include "fsassetblacklist.h" +#include "fsprofiler.h" // Tracy profiler support + #if (LL_LINUX || LL_SOLARIS) && LL_GTK #include "glib.h" #endif // (LL_LINUX || LL_SOLARIS) && LL_GTK @@ -1653,17 +1655,19 @@ bool LLAppViewer::doFrame() LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); LLSD newFrame; // Tracy enabling +#ifdef TRACY_ENABLE static bool one_time{false}; static LLCachedControl tracy_enable_when_connected(gSavedSettings, "FSTracyEnableWhenConnected"); if( !one_time && (gFrameCount % 10 == 0) ) { - if(!LLTrace::active && tracy_enable_when_connected && TracyIsConnected) + if(!FSProfiler::active && tracy_enable_when_connected && TracyIsConnected) { - LLTrace::active = true; + FSProfiler::active = true; one_time=true; // prevent reset race if we disable manually. LL_INFOS() << "Tracy profiler or collector connected" << LL_ENDL; } } +#endif // // MaxFPS Viewer-Chui merge error LLTimer periodicRenderingTimer; @@ -1965,7 +1969,7 @@ bool LLAppViewer::doFrame() LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - FrameMark; // Tracy support delineate Frame + FSFrameMark; // Tracy support delineate Frame return ! LLApp::isRunning(); } diff --git a/indra/tools/vstool/DispatchUtility.cs b/indra/tools/vstool/DispatchUtility.cs deleted file mode 100644 index 6056ac55a1..0000000000 --- a/indra/tools/vstool/DispatchUtility.cs +++ /dev/null @@ -1,271 +0,0 @@ -#region Using Directives - -using System; -using System.Collections.Generic; -using System.Text; -using System.Runtime.InteropServices; -using System.Reflection; -using System.Security.Permissions; - -#endregion - -namespace TestDispatchUtility -{ - /// - /// Provides helper methods for working with COM IDispatch objects that have a registered type library. - /// - public static class DispatchUtility - { - #region Private Constants - - private const int S_OK = 0; //From WinError.h - private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800 - - #endregion - - #region Public Methods - - /// - /// Gets whether the specified object implements IDispatch. - /// - /// An object to check. - /// True if the object implements IDispatch. False otherwise. - public static bool ImplementsIDispatch(object obj) - { - bool result = obj is IDispatchInfo; - return result; - } - - /// - /// Gets a Type that can be used with reflection. - /// - /// An object that implements IDispatch. - /// Whether an exception should be thrown if a Type can't be obtained. - /// A .NET Type that can be used with reflection. - /// If doesn't implement IDispatch. - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - public static Type GetType(object obj, bool throwIfNotFound) - { - RequireReference(obj, "obj"); - Type result = GetType((IDispatchInfo)obj, throwIfNotFound); - return result; - } - - /// - /// Tries to get the DISPID for the requested member name. - /// - /// An object that implements IDispatch. - /// The name of a member to lookup. - /// If the method returns true, this holds the DISPID on output. - /// If the method returns false, this value should be ignored. - /// True if the member was found and resolved to a DISPID. False otherwise. - /// If doesn't implement IDispatch. - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - public static bool TryGetDispId(object obj, string name, out int dispId) - { - RequireReference(obj, "obj"); - bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId); - return result; - } - - /// - /// Invokes a member by DISPID. - /// - /// An object that implements IDispatch. - /// The DISPID of a member. This can be obtained using - /// . - /// The arguments to pass to the member. - /// The member's return value. - /// - /// This can invoke a method or a property get accessor. - /// - public static object Invoke(object obj, int dispId, object[] args) - { - string memberName = "[DispId=" + dispId + "]"; - object result = Invoke(obj, memberName, args); - return result; - } - - /// - /// Invokes a member by name. - /// - /// An object. - /// The name of the member to invoke. - /// The arguments to pass to the member. - /// The member's return value. - /// - /// This can invoke a method or a property get accessor. - /// - public static object Invoke(object obj, string memberName, object[] args) - { - RequireReference(obj, "obj"); - Type type = obj.GetType(); - object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty, - null, obj, args, null); - return result; - } - - #endregion - - #region Private Methods - - /// - /// Requires that the value is non-null. - /// - /// The type of the value. - /// The value to check. - /// The name of the value. - private static void RequireReference(T value, string name) where T : class - { - if (value == null) - { - throw new ArgumentNullException(name); - } - } - - /// - /// Gets a Type that can be used with reflection. - /// - /// An object that implements IDispatch. - /// Whether an exception should be thrown if a Type can't be obtained. - /// A .NET Type that can be used with reflection. - private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound) - { - RequireReference(dispatch, "dispatch"); - - Type result = null; - int typeInfoCount; - int hr = dispatch.GetTypeInfoCount(out typeInfoCount); - if (hr == S_OK && typeInfoCount > 0) - { - // Type info isn't usually culture-aware for IDispatch, so we might as well pass - // the default locale instead of looking up the current thread's LCID each time - // (via CultureInfo.CurrentCulture.LCID). - dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result); - } - - if (result == null && throwIfNotFound) - { - // If the GetTypeInfoCount called failed, throw an exception for that. - Marshal.ThrowExceptionForHR(hr); - - // Otherwise, throw the same exception that Type.GetType would throw. - throw new TypeLoadException(); - } - - return result; - } - - /// - /// Tries to get the DISPID for the requested member name. - /// - /// An object that implements IDispatch. - /// The name of a member to lookup. - /// If the method returns true, this holds the DISPID on output. - /// If the method returns false, this value should be ignored. - /// True if the member was found and resolved to a DISPID. False otherwise. - private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId) - { - RequireReference(dispatch, "dispatch"); - RequireReference(name, "name"); - - bool result = false; - - // Members names aren't usually culture-aware for IDispatch, so we might as well - // pass the default locale instead of looking up the current thread's LCID each time - // (via CultureInfo.CurrentCulture.LCID). - Guid iidNull = Guid.Empty; - int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId); - - const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h - const int DISPID_UNKNOWN = -1; //From OAIdl.idl - if (hr == S_OK) - { - result = true; - } - else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN) - { - // This is the only supported "error" case because it means IDispatch - // is saying it doesn't know the member we asked about. - result = false; - } - else - { - // The other documented result codes are all errors. - Marshal.ThrowExceptionForHR(hr); - } - - return result; - } - - #endregion - - #region Private Interfaces - - /// - /// A partial declaration of IDispatch used to lookup Type information and DISPIDs. - /// - /// - /// This interface only declares the first three methods of IDispatch. It omits the - /// fourth method (Invoke) because there are already plenty of ways to do dynamic - /// invocation in .NET. But the first three methods provide dynamic type metadata - /// discovery, which .NET doesn't provide normally if you have a System.__ComObject - /// RCW instead of a strongly-typed RCW. - /// - /// Note: The original declaration of IDispatch is in OAIdl.idl. - /// - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("00020400-0000-0000-C000-000000000046")] - private interface IDispatchInfo - { - /// - /// Gets the number of Types that the object provides (0 or 1). - /// - /// Returns 0 or 1 for the number of Types provided by . - /// - /// http://msdn.microsoft.com/en-us/library/da876d53-cb8a-465c-a43e-c0eb272e2a12(VS.85) - /// - [PreserveSig] - int GetTypeInfoCount(out int typeInfoCount); - - /// - /// Gets the Type information for an object if returned 1. - /// - /// Must be 0. - /// Typically, LOCALE_SYSTEM_DEFAULT (2048). - /// Returns the object's Type information. - /// - /// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85) - /// - void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler, - MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo); - - /// - /// Gets the DISPID of the specified member name. - /// - /// Must be IID_NULL. Pass a copy of Guid.Empty. - /// The name of the member to look up. - /// Must be 1. - /// Typically, LOCALE_SYSTEM_DEFAULT (2048). - /// If a member with the requested - /// is found, this returns its DISPID and the method's return value is 0. - /// If the method returns a non-zero value, then this parameter's output value is - /// undefined. - /// Zero for success. Non-zero for failure. - /// - /// http://msdn.microsoft.com/en-us/library/6f6cf233-3481-436e-8d6a-51f93bf91619(VS.85) - /// - [PreserveSig] - int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId); - - // NOTE: The real IDispatch also has an Invoke method next, but we don't need it. - // We can invoke methods using .NET's Type.InvokeMember method with the special - // [DISPID=n] syntax for member "names", or we can get a .NET Type using GetTypeInfo - // and invoke methods on that through reflection. - // Type.InvokeMember: http://msdn.microsoft.com/en-us/library/de3dhzwy.aspx - } - - #endregion - } -} diff --git a/indra/tools/vstool/README.txt b/indra/tools/vstool/README.txt deleted file mode 100644 index e419180031..0000000000 --- a/indra/tools/vstool/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -VSTool is a command line utility to manipulate VisualStudio settings. - -The windows cmake project configuration uses VSTool.exe - -A handy upgrade: - figure out how to make cmake build this csharp app - - or write the app using script (jscript?!?) so it doesn't need to be built. - - diff --git a/indra/tools/vstool/VSTool.csproj b/indra/tools/vstool/VSTool.csproj deleted file mode 100755 index 7f431e85c7..0000000000 --- a/indra/tools/vstool/VSTool.csproj +++ /dev/null @@ -1,98 +0,0 @@ - - - - Local - 8.0.50727 - 2.0 - {96943E2D-1373-4617-A117-D0F997A94919} - Debug - AnyCPU - - - - - VSTool - - - JScript - Grid - IE50 - false - Exe - VSTool - Always - VSTool.VSToolMain - - - - - v2.0 - 2.0 - - - .\ - false - 285212672 - false - - - DEBUG;TRACE - - - true - 4096 - false - - - false - false - false - false - 4 - full - prompt - - - .\ - false - 285212672 - false - - - TRACE - - - false - 4096 - false - - - true - false - false - false - 4 - none - prompt - - - - System - - - System.Data - - - - - Code - - - - - - - - - - \ No newline at end of file diff --git a/indra/tools/vstool/VSTool.exe b/indra/tools/vstool/VSTool.exe deleted file mode 100755 index 751540413a0af3256f6bbb30f9a49c7232699d9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 W1gZ#B5vU?iMWBj66@fnq1pXH?h%2K2 diff --git a/indra/tools/vstool/VSTool.sln b/indra/tools/vstool/VSTool.sln deleted file mode 100755 index 21e3d75971..0000000000 --- a/indra/tools/vstool/VSTool.sln +++ /dev/null @@ -1,19 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSTool", "VSTool.csproj", "{96943E2D-1373-4617-A117-D0F997A94919}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {96943E2D-1373-4617-A117-D0F997A94919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96943E2D-1373-4617-A117-D0F997A94919}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96943E2D-1373-4617-A117-D0F997A94919}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96943E2D-1373-4617-A117-D0F997A94919}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/indra/tools/vstool/app.config b/indra/tools/vstool/app.config deleted file mode 100644 index 8494f728ff..0000000000 --- a/indra/tools/vstool/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/indra/tools/vstool/main.cs b/indra/tools/vstool/main.cs deleted file mode 100755 index 1d6b2f14d1..0000000000 --- a/indra/tools/vstool/main.cs +++ /dev/null @@ -1,733 +0,0 @@ -// Code about getting running instances visual studio -// was borrowed from -// http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using Microsoft.CSharp; - -namespace VSTool -{ - // The MessageFilter class comes from: - // http://msdn.microsoft.com/en-us/library/ms228772(VS.80).aspx - // It allows vstool to get timing error messages from - // visualstudio and handle them. - public class MessageFilter : IOleMessageFilter - { - // - // Class containing the IOleMessageFilter - // thread error-handling functions. - - // Start the filter. - public static void Register() - { - IOleMessageFilter newFilter = new MessageFilter(); - IOleMessageFilter oldFilter = null; - CoRegisterMessageFilter(newFilter, out oldFilter); - } - - // Done with the filter, close it. - public static void Revoke() - { - IOleMessageFilter oldFilter = null; - CoRegisterMessageFilter(null, out oldFilter); - } - - // - // IOleMessageFilter functions. - // Handle incoming thread requests. - int IOleMessageFilter.HandleInComingCall(int dwCallType, - System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr - lpInterfaceInfo) - { - //Return the flag SERVERCALL_ISHANDLED. - return 0; - } - - // Thread call was rejected, so try again. - int IOleMessageFilter.RetryRejectedCall(System.IntPtr - hTaskCallee, int dwTickCount, int dwRejectType) - { - if (dwRejectType == 2) - // flag = SERVERCALL_RETRYLATER. - { - // Retry the thread call immediately if return >=0 & - // <100. - return 99; - } - // Too busy; cancel call. - return -1; - } - - int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, - int dwTickCount, int dwPendingType) - { - //Return the flag PENDINGMSG_WAITDEFPROCESS. - return 2; - } - - // Implement the IOleMessageFilter interface. - [DllImport("Ole32.dll")] - private static extern int - CoRegisterMessageFilter(IOleMessageFilter newFilter, out - IOleMessageFilter oldFilter); - } - - [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), - InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] - interface IOleMessageFilter - { - [PreserveSig] - int HandleInComingCall( - int dwCallType, - IntPtr hTaskCaller, - int dwTickCount, - IntPtr lpInterfaceInfo); - - [PreserveSig] - int RetryRejectedCall( - IntPtr hTaskCallee, - int dwTickCount, - int dwRejectType); - - [PreserveSig] - int MessagePending( - IntPtr hTaskCallee, - int dwTickCount, - int dwPendingType); - } - - class ViaCOM - { - public static object GetProperty(object from_obj, string prop_name) - { - try - { - Type objType = from_obj.GetType(); - return objType.InvokeMember( - prop_name, - BindingFlags.GetProperty, null, - from_obj, - null); - } - catch (Exception e) - { - Console.WriteLine("Error getting property: \"{0}\"", prop_name); - Console.WriteLine(e.Message); - throw e; - } - } - - public static object SetProperty(object from_obj, string prop_name, object new_value) - { - try - { - object[] args = { new_value }; - Type objType = from_obj.GetType(); - return objType.InvokeMember( - prop_name, - BindingFlags.DeclaredOnly | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.Instance | - BindingFlags.SetProperty, - null, - from_obj, - args); - } - catch (Exception e) - { - Console.WriteLine("Error setting property: \"{0}\"", prop_name); - Console.WriteLine(e.Message); - throw e; - } - } - - public static object CallMethod(object from_obj, string method_name, params object[] args) - { - try - { - Type objType = from_obj.GetType(); - return objType.InvokeMember( - method_name, - BindingFlags.DeclaredOnly | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.Instance | - BindingFlags.InvokeMethod, - null, - from_obj, - args); - } - catch (Exception e) - { - Console.WriteLine("Error calling method \"{0}\"", method_name); - Console.WriteLine(e.Message); - throw e; - } - } - }; - - /// - /// The main entry point class for VSTool. - /// - class VSToolMain - { - #region Interop imports - [DllImport("ole32.dll")] - public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); - - [DllImport("ole32.dll")] - public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc); - #endregion - - static System.Boolean ignore_case = true; - - static string solution_name = null; - static bool use_new_vs = false; - static Hashtable projectDict = new Hashtable(); - static string startup_project = null; - static string config = null; - - static object dte = null; - static object solution = null; - - /// - /// The main entry point for the application. - /// - [STAThread] - static int Main(string[] args) - { - int retVal = 0; - bool need_save = false; - - try - { - parse_command_line(args); - - Console.WriteLine("Editing solution: {0}", solution_name); - - bool found_open_solution = GetDTEAndSolution(); - - if (dte == null || solution == null) - { - retVal = 1; - } - else - { - MessageFilter.Register(); - - // Walk through all of the projects in the solution - // and list the type of each project. - foreach (DictionaryEntry p in projectDict) - { - string project_name = (string)p.Key; - string working_dir = (string)p.Value; - if (SetProjectWorkingDir(solution, project_name, working_dir)) - { - need_save = true; - } - } - - if (config != null) - { - need_save = SetActiveConfig(config); - } - - if (startup_project != null) - { - need_save = SetStartupProject(startup_project); - } - - if (need_save) - { - if (found_open_solution == false) - { - ViaCOM.CallMethod(solution, "Close", null); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.Message); - retVal = 1; - } - finally - { - if (solution != null) - { - Marshal.ReleaseComObject(solution); - solution = null; - } - - if (dte != null) - { - Marshal.ReleaseComObject(dte); - dte = null; - } - - MessageFilter.Revoke(); - } - return retVal; - } - - public static bool parse_command_line(string[] args) - { - string options_desc = - "--solution : MSVC solution name. (required)\n" + - "--use_new_vs : Ignore running versions of visual studio.\n" + - "--workingdir : Set working dir of a VC project.\n" + - "--config : Set the active config for the solution.\n" + - "--startup : Set the startup project for the solution.\n"; - - try - { - // Command line param parsing loop. - int i = 0; - for (; i < args.Length; ++i) - { - if ("--solution" == args[i]) - { - if (solution_name != null) - { - throw new ApplicationException("Found second --solution option"); - } - solution_name = args[++i]; - } - else if ("--use_new_vs" == args[i]) - { - use_new_vs = true; - } - - else if ("--workingdir" == args[i]) - { - string project_name = args[++i]; - string working_dir = args[++i]; - projectDict.Add(project_name, working_dir); - } - else if ("--config" == args[i]) - { - if (config != null) - { - throw new ApplicationException("Found second --config option"); - } - config = args[++i]; - } - else if ("--startup" == args[i]) - { - if (startup_project != null) - { - throw new ApplicationException("Found second --startup option"); - } - startup_project = args[++i]; - } - else - { - throw new ApplicationException("Found unrecognized token on command line: " + args[i]); - } - } - - if (solution_name == null) - { - throw new ApplicationException("The --solution option is required."); - } - } - catch(ApplicationException e) - { - - Console.WriteLine("Oops! " + e.Message); - Console.Write("Command line:"); - foreach (string arg in args) - { - Console.Write(" " + arg); - } - Console.Write("\n\n"); - Console.WriteLine("VSTool command line usage"); - Console.Write(options_desc); - throw e; - } - return true; - } - - public static bool GetDTEAndSolution() - { - bool found_open_solution = true; - - Console.WriteLine("Looking for existing VisualStudio instance..."); - - // Get an instance of the currently running Visual Studio .NET IDE. - // dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.7.1"); - string full_solution_name = System.IO.Path.GetFullPath(solution_name); - if (false == use_new_vs) - { - dte = GetIDEInstance(full_solution_name); - } - - if (dte == null) - { - try - { - Console.WriteLine(" Didn't find open solution, starting new background VisualStudio instance..."); - Console.WriteLine(" Reading .sln file version..."); - string version = GetSolutionVersion(full_solution_name); - - Console.WriteLine(" Using version: {0}...", version); - string progid = GetVSProgID(version); - - Type objType = Type.GetTypeFromProgID(progid); - dte = System.Activator.CreateInstance(objType); - Console.WriteLine(" Reading solution: \"{0}\"", full_solution_name); - - solution = ViaCOM.GetProperty(dte, "Solution"); - object[] openArgs = { full_solution_name }; - ViaCOM.CallMethod(solution, "Open", openArgs); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - Console.WriteLine("Quitting do to error opening: {0}", full_solution_name); - solution = null; - dte = null; - return found_open_solution; - } - found_open_solution = false; - } - - if (solution == null) - { - solution = ViaCOM.GetProperty(dte, "Solution"); - } - - return found_open_solution; - } - - /// - /// Get the DTE object for the instance of Visual Studio IDE that has - /// the specified solution open. - /// - /// The absolute filename of the solution - /// Corresponding DTE object or null if no such IDE is running - public static object GetIDEInstance( string solutionFile ) - { - Hashtable runningInstances = GetIDEInstances( true ); - IDictionaryEnumerator enumerator = runningInstances.GetEnumerator(); - - while ( enumerator.MoveNext() ) - { - try - { - object ide = enumerator.Value; - if (ide != null) - { - object sol = ViaCOM.GetProperty(ide, "Solution"); - if (0 == string.Compare((string)ViaCOM.GetProperty(sol, "FullName"), solutionFile, ignore_case)) - { - return ide; - } - } - } - catch{} - } - - return null; - } - - /// - /// Get a table of the currently running instances of the Visual Studio .NET IDE. - /// - /// Only return instances that have opened a solution - /// A hashtable mapping the name of the IDE in the running object table to the corresponding DTE object - public static Hashtable GetIDEInstances( bool openSolutionsOnly ) - { - Hashtable runningIDEInstances = new Hashtable(); - Hashtable runningObjects = GetRunningObjectTable(); - - IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator(); - while ( rotEnumerator.MoveNext() ) - { - string candidateName = (string) rotEnumerator.Key; - if (!candidateName.StartsWith("!VisualStudio.DTE")) - continue; - - object ide = rotEnumerator.Value; - if (ide == null) - continue; - - if (openSolutionsOnly) - { - try - { - object sol = ViaCOM.GetProperty(ide, "Solution"); - string solutionFile = (string)ViaCOM.GetProperty(sol, "FullName"); - if (solutionFile != String.Empty) - { - runningIDEInstances[ candidateName ] = ide; - } - } - catch {} - } - else - { - runningIDEInstances[ candidateName ] = ide; - } - } - return runningIDEInstances; - } - - /// - /// Get a snapshot of the running object table (ROT). - /// - /// A hashtable mapping the name of the object in the ROT to the corresponding object - [STAThread] - public static Hashtable GetRunningObjectTable() - { - Hashtable result = new Hashtable(); - - int numFetched = 0; - IRunningObjectTable runningObjectTable; - IEnumMoniker monikerEnumerator; - IMoniker[] monikers = new IMoniker[1]; - - GetRunningObjectTable(0, out runningObjectTable); - runningObjectTable.EnumRunning(out monikerEnumerator); - monikerEnumerator.Reset(); - - while (monikerEnumerator.Next(1, monikers, new IntPtr(numFetched)) == 0) - { - IBindCtx ctx; - CreateBindCtx(0, out ctx); - - string runningObjectName; - monikers[0].GetDisplayName(ctx, null, out runningObjectName); - - object runningObjectVal; - runningObjectTable.GetObject( monikers[0], out runningObjectVal); - - result[ runningObjectName ] = runningObjectVal; - } - - return result; - } - - public static string GetSolutionVersion(string solutionFullFileName) - { - string version; - System.IO.StreamReader solutionStreamReader = null; - string firstLine; - string format; - - try - { - solutionStreamReader = new System.IO.StreamReader(solutionFullFileName); - do - { - firstLine = solutionStreamReader.ReadLine(); - } - while (firstLine == ""); - - format = firstLine.Substring(firstLine.LastIndexOf(" ")).Trim(); - - switch(format) - { - case "7.00": - version = "VC70"; - break; - - case "8.00": - version = "VC71"; - break; - - case "9.00": - version = "VC80"; - break; - - case "10.00": - version = "VC90"; - break; - - case "11.00": - version = "VC100"; - break; - - case "12.00": - version = "VC150"; - break; - - default: - throw new ApplicationException("Unknown .sln version: " + format); - } - } - finally - { - if(solutionStreamReader != null) - { - solutionStreamReader.Close(); - } - } - - return version; - } - - public static string GetVSProgID(string version) - { - string progid = null; - switch(version) - { - case "VC70": - progid = "VisualStudio.DTE.7"; - break; - - case "VC71": - progid = "VisualStudio.DTE.7.1"; - break; - - case "VC80": - progid = "VisualStudio.DTE.8.0"; - break; - - case "VC90": - progid = "VisualStudio.DTE.9.0"; - break; - - case "VC100": - progid = "VisualStudio.DTE.10.0"; - break; - - case "VC120": - progid = "VisualStudio.DTE.12.0"; - break; - - case "VC150": - progid = "VisualStudio.DTE.15.0"; - break; - - default: - throw new ApplicationException("Can't handle VS version: " + version); - } - - return progid; - } - - public static bool SetProjectWorkingDir(object sol, string project_name, string working_dir) - { - bool made_change = false; - Console.WriteLine("Looking for project {0}...", project_name); - try - { - object prjs = ViaCOM.GetProperty(sol, "Projects"); - object count = ViaCOM.GetProperty(prjs, "Count"); - for(int i = 1; i <= (int)count; ++i) - { - object[] prjItemArgs = { (object)i }; - object prj = ViaCOM.CallMethod(prjs, "Item", prjItemArgs); - string name = (string)ViaCOM.GetProperty(prj, "Name"); - if (0 == string.Compare(name, project_name, ignore_case)) - { - Console.WriteLine("Found project: {0}", project_name); - Console.WriteLine("Setting working directory"); - - string full_project_name = (string)ViaCOM.GetProperty(prj, "FullName"); - Console.WriteLine(full_project_name); - - // *NOTE:Mani Thanks to incompatibilities between different versions of the - // VCProjectEngine.dll assembly, we can't cast the objects recevied from the DTE to - // the VCProjectEngine types from a different version than the one built - // with. ie, VisualStudio.DTE.7.1 objects can't be converted in a project built - // in VS 8.0. To avoid this problem, we can use the com object interfaces directly, - // without the type casting. Its tedious code, but it seems to work. - - // oCfgs should be assigned to a 'Project.Configurations' collection. - object oCfgs = ViaCOM.GetProperty(ViaCOM.GetProperty(prj, "Object"), "Configurations"); - - // oCount will be assigned to the number of configs present in oCfgs. - object oCount = ViaCOM.GetProperty(oCfgs, "Count"); - - for (int cfgIndex = 1; cfgIndex <= (int)oCount; ++cfgIndex) - { - object[] itemArgs = {(object)cfgIndex}; - object oCfg = ViaCOM.CallMethod(oCfgs, "Item", itemArgs); - object oDebugSettings = ViaCOM.GetProperty(oCfg, "DebugSettings"); - ViaCOM.SetProperty(oDebugSettings, "WorkingDirectory", (object)working_dir); - } - - break; - } - } - made_change = true; - } - catch( Exception e ) - { - Console.WriteLine(e.Message); - Console.WriteLine("Failed to set working dir for project, {0}.", project_name); - } - - return made_change; - } - - public static bool SetStartupProject(string startup_project) - { - bool result = false; - try - { - // You need the 'unique name of the project to set StartupProjects. - // find the project by generic name. - Console.WriteLine("Trying to set \"{0}\" to the startup project", startup_project); - object prjs = ViaCOM.GetProperty(solution, "Projects"); - object count = ViaCOM.GetProperty(prjs, "Count"); - for (int i = 1; i <= (int)count; ++i) - { - object[] itemArgs = { (object)i }; - object prj = ViaCOM.CallMethod(prjs, "Item", itemArgs); - object prjName = ViaCOM.GetProperty(prj, "Name"); - if (0 == string.Compare((string)prjName, startup_project, ignore_case)) - { - object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild"); - ViaCOM.SetProperty(solBuild, "StartupProjects", ViaCOM.GetProperty(prj, "UniqueName")); - Console.WriteLine(" Success!"); - result = true; - break; - } - } - - if (result == false) - { - Console.WriteLine(" Could not find project \"{0}\" in the solution.", startup_project); - } - } - catch (Exception e) - { - Console.WriteLine(" Failed to set the startup project!"); - Console.WriteLine(e.Message); - } - return result; - } - - public static bool SetActiveConfig(string config) - { - bool result = false; - try - { - Console.WriteLine("Trying to set active config to \"{0}\"", config); - object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild"); - object solCfgs = ViaCOM.GetProperty(solBuild, "SolutionConfigurations"); - object[] itemArgs = { (object)config }; - object solCfg = ViaCOM.CallMethod(solCfgs, "Item", itemArgs); - ViaCOM.CallMethod(solCfg, "Activate", null); - Console.WriteLine(" Success!"); - result = true; - } - catch (Exception e) - { - Console.WriteLine(" Failed to set \"{0}\" as the active config.", config); - Console.WriteLine(e.Message); - } - return result; - } - } -} From 1f9246f0f3933e6711718b8a984fb9426d5c2eac Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 21 Mar 2021 14:47:31 +0000 Subject: [PATCH 088/121] Ignore vscode generated stuff --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f94d376ae5..73af4d1201 100755 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,5 @@ my_autobuild.xml compile_commands.json # ignore tracy for now -indra/tracy \ No newline at end of file +indra/tracy +indra/tools/vstool/ From eb0bfc9e10daacb0cea2a2bacb7cd089efadbf81 Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 21 Mar 2021 14:59:02 +0000 Subject: [PATCH 089/121] more cleanup of telemetry support remove superfluous cmake changes rename profiler to telemetry to avoid confusion --- indra/cmake/00-Common.cmake | 5 --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/FSTracyClient.cpp | 10 ------ indra/llcommon/fstelemetry.cpp | 31 +++++++++++++++++++ .../llcommon/{fsprofiler.h => fstelemetry.h} | 30 ++++++++++-------- indra/llcommon/llfasttimer.h | 2 +- indra/llfilesystem/llfilesystem.cpp | 1 - indra/newview/app_settings/settings.xml | 15 +++++++-- indra/newview/llappviewer.cpp | 2 +- indra/newview/llviewermenu.cpp | 17 ++++++++++ .../skins/default/xui/en/menu_viewer.xml | 26 ++++++++++++++++ 11 files changed, 107 insertions(+), 33 deletions(-) create mode 100644 indra/llcommon/fstelemetry.cpp rename indra/llcommon/{fsprofiler.h => fstelemetry.h} (55%) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 97f8e8690e..5dd44a953b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -139,11 +139,6 @@ if (WINDOWS) # /arch:SSE2 /fp:fast ) -# Add Tracy profiler support - if (USE_TRACY_PROFILER) - add_definitions( /DTRACY_ENABLE /DTRACY_NO_FASTTIMERS ) - endif() -# # Nicky: x64 implies SSE2 if( ADDRESS_SIZE EQUAL 32 ) add_definitions( /arch:SSE2 ) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 879a7c4a42..ed72fee9d2 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -257,6 +257,7 @@ set(llcommon_HEADER_FILES # Add all nd* files. memory pool, intrinsics, ... # Tracy Profiler support +list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp) if (USE_TRACY_PROFILER) list(APPEND llcommon_SOURCE_FILES FSTracyClient.cpp) endif() diff --git a/indra/llcommon/FSTracyClient.cpp b/indra/llcommon/FSTracyClient.cpp index d40c042113..867a5442ac 100644 --- a/indra/llcommon/FSTracyClient.cpp +++ b/indra/llcommon/FSTracyClient.cpp @@ -10,19 +10,9 @@ // // Define TRACY_ENABLE to enable profiler. -// #define __CYGWIN__ #include "common/TracySystem.cpp" #ifdef TRACY_ENABLE -// are we actively profiling? -// At some point this should move to fsprofiler.cpp to correspond with the headerfile -#ifdef TRACY_ENABLE -namespace FSProfiler -{ - bool active{false}; -} -#endif -// #ifdef _MSC_VER # pragma warning(push, 0) diff --git a/indra/llcommon/fstelemetry.cpp b/indra/llcommon/fstelemetry.cpp new file mode 100644 index 0000000000..c2bc80b66a --- /dev/null +++ b/indra/llcommon/fstelemetry.cpp @@ -0,0 +1,31 @@ +/** + * @file fstelemetry.cpp + * @brief fstelemetry Telemetry abstraction for FS + * + * $LicenseInfo:firstyear=2021&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2021, The Phoenix Firestorm Project, 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 + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ +#include "fstelemetry.h" +namespace FSTelemetry +{ + bool active{false}; +} \ No newline at end of file diff --git a/indra/llcommon/fsprofiler.h b/indra/llcommon/fstelemetry.h similarity index 55% rename from indra/llcommon/fsprofiler.h rename to indra/llcommon/fstelemetry.h index 41ffa6a17f..cd7688907b 100644 --- a/indra/llcommon/fsprofiler.h +++ b/indra/llcommon/fstelemetry.h @@ -1,32 +1,36 @@ #pragma once -#ifndef FS_PROFILER_H_INCLUDED -#define FS_PROFILER_H_INCLUDED +#ifndef FS_TELEMETRY_H_INCLUDED +#define FS_TELEMETRY_H_INCLUDED // define a simple set of empty macros that allow us to build without the Tracy profiler installed in 3p // this is similar to the profiler abstraction used by LL but as they have no plans to release that any time soon we'll replace it -// Just a minimal set at the moment will add locks/gpu/memory and other stuff later +// Just a minimal set at the moment will add locks/gpu/memory and other stuff later. #ifdef TRACY_ENABLE #include "Tracy.hpp" -namespace FSProfiler -{ - extern bool active; -} -#define FSZone ZoneNamed( ___tracy_scoped_zone, FSProfiler::active) -#define FSZoneN( name ) ZoneNamedN( ___tracy_scoped_zone, name, FSProfiler::active) -#define FSZoneC(color) ZoneNamedC( ___tracy_scoped_zone, color, FSProfiler::active) -#define FSZoneNC(name, color) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSProfiler::active) +#define FSZone ZoneNamed( ___tracy_scoped_zone, FSTelemetry::active) +#define FSZoneN( name ) ZoneNamedN( ___tracy_scoped_zone, name, FSTelemetry::active) +#define FSZoneC( color ) ZoneNamedC( ___tracy_scoped_zone, color, FSTelemetry::active) +#define FSZoneNC( name, color ) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSTelemetry::active) #define FSPlot( name, value ) TracyPlot( name, value) #define FSFrameMark FrameMark +#define FSTelemetryIsConnected TracyIsConnected #else #define FSZone #define FSZoneN( name ) -#define FSZoneC(color) -#define FSZoneNC(name, color) +#define FSZoneC( color ) +#define FSZoneNC( name, color ) #define FSPlot( name, value ) #define FSFrameMark +#define FSTelemetryIsConnected #endif // TRACY_ENABLE + +namespace FSTelemetry +{ + extern bool active; +} + #endif \ No newline at end of file diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 70c0735f35..5bdce0817d 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -41,7 +41,7 @@ // Add Tracy profiler support // #define LL_RECORD_BLOCK_TIME(timer_stat) \ // const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); -#include "fsprofiler.h" +#include "fstelemetry.h" #ifdef TRACY_ENABLE // #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well. #ifdef TRACY_NO_FASTTIMERS diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 4b2b28af80..9d1a4605f3 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -33,7 +33,6 @@ #include "llfilesystem.h" #include "llfasttimer.h" #include "lldiskcache.h" -#include "fsprofiler.h" const S32 LLFileSystem::READ = 0x00000001; const S32 LLFileSystem::WRITE = 0x00000002; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9983a79b54..5f587acf7f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -24628,10 +24628,10 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 - FSTracyEnableWhenConnected + FSTelemetryEnableWhenConnected Comment - Enable profiling as soon as a server connects + Enable telemetry as soon as a server connects Persist 1 Type @@ -24639,6 +24639,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSTelemetryActive + + Comment + Enable profiling as soon as a server connects. Off by default and non-persistent. Use with FSTelemetryEnableWhenConnected to initiate on startup + Persist + 0 + Type + Boolean + Value + 0 + FSFilterGrowlKeywordDuplicateIMs Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f16570c2de..0ba8e0ee92 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -282,7 +282,7 @@ #include "fsradar.h" #include "fsassetblacklist.h" -#include "fsprofiler.h" // Tracy profiler support +#include "fstelemetry.h" // Tracy profiler support #if (LL_LINUX || LL_SOLARIS) && LL_GTK #include "glib.h" diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fd777ad8ff..855e806d35 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -9475,6 +9475,20 @@ class LLAdvancedToggleDoubleClickTeleport: public view_listener_t } }; +// Add telemetry controls to the viewer menus +class FSTelemetryToggleActive : public view_listener_t +{ +protected: + + bool handleEvent(const LLSD& userdata) + { + BOOL checked = gSavedSettings.getBOOL( "FSTelemetryActive" ); + gSavedSettings.setBOOL( "FSTelemetryActive", !checked ); + FSTelemetry::active = !checked; + return true; + } +}; +// void menu_toggle_attached_lights(void* user_data) { LLPipeline::sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); @@ -11831,6 +11845,9 @@ void initialize_menus() //Develop (clear cache immediately) commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) ); + // Add telemetry controls to the viewer Develop menu (Toggle profiling) + view_listener_t::addMenu(new FSTelemetryToggleActive(), "Develop.ToggleTelemetry"); + // Admin >Object view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy"); view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index fc5d99a73e..88135aeedb 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3568,6 +3568,32 @@ function="Advanced.ToggleShowObjectUpdates" /> + + + + + + + + + + From 2ce328282a5264f5d220f5368513a249a159a6fa Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 21 Mar 2021 15:13:02 +0000 Subject: [PATCH 090/121] profiling cleanup ready to merge back put proper markers in place where forgotten before --- indra/llcommon/fstelemetry.h | 37 ++++++++++++++++++++++++-- indra/llfilesystem/llfilesystem.cpp | 40 ++++++++++------------------- indra/newview/llappviewer.cpp | 19 +++++++++----- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/indra/llcommon/fstelemetry.h b/indra/llcommon/fstelemetry.h index cd7688907b..87ec75b00a 100644 --- a/indra/llcommon/fstelemetry.h +++ b/indra/llcommon/fstelemetry.h @@ -1,12 +1,42 @@ #pragma once #ifndef FS_TELEMETRY_H_INCLUDED #define FS_TELEMETRY_H_INCLUDED +/** + * @file fstelemetry.h + * @brief fstelemetry Telemetry abstraction for FS + * + * $LicenseInfo:firstyear=2021&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2021, The Phoenix Firestorm Project, 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 + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ // define a simple set of empty macros that allow us to build without the Tracy profiler installed in 3p // this is similar to the profiler abstraction used by LL but as they have no plans to release that any time soon we'll replace it // Just a minimal set at the moment will add locks/gpu/memory and other stuff later. -#ifdef TRACY_ENABLE +// generic switch (in case we ever add others or incorporate commercial tools like RAD Games if LL were to share a license) +// turn off in the else statement below. +#define FS_HAS_TELEMETRY_SUPPORT + +#ifdef TRACY_ENABLE // (Tracy open source telemetry) #include "Tracy.hpp" #define FSZone ZoneNamed( ___tracy_scoped_zone, FSTelemetry::active) @@ -17,7 +47,10 @@ #define FSFrameMark FrameMark #define FSTelemetryIsConnected TracyIsConnected -#else +#else // (no telemetry) + +// No we don't want no stinkin' telemetry. move along +#undef FS_HAS_TELEMETRY_SUPPORT #define FSZone #define FSZoneN( name ) diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 9d1a4605f3..6d3b7511a5 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -57,7 +57,7 @@ LLFileSystem::~LLFileSystem() // static bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { - FSZoneC(tracy::Color::Gold); + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -86,7 +86,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) // { - FSZoneC(tracy::Color::Gold); + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -104,8 +104,7 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, const LLUUID& new_file_id, const LLAssetType::EType new_file_type) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance std::string old_id_str; old_file_id.toString(old_id_str); const std::string extra_info = ""; @@ -136,8 +135,7 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp // static S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -163,8 +161,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi BOOL LLFileSystem::read(U8* buffer, S32 bytes) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance BOOL success = TRUE; std::string id; @@ -223,22 +220,19 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes) S32 LLFileSystem::getLastBytesRead() { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance return mBytesRead; } BOOL LLFileSystem::eof() { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance return mPosition >= getSize(); } BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; mFileID.toString(id_str); const std::string extra_info = ""; @@ -348,8 +342,7 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) BOOL LLFileSystem::seek(S32 offset, S32 origin) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance if (-1 == origin) { origin = mPosition; @@ -380,30 +373,26 @@ BOOL LLFileSystem::seek(S32 offset, S32 origin) S32 LLFileSystem::tell() const { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance return mPosition; } S32 LLFileSystem::getSize() { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance return LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance // offer up a huge size since we don't care what the max is return INT_MAX; } BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; @@ -414,8 +403,7 @@ BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_typ BOOL LLFileSystem::remove() { - FSZoneC(tracy::Color::Gold); -; + FSZoneC(tracy::Color::Gold); // measure cache performance LLFileSystem::removeFile(mFileID, mFileType); return TRUE; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0ba8e0ee92..92ce80dfdc 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1654,17 +1654,24 @@ bool LLAppViewer::doFrame() { LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); LLSD newFrame; -// Tracy enabling -#ifdef TRACY_ENABLE +// telemetry enabling. +// This ifdef is optional but better to avoid even low overhead code in main loop where not needed. +#ifdef FS_HAS_TELEMETRY_SUPPORT static bool one_time{false}; - static LLCachedControl tracy_enable_when_connected(gSavedSettings, "FSTracyEnableWhenConnected"); + static LLCachedControl profiling_enabled_when_connected(gSavedSettings, "FSTelemetryEnableWhenConnected"); if( !one_time && (gFrameCount % 10 == 0) ) { - if(!FSProfiler::active && tracy_enable_when_connected && TracyIsConnected) + if(!FSTelemetry::active && profiling_enabled_when_connected && FSTelemetryIsConnected) { - FSProfiler::active = true; + FSTelemetry::active = true; + gSavedSettings.setBOOL("FSTelemetryActive", TRUE); // keep the setting in sync. one_time=true; // prevent reset race if we disable manually. - LL_INFOS() << "Tracy profiler or collector connected" << LL_ENDL; + LL_INFOS() << "Profiler or collector connected" << LL_ENDL; + } + else if(!profiling_enabled_when_connected) + { + // no point in checking if we are not waiting. + one_time = true; } } #endif From ac9b87ad1e4cb7e6041b201fc69348591b453325 Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 21 Mar 2021 19:57:11 +0000 Subject: [PATCH 091/121] Fix comment handling silliness (hopefully) --- indra/llcommon/llfasttimer.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 5bdce0817d..2374483c73 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -39,8 +39,10 @@ #define LL_FASTTIMER_USE_RDTSC 1 // Add Tracy profiler support -// #define LL_RECORD_BLOCK_TIME(timer_stat) \ -// const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +/* +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +*/ #include "fstelemetry.h" #ifdef TRACY_ENABLE // #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well. From 938d9d5f8e9bb6a52341233029645c7439d0527f Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 21 Mar 2021 21:45:18 +0000 Subject: [PATCH 092/121] use LL_WINDOWS instead --- indra/llcommon/FSTracyClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/FSTracyClient.cpp b/indra/llcommon/FSTracyClient.cpp index 867a5442ac..79ccc84b4c 100644 --- a/indra/llcommon/FSTracyClient.cpp +++ b/indra/llcommon/FSTracyClient.cpp @@ -14,7 +14,7 @@ #ifdef TRACY_ENABLE -#ifdef _MSC_VER +#ifdef LL_WINDOWS # pragma warning(push, 0) #endif @@ -48,7 +48,7 @@ # endif #endif -#ifdef _MSC_VER +#ifdef LL_WINDOWS # pragma comment(lib, "ws2_32.lib") # pragma comment(lib, "dbghelp.lib") # pragma warning(pop) From af4e66d5215513ea0fb89fa01b4a8ea961cbcf67 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 01:06:53 +0100 Subject: [PATCH 093/121] Add Tracy 3p to autobuild.xml --- autobuild.xml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/autobuild.xml b/autobuild.xml index bcb424112c..83fe052c56 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -261,6 +261,36 @@ version 1.2.15 + Tracy + + copyright + Copyright (c) 2017-2021, Bartosz Taudul + description + A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications. + license + BSD + license_file + LICENSES/Tracy.txt + name + Tracy + platforms + + windows + + archive + + hash + 8e4cc0d82acbf83f10fb866302fd7519 + url + http://3p.firestormviewer.org/Tracy-v0.7.6-windows-210761143.tar.bz2 + + name + linux + + + version + 0.7.6 + apr_suite copyright From d05ffe1693e3ad7968651739ee9dd6285111171a Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 01:13:47 +0100 Subject: [PATCH 094/121] Restore VSTool --- .gitignore | 1 - indra/tools/vstool/DispatchUtility.cs | 271 ++++++++++ indra/tools/vstool/README.txt | 9 + indra/tools/vstool/VSTool.csproj | 98 ++++ indra/tools/vstool/VSTool.exe | Bin 0 -> 24576 bytes indra/tools/vstool/VSTool.sln | 19 + indra/tools/vstool/app.config | 3 + indra/tools/vstool/main.cs | 733 ++++++++++++++++++++++++++ 8 files changed, 1133 insertions(+), 1 deletion(-) create mode 100644 indra/tools/vstool/DispatchUtility.cs create mode 100644 indra/tools/vstool/README.txt create mode 100644 indra/tools/vstool/VSTool.csproj create mode 100644 indra/tools/vstool/VSTool.exe create mode 100644 indra/tools/vstool/VSTool.sln create mode 100644 indra/tools/vstool/app.config create mode 100644 indra/tools/vstool/main.cs diff --git a/.gitignore b/.gitignore index 73af4d1201..5fa4a04a62 100755 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,3 @@ my_autobuild.xml compile_commands.json # ignore tracy for now indra/tracy -indra/tools/vstool/ diff --git a/indra/tools/vstool/DispatchUtility.cs b/indra/tools/vstool/DispatchUtility.cs new file mode 100644 index 0000000000..6056ac55a1 --- /dev/null +++ b/indra/tools/vstool/DispatchUtility.cs @@ -0,0 +1,271 @@ +#region Using Directives + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Security.Permissions; + +#endregion + +namespace TestDispatchUtility +{ + /// + /// Provides helper methods for working with COM IDispatch objects that have a registered type library. + /// + public static class DispatchUtility + { + #region Private Constants + + private const int S_OK = 0; //From WinError.h + private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800 + + #endregion + + #region Public Methods + + /// + /// Gets whether the specified object implements IDispatch. + /// + /// An object to check. + /// True if the object implements IDispatch. False otherwise. + public static bool ImplementsIDispatch(object obj) + { + bool result = obj is IDispatchInfo; + return result; + } + + /// + /// Gets a Type that can be used with reflection. + /// + /// An object that implements IDispatch. + /// Whether an exception should be thrown if a Type can't be obtained. + /// A .NET Type that can be used with reflection. + /// If doesn't implement IDispatch. + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public static Type GetType(object obj, bool throwIfNotFound) + { + RequireReference(obj, "obj"); + Type result = GetType((IDispatchInfo)obj, throwIfNotFound); + return result; + } + + /// + /// Tries to get the DISPID for the requested member name. + /// + /// An object that implements IDispatch. + /// The name of a member to lookup. + /// If the method returns true, this holds the DISPID on output. + /// If the method returns false, this value should be ignored. + /// True if the member was found and resolved to a DISPID. False otherwise. + /// If doesn't implement IDispatch. + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public static bool TryGetDispId(object obj, string name, out int dispId) + { + RequireReference(obj, "obj"); + bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId); + return result; + } + + /// + /// Invokes a member by DISPID. + /// + /// An object that implements IDispatch. + /// The DISPID of a member. This can be obtained using + /// . + /// The arguments to pass to the member. + /// The member's return value. + /// + /// This can invoke a method or a property get accessor. + /// + public static object Invoke(object obj, int dispId, object[] args) + { + string memberName = "[DispId=" + dispId + "]"; + object result = Invoke(obj, memberName, args); + return result; + } + + /// + /// Invokes a member by name. + /// + /// An object. + /// The name of the member to invoke. + /// The arguments to pass to the member. + /// The member's return value. + /// + /// This can invoke a method or a property get accessor. + /// + public static object Invoke(object obj, string memberName, object[] args) + { + RequireReference(obj, "obj"); + Type type = obj.GetType(); + object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty, + null, obj, args, null); + return result; + } + + #endregion + + #region Private Methods + + /// + /// Requires that the value is non-null. + /// + /// The type of the value. + /// The value to check. + /// The name of the value. + private static void RequireReference(T value, string name) where T : class + { + if (value == null) + { + throw new ArgumentNullException(name); + } + } + + /// + /// Gets a Type that can be used with reflection. + /// + /// An object that implements IDispatch. + /// Whether an exception should be thrown if a Type can't be obtained. + /// A .NET Type that can be used with reflection. + private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound) + { + RequireReference(dispatch, "dispatch"); + + Type result = null; + int typeInfoCount; + int hr = dispatch.GetTypeInfoCount(out typeInfoCount); + if (hr == S_OK && typeInfoCount > 0) + { + // Type info isn't usually culture-aware for IDispatch, so we might as well pass + // the default locale instead of looking up the current thread's LCID each time + // (via CultureInfo.CurrentCulture.LCID). + dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result); + } + + if (result == null && throwIfNotFound) + { + // If the GetTypeInfoCount called failed, throw an exception for that. + Marshal.ThrowExceptionForHR(hr); + + // Otherwise, throw the same exception that Type.GetType would throw. + throw new TypeLoadException(); + } + + return result; + } + + /// + /// Tries to get the DISPID for the requested member name. + /// + /// An object that implements IDispatch. + /// The name of a member to lookup. + /// If the method returns true, this holds the DISPID on output. + /// If the method returns false, this value should be ignored. + /// True if the member was found and resolved to a DISPID. False otherwise. + private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId) + { + RequireReference(dispatch, "dispatch"); + RequireReference(name, "name"); + + bool result = false; + + // Members names aren't usually culture-aware for IDispatch, so we might as well + // pass the default locale instead of looking up the current thread's LCID each time + // (via CultureInfo.CurrentCulture.LCID). + Guid iidNull = Guid.Empty; + int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId); + + const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h + const int DISPID_UNKNOWN = -1; //From OAIdl.idl + if (hr == S_OK) + { + result = true; + } + else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN) + { + // This is the only supported "error" case because it means IDispatch + // is saying it doesn't know the member we asked about. + result = false; + } + else + { + // The other documented result codes are all errors. + Marshal.ThrowExceptionForHR(hr); + } + + return result; + } + + #endregion + + #region Private Interfaces + + /// + /// A partial declaration of IDispatch used to lookup Type information and DISPIDs. + /// + /// + /// This interface only declares the first three methods of IDispatch. It omits the + /// fourth method (Invoke) because there are already plenty of ways to do dynamic + /// invocation in .NET. But the first three methods provide dynamic type metadata + /// discovery, which .NET doesn't provide normally if you have a System.__ComObject + /// RCW instead of a strongly-typed RCW. + /// + /// Note: The original declaration of IDispatch is in OAIdl.idl. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("00020400-0000-0000-C000-000000000046")] + private interface IDispatchInfo + { + /// + /// Gets the number of Types that the object provides (0 or 1). + /// + /// Returns 0 or 1 for the number of Types provided by . + /// + /// http://msdn.microsoft.com/en-us/library/da876d53-cb8a-465c-a43e-c0eb272e2a12(VS.85) + /// + [PreserveSig] + int GetTypeInfoCount(out int typeInfoCount); + + /// + /// Gets the Type information for an object if returned 1. + /// + /// Must be 0. + /// Typically, LOCALE_SYSTEM_DEFAULT (2048). + /// Returns the object's Type information. + /// + /// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85) + /// + void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler, + MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo); + + /// + /// Gets the DISPID of the specified member name. + /// + /// Must be IID_NULL. Pass a copy of Guid.Empty. + /// The name of the member to look up. + /// Must be 1. + /// Typically, LOCALE_SYSTEM_DEFAULT (2048). + /// If a member with the requested + /// is found, this returns its DISPID and the method's return value is 0. + /// If the method returns a non-zero value, then this parameter's output value is + /// undefined. + /// Zero for success. Non-zero for failure. + /// + /// http://msdn.microsoft.com/en-us/library/6f6cf233-3481-436e-8d6a-51f93bf91619(VS.85) + /// + [PreserveSig] + int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId); + + // NOTE: The real IDispatch also has an Invoke method next, but we don't need it. + // We can invoke methods using .NET's Type.InvokeMember method with the special + // [DISPID=n] syntax for member "names", or we can get a .NET Type using GetTypeInfo + // and invoke methods on that through reflection. + // Type.InvokeMember: http://msdn.microsoft.com/en-us/library/de3dhzwy.aspx + } + + #endregion + } +} diff --git a/indra/tools/vstool/README.txt b/indra/tools/vstool/README.txt new file mode 100644 index 0000000000..e419180031 --- /dev/null +++ b/indra/tools/vstool/README.txt @@ -0,0 +1,9 @@ +VSTool is a command line utility to manipulate VisualStudio settings. + +The windows cmake project configuration uses VSTool.exe + +A handy upgrade: + figure out how to make cmake build this csharp app + - or write the app using script (jscript?!?) so it doesn't need to be built. + + diff --git a/indra/tools/vstool/VSTool.csproj b/indra/tools/vstool/VSTool.csproj new file mode 100644 index 0000000000..7f431e85c7 --- /dev/null +++ b/indra/tools/vstool/VSTool.csproj @@ -0,0 +1,98 @@ + + + + Local + 8.0.50727 + 2.0 + {96943E2D-1373-4617-A117-D0F997A94919} + Debug + AnyCPU + + + + + VSTool + + + JScript + Grid + IE50 + false + Exe + VSTool + Always + VSTool.VSToolMain + + + + + v2.0 + 2.0 + + + .\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + .\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + System + + + System.Data + + + + + Code + + + + + + + + + + \ No newline at end of file diff --git a/indra/tools/vstool/VSTool.exe b/indra/tools/vstool/VSTool.exe new file mode 100644 index 0000000000000000000000000000000000000000..751540413a0af3256f6bbb30f9a49c7232699d9a GIT binary patch literal 24576 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 W1gZ#B5vU?iMWBj66@fnq1pXH?h%2K2 literal 0 HcmV?d00001 diff --git a/indra/tools/vstool/VSTool.sln b/indra/tools/vstool/VSTool.sln new file mode 100644 index 0000000000..21e3d75971 --- /dev/null +++ b/indra/tools/vstool/VSTool.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSTool", "VSTool.csproj", "{96943E2D-1373-4617-A117-D0F997A94919}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {96943E2D-1373-4617-A117-D0F997A94919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96943E2D-1373-4617-A117-D0F997A94919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96943E2D-1373-4617-A117-D0F997A94919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96943E2D-1373-4617-A117-D0F997A94919}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/indra/tools/vstool/app.config b/indra/tools/vstool/app.config new file mode 100644 index 0000000000..8494f728ff --- /dev/null +++ b/indra/tools/vstool/app.config @@ -0,0 +1,3 @@ + + + diff --git a/indra/tools/vstool/main.cs b/indra/tools/vstool/main.cs new file mode 100644 index 0000000000..1d6b2f14d1 --- /dev/null +++ b/indra/tools/vstool/main.cs @@ -0,0 +1,733 @@ +// Code about getting running instances visual studio +// was borrowed from +// http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Microsoft.CSharp; + +namespace VSTool +{ + // The MessageFilter class comes from: + // http://msdn.microsoft.com/en-us/library/ms228772(VS.80).aspx + // It allows vstool to get timing error messages from + // visualstudio and handle them. + public class MessageFilter : IOleMessageFilter + { + // + // Class containing the IOleMessageFilter + // thread error-handling functions. + + // Start the filter. + public static void Register() + { + IOleMessageFilter newFilter = new MessageFilter(); + IOleMessageFilter oldFilter = null; + CoRegisterMessageFilter(newFilter, out oldFilter); + } + + // Done with the filter, close it. + public static void Revoke() + { + IOleMessageFilter oldFilter = null; + CoRegisterMessageFilter(null, out oldFilter); + } + + // + // IOleMessageFilter functions. + // Handle incoming thread requests. + int IOleMessageFilter.HandleInComingCall(int dwCallType, + System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr + lpInterfaceInfo) + { + //Return the flag SERVERCALL_ISHANDLED. + return 0; + } + + // Thread call was rejected, so try again. + int IOleMessageFilter.RetryRejectedCall(System.IntPtr + hTaskCallee, int dwTickCount, int dwRejectType) + { + if (dwRejectType == 2) + // flag = SERVERCALL_RETRYLATER. + { + // Retry the thread call immediately if return >=0 & + // <100. + return 99; + } + // Too busy; cancel call. + return -1; + } + + int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, + int dwTickCount, int dwPendingType) + { + //Return the flag PENDINGMSG_WAITDEFPROCESS. + return 2; + } + + // Implement the IOleMessageFilter interface. + [DllImport("Ole32.dll")] + private static extern int + CoRegisterMessageFilter(IOleMessageFilter newFilter, out + IOleMessageFilter oldFilter); + } + + [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + interface IOleMessageFilter + { + [PreserveSig] + int HandleInComingCall( + int dwCallType, + IntPtr hTaskCaller, + int dwTickCount, + IntPtr lpInterfaceInfo); + + [PreserveSig] + int RetryRejectedCall( + IntPtr hTaskCallee, + int dwTickCount, + int dwRejectType); + + [PreserveSig] + int MessagePending( + IntPtr hTaskCallee, + int dwTickCount, + int dwPendingType); + } + + class ViaCOM + { + public static object GetProperty(object from_obj, string prop_name) + { + try + { + Type objType = from_obj.GetType(); + return objType.InvokeMember( + prop_name, + BindingFlags.GetProperty, null, + from_obj, + null); + } + catch (Exception e) + { + Console.WriteLine("Error getting property: \"{0}\"", prop_name); + Console.WriteLine(e.Message); + throw e; + } + } + + public static object SetProperty(object from_obj, string prop_name, object new_value) + { + try + { + object[] args = { new_value }; + Type objType = from_obj.GetType(); + return objType.InvokeMember( + prop_name, + BindingFlags.DeclaredOnly | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.SetProperty, + null, + from_obj, + args); + } + catch (Exception e) + { + Console.WriteLine("Error setting property: \"{0}\"", prop_name); + Console.WriteLine(e.Message); + throw e; + } + } + + public static object CallMethod(object from_obj, string method_name, params object[] args) + { + try + { + Type objType = from_obj.GetType(); + return objType.InvokeMember( + method_name, + BindingFlags.DeclaredOnly | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.InvokeMethod, + null, + from_obj, + args); + } + catch (Exception e) + { + Console.WriteLine("Error calling method \"{0}\"", method_name); + Console.WriteLine(e.Message); + throw e; + } + } + }; + + /// + /// The main entry point class for VSTool. + /// + class VSToolMain + { + #region Interop imports + [DllImport("ole32.dll")] + public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); + + [DllImport("ole32.dll")] + public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc); + #endregion + + static System.Boolean ignore_case = true; + + static string solution_name = null; + static bool use_new_vs = false; + static Hashtable projectDict = new Hashtable(); + static string startup_project = null; + static string config = null; + + static object dte = null; + static object solution = null; + + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main(string[] args) + { + int retVal = 0; + bool need_save = false; + + try + { + parse_command_line(args); + + Console.WriteLine("Editing solution: {0}", solution_name); + + bool found_open_solution = GetDTEAndSolution(); + + if (dte == null || solution == null) + { + retVal = 1; + } + else + { + MessageFilter.Register(); + + // Walk through all of the projects in the solution + // and list the type of each project. + foreach (DictionaryEntry p in projectDict) + { + string project_name = (string)p.Key; + string working_dir = (string)p.Value; + if (SetProjectWorkingDir(solution, project_name, working_dir)) + { + need_save = true; + } + } + + if (config != null) + { + need_save = SetActiveConfig(config); + } + + if (startup_project != null) + { + need_save = SetStartupProject(startup_project); + } + + if (need_save) + { + if (found_open_solution == false) + { + ViaCOM.CallMethod(solution, "Close", null); + } + } + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + retVal = 1; + } + finally + { + if (solution != null) + { + Marshal.ReleaseComObject(solution); + solution = null; + } + + if (dte != null) + { + Marshal.ReleaseComObject(dte); + dte = null; + } + + MessageFilter.Revoke(); + } + return retVal; + } + + public static bool parse_command_line(string[] args) + { + string options_desc = + "--solution : MSVC solution name. (required)\n" + + "--use_new_vs : Ignore running versions of visual studio.\n" + + "--workingdir : Set working dir of a VC project.\n" + + "--config : Set the active config for the solution.\n" + + "--startup : Set the startup project for the solution.\n"; + + try + { + // Command line param parsing loop. + int i = 0; + for (; i < args.Length; ++i) + { + if ("--solution" == args[i]) + { + if (solution_name != null) + { + throw new ApplicationException("Found second --solution option"); + } + solution_name = args[++i]; + } + else if ("--use_new_vs" == args[i]) + { + use_new_vs = true; + } + + else if ("--workingdir" == args[i]) + { + string project_name = args[++i]; + string working_dir = args[++i]; + projectDict.Add(project_name, working_dir); + } + else if ("--config" == args[i]) + { + if (config != null) + { + throw new ApplicationException("Found second --config option"); + } + config = args[++i]; + } + else if ("--startup" == args[i]) + { + if (startup_project != null) + { + throw new ApplicationException("Found second --startup option"); + } + startup_project = args[++i]; + } + else + { + throw new ApplicationException("Found unrecognized token on command line: " + args[i]); + } + } + + if (solution_name == null) + { + throw new ApplicationException("The --solution option is required."); + } + } + catch(ApplicationException e) + { + + Console.WriteLine("Oops! " + e.Message); + Console.Write("Command line:"); + foreach (string arg in args) + { + Console.Write(" " + arg); + } + Console.Write("\n\n"); + Console.WriteLine("VSTool command line usage"); + Console.Write(options_desc); + throw e; + } + return true; + } + + public static bool GetDTEAndSolution() + { + bool found_open_solution = true; + + Console.WriteLine("Looking for existing VisualStudio instance..."); + + // Get an instance of the currently running Visual Studio .NET IDE. + // dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.7.1"); + string full_solution_name = System.IO.Path.GetFullPath(solution_name); + if (false == use_new_vs) + { + dte = GetIDEInstance(full_solution_name); + } + + if (dte == null) + { + try + { + Console.WriteLine(" Didn't find open solution, starting new background VisualStudio instance..."); + Console.WriteLine(" Reading .sln file version..."); + string version = GetSolutionVersion(full_solution_name); + + Console.WriteLine(" Using version: {0}...", version); + string progid = GetVSProgID(version); + + Type objType = Type.GetTypeFromProgID(progid); + dte = System.Activator.CreateInstance(objType); + Console.WriteLine(" Reading solution: \"{0}\"", full_solution_name); + + solution = ViaCOM.GetProperty(dte, "Solution"); + object[] openArgs = { full_solution_name }; + ViaCOM.CallMethod(solution, "Open", openArgs); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + Console.WriteLine("Quitting do to error opening: {0}", full_solution_name); + solution = null; + dte = null; + return found_open_solution; + } + found_open_solution = false; + } + + if (solution == null) + { + solution = ViaCOM.GetProperty(dte, "Solution"); + } + + return found_open_solution; + } + + /// + /// Get the DTE object for the instance of Visual Studio IDE that has + /// the specified solution open. + /// + /// The absolute filename of the solution + /// Corresponding DTE object or null if no such IDE is running + public static object GetIDEInstance( string solutionFile ) + { + Hashtable runningInstances = GetIDEInstances( true ); + IDictionaryEnumerator enumerator = runningInstances.GetEnumerator(); + + while ( enumerator.MoveNext() ) + { + try + { + object ide = enumerator.Value; + if (ide != null) + { + object sol = ViaCOM.GetProperty(ide, "Solution"); + if (0 == string.Compare((string)ViaCOM.GetProperty(sol, "FullName"), solutionFile, ignore_case)) + { + return ide; + } + } + } + catch{} + } + + return null; + } + + /// + /// Get a table of the currently running instances of the Visual Studio .NET IDE. + /// + /// Only return instances that have opened a solution + /// A hashtable mapping the name of the IDE in the running object table to the corresponding DTE object + public static Hashtable GetIDEInstances( bool openSolutionsOnly ) + { + Hashtable runningIDEInstances = new Hashtable(); + Hashtable runningObjects = GetRunningObjectTable(); + + IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator(); + while ( rotEnumerator.MoveNext() ) + { + string candidateName = (string) rotEnumerator.Key; + if (!candidateName.StartsWith("!VisualStudio.DTE")) + continue; + + object ide = rotEnumerator.Value; + if (ide == null) + continue; + + if (openSolutionsOnly) + { + try + { + object sol = ViaCOM.GetProperty(ide, "Solution"); + string solutionFile = (string)ViaCOM.GetProperty(sol, "FullName"); + if (solutionFile != String.Empty) + { + runningIDEInstances[ candidateName ] = ide; + } + } + catch {} + } + else + { + runningIDEInstances[ candidateName ] = ide; + } + } + return runningIDEInstances; + } + + /// + /// Get a snapshot of the running object table (ROT). + /// + /// A hashtable mapping the name of the object in the ROT to the corresponding object + [STAThread] + public static Hashtable GetRunningObjectTable() + { + Hashtable result = new Hashtable(); + + int numFetched = 0; + IRunningObjectTable runningObjectTable; + IEnumMoniker monikerEnumerator; + IMoniker[] monikers = new IMoniker[1]; + + GetRunningObjectTable(0, out runningObjectTable); + runningObjectTable.EnumRunning(out monikerEnumerator); + monikerEnumerator.Reset(); + + while (monikerEnumerator.Next(1, monikers, new IntPtr(numFetched)) == 0) + { + IBindCtx ctx; + CreateBindCtx(0, out ctx); + + string runningObjectName; + monikers[0].GetDisplayName(ctx, null, out runningObjectName); + + object runningObjectVal; + runningObjectTable.GetObject( monikers[0], out runningObjectVal); + + result[ runningObjectName ] = runningObjectVal; + } + + return result; + } + + public static string GetSolutionVersion(string solutionFullFileName) + { + string version; + System.IO.StreamReader solutionStreamReader = null; + string firstLine; + string format; + + try + { + solutionStreamReader = new System.IO.StreamReader(solutionFullFileName); + do + { + firstLine = solutionStreamReader.ReadLine(); + } + while (firstLine == ""); + + format = firstLine.Substring(firstLine.LastIndexOf(" ")).Trim(); + + switch(format) + { + case "7.00": + version = "VC70"; + break; + + case "8.00": + version = "VC71"; + break; + + case "9.00": + version = "VC80"; + break; + + case "10.00": + version = "VC90"; + break; + + case "11.00": + version = "VC100"; + break; + + case "12.00": + version = "VC150"; + break; + + default: + throw new ApplicationException("Unknown .sln version: " + format); + } + } + finally + { + if(solutionStreamReader != null) + { + solutionStreamReader.Close(); + } + } + + return version; + } + + public static string GetVSProgID(string version) + { + string progid = null; + switch(version) + { + case "VC70": + progid = "VisualStudio.DTE.7"; + break; + + case "VC71": + progid = "VisualStudio.DTE.7.1"; + break; + + case "VC80": + progid = "VisualStudio.DTE.8.0"; + break; + + case "VC90": + progid = "VisualStudio.DTE.9.0"; + break; + + case "VC100": + progid = "VisualStudio.DTE.10.0"; + break; + + case "VC120": + progid = "VisualStudio.DTE.12.0"; + break; + + case "VC150": + progid = "VisualStudio.DTE.15.0"; + break; + + default: + throw new ApplicationException("Can't handle VS version: " + version); + } + + return progid; + } + + public static bool SetProjectWorkingDir(object sol, string project_name, string working_dir) + { + bool made_change = false; + Console.WriteLine("Looking for project {0}...", project_name); + try + { + object prjs = ViaCOM.GetProperty(sol, "Projects"); + object count = ViaCOM.GetProperty(prjs, "Count"); + for(int i = 1; i <= (int)count; ++i) + { + object[] prjItemArgs = { (object)i }; + object prj = ViaCOM.CallMethod(prjs, "Item", prjItemArgs); + string name = (string)ViaCOM.GetProperty(prj, "Name"); + if (0 == string.Compare(name, project_name, ignore_case)) + { + Console.WriteLine("Found project: {0}", project_name); + Console.WriteLine("Setting working directory"); + + string full_project_name = (string)ViaCOM.GetProperty(prj, "FullName"); + Console.WriteLine(full_project_name); + + // *NOTE:Mani Thanks to incompatibilities between different versions of the + // VCProjectEngine.dll assembly, we can't cast the objects recevied from the DTE to + // the VCProjectEngine types from a different version than the one built + // with. ie, VisualStudio.DTE.7.1 objects can't be converted in a project built + // in VS 8.0. To avoid this problem, we can use the com object interfaces directly, + // without the type casting. Its tedious code, but it seems to work. + + // oCfgs should be assigned to a 'Project.Configurations' collection. + object oCfgs = ViaCOM.GetProperty(ViaCOM.GetProperty(prj, "Object"), "Configurations"); + + // oCount will be assigned to the number of configs present in oCfgs. + object oCount = ViaCOM.GetProperty(oCfgs, "Count"); + + for (int cfgIndex = 1; cfgIndex <= (int)oCount; ++cfgIndex) + { + object[] itemArgs = {(object)cfgIndex}; + object oCfg = ViaCOM.CallMethod(oCfgs, "Item", itemArgs); + object oDebugSettings = ViaCOM.GetProperty(oCfg, "DebugSettings"); + ViaCOM.SetProperty(oDebugSettings, "WorkingDirectory", (object)working_dir); + } + + break; + } + } + made_change = true; + } + catch( Exception e ) + { + Console.WriteLine(e.Message); + Console.WriteLine("Failed to set working dir for project, {0}.", project_name); + } + + return made_change; + } + + public static bool SetStartupProject(string startup_project) + { + bool result = false; + try + { + // You need the 'unique name of the project to set StartupProjects. + // find the project by generic name. + Console.WriteLine("Trying to set \"{0}\" to the startup project", startup_project); + object prjs = ViaCOM.GetProperty(solution, "Projects"); + object count = ViaCOM.GetProperty(prjs, "Count"); + for (int i = 1; i <= (int)count; ++i) + { + object[] itemArgs = { (object)i }; + object prj = ViaCOM.CallMethod(prjs, "Item", itemArgs); + object prjName = ViaCOM.GetProperty(prj, "Name"); + if (0 == string.Compare((string)prjName, startup_project, ignore_case)) + { + object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild"); + ViaCOM.SetProperty(solBuild, "StartupProjects", ViaCOM.GetProperty(prj, "UniqueName")); + Console.WriteLine(" Success!"); + result = true; + break; + } + } + + if (result == false) + { + Console.WriteLine(" Could not find project \"{0}\" in the solution.", startup_project); + } + } + catch (Exception e) + { + Console.WriteLine(" Failed to set the startup project!"); + Console.WriteLine(e.Message); + } + return result; + } + + public static bool SetActiveConfig(string config) + { + bool result = false; + try + { + Console.WriteLine("Trying to set active config to \"{0}\"", config); + object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild"); + object solCfgs = ViaCOM.GetProperty(solBuild, "SolutionConfigurations"); + object[] itemArgs = { (object)config }; + object solCfg = ViaCOM.CallMethod(solCfgs, "Item", itemArgs); + ViaCOM.CallMethod(solCfg, "Activate", null); + Console.WriteLine(" Success!"); + result = true; + } + catch (Exception e) + { + Console.WriteLine(" Failed to set \"{0}\" as the active config.", config); + Console.WriteLine(e.Message); + } + return result; + } + } +} From 7e565a9bcdf5d2af4df34d8d5e66197458a0922b Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 01:29:59 +0100 Subject: [PATCH 095/121] Change filename casing --- indra/llcommon/CMakeLists.txt | 2 +- indra/llcommon/{FSTracyClient.cpp => fstracyclient.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename indra/llcommon/{FSTracyClient.cpp => fstracyclient.cpp} (100%) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ed72fee9d2..c2ee4bff0d 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -259,7 +259,7 @@ set(llcommon_HEADER_FILES # Tracy Profiler support list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp) if (USE_TRACY_PROFILER) - list(APPEND llcommon_SOURCE_FILES FSTracyClient.cpp) + list(APPEND llcommon_SOURCE_FILES fstracyclient.cpp) endif() SET( llcommon_ND_SOURCE_FILES diff --git a/indra/llcommon/FSTracyClient.cpp b/indra/llcommon/fstracyclient.cpp similarity index 100% rename from indra/llcommon/FSTracyClient.cpp rename to indra/llcommon/fstracyclient.cpp From 6104c3aafcc5e0590e66e260aaa2c17303e34ccb Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 11:17:09 +0100 Subject: [PATCH 096/121] Now that Kokua yoinked Contact Sets, we can clean up some ugly dependencies... :P --- indra/llmessage/CMakeLists.txt | 6 +++++ indra/llmessage/llavatarnamecache.cpp | 36 ++++++++++++++++----------- indra/llmessage/llavatarnamecache.h | 11 ++++++++ indra/newview/lggcontactsets.cpp | 7 ++++++ indra/newview/lggcontactsets.h | 2 ++ indra/newview/llappviewer.cpp | 1 + indra/newview/llstartup.cpp | 8 ++++-- 7 files changed, 54 insertions(+), 17 deletions(-) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 952cd085ae..68f83c790c 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -19,6 +19,7 @@ include(Python) include(Tut) include(Python) include(JsonCpp) +include(LLXML) # For accessing settings include_directories (${CMAKE_CURRENT_SOURCE_DIR}) @@ -26,6 +27,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOREHTTP_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} # For accessing settings ${LLMESSAGE_INCLUDE_DIRS} ${LLFILESYSTEM_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} @@ -217,6 +219,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} @@ -235,6 +238,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} @@ -265,6 +269,7 @@ if (LINUX) ${WINDOWS_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} ${LLCOMMON_LIBRARIES} @@ -281,6 +286,7 @@ else (LINUX) ${WINDOWS_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 795860e9f7..d9f8d49b6d 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -48,8 +48,9 @@ #include #include -#include "../newview/lggcontactsets.h" -#include "../llxml/llcontrol.h" + +#include "llcontrol.h" // Optional legacy name cache expiration + // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; // Maximum time an unrefreshed cache entry is allowed. @@ -213,13 +214,15 @@ void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &h LLAvatarName av_name; // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { LLSD info(row); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name.fromLLSD(info); } else @@ -647,13 +650,15 @@ bool LLAvatarNameCache::getName(const LLUUID& agent_id, LLAvatarName *av_name) { *av_name = it->second; // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { LLSD info = av_name->asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name->fromLLSD(info); } // Contact sets alias @@ -711,14 +716,15 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::getNameCallback(cons LLSD test = av_name.asLLSD(); // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { - LL_DEBUGS("AvNameCache") << "DN cache hit via alias " << agent_id << LL_ENDL; LLSD info = av_name.asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name.fromLLSD(info); } // Contact sets alias diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 9663981faf..6b7aecad87 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -44,6 +44,14 @@ public: typedef boost::signals2::signal use_display_name_signal_t; typedef boost::function account_name_changed_callback_t; + // Contact sets + typedef boost::function custom_name_check_callback_t; + void setCustomNameCheckCallback(custom_name_check_callback_t cb) + { + mCustomNameCheckCallback = cb; + } + // + // Import/export the name cache to file. bool importFile(std::istream& istr); void exportFile(std::ostream& ostr); @@ -200,6 +208,9 @@ private: // Time when unrefreshed cached names were checked last. F64 mLastExpireCheck; + + // Contact sets + custom_name_check_callback_t mCustomNameCheckCallback; }; // Parse a cache-control header to get the max-age delta-seconds. diff --git a/indra/newview/lggcontactsets.cpp b/indra/newview/lggcontactsets.cpp index 8ad4cce2df..4a1c76113f 100644 --- a/indra/newview/lggcontactsets.cpp +++ b/indra/newview/lggcontactsets.cpp @@ -1048,6 +1048,13 @@ LGGContactSets::ContactSet* LGGContactSets::getContactSet(const std::string& set return NULL; } +bool LGGContactSets::checkCustomName(const LLUUID& id, bool& dn_removed, std::string& pseudonym) +{ + dn_removed = hasDisplayNameRemoved(id); + pseudonym = getPseudonym(id); + return hasPseudonym(id); +} + // static bool LGGContactSets::handleAddContactSetCallback(const LLSD& notification, const LLSD& response) { diff --git a/indra/newview/lggcontactsets.h b/indra/newview/lggcontactsets.h index fa326210e7..76d87cdc7a 100644 --- a/indra/newview/lggcontactsets.h +++ b/indra/newview/lggcontactsets.h @@ -72,6 +72,8 @@ public: bool hasDisplayNameRemoved(const LLUUID& friend_id); bool hasDisplayNameRemoved(uuid_vec_t ids); + bool checkCustomName(const LLUUID& id, bool& dn_removed, std::string& pseudonym); + string_vec_t getFriendSets(const LLUUID& friend_id); string_vec_t getAllContactSets(); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 92ce80dfdc..2015a2d64c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -6197,6 +6197,7 @@ void LLAppViewer::disconnectViewer() gInventory.getLibraryOwnerID()); } + LLAvatarNameCache::instance().setCustomNameCheckCallback(LLAvatarNameCache::custom_name_check_callback_t()); // Contact sets saveNameCache(); if (LLExperienceCache::instanceExists()) { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5aba427bc7..7f34d172aa 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2504,8 +2504,12 @@ bool idle_startup() LLAvatarTracker::instance().addBuddyList(list); display_startup(); } - - LGGContactSets::getInstance()->loadFromDisk(); // [FS:CR] Load contact sets + + // Contact sets + LGGContactSets* cs_instance = LGGContactSets::getInstance(); + cs_instance->loadFromDisk(); + LLAvatarNameCache::instance().setCustomNameCheckCallback(boost::bind(&LGGContactSets::checkCustomName, cs_instance, _1, _2, _3)); + // bool show_hud = false; LLSD tutorial_setting = response["tutorial_setting"]; From 3e7575859a51415128cc7794dfbb84ad8d432743 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 11:18:29 +0100 Subject: [PATCH 097/121] Add some more headers to pre-compiled header file - maybe it helps a bit... --- indra/newview/llviewerprecompiledheaders.h | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index e378c2448a..3bed65e1c4 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -106,5 +106,31 @@ // Library includes from llmessage project #include "llcachename.h" +// Firestorm additions +#include "llavatarname.h" +#include "llavatarnamecache.h" +#include "llcorehttputil.h" +#include "llformat.h" +#include "llmatrix4a.h" +#include "llvector4a.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #endif From 050a4763c78b2b89e482583e885b368128b2902e Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Mon, 22 Mar 2021 16:32:31 +0100 Subject: [PATCH 098/121] Updated Polish translation --- indra/newview/skins/default/xui/pl/menu_viewer.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/skins/default/xui/pl/menu_viewer.xml b/indra/newview/skins/default/xui/pl/menu_viewer.xml index 1e8c1e6d4c..523af5fc4f 100644 --- a/indra/newview/skins/default/xui/pl/menu_viewer.xml +++ b/indra/newview/skins/default/xui/pl/menu_viewer.xml @@ -402,6 +402,10 @@ + + + + From 41cb6ae1192378f1cf8cce9ed972e7b619464cc9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 22 Mar 2021 17:18:33 +0100 Subject: [PATCH 099/121] Update German translation --- indra/newview/skins/default/xui/de/menu_viewer.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml index 48bcf1f4a4..ec5ae9c9c6 100644 --- a/indra/newview/skins/default/xui/de/menu_viewer.xml +++ b/indra/newview/skins/default/xui/de/menu_viewer.xml @@ -402,7 +402,11 @@ - + + + + + From 9fc1b51ae85cbb1a25f4058c0866d29f99724183 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 22 Mar 2021 23:45:25 +0200 Subject: [PATCH 100/121] SL-14993 Crash accessing mInvBindMatrix --- indra/llprimitive/llmodel.cpp | 30 +++++++++++++++++----------- indra/newview/llskinningutil.cpp | 34 +++----------------------------- indra/newview/llskinningutil.h | 1 - 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index a2d9b4cd9b..702a1b5238 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -434,7 +434,7 @@ void LLModel::generateNormals(F32 angle_cutoff) if (vol_face.mNumIndices > 65535) { - LL_WARNS() << "Too many vertices for normal generation to work." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL; continue; } @@ -1100,7 +1100,7 @@ bool LLModel::loadModel(std::istream& is) { if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) { - LL_WARNS() << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; return false; } } @@ -1132,7 +1132,7 @@ bool LLModel::loadModel(std::istream& is) if (header[lod_name[lod]]["offset"].asInteger() == -1 || header[lod_name[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD - LL_WARNS() << "LoD data is invalid!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL; return false; } @@ -1195,7 +1195,7 @@ bool LLModel::loadModel(std::istream& is) } else { - LL_WARNS() << "unpackVolumeFaces failed!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL; } return false; @@ -1223,7 +1223,7 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) if (!foundRef) { - LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; + LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; return false; } } @@ -1259,7 +1259,7 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn bool isASubset = isMaterialListSubset( ref ); if ( !isASubset ) { - LL_INFOS()<<"Material of model is not a subset of reference."<= face.mNumVertices) { - LL_WARNS() << "Face has invalid index." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid index." << LL_ENDL; return false; } } if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) { - LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid number of indices." << LL_ENDL; return false; } @@ -1879,7 +1887,7 @@ bool validate_model(const LLModel* mdl) { if (mdl->getNumVolumeFaces() == 0) { - LL_WARNS() << "Model has no faces!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Model has no faces!" << LL_ENDL; return false; } @@ -1887,13 +1895,13 @@ bool validate_model(const LLModel* mdl) { if (mdl->getVolumeFace(i).mNumVertices == 0) { - LL_WARNS() << "Face has no vertices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL; return false; } if (mdl->getVolumeFace(i).mNumIndices == 0) { - LL_WARNS() << "Face has no indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no indices." << LL_ENDL; return false; } diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index 1fb63c7444..f325315933 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -309,7 +309,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a if (vol_face.mJointRiggingInfoTab.needsUpdate()) { S32 num_verts = vol_face.mNumVertices; - if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0)) + S32 num_joints = skin->mJointNames.size(); + if (num_verts > 0 && vol_face.mWeights && num_joints > 0) { initJointNums(const_cast(skin), avatar); if (vol_face.mJointRiggingInfoTab.size()==0) @@ -343,7 +344,7 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a for (U32 k=0; k<4; ++k) { S32 joint_index = idx[k]; - if (wght[k] > 0.0f) + if (wght[k] > 0.0f && num_joints > joint_index) { S32 joint_num = skin->mJointNums[joint_index]; if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS) @@ -394,35 +395,6 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a } } -void LLSkinningUtil::updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab) -{ - LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO); - for (S32 i=0; i < num_verts; i++) - { - LLVector4a& pos = positions[i]; - LLVector4a& wght = weights[i]; - for (U32 k=0; k<4; ++k) - { - S32 joint_num = skin->mJointNums[joint_indices[k]]; - llassert(joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS); - { - rig_info_tab[joint_num].setIsRiggedTo(true); - LLMatrix4a bind_shape; - bind_shape.loadu(skin->mBindShapeMatrix); - LLMatrix4a inv_bind; - inv_bind.loadu(skin->mInvBindMatrix[joint_indices[k]]); - LLMatrix4a mat; - matMul(bind_shape, inv_bind, mat); - LLVector4a pos_joint_space; - mat.affineTransform(pos, pos_joint_space); - pos_joint_space.mul(wght[k]); - LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents(); - update_min_max(extents[0], extents[1], pos_joint_space); - } - } - } -} - // This is used for extracting rotation from a bind shape matrix that // already has scales baked in LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4) diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index 549aa6a29f..efe7c85997 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -67,7 +67,6 @@ namespace LLSkinningUtil void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar); void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face); - void updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab); LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4); }; From 795ec5c43cbb73cc37f1843b4ec7fecad7dab7bb Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Tue, 23 Mar 2021 00:16:54 +0100 Subject: [PATCH 101/121] FIRE-30859 Russian translation small update, by Romka Swallowtail --- indra/newview/skins/default/xui/ru/menu_viewer.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/skins/default/xui/ru/menu_viewer.xml b/indra/newview/skins/default/xui/ru/menu_viewer.xml index 1e725f28ad..87e3202e01 100644 --- a/indra/newview/skins/default/xui/ru/menu_viewer.xml +++ b/indra/newview/skins/default/xui/ru/menu_viewer.xml @@ -443,6 +443,10 @@ + + + + From aba9a7319332009b01e1cbf9b3e545068c953079 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 22 Mar 2021 23:45:25 +0200 Subject: [PATCH 102/121] SL-14993 Crash accessing mInvBindMatrix --- indra/llprimitive/llmodel.cpp | 30 +++++++++++++++++----------- indra/newview/llskinningutil.cpp | 34 +++----------------------------- indra/newview/llskinningutil.h | 1 - 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index d774f9276d..548913ecaf 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -446,7 +446,7 @@ void LLModel::generateNormals(F32 angle_cutoff) if (vol_face.mNumIndices > 65535) { - LL_WARNS() << "Too many vertices for normal generation to work." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL; continue; } @@ -1115,7 +1115,7 @@ bool LLModel::loadModel(std::istream& is) { if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) { - LL_WARNS() << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; return false; } } @@ -1147,7 +1147,7 @@ bool LLModel::loadModel(std::istream& is) if (header[lod_name[lod]]["offset"].asInteger() == -1 || header[lod_name[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD - LL_WARNS() << "LoD data is invalid!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL; return false; } @@ -1210,7 +1210,7 @@ bool LLModel::loadModel(std::istream& is) } else { - LL_WARNS() << "unpackVolumeFaces failed!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL; } return false; @@ -1238,7 +1238,7 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) if (!foundRef) { - LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; + LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; return false; } } @@ -1274,7 +1274,7 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn bool isASubset = isMaterialListSubset( ref ); if ( !isASubset ) { - LL_INFOS()<<"Material of model is not a subset of reference."<= face.mNumVertices) { - LL_WARNS() << "Face has invalid index." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid index." << LL_ENDL; return false; } } if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) { - LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid number of indices." << LL_ENDL; return false; } @@ -1900,7 +1908,7 @@ bool validate_model(const LLModel* mdl) { if (mdl->getNumVolumeFaces() == 0) { - LL_WARNS() << "Model has no faces!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Model has no faces!" << LL_ENDL; return false; } @@ -1908,13 +1916,13 @@ bool validate_model(const LLModel* mdl) { if (mdl->getVolumeFace(i).mNumVertices == 0) { - LL_WARNS() << "Face has no vertices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL; return false; } if (mdl->getVolumeFace(i).mNumIndices == 0) { - LL_WARNS() << "Face has no indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no indices." << LL_ENDL; return false; } diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index e1f617ace6..d1e3e9b943 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -372,7 +372,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a if (vol_face.mJointRiggingInfoTab.needsUpdate()) { S32 num_verts = vol_face.mNumVertices; - if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0)) + S32 num_joints = skin->mJointNames.size(); + if (num_verts > 0 && vol_face.mWeights && num_joints > 0) { initJointNums(const_cast(skin), avatar); if (vol_face.mJointRiggingInfoTab.size()==0) @@ -406,7 +407,7 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a for (U32 k=0; k<4; ++k) { S32 joint_index = idx[k]; - if (wght[k] > 0.0f) + if (wght[k] > 0.0f && num_joints > joint_index) { S32 joint_num = skin->mJointNums[joint_index]; if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS) @@ -457,35 +458,6 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a } } -void LLSkinningUtil::updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab) -{ - LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO); - for (S32 i=0; i < num_verts; i++) - { - LLVector4a& pos = positions[i]; - LLVector4a& wght = weights[i]; - for (U32 k=0; k<4; ++k) - { - S32 joint_num = skin->mJointNums[joint_indices[k]]; - llassert(joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS); - { - rig_info_tab[joint_num].setIsRiggedTo(true); - LLMatrix4a bind_shape; - bind_shape.loadu(skin->mBindShapeMatrix); - LLMatrix4a inv_bind; - inv_bind.loadu(skin->mInvBindMatrix[joint_indices[k]]); - LLMatrix4a mat; - matMul(bind_shape, inv_bind, mat); - LLVector4a pos_joint_space; - mat.affineTransform(pos, pos_joint_space); - pos_joint_space.mul(wght[k]); - LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents(); - update_min_max(extents[0], extents[1], pos_joint_space); - } - } - } -} - // This is used for extracting rotation from a bind shape matrix that // already has scales baked in LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4) diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index 0f25f96ed3..cb7653603b 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -70,7 +70,6 @@ namespace LLSkinningUtil void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar); void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face); - void updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab); LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4); }; From 8387c0e6cf2f4521e8ade56e5f157f52ced3a6b5 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 23 Mar 2021 17:47:31 +0100 Subject: [PATCH 103/121] Linux; KDU update --- autobuild.xml | 6 +++--- indra/llkdu/llimagej2ckdu.h | 2 +- indra/llkdu/llkdumem.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 83fe052c56..0254355aa5 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -285,7 +285,7 @@ http://3p.firestormviewer.org/Tracy-v0.7.6-windows-210761143.tar.bz2 name - linux + windows version @@ -1944,9 +1944,9 @@ archive hash - eba43dcfeb1c14aab25f588d9f625839 + dce2cc624e216eb991412ec0c5538cf9 url - file:///opt/firestorm/kdu-8.0.6-linux64-202302217.tar.bz2 + file:///opt/firestorm/kdu-8.1-linux64-210652339.tar.bz2 name linux64 diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index d82189b5df..9de1159009 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -32,7 +32,7 @@ // // KDU core header files // -#ifndef LL_WINDOWS +#ifdef LL_DARWIN #define KDU_NO_THREADS #endif diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 44757a4bfc..28164e157e 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,7 +28,7 @@ #define LL_LLKDUMEM_H // Support classes for reading and writing from memory buffers in KDU -#ifndef LL_WINDOWS +#ifdef LL_DARWIN #define KDU_NO_THREADS #endif From d65e141b0bd9594494805e63d313b1800dd9ddb8 Mon Sep 17 00:00:00 2001 From: Beq Date: Wed, 24 Mar 2021 01:52:21 +0000 Subject: [PATCH 104/121] Make blackmagic windows specific --- indra/llcommon/fstracyclient.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/fstracyclient.cpp b/indra/llcommon/fstracyclient.cpp index 79ccc84b4c..55c05f0cc8 100644 --- a/indra/llcommon/fstracyclient.cpp +++ b/indra/llcommon/fstracyclient.cpp @@ -22,13 +22,18 @@ #include "client/TracyProfiler.cpp" #include "client/TracyCallstack.cpp" #include "client/TracySysTime.cpp" -#ifndef __CYGWIN__ + +// Black magic on Windows to deal with windows entry points that are not found in a standard LL windows build +// This is less invasive than changing the 3p library itself. +// TODO: work out why the windows trace subsystem stuff is not defined in our builds, this then goes away (hopefully) +#if defined(LL_WINDOWS) && !defined(__CYGWIN__) #define __CYGWIN__ #include "client/TracySysTrace.cpp" #undef __CYGWIN__ #else #include "client/TracySysTrace.cpp" #endif + #include "common/TracySocket.cpp" #include "client/tracy_rpmalloc.cpp" #include "client/TracyDxt1.cpp" From 52a0c0365964f343538081f8713cbf1ac19ef18b Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Wed, 24 Mar 2021 20:10:41 +0100 Subject: [PATCH 105/121] Minor credits update --- indra/newview/skins/default/xui/en/floater_about.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index 932effd92c..b71591f5e5 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -190,7 +190,7 @@ Special thanks to our Firestorm Support Team, wiki editors, educators, and trans top_pad="4" width="450" word_wrap="true"> -Afrika Burton, Albatroz Hird, Alexru Resident, alison Seesaw, AnaLucia Loon, Angell Airy, Annuccia Resident, Annuccia Vuckovic, Anubi Alter, Atthosz Amiot, Ayelin Ethaniel, Basement Desade, Bia Scribe, Bluezy Bleac, Clarke2, Christy Mansbridge, Chymy India, Cordoba Cluny, Damian Zhaoying, DARK Mirabella, Emmanuella Checchinato, Ed Merryman, Eressea Karsin, Erick Gregan, F0RBIDDEN, Fabry String, Fetish3d, Flandria Connolly, Foksy, Franklyn Watanabe, FreeSpirit Simmering, Gee McAuley, Greatfox Snowpaw, Gweneth Lange, Hatake Kohime, Hiroshi Kumaki, Hope Dreier, Jodi Nikolaidis, JogiTE Clip, Kool Koolhoven, Kosmox Voom, Lachrimo Skytower, Lalwende Leakey, Landaree Levee, Lassie, Lette Ponnier, Lina Pussycat, Lyn Mimistrobell, Marianne Qunhua, Marybeth Oceanlane, Mel Hinarada, Mickeala Praga, Miller Thor, Miro Collas, Mister Acacia, Morgause Whiteberry, Mysty Saunders, Nano Bouscario, narcisssia McMahon, Natem Andel, Nicoletta Schnute, Nisa Maverick, NogarDrevlis Lectar, Norton Burns, PanteraPolnocy, Peewee Musytari, Pisano Smit, PixelProphet Lane, Pol Xaron, Poledra Behemoth, Programmtest, Rander Teskat, Ricky Munz, Riku Highfield, RINOBIT Footman, Robert0 Siamendes, Roth Grut, Sabah Back, SaHaRa Connor, Selene Gregoire, Selo Jacobus, silvanaf Demina, Sniper Siemans, Spartaco Zemenis, Spino Forcella, Srilania Svoboda, Sunset Faulkes, Tanja Levenque, TankMaster Finesmith, Tarlyn Dagger, Thea Brianna, Toy Wylie, Whirly Fizzle, Willow Wilder, Wolfspirit Magic and XLR8RRICK Hudson. +Afrika Burton, Albatroz Hird, Alexru Resident, alison Seesaw, AnaLucia Loon, Angell Airy, Annuccia Resident, Annuccia Vuckovic, Anubi Alter, Atthosz Amiot, Ayelin Ethaniel, Basement Desade, Bia Scribe, Bluezy Bleac, Clarke2, Christy Mansbridge, Chymy India, Cordoba Cluny, Damian Zhaoying, DARK Mirabella, Emmanuella Checchinato, Ed Merryman, Enigma Conundrum, Eressea Karsin, Erick Gregan, F0RBIDDEN, Fabry String, Fetish3d, Flandria Connolly, Foksy, FreeSpirit Simmering, Gee McAuley, Greatfox Snowpaw, Gweneth Lange, Hatake Kohime, Hiroshi Kumaki, Hope Dreier, Jodi Nikolaidis, JogiTE Clip, Kool Koolhoven, Kosmox Voom, Lachrimo Skytower, Lalwende Leakey, Landaree Levee, Lassie, Lette Ponnier, Lina Pussycat, Lyn Mimistrobell, Marianne Qunhua, Marybeth Oceanlane, Mel Hinarada, Mickeala Praga, Miller Thor, Miro Collas, Mister Acacia, Morgause Whiteberry, Mysty Saunders, Nano Bouscario, narcisssia McMahon, Natem Andel, Nicoletta Schnute, Nisa Maverick, NogarDrevlis Lectar, Norton Burns, PanteraPolnocy, Peewee Musytari, Pisano Smit, PixelProphet Lane, Pol Xaron, Poledra Behemoth, Programmtest, Rander Teskat, Ricky Munz, Riku Highfield, RINOBIT Footman, Robert0 Siamendes, Roth Grut, Sabah Back, SaHaRa Connor, Selene Gregoire, Selo Jacobus, silvanaf Demina, Sniper Siemans, Spartaco Zemenis, Spino Forcella, Srilania Svoboda, Sunset Faulkes, Tanja Levenque, TankMaster Finesmith, Tarlyn Dagger, Thea Brianna, Toy Wylie, Whirly Fizzle, Willow Wilder, Wolfspirit Magic and XLR8RRICK Hudson. Date: Wed, 24 Mar 2021 21:01:34 +0100 Subject: [PATCH 106/121] FIRE-30863 French translation update, by Laurent Bechir --- .../skins/default/xui/fr/floater_phototools.xml | 1 + .../default/xui/fr/menu_inventory_gear_default.xml | 3 +++ indra/newview/skins/default/xui/fr/menu_viewer.xml | 6 ++++++ .../skins/default/xui/fr/panel_preferences_UI.xml | 12 ++++++++++++ .../skins/default/xui/fr/panel_preferences_move.xml | 2 +- 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/fr/floater_phototools.xml b/indra/newview/skins/default/xui/fr/floater_phototools.xml index fde691f09f..a0ce82854b 100644 --- a/indra/newview/skins/default/xui/fr/floater_phototools.xml +++ b/indra/newview/skins/default/xui/fr/floater_phototools.xml @@ -202,6 +202,7 @@ + diff --git a/indra/newview/skins/default/xui/fr/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/fr/menu_inventory_gear_default.xml index 3b52460656..da39ae8a42 100644 --- a/indra/newview/skins/default/xui/fr/menu_inventory_gear_default.xml +++ b/indra/newview/skins/default/xui/fr/menu_inventory_gear_default.xml @@ -27,5 +27,8 @@ + + + diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index a969fcbd9c..e9ac21e01e 100644 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -116,10 +116,16 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml b/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml index b42d93f681..db08c38422 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml @@ -71,6 +71,18 @@ + Format heure : + + + + + + + + + + + Barres de Navigation & de Favoris : diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml index 5349165b7d..f94e49f8f2 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml @@ -47,7 +47,7 @@ - + Prévision de mouvement si changement de région : From 91b2000f01253465dc63cb771c80e212ff6b1bf0 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Fri, 26 Mar 2021 22:15:36 +0100 Subject: [PATCH 107/121] FIRE-30867 French translation update, by Laurent Bechir --- .../newview/skins/default/xui/fr/strings.xml | 527 ++++++++++++++++++ 1 file changed, 527 insertions(+) diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 39a35ff473..ba3cce86f6 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -6041,4 +6041,531 @@ correctement. Si vous continuez à recevoir ce message d'erreur, veuillez v [https://community.secondlife.com/knowledgebase/english/error-messages-r520/#Section__3 Base de connaissances] + + Aucune vue de la caméra n'a encore été enregistrée. + + + Impossible de restaurer la vue de la caméra parce que la position de la caméra est au-delà de la distance d'affichage. + + + En raison des modifications apportées au code, il n'est plus nécessaire d'utiliser ce geste. Le réglage progressif de la distance d'affichage a été activé.. + + + Position de la caméra [POS] copiée dans le presse-papiers. + + + Distance d'affichage réglèe [DISTANCE]m. + + + Résultat total pour [DICE]d[FACES][MODIFIER]: [RESULT]. + + + Vous devez fournir des valeurs positives pour les dés (max 100) et les faces (max 1000). + + + Si vous souhaitez utiliser un modificateur, vous devez fournir un numéro de modificateur valide et le type de modificateur. Le modificateur doit être compris entre -1000 et 1000. Les types de modificateurs valides sont "+" (bonus), "-" (pénalités), ">", "<" (succès), "r>", "r<", "r" (relancer), "!p>", "!p<", "!p" (pénétrations), "!>", "!<" and "!" (explosions). Vous ne pouvez utiliser qu'un seul type de modificateur par tirage. Exemples : "[COMMAND] 1 20 + 5", "[COMMAND] 5 40 > 15", "[COMMAND] 10 25 ! 25", "[COMMAND] 15 25 !< 10". + + + Explosé + + + Pénétré + + + Succès + + + Relancer + + + Cette opération ne peut être réalisée car le viewer se figerait dans une boucle infinie. Veuillez modifier les critères. + + + '[RAND]' n'est pas une expression valable pour RAND(min,max). MAX doit être supérieur à MIN, les deux dans une fourchette de -10000 à 10000. + + + Bande passante maximale réglée à [VALUE] KBPS. + + + Téléportation proposée à [NAME]. + + + + + avec support de Havok + + + avec support de OpenSimulator + + + + + '[TEXT]' introuvable + + + Aucun résultat + + + Recherche en cours... + + + Toutes catégories + + + Certains termes de votre requête de recherche ont été exclus en raison de restrictions de contenu, comme précisé dans les Community Standards. + + + Vos termes de recherche étant trop courts, aucune recherche n'a été effectuée. + + + La recherche à l'ancienne a été désactivée dans cette région. + + + Basse (1/7) + + + Moyenne-Basse (2/7) + + + Moyenne (3/7) + + + Moyenne-Élevée (4/7) + + + Élevée (5/7) + + + Élevée-Ultra (6/7) + + + Ultra (7/7) + + + Inconnu, au-delà de la normale, vérifiez le paramètre de débogage RenderQualityPerformance + + + [APP_NAME] ne peut pas créer le bridge LSL si "Activer le Bridge LSL" est désactivé dans les Préférences. + + + [APP_NAME] n'a pas pu créer un bridge LSL. Veuillez activer votre bibliothèque et vous reconnecter. + + + Création d'un bridge en cours, impossible d'en lancer un autre. Veuillez attendre quelques minutes avant de réessayer. + + + Création du bridge. Cela peut prendre un moment, veuillez patienter. + + + Bridge non créé. Le script de bridge n'a pas pu être créé. + + + Bridge non créé. Le bridge porte un nom incorrect. Veuillez utiliser le menu 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour recréer le bridge. + + + Bridge non créé. Le bridge n'a pas pu être trouvé dans l'inventaire. Veuillez utiliser le menu 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour recréer le bridge. + + + Le bridge n'a pas réussi à s'attacher. Il ne s'agit pas de la version actuelle du bridge. Veuillez utiliser le menu 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour recréer le bridge. + + + Le bridge ne s'est pas attaché. Le bridge n'a pas été trouvé dans le bon emplacement de l'inventaire. Veuillez utiliser le menu 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour recréer le bridge. + + + Le bridge ne s'est pas attaché. Quelque chose d'autre utilisait le point d'attache du bridge. Veuillez utiliser le menu 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour recréer le bridge. + + + L'objet bridge n'a pas pu être trouvé. Impossible de procéder à sa création, fin de tâche. + + + L'inventaire du bridge contient des éléments inattendus. + + + Le bridge n'a pas fini de se créer, vous devrez peut-être utiliser la fonction 'Moi/Santé de l'avatar/Recréer le Bridge' LSL de [APP_NAME] pour le recréer avant de l'utiliser. + + + Bridge détaché. + + + Bridge créé. + + + Infos sur le script : '[OBJECT_NAME]': [[OBJECT_RUNNING_SCRIPT_COUNT]/[OBJECT_TOTAL_SCRIPT_COUNT]] scripts en cours d'exécution, [OBJECT_SCRIPT_MEMORY] KB taille limite de mémoire autorisée, [OBJECT_SCRIPT_TIME] ms de temps CPU utilisé.[PATHFINDING_TEXT] + + + Temps moyen d'utilisation du CPU pour la navigation avec recherche de chemin : [OBJECT_CHARACTER_TIME] ms. + + +ID de l'objet : [INSPECTING_KEY] +Description: [OBJECT_DESC] +Prim racine : [OBJECT_ROOT] +Prim count: [OBJECT_PRIM_COUNT] +Impact sur le terrain : [OBJECT_PRIM_EQUIVALENCE] +Articles de l'inventaire : [OBJECT_TOTAL_INVENTORY_COUNT] +Vélocité : [OBJECT_VELOCITY] +Position: [OBJECT_POS] +Rotation: [OBJECT_ROT] +Vitesse angulaire : [OBJECT_OMEGA] (radians per second) +Créateur : [OBJECT_CREATOR] +Propriétaire : [OBJECT_OWNER] +Propriétaire précédent : [OBJECT_LAST_OWNER_ID] +Posé par : [OBJECT_REZZER_KEY] +Groupe : [OBJECT_GROUP] +Date de création : [OBJECT_CREATION_TIME] +Type de cheminement : [OBJECT_PATHFINDING_TYPE] +Point d'attache : [OBJECT_ATTACHED_POINT] +Attaché temporairement : [OBJECT_TEMP_ATTACHED] +Votre position actuelle : [AVATAR_POS] + + + Info script : L'objet à vérifier n'est pas valable ou est hors de portée. + + + Info script : Réception d'une réponse malformée du bridge. Réessayez. + + + NOTICE : Un ou plusieurs scripts ont été ajoutés au bridge de votre [APP_NAME] ! Si vous ne vous attendiez pas à recevoir ce message, utilisez le menu Moi/Santé de l'avatar/Recréer le bridge LSL de [APP_NAME] pour recréer le bridge. + + + NOTICE : Le script du Bridge utilise l'ancien LSO (Limite de mémoire de 16 Ko) au lieu de la nouvelle machine virtuelle Mono (Limite de mémoire de 64 Ko), qui crée une forte probabilité de collision entre piles et de défaillance du bridge par manque de mémoire. Utilisez le menu Moi/Santé de l'avatar/Recréer le bridge LSL de [APP_NAME] pour recréer le bridge. Si vous voyez ce message à nouveau - veuillez essayer à nouveau dans une autre région. + + + + + [APP_NAME] Animation Overrider enabled. + + + [APP_NAME] Animation Overrider disabled. + + + Pause par un attachement scripté. + + + Relancé par un attachement scripté. + + + Animations de position debout mises en pause par un attachement scripté. + + + Animations de position debout relancées par un attachement scripté. + + + + + Distance d'affichage + + + Particules max. + + + LOD de l'avatar + + + Facteur de LOD + + + Avatars max. + + + Décalage des étiquettes + + + Étiquettes de nom + + + Cible du LookAt + + + Couleur sous le curseur + + + + + Pose T + + + Bras baissés, jambes jointes + + + Bras baissés, assis + + + Bras en bas, jambes jointes + + + Bras en bas, jambes écartées + + + Bras vers l'avant, jambes écartées + + + Bras vers l'avant, jambes jointes + + + Bras tendus, jambes écartées + + + Bras tendus, assis + + + Bras vers le haut, jambes écartées + + + Bras vers le haut, jambes serrées + + + + Environnement partagé + + + Basé sur le cycle du jour + + + Rien + + + + Mute de la discussion de groupe de [NAME]. + + + Vous avez été invité à une conférence (ad-hoc) par [AVATAR_NAME], mais la visionneuse l'a automatiquement ignoré à cause de vos réglages. + + + + + L'appareil photo ne peut pas faire la mise au point sur l'utilisateur [AVATARNAME] parce qu'il est en dehors de votre distance d'affichage. + + + est entré dans la distance d'affichage ([DISTANCE] m). + + + a quitté la distance d'affichage. + + + est entré portée de discussion ([DISTANCE] m). + + + est hors de portée de discussion. + + + est entré dans la région. + + + est entré dans la région ([DISTANCE] m). + + + a quitté la région. + + + a déclenché l'alerte sur l'âge. Âge: [AGE] jour(s) + + + + Le nombre total de scripts dans la région est passé de [OLD_VALUE] à [NEW_VALUE] ([DIFFERENCE]). + + + Le nombre total de scripts dans la région est passé de [OLD_VALUE] à [NEW_VALUE] ([DIFFERENCE]). + + + Le basculement du préprocesseur ne prendra effet que lorsque vous fermerez et rouvrirez cet éditeur. + + + + + + Démarrage du préprocesseur de [APP_NAME]... + + + Avertissement : Préprocesseur non supporté dans cette version. ([WHERE]) + + + Préprocesseur de [APP_NAME] désactivé par la directive à la ligne [LINENUMBER]. + + + Réglages : + + + [ERR_NAME] ([LINENUMBER]): [ERR_DESC] + + + [SEVERITY]: [ERR_NAME] ([LINENUMBER]): [ERR_DESC] + + + [ERR_NAME] ([LINENUMBER]): exception captée: [ERR_DESC] + + + [ERR_NAME] ([LINENUMBER]): exception inattendue captée. + + + Détection de la directive compile-as-LSL2 outrepassant les préférences. + + + Détection de la directive compile-as-Mono outrepassant les préférences. + + + + + L'affichage de votre position actuelle dans la barre de menu a été désactivé par défaut pour la série de skin Starlight. + + + L'affichage de la barre de navigation a été activé par défaut pour la série de skin Starlight. + + + + + [SECONDS] plus tôt + + + Toujours en cours + + + inconnu + + + + est en ligne. + + + est hors ligne. + + + + Nouveau + + + Agent inconnu + + + Vous faites partie de [COUNT] groups ([REMAINING] restant). + + + Vous faites partie de [COUNT] groupes. + + + Code d'erreur SL : Format de message non valide. Réessayer plus tard. + + + Code d'erreur SL : La requête a expiré. + + + Code d'erreur SL : [STATUS] ( https://en.wikipedia.org/wiki/List_of_HTTP_status_codes ). + + + Téléportation par double clic activée. + + + Téléportation par double clic désactivée. + + + Toujours courir activé. + + + Toujours courir désactivé. + + + La région où vous êtes est sur le point de redémarrer. Si vous restez, vous serez déconnecté. + + + '[NAME]' a été ajouté à la liste de blocage. + + + '[NAME]' a été supprimé de la liste de blocage. + + + Je demande des informations sur la configuration de votre système. + + + Je demande des informations sur la configuration de votre système pour la raison suivante : [REASON] + + + Informations envoyées : [DATA] + + + Demande refusée. + + + La raison invoquée est la suivante : [REASON] + + + + + Tous les éléments + + + Eléments récents + + + Eléments portés + + + + Un membre du groupe nommé [NAME] + + + (en ligne) + + + + Normalement + + + Jamais + + + Entièrement + + + + Complexité : [COMPLEXITY] + + + Surface de la texture : [TEXTURE_AREA] m² + + + + Police inconnue + + + Mode inconnu + + + Texte + + + Envoi... +[ASSET_NAME] + + + Oui + + + Non + + + Porter le dossier de l'inventaire + + + Image pour le vêtement + + + (Script inconnu) + + + [COST] L$ + + + + Importation des réglages Windlights... + + + + + Aucun élément + + + 1 élément + + + [NUM_ELEMENTS] éléments + From bf78fdf362ebcb74afa3e66f14050a14fe715d4b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 24 Mar 2021 22:20:28 +0200 Subject: [PATCH 108/121] SL-14992 Replaced LLAPRFile's isExist with LLDirUtil's fileExists in fmodstudio This particular case of LLAPRFile crashes due to thread issues (and if it doesn't it might be affecting some other apr call due to using default pool). Function is not opening the .dsf file in question and LLAPRFile won't ensure that file exists till the end of the function, it just checks that file exists at a given moment. No point to overcomplicate things by adding thread safe pool, so replaced with dirutil. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 3790107fd1..259bea4b67 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -804,7 +804,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename) return false; } - if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB)) + if (!gDirUtilp->fileExists(filename)) { // File not found, abort. return false; From aa8acc6deb56c4400099c5f371ce61621c5f37d3 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 27 Mar 2021 12:17:51 +0100 Subject: [PATCH 109/121] No need to build empty cpp files --- indra/newview/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 49bbd8520d..2dd2138c43 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -250,7 +250,7 @@ set(viewer_SOURCE_FILES llblockedlistitem.cpp llblocklist.cpp llbox.cpp - llbreadcrumbview.cpp + #llbreadcrumbview.cpp # Unused llbrowsernotification.cpp llbuycurrencyhtml.cpp llcallingcard.cpp @@ -490,7 +490,7 @@ set(viewer_SOURCE_FILES lllandmarkactions.cpp lllandmarklist.cpp lllegacyatmospherics.cpp - lllistbrowser.cpp + #lllistbrowser.cpp # Unused lllistcontextmenu.cpp lllistview.cpp lllocalbitmaps.cpp @@ -747,7 +747,7 @@ set(viewer_SOURCE_FILES llurlhistory.cpp llurllineeditorctrl.cpp llurlwhitelist.cpp - llvectorperfoptions.cpp + #llvectorperfoptions.cpp # Unused llversioninfo.cpp llviewchildren.cpp llviewerassetstats.cpp @@ -1018,7 +1018,7 @@ set(viewer_HEADER_FILES llblockedlistitem.h llblocklist.h llbox.h - llbreadcrumbview.h + #llbreadcrumbview.h # Unused llbuycurrencyhtml.h llcallingcard.h llcapabilityprovider.h @@ -1259,7 +1259,7 @@ set(viewer_HEADER_FILES lllandmarkactions.h lllandmarklist.h lllightconstants.h - lllistbrowser.h + #lllistbrowser.h # Unused lllistcontextmenu.h lllistview.h lllocalbitmaps.h @@ -1509,7 +1509,7 @@ set(viewer_HEADER_FILES llurlhistory.h llurllineeditorctrl.h llurlwhitelist.h - llvectorperfoptions.h + #llvectorperfoptions.h # Unused llversioninfo.h llviewchildren.h llviewerassetstats.h From b25a3e54485128a6597bad8077e70064c63e76cb Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 28 Mar 2021 14:09:39 +0200 Subject: [PATCH 110/121] Correct KDU version check. --- indra/llkdu/llimagej2ckdu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index d9449db6ad..300a60c551 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1121,7 +1121,7 @@ void set_default_colour_weights(kdu_params *siz) } // Fix image encoding for KDU >= 8.0.4 -#if KDU_MAJOR_VERSION >= 8 && KDU_MINOR_VERSION >= 0 && KDU_PATCH_VERSION >= 4 +#if (KDU_MAJOR_VERSION*10000 + KDU_MINOR_VERSION*100 + KDU_PATCH_VERSION) >= 80004 cod = siz->access_cluster(ENC_params); assert(cod != NULL); #endif From d3ed0015f5c4ca60d8b2935bd23e29ab0b7f6208 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 28 Mar 2021 14:10:09 +0200 Subject: [PATCH 111/121] Windows; KDU 8.1 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 0254355aa5..4a30eaeb74 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1956,9 +1956,9 @@ archive hash - f5c682b490672aadf97ec9b938b351a9 + 1cd284c85b5f05fff6b286e85b55ebf2 url - file:///c:/cygwin/opt/firestorm/kdu-8.0.6-windows-202302209.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2 name windows @@ -1968,9 +1968,9 @@ archive hash - f5c682b490672aadf97ec9b938b351a9 + 1cd284c85b5f05fff6b286e85b55ebf2 url - file:///c:/cygwin/opt/firestorm/kdu-8.0.6-windows-202302209.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2 name windows64 From a22cf012713d2916c527543336f0c6ef1a552553 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 28 Mar 2021 19:26:15 +0200 Subject: [PATCH 112/121] if using a cache like stashed.io use /Z7 for debug format as /Zi is generally incompatible with all caches for cl.exe --- indra/cmake/00-Common.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 5dd44a953b..8e62e95bac 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -60,6 +60,15 @@ if (WINDOWS) # Don't build DLLs. set(BUILD_SHARED_LIBS OFF) + if( USE_COMPILERCACHE ) + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_REELASE}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + endif() + # for "backwards compatibility", cmake sneaks in the Zm1000 option which royally # screws incredibuild. this hack disables it. # for details see: http://connect.microsoft.com/VisualStudio/feedback/details/368107/clxx-fatal-error-c1027-inconsistent-values-for-ym-between-creation-and-use-of-precompiled-headers From f47f33f999506bf6e434057a3af1b4cf03e66d66 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 29 Mar 2021 20:44:11 +0200 Subject: [PATCH 113/121] Remove more unused stuff from build --- indra/newview/CMakeLists.txt | 4 ++-- indra/newview/llfloaterauction.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2dd2138c43..3e0e5b86d3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -652,7 +652,7 @@ set(viewer_SOURCE_FILES llregioninfomodel.cpp llregionposition.cpp llremoteparcelrequest.cpp - llsavedsettingsglue.cpp + #llsavedsettingsglue.cpp # Unused llsaveoutfitcombobtn.cpp llscenemonitor.cpp llsceneview.cpp @@ -1410,7 +1410,7 @@ set(viewer_HEADER_FILES llremoteparcelrequest.h llresourcedata.h llrootview.h - llsavedsettingsglue.h + #llsavedsettingsglue.h # Unused llsaveoutfitcombobtn.h llscenemonitor.h llsceneview.h diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 0bac4cff3f..20b62a063a 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -43,7 +43,7 @@ #include "llmimetypes.h" #include "llnotifications.h" #include "llnotificationsutil.h" -#include "llsavedsettingsglue.h" +// #include "llsavedsettingsglue.h" # Unused #include "llviewertexturelist.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" From 8da5956e7dc7f14712be9caaaf9dc71fb2a83588 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Tue, 30 Mar 2021 17:01:23 +0200 Subject: [PATCH 114/121] Load Firestorm's registration page from within the viewer itself --- indra/newview/fspanellogin.cpp | 5 ++++- indra/newview/installers/windows/installer_template.nsi | 2 +- indra/newview/skins/default/xui/de/panel_login.xml | 2 +- indra/newview/skins/default/xui/de/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/de/strings.xml | 2 +- indra/newview/skins/default/xui/en/panel_login.xml | 2 +- indra/newview/skins/default/xui/en/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/en/strings.xml | 2 +- indra/newview/skins/default/xui/es/panel_login.xml | 2 +- indra/newview/skins/default/xui/es/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/es/strings.xml | 2 +- indra/newview/skins/default/xui/fr/panel_login.xml | 2 +- indra/newview/skins/default/xui/fr/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/fr/strings.xml | 2 +- indra/newview/skins/default/xui/it/panel_login.xml | 2 +- indra/newview/skins/default/xui/it/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/ja/panel_login.xml | 2 +- indra/newview/skins/default/xui/ja/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/ja/strings.xml | 2 +- indra/newview/skins/default/xui/pl/notifications.xml | 2 +- indra/newview/skins/default/xui/pt/notifications.xml | 2 +- indra/newview/skins/default/xui/pt/panel_login.xml | 2 +- indra/newview/skins/default/xui/pt/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/pt/strings.xml | 2 +- indra/newview/skins/default/xui/ru/strings.xml | 2 +- indra/newview/skins/default/xui/tr/panel_login.xml | 2 +- indra/newview/skins/default/xui/tr/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/tr/strings.xml | 2 +- indra/newview/skins/default/xui/zh/panel_login.xml | 2 +- indra/newview/skins/default/xui/zh/panel_login_first.xml | 2 +- indra/newview/skins/default/xui/zh/strings.xml | 2 +- 31 files changed, 34 insertions(+), 31 deletions(-) diff --git a/indra/newview/fspanellogin.cpp b/indra/newview/fspanellogin.cpp index 2f5d1449ec..c8ddc70d8a 100644 --- a/indra/newview/fspanellogin.cpp +++ b/indra/newview/fspanellogin.cpp @@ -996,7 +996,10 @@ void FSPanelLogin::onClickNewAccount(void*) LLWeb::loadURLInternal(grid_info[GRID_REGISTER_NEW_ACCOUNT]); else #endif // OPENSIM - LLWeb::loadURLExternal(LLTrans::getString("create_account_url")); + // Load Firestorm's registration page from within the viewer itself + // LLWeb::loadURLExternal(LLTrans::getString("create_account_url")); + LLWeb::loadURLInternal(LLTrans::getString("create_account_url")); + // } } diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 3be82ce9be..14e09d4773 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -408,7 +408,7 @@ CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \ WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Account.url" \ "InternetShortcut" "URL" \ - "https://join.secondlife.com/" + "https://www.firestormviewer.org/join-secondlife/" WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Your Account.url" \ "InternetShortcut" "URL" \ "https://www.secondlife.com/account/" diff --git a/indra/newview/skins/default/xui/de/panel_login.xml b/indra/newview/skins/default/xui/de/panel_login.xml index 287f55a127..4467f1adb2 100644 --- a/indra/newview/skins/default/xui/de/panel_login.xml +++ b/indra/newview/skins/default/xui/de/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=de - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/de/panel_login_first.xml b/indra/newview/skins/default/xui/de/panel_login_first.xml index 038001157e..034c2d60cc 100644 --- a/indra/newview/skins/default/xui/de/panel_login_first.xml +++ b/indra/newview/skins/default/xui/de/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=de - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 2f9df6fae8..c0f38e459e 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -216,7 +216,7 @@ Voice-Serverversion: [VOICE_VERSION] Beenden - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Second Life Main Grid (Agni) diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index ade004f9d0..28e0649f99 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -14,7 +14,7 @@ - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ Network error: Could not establish connection, please check your network connection. Login failed. Quit - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Second Life Main Grid (Agni) Second Life Beta Test Grid (Aditi) diff --git a/indra/newview/skins/default/xui/es/panel_login.xml b/indra/newview/skins/default/xui/es/panel_login.xml index f79d3b9422..5b78c7f9ac 100644 --- a/indra/newview/skins/default/xui/es/panel_login.xml +++ b/indra/newview/skins/default/xui/es/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=es - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/es/panel_login_first.xml b/indra/newview/skins/default/xui/es/panel_login_first.xml index ccb6858351..71de7e6a97 100644 --- a/indra/newview/skins/default/xui/es/panel_login_first.xml +++ b/indra/newview/skins/default/xui/es/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=es - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 489ced7b00..960bb5fe48 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -185,7 +185,7 @@ Versión del servidor de voz: [VOICE_VERSION] Salir - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Ya no puedes acceder a Second Life con el visor que estás utilizando. Visita la siguiente página para descargar un nuevo visor: diff --git a/indra/newview/skins/default/xui/fr/panel_login.xml b/indra/newview/skins/default/xui/fr/panel_login.xml index e168aeb94a..08c5ea67f0 100644 --- a/indra/newview/skins/default/xui/fr/panel_login.xml +++ b/indra/newview/skins/default/xui/fr/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=fr - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/fr/panel_login_first.xml b/indra/newview/skins/default/xui/fr/panel_login_first.xml index 8f40d0230c..7db6ac7bf6 100644 --- a/indra/newview/skins/default/xui/fr/panel_login_first.xml +++ b/indra/newview/skins/default/xui/fr/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=fr - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index ba3cce86f6..7dedaef05e 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -173,7 +173,7 @@ Version serveur vocal : [VOICE_VERSION] Quitter - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Grille principale de Second Life (Agni) diff --git a/indra/newview/skins/default/xui/it/panel_login.xml b/indra/newview/skins/default/xui/it/panel_login.xml index 7a5ed68fac..47c20e1950 100644 --- a/indra/newview/skins/default/xui/it/panel_login.xml +++ b/indra/newview/skins/default/xui/it/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=it - http://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/it/panel_login_first.xml b/indra/newview/skins/default/xui/it/panel_login_first.xml index 5b04fd411a..cb3d7c656e 100644 --- a/indra/newview/skins/default/xui/it/panel_login_first.xml +++ b/indra/newview/skins/default/xui/it/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=it - http://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/ja/panel_login.xml b/indra/newview/skins/default/xui/ja/panel_login.xml index 86e7e9dac4..07b12881d9 100644 --- a/indra/newview/skins/default/xui/ja/panel_login.xml +++ b/indra/newview/skins/default/xui/ja/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=ja - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/ja/panel_login_first.xml b/indra/newview/skins/default/xui/ja/panel_login_first.xml index 644cee25f2..a910a7e7bf 100644 --- a/indra/newview/skins/default/xui/ja/panel_login_first.xml +++ b/indra/newview/skins/default/xui/ja/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=ja - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 95ac48f0ac..7ffc21e60d 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -134,7 +134,7 @@ 終了 - http://join.secondlife.com/index.php?lang=ja-JP&sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ https://www.firestormviewer.org/choose-your-platform/ diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml index d73789e4f0..8ea6d93b2e 100644 --- a/indra/newview/skins/default/xui/pl/notifications.xml +++ b/indra/newview/skins/default/xui/pl/notifications.xml @@ -1387,7 +1387,7 @@ Możesz normalnie używać [APP_NAME], inni użytkownicy będą Cię widzieli po Instalacja [APP_NAME] zakończona. Jeżeli używasz [CURRENT_GRID] po raz pierwszy to musisz stworzyć konto żeby móc się zalogować. -Czy chcesz przejść na stronę [http://join.secondlife.com secondlife.com] żeby stworzyć nowe konto? +Czy chcesz przejść na stronę [https://www.firestormviewer.org/join-secondlife/ firestormviewer.org] żeby stworzyć nowe konto? diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index 56f61724fc..2526577265 100644 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -1383,7 +1383,7 @@ Enquando isso, use o [CURRENT_GRID] normalmente. Seu visual será exibido corret A instalação do [APP_NAME] está pronta. Se você ainda não conhece o [CURRENT_GRID], basta criar uma conta para começar. -Voltar para [http://join.secondlife.com secondlife.com] para criar sua conta? +Voltar para [https://www.firestormviewer.org/join-secondlife/ firestormviewer.org] para criar sua conta? diff --git a/indra/newview/skins/default/xui/pt/panel_login.xml b/indra/newview/skins/default/xui/pt/panel_login.xml index ffe637b3c1..b1e8bae513 100644 --- a/indra/newview/skins/default/xui/pt/panel_login.xml +++ b/indra/newview/skins/default/xui/pt/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=pt - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/pt/panel_login_first.xml b/indra/newview/skins/default/xui/pt/panel_login_first.xml index 86c61163bc..8b2ef04f95 100644 --- a/indra/newview/skins/default/xui/pt/panel_login_first.xml +++ b/indra/newview/skins/default/xui/pt/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=pt - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 10ae9fad15..758c723712 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -176,7 +176,7 @@ Versão do servidor de voz: [VOICE_VERSION] Sair - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ http://secondlife.com/download diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index b66ec18c51..afadbd7788 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -225,7 +225,7 @@ SLURL: <nolink>[SLURL]</nolink> Выйти - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Second Life Главная Сетка (Agni) diff --git a/indra/newview/skins/default/xui/tr/panel_login.xml b/indra/newview/skins/default/xui/tr/panel_login.xml index e9fec9783f..d21bf3ba38 100644 --- a/indra/newview/skins/default/xui/tr/panel_login.xml +++ b/indra/newview/skins/default/xui/tr/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/tr/panel_login_first.xml b/indra/newview/skins/default/xui/tr/panel_login_first.xml index 1fc80c2b97..46dc8e8414 100644 --- a/indra/newview/skins/default/xui/tr/panel_login_first.xml +++ b/indra/newview/skins/default/xui/tr/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 571e7335ac..d50dd1893b 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -188,7 +188,7 @@ Ses Sunucusu Sürümü: [VOICE_VERSION] Çık - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ Second Life Ana Ağı (Agni) diff --git a/indra/newview/skins/default/xui/zh/panel_login.xml b/indra/newview/skins/default/xui/zh/panel_login.xml index 59618972af..70c06b14bc 100644 --- a/indra/newview/skins/default/xui/zh/panel_login.xml +++ b/indra/newview/skins/default/xui/zh/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/zh/panel_login_first.xml b/indra/newview/skins/default/xui/zh/panel_login_first.xml index 4d72fcdd03..1f7990e506 100644 --- a/indra/newview/skins/default/xui/zh/panel_login_first.xml +++ b/indra/newview/skins/default/xui/zh/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php - http://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index 8532adb490..a78afb2d2a 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -187,7 +187,7 @@ J2C 解碼器版本: [J2C_VERSION] 結束退出 - http://join.secondlife.com/?sourceid=[sourceid] + https://www.firestormviewer.org/join-secondlife/ http://secondlife.com/download From 242a3121597da0954c2a336af33e9c1388b3aa44 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 31 Mar 2021 23:30:07 +0200 Subject: [PATCH 115/121] Update tracy --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 4a30eaeb74..36944d5bf0 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -275,17 +275,17 @@ Tracy platforms - windows + common archive hash - 8e4cc0d82acbf83f10fb866302fd7519 + b0a0fc6b7bda02fe14cccea2dd342058 url - http://3p.firestormviewer.org/Tracy-v0.7.6-windows-210761143.tar.bz2 + http://3p.firestormviewer.org/Tracy-0.7.6-windows-210902110.tar.bz2 name - windows + common version From 785d9c03e6ba3a89e17581630fd0c9eec4e6e216 Mon Sep 17 00:00:00 2001 From: Nicky Date: Thu, 1 Apr 2021 00:02:23 +0200 Subject: [PATCH 116/121] Need to compile against Win7 to use tracy. --- indra/cmake/Tracy.cmake | 2 +- indra/llcommon/fstracyclient.cpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/indra/cmake/Tracy.cmake b/indra/cmake/Tracy.cmake index db1e609fb8..e471f0109e 100644 --- a/indra/cmake/Tracy.cmake +++ b/indra/cmake/Tracy.cmake @@ -5,7 +5,7 @@ if (USE_TRACY_PROFILER) if (WINDOWS) # set(TRACY_LIBRARIES # ${ARCH_PREBUILT_DIRS_RELEASE}/Tracy.lib - add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS) + add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS -DWINVER=0x0601 ) set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy) elseif (DARWIN) message(FATAL_ERROR "Tracy is not yet implemented in FS for OSX.") diff --git a/indra/llcommon/fstracyclient.cpp b/indra/llcommon/fstracyclient.cpp index 55c05f0cc8..f21c8fd197 100644 --- a/indra/llcommon/fstracyclient.cpp +++ b/indra/llcommon/fstracyclient.cpp @@ -23,16 +23,7 @@ #include "client/TracyCallstack.cpp" #include "client/TracySysTime.cpp" -// Black magic on Windows to deal with windows entry points that are not found in a standard LL windows build -// This is less invasive than changing the 3p library itself. -// TODO: work out why the windows trace subsystem stuff is not defined in our builds, this then goes away (hopefully) -#if defined(LL_WINDOWS) && !defined(__CYGWIN__) -#define __CYGWIN__ #include "client/TracySysTrace.cpp" -#undef __CYGWIN__ -#else -#include "client/TracySysTrace.cpp" -#endif #include "common/TracySocket.cpp" #include "client/tracy_rpmalloc.cpp" From d891eb44ef3b88ff34cd9e3249f0688d414d022d Mon Sep 17 00:00:00 2001 From: Nicky Date: Thu, 1 Apr 2021 01:24:28 +0200 Subject: [PATCH 117/121] Tracy for Linux. --- indra/cmake/Tracy.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/cmake/Tracy.cmake b/indra/cmake/Tracy.cmake index e471f0109e..b3bfb6fc65 100644 --- a/indra/cmake/Tracy.cmake +++ b/indra/cmake/Tracy.cmake @@ -10,6 +10,7 @@ if (USE_TRACY_PROFILER) elseif (DARWIN) message(FATAL_ERROR "Tracy is not yet implemented in FS for OSX.") else (WINDOWS) - message(FATAL_ERROR "Tracy is not yet implemented in FS for Linux.") + add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS ) + set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy) endif (WINDOWS) endif (USE_TRACY_PROFILER) From b049a91f295e03e18ba0cb83d5ba7b049a927a63 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 1 Apr 2021 19:03:38 +0200 Subject: [PATCH 118/121] FIRE-30862: Improve icon visibility in location input control for Firestorm Dark skin --- .../newview/skins/firestorm/themes/dark/textures/textures.xml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 indra/newview/skins/firestorm/themes/dark/textures/textures.xml diff --git a/indra/newview/skins/firestorm/themes/dark/textures/textures.xml b/indra/newview/skins/firestorm/themes/dark/textures/textures.xml new file mode 100644 index 0000000000..a01e155692 --- /dev/null +++ b/indra/newview/skins/firestorm/themes/dark/textures/textures.xml @@ -0,0 +1,4 @@ + + + + From 167e45e309ebeaccb346b8ca05884b8e10bf05eb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Apr 2021 13:37:32 -0400 Subject: [PATCH 119/121] Increment viewer version to 6.4.18 following promotion of DRTVWR-514 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index dbe6222fdd..a0b6fce83f 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.4.17 +6.4.18 From 69e4e6036d478aed9ae19733786429b2adeeacb0 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 1 Apr 2021 21:03:21 +0200 Subject: [PATCH 120/121] Let's output these messages as status messages... --- indra/CMakeLists.txt | 18 +++++++++--------- indra/cmake/BuildVersion.cmake | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index b49e5b1668..bab6ad5770 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -31,20 +31,20 @@ if (OPENSIM) add_definitions(-DOPENSIM=1) if (SINGLEGRID) add_definitions(-DSINGLEGRID=1 -DSINGLEGRID_URI=\"${SINGLEGRID_URI}\") - message("compiling with OpenSim support - Single Grid version (${SINGLEGRID_URI})") + message(STATUS "Compiling with OpenSim support - Single Grid version (${SINGLEGRID_URI})") else (SINGLEGRID) - message("compiling with OpenSim support") + message(STATUS "Compiling with OpenSim support") endif (SINGLEGRID) else (OPENSIM) if (SINGLEGRID) - message( WARNING "Value for SINGLEGRID is unused during Havok/SecondLife builds" ) + message(WARNING "Value for SINGLEGRID is unused during Havok/SecondLife builds" ) endif() - message("compiling without OpenSim support") + message(STATUS "Compiling without OpenSim support") endif (OPENSIM) if (HAVOK_TPV) add_definitions(-DHAVOK_TPV=1) - message("compiling with Havok libraries") + message(STATUS "Compiling with Havok libraries") endif (HAVOK_TPV) # @@ -52,7 +52,7 @@ endif (HAVOK_TPV) option(TESTBUILD "Generating test build" OFF) if(TESTBUILD AND TESTBUILDPERIOD) add_definitions(-DTESTBUILD=1 -DTESTBUILDPERIOD=${TESTBUILDPERIOD}) - message("creating test build version; test period: ${TESTBUILDPERIOD} days") + message(STATUS "Creating test build version; test period: ${TESTBUILDPERIOD} days") endif(TESTBUILD AND TESTBUILDPERIOD) # @@ -64,13 +64,13 @@ if (USE_AVX_OPTIMIZATION) message(FATAL_ERROR "You cannot use AVX and AVX2 at the same time!") else (USE_AVX2_OPTIMIZATION) add_definitions(-DUSE_AVX_OPTIMIZATION=1) - message("compiling with AVX optimizations") + message(STATUS "Compiling with AVX optimizations") endif (USE_AVX2_OPTIMIZATION) elseif (USE_AVX2_OPTIMIZATION) add_definitions(-DUSE_AVX2_OPTIMIZATION=1) - message("compiling with AVX2 optimizations") + message(STATUS "Compiling with AVX2 optimizations") else (USE_AVX_OPTIMIZATION) - message("compiling without AVX optimizations") + message(STATUS "Compiling without AVX optimizations") endif (USE_AVX_OPTIMIZATION) # [AVX Optimization] diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 86e42fa0ba..3406e5fd15 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -12,7 +12,7 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n if (DEFINED ENV{revision}) set(VIEWER_VERSION_REVISION $ENV{revision}) - message("Revision (from environment): ${VIEWER_VERSION_REVISION}") + message(STATUS "Revision (from environment): ${VIEWER_VERSION_REVISION}") elseif (DEFINED ENV{AUTOBUILD_BUILD_ID}) set(VIEWER_VERSION_REVISION $ENV{AUTOBUILD_BUILD_ID}) @@ -38,23 +38,23 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n OUTPUT_STRIP_TRAILING_WHITESPACE ) if ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - message("Revision (from hg) ${VIEWER_VERSION_REVISION}") + message(STATUS "Revision (from hg) ${VIEWER_VERSION_REVISION}") else ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - message("Revision not set (repository not found?); using 0") + message(STATUS "Revision not set (repository not found?); using 0") set(VIEWER_VERSION_REVISION 0 ) endif ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") else (DEFINED MERCURIAL AND DEFINED SED) - message("Revision not set: 'hg' or 'sed' not found; using 0") + message(STATUS "Revision not set: 'hg' or 'sed' not found; using 0") set(VIEWER_VERSION_REVISION 0) endif (DEFINED MERCURIAL AND DEFINED SED) endif (DEFINED ENV{revision}) - message("Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") + message(STATUS "Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") else ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) message(SEND_ERROR "Cannot get viewer version from '${VIEWER_VERSION_BASE_FILE}'") endif ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) if ("${VIEWER_VERSION_REVISION}" STREQUAL "") - message("Ultimate fallback, revision was blank or not set: will use 0") + message(STATUS "Ultimate fallback, revision was blank or not set: will use 0") set(VIEWER_VERSION_REVISION 0) endif ("${VIEWER_VERSION_REVISION}" STREQUAL "") From 628e9b914b982b9f482530b4ff29bd89a5efd9ef Mon Sep 17 00:00:00 2001 From: Zi Ree Date: Fri, 2 Apr 2021 16:48:40 +0200 Subject: [PATCH 121/121] Make the XUI preview tool sort by file name automatically and make that column a bit wider --- indra/newview/skins/default/xui/en/floater_ui_preview.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/indra/newview/skins/default/xui/en/floater_ui_preview.xml b/indra/newview/skins/default/xui/en/floater_ui_preview.xml index 04a51c07d0..77351b3a43 100644 --- a/indra/newview/skins/default/xui/en/floater_ui_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_ui_preview.xml @@ -208,6 +208,7 @@ or specifying its path in the "Editor Path" field. right="-10" tab_group="1" search_column="1" + sort_column="1" top="80"> + dynamic_width="true" /> + name="top_level_node_column" + width="250" />