diff options
Diffstat (limited to 'indra/newview')
198 files changed, 5621 insertions, 1313 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6c8d7b910e..4d79138aaf 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,6 +30,7 @@ include(LLPrimitive) include(LLWindow) include(SDL2) include(NDOF) +include(NFDE) include(NVAPI) include(OPENAL) include(OpenGL) @@ -734,6 +735,11 @@ set(viewer_SOURCE_FILES llxmlrpctransaction.cpp noise.cpp pipeline.cpp + rlvactions.cpp + rlvcommon.cpp + rlvfloaters.cpp + rlvhelper.cpp + rlvhandler.cpp ) set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING @@ -1399,6 +1405,12 @@ set(viewer_HEADER_FILES llxmlrpctransaction.h noise.h pipeline.h + rlvdefines.h + rlvactions.h + rlvcommon.h + rlvfloaters.h + rlvhelper.h + rlvhandler.h roles_constants.h VertexCache.h VorbisFramework.h @@ -1933,6 +1945,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ll::ndof ll::tracy ll::openxr + ll::nfde ) if( TARGET ll::intel_memops ) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 36abebc942..049ec3ad34 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2469,6 +2469,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>UseGroupMemberPagination</key> + <map> + <key>Comment</key> + <string>Enable pagination of group memeber list 50 members at a time.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DisplayTimecode</key> <map> <key>Comment</key> @@ -4122,6 +4133,17 @@ <key>Value</key> <array /> </map> + <key>LuaDebugShowSource</key> + <map> + <key>Comment</key> + <string>Show source info in Lua Debug Console output</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>GridStatusRSS</key> <map> <key>Comment</key> @@ -4630,6 +4652,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>MapShowGridCoords</key> + <map> + <key>Comment</key> + <string>Shows/hides the grid coordinates of each region on the world map.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>MiniMapAutoCenter</key> <map> <key>Comment</key> @@ -8020,6 +8053,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderDebugTextureLabel</key> + <map> + <key>Comment</key> + <string>Enable texture labels via glObjectLabel.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderDebugTextureLabelLocalFilesSession</key> + <map> + <key>Comment</key> + <string>Enumerate textures with local file names. Doesn't do anything until RenderDebugTextureLabel is set to true. Requires restart AND applies on the next session only.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderDelayCreation</key> <map> <key>Comment</key> @@ -9234,7 +9289,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1</real> + <real>1.0</real> </map> <key>RenderReflectionProbeDrawDistance</key> @@ -10045,7 +10100,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1.0</real> + <real>0.7</real> </map> <key>RenderTonemapType</key> <map> @@ -10056,7 +10111,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>ReplaySession</key> <map> @@ -10080,6 +10135,83 @@ <key>Value</key> <string>https://feedback.secondlife.com/</string> </map> + <key>RestrainedLove</key> + <map> + <key>Comment</key> + <string>Toggles RLVa features (requires restart)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RestrainedLoveDebug</key> + <map> + <key>Comment</key> + <string>Toggles RLVa debug mode (displays the commands when in debug mode)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RLVaBlockedExperiences</key> + <map> + <key>Comment</key> + <string>List of experiences blocked from interacting with RLVa</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>bfe25fb4-222c-11e5-85a2-fa4c4ccaa202</string> + </map> + <key>RLVaDebugHideUnsetDuplicate</key> + <map> + <key>Comment</key> + <string>Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RLVaEnableTemporaryAttachments</key> + <map> + <key>Comment</key> + <string>Allows temporary attachments (regardless of origin) to issue RLV commands</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> + <key>RLVaExperimentalCommands</key> + <map> + <key>Comment</key> + <string>Enables the experimental command set</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> + <key>RLVaTopLevelMenu</key> + <map> + <key>Comment</key> + <string>Show the RLVa specific menu as a top level menu</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> <key>RevokePermsOnStopAnimation</key> <map> <key>Comment</key> @@ -11639,6 +11771,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>TextureDiscardBackgroundedTime</key> + <map> + <key>Comment</key> + <string>Specify how long to wait before discarding texture data after viewer is backgrounded. (zero or negative to disable)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>60.0</real> + </map> + <key>TextureDiscardMinimizedTime</key> + <map> + <key>Comment</key> + <string>Specify how long to wait before discarding texture data after viewer is minimized. (zero or negative to disable)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> <key>TextureFetchConcurrency</key> <map> <key>Comment</key> @@ -12967,9 +13121,9 @@ <key>Use24HourClock</key> <map> <key>Comment</key> - <string>12 vs 24. At the moment only for region restart schedule floater</string> + <string>12 vs 24. At the moment coverage is partial</string> <key>Persist</key> - <integer>0</integer> + <integer>1</integer> <key>Type</key> <string>Boolean</string> <key>Value</key> @@ -13624,7 +13778,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>WarningsAsChat</key> <map> @@ -15268,7 +15422,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>5</integer> + <integer>8</integer> </map> <key>TerrainPaintResolution</key> <map> diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl index cf20653a0f..a79a56d725 100644 --- a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl @@ -1,5 +1,5 @@ /** - * @file terrainBakeF.glsl + * @file pbrTerrainBakeF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl new file mode 100644 index 0000000000..e79e9010e6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl @@ -0,0 +1,44 @@ +/** + * @file terrainStampF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +// Paint texture to stamp into the "paintmap" +uniform sampler2D diffuseMap; + +in vec2 vary_texcoord0; + +void main() +{ + vec4 col = texture(diffuseMap, vary_texcoord0); + if (col.a <= 0.0f) + { + discard; + } + + frag_color = max(col, vec4(0)); +} diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl new file mode 100644 index 0000000000..294fa6be26 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl @@ -0,0 +1,39 @@ +/** + * @file terrainStampV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +uniform mat4 modelview_projection_matrix; +uniform vec2 terrain_stamp_scale; + +in vec3 position; + +out vec2 vary_texcoord0; + +void main() +{ + gl_Position = modelview_projection_matrix * vec4(position, 1.0); + // Positions without transforms are treated as UVs for the purpose of this shader. + vary_texcoord0.xy = terrain_stamp_scale * position.xy; +} + diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 24fd7928a6..6390e43b7a 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 63 +version 64 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -77,11 +77,13 @@ RenderScreenSpaceReflections 1 1 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderDownScaleMethod 1 1 RenderCASSharpness 1 1 - +RenderExposure 1 4 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -119,6 +121,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -156,6 +161,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -193,6 +201,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -230,6 +241,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -267,6 +281,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -304,6 +321,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -341,6 +361,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index dc5d1ff6c4..9465acae6b 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 29 +version 30 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -81,7 +81,9 @@ RenderHeroProbeUpdateRate 1 4 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderDownScaleMethod 1 1 RenderCASSharpness 1 1 - +RenderExposure 1 4 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -119,6 +121,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -156,6 +161,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -193,6 +201,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -230,6 +241,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -267,6 +281,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -304,6 +321,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -341,6 +361,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 06ad730a40..9ba922c3ce 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 61 +version 62 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -77,9 +77,12 @@ RenderReflectionProbeLevel 1 3 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 1 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -117,6 +120,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -154,6 +160,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -191,6 +200,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -228,6 +240,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -265,6 +280,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (SSAO + all shadows) @@ -302,6 +320,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -339,6 +360,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ed66753267..f596ce04f8 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -500,7 +500,7 @@ void GLTFSceneManager::update() LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) { LLAppViewer::instance()->postToMainCoro( - [=]() + [=, this]() { if (mUploadingAsset) { diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ca35608175..3d4f5e1054 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -355,20 +355,6 @@ bool LLAgent::isMicrophoneOn(const LLSD& sdname) return LLVoiceClient::getInstance()->getUserPTTState(); } -//static -void LLAgent::toggleHearMediaSoundFromAvatar() -{ - const S32 mediaSoundsEarLocation = gSavedSettings.getS32("MediaSoundsEarLocation"); - gSavedSettings.setS32("MediaSoundsEarLocation", !mediaSoundsEarLocation); -} - -//static -void LLAgent::toggleHearVoiceFromAvatar() -{ - const S32 voiceEarLocation = gSavedSettings.getS32("VoiceEarLocation"); - gSavedSettings.setS32("VoiceEarLocation", !voiceEarLocation); -} - // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 448ee575f5..489131d974 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -377,13 +377,6 @@ private: bool mVoiceConnected; //-------------------------------------------------------------------- - // Sound - //-------------------------------------------------------------------- -public: - static void toggleHearMediaSoundFromAvatar(); - static void toggleHearVoiceFromAvatar(); - - //-------------------------------------------------------------------- // Chat //-------------------------------------------------------------------- public: diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 14e443ec4e..3d07ead95d 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -32,11 +32,14 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavatarname.h" +#include "llavatarnamecache.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llinventorymodel.h" #include "llslurl.h" #include "llurldispatcher.h" +#include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -46,7 +49,8 @@ #include "llsdutil_math.h" #include "lltoolgrab.h" #include "llhudeffectlookat.h" -#include "llagentcamera.h" +#include "llviewercamera.h" +#include "resultset.h" #include <functional> static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f; @@ -176,6 +180,32 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "Return information about [\"item_id\"] animation", &LLAgentListener::getAnimationInfo, llsd::map("item_id", LLSD(), "reply", LLSD())); + + add("getID", + "Return your own avatar ID", + &LLAgentListener::getID, + llsd::map("reply", LLSD())); + + add("getNearbyAvatarsList", + "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\", \"region_id\" fields", + &LLAgentListener::getNearbyAvatarsList, + llsd::map("reply", LLSD())); + + add("getNearbyObjectsList", + "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\", \"region_id\" fields", + &LLAgentListener::getNearbyObjectsList, + llsd::map("reply", LLSD())); + + add("getAgentScreenPos", + "Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n" + "reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n" + "avatar render position is used as the point", + &LLAgentListener::getAgentScreenPos, + llsd::map("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -693,3 +723,118 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data) "priority", motion->getPriority()); } } + +void LLAgentListener::getID(LLSD const& event_data) +{ + Response response(llsd::map("id", gAgentID), event_data); +} + +struct AvResultSet : public LL::VectorResultSet<LLVOAvatar*> +{ + AvResultSet() : super("nearby_avatars") {} + LLSD getSingleFrom(LLVOAvatar* const& av) const override + { + LLAvatarName av_name; + LLAvatarNameCache::get(av->getID(), &av_name); + LLVector3 region_pos = av->getCharacterPosition(); + return llsd::map("id", av->getID(), + "global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)), + "region_pos", ll_sd_from_vector3(region_pos), + "name", av_name.getUserName(), + "region_id", av->getRegion()->getRegionID()); + } +}; + +struct ObjResultSet : public LL::VectorResultSet<LLViewerObject*> +{ + ObjResultSet() : super("nearby_objects") {} + LLSD getSingleFrom(LLViewerObject* const& obj) const override + { + return llsd::map("id", obj->getID(), + "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), + "region_pos", ll_sd_from_vector3(obj->getPositionRegion()), + "region_id", obj->getRegion()->getRegionID()); + } +}; + + +F32 get_search_radius(LLSD const& event_data) +{ + static LLCachedControl<F32> render_far_clip(gSavedSettings, "RenderFarClip", 64); + F32 dist = render_far_clip; + if (event_data.has("dist")) + { + dist = llclamp((F32)event_data["dist"].asReal(), 1, 512); + } + return dist * dist; +} + +void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto avresult = new AvResultSet; + + F32 radius = get_search_radius(event_data); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf()) + { + if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius)) + { + avresult->mVector.push_back(avatar); + } + } + } + response["result"] = avresult->getKeyLength(); +} + +void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto objresult = new ObjResultSet; + + F32 radius = get_search_radius(event_data); + S32 num_objects = gObjectList.getNumObjects(); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (S32 i = 0; i < num_objects; ++i) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && object->getVolume() && !object->isAttachment()) + { + if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius)) + { + objresult->mVector.push_back(object); + } + } + } + response["result"] = objresult->getKeyLength(); +} + +void LLAgentListener::getAgentScreenPos(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + LLVector3 render_pos; + if (event_data.has("avatar_id") && (event_data["avatar_id"].asUUID() != gAgentID)) + { + LLUUID avatar_id(event_data["avatar_id"]); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && (avatar->getID() == avatar_id)) + { + render_pos = avatar->getRenderPosition(); + break; + } + } + } + else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid()) + { + render_pos = gAgentAvatarp->getRenderPosition(); + } + LLCoordGL screen_pos; + response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false); + response["x"] = screen_pos.mX; + response["y"] = screen_pos.mY; +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 05724ff443..8801c9f7ea 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -67,6 +67,11 @@ private: void stopAnimation(LLSD const &event_data); void getAnimationInfo(LLSD const &event_data); + void getID(LLSD const& event_data); + void getNearbyAvatarsList(LLSD const& event_data); + void getNearbyObjectsList(LLSD const& event_data); + void getAgentScreenPos(LLSD const& event_data); + LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const; private: diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 75cd189980..423fe2469a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -255,6 +255,10 @@ using namespace LL; #include "llcoproceduremanager.h" #include "llviewereventrecorder.h" +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -269,10 +273,6 @@ using namespace LL; // define a self-registering event API object #include "llappviewerlistener.h" -#if LL_LINUX && LL_GTK -#include "glib.h" -#endif // (LL_LINUX) && LL_GTK - static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor @@ -1818,6 +1818,9 @@ bool LLAppViewer::cleanup() //ditch LLVOAvatarSelf instance gAgentAvatarp = NULL; + // Sanity check to catch cases where someone forgot to do an RlvActions::isRlvEnabled() check + LL_ERRS_IF(!RlvHandler::isEnabled() && RlvHandler::instanceExists()) << "RLV handler instance exists even though RLVa is disabled" << LL_ENDL; + LLNotifications::instance().clear(); // workaround for DEV-35406 crash on shutdown @@ -2912,6 +2915,14 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setBOOL("RenderDebugGLSession", false); } + if (gSavedSettings.getBOOL("RenderDebugTextureLabelLocalFilesSession")) + { + gDebugTextureLabelLocalFilesSession = true; + // Texture label accounting for local files takes up memory in the + // background, so don't persist. + gSavedSettings.setBOOL("RenderDebugTextureLabelLocalFilesSession", false); + } + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); if (skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) { @@ -3448,6 +3459,7 @@ LLSD LLAppViewer::getViewerInfo() const } #endif + info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)"; info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); // Settings diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 7ce74649e2..3dbbda11f9 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -39,8 +39,9 @@ #include "llfindlocale.h" #include <exception> - +#ifdef LL_GLIB #include <gio/gio.h> +#endif #include <resolv.h> #if (__GLIBC__*1000 + __GLIBC_MINOR__) >= 2034 diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 6f6b89ea81..aff959d279 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -285,25 +285,18 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate make_ui_sound("UISndStartIM"); } -/* AD *TODO: Is this function needed any more? - I fixed it a bit(added check for canCall), but it appears that it is not used - anywhere. Maybe it should be removed? // static -bool LLAvatarActions::isCalling(const LLUUID &id) +bool LLAvatarActions::canCall() { - if (id.isNull() || !canCall()) - { - return false; - } - - LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); - return (LLIMModel::getInstance()->findIMSession(session_id) != NULL); -}*/ + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + return voice_client->voiceEnabled() && voice_client->isVoiceWorking(); +} -//static -bool LLAvatarActions::canCall() +// static +bool LLAvatarActions::canCallTo(const LLUUID& id) { - return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + return voice_client->voiceEnabled() && voice_client->isVoiceWorking() && voice_client->getVoiceEnabled(id); } // static diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 1f5a42ed22..6f469e96ce 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -172,18 +172,15 @@ public: static bool canBlock(const LLUUID& id); /** - * Return true if the avatar is in a P2P voice call with a given user + * @return true if voice calls are available */ - /* AD *TODO: Is this function needed any more? - I fixed it a bit(added check for canCall), but it appears that it is not used - anywhere. Maybe it should be removed? - static bool isCalling(const LLUUID &id);*/ + static bool canCall(); /** * @return true if call to the resident can be made */ + static bool canCallTo(const LLUUID& id); - static bool canCall(); /** * Invite avatar to a group. */ diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 3e02820feb..bb5ee558ca 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -426,6 +426,7 @@ public: time_t current_time = time_corrected(); time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime); + // Report abuse shouldn't use AM/PM, use 24-hour time time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" + LLTrans::getString("TimeYear") + "] [" diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index ed563cbec9..ed16a4b135 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time) LLSD substitution; substitution["datetime"] = (S32)utc_time.value(); - timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" - +LLTrans::getString ("TimeDay")+"]/[" - +LLTrans::getString ("TimeYear")+"] [" - +LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] ["; + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp index c347d3e1be..d5f1e25151 100644 --- a/indra/newview/llcylinder.cpp +++ b/indra/newview/llcylinder.cpp @@ -29,7 +29,7 @@ #include "llcylinder.h" #include "llerror.h" -#include "math.h" +#include <cmath> #include "llmath.h" #include "noise.h" #include "v3math.h" diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 51157fa430..032433434d 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -41,15 +41,17 @@ # include "llfilepicker.h" #endif -#ifdef LL_FLTK - #include "FL/Fl.H" - #include "FL/Fl_Native_File_Chooser.H" -#endif - #if LL_WINDOWS #include <shlobj.h> #endif +#if LL_NFD +#include "nfd.hpp" +#if LL_USE_SDL_WINDOW +#include "nfd_sdl2.h" +#endif +#endif + // // Implementation // @@ -70,7 +72,95 @@ bool LLDirPicker::check_local_file_access_enabled() return true; } -#if LL_WINDOWS +#if LL_NFD + +LLDirPicker::LLDirPicker() : + mFileName(nullptr), + mLocked(false) +{ + reset(); +} + +LLDirPicker::~LLDirPicker() +{ +} + + +void LLDirPicker::reset() +{ + mDir.clear(); +} + +bool LLDirPicker::getDir(std::string* filename, bool blocking) +{ + if( mLocked ) + { + return false; + } + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + bool success = false; + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + // initialize NFD + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + // show the dialog + nfdresult_t result = NFD::PickFolder(outPath, nullptr, windowHandle); + if (result == NFD_OKAY) + { + mDir = std::string(outPath.get()); + success = true; + } + else if (result == NFD_CANCEL) + { + LL_INFOS() << "User pressed cancel." << LL_ENDL; + } + else + { + LL_INFOS() << "DirPicker Error: " << NFD::GetError() << LL_ENDL; + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +std::string LLDirPicker::getDirName() +{ + return mDir; +} + +#elif LL_WINDOWS LLDirPicker::LLDirPicker() : mFileName(NULL), @@ -219,28 +309,20 @@ LLDirPicker::LLDirPicker() : mFileName(NULL), mLocked(false) { -#ifndef LL_FLTK mFilePicker = new LLFilePicker(); -#endif reset(); } LLDirPicker::~LLDirPicker() { -#ifndef LL_FLTK delete mFilePicker; -#endif } void LLDirPicker::reset() { -#ifndef LL_FLTK if (mFilePicker) mFilePicker->reset(); -#else - mDir = ""; -#endif } bool LLDirPicker::getDir(std::string* filename, bool blocking) @@ -253,39 +335,16 @@ bool LLDirPicker::getDir(std::string* filename, bool blocking) return false; } -#ifdef LL_FLTK - gViewerWindow->getWindow()->beforeDialog(); - Fl_Native_File_Chooser flDlg; - flDlg.title(LLTrans::getString("choose_the_directory").c_str()); - flDlg.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY ); - int res = flDlg.show(); - gViewerWindow->getWindow()->afterDialog(); - if( res == 0 ) - { - char const *pDir = flDlg.filename(0); - if( pDir ) - mDir = pDir; - } - else if( res == -1 ) - { - LL_WARNS() << "FLTK failed: " << flDlg.errmsg() << LL_ENDL; - } - return !mDir.empty(); -#endif return false; } std::string LLDirPicker::getDirName() { -#ifndef LL_FLTK if (mFilePicker) { return mFilePicker->getFirstFile(); } return ""; -#else - return mDir; -#endif } #else // not implemented @@ -322,7 +381,7 @@ std::queue<LLDirPickerThread*> LLDirPickerThread::sDeadQ; void LLDirPickerThread::getFile() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) start(); #else run(); @@ -332,7 +391,7 @@ void LLDirPickerThread::getFile() //virtual void LLDirPickerThread::run() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) bool blocking = false; #else bool blocking = true; // modal diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index 2ac3db7c2e..dc740caab2 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -77,10 +77,8 @@ private: #if LL_LINUX || LL_DARWIN // On Linux we just implement LLDirPicker on top of LLFilePicker -#ifndef LL_FLTK LLFilePicker *mFilePicker; #endif -#endif std::string* mFileName; diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 5e676bc5b3..57def49539 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -296,7 +296,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures() // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL1); @@ -557,7 +557,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials) // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE) { @@ -769,7 +769,7 @@ void LLDrawPoolTerrain::renderFull4TU() } LLVertexBuffer::unbind(); - // Disable multitexture + // Disable textures gGL.getTexUnit(3)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(3)->disable(); gGL.getTexUnit(3)->activate(); @@ -949,7 +949,7 @@ void LLDrawPoolTerrain::renderFull2TU() // Restore blend state gGL.setSceneBlendType(LLRender::BT_ALPHA); - // Disable multitexture + // Disable textures gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->disable(); diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 3639064cc4..ca0b58b611 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -36,9 +36,11 @@ #include "llviewercontrol.h" #include "llwindow.h" // beforeDialog() -#if LL_LINUX -#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers -#include "llhttpconstants.h" // file picker uses some of thes constants on Linux +#if LL_NFD +#include "nfd.hpp" +#if LL_USE_SDL_WINDOW +#include "nfd_sdl2.h" +#endif #endif // @@ -80,7 +82,7 @@ LLFilePicker::LLFilePicker() { reset(); -#if LL_WINDOWS +#if LL_WINDOWS && !LL_NFD mOFN.lStructSize = sizeof(OPENFILENAMEW); mOFN.hwndOwner = NULL; // Set later mOFN.hInstance = NULL; @@ -165,7 +167,431 @@ void LLFilePicker::reset() mCurrentFile = 0; } +#if LL_NFD +std::vector<nfdfilteritem_t> LLFilePicker::setupFilter(ELoadFilter filter) +{ + std::vector<nfdfilteritem_t> filter_vec; + switch (filter) + { + case FFLOAD_EXE: #if LL_WINDOWS + filter_vec.emplace_back(nfdfilteritem_t{"Executables", "exe"}); +#endif + break; + case FFLOAD_ALL: + // Empty to allow picking all files by default + break; + case FFLOAD_WAV: + filter_vec.emplace_back(nfdfilteritem_t{"Sounds", "wav"}); + break; + case FFLOAD_IMAGE: + filter_vec.emplace_back(nfdfilteritem_t{"Images", "tga,bmp,jpg,jpeg,png"}); + break; + case FFLOAD_ANIM: + filter_vec.emplace_back(nfdfilteritem_t{"Animations", "bvh,anim"}); + break; + case FFLOAD_GLTF: + case FFLOAD_MATERIAL: + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Files", "gltf,glb"}); + break; + case FFLOAD_COLLADA: + filter_vec.emplace_back(nfdfilteritem_t{"Scene", "dae"}); + break; + case FFLOAD_XML: + filter_vec.emplace_back(nfdfilteritem_t{"XML files", "xml"}); + break; + case FFLOAD_SLOBJECT: + filter_vec.emplace_back(nfdfilteritem_t{"Objects", "slobject"}); + break; + case FFLOAD_RAW: + filter_vec.emplace_back(nfdfilteritem_t{"RAW files", "raw"}); + break; + case FFLOAD_MODEL: + filter_vec.emplace_back(nfdfilteritem_t{"Model files", "dae"}); + break; + case FFLOAD_HDRI: + filter_vec.emplace_back(nfdfilteritem_t{"EXR files", "exr"}); + break; + case FFLOAD_MATERIAL_TEXTURE: + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Import", "gltf,glb,tga,bmp,jpg,jpeg,png"}); + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Files", "gltf,glb"}); + filter_vec.emplace_back(nfdfilteritem_t{"Images", "tga,bmp,jpg,jpeg,png"}); + break; + case FFLOAD_SCRIPT: + filter_vec.emplace_back(nfdfilteritem_t{"Script files (*.lsl)", "lsl"}); + break; + case FFLOAD_DICTIONARY: + filter_vec.emplace_back(nfdfilteritem_t{"Dictionary files", "dic,xcu"}); + break; + case FFLOAD_LUA: + filter_vec.emplace_back(nfdfilteritem_t{"Script files (*.lua)", "lua"}); + break; + default: + break; + } + return filter_vec; +} + +bool LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + // prepare filters for the dialog + auto filterItem = setupFilter(filter); + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + reset(); + + // show the dialog + nfdresult_t result = NFD::OpenDialog(outPath, filterItem.data(), narrow(filterItem.size()), nullptr, windowHandle); + if (result == NFD_OKAY) + { + mFiles.push_back(outPath.get()); + success = true; + } + + if (blocking) + { + send_agent_resume(); + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getOpenFileModeless(ELoadFilter filter, + void (*callback)(bool, std::vector<std::string> &, void*), + void *userdata) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} + +bool LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + auto filterItem = setupFilter(filter); + + reset(); + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + // auto-freeing memory + NFD::UniquePathSet outPaths; + + // show the dialog + nfdresult_t result = NFD::OpenDialogMultiple(outPaths, filterItem.data(), narrow(filterItem.size()), nullptr, windowHandle); + if (result == NFD_OKAY) + { + LL_INFOS() << "Success!" << LL_ENDL; + + nfdpathsetsize_t numPaths; + NFD::PathSet::Count(outPaths, numPaths); + + nfdpathsetsize_t i; + for (i = 0; i < numPaths; ++i) + { + NFD::UniquePathSetPath path; + NFD::PathSet::GetPath(outPaths, i, path); + mFiles.push_back(path.get()); + LL_INFOS() << "Path " << i << ": " << path.get() << LL_ENDL; + } + success = true; + } + else if (result == NFD_CANCEL) + { + LL_INFOS() << "User pressed cancel." << LL_ENDL; + } + else + { + LL_INFOS() << "Error: " << NFD::GetError() << LL_ENDL; + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter, + void (*callback)(bool, std::vector<std::string> &, void*), + void *userdata ) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} + +bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + std::vector<nfdfilteritem_t> filter_vec; + std::string saved_filename = filename; + switch( filter ) + { + case FFSAVE_ALL: + filter_vec.emplace_back(nfdfilteritem_t{"WAV Sounds", "wav"}); + filter_vec.emplace_back(nfdfilteritem_t{"Targa, Bitmap Images", "tga,bmp"}); + break; + case FFSAVE_WAV: + if (filename.empty()) + { + saved_filename = "untitled.wav"; + } + filter_vec.emplace_back(nfdfilteritem_t{"WAV Sounds", "wav"}); + break; + case FFSAVE_TGA: + if (filename.empty()) + { + saved_filename = "untitled.tga"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Targa Images", "tga"}); + break; + case FFSAVE_BMP: + if (filename.empty()) + { + saved_filename = "untitled.bmp"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Bitmap Images", "bmp"}); + break; + case FFSAVE_PNG: + if (filename.empty()) + { + saved_filename = "untitled.png"; + } + filter_vec.emplace_back(nfdfilteritem_t{"PNG Images", "png"}); + break; + case FFSAVE_TGAPNG: + if (filename.empty()) + { + saved_filename = "untitled.png"; + } + + filter_vec.emplace_back(nfdfilteritem_t{"PNG Images", "png"}); + filter_vec.emplace_back(nfdfilteritem_t{"Targa Images", "tga"}); + filter_vec.emplace_back(nfdfilteritem_t{"JPEG Images", "jpg,jpeg"}); + filter_vec.emplace_back(nfdfilteritem_t{"Jpeg2000 Images", "j2c"}); + filter_vec.emplace_back(nfdfilteritem_t{"Bitmap Images", "bmp"}); + break; + case FFSAVE_JPEG: + if (filename.empty()) + { + saved_filename = "untitled.jpeg"; + } + filter_vec.emplace_back(nfdfilteritem_t{"JPEG Images", "jpg,jpeg"}); + break; + case FFSAVE_AVI: + if (filename.empty()) + { + saved_filename = "untitled.avi"; + } + filter_vec.emplace_back(nfdfilteritem_t{"AVI Movie File", "avi"}); + break; + case FFSAVE_ANIM: + if (filename.empty()) + { + saved_filename = "untitled.xaf"; + } + filter_vec.emplace_back(nfdfilteritem_t{"XAF Anim File", "xaf"}); + break; + case FFSAVE_XML: + if (filename.empty()) + { + saved_filename = "untitled.xml"; + } + filter_vec.emplace_back(nfdfilteritem_t{"XML File", "xml"}); + break; + case FFSAVE_COLLADA: + if (filename.empty()) + { + saved_filename = "untitled.collada"; + } + filter_vec.emplace_back(nfdfilteritem_t{"COLLADA File", "collada"}); + break; + case FFSAVE_RAW: + if (filename.empty()) + { + saved_filename = "untitled.raw"; + } + filter_vec.emplace_back(nfdfilteritem_t{"RAW files", "raw"}); + break; + case FFSAVE_J2C: + if (filename.empty()) + { + saved_filename = "untitled.j2c"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Compressed Images", "j2c"}); + break; + case FFSAVE_SCRIPT: + if (filename.empty()) + { + saved_filename = "untitled.lsl"; + } + filter_vec.emplace_back(nfdfilteritem_t{"LSL Files", "lsl"}); + break; + default: + return false; + } + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + reset(); + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + { + NFD::UniquePath savePath; + + // show the dialog + nfdresult_t result = NFD::SaveDialog(savePath, filter_vec.data(), narrow(filter_vec.size()), nullptr, saved_filename.c_str(), windowHandle); + if (result == NFD_OKAY) { + mFiles.push_back(savePath.get()); + success = true; + } + gKeyboard->resetKeys(); + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getSaveFileModeless(ESaveFilter filter, + const std::string& filename, + void (*callback)(bool, std::string&, void*), + void *userdata) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} +#elif LL_WINDOWS bool LLFilePicker::setupFilter(ELoadFilter filter) { @@ -1104,488 +1530,6 @@ bool LLFilePicker::getSaveFileModeless(ESaveFilter filter, #elif LL_LINUX -# if LL_GTK - -// static -void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data) -{ - // We need to run g_filename_to_utf8 in the user's locale - std::string saved_locale(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, ""); - - LLFilePicker* picker = (LLFilePicker*) user_data; - GError *error = NULL; - gchar* filename_utf8 = g_filename_to_utf8((gchar*)data, - -1, NULL, NULL, &error); - if (error) - { - // *FIXME. - // This condition should really be notified to the user, e.g. - // through a message box. Just logging it is inappropriate. - - // g_filename_display_name is ideal, but >= glib 2.6, so: - // a hand-rolled hacky makeASCII which disallows control chars - std::string display_name; - for (const gchar *str = (const gchar *)data; *str; str++) - { - display_name += (char)((*str >= 0x20 && *str <= 0x7E) ? *str : '?'); - } - LL_WARNS() << "g_filename_to_utf8 failed on \"" << display_name << "\": " << error->message << LL_ENDL; - } - - if (filename_utf8) - { - picker->mFiles.push_back(std::string(filename_utf8)); - LL_DEBUGS() << "ADDED FILE " << filename_utf8 << LL_ENDL; - g_free(filename_utf8); - } - - setlocale(LC_ALL, saved_locale.c_str()); -} - -// static -void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) -{ - LLFilePicker* picker = (LLFilePicker*)user_data; - - LL_DEBUGS() << "GTK DIALOG RESPONSE " << response << LL_ENDL; - - if (response == GTK_RESPONSE_ACCEPT) - { - GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); - g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data); - g_slist_foreach(file_list, (GFunc)g_free, NULL); - g_slist_free (file_list); - } - - // let's save the extension of the last added file(considering current filter) - GtkFileFilter *gfilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(widget)); - if(gfilter) - { - std::string filter = gtk_file_filter_get_name(gfilter); - - if(filter == LLTrans::getString("png_image_files")) - { - picker->mCurrentExtension = ".png"; - } - else if(filter == LLTrans::getString("targa_image_files")) - { - picker->mCurrentExtension = ".tga"; - } - } - - // set the default path for this usage context. - const char* cur_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget)); - if (cur_folder != NULL) - { - picker->mContextToPathMap[picker->mCurContextName] = cur_folder; - } - - gtk_widget_destroy(widget); - gtk_main_quit(); -} - - -GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::string context) -{ -#ifndef LL_MESA_HEADLESS - if (LLWindowSDL::ll_try_gtk_init()) - { - GtkWidget *win = NULL; - GtkFileChooserAction pickertype = - is_save? - (is_folder? - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER : - GTK_FILE_CHOOSER_ACTION_SAVE) : - (is_folder? - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : - GTK_FILE_CHOOSER_ACTION_OPEN); - - win = gtk_file_chooser_dialog_new(NULL, NULL, - pickertype, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - is_folder ? - GTK_STOCK_APPLY : - (is_save ? - GTK_STOCK_SAVE : - GTK_STOCK_OPEN), - GTK_RESPONSE_ACCEPT, - (gchar *)NULL); - mCurContextName = context; - - // get the default path for this usage context if it's been - // seen before. - std::map<std::string,std::string>::iterator - this_path = mContextToPathMap.find(context); - if (this_path != mContextToPathMap.end()) - { - gtk_file_chooser_set_current_folder - (GTK_FILE_CHOOSER(win), - this_path->second.c_str()); - } - -# if LL_X11 - // Make GTK tell the window manager to associate this - // dialog with our non-GTK raw X11 window, which should try - // to keep it on top etc. - Window XWindowID = LLWindowSDL::get_SDL_XWindowID(); - if (None != XWindowID) - { - gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin - GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID); - gdk_window_set_transient_for(GTK_WIDGET(win)->window, - gdkwin); - } - else - { - LL_WARNS() << "Hmm, couldn't get xwid to use for transient." << LL_ENDL; - } -# endif //LL_X11 - - g_signal_connect (GTK_FILE_CHOOSER(win), - "response", - G_CALLBACK(LLFilePicker::chooser_responder), - this); - - gtk_window_set_modal(GTK_WINDOW(win), TRUE); - - /* GTK 2.6: if (is_folder) - gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win), - TRUE); */ - - return GTK_WINDOW(win); - } - else - { - return NULL; - } -#else - return NULL; -#endif //LL_MESA_HEADLESS -} - -static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter, - GtkWindow *picker, - std::string filtername) -{ - gtk_file_filter_set_name(gfilter, filtername.c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter); - GtkFileFilter *allfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(allfilter, "*"); - gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter); -} - -static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker, - std::string pattern, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, pattern.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker, - std::string mime, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_mime_type(gfilter, mime.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, "audio/x-wav", - LLTrans::getString("sound_files") + " (*.wav)"); -} - -static std::string add_anim_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.bvh"); - gtk_file_filter_add_pattern(gfilter, "*.anim"); - std::string filtername = LLTrans::getString("animation_files") + " (*.bvh; *.anim)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_xml_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_pattern_filter_to_gtkchooser(picker, "*.xml", - LLTrans::getString("xml_files") + " (*.xml)"); -} - -static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_pattern_filter_to_gtkchooser(picker, "*.dae", - LLTrans::getString("scene_files") + " (*.dae)"); -} - -static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.tga"); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_JPEG.c_str()); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_PNG.c_str()); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_BMP.c_str()); - std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_script_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, HTTP_CONTENT_TEXT_PLAIN, - LLTrans::getString("script_files") + " (*.lsl)"); -} - -static std::string add_dictionary_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, HTTP_CONTENT_TEXT_PLAIN, - LLTrans::getString("dictionary_files") + " (*.dic; *.xcu)"); -} - -static std::string add_save_texture_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter_tga = gtk_file_filter_new(); - GtkFileFilter *gfilter_png = gtk_file_filter_new(); - - gtk_file_filter_add_pattern(gfilter_tga, "*.tga"); - gtk_file_filter_add_mime_type(gfilter_png, "image/png"); - std::string caption = LLTrans::getString("save_texture_image_files") + " (*.tga; *.png)"; - gtk_file_filter_set_name(gfilter_tga, LLTrans::getString("targa_image_files").c_str()); - gtk_file_filter_set_name(gfilter_png, LLTrans::getString("png_image_files").c_str()); - - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter_png); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter_tga); - return caption; -} - -bool LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename, bool blocking ) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(true, false, "savefile"); - - if (picker) - { - std::string suggest_name = "untitled"; - std::string suggest_ext = ""; - std::string caption = LLTrans::getString("save_file_verb") + " "; - switch (filter) - { - case FFSAVE_WAV: - caption += add_wav_filter_to_gtkchooser(picker); - suggest_ext = ".wav"; - break; - case FFSAVE_TGA: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)"); - suggest_ext = ".tga"; - break; - case FFSAVE_BMP: - caption += add_simple_mime_filter_to_gtkchooser - (picker, HTTP_CONTENT_IMAGE_BMP, LLTrans::getString("bitmap_image_files") + " (*.bmp)"); - suggest_ext = ".bmp"; - break; - case FFSAVE_PNG: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "image/png", LLTrans::getString("png_image_files") + " (*.png)"); - suggest_ext = ".png"; - break; - case FFSAVE_TGAPNG: - caption += add_save_texture_filter_to_gtkchooser(picker); - suggest_ext = ".png"; - break; - case FFSAVE_AVI: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "video/x-msvideo", - LLTrans::getString("avi_movie_file") + " (*.avi)"); - suggest_ext = ".avi"; - break; - case FFSAVE_ANIM: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)"); - suggest_ext = ".xaf"; - break; - case FFSAVE_XML: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)"); - suggest_ext = ".xml"; - break; - case FFSAVE_RAW: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)"); - suggest_ext = ".raw"; - break; - case FFSAVE_J2C: - // *TODO: Should this be 'image/j2c' ? - caption += add_simple_mime_filter_to_gtkchooser - (picker, "images/jp2", - LLTrans::getString("compressed_image_files") + " (*.j2c)"); - suggest_ext = ".j2c"; - break; - case FFSAVE_SCRIPT: - caption += add_script_filter_to_gtkchooser(picker); - suggest_ext = ".lsl"; - break; - default:; - break; - } - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - if (filename.empty()) - { - suggest_name += suggest_ext; - - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), - suggest_name.c_str()); - } - else - { - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), filename.c_str()); - } - - gtk_widget_show_all(GTK_WIDGET(picker)); - - gtk_main(); - - rtn = (getFileCount() == 1); - - if(rtn && filter == FFSAVE_TGAPNG) - { - std::string selected_file = mFiles.back(); - mFiles.pop_back(); - mFiles.push_back(selected_file + mCurrentExtension); - } - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -bool LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking ) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - std::string caption = LLTrans::getString("load_file_verb") + " "; - std::string filtername = ""; - switch (filter) - { - case FFLOAD_WAV: - filtername = add_wav_filter_to_gtkchooser(picker); - break; - case FFLOAD_ANIM: - filtername = add_anim_filter_to_gtkchooser(picker); - break; - case FFLOAD_XML: - filtername = add_xml_filter_to_gtkchooser(picker); - break; - case FFLOAD_GLTF: - filtername = dead_code_should_blow_up_here(picker); - break; - case FFLOAD_COLLADA: - filtername = add_collada_filter_to_gtkchooser(picker); - break; - case FFLOAD_IMAGE: - filtername = add_imageload_filter_to_gtkchooser(picker); - break; - case FFLOAD_SCRIPT: - filtername = add_script_filter_to_gtkchooser(picker); - break; - case FFLOAD_DICTIONARY: - filtername = add_dictionary_filter_to_gtkchooser(picker); - break; - default:; - break; - } - - caption += filtername; - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - - rtn = (getFileCount() == 1); - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -bool LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker), - TRUE); - - gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - rtn = !mFiles.empty(); - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -# else // LL_GTK - // Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with // static results, when we don't have a real filepicker. @@ -1673,8 +1617,6 @@ bool LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter, return false; } -#endif // LL_GTK - #else // not implemented bool LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index e0bd32fe70..8c89570174 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -54,6 +54,10 @@ #include <commdlg.h> #endif +#if LL_NFD +#include "nfd.hpp" +#endif + class LLFilePicker { public: @@ -151,14 +155,18 @@ private: // is enabled and if not, tidy up and indicate we're not allowed to do this. bool check_local_file_access_enabled(); -#if LL_WINDOWS +#if LL_NFD + std::vector<nfdfilteritem_t> setupFilter(ELoadFilter filter); +#endif + +#if LL_WINDOWS && !LL_NFD OPENFILENAMEW mOFN; // for open and save dialogs WCHAR mFilesW[FILENAME_BUFFER_SIZE]; bool setupFilter(ELoadFilter filter); #endif -#if LL_DARWIN +#if LL_DARWIN && !LL_NFD S32 mPickOptions; std::vector<std::string> mFileVector; @@ -174,14 +182,6 @@ private: void *userdata); #endif -#if LL_FLTK - enum EType - { - eSaveFile, eOpenFile, eOpenMultiple - }; - bool openFileDialog( int32_t filter, bool blocking, EType aType ); -#endif - std::vector<std::string> mFiles; S32 mCurrentFile; bool mLocked; diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index 7d098b2676..8897ad55e0 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -151,7 +151,6 @@ void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 so { S32 num_output_sections = 1<<dest_sections; LLVector3 scale = mVO->mDrawable->getScale(); - F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections); F32 section_length = scale.mV[VZ] / (F32)num_output_sections; if (source_sections == -1) { @@ -183,6 +182,7 @@ void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 so // Iterate from right to left since it may be an in-place computation S32 step_shift = dest_sections-source_sections; S32 num_steps = 1<<step_shift; + F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections); for (S32 section=num_output_sections-num_steps; section>=0; section -= num_steps) { LLFlexibleObjectSection *last_source_section = &source[section>>step_shift]; diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp index d56e6cdf20..2a4f1ddd12 100644 --- a/indra/newview/llfloaterbump.cpp +++ b/indra/newview/llfloaterbump.cpp @@ -56,7 +56,7 @@ LLFloaterBump::LLFloaterBump(const LLSD& key) mCommitCallbackRegistrar.add("ShowAgentProfile", { boost::bind(&LLFloaterBump::showProfile, this), cb_info::UNTRUSTED_THROTTLE }); mCommitCallbackRegistrar.add("Avatar.InviteToGroup", { boost::bind(&LLFloaterBump::inviteToGroup, this), cb_info::UNTRUSTED_THROTTLE }); mCommitCallbackRegistrar.add("Avatar.Call", { boost::bind(&LLFloaterBump::startCall, this), cb_info::UNTRUSTED_BLOCK }); - mEnableCallbackRegistrar.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); + mEnableCallbackRegistrar.add("Avatar.EnableCall", { boost::bind(&LLFloaterBump::canCall, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Avatar.AddFriend", { boost::bind(&LLFloaterBump::addFriend, this), cb_info::UNTRUSTED_THROTTLE }); mEnableCallbackRegistrar.add("Avatar.EnableAddFriend", boost::bind(&LLFloaterBump::enableAddFriend, this)); mCommitCallbackRegistrar.add("Avatar.Mute", { boost::bind(&LLFloaterBump::muteAvatar, this), cb_info::UNTRUSTED_BLOCK }); @@ -214,6 +214,11 @@ void LLFloaterBump::startCall() LLAvatarActions::startCall(mItemUUID); } +bool LLFloaterBump::canCall() +{ + return LLAvatarActions::canCallTo(mItemUUID); +} + void LLFloaterBump::reportAbuse() { LLFloaterReporter::showFromAvatar(mItemUUID, "av_name"); diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h index 53e730d73f..48d6f7e33e 100644 --- a/indra/newview/llfloaterbump.h +++ b/indra/newview/llfloaterbump.h @@ -52,6 +52,7 @@ public: void startIM(); void startCall(); + bool canCall(); void reportAbuse(); void showProfile(); void addFriend(); diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 1e2d790cfc..ed24a8af57 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1592,15 +1592,15 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v } else if ("can_call" == item) { + if (is_single_select) + { + return LLAvatarActions::canCallTo(single_id); + } return LLAvatarActions::canCall(); } else if ("can_open_voice_conversation" == item) { - return is_single_select && LLAvatarActions::canCall(); - } - else if ("can_open_voice_conversation" == item) - { - return is_single_select && LLAvatarActions::canCall(); + return is_single_select && LLAvatarActions::canCallTo(single_id); } else if ("can_zoom_in" == item) { diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 6c3e8391cd..f108b5198e 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -37,6 +37,7 @@ #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" +#include "llregex.h" #include "llresizebar.h" #include "llresizehandle.h" #include "lldraghandle.h" @@ -55,6 +56,7 @@ #include "llfloaterimnearbychatlistener.h" #include "llagent.h" // gAgent #include "llgesturemgr.h" +#include "llluamanager.h" #include "llmultigesture.h" #include "llkeyboard.h" #include "llanimationstates.h" @@ -606,6 +608,13 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text)) { utf8_revised_text = utf8text; + // check if the message is /filename.lua and execute the Lua script + static const boost::regex is_lua_script("^/.*\\.luau?(?:\\s+\\S+)*$"); + if (ll_regex_match(utf8text, is_lua_script)) + { + LLLUAmanager::runScriptFile(utf8text.substr(1)); + utf8_revised_text.clear(); + } } } else diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index b2f2984c65..0855a628fb 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -39,6 +39,7 @@ #include "llchicletbar.h" #include "lldraghandle.h" #include "llemojidictionary.h" +#include "llemojihelper.h" #include "llfloaterreg.h" #include "llfloateremojipicker.h" #include "llfloaterimsession.h" @@ -298,6 +299,8 @@ bool LLFloaterIMSessionTab::postBuild() mEmojiPickerShowBtn = getChild<LLButton>("emoji_picker_show_btn"); mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); }); + mEmojiPickerShowBtn->setMouseDownCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnDown(); }); + mEmojiCloseConn = LLEmojiHelper::instance().setCloseCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerClosed(); }); mGearBtn = getChild<LLButton>("gear_btn"); mAddBtn = getChild<LLButton>("add_btn"); @@ -526,8 +529,43 @@ void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked() void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked() { - mInputEditor->setFocus(true); - mInputEditor->showEmojiHelper(); + if (!mEmojiPickerShowBtn->getToggleState()) + { + mInputEditor->hideEmojiHelper(); + mInputEditor->setFocus(true); + mInputEditor->showEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(true); // in case hideEmojiHelper closed a visible instance + } + else + { + mInputEditor->hideEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(false); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerShowBtnDown() +{ + if (mEmojiHelperLastCallbackFrame == LLFrameTimer::getFrameCount()) + { + // Helper gets closed by focus lost event on Down before before onEmojiPickerShowBtnDown + // triggers. + // If this condition is true, user pressed button and it was 'toggled' during press, + // restore 'toggled' state so that button will not reopen helper. + mEmojiPickerShowBtn->setToggleState(true); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerClosed() +{ + if (mEmojiPickerShowBtn->getToggleState()) + { + mEmojiPickerShowBtn->setToggleState(false); + // Helper gets closed by focus lost event on Down before onEmojiPickerShowBtnDown + // triggers. If mEmojiHelperLastCallbackFrame is set and matches Down, means close + // was triggered by user's press. + // A bit hacky, but I can't think of a better way to handle this without rewriting helper. + mEmojiHelperLastCallbackFrame = LLFrameTimer::getFrameCount(); + } } void LLFloaterIMSessionTab::initEmojiRecentPanel() @@ -591,8 +629,19 @@ void LLFloaterIMSessionTab::deleteAllChildren() std::string LLFloaterIMSessionTab::appendTime() { - std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" - "[" + LLTrans::getString("TimeMin") + "]"; + std::string timeStr; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:" + "[" + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:" + "[" + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32)time_corrected(); diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index bee5c8c2c4..890c920bbe 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -227,6 +227,8 @@ private: void onEmojiRecentPanelToggleBtnClicked(); void onEmojiPickerShowBtnClicked(); + void onEmojiPickerShowBtnDown(); + void onEmojiPickerClosed(); void initEmojiRecentPanel(); void onEmojiRecentPanelFocusReceived(); void onEmojiRecentPanelFocusLost(); @@ -241,6 +243,9 @@ private: S32 mInputEditorPad; S32 mChatLayoutPanelHeight; S32 mFloaterHeight; + + boost::signals2::connection mEmojiCloseConn; + U32 mEmojiHelperLastCallbackFrame = { 0 }; }; diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 0f1eb0cef0..5dea46843c 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -220,7 +220,8 @@ void LLFloaterInspect::refresh() } time_t timestamp = (time_t) (obj->mCreationDate/1000000); - std::string timeStr = getString("timeStamp"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("timeStamp") : getString("timeStampAMPM"); LLSD substitution; substitution["datetime"] = (S32) timestamp; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 52a3e78d04..5c5219bcdd 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -733,7 +733,8 @@ void LLPanelLandGeneral::refresh() // Display claim date time_t claim_date = parcel->getClaimDate(); - std::string claim_date_str = getString("time_stamp_template"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string claim_date_str = use_24h ? getString("time_stamp_template") : getString("time_stamp_template_ampm"); LLSD substitution; substitution["datetime"] = (S32) claim_date; LLStringUtil::format (claim_date_str, substitution); diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 06877df816..362a02a642 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild() .listen("LLFloaterLUADebug", [mResultOutput=mResultOutput](const LLSD& data) { - mResultOutput->pasteTextWithLinebreaks(data.asString()); + LLCachedControl<bool> show_source_info(gSavedSettings, "LuaDebugShowSource", false); + std::string source_info = show_source_info ? data["source_info"].asString() : ""; + mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString()), true); mResultOutput->addLineBreakChar(true); return false; }); diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 0eba45ec29..6b3d87543a 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -51,9 +51,9 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) }, cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata) { - if (mScriptList->hasSelectedItem()) + std::vector<LLSD> coros = mScriptList->getAllSelectedValues(); + for (auto coro_name : coros) { - std::string coro_name = mScriptList->getSelectedValue(); LLCoros::instance().killreq(coro_name); } }, cb_info::UNTRUSTED_BLOCK }); @@ -97,7 +97,7 @@ void LLFloaterLUAScripts::draw() void LLFloaterLUAScripts::populateScriptList() { S32 prev_pos = mScriptList->getScrollPos(); - LLSD prev_selected = mScriptList->getSelectedValue(); + std::vector<LLSD> prev_selected = mScriptList->getAllSelectedValues(); mScriptList->clearRows(); mScriptList->updateColumns(true); std::map<std::string, std::string> scripts = LLLUAmanager::getScriptNames(); @@ -112,7 +112,10 @@ void LLFloaterLUAScripts::populateScriptList() mScriptList->addElement(row); } mScriptList->setScrollPos(prev_pos); - mScriptList->setSelectedByValue(prev_selected, true); + for (auto value : prev_selected) + { + mScriptList->setSelectedByValue(value, true); + } } void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) @@ -120,10 +123,13 @@ void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) LLScrollListItem *item = mScriptList->hitItem(x, y); if (item) { - mScriptList->selectItemAt(x, y, MASK_NONE); + if (!item->getSelected()) + mScriptList->selectItemAt(x, y, MASK_NONE); + auto menu = mContextMenuHandle.get(); if (menu) { + menu->setItemEnabled(std::string("open_folder"), (mScriptList->getNumSelected() == 1)); menu->show(x, y); LLMenuGL::showPopup(this, menu, x, y); } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 0c96c93d31..d7ac8f0552 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -473,6 +473,8 @@ bool LLFloaterPreference::postBuild() getChild<LLUICtrl>("log_path_string")->setEnabled(false); // make it read-only but selectable getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + mTimeFormatCombobox = getChild<LLComboBox>("time_format_combobox"); + mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this)); getChild<LLComboBox>("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild<LLComboBox>("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); @@ -1108,6 +1110,13 @@ void LLFloaterPreference::onLanguageChange() } } +void LLFloaterPreference::onTimeFormatChange() +{ + std::string val = mTimeFormatCombobox->getValue(); + gSavedSettings.setBOOL("Use24HourClock", val == "1"); + onLanguageChange(); +} + void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) { mNotificationOptions[OptionName] = getChild<LLComboBox>(OptionName)->getSelectedItemLabel(); @@ -1331,6 +1340,8 @@ void LLFloaterPreference::refresh() advanced->refresh(); } updateClickActionViews(); + + mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0"); } void LLFloaterPreference::onCommitWindowedMode() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index e06e758e3a..d5bd43e0ab 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -124,6 +124,7 @@ protected: void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); + void onTimeFormatChange(); void onNotificationsChange(const std::string& OptionName); void onNameTagOpacityChange(const LLSD& newvalue); @@ -242,6 +243,7 @@ private: LLButton* mDeleteTranscriptsBtn = nullptr; LLButton* mEnablePopupBtn = nullptr; LLButton* mDisablePopupBtn = nullptr; + LLComboBox* mTimeFormatCombobox = nullptr; std::unique_ptr< ll::prefs::SearchData > mSearchData; bool mSearchDataDirty; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 165fb5ef88..ac3e942e15 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -148,40 +148,6 @@ public: static LLSD getIDs( sparam_t::const_iterator it, sparam_t::const_iterator end, S32 count ); }; - -/* -void unpack_request_params( - LLMessageSystem* msg, - LLDispatcher::sparam_t& strings, - LLDispatcher::iparam_t& integers) -{ - char str_buf[MAX_STRING]; - S32 str_count = msg->getNumberOfBlocksFast(_PREHASH_StringData); - S32 i; - for (i = 0; i < str_count; ++i) - { - // we treat the SParam as binary data (since it might be an - // LLUUID in compressed form which may have embedded \0's,) - str_buf[0] = '\0'; - S32 data_size = msg->getSizeFast(_PREHASH_StringData, i, _PREHASH_SParam); - if (data_size >= 0) - { - msg->getBinaryDataFast(_PREHASH_StringData, _PREHASH_SParam, - str_buf, data_size, i, MAX_STRING - 1); - strings.push_back(std::string(str_buf, data_size)); - } - } - - U32 int_buf; - S32 int_count = msg->getNumberOfBlocksFast(_PREHASH_IntegerData); - for (i = 0; i < int_count; ++i) - { - msg->getU32("IntegerData", "IParam", int_buf, i); - integers.push_back(int_buf); - } -} -*/ - class LLPanelRegionEnvironment : public LLPanelEnvironmentInfo { public: @@ -326,8 +292,8 @@ void LLFloaterRegionInfo::onOpen(const LLSD& key) disableTabCtrls(); return; } - refreshFromRegion(gAgent.getRegion()); - requestRegionInfo(); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::BeforeRequestRegionInfo); + requestRegionInfo(true); if (!mGodLevelChangeSlot.connected()) { @@ -347,12 +313,14 @@ void LLFloaterRegionInfo::onRegionChanged() { if (getVisible()) //otherwise onOpen will do request { - requestRegionInfo(); + requestRegionInfo(false); } } -void LLFloaterRegionInfo::requestRegionInfo() +void LLFloaterRegionInfo::requestRegionInfo(bool is_opening) { + mIsRegionInfoRequestedFromOpening = is_opening; + LLTabContainer* tab = findChild<LLTabContainer>("region_panels"); if (tab) { @@ -523,8 +491,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChild<LLSpinCtrl>("agent_limit_spin")->setMaxValue((F32)hard_agent_limit); - LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral(); - if (panel) + if (LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral()) { panel_general->setObjBonusFactor(object_bonus_factor); } @@ -537,6 +504,18 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChildView("access_combo")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate() && !teen_grid)); panel->setCtrlsEnabled(allow_modify); + panel->getChild<LLLineEditor>("estate_id")->setValue((S32)region_info.mEstateID); + + if (region) + { + panel->getChild<LLLineEditor>("grid_position_x")->setValue((S32)(region->getOriginGlobal()[VX] / 256)); + panel->getChild<LLLineEditor>("grid_position_y")->setValue((S32)(region->getOriginGlobal()[VY] / 256)); + } + else + { + panel->getChild<LLLineEditor>("grid_position_x")->setDefaultText(); + panel->getChild<LLLineEditor>("grid_position_y")->setDefaultText(); + } // DEBUG PANEL panel = tab->getChild<LLPanel>("Debug"); @@ -561,21 +540,25 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) { // Note: region info also causes LLRegionInfoModel::instance().update(msg); -> requestRegion(); -> changed message // we need to know env version here and in update(msg) to know when to request and when not to, when to filter 'changed' - floater->refreshFromRegion(gAgent.getRegion()); + ERefreshFromRegionPhase phase = floater->mIsRegionInfoRequestedFromOpening ? + ERefreshFromRegionPhase::AfterRequestRegionInfo : + ERefreshFromRegionPhase::NotFromFloaterOpening; + floater->refreshFromRegion(gAgent.getRegion(), phase); } // else will rerequest on onOpen either way } // static -void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) { - if (region != gAgent.getRegion()) { return; } - - LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); - if (!floater) { return; } + if (region != gAgent.getRegion()) + return; - if (floater->getVisible() && region == gAgent.getRegion()) + if (LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info")) { - floater->refreshFromRegion(region); + if (floater->getVisible() && region == gAgent.getRegion()) + { + floater->refreshFromRegion(region, ERefreshFromRegionPhase::NotFromFloaterOpening); + } } } @@ -682,7 +665,7 @@ void LLFloaterRegionInfo::onTabSelected(const LLSD& param) active_panel->onOpen(LLSD()); } -void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (!region) { @@ -692,7 +675,7 @@ void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) // call refresh from region on all panels for (const auto& infoPanel : mInfoPanels) { - infoPanel->refreshFromRegion(region); + infoPanel->refreshFromRegion(region, phase); } mEnvironmentPanel->refreshFromRegion(region); } @@ -725,7 +708,7 @@ void LLFloaterRegionInfo::onGodLevelChange(U8 god_level) LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); if (floater && floater->getVisible()) { - refreshFromRegion(gAgent.getRegion()); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } } @@ -795,7 +778,7 @@ void LLPanelRegionInfo::updateChild(LLUICtrl* child_ctr) } // virtual -bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (region) mHost = region->getHost(); return true; @@ -823,12 +806,10 @@ void LLPanelRegionInfo::sendEstateOwnerMessage( } else { - strings_t::const_iterator it = strings.begin(); - strings_t::const_iterator end = strings.end(); - for(; it != end; ++it) + for (const std::string& string : strings) { msg->nextBlock("ParamList"); - msg->addString("Parameter", *it); + msg->addString("Parameter", string); } } msg->sendReliable(mHost); @@ -887,13 +868,16 @@ void LLPanelRegionInfo::onClickManageRestartSchedule() ///////////////////////////////////////////////////////////////////////////// // LLPanelRegionGeneralInfo // -bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); getChildView("apply_btn")->setEnabled(false); getChildView("access_text")->setEnabled(allow_modify); // getChildView("access_combo")->setEnabled(allow_modify); + getChildView("estate_id")->setEnabled(false); + getChildView("grid_position_x")->setEnabled(false); + getChildView("grid_position_y")->setEnabled(false); // now set in processRegionInfo for teen grid detection getChildView("kick_btn")->setEnabled(allow_modify); getChildView("kick_all_btn")->setEnabled(allow_modify); @@ -904,7 +888,7 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) // Data gets filled in by processRegionInfo - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } bool LLPanelRegionGeneralInfo::postBuild() @@ -1173,7 +1157,7 @@ bool LLPanelRegionDebugInfo::postBuild() } // virtual -bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); @@ -1191,7 +1175,7 @@ bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) getChildView("cancel_restart_btn")->setEnabled(allow_modify); getChildView("region_debug_console_btn")->setEnabled(allow_modify); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } // virtual @@ -1233,7 +1217,7 @@ void LLPanelRegionDebugInfo::callbackAvatarID(const uuid_vec_t& ids, const std:: if (ids.empty() || names.empty()) return; mTargetAvatar = ids[0]; getChild<LLUICtrl>("target_avatar_name")->setValue(LLSD(names[0].getCompleteName())); - refreshFromRegion( gAgent.getRegion() ); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } // static @@ -1675,7 +1659,7 @@ void LLPanelRegionTerrainInfo::updateForMaterialType() } // virtual -bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool owner_or_god = gAgent.isGodlike() || (region && (region->getOwner() == gAgent.getID())); @@ -1819,7 +1803,7 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) getChildView("upload_raw_btn")->setEnabled(owner_or_god); getChildView("bake_terrain_btn")->setEnabled(owner_or_god); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } @@ -2289,25 +2273,27 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) refresh(); } -bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateControls(region); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - - // We want estate info. To make sure it works across region - // boundaries and multiple packets, we add a serial number to the - // integers and track against that on update. - strings_t strings; - //integers_t integers; - //LLFloaterRegionInfo::incrementSerial(); - LLFloaterRegionInfo::nextInvoice(); - LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + if (phase != ERefreshFromRegionPhase::BeforeRequestRegionInfo) + { + // We want estate info. To make sure it works across region + // boundaries and multiple packets, we add a serial number to the + // integers and track against that on update. + strings_t strings; + //integers_t integers; + //LLFloaterRegionInfo::incrementSerial(); + LLFloaterRegionInfo::nextInvoice(); + LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); + //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); - sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + } refresh(); @@ -2528,7 +2514,7 @@ LLPanelEstateCovenant::LLPanelEstateCovenant() } // virtual -bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { LLTextBox* region_name = getChild<LLTextBox>("region_name_text"); if (region_name) @@ -2574,13 +2560,17 @@ bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) getChild<LLButton>("reset_covenant")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate())); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - LLMessageSystem *msg = gMessageSystem; - msg->newMessage("EstateCovenantRequest"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID()); - msg->sendReliable(region->getHost()); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + gMessageSystem->newMessage("EstateCovenantRequest"); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->sendReliable(region->getHost()); + } + return rv; } @@ -3128,7 +3118,7 @@ std::string LLPanelRegionExperiences::regionCapabilityQuery(LLViewerRegion* regi return region->getCapability(cap); } -bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); @@ -3151,10 +3141,13 @@ bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) mTrusted->loading(); mTrusted->setReadonly(!allow_modify); - LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), - boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), + boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); + } - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) @@ -4181,35 +4174,32 @@ void LLPanelEstateAccess::copyListToClipboard(std::string list_name) { LLPanelEstateAccess* panel = LLFloaterRegionInfo::getPanelAccess(); if (!panel) return; - LLNameListCtrl* name_list = panel->getChild<LLNameListCtrl>(list_name); - if (!name_list) return; + LLNameListCtrl* name_list = panel->getChild<LLNameListCtrl>(list_name); std::vector<LLScrollListItem*> list_vector = name_list->getAllData(); - if (list_vector.size() == 0) return; + if (list_vector.empty()) + return; LLSD::String list_to_copy; - for (std::vector<LLScrollListItem*>::const_iterator iter = list_vector.begin(); - iter != list_vector.end(); - iter++) + for (LLScrollListItem* item : list_vector) { - LLScrollListItem *item = (*iter); if (item) { + if (!list_to_copy.empty()) + { + list_to_copy += "\n"; + } list_to_copy += item->getColumn(0)->getValue().asString(); } - if (std::next(iter) != list_vector.end()) - { - list_to_copy += "\n"; - } } LLClipboard::instance().copyToClipboard(utf8str_to_wstring(list_to_copy), 0, static_cast<S32>(list_to_copy.length())); } -bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateLists(); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } //========================================================================= diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 65c1291728..119f7c98f3 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -71,6 +71,13 @@ class LLPanelRegionEnvironment; class LLEventTimer; +enum class ERefreshFromRegionPhase +{ + NotFromFloaterOpening, + BeforeRequestRegionInfo, + AfterRequestRegionInfo +}; + class LLFloaterRegionInfo : public LLFloater { friend class LLFloaterReg; @@ -85,7 +92,7 @@ public: // get and process region info if necessary. static void processRegionInfo(LLMessageSystem* msg); - static void sRefreshFromRegion(LLViewerRegion* region); + static void refreshFromRegion(LLViewerRegion* region); static const LLUUID& getLastInvoice() { return sRequestInvoice; } static void nextInvoice() { sRequestInvoice.generate(); } @@ -104,7 +111,7 @@ public: void refresh() override; void onRegionChanged(); - void requestRegionInfo(); + void requestRegionInfo(bool is_opening); void enableTopButtons(); void disableTopButtons(); @@ -116,7 +123,7 @@ private: protected: void onTabSelected(const LLSD& param); void disableTabCtrls(); - void refreshFromRegion(LLViewerRegion* region); + void refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); void onGodLevelChange(U8 god_level); // member data @@ -124,6 +131,7 @@ protected: typedef std::vector<LLPanelRegionInfo*> info_panels_t; info_panels_t mInfoPanels; LLPanelRegionEnvironment *mEnvironmentPanel; + bool mIsRegionInfoRequestedFromOpening { false }; //static S32 sRequestSerial; // serial # of last EstateOwnerRequest static LLUUID sRequestInvoice; @@ -144,7 +152,7 @@ public: void onChangeAnything(); static void onChangeText(LLLineEditor* caller, void* user_data); - virtual bool refreshFromRegion(LLViewerRegion* region); + virtual bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); virtual bool estateUpdate(LLMessageSystem* msg) { return true; } bool postBuild() override; @@ -190,7 +198,7 @@ public: : LLPanelRegionInfo() {} ~LLPanelRegionGeneralInfo() {} - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool postBuild() override; @@ -222,7 +230,7 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; protected: bool sendUpdate() override; @@ -254,8 +262,8 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; // refresh local settings from region update from simulator - void setEnvControls(bool available); // Whether environment settings are available for this region + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; // refresh local settings from region update from simulator + void setEnvControls(bool available); // Whether environment settings are available for this region bool validateTextureSizes(); bool validateMaterials(); @@ -327,7 +335,7 @@ public: static void updateEstateName(const std::string& name); static void updateEstateOwnerName(const std::string& name); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; bool postBuild() override; @@ -364,7 +372,7 @@ public: bool postBuild() override; void updateChild(LLUICtrl* child_ctrl) override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; // LLView overrides @@ -430,7 +438,7 @@ public: static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id); static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; void sendPurchaseRequest()const; void processResponse( const LLSD& content ); @@ -470,7 +478,7 @@ public: void setPendingUpdate(bool pending) { mPendingUpdate = pending; } bool getPendingUpdate() { return mPendingUpdate; } - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; private: void onClickAddAllowedAgent(); diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index dda7266220..4674cd68b6 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -486,8 +486,11 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id); - mLocationEditor->setFocus( true); - gFocusMgr.triggerFocusFlash(); + if (hasFocus()) + { + mLocationEditor->setFocus( true); + gFocusMgr.triggerFocusFlash(); + } buildAvatarIDList(); buildLandmarkIDLists(); diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 590cd09a31..e050fb77e0 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -422,6 +422,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id) { LLChat chat; @@ -451,6 +452,28 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, bool is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && LLMuteList::isLinden(name); + /*** + * The simulator may have flagged this sender as a bot, if the viewer would like to display + * the chat text in a different color or font, the below code is how the viewer can + * tell if the sender is a bot. + *----------------------------------------------------- + bool is_bot = false; + if (metadata.has("sender")) + { // The server has identified this sender as a bot. + is_bot = metadata["sender"]["bot"].asBoolean(); + } + *----------------------------------------------------- + */ + + std::string notice_name; + LLSD notice_args; + if (metadata.has("notice")) + { // The server has injected a notice into the IM conversation. + // These will be things like bot notifications, etc. + notice_name = metadata["notice"]["id"].asString(); + notice_args = metadata["notice"]["data"]; + } + chat.mMuted = is_muted; chat.mFromID = from_id; chat.mFromName = name; @@ -544,7 +567,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else { - // standard message, not from system + // standard message, server may have injected a notice into the conversation. std::string saved; if (offline == IM_OFFLINE) { @@ -579,8 +602,17 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, region_message = true; } } - gIMMgr->addMessage( - session_id, + + std::string real_name; + + if (!notice_name.empty()) + { // The simulator has injected some sort of notice into the conversation. + // findString will only replace the contents of buffer if the notice_id is found. + LLTrans::findString(buffer, notice_name, notice_args); + real_name = SYSTEM_FROM; + } + + gIMMgr->addMessage(session_id, from_id, name, buffer, @@ -591,7 +623,9 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, region_id, position, region_message, - timestamp); + timestamp, + LLUUID::null, + real_name); } else { @@ -1627,6 +1661,12 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) from_group = message_data["from_group"].asString() == "Y"; } + LLSD metadata; + if (message_data.has("metadata")) + { + metadata = message_data["metadata"]; + } + EInstantMessage dialog = static_cast<EInstantMessage>(message_data["dialog"].asInteger()); LLUUID session_id = message_data["transaction-id"].asUUID(); if (session_id.isNull() && dialog == IM_FROM_TASK) @@ -1654,6 +1694,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) local_bin_bucket.data(), S32(local_bin_bucket.size()), local_sender, + metadata, message_data["asset_id"].asUUID()); }); diff --git a/indra/newview/llimprocessing.h b/indra/newview/llimprocessing.h index 030d28b198..66ffc59ae0 100644 --- a/indra/newview/llimprocessing.h +++ b/indra/newview/llimprocessing.h @@ -48,6 +48,7 @@ public: U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id = LLUUID::null); // Either receives list of offline messages from 'ReadOfflineMsgs' capability diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 1402cc8c37..beb32dd6fb 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3143,9 +3143,16 @@ void LLIMMgr::addMessage( const LLUUID& region_id, const LLVector3& position, bool is_region_msg, - U32 timestamp) // May be zero + U32 timestamp, // May be zero + LLUUID display_id, + std::string_view display_name) { LLUUID other_participant_id = target_id; + std::string message_display_name = (display_name.empty()) ? from : std::string(display_name); + if (display_id.isNull() && (display_name.empty())) + { + display_id = other_participant_id; + } LLUUID new_session_id = session_id; if (new_session_id.isNull()) @@ -3241,7 +3248,7 @@ void LLIMMgr::addMessage( } //Play sound for new conversations - if (!skip_message & !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation"))) + if (!skip_message && !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation"))) { make_ui_sound("UISndNewIncomingIMSession"); } @@ -3255,7 +3262,7 @@ void LLIMMgr::addMessage( if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message) { - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_region_msg, timestamp); + LLIMModel::instance().addMessage(new_session_id, message_display_name, display_id, msg, true, is_region_msg, timestamp); } // Open conversation floater if offline messages are present diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 233fb075e8..162a8d93dd 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -368,7 +368,9 @@ public: const LLUUID& region_id = LLUUID::null, const LLVector3& position = LLVector3::zero, bool is_region_msg = false, - U32 timestamp = 0); + U32 timestamp = 0, + LLUUID display_id = LLUUID::null, + std::string_view display_name = ""); void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 896c7d8d70..2deb7caad5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4189,6 +4189,32 @@ void LLFolderBridge::pasteLinkFromClipboard() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = model->getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + // otehrwise AIS will return 'Cannot link to items with a NULL asset_id.' + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + + LLPointer<LLInventoryCallback> cb = NULL; LLInventoryPanel* panel = mInventoryPanel.get(); if (panel->getRootFolder()->isSingleFolderMode()) diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index c4f93cee98..c7f2b0cb72 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -2105,6 +2105,30 @@ void LLInventoryGallery::pasteAsLink() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = gInventory.getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + bool paste_into_root = mSelectedItemIDs.empty(); for (LLUUID& dest : mSelectedItemIDs) { @@ -2753,6 +2777,7 @@ static LLDefaultChildRegistry::Register<LLInventoryGalleryItem> r("inventory_gal LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p) : LLPanel(p), mSelected(false), + mWorn(false), mDefaultImage(true), mItemName(""), mWornSuffix(""), diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index c84b9abba0..340ecfcbbc 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -598,20 +598,23 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men bool has_children = false; bool is_full_perm_item = false; bool is_copyable = false; - LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id); + + LLViewerInventoryCategory* selected_category = nullptr; + LLViewerInventoryItem* selected_item = nullptr; if(is_folder) { - LLInventoryCategory* category = gInventory.getCategory(selected_id); - if (category) + selected_category = dynamic_cast<LLViewerInventoryCategory*>(obj); + if (selected_category) { - folder_type = category->getPreferredType(); + folder_type = selected_category->getPreferredType(); is_system_folder = LLFolderType::lookupIsProtectedType(folder_type); has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO); } } else { + selected_item = dynamic_cast<LLViewerInventoryItem*>(obj); if (selected_item) { is_full_perm_item = selected_item->getIsFullPerm(); @@ -730,8 +733,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits) { - LLViewerInventoryCategory* category = gInventory.getCategory(selected_id); - if (!category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(category)) + if (!selected_category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(selected_category)) { items.push_back(std::string("New Folder")); } @@ -759,6 +761,22 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men if (inventory_linking) { items.push_back(std::string("Paste As Link")); + + if (selected_item) + { + if (!LLAssetType::lookupCanLink(selected_item->getActualType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + else if (gInventory.isObjectDescendentOf(selected_item->getUUID(), gInventory.getLibraryRootFolderID())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + } + else if (selected_category && LLFolderType::lookupIsProtectedType(selected_category->getPreferredType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } } } if (is_folder && is_agent_inventory) @@ -979,9 +997,8 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_folder) { - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); - if (cat - && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) + if (selected_category + && !LLFolderType::lookupIsProtectedType(selected_category->getPreferredType()) && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())) { can_list = true; diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 79726c3e0b..13ca12a69a 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -107,16 +107,13 @@ LLInventoryListener::LLInventoryListener() // This struct captures (possibly large) category results from // getDirectDescendants() and collectDescendantsIf(). -struct CatResultSet: public LL::ResultSet +struct CatResultSet: public LL::VectorResultSet<LLInventoryModel::cat_array_t::value_type> { - CatResultSet(): LL::ResultSet("categories") {} - LLInventoryModel::cat_array_t mCategories; - - int getLength() const override { return narrow(mCategories.size()); } - LLSD getSingle(int index) const override + CatResultSet(): super("categories") {} + LLSD getSingleFrom(const LLPointer<LLViewerInventoryCategory>& cat) const override { - auto cat = mCategories[index]; - return llsd::map("name", cat->getName(), + return llsd::map("id", cat->getUUID(), + "name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType())); } @@ -124,16 +121,13 @@ struct CatResultSet: public LL::ResultSet // This struct captures (possibly large) item results from // getDirectDescendants() and collectDescendantsIf(). -struct ItemResultSet: public LL::ResultSet +struct ItemResultSet: public LL::VectorResultSet<LLInventoryModel::item_array_t::value_type> { - ItemResultSet(): LL::ResultSet("items") {} - LLInventoryModel::item_array_t mItems; - - int getLength() const override { return narrow(mItems.size()); } - LLSD getSingle(int index) const override + ItemResultSet(): super("items") {} + LLSD getSingleFrom(const LLPointer<LLViewerInventoryItem>& item) const override { - auto item = mItems[index]; - return llsd::map("name", item->getName(), + return llsd::map("id", item->getUUID(), + "name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), "inv_type", LLInventoryType::lookup(item->getInventoryType()), @@ -158,14 +152,14 @@ void LLInventoryListener::getItemsInfo(LLSD const &data) LLViewerInventoryItem* item = gInventory.getItem(it); if (item) { - itemresult->mItems.push_back(item); + itemresult->mVector.push_back(item); } else { LLViewerInventoryCategory *cat = gInventory.getCategory(it); if (cat) { - catresult->mCategories.push_back(cat); + catresult->mVector.push_back(cat); } } } @@ -200,8 +194,8 @@ void LLInventoryListener::getDirectDescendants(LLSD const &data) auto catresult = new CatResultSet; auto itemresult = new ItemResultSet; - catresult->mCategories = *cats; - itemresult->mItems = *items; + catresult->mVector = *cats; + itemresult->mVector = *items; response["categories"] = catresult->getKeyLength(); response["items"] = itemresult->getKeyLength(); @@ -258,8 +252,8 @@ void LLInventoryListener::collectDescendantsIf(LLSD const &data) // collectDescendentsIf() method so it doesn't steal too many cycles. gInventory.collectDescendentsIf( folder_id, - catresult->mCategories, - itemresult->mItems, + catresult->mVector, + itemresult->mVector, LLInventoryModel::EXCLUDE_TRASH, collector); @@ -267,14 +261,6 @@ void LLInventoryListener::collectDescendantsIf(LLSD const &data) response["items"] = itemresult->getKeyLength(); } -/*==========================================================================*| -void LLInventoryListener::getSingle(LLSD const& data) -{ - auto result = LL::ResultSet::getInstance(data["result"]); - sendReply(llsd::map("single", result->getSingle(data["index"])), data); -} -|*==========================================================================*/ - void LLInventoryListener::getSlice(LLSD const& data) { auto result = LL::ResultSet::getInstance(data["result"]); diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 56e5555ba5..c048b028f5 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -206,7 +206,7 @@ private: std::string mEditLandmarkTooltip; // this field holds a human-readable form of the location string, it is needed to be able to compare copy-pated value and real location std::string mHumanReadableLocation; - bool isHumanReadableLocationVisible; + bool isHumanReadableLocationVisible = true; std::string mMaturityHelpTopic; }; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index bf49f33049..51f30dd86b 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -78,8 +78,8 @@ const static std::string MULTI_LINE_PREFIX(" "); * * Note: "You" was used as an avatar names in viewers of previous versions */ -const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); -const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); +const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); +const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]|\\[\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]).*"); /** * Regular expression suitable to match names like @@ -150,6 +150,10 @@ public: void checkAndCutOffDate(std::string& time_str) { + if (time_str.size() < 10) // not enough space for a date + { + return; + } // Cuts off the "%Y/%m/%d" from string for todays timestamps. // Assume that passed string has at least "%H:%M" time format. date log_date(not_a_date_time); @@ -166,20 +170,12 @@ public: if ( days_alive == zero_days ) { - // Yep, today's so strip "%Y/%m/%d" info - ptime stripped_time(not_a_date_time); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << time_str; - mTimeStream >> stripped_time; - mTimeStream.clear(); - - time_str.clear(); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << stripped_time; - mTimeStream >> time_str; - mTimeStream.clear(); + size_t pos = time_str.find_first_of(' '); + if (pos != std::string::npos) + { + time_str.erase(0, pos + 1); + LLStringUtil::trim(time_str); + } } LL_DEBUGS("LLChatLogParser") @@ -308,16 +304,22 @@ std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate) std::string timeStr; if (withdate) { - timeStr = "[" + LLTrans::getString ("TimeYear") + "]/[" - + LLTrans::getString ("TimeMonth") + "]/[" - + LLTrans::getString ("TimeDay") + "] [" - + LLTrans::getString ("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin") + "]"; + timeStr = "[" + LLTrans::getString("TimeYear") + "]/[" + + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "] "; + } + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; } else { - timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + timeStr += "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; } LLSD substitution; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7fe5c1ece0..9b6dcc5277 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack - std::ostringstream out; - out << lua_tostring(L, -1); + std::string source_info{ lua_tostring(L, -1) }; lua_pop(L, 1); + + std::ostringstream out; const char* sep = ""; // 'where' info ends with ": " // now iterate over arbitrary args, calling Lua tostring() on each and // concatenating with separators @@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) // capture message string std::string msg{ out.str() }; // put message out there for any interested party (*koff* LLFloaterLUADebug *koff*) - LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); + LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info)); llcoro::suspend(); - return msg; + return source_info + msg; } lua_function(print_debug, "print_debug(args...): DEBUG level logging") @@ -317,7 +318,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : void LLRequireResolver::findModule() { // If mPathToResolve is absolute, this replaces mSourceDir. - auto absolutePath = (mSourceDir / mPathToResolve).u8string(); + fsyspath absolutePath(mSourceDir / mPathToResolve); // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); @@ -333,7 +334,7 @@ void LLRequireResolver::findModule() // not already cached - prep error message just in case auto fail{ - [L=L, path=mPathToResolve.u8string()]() + [L=L, path=mPathToResolve.string()]() { luaL_error(L, "could not find require('%s')", path.data()); }}; if (mPathToResolve.is_absolute()) @@ -348,10 +349,10 @@ void LLRequireResolver::findModule() { // if path is already absolute, operator/() preserves it auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); - std::string absolutePathOpt = (abspath / mPathToResolve).u8string(); + fsyspath absolutePathOpt = (abspath / mPathToResolve); if (absolutePathOpt.empty()) - luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); + luaL_error(L, "error requiring module '%s'", mPathToResolve.string().data()); if (findModuleImpl(absolutePathOpt)) return; diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index b5e494379d..dc7511b642 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -3177,14 +3177,7 @@ void LLMaterialEditor::applyToSelection() { LL_WARNS("MaterialEditor") << "Not connected to materials capable region, missing ModifyMaterialParams cap" << LL_ENDL; - // Fallback local preview. Will be removed once override systems is finished and new cap is deployed everywhere. - LLPointer<LLFetchedGLTFMaterial> mat = new LLFetchedGLTFMaterial(); - getGLTFMaterial(mat); - static const LLUUID placeholder("984e183e-7811-4b05-a502-d79c6f978a98"); - gGLTFMaterialList.addMaterial(placeholder, mat); - LLRenderMaterialFunctor mat_func(placeholder); - LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); - selected_objects->applyToTEs(&mat_func); + LLNotificationsUtil::add("MissingMaterialCaps"); } } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 49260d5c50..ef9e8d7dac 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -1022,10 +1022,6 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_NAVIGATE_COMPLETE: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL; - if(mHidingInitialLoad) - { - mHidingInitialLoad = false; - } }; break; diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 9f9564af46..5d3d8f3ba2 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -201,7 +201,6 @@ public: mStretchToFill, mMaintainAspectRatio, mHideLoading, - mHidingInitialLoad, mClearCache, mHoverTextChanged, mDecoupleTextureSize, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 5fc49b32ea..e4e70b02f9 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -3959,7 +3959,7 @@ void LLMeshRepository::notifyLoadedMeshes() } // erase from background thread - mThread->mWorkQueue.post([=]() + mThread->mWorkQueue.post([=, this]() { mThread->mSkinMap.erase(id); }); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index d864a07615..c7572c9cbf 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic<S32> sActiveHeaderRequests; + static std::atomic<S32> sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 1e8b7d39cc..ecca593d51 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -3365,7 +3365,7 @@ bool LLModelPreview::render() mFMP->childSetEnabled("upload_joints", upload_skin); } - F32 explode = (F32)mFMP->childGetValue("physics_explode").asReal(); + F32 physics_explode = (F32)mFMP->childGetValue("physics_explode").asReal(); LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview @@ -3565,7 +3565,7 @@ bool LLModelPreview::render() LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; if (decomp) { - LLMutexLock(decomp->mMutex); + LLMutexLock lock(decomp->mMutex); LLModel::Decomposition& physics = model->mPhysics; @@ -3590,12 +3590,12 @@ bool LLModelPreview::render() for (U32 i = 0; i < physics.mMesh.size(); ++i) { - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.pushMatrix(); LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; - offset *= explode; + offset *= physics_explode; gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); } @@ -3610,7 +3610,7 @@ bool LLModelPreview::render() gGL.diffuseColor4ubv(hull_colors[i].mV); LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions); - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.popMatrix(); } @@ -3625,7 +3625,8 @@ bool LLModelPreview::render() if (render_mesh) { auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - if (pass > 0){ + if (pass > 0) + { for (size_t i = 0; i < num_models; ++i) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -3685,7 +3686,7 @@ bool LLModelPreview::render() LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; if (decomp) { - LLMutexLock(decomp->mMutex); + LLMutexLock lock(decomp->mMutex); LLModel::Decomposition& physics = model->mPhysics; @@ -3805,7 +3806,8 @@ bool LLModelPreview::render() } } - for (U32 i = 0, e = static_cast<U32>(mVertexBuffer[mPreviewLOD][model].size()); i < e; ++i) + std::size_t size = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < size; ++i) { model->mSkinInfo.updateHash(); LLRenderPass::uploadMatrixPalette(mPreviewAvatar, &model->mSkinInfo); diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 483ddcdd63..bf9aa66094 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -96,6 +96,7 @@ LLNetMap::LLNetMap (const Params & p) mPopupWorldPos(0.f, 0.f, 0.f), mMouseDown(0, 0), mPanning(false), + mCentering(false), mUpdateNow(false), mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ), mObjectRawImagep(), diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index 5b8b28ebe6..9a33bcb1b9 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -38,6 +38,7 @@ #include "lluicolortable.h" #include "message.h" #include "llnotificationsutil.h" +#include "llviewercontrol.h" #include <boost/regex.hpp> LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p), @@ -133,10 +134,22 @@ std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_sta default: timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" +LLTrans::getString("TimeDay")+"]/[" - +LLTrans::getString("TimeYear")+"] [" - +LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] [" - +LLTrans::getString("TimeTimezone")+"]"; + +LLTrans::getString("TimeYear")+"] ["; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } break; } LLSD substitution; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 0d6ca1cb94..2d659cd19d 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -440,7 +440,7 @@ void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2); + mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2); if (mVertical) { mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; diff --git a/indra/newview/llpanelenvironment.cpp b/indra/newview/llpanelenvironment.cpp index be61c44b7c..d3df88b65e 100644 --- a/indra/newview/llpanelenvironment.cpp +++ b/indra/newview/llpanelenvironment.cpp @@ -48,6 +48,7 @@ #include "llappviewer.h" #include "llcallbacklist.h" +#include "llviewercontrol.h" #include "llviewerparcelmgr.h" #include "llinventorymodel.h" @@ -939,19 +940,29 @@ void LLPanelEnvironmentInfo::udpateApparentTimeOfDay() S32Hours hourofday(secondofday); S32Seconds secondofhour(secondofday - hourofday); S32Minutes minutesofhour(secondofhour); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); bool am_pm(hourofday.value() >= 12); - if (hourofday.value() < 1) - hourofday = S32Hours(12); - if (hourofday.value() > 12) - hourofday -= S32Hours(12); + if (!use_24h) + { + if (hourofday.value() < 1) + hourofday = S32Hours(12); + if (hourofday.value() > 12) + hourofday -= S32Hours(12); + } std::string lblminute(((minutesofhour.value() < 10) ? "0" : "") + LLSD(minutesofhour.value()).asString()); - mLabelApparentTime->setTextArg("[HH]", LLSD(hourofday.value()).asString()); mLabelApparentTime->setTextArg("[MM]", lblminute); - mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + if (use_24h) + { + mLabelApparentTime->setTextArg("[AP]", std::string()); + } + else + { + mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + } mLabelApparentTime->setTextArg("[PRC]", LLSD((S32)(100 * perc)).asString()); } diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 4404efff98..e1f2d7588c 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1360,7 +1360,8 @@ void LLPanelGroupMembersSubTab::activate() { if (!gdatap || !gdatap->isMemberDataComplete()) { - const U32 page_size = 50; + static LLCachedControl<bool> enable_pagination(gSavedSettings, "UseGroupMemberPagination", false); + const U32 page_size = enable_pagination() ? 50 : 0; std::string sort_column_name = mMembersList->getSortColumnName(); bool sort_descending = !mMembersList->getSortAscending(); LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID, page_size, 0, sort_column_name, sort_descending); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 41373cd7f5..7596c0eba8 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -41,6 +41,7 @@ #include "lllandmarkactions.h" #include "llparcel.h" #include "llslurl.h" +#include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -326,7 +327,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) } else { - std::string timeStr = getString("acquired_date"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquired_date") : getString("acquired_date_ampm"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 60017db51d..1c2dac6d6e 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -1307,7 +1307,7 @@ void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type showWearablesListView(); //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE - applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type)); + applyListViewFilter(EListViewItemType(LVIT_SHAPE + EListViewItemType(type))); mWearableItemsList->setMenuWearableType(type); } diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 1af585708e..955b5e7730 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -777,7 +777,7 @@ void LLPanelPrimMediaControls::draw() else if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); - alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + alpha *= llmax(lerp(1.0f, 0.0f, time / mControlFadeTime), 0.0f); if(time >= mControlFadeTime) { diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 2086706bf8..5c54a3fc27 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -754,8 +754,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key) resetData(); - LLUUID avatar_id = getAvatarId(); - bool own_profile = getSelfProfile(); mGroupList->setShowNone(!own_profile); @@ -793,7 +791,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key) if (!own_profile) { - mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : true); updateOnlineStatus(); fillRightsData(); } @@ -1214,17 +1211,6 @@ void LLPanelProfileSecondLife::changed(U32 mask) } } -// virtual, called by LLVoiceClient -void LLPanelProfileSecondLife::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) -{ - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) - { - return; - } - - mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : true); -} - void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id) { if (avatar_id.notNull()) @@ -1502,7 +1488,7 @@ bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata) } else if (item_name == "voice_call") { - return mVoiceStatus; + return LLAvatarActions::canCallTo(agent_id); } else if (item_name == "chat_history") { diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index c207a4162a..ba00e12441 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -33,7 +33,6 @@ #include "llpanel.h" #include "llpanelavatar.h" #include "llmediactrl.h" -#include "llvoiceclient.h" // class LLPanelProfileClassifieds; // class LLTabContainer; @@ -70,7 +69,6 @@ class LLViewerFetchedTexture; class LLPanelProfileSecondLife : public LLPanelProfilePropertiesProcessorTab , public LLFriendObserver - , public LLVoiceClientStatusObserver { public: LLPanelProfileSecondLife(); @@ -89,10 +87,6 @@ public: */ void changed(U32 mask) override; - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override; - void setAvatarId(const LLUUID& avatar_id) override; bool postBuild() override; @@ -203,7 +197,6 @@ private: LLHandle<LLFloater> mFloaterTexturePickerHandle; bool mHasUnsavedDescriptionChanges; - bool mVoiceStatus; bool mWaitingForImageUpload; bool mAllowPublish; bool mHideAge; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index ebfdafdfe2..8097e0ac0b 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -42,6 +42,7 @@ #include "llnotificationsutil.h" #include "lltextbox.h" #include "lltoggleablemenu.h" +#include "llviewercontrol.h" #include "llviewermenu.h" #include "lllandmarkactions.h" #include "llclipboard.h" @@ -215,8 +216,18 @@ std::string LLTeleportHistoryFlatItem::getTimestamp() // Only show timestamp for today and yesterday if(time_diff < seconds_today + seconds_in_day) { - timestamp = "[" + LLTrans::getString("TimeHour12")+"]:[" - + LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timestamp = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timestamp = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + LLTrans::getString("TimeAMPM") + "]"; + } + LLSD substitution; substitution["datetime"] = (S32) date.secondsSinceEpoch(); LLStringUtil::format(timestamp, substitution); diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 60877494e7..e65ba523f0 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -42,7 +42,7 @@ static LLPanelInjector<LLPanelVoiceDeviceSettings> t_panel_group_general("panel_voice_device_settings"); static const std::string DEFAULT_DEVICE("Default"); - +static const std::string NO_DEVICE("No Device"); LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() : LLPanel() @@ -51,12 +51,10 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() mCtrlOutputDevices = NULL; mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mDevicesUpdated = false; //obsolete mUseTuningMode = true; // grab "live" mic volume level mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - } LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() @@ -81,7 +79,7 @@ bool LLPanelVoiceDeviceSettings::postBuild() boost::bind(&LLPanelVoiceDeviceSettings::onCommitUnmute, this)); mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text"); - mLocalizedDeviceNames["No Device"] = getString("name_no_device"); + mLocalizedDeviceNames[NO_DEVICE] = getString("name_no_device"); mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device"); mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this)); @@ -115,7 +113,7 @@ void LLPanelVoiceDeviceSettings::draw() bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled(); if (voice_enabled) { - getChildView("wait_text")->setVisible( !is_in_tuning_mode && mUseTuningMode); + getChildView("wait_text")->setVisible(!is_in_tuning_mode && mUseTuningMode); getChildView("disabled_text")->setVisible(false); mUnmuteBtn->setVisible(false); } @@ -212,56 +210,29 @@ void LLPanelVoiceDeviceSettings::cancel() void LLPanelVoiceDeviceSettings::refresh() { - //grab current volume + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + + // grab current volume LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider"); + // set mic volume tuning slider based on last mic volume setting F32 current_volume = (F32)volume_slider->getValue().asReal(); - LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume); + voice_client->tuningSetMicVolume(current_volume); // Fill in popup menus - bool device_settings_available = LLVoiceClient::getInstance()->deviceSettingsAvailable(); + bool device_settings_available = voice_client->deviceSettingsAvailable(); + bool device_settings_updated = voice_client->deviceSettingsUpdated(); if (mCtrlInputDevices) { - mCtrlInputDevices->setEnabled(device_settings_available); - } - - if (mCtrlOutputDevices) - { - mCtrlOutputDevices->setEnabled(device_settings_available); - } - - getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available); - - if(!device_settings_available) - { - // The combo boxes are disabled, since we can't get the device settings from the daemon just now. - // Put the currently set default (ONLY) in the box, and select it. - if(mCtrlInputDevices) - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM); - mCtrlInputDevices->setValue(mInputDevice); - } - if(mCtrlOutputDevices) - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM); - mCtrlOutputDevices->setValue(mOutputDevice); - } - } - else if (LLVoiceClient::getInstance()->deviceSettingsUpdated()) - { - LLVoiceDeviceList::const_iterator device; - - if(mCtrlInputDevices) + if (device_settings_available && !voice_client->getCaptureDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlInputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlInputDevices->removeall(); mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - for (auto& device : devices) + for (auto& device : voice_client->getCaptureDevices()) { mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -275,16 +246,24 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlInputDevices->setEnabled(false); + mCtrlInputDevices->removeall(); + mCtrlInputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } + } - if(mCtrlOutputDevices) + if (mCtrlOutputDevices) + { + if (device_settings_available && !voice_client->getRenderDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlOutputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlOutputDevices->removeall(); mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for (auto& device : devices) + for (auto& device : voice_client->getRenderDevices()) { mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -298,7 +277,15 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlOutputDevices->setEnabled(false); + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } } + + getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available && !voice_client->getCaptureDevices().empty()); } void LLPanelVoiceDeviceSettings::initialize() diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h index 815396cbd1..d0d14c212c 100644 --- a/indra/newview/llpanelvoicedevicesettings.h +++ b/indra/newview/llpanelvoicedevicesettings.h @@ -63,7 +63,6 @@ protected: class LLComboBox *mCtrlInputDevices; class LLComboBox *mCtrlOutputDevices; class LLButton *mUnmuteBtn; - bool mDevicesUpdated; bool mUseTuningMode; std::map<std::string, std::string> mLocalizedDeviceNames; }; diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 7cbbb89313..66dcd2f7ba 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -517,6 +517,7 @@ void LLPreviewGesture::addKeys() void LLPreviewGesture::addAnimations() { LLComboBox* combo = mAnimationCombo; + LLUUID old_value = combo->getCurrentID(); combo->removeall(); @@ -565,6 +566,8 @@ void LLPreviewGesture::addAnimations() combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM); } + + combo->setCurrentByID(old_value); } @@ -1410,7 +1413,7 @@ void LLPreviewGesture::onCommitAnimation(LLUICtrl* ctrl, void* data) { // Assign the animation name LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; - if (self->mAnimationCombo->getCurrentIndex() == 0) + if (self->mAnimationCombo->getCurrentIndex() <= 0) { anim_step->mAnimName.clear(); anim_step->mAnimAssetID.setNull(); diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 1b657d9ea1..800fc7ec0c 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -441,6 +441,16 @@ void LLPreviewTexture::onFileLoadedForSave(bool success, self->getWindow()->decBusyCount(); self->mLoadingFullImage = false; } + if (!success) + { + LL_WARNS("FileSaveAs") << "Failed to download file " << *item_uuid << " for saving." + << " Is missing: " << (src_vi->isMissingAsset() ? "true" : "false") + << " Discard: " << src_vi->getDiscardLevel() + << " Raw discard: " << discard_level + << " Size: " << src_vi->getWidth() << "x" << src_vi->getHeight() + << " Has GL texture: " << (src_vi->hasGLTexture() ? "true" : "false") + << " Has saved raw image: " << (src_vi->hasSavedRawImage() ? "true" : "false") << LL_ENDL; + } } if( self && final && success ) diff --git a/indra/newview/llscrollingpanelparam.h b/indra/newview/llscrollingpanelparam.h index 3aba4e4e40..93daf22e76 100644 --- a/indra/newview/llscrollingpanelparam.h +++ b/indra/newview/llscrollingpanelparam.h @@ -81,7 +81,6 @@ public: protected: LLTimer mMouseDownTimer; // timer for how long mouse has been held down on a hint. F32 mLastHeldTime; - bool mAllowModify; LLButton* mLessBtn; LLButton* mMoreBtn; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 51da051340..d0347c7abf 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -3133,6 +3133,8 @@ void LLSelectMgr::adjustTexturesByScale(bool send_to_sim, bool stretch) F32 scale_x = 1; F32 scale_y = 1; + F32 offset_x = 0; + F32 offset_y = 0; for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) { @@ -3149,6 +3151,21 @@ void LLSelectMgr::adjustTexturesByScale(bool send_to_sim, bool stretch) scale_y = scale_ratio.mV[t_axis] * object_scale.mV[t_axis]; } material->mTextureTransform[i].mScale.set(scale_x, scale_y); + + LLVector2 scales = selectNode->mGLTFScales[te_num][i]; + LLVector2 offsets = selectNode->mGLTFOffsets[te_num][i]; + F64 int_part = 0; + offset_x = (F32)modf((offsets[VX] + (scales[VX] - scale_x)) / 2, &int_part); + if (offset_x < 0) + { + offset_x++; + } + offset_y = (F32)modf((offsets[VY] + (scales[VY] - scale_y)) / 2, &int_part); + if (offset_y < 0) + { + offset_y++; + } + material->mTextureTransform[i].mOffset.set(offset_x, offset_y); } LLFetchedGLTFMaterial* render_mat = (LLFetchedGLTFMaterial*)tep->getGLTFRenderMaterial(); @@ -6888,6 +6905,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) { mTextureScaleRatios.clear(); mGLTFScaleRatios.clear(); + mGLTFScales.clear(); + mGLTFOffsets.clear(); if (mObject.notNull()) { @@ -6928,6 +6947,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) F32 scale_x = 1; F32 scale_y = 1; std::vector<LLVector3> material_v_vec; + std::vector<LLVector2> material_scales_vec; + std::vector<LLVector2> material_offset_vec; for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) { if (material) @@ -6935,12 +6956,16 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) LLGLTFMaterial::TextureTransform& transform = material->mTextureTransform[i]; scale_x = transform.mScale[VX]; scale_y = transform.mScale[VY]; + material_scales_vec.push_back(transform.mScale); + material_offset_vec.push_back(transform.mOffset); } else { // Not having an override doesn't mean that there is no material scale_x = 1; scale_y = 1; + material_scales_vec.emplace_back(scale_x, scale_y); + material_offset_vec.emplace_back(0.f, 0.f); } if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR) @@ -6956,6 +6981,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) material_v_vec.push_back(material_v); } mGLTFScaleRatios.push_back(material_v_vec); + mGLTFScales.push_back(material_scales_vec); + mGLTFOffsets.push_back(material_offset_vec); } } } diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index f8242675dc..f2b1e691b5 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -240,6 +240,8 @@ public: gltf_materials_vec_t mSavedGLTFOverrideMaterials; std::vector<LLVector3> mTextureScaleRatios; std::vector< std::vector<LLVector3> > mGLTFScaleRatios; + std::vector< std::vector<LLVector2> > mGLTFScales; + std::vector< std::vector<LLVector2> > mGLTFOffsets; std::vector<LLVector3> mSilhouetteVertices; // array of vertices to render silhouette of object std::vector<LLVector3> mSilhouetteNormals; // array of normals to render silhouette of object bool mSilhouetteExists; // need to generate silhouette? diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 34b8535c84..fee0bbcda4 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -671,7 +671,8 @@ void LLSettingsVOSky::updateSettings() // After some A/B comparison of relesae vs EEP, tweak to allow strength to fall below 2 // at night, for better match. (mSceneLightStrength is a divisor, so lower value means brighter // local lights) - F32 sun_dynamic_range = llmax(gSavedSettings.getF32("RenderSunDynamicRange"), 0.0001f); + LLCachedControl<F32> sdr(gSavedSettings, "RenderSunDynamicRange", 1.f); + F32 sun_dynamic_range = llmax(sdr(), 0.0001f); mSceneLightStrength = 2.0f * (0.75f + sun_dynamic_range * dp); gSky.setSunAndMoonDirectionsCFR(sun_direction, moon_direction); diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index fccf745a74..385e4314a9 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -483,7 +483,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } else { - std::string timeStr = getString("acquiredDate"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquiredDate") : getString("acquiredDateAMPM"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp index 82caa14433..66fbe0029a 100644 --- a/indra/newview/llsky.cpp +++ b/indra/newview/llsky.cpp @@ -39,7 +39,7 @@ // linden library includes #include "llerror.h" #include "llmath.h" -#include "math.h" +#include <cmath> #include "v4color.h" #include "llviewerobjectlist.h" diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h index 32599dcee2..d06f181357 100644 --- a/indra/newview/llsky.h +++ b/indra/newview/llsky.h @@ -28,7 +28,6 @@ #define LL_LLSKY_H #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp index e51aeb6080..09c57eaf1d 100644 --- a/indra/newview/llsprite.cpp +++ b/indra/newview/llsprite.cpp @@ -37,7 +37,7 @@ #include "llsprite.h" -#include "math.h" +#include <cmath> #include "lldrawable.h" #include "llface.h" diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h index 44439bd30c..d6e8e37ec9 100644 --- a/indra/newview/llsprite.h +++ b/indra/newview/llsprite.h @@ -27,8 +27,6 @@ #ifndef LL_LLSPRITE_H #define LL_LLSPRITE_H -////#include "vmath.h" -//#include "llmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 83d6b57fa4..dd8c6d989e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -204,6 +204,7 @@ #include "threadpool.h" #include "llperfstats.h" +#include "rlvhandler.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -875,6 +876,8 @@ bool idle_startup() return false; } + RlvHandler::setEnabled(gSavedSettings.get<bool>(Rlv::Settings::Main)); + // reset the values that could have come in from a slurl // DEV-42215: Make sure they're not empty -- gUserCredential // might already have been set from gSavedSettings, and it's too bad diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index 10a104730b..fc72ab7db7 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -27,7 +27,6 @@ #ifndef LL_LLSURFACE_H #define LL_LLSURFACE_H -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 8ccde74c93..a858f92d8e 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -31,6 +31,8 @@ // library includes #include "llglslshader.h" #include "llrendertarget.h" +#include "llrender2dutils.h" +#include "llshadermgr.h" #include "llvertexbuffer.h" // newview includes @@ -38,18 +40,31 @@ #include "llsurface.h" #include "llsurfacepatch.h" #include "llviewercamera.h" +#include "llviewercontrol.h" #include "llviewerregion.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -// static -bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +namespace +{ +#ifdef SHOW_ASSERT +void check_tex(const LLViewerTexture& tex) { llassert(tex.getComponents() == 3); llassert(tex.getWidth() > 0 && tex.getHeight() > 0); llassert(tex.getWidth() == tex.getHeight()); llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); +} +#else +#define check_tex(tex) +#endif +} // namespace + +// static +bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +{ + check_tex(tex); const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -76,8 +91,8 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& // Bind the debug shader and render terrain to tex // Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake LLRenderTarget scratch_target; - const S32 dim = llmin(tex.getWidth(), tex.getHeight()); - scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, LLTexUnit::eTextureMipGeneration::TMG_NONE); if (!scratch_target.isComplete()) { @@ -104,6 +119,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const F32 region_half_width = region_width / 2.0f; const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE; LLViewerCamera camera; + // TODO: Huh... I just realized this view vector is not completely vertical const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent(); const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center; camera.lookAt(camera_origin, region_center, LLVector3::y_axis); @@ -237,6 +253,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); LLPointer<LLViewerTexture> alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D); + // TODO: Consider using LLGLSLShader::bindTexture gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture); gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); @@ -250,6 +267,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const U32 vertex_offset = n * patch_index; llassert(index_offset + ni <= region_indices); llassert(vertex_offset + n <= region_vertices); + // TODO: Try a single big drawRange and see if that still works buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset); } } @@ -269,7 +287,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& gGL.flush(); LLVertexBuffer::unbind(); // Final step: Copy the output to the terrain paintmap - const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim); + const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, tex.getWidth(), tex.getHeight()); if (!success) { LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL; @@ -283,3 +301,687 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } + +// *TODO: Decide when to apply the paint queue - ideally once per frame per region +// Applies paints and then clears the paint queue +// *NOTE The paint queue is also cleared when setting the paintmap texture +void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +{ + if (queue.empty()) { return; } + + check_tex(tex); + + gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + + // glTexSubImage2D replaces all pixels in the rectangular region. That + // makes it unsuitable for alpha. + llassert(queue.getComponents() == LLTerrainPaint::RGB); + constexpr GLenum pixformat = GL_RGB; + + const std::vector<LLTerrainPaint::ptr_t>& queue_list = queue.get(); + for (size_t i = 0; i < queue_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + queue.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint = queue_list[i]; + + if (paint->mData.empty()) { continue; } + constexpr GLint level = 0; + if ((paint->mStartX >= tex.getWidth() - 1) || (paint->mStartY >= tex.getHeight() - 1)) { continue; } + constexpr GLint miplevel = 0; + const S32 x_offset = paint->mStartX; + const S32 y_offset = paint->mStartY; + const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); + const U8* pixels = paint->mData.data(); + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + // *TODO: Performance suggestion: Use the sub-image utility function + // that LLImageGL::setSubImage uses to split texture updates into + // lines, if that's faster. + glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); + stop_glerror(); + } + + // Generating mipmaps at the end... + glGenerateMipmap(GL_TEXTURE_2D); + stop_glerror(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + queue.clear(); +} + +namespace +{ + +// A general-purpose vertex buffer of a quad for stamping textures on the z=0 +// plane. +// *NOTE: Because we know the vertex XY coordinates go from 0 to 1 +// pre-transform, UVs can be calculated from the vertices +LLVertexBuffer* get_paint_triangle_buffer() +{ + static LLPointer<LLVertexBuffer> buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); + static bool initialized = false; + if (!initialized) + { + // Two triangles forming a square from (0,0) to (1,1) + buf->allocateBuffer(/*vertices =*/ 4, /*indices =*/ 6); + LLStrider<U16> indices; + LLStrider<LLVector3> vertices; + buf->getVertexStrider(vertices); + buf->getIndexStrider(indices); + // y + // 2....3 + // ^ . . + // | 0....1 + // | + // -------> x + // + // triangle 1: 0,1,2 + // triangle 2: 1,3,2 + (*(vertices++)).set(0.0f, 0.0f, 0.0f); + (*(vertices++)).set(1.0f, 0.0f, 0.0f); + (*(vertices++)).set(0.0f, 1.0f, 0.0f); + (*(vertices++)).set(1.0f, 1.0f, 0.0f); + *(indices++) = 0; + *(indices++) = 1; + *(indices++) = 2; + *(indices++) = 1; + *(indices++) = 3; + *(indices++) = 2; + buf->unmapBuffer(); + } + return buf.get(); +} + +}; + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) +{ + check_tex(tex); + llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glm::mat4 model; + { + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each RGBA paint to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector<LLTerrainPaint::ptr_t>& queue_in_list = queue_in.get(); + for (size_t i = 0; i < queue_in_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for paint operations (this + // could change in the future). + queue_in.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i]; + + // Modelview matrix for the current paint + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + glm::mat4 model; + { + model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + // Generate temporary stamp texture from paint contents. + // Our stamp image needs to be a power of two. + // Because the paint data may not cover a whole power-of-two region, + // allocate a bigger 2x2 image if needed, but set the image data later + // for a subset of the image. + // Pixel data outside this subset is left undefined. We will use + // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the + // image we care about. + const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX)))); + const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY)))); + LLPointer<LLImageGL> stamp_image; + { + // Create image object (dimensions not yet initialized in GL) + U32 stamp_tex_name; + LLImageGL::generateTextures(1, &stamp_tex_name); + const U32 components = paint_in->mComponents; + constexpr LLGLenum target = GL_TEXTURE_2D; + const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8; + const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB; + constexpr LLGLenum type = GL_UNSIGNED_BYTE; + stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP); + // Nearest-neighbor filtering to reduce surprises + stamp_image->setFilteringOption(LLTexUnit::TFO_POINT); + + // Initialize the image dimensions in GL + constexpr U8* undefined_data_for_now = nullptr; + gGL.getTexUnit(0)->bind(stamp_image, false, true); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + stamp_image->setSize(width_rounded, height_rounded, components); + stamp_image->setDiscardLevel(0); + + // Manually set a subset of the image in GL + const U8* data = paint_in->mData.data(); + const S32 data_width = paint_in->mWidthX; + const S32 data_height = paint_in->mWidthY; + constexpr S32 origin = 0; + // width = data_width; height = data_height. i.e.: Copy the full + // contents of data into the image. + stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height); + } + + // Apply ("stamp") the paint to the render target + { + shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image); + const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded); + const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>(); + { + paint_out->mStartX = paint_in->mStartX; + paint_out->mStartY = paint_in->mStartY; + paint_out->mWidthX = paint_in->mWidthX; + paint_out->mWidthY = paint_in->mWidthY; + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later + paint_out->mComponents = LLTerrainPaint::RGB; +#ifdef SHOW_ASSERT + paint_out->assert_confined_to(tex); +#endif + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) +{ + check_tex(tex); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glm::mat4 model; + { + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each brush stroke to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector<LLTerrainBrush::ptr_t>& brush_list = queue_in.get(); + for (size_t i = 0; i < brush_list.size(); ++i) + { + const LLTerrainBrush::ptr_t& brush_in = brush_list[i]; + + // Modelview matrix for the current brush + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + // *NOTE: Brush path information is in region space. It will need to be + // converted to paintmap pixel space before it makes sense. + F32 brush_width_x; + F32 brush_width_y; + F32 brush_start_x; + F32 brush_start_y; + { + F32 min_x = brush_in->mPath[0].mV[VX]; + F32 max_x = min_x; + F32 min_y = brush_in->mPath[0].mV[VY]; + F32 max_y = min_y; + for (size_t i = 1; i < brush_in->mPath.size(); ++i) + { + const F32 x = brush_in->mPath[i].mV[VX]; + const F32 y = brush_in->mPath[i].mV[VY]; + min_x = llmin(min_x, x); + max_x = llmax(max_x, x); + min_y = llmin(min_y, y); + max_y = llmax(max_y, y); + } + brush_width_x = brush_in->mBrushSize + (max_x - min_x); + brush_width_y = brush_in->mBrushSize + (max_y - min_y); + brush_start_x = min_x - (brush_in->mBrushSize / 2.0f); + brush_start_y = min_y - (brush_in->mBrushSize / 2.0f); + // Convert brush path information to paintmap pixel space from region + // space. + brush_width_x *= tex.getWidth() / region.getWidth(); + brush_width_y *= tex.getHeight() / region.getWidth(); + brush_start_x *= tex.getWidth() / region.getWidth(); + brush_start_y *= tex.getHeight() / region.getWidth(); + } + glm::mat4 model; + { + model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f)); + model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + // Apply the "brush" to the render target + { + // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>(); + { + paint_out->mStartX = U16(floor(brush_start_x)); + paint_out->mStartY = U16(floor(brush_start_y)); + const F32 dX = brush_start_x - F32(paint_out->mStartX); + const F32 dY = brush_start_y - F32(paint_out->mStartY); + paint_out->mWidthX = U16(ceil(brush_width_x + dX)); + paint_out->mWidthY = U16(ceil(brush_width_y + dY)); + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later + paint_out->mComponents = LLTerrainPaint::RGB; + // The brush strokes are expected to sometimes partially venture + // outside of the paintmap bounds. + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +template<typename T> +LLTerrainQueue<T>::LLTerrainQueue(LLTerrainQueue<T>& other) +{ + *this = other; +} + +template<typename T> +LLTerrainQueue<T>& LLTerrainQueue<T>::operator=(LLTerrainQueue<T>& other) +{ + mList = other.mList; + return *this; +} + +template<typename T> +bool LLTerrainQueue<T>::enqueue(std::shared_ptr<T>& t, bool dry_run) +{ + if (!dry_run) { mList.push_back(t); } + return true; +} + +template<typename T> +bool LLTerrainQueue<T>::enqueue(std::vector<std::shared_ptr<T>>& list) +{ + constexpr bool dry_run = true; + for (auto& t : list) + { + if (!enqueue(t, dry_run)) { return false; } + } + for (auto& t : list) + { + enqueue(t); + } + return true; +} + +template<typename T> +size_t LLTerrainQueue<T>::size() const +{ + return mList.size(); +} + +template<typename T> +bool LLTerrainQueue<T>::empty() const +{ + return mList.empty(); +} + +template<typename T> +void LLTerrainQueue<T>::clear() +{ + mList.clear(); +} + +void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const +{ + llassert(mStartX >= 0 && mStartX < tex.getWidth()); + llassert(mStartY >= 0 && mStartY < tex.getHeight()); + llassert(mWidthX <= tex.getWidth() - mStartX); + llassert(mWidthY <= tex.getHeight() - mStartY); +} + +void LLTerrainPaint::confine_to(const LLTexture& tex) +{ + mStartX = llmax(mStartX, 0); + mStartY = llmax(mStartY, 0); + mWidthX = llmin(mWidthX, tex.getWidth() - mStartX); + mWidthY = llmin(mWidthY, tex.getHeight() - mStartY); + assert_confined_to(tex); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) +: mComponents(components) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other) +: LLTerrainQueue<LLTerrainPaint>(other) +, mComponents(other.mComponents) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other) +{ + LLTerrainQueue<LLTerrainPaint>::operator=(other); + mComponents = other.mComponents; + return *this; +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) +{ + llassert(paint); + if (!paint) { return false; } + + // The paint struct should be pre-validated before this code is reached. + llassert(!paint->mData.empty()); + // The internal paint map image is currently 8 bits, so that's the maximum + // allowed bit depth. + llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); + llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY)); + llassert(paint->mWidthX > 0); + llassert(paint->mWidthY > 0); +#ifdef SHOW_ASSERT + static LLCachedControl<U32> max_texture_width(gSavedSettings, "RenderMaxTextureResolution", 2048); +#endif + llassert(paint->mWidthX <= max_texture_width); + llassert(paint->mWidthY <= max_texture_width); + llassert(paint->mStartX < max_texture_width); + llassert(paint->mStartY < max_texture_width); + + return LLTerrainQueue<LLTerrainPaint>::enqueue(paint, dry_run); +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue) +{ + return LLTerrainQueue<LLTerrainPaint>::enqueue(queue.mList); +} + +void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) +{ + llassert(target_bit_depth > 0 && target_bit_depth <= 8); + llassert(index < mList.size()); + + LLTerrainPaint::ptr_t& paint = mList[index]; + if (paint->mBitDepth == target_bit_depth) { return; } + + const F32 old_bit_max = F32((1 << paint->mBitDepth) - 1); + const F32 new_bit_max = F32((1 << target_bit_depth) - 1); + const F32 bit_conversion_factor = new_bit_max / old_bit_max; + + for (U8& color : paint->mData) + { + color = (U8)llround(F32(color) * bit_conversion_factor); + } + + paint->mBitDepth = target_bit_depth; +} + +LLTerrainBrushQueue::LLTerrainBrushQueue() +: LLTerrainQueue<LLTerrainBrush>() +{ +} + +LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other) +: LLTerrainQueue<LLTerrainBrush>(other) +{ +} + +LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other) +{ + LLTerrainQueue<LLTerrainBrush>::operator=(other); + return *this; +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run) +{ + llassert(brush->mBrushSize > 0); + llassert(!brush->mPath.empty()); + llassert(brush->mPathOffset < brush->mPath.size()); + llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error + return LLTerrainQueue<LLTerrainBrush>::enqueue(brush, dry_run); +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) +{ + return LLTerrainQueue<LLTerrainBrush>::enqueue(queue.mList); +} + +template class LLTerrainQueue<LLTerrainPaint>; +template class LLTerrainQueue<LLTerrainBrush>; diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 66827862c5..d4859b747b 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,9 +26,20 @@ #pragma once +#include <memory> +#include <vector> + +#include "stdtypes.h" +#include "v2math.h" + +class LLTexture; class LLViewerRegion; class LLViewerTexture; +class LLTerrainPaintQueue; +class LLTerrainBrushQueue; +// TODO: Terrain painting across regions. Assuming painting is confined to one +// region for now. class LLTerrainPaintMap { public: @@ -39,4 +50,121 @@ public: // to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP. // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + + // This operation clears the queue + // TODO: Decide if clearing the queue is needed - seems inconsistent + static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); + // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke) + static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in); +}; + +template<typename T> +class LLTerrainQueue +{ +public: + LLTerrainQueue() = default; + LLTerrainQueue(LLTerrainQueue<T>& other); + LLTerrainQueue& operator=(LLTerrainQueue<T>& other); + + bool enqueue(std::shared_ptr<T>& t, bool dry_run = false); + size_t size() const; + bool empty() const; + void clear(); + + const std::vector<std::shared_ptr<T>>& get() const { return mList; } + +protected: + bool enqueue(std::vector<std::shared_ptr<T>>& list); + + std::vector<std::shared_ptr<T>> mList; +}; + +// Enqueued paint operations, in texture coordinates. +// mData is always RGB or RGBA (determined by mComponents), with each U8 +// storing one color with a max value of (1 >> mBitDepth) - 1 +struct LLTerrainPaint +{ + using ptr_t = std::shared_ptr<LLTerrainPaint>; + + U16 mStartX; + U16 mStartY; + U16 mWidthX; + U16 mWidthY; + U8 mBitDepth; + U8 mComponents; + static constexpr U8 RGB = 3; + static constexpr U8 RGBA = 4; + std::vector<U8> mData; + + // Asserts that this paint's start/width fit within the bounds of the + // provided texture dimensions. + void assert_confined_to(const LLTexture& tex) const; + // Confines this paint's start/width so it fits within the bounds of the + // provided texture dimensions. + // Does not allocate mData. + void confine_to(const LLTexture& tex); +}; + +class LLTerrainPaintQueue : public LLTerrainQueue<LLTerrainPaint> +{ +public: + LLTerrainPaintQueue() = delete; + // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) + LLTerrainPaintQueue(U8 components); + LLTerrainPaintQueue(LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other); + + bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); + bool enqueue(LLTerrainPaintQueue& queue); + + U8 getComponents() const { return mComponents; } + // Convert mBitDepth for the LLTerrainPaint in the queue at index + // If mBitDepth is already equal to target_bit_depth, no conversion takes + // place. + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + void convertBitDepths(size_t index, U8 target_bit_depth); + +private: + U8 mComponents; +}; + +struct LLTerrainBrush +{ + using ptr_t = std::shared_ptr<LLTerrainBrush>; + + // Width of the brush in region space. The brush is a square texture with + // alpha. + F32 mBrushSize; + // Brush path points in region space, excluding the vertical axis, which + // does not contribute to the paint map. + std::vector<LLVector2> mPath; + // Offset of the brush path to actually start drawing at. An offset of 0 + // indicates that a brush stroke has just started (i.e. the user just + // pressed down the mouse button). An offset greater than 0 indicates the + // continuation of a brush stroke. Skipped entries in mPath are not drawn + // directly, but are used for stroke orientation and path interpolation. + // TODO: For the initial implementation, mPathOffset will be 0 and mPath + // will be of length of at most 1, leading to discontinuous paint paths. + // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a + // stroke with linear interpolation. It is unlikely that we will implement + // anything more sophisticated than that for now. + U8 mPathOffset; + // Indicates if this is the end of the brush stroke. Can occur if the mouse + // button is lifted, or if the mouse temporarily stops while held down. + bool mPathEnd; +}; + +class LLTerrainBrushQueue : public LLTerrainQueue<LLTerrainBrush> +{ +public: + LLTerrainBrushQueue(); + LLTerrainBrushQueue(LLTerrainBrushQueue& other); + LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other); + + bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false); + bool enqueue(LLTerrainBrushQueue& queue); }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 69c40066b4..7b6f591254 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2090,7 +2090,7 @@ bool LLTextureFetchWorker::deleteOK() // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { - if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) + if (!mFetcher->mTextureCache || mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) { mCacheReadHandle = LLTextureCache::nullHandle(); } @@ -2101,7 +2101,7 @@ bool LLTextureFetchWorker::deleteOK() } if (mCacheWriteHandle != LLTextureCache::nullHandle()) { - if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) + if (!mFetcher->mTextureCache || mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) { mCacheWriteHandle = LLTextureCache::nullHandle(); } @@ -2832,7 +2832,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { LL_PROFILE_ZONE_SCOPED; - mRequestQueue.tryPost([=]() + mRequestQueue.tryPost([=, this]() { LLTextureFetchWorker* worker = getWorker(id); if (worker) @@ -3523,8 +3523,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) //if (! gViewerAssetStatsThread1) // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); + static std::atomic<bool> reporting_started(false); + static std::atomic<S32> report_sequence(0); // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 3c3440d41a..95653dc19b 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -87,10 +87,21 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi std::string timeStr = "[" + LLTrans::getString("TimeWeek") + "], [" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] [" - + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString("TimeMin") + "] [" - + LLTrans::getString("TimeTimezone") + "]"; + + LLTrans::getString("TimeYear") + "] ["; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } const LLDate timeStamp = notification->getDate(); LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate(); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 166542324d..647210b7e6 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -306,7 +306,7 @@ public: if (tokens.size() < 1) return false; LLVector3 coords(128, 128, 0); - if (tokens.size() <= 4) + if (tokens.size() >= 3) // Require at least [1] and [2] { coords = LLVector3((F32)tokens[1].asReal(), (F32)tokens[2].asReal(), diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 865d7fd442..184c0e7d8b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -76,6 +76,7 @@ #include "llslurl.h" #include "llstartup.h" #include "llperfstats.h" +#include "rlvcommon.h" // Third party library includes #include <boost/algorithm/string.hpp> @@ -910,6 +911,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); { + setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged); const char* transform_suffixes[] = { "ScaleU", @@ -932,6 +934,8 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "TerrainPaintBitDepth", handleSetShaderChanged); setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); + + setting_setup_signal_listener(gSavedSettings, Rlv::Settings::TopLevelMenu, Rlv::Util::menuToggleVisible); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 1f4502323c..b0e07d8ea3 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -79,6 +79,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewershadermgr.h" +#include "llviewerstats.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" #include "llvoavatarself.h" @@ -472,6 +473,35 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) // true = minimized, do not show/update the TP screen. HB update_tp_display(true); } + + // Run texture subsystem to discard memory while backgrounded + if (!gNonInteractive) + { + LL_PROFILE_ZONE_NAMED("Update Images"); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Class"); + LLViewerTexture::updateClass(); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Image Update Bump"); + gBumpImageList.updateImages(); // must be called before gTextureList version so that it's textures are thrown out first. + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List"); + F32 max_image_decode_time = 0.050f * gFrameIntervalSeconds.value(); // 50 ms/second decode time + max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f); // min 2ms/frame, max 5ms/frame) + gTextureList.updateImages(max_image_decode_time); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("GLTF Materials Cleanup"); + // remove dead gltf materials + gGLTFMaterialList.flushMaterials(); + } + } return; } @@ -1085,6 +1115,9 @@ void getProfileStatsContext(boost::json::object& stats) context.emplace("parcelid", parcel->getLocalID()); } context.emplace("time", LLDate::now().toHTTPDateString("%Y-%m-%dT%H:%M:%S")); + + // supplement with stats packet + stats.emplace("stats", LlsdToJson(capture_viewer_stats(true))); } std::string getProfileStatsFilename() diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 9d9961d51c..b2a7d875ab 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -176,6 +176,7 @@ #include "llpreviewtexture.h" #include "llscriptfloater.h" #include "llsyswellwindow.h" +#include "rlvfloaters.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -483,6 +484,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionDebugConsole>); LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionInfo>); LLFloaterReg::add("region_restarting", "floater_region_restarting.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionRestarting>); + LLFloaterReg::add("rlv_console", "floater_rlv_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<Rlv::FloaterConsole>); LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebug>); LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebugOutput>); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 92db598410..9633e54f29 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2903,14 +2903,14 @@ void LLViewerMediaImpl::update() media_tex->ref(); main_queue->postTo( mTexUpdateQueue, // Worker thread queue - [=]() // work done on update worker thread + [=, this]() // work done on update worker thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); #endif doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true); }, - [=]() // callback to main thread + [=, this]() // callback to main thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 39aa85beea..68c38c3692 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -117,6 +117,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "lltoolselectland.h" +#include "llterrainpaintmap.h" #include "lltrans.h" #include "llviewerdisplay.h" //for gWindowResized #include "llviewergenericmessage.h" @@ -151,6 +152,7 @@ #include "llviewershadermgr.h" #include "gltfscenemanager.h" #include "gltf/asset.h" +#include "rlvcommon.h" using namespace LLAvatarAppearanceDefines; @@ -1428,16 +1430,21 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t return false; } + // This calls gLocalTerrainMaterials.setPaintType + // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first + // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack. + // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime. + gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); + U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution"); // Ensure a reasonable image size of power two const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); dim = llclamp(dim, 16, max_resolution); dim = 1 << U32(std::ceil(std::log2(dim))); + // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example) LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3); LLPointer<LLViewerTexture> tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); - // This calls gLocalTerrainMaterials.setPaintType - gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); // If baking the paintmap failed, set the paintmap to nullptr. This // causes LLDrawPoolTerrain to use a blank paintmap instead. if (!success) { tex = nullptr; } @@ -1447,6 +1454,92 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t } }; +class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerTexture* tex = gLocalTerrainMaterials.getPaintMap(); + if (!tex) + { + LL_WARNS() << "No local paint map available to edit" << LL_ENDL; + return false; + } + + LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue(); + LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); + + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL; + return false; + } + // TODO: Create the brush + // Just a dab for now + LLTerrainBrush::ptr_t brush = std::make_shared<LLTerrainBrush>(); + brush->mBrushSize = 16.0f; + brush->mPath.emplace_back(17.0f, 17.0f); + brush->mPathOffset = 0; + brush->mPathEnd = true; + brush_queue.enqueue(brush); + LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue); + //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit) + + // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires) +#if 0 + // Enqueue a paint + // Modifies a subsection of the region paintmap with the material in + // the last slot. + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + LLTerrainPaint::ptr_t paint = std::make_shared<LLTerrainPaint>(); + const U16 width = 33; + paint->mStartX = 1; + paint->mStartY = 1; + paint->mWidthX = width; + paint->mWidthY = width; + constexpr U8 bit_depth = 5; + paint->mBitDepth = bit_depth; + constexpr U8 max_value = (1 << bit_depth) - 1; + const size_t pixel_count = width * width; + const U8 components = LLTerrainPaint::RGBA; + paint->mComponents = components; + paint->mData.resize(components * pixel_count); + for (size_t h = 0; h < paint->mWidthY; ++h) + { + for (size_t w = 0; w < paint->mWidthX; ++w) + { + const size_t pixel = (h * paint->mWidthX) + w; + // Solid blue color + paint->mData[(components*pixel) + components - 2] = max_value; // blue + //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise + //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2))); + //paint->mData[(components*pixel) + components - 1] = alpha; // alpha + // Alpha "frame" + const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1); + const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) || + ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0)); + const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill)); + paint->mData[(components*pixel) + components - 1] = alpha; // alpha + } + } + paint_request_queue.enqueue(paint); +#endif + + // Apply the paint queues ad-hoc right here for now. + // *TODO: Eventually the paint queue(s) should be applied at a + // predictable time in the viewer frame loop. + // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified! + LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue); + LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue(); + paint_map_queue.enqueue(paint_send_queue); + LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue); + + return true; + } +}; + class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -6280,6 +6373,8 @@ void show_debug_menus() gMenuBarView->setItemVisible("Advanced", debug); // gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden + Rlv::Util::menuToggleVisible(); + gMenuBarView->setItemVisible("Debug", qamode); gMenuBarView->setItemEnabled("Debug", qamode); @@ -7713,15 +7808,29 @@ class LLAvatarSendIM : public view_listener_t class LLAvatarCall : public view_listener_t { + static LLVOAvatar* findAvatar() + { + return find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + } + bool handleEvent(const LLSD& userdata) { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) + if (LLVOAvatar* avatar = findAvatar()) { LLAvatarActions::startCall(avatar->getID()); } return true; } + +public: + static bool isAvailable() + { + if (LLVOAvatar* avatar = findAvatar()) + { + return LLAvatarActions::canCallTo(avatar->getID()); + } + return LLAvatarActions::canCall(); + } }; namespace @@ -9624,8 +9733,6 @@ void initialize_menus() registrar.add("Agent.ToggleMicrophone", boost::bind(&LLAgent::toggleMicrophone, _2), cb_info::UNTRUSTED_BLOCK); enable.add("Agent.IsMicrophoneOn", boost::bind(&LLAgent::isMicrophoneOn, _2)); enable.add("Agent.IsActionAllowed", boost::bind(&LLAgent::isActionAllowed, _2)); - registrar.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar), cb_info::UNTRUSTED_BLOCK); - registrar.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar), cb_info::UNTRUSTED_BLOCK); // File menu init_menu_file(); @@ -9810,6 +9917,7 @@ void initialize_menus() // Develop > Terrain view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap"); + view_listener_t::addMenu(new LLAdvancedTerrainEditLocalPaintMap(), "Advanced.TerrainEditLocalPaintMap"); view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap"); // Advanced > UI @@ -9981,7 +10089,7 @@ void initialize_menus() registrar.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector)); view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call", cb_info::UNTRUSTED_BLOCK); - enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); + enable.add("Avatar.EnableCall", boost::bind(&LLAvatarCall::isAvailable)); view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse", cb_info::UNTRUSTED_THROTTLE); view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile"); view_listener_t::addMenu(new LLAvatarTogglePicks(), "Avatar.TogglePicks"); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 2a871a24af..14c9c53a9c 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -137,10 +137,10 @@ std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ; void LLFilePickerThread::getFile() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) // Todo: get rid of LLFilePickerThread and make this modeless start(); -#elif LL_DARWIN +#elif LL_DARWIN && !LL_NFD runModeless(); #else run(); @@ -150,7 +150,7 @@ void LLFilePickerThread::getFile() //virtual void LLFilePickerThread::run() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) bool blocking = false; #else bool blocking = true; // modal diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index da019d60d8..d722851851 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -118,6 +118,8 @@ #include "llpanelplaceprofile.h" #include "llviewerregion.h" #include "llfloaterregionrestarting.h" +#include "rlvactions.h" +#include "rlvhandler.h" #include "llnotificationmanager.h" // #include "llexperiencecache.h" @@ -2137,6 +2139,21 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) EInstantMessage dialog = (EInstantMessage)d; LLHost sender = msg->getSender(); + LLSD metadata; + if (msg->getNumberOfBlocksFast(_PREHASH_MetaData) > 0) + { + S32 metadata_size = msg->getSizeFast(_PREHASH_MetaData, 0, _PREHASH_Data); + std::string metadata_buffer; + metadata_buffer.resize(metadata_size, 0); + + msg->getBinaryDataFast(_PREHASH_MetaData, _PREHASH_Data, &metadata_buffer[0], metadata_size, 0, metadata_size ); + std::stringstream metadata_stream(metadata_buffer); + if (LLSDSerialize::fromBinary(metadata, metadata_stream, metadata_size) == LLSDParser::PARSE_FAILURE) + { + metadata.clear(); + } + } + LLIMProcessing::processNewMessage(from_id, from_group, to_id, @@ -2151,7 +2168,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) position, binary_bucket, binary_bucket_size, - sender); + sender, + metadata); } void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id) @@ -2382,15 +2400,16 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) } bool is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); + bool show_script_chat_particles = chat.mSourceType == CHAT_SOURCE_OBJECT + && chat.mChatType != CHAT_TYPE_DEBUG_MSG + && gSavedSettings.getBOOL("EffectScriptChatParticles"); chatter = gObjectList.findObject(from_id); if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); // Make swirly things only for talking objects. (not script debug messages, though) - if (chat.mSourceType == CHAT_SOURCE_OBJECT - && chat.mChatType != CHAT_TYPE_DEBUG_MSG - && gSavedSettings.getBOOL("EffectScriptChatParticles") ) + if (show_script_chat_particles && (!RlvActions::isRlvEnabled() || CHAT_TYPE_OWNER != chat.mChatType) ) { LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); psc->setSourceObject(chatter); @@ -2482,8 +2501,25 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) case CHAT_TYPE_SHOUT: prefix += LLTrans::getString("shout") + " "; break; - case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: + if (RlvActions::isRlvEnabled()) + { + if (RlvHandler::instance().handleSimulatorChat(mesg, chat, chatter)) + { + break; + } + else if (show_script_chat_particles) + { + LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); + psc->setSourceObject(chatter); + psc->setColor(color); + //We set the particles to be owned by the object's owner, + //just in case they should be muted by the mute list + psc->setOwnerUUID(owner_id); + LLViewerPartSim::getInstance()->addPartSource(psc); + } + } + case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_NORMAL: case CHAT_TYPE_DIRECT: break; @@ -6623,7 +6659,6 @@ void process_initiate_download(LLMessageSystem* msg, void**) (void**)new std::string(viewer_filename)); } - void process_script_teleport_request(LLMessageSystem* msg, void**) { if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; @@ -6637,6 +6672,11 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) msg->getString("Data", "SimName", sim_name); msg->getVector3("Data", "SimPosition", pos); msg->getVector3("Data", "LookAt", look_at); + U32 flags = (BEACON_SHOW_MAP | BEACON_FOCUS_MAP); + if (msg->has("Options")) + { + msg->getU32("Options", "Flags", flags); + } LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance(); if(instance) @@ -6647,7 +6687,13 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) << LL_ENDL; instance->trackURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); - LLFloaterReg::showInstance("world_map", "center"); + if (flags & BEACON_SHOW_MAP) + { + bool old_auto_focus = instance->getAutoFocus(); + instance->setAutoFocus(flags & BEACON_FOCUS_MAP); + instance->openFloater("center"); + instance->setAutoFocus(old_auto_focus); + } } // remove above two lines and replace with below line diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 8c24b2438b..d92341eb96 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1750,6 +1750,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use { instance->mTeleportFinishedSignal(instance->mTeleportInProgressPosition, false); } + instance->postTeleportFinished(instance->mTeleportWithinRegion); + instance->mTeleportWithinRegion = false; } parcel->setParcelEnvironmentVersion(parcel_environment_version); LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL; @@ -2709,6 +2711,8 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos // Local teleport. We already have the agent parcel data. // Emit the signal immediately. getInstance()->mTeleportFinishedSignal(new_pos, local); + + postTeleportFinished(true); } else { @@ -2717,12 +2721,14 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos // Let's wait for the update and then emit the signal. mTeleportInProgressPosition = new_pos; mTeleportInProgress = true; + mTeleportWithinRegion = local; } } void LLViewerParcelMgr::onTeleportFailed() { mTeleportFailedSignal(); + LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", false)); } bool LLViewerParcelMgr::getTeleportInProgress() @@ -2730,3 +2736,17 @@ bool LLViewerParcelMgr::getTeleportInProgress() return mTeleportInProgress // case where parcel data arrives after teleport || gAgent.getTeleportState() > LLAgent::TELEPORT_NONE; // For LOCAL, no mTeleportInProgress } + +void LLViewerParcelMgr::postTeleportFinished(bool local) +{ + auto post = []() { LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", true)); }; + if (local) + { + static LLCachedControl<F32> teleport_local_delay(gSavedSettings, "TeleportLocalDelay"); + LL::Timers::instance().scheduleAfter(post, teleport_local_delay + 0.5f); + } + else + { + post(); + } +} diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 974ea39359..4d54593c1e 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -295,6 +295,8 @@ public: void onTeleportFailed(); bool getTeleportInProgress(); + void postTeleportFinished(bool local); + static bool isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power); static bool isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power); @@ -344,8 +346,11 @@ private: std::vector<LLParcelObserver*> mObservers; + // Used to communicate between onTeleportFinished() and processParcelProperties() bool mTeleportInProgress; + bool mTeleportWithinRegion{ false }; LLVector3d mTeleportInProgressPosition; + teleport_finished_signal_t mTeleportFinishedSignal; teleport_failed_signal_t mTeleportFailedSignal; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 03d5563c65..739821d2f5 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -38,7 +38,6 @@ #include "llregionhandle.h" #include "llsurface.h" #include "message.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" @@ -3156,7 +3155,7 @@ void LLViewerRegion::unpackRegionHandshake() std::string cap = getCapability("ModifyRegion"); // needed for queueQuery if (cap.empty()) { - LLFloaterRegionInfo::sRefreshFromRegion(this); + LLFloaterRegionInfo::refreshFromRegion(this); } else { @@ -3168,7 +3167,7 @@ void LLViewerRegion::unpackRegionHandshake() LLVLComposition* compp = region->getComposition(); if (!compp) { return; } compp->apply(composition_changes); - LLFloaterRegionInfo::sRefreshFromRegion(region); + LLFloaterRegionInfo::refreshFromRegion(region); }); } } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a8fe221d98..c219b0575e 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; LLGLSLShader gPBRTerrainBakeProgram; +LLGLSLShader gTerrainStampProgram; //object shaders LLGLSLShader gObjectPreviewProgram; @@ -984,7 +985,10 @@ bool LLViewerShaderMgr::loadShadersWater() return loadShadersWater(); } - LLWorld::getInstance()->updateWaterObjects(); + if (LLWorld::instanceExists()) + { + LLWorld::getInstance()->updateWaterObjects(); + } return true; } @@ -3170,6 +3174,34 @@ bool LLViewerShaderMgr::loadShadersInterface() } } + if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled")) + { + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + //llassert(success); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL; + gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false); + // continue as if this shader never happened + success = true; + } + } + } + if (success) { gAlphaMaskProgram.mName = "Alpha Mask Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b08796025a..e654967c46 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; extern LLGLSLShader gPBRTerrainBakeProgram; +extern LLGLSLShader gTerrainStampProgram; //output tex0[tc0] - tex1[tc1] extern LLGLSLShader gTwoTextureCompareProgram; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 9d4c072909..a4b06b1e1a 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -67,6 +67,7 @@ #include "lluiusage.h" #include "lltranslate.h" #include "llluamanager.h" +#include "scope_exit.h" // "Minimal Vulkan" to get max API Version @@ -510,7 +511,6 @@ void send_viewer_stats(bool include_preferences) return; } - LLSD body; std::string url = gAgent.getRegion()->getCapability("ViewerStats"); if (url.empty()) { @@ -518,8 +518,18 @@ void send_viewer_stats(bool include_preferences) return; } - LLViewerStats::instance().getRecording().pause(); + LLSD body = capture_viewer_stats(include_preferences); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "Statistics posted to sim", "Failed to post statistics to sim"); +} + +LLSD capture_viewer_stats(bool include_preferences) +{ + LLViewerStats& vstats{ LLViewerStats::instance() }; + vstats.getRecording().pause(); + LL::scope_exit cleanup([&vstats]{ vstats.getRecording().resume(); }); + LLSD body; LLSD &agent = body["agent"]; time_t ltime; @@ -791,10 +801,7 @@ void send_viewer_stats(bool include_preferences) body["session_id"] = gAgentSessionID; LLViewerStats::getInstance()->addToMessage(body); - - LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, - "Statistics posted to sim", "Failed to post statistics to sim"); - LLViewerStats::instance().getRecording().resume(); + return body; } LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name) diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 8aed1c537e..dd66fee436 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -281,6 +281,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. void update_statistics(); +LLSD capture_viewer_stats(bool include_preferences); void send_viewer_stats(bool include_preferences); void update_texture_time(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 0f9c65893d..82fefde0a7 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -43,6 +43,7 @@ #include "message.h" #include "lltimer.h" #include "v4coloru.h" +#include "llnotificationsutil.h" // viewer includes #include "llimagegl.h" @@ -505,8 +506,12 @@ void LLViewerTexture::updateClass() F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget; - // try to leave half a GB for everyone else, but keep at least 768MB for ourselves - F32 target = llmax(budget - 512.f, MIN_VRAM_BUDGET); + // Try to leave at least half a GB for everyone else and for bias, + // but keep at least 768MB for ourselves + // Viewer can 'overshoot' target when scene changes, if viewer goes over budget it + // can negatively impact performance, so leave 20% of a breathing room for + // 'bias' calculation to kick in. + F32 target = llmax(llmin(budget - 512.f, budget * 0.8f), MIN_VRAM_BUDGET); sFreeVRAMMegabytes = target - used; F32 over_pct = (used - target) / target; @@ -522,7 +527,7 @@ void LLViewerTexture::updateClass() // slam to 1.5 bias the moment we hit low memory (discards off screen textures immediately) sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.5f); - if (is_sys_low) + if (is_sys_low || over_pct > 2.f) { // if we're low on system memory, emergency purge off screen textures to avoid a death spiral LL_WARNS() << "Low system memory detected, emergency downrezzing off screen textures" << LL_ENDL; for (auto& image : gTextureList) @@ -542,7 +547,6 @@ void LLViewerTexture::updateClass() { static LLCachedControl<F32> low_mem_min_discard_increment(gSavedSettings, "RenderLowMemMinDiscardIncrement", .1f); sDesiredDiscardBias += (F32) low_mem_min_discard_increment * (F32) gFrameIntervalSeconds; - sEvaluationTimer.reset(); } } else @@ -558,20 +562,49 @@ void LLViewerTexture::updateClass() } // set to max discard bias if the window has been backgrounded for a while + static F32 last_desired_discard_bias = 1.f; static bool was_backgrounded = false; static LLFrameTimer backgrounded_timer; + static LLCachedControl<F32> minimized_discard_time(gSavedSettings, "TextureDiscardMinimizedTime", 1.f); + static LLCachedControl<F32> backgrounded_discard_time(gSavedSettings, "TextureDiscardBackgroundedTime", 60.f); bool in_background = (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) || !gFocusMgr.getAppHasFocus(); - + bool is_minimized = gViewerWindow && gViewerWindow->getWindow()->getMinimized() && in_background; if (in_background) { - if (backgrounded_timer.getElapsedTimeF32() > 10.f) + F32 discard_time = is_minimized ? minimized_discard_time : backgrounded_discard_time; + if (discard_time > 0.f && backgrounded_timer.getElapsedTimeF32() > discard_time) { if (!was_backgrounded) { - LL_INFOS() << "Viewer is backgrounded, freeing up video memory." << LL_ENDL; + std::string notification_name; + std::string setting; + if (is_minimized) + { + notification_name = "TextureDiscardMinimized"; + setting = "TextureDiscardMinimizedTime"; + } + else + { + notification_name = "TextureDiscardBackgrounded"; + setting = "TextureDiscardBackgroundedTime"; + } + + LL_INFOS() << "Viewer was " << (is_minimized ? "minimized" : "backgrounded") << " for " << discard_time + << "s, freeing up video memory." << LL_ENDL; + + LLNotificationsUtil::add(notification_name, llsd::map("DELAY", discard_time), LLSD(), + [=](const LLSD& notification, const LLSD& response) + { + if (response["Cancel_okcancelignore"].asBoolean()) + { + LL_INFOS() << "User chose to disable texture discard on " << (is_minimized ? "minimizing." : "backgrounding.") << LL_ENDL; + gSavedSettings.setF32(setting, -1.f); + } + }); + last_desired_discard_bias = sDesiredDiscardBias; + was_backgrounded = true; } - was_backgrounded = true; sDesiredDiscardBias = 4.f; } } @@ -580,9 +613,9 @@ void LLViewerTexture::updateClass() backgrounded_timer.reset(); if (was_backgrounded) { // if the viewer was backgrounded - LL_INFOS() << "Viewer is no longer backgrounded, resuming normal texture usage." << LL_ENDL; + LL_INFOS() << "Viewer is no longer backgrounded or minimized, resuming normal texture usage." << LL_ENDL; was_backgrounded = false; - sDesiredDiscardBias = 1.f; + sDesiredDiscardBias = last_desired_discard_bias; } } @@ -1658,6 +1691,7 @@ void LLViewerFetchedTexture::processTextureStats() if(mDesiredDiscardLevel > mMinDesiredDiscardLevel)//need to load more { mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); mFullyLoaded = false; } //setDebugText("fully loaded"); @@ -1707,6 +1741,7 @@ void LLViewerFetchedTexture::processTextureStats() log((F32)mFullHeight / mKnownDrawHeight) / log_2); mDesiredDiscardLevel = llclamp(mDesiredDiscardLevel, (S8)0, (S8)getMaxDiscardLevel()); mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } mKnownDrawSizeChanged = false; @@ -2431,6 +2466,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() if (mIsRawImageValid) { // If we have an existing raw image, we have a baseline for the raw and auxiliary quality levels. + current_raw_discard = mRawDiscardLevel; best_raw_discard = llmin(best_raw_discard, mRawDiscardLevel); best_aux_discard = llmin(best_aux_discard, mRawDiscardLevel); // We always decode the aux when we decode the base raw current_aux_discard = llmin(current_aux_discard, best_aux_discard); @@ -2889,10 +2925,12 @@ void LLViewerLODTexture::processTextureStats() { // If the image has not been significantly visible in a while, we don't want it mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1)); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } else if (!mFullWidth || !mFullHeight) { mDesiredDiscardLevel = getMaxDiscardLevel(); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } else { @@ -2962,6 +3000,7 @@ void LLViewerLODTexture::processTextureStats() // stop requesting more mDesiredDiscardLevel = current_discard; } + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0) diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 7b89ae4e44..489e5980d6 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -364,10 +364,31 @@ void LLViewerTextureList::shutdown() mInitialized = false ; //prevent loading textures again. } +namespace +{ + +std::string tex_name_as_string(const LLViewerFetchedTexture* image) +{ + if (!image->getGLTexture()) { return std::string("N/A"); } + return std::to_string(image->getGLTexture()->getTexName()); +} + +const std::string& tex_label_as_string(const LLViewerFetchedTexture* image, std::string& label) +{ + bool error; + image->getGLObjectLabel(label, error); + if (error) { label.assign("N/A"); } + return label; +} + +}; + void LLViewerTextureList::dump() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - LL_INFOS() << "LLViewerTextureList::dump()" << LL_ENDL; + LL_INFOS() << __FUNCTION__ << "()" << LL_ENDL; + + std::string label; for (image_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it) { LLViewerFetchedTexture* image = *it; @@ -378,6 +399,9 @@ void LLViewerTextureList::dump() << " discard " << image->getDiscardLevel() << " desired " << image->getDesiredDiscardLevel() << " http://asset.siva.lindenlab.com/" << image->getID() << ".texture" + << " faces " << image->getTotalNumFaces() + << " texname " << tex_name_as_string(image) + << " label \"" << tex_label_as_string(image, label) << "\"" << LL_ENDL; } } @@ -422,7 +446,12 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& std::string url = "file://" + full_path; - return getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); + LLViewerFetchedTexture* tex = getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); + if (gDebugTextureLabelLocalFilesSession) + { + gTextureList.mNameTextureList.push_back(LLViewerTextureList::NameElement(tex, filename)); + } + return tex; } LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url, @@ -844,6 +873,10 @@ void LLViewerTextureList::updateImages(F32 max_time) //handle results from decode threads updateImagesCreateTextures(remaining_time); + // Label all images (if enabled) + updateImagesNameTextures(); + labelAll(); + bool didone = false; for (image_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ) @@ -1113,6 +1146,73 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } +void LLViewerTextureList::updateImagesNameTextures() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + if (gGLManager.mIsDisabled) { return; } + static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false); + if (!debug_texture_label()) { return; } + + static GLsizei max_length = 0; + if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); } + + auto it = mNameTextureList.begin(); + while (it != mNameTextureList.end()) // For ALL textures needing names + { + LLViewerFetchedTexture* tex = it->mTex; + // Check that the texture is in the list first (otherwise it may be a dead pointer) + // A raw pointer ensures textures are cleaned up when this code isn't running. + const bool alive = mImageList.find(tex) != mImageList.end(); + + if (alive) + { + if (tex->hasGLTexture()) + { + if(tex->getTexName()) + { + tex->setGLObjectLabel(it->mPrefix, true); + it = mNameTextureList.erase(it); // Assume no rename needed + } + else + { + ++it; // Not ready + } + } + else + { + ++it; // Not ready + } + } + else + { + it = mNameTextureList.erase(it); // Remove dead pointer + } + } +} + +void LLViewerTextureList::labelAll() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false); + if (!debug_texture_label()) { return; } + + static const std::string local_prefix = "lltexlocal"; + static const std::string other_prefix = "lltexother"; + + std::string label; + bool error; + for (LLViewerFetchedTexture* image : mImageList) + { + image->getGLObjectLabel(label, error); + if (!error && label.empty()) + { + const S32 category = image->getGLTexture()->getCategory(); + const std::string& new_prefix = category == LLGLTexture::LOCAL ? local_prefix : other_prefix; + image->setGLObjectLabel(new_prefix, true); + } + } +} + F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -1169,6 +1269,11 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) //update MIN_UPDATE_COUNT or 5% of other textures, whichever is greater update_count = llmax((U32) MIN_UPDATE_COUNT, (U32) mUUIDMap.size()/20); + if (LLViewerTexture::sDesiredDiscardBias > 1.f) + { + // we are over memory target, update more agresively + update_count = (S32)(update_count * LLViewerTexture::sDesiredDiscardBias); + } update_count = llmin(update_count, (U32) mUUIDMap.size()); { // copy entries out of UUID map to avoid iterator invalidation from deletion inside updateImageDecodeProiroty or updateFetch below @@ -1295,6 +1400,10 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) max_time = llmax(max_time, .001f); F32 create_time = updateImagesCreateTextures(max_time); + // Label all images (if enabled) + updateImagesNameTextures(); + labelAll(); + LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " << " fetch_pending " << fetch_pending << " create_time " << create_time @@ -1318,6 +1427,11 @@ bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image, raw_image->getComponents()); LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions); + if (compressedImage.isNull()) + { + LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; + return false; + } if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions) { std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", @@ -1328,12 +1442,6 @@ bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image, compressedImage->setLastError(reason); return false; } - if (compressedImage.isNull()) - { - compressedImage->setLastError("Couldn't convert the image to jpeg2000."); - LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; - return false; - } if (!compressedImage->save(out_filename)) { compressedImage->setLastError("Couldn't create the jpeg2000 image for upload."); diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 7c7112f4cf..08dd2d0f7f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -153,6 +153,9 @@ private: void updateImagesUpdateStats(); F32 updateImagesLoadingFastCache(F32 max_time); + void updateImagesNameTextures(); + void labelAll(); + void addImage(LLViewerFetchedTexture *image, ETexListType tex_type); void deleteImage(LLViewerFetchedTexture *image); @@ -214,6 +217,14 @@ public: // images that have been loaded but are waiting to be uploaded to GL image_queue_t mCreateTextureList; + struct NameElement + { + NameElement(LLViewerFetchedTexture* tex, const std::string& prefix) : mTex(tex), mPrefix(prefix) {} + LLViewerFetchedTexture* mTex; + std::string mPrefix; + }; + std::vector<NameElement> mNameTextureList; + // images that must be downscaled quickly so we don't run out of memory image_queue_t mDownScaleQueue; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c747319940..d339d0c37c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -268,9 +268,6 @@ static const F32 MIN_DISPLAY_SCALE = 0.75f; static const char KEY_MOUSELOOK = 'M'; -static LLCachedControl<std::string> sSnapshotBaseName(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot")); -static LLCachedControl<std::string> sSnapshotDir(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseDir", "")); - LLTrace::SampleStatHandle<> LLViewerWindow::sMouseVelocityStat("Mouse Velocity"); @@ -1330,7 +1327,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if ( obj != mDragHoveredObject) + if (obj != mDragHoveredObject) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); @@ -1935,6 +1932,11 @@ LLViewerWindow::LLViewerWindow(const Params& p) } LLFontManager::initClass(); + + // fonts use an GL_UNSIGNED_BYTE image format, + // so they need convertion, init buffers if needed + LLImageGL::allocateConversionBuffer(); + // Init font system, load default fonts and generate basic glyphs // currently it takes aprox. 0.5 sec and we would load these fonts anyway // before login screen. @@ -2007,6 +2009,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) std::string LLViewerWindow::getLastSnapshotDir() { + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); return sSnapshotDir; } @@ -3777,7 +3780,7 @@ void LLViewerWindow::updateLayout() { LLToolMgr* tool_mgr = LLToolMgr::getInstance(); LLTool* tool = tool_mgr->getCurrentTool(); - LLCachedControl<bool> freeze_time(gSavedSettings, "FreezeTime"); + static LLCachedControl<bool> freeze_time(gSavedSettings, "FreezeTime"); if (gFloaterTools != NULL && tool != NULL && tool != gToolNull @@ -4706,6 +4709,7 @@ void LLViewerWindow::saveImageNumbered(LLImageFormatted *image, bool force_picke // Get a base file location if needed. if (force_picker || !isSnapshotLocSet()) { + static LLCachedControl<std::string> sSnapshotBaseName(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot"); std::string proposed_name(sSnapshotBaseName); // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. @@ -4751,6 +4755,9 @@ void LLViewerWindow::onSelectionFailure(const snapshot_saved_signal_t::slot_type void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) { + static LLCachedControl<std::string> sSnapshotBaseName(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot"); + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); + std::string lastSnapshotDir = LLViewerWindow::getLastSnapshotDir(); if (lastSnapshotDir.empty()) { @@ -4902,8 +4909,8 @@ void LLViewerWindow::playSnapshotAnimAndSound() bool LLViewerWindow::isSnapshotLocSet() const { - std::string snapshot_dir = sSnapshotDir; - return !snapshot_dir.empty(); + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); + return !sSnapshotDir().empty(); } void LLViewerWindow::resetSnapshotLoc() const diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index 52f413792a..45b4da0aac 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -35,6 +35,7 @@ // std headers // external library headers // other Linden headers +#include "llcallbacklist.h" #include "llviewerwindow.h" LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): @@ -46,8 +47,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): add("saveSnapshot", "Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n" "[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" - "type: \"COLOR\", \"DEPTH\"\n" - "Post on [\"reply\"] an event containing [\"result\"]", + "type: \"COLOR\", \"DEPTH\"\n", &LLViewerWindowListener::saveSnapshot, llsd::map("filename", LLSD::String(), "reply", LLSD())); add("requestReshape", @@ -57,8 +57,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): void LLViewerWindowListener::saveSnapshot(const LLSD& event) const { - Response response(LLSD(), event); - typedef std::map<LLSD::String, LLSnapshotModel::ESnapshotLayerType> TypeMap; TypeMap types; #define tp(name) types[#name] = LLSnapshotModel::SNAPSHOT_TYPE_##name @@ -88,7 +86,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const TypeMap::const_iterator found = types.find(event["type"]); if (found == types.end()) { - return response.error(stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")); + sendReply(llsd::map("error", stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")), event); + return; } type = found->second; } @@ -96,7 +95,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const std::string filename(event["filename"]); if (filename.empty()) { - return response.error(stringize("File path is empty.")); + sendReply(llsd::map("error", stringize("File path is empty.")), event); + return; } LLSnapshotModel::ESnapshotFormat format(LLSnapshotModel::SNAPSHOT_FORMAT_BMP); @@ -115,9 +115,13 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const } else if (ext != "bmp") { - return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")); + sendReply(llsd::map("error", stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")), event); + return; } - response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format); + // take snapshot on the main coro + doOnIdleOneTime([this, event, filename, width, height, showui, showhud, rebuild, type, format]() + { sendReply(llsd::map("result", mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format)), event); }); + } void LLViewerWindowListener::requestReshape(LLSD const & event_data) const diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 077e6e6cb1..ca76d93cd7 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -320,7 +320,11 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); + const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; + // The paint map has changed, so edits are no longer valid + mPaintRequestQueue.clear(); + mPaintMapQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index f15f9bff6a..21fd484375 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -31,6 +31,7 @@ #include "llviewershadermgr.h" #include "llviewertexture.h" #include "llpointer.h" +#include "llterrainpaintmap.h" #include "llimage.h" @@ -87,6 +88,18 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Queue of client-triggered brush operations that need to be converted + // into a form that can be sent to the server. + // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue) + LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; } + // Queue of client-triggered paint operations that need to be converted + // into a form that can be sent to the server. + // Paints in this queue are in RGBA format. + LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; } + // Paint queue for current paint map - this queue gets applied directly to + // the paint map. Paints within are assumed to have already been sent to + // the server. Paints in this queue are in RGB format. + LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; } protected: void unboost(); @@ -105,6 +118,9 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer<LLViewerTexture> mPaintMap; + LLTerrainBrushQueue mBrushQueue; + LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; + LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; // Local materials to override all regions diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c8a4e4c205..bfed8b9d39 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -687,7 +687,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mLoadedCallbacksPaused(false), mLoadedCallbackTextures(0), - mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar", false)), mLastRezzedStatus(-1), mIsEditingAppearance(false), mUseLocalAppearance(false), @@ -3767,6 +3766,7 @@ LLVector3 LLVOAvatar::idleCalcNameTagPosition(const LLVector3 &root_pos_last) // Avoid of crossing the name tag by the water surface if (mNameText) { + bool hidden = false; F32 water_height = getRegion()->getWaterHeight(); static const F32 WATER_HEIGHT_ABOVE_DELTA = 0.25; if (name_position[VZ] < water_height + WATER_HEIGHT_ABOVE_DELTA) @@ -3780,8 +3780,19 @@ LLVector3 LLVOAvatar::idleCalcNameTagPosition(const LLVector3 &root_pos_last) { name_position[VZ] = water_height + WATER_HEIGHT_ABOVE_DELTA; } + else + { + // Hide the name tag when the camera is above the water + // but the avatar is deep enough under the water surface + hidden = true; + } } } + + if (mNameText->getHidden() != hidden) + { + mNameText->setHidden(hidden); + } } return name_position; @@ -8511,7 +8522,8 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) bool LLVOAvatar::isFullyLoaded() const { - return (mRenderUnloadedAvatar || mFullyLoaded); + static LLCachedControl<bool> render_unloaded_avatars(gSavedSettings, "RenderUnloadedAvatar", false); + return (render_unloaded_avatars || mFullyLoaded); } bool LLVOAvatar::isTooComplex() const diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index dd1725c322..a9dea1f1e3 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -668,8 +668,6 @@ private: LLVector3 mLastAnimExtents[2]; LLVector3 mLastAnimBasePos; - LLCachedControl<bool> mRenderUnloadedAvatar; - //-------------------------------------------------------------------- // Wind rippling in clothes //-------------------------------------------------------------------- @@ -1014,7 +1012,7 @@ public: void startTyping() { mTyping = true; mTypingTimer.reset(); } void stopTyping() { mTyping = false; } private: - bool mVisibleChat; + bool mVisibleChat = false; //-------------------------------------------------------------------- // Lip synch morphs @@ -1196,7 +1194,7 @@ public: static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame) protected: S32 getUnbakedPixelAreaRank(); - bool mHasGrey; + bool mHasGrey = false; private: F32 mMinPixelArea; F32 mMaxPixelArea; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 1a35a71706..243cba6ffd 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -391,12 +391,21 @@ void LLVoiceClient::setRenderDevice(const std::string& name) LLWebRTCVoiceClient::getInstance()->setRenderDevice(name); } +bool LLVoiceClient::isCaptureNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isCaptureNoDevice(); +} + +bool LLVoiceClient::isRenderNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isRenderNoDevice(); +} + const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { return LLWebRTCVoiceClient::getInstance()->getCaptureDevices(); } - const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() { return LLWebRTCVoiceClient::getInstance()->getRenderDevices(); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index d53f512d82..2731b0cc7f 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -192,6 +192,9 @@ public: virtual LLVoiceDeviceList& getCaptureDevices()=0; virtual LLVoiceDeviceList& getRenderDevices()=0; + virtual bool isCaptureNoDevice() = 0; + virtual bool isRenderNoDevice() = 0; + virtual void getParticipantList(std::set<LLUUID> &participants)=0; virtual bool isParticipant(const LLUUID& speaker_id)=0; //@} @@ -392,6 +395,8 @@ public: void setCaptureDevice(const std::string& name); void setRenderDevice(const std::string& name); + bool isCaptureNoDevice(); + bool isRenderNoDevice(); void setHidden(bool hidden); const LLVoiceDeviceList& getCaptureDevices(); diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 7691ac54f3..c5704982b8 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -337,7 +337,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) //--------------------------------------------------- void LLVoiceVisualizer::render() { - static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", false); + static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", true); if (!mVoiceEnabled || !show_visualizer) { return; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 4659fa5f71..b8ddc1f255 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2775,6 +2775,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) } } } + void LLVivoxVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; @@ -6637,13 +6638,13 @@ void LLVivoxVoiceClient::expireVoiceFonts() } } - LLSD args; - args["URL"] = LLTrans::getString("voice_morphing_url"); - args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); - // Give a notification if any voice fonts have expired. if (have_expired) { + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); + if (expired_in_use) { LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3167705528..cec8b71442 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -121,6 +121,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override { return false; }; + bool isRenderNoDevice() override { return false; }; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 7161f5af5e..6d4f3fe356 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -203,6 +203,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mTuningMode(false), mTuningMicGain(0.0), mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume + mDeviceSettingsAvailable(false), mDevicesListUpdated(false), mSpatialCoordsDirty(false), @@ -218,7 +219,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mAvatarNameCacheConnection(), mIsInTuningMode(false), mIsProcessingChannels(false), - mIsCoroutineActive(false), + mIsTimerActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr) { @@ -295,6 +296,20 @@ void LLWebRTCVoiceClient::cleanUp() mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "Exiting" << LL_ENDL; + stopTimer(); +} + +void LLWebRTCVoiceClient::stopTimer() +{ + if (mIsTimerActive) + { + LLMuteList::instanceExists(); + { + LLMuteList::getInstance()->removeObserver(this); + } + mIsTimerActive = false; + LL::Timers::instance().cancel(mVoiceTimerHandle); + } } void LLWebRTCVoiceClient::LogMessage(llwebrtc::LLWebRTCLogCallback::LogLevel level, const std::string& message) @@ -443,8 +458,7 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) //--------------------------------------------------- // Primary voice loop. -// This voice loop is called every 100ms plus the time it -// takes to process the various functions called in the loop +// This voice loop is called every 100ms // The loop does the following: // * gates whether we do channel processing depending on // whether we're running a WebRTC voice channel or @@ -456,102 +470,94 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) // connection to various voice channels. // * Sends updates to the voice server when this agent's // voice levels, or positions have changed. -void LLWebRTCVoiceClient::voiceConnectionCoro() +void LLWebRTCVoiceClient::connectionTimer() { - LL_DEBUGS("Voice") << "starting" << LL_ENDL; - mIsCoroutineActive = true; - LLCoros::set_consuming(true); + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE; try { - LLMuteList::getInstance()->addObserver(this); - while (!sShuttingDown) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop") - // TODO: Doing some measurement and calculation here, - // we could reduce the timeout to take into account the - // time spent on the previous loop to have the loop - // cycle at exactly 100ms, instead of 100ms + loop - // execution time. - // Could help with voice updates making for smoother - // voice when we're busy. - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - if (sShuttingDown) return; // 'this' migh already be invalid - bool voiceEnabled = mVoiceEnabled; - - if (!isAgentAvatarValid()) + bool voiceEnabled = mVoiceEnabled; + + if (!isAgentAvatarValid()) + { + if (sShuttingDown) { - continue; + cleanUp(); } + return; + } - LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp) + LLViewerRegion* regionp = gAgent.getRegion(); + if (!regionp) + { + if (sShuttingDown) { - continue; + cleanUp(); } + return; + } - if (!mProcessChannels) + if (!mProcessChannels) + { + // we've switched away from webrtc voice, so shut all channels down. + // leave channel can be called again and again without adverse effects. + // it merely tells channels to shut down if they're not already doing so. + leaveChannel(false); + } + else if (inSpatialChannel()) + { + bool useEstateVoice = true; + // add session for region or parcel voice. + if (!regionp || regionp->getRegionID().isNull()) { - // we've switched away from webrtc voice, so shut all channels down. - // leave channel can be called again and again without adverse effects. - // it merely tells channels to shut down if they're not already doing so. - leaveChannel(false); + // no region, no voice. + return; } - else if (inSpatialChannel()) - { - bool useEstateVoice = true; - // add session for region or parcel voice. - if (!regionp || regionp->getRegionID().isNull()) - { - // no region, no voice. - continue; - } - voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); + voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); - if (voiceEnabled) + if (voiceEnabled) + { + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // check to see if parcel changed. + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - // check to see if parcel changed. - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + // parcel voice + if (!parcel->getParcelFlagAllowVoice()) { - // parcel voice - if (!parcel->getParcelFlagAllowVoice()) - { - voiceEnabled = false; - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) - { - // use the parcel-specific voice channel. - S32 parcel_local_id = parcel->getLocalID(); - std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); - - useEstateVoice = false; - if (!inOrJoiningChannel(channelID)) - { - startParcelSession(channelID, parcel_local_id); - } - } + voiceEnabled = false; } - if (voiceEnabled && useEstateVoice && !inEstateChannel()) + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) { - // estate voice - startEstateSession(); + // use the parcel-specific voice channel. + S32 parcel_local_id = parcel->getLocalID(); + std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + + useEstateVoice = false; + if (!inOrJoiningChannel(channelID)) + { + startParcelSession(channelID, parcel_local_id); + } } } - if (!voiceEnabled) + if (voiceEnabled && useEstateVoice && !inEstateChannel()) { - // voice is disabled, so leave and disable PTT - leaveChannel(true); - } - else - { - // we're in spatial voice, and voice is enabled, so determine positions in order - // to send position updates. - updatePosition(); + // estate voice + startEstateSession(); } } + if (!voiceEnabled) + { + // voice is disabled, so leave and disable PTT + leaveChannel(true); + } + else + { + // we're in spatial voice, and voice is enabled, so determine positions in order + // to send position updates. + updatePosition(); + } LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (sShuttingDown) { return; @@ -565,10 +571,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() }); } } - catch (const LLCoros::Stop&) - { - LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL; - } catch (const LLContinueError&) { LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient"); @@ -582,7 +584,10 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() throw; } - cleanUp(); + if (sShuttingDown) + { + cleanUp(); + } } // For spatial, determine which neighboring regions to connect to @@ -640,12 +645,14 @@ void LLWebRTCVoiceClient::leaveAudioSession() void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.clear(); } void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Capture Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.push_back(device); } @@ -658,6 +665,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) { mWebRTCDeviceInterface->setCaptureDevice(name); } + void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; @@ -669,7 +677,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi { LL::WorkQueue::postMaybe(mMainQueue, - [=] + [=, this] { OnDevicesChangedImpl(render_devices, capture_devices); }); @@ -703,20 +711,22 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi } setCaptureDevice(inputDevice); + mDeviceSettingsAvailable = true; setDevicesListUpdated(true); } void LLWebRTCVoiceClient::clearRenderDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.clear(); } void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Render Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.push_back(device); - } LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() @@ -729,6 +739,16 @@ void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) mWebRTCDeviceInterface->setRenderDevice(name); } +bool LLWebRTCVoiceClient::isCaptureNoDevice() +{ + return mCaptureDevices.empty() || mWebRTCDeviceInterface->isCaptureNoDevice(); +} + +bool LLWebRTCVoiceClient::isRenderNoDevice() +{ + return mRenderDevices.empty() || mWebRTCDeviceInterface->isRenderNoDevice(); +} + void LLWebRTCVoiceClient::tuningStart() { if (!mIsInTuningMode) @@ -754,11 +774,15 @@ bool LLWebRTCVoiceClient::inTuningMode() void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { - mTuningMicGain = volume; + mTuningMicGain = volume; } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) { + if (isRenderNoDevice()) + { + volume = 0; + } if (volume != mTuningSpeakerVolume) { @@ -768,14 +792,17 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::getAudioLevel() { - if (mIsInTuningMode) + if (isCaptureNoDevice()) { - return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; + return 0; } - else + + if (mIsInTuningMode) { - return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; + return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; } + + return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; } float LLWebRTCVoiceClient::tuningGetEnergy(void) @@ -783,15 +810,6 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void) return getAudioLevel(); } -bool LLWebRTCVoiceClient::deviceSettingsAvailable() -{ - bool result = true; - - if(mRenderDevices.empty() || mCaptureDevices.empty()) - result = false; - - return result; -} bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; @@ -801,7 +819,7 @@ bool LLWebRTCVoiceClient::deviceSettingsUpdated() void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if(clearCurrentList) + if (clearCurrentList) { clearCaptureDevices(); clearRenderDevices(); @@ -809,7 +827,6 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) mWebRTCDeviceInterface->refreshDevices(); } - void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; @@ -1340,7 +1357,7 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const // webrtc is working if the coroutine is active in the case of // webrtc. WebRTC doesn't need to connect to a secondary process // or a login server to become active. - return mIsCoroutineActive; + return mIsTimerActive; } // Returns true if calling back the session URI after the session has closed is possible. @@ -1523,9 +1540,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { if (volume != mSpeakerVolume) { - { - mSpeakerVolume = volume; - } + mSpeakerVolume = volume; sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); } } @@ -1544,7 +1559,6 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain) } } - void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE; @@ -1552,7 +1566,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) LL_DEBUGS("Voice") << "( " << (enabled ? "enabled" : "disabled") << " )" << " was "<< (mVoiceEnabled ? "enabled" : "disabled") - << " coro "<< (mIsCoroutineActive ? "active" : "inactive") + << " coro "<< (mIsTimerActive ? "active" : "inactive") << LL_ENDL; if (enabled != mVoiceEnabled) @@ -1569,10 +1583,12 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; mSpatialCoordsDirty = true; updatePosition(); - if (!mIsCoroutineActive) + if (!mIsTimerActive) { - LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", - boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); + LL_DEBUGS("Voice") << "Starting" << LL_ENDL; + mIsTimerActive = true; + LLMuteList::getInstance()->addObserver(this); + mVoiceTimerHandle = LL::Timers::instance().scheduleEvery([this]() { connectionTimer(); return false; }, UPDATE_THROTTLE_SECONDS); } else { @@ -2205,7 +2221,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) @@ -2228,7 +2244,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs // callback from llwebrtc void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); }); } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2346,7 +2362,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2373,7 +2389,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2395,7 +2411,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; if (!mShutDown) { @@ -2409,7 +2425,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() void LLVoiceWebRTCConnection::OnPeerConnectionClosed() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL; if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE) { @@ -2881,7 +2897,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // llwebrtc callback void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); } // @@ -3037,7 +3053,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ff82d2739d..88ead98950 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -30,6 +30,7 @@ class LLWebRTCProtocolParser; #include "lliopipe.h" #include "llpumpio.h" +#include "llcallbacklist.h" #include "llchainio.h" #include "lliosocket.h" #include "v3math.h" @@ -113,7 +114,7 @@ public: /// @name Devices //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - bool deviceSettingsAvailable() override; + bool deviceSettingsAvailable() override { return mDeviceSettingsAvailable; } bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the WebRTC daemon for the current list of input/output devices. @@ -125,6 +126,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override; + bool isRenderNoDevice() override; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} @@ -447,19 +451,23 @@ private: // Coroutine support methods //--- - void voiceConnectionCoro(); + void connectionTimer(); //--- /// Clean up objects created during a voice session. void cleanUp(); + // stop state machine + void stopTimer(); + LL::WorkQueue::weak_t mMainQueue; bool mTuningMode; F32 mTuningMicGain; int mTuningSpeakerVolume; + bool mDeviceSettingsAvailable; bool mDevicesListUpdated; // set to true when the device list has been updated - // and false when the panelvoicedevicesettings has queried for an update status. + // and false when the panelvoicedevicesettings has queried for an update status. std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. @@ -534,7 +542,8 @@ private: bool mIsInTuningMode; bool mIsProcessingChannels; - bool mIsCoroutineActive; + bool mIsTimerActive; + LL::Timers::handle_t mVoiceTimerHandle; // These variables can last longer than WebRTC in coroutines so we need them as static static bool sShuttingDown; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 31f1bbcc21..39728b6818 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -786,7 +786,24 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) LLFace* face = mDrawable->getFace(i); if (!face) continue; const LLTextureEntry *te = face->getTextureEntry(); - LLViewerTexture *imagep = face->getTexture(); + LLViewerTexture *imagep = nullptr; + U32 ch_min; + U32 ch_max; + if (!te->getGLTFRenderMaterial()) + { + ch_min = LLRender::DIFFUSE_MAP; + ch_max = LLRender::SPECULAR_MAP; + } + else + { + ch_min = LLRender::BASECOLOR_MAP; + ch_max = LLRender::EMISSIVE_MAP; + } + for (U32 ch = ch_min; (!imagep && ch <= ch_max); ++ch) + { + // Get _a_ non-null texture if possible (usually diffuse/basecolor, but could be something else) + imagep = face->getTexture(ch); + } if (!imagep || !te || face->mExtents[0].equals3(face->mExtents[1])) { diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index bf171fe954..d9fcd5811d 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llwatchdog.h" +#include "llmutex.h" #include "llthread.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 7962c28e6d..2fd45337fc 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -32,6 +32,7 @@ #include "message.h" #include "lltracker.h" #include "lluistring.h" +#include "llviewercontrol.h" #include "llviewertexturelist.h" #include "lltrans.h" #include "llgltexture.h" @@ -117,6 +118,7 @@ LLVector3d LLSimInfo::getGlobalOrigin() const { return from_region_handle(mHandle); } + LLVector3 LLSimInfo::getLocalPos(LLVector3d global_pos) const { LLVector3d sim_origin = from_region_handle(mHandle); @@ -492,9 +494,20 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& case MAP_ITEM_MATURE_EVENT: case MAP_ITEM_ADULT_EVENT: { - std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"] [" - +LLTrans::getString ("TimeAMPM")+"]"; + std::string timeStr; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + std::string timeStr = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32) extra; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 6b2bd3e6fb..1be6a6cfff 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -517,26 +517,38 @@ void LLWorldMapView::draw() // Draw the region name in the lower left corner if (mMapScale >= DRAW_TEXT_THRESHOLD) { - std::string mesg; + static LLCachedControl<bool> print_coords(gSavedSettings, "MapShowGridCoords"); + static LLFontGL* font = LLFontGL::getFontSansSerifSmallBold(); + + auto print = [&](std::string text, F32 x, F32 y, bool use_ellipses) + { + font->renderUTF8(text, 0, + (F32)llfloor(left + x), (F32)llfloor(bottom + y), + LLColor4::white, + LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, + S32_MAX, //max_chars + (S32)mMapScale, //max_pixels + NULL, + use_ellipses); + }; + + std::string grid_name = info->getName(); if (info->isDown()) { - mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str()); + grid_name += " (" + sStringsMap["offline"] + ")"; } - else + + if (print_coords) { - mesg = info->getName(); + print(grid_name, 3, 14, true); + // Obtain and print the grid map coordinates + LLVector3d region_pos = info->getGlobalOrigin(); + std::string grid_coords = llformat("[%.0f, %.0f]", region_pos[VX] / 256, region_pos[VY] / 256); + print(grid_coords, 3, 2, false); } - if (!mesg.empty()) + else { - LLFontGL::getFontSansSerifSmallBold()->renderUTF8( - mesg, 0, - (F32)llfloor(left + 3), (F32)llfloor(bottom + 2), - LLColor4::white, - LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, - S32_MAX, //max_chars - (S32)mMapScale, //max_pixels - NULL, - /*use_ellipses*/true); + print(grid_name, 3, 2, true); } } } diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index d8ea2b884f..d198f7a33b 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -30,7 +30,7 @@ #include "llviewercontrol.h" // LLControlGroup #include "llviewertexturelist.h" -#include "math.h" // log() +#include <cmath> // log() // Turn this on to output tile stats in the standard output #define DEBUG_TILES_STAT 0 diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 0f956d8350..5b24d46bb0 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -313,6 +313,8 @@ bool LLXMLRPCTransaction::Impl::process() if (mHasResponse && !mResponseParsed) { LLXMLNodePtr root; + bool strip_escaped_strings = LLXMLNode::sStripEscapedStrings; + LLXMLNode::sStripEscapedStrings = false; if (!LLXMLNode::parseBuffer(mResponseText.data(), mResponseText.size(), root, nullptr)) { @@ -329,6 +331,7 @@ bool LLXMLRPCTransaction::Impl::process() LL_WARNS() << "XMLRPC response parsing failed; request URI: " << mURI << LL_ENDL; } + LLXMLNode::sStripEscapedStrings = strip_escaped_strings; mResponseParsed = true; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 84f026699e..0b4b98d674 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -11248,21 +11248,24 @@ public: } }; - +// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled void LLPipeline::rebuildDrawInfo() { - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + const U32 types_to_traverse[] = { - LLViewerRegion* region = *iter; - - LLOctreeDirty dirty; - - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME); - dirty.traverse(part->mOctree); + LLViewerRegion::PARTITION_VOLUME, + LLViewerRegion::PARTITION_BRIDGE, + LLViewerRegion::PARTITION_AVATAR + }; - part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); - dirty.traverse(part->mOctree); + LLOctreeDirty dirty; + for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList()) + { + for (U32 type : types_to_traverse) + { + LLSpatialPartition* part = region->getSpatialPartition(type); + dirty.traverse(part->mOctree); + } } } diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp new file mode 100644 index 0000000000..110beeafc0 --- /dev/null +++ b/indra/newview/rlvactions.cpp @@ -0,0 +1,42 @@ +/** + * @file rlvactions.cpp + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "rlvactions.h" +#include "rlvhandler.h" + +// ============================================================================ +// Helper functions +// + +bool RlvActions::isRlvEnabled() +{ + return RlvHandler::isEnabled(); +} + +// ============================================================================ diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h new file mode 100644 index 0000000000..cb0df95e37 --- /dev/null +++ b/indra/newview/rlvactions.h @@ -0,0 +1,46 @@ +/** + * @file rlvactions.h + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +// ============================================================================ +// RlvActions - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible +// + +class RlvActions +{ + // ================ + // Helper functions + // ================ +public: + /* + * Convenience function to check if RLVa is enabled without having to include rlvhandler.h + */ + static bool isRlvEnabled(); +}; + +// ============================================================================ diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp new file mode 100644 index 0000000000..4140659715 --- /dev/null +++ b/indra/newview/rlvcommon.cpp @@ -0,0 +1,134 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llchat.h" +#include "lldbstrings.h" +#include "llversioninfo.h" +#include "llviewermenu.h" +#include "llviewerstats.h" +#include "message.h" +#include <boost/algorithm/string.hpp> + +#include "rlvcommon.h" + +#include "llviewercontrol.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// RlvStrings +// + +std::string Strings::getVersion(bool wants_legacy) +{ + return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)", + !wants_legacy ? "RestrainedLove" : "RestrainedLife", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch); +} + +std::string Strings::getVersionAbout() +{ + return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild()); +} + +std::string Strings::getVersionNum() +{ + return llformat("%d%02d%02d%02d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build); +} + +std::string Strings::getVersionImplNum() +{ + return llformat("%d%02d%02d%02d", + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId); +} + +// ============================================================================ +// RlvUtil +// + +void Util::menuToggleVisible() +{ + bool isTopLevel = gSavedSettings.getBOOL(Settings::TopLevelMenu); + bool isRlvEnabled = RlvHandler::isEnabled(); + + LLMenuGL* menuRLVaMain = gMenuBarView->findChildMenuByName("RLVa Main", false); + LLMenuGL* menuAdvanced = gMenuBarView->findChildMenuByName("Advanced", false); + LLMenuGL* menuRLVaEmbed= menuAdvanced->findChildMenuByName("RLVa Embedded", false); + + gMenuBarView->setItemVisible("RLVa Main", isRlvEnabled && isTopLevel); + menuAdvanced->setItemVisible("RLVa Embedded", isRlvEnabled && !isTopLevel); + + if ( isRlvEnabled && menuRLVaMain && menuRLVaEmbed && + ( (isTopLevel && 1 == menuRLVaMain->getItemCount()) || (!isTopLevel && 1 == menuRLVaEmbed->getItemCount())) ) + { + LLMenuGL* menuFrom = isTopLevel ? menuRLVaEmbed : menuRLVaMain; + LLMenuGL* menuTo = isTopLevel ? menuRLVaMain : menuRLVaEmbed; + while (LLMenuItemGL* pItem = menuFrom->getItem(1)) + { + menuFrom->removeChild(pItem); + menuTo->addChild(pItem); + pItem->updateBranchParent(menuTo); + } + } +} + +bool Util::parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator) +{ + if (!strInput.empty()) + boost::split(optionList, strInput, boost::is_any_of(strSeparator)); + return !optionList.empty(); +} + +bool Util::sendChatReply(S32 nChannel, const std::string& strUTF8Text) +{ + if (!isValidReplyChannel(nChannel)) + return false; + + // Copy/paste from send_chat_from_viewer() + gMessageSystem->newMessageFast(_PREHASH_ChatFromViewer); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ChatData); + gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); + gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); + gMessageSystem->addS32("Channel", nChannel); + gAgent.sendReliableMessage(); + add(LLStatViewer::CHAT_COUNT, 1); + + return true; +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h new file mode 100644 index 0000000000..6f1bbbbdc6 --- /dev/null +++ b/indra/newview/rlvcommon.h @@ -0,0 +1,72 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "rlvdefines.h" + +namespace Rlv +{ + // ============================================================================ + // RlvStrings + // + + class Strings + { + public: + static std::string getVersion(bool wants_legacy); + static std::string getVersionAbout(); + static std::string getVersionImplNum(); + static std::string getVersionNum(); + }; + + // ============================================================================ + // RlvUtil + // + + namespace Util + { + bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); + void menuToggleVisible(); + bool parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator = Constants::OptionSeparator); + bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); + bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); + }; + + inline bool Util::isValidReplyChannel(S32 nChannel, bool isLoopback) + { + return (nChannel > (!isLoopback ? 0 : -1)) && (CHAT_CHANNEL_DEBUG != nChannel); + } + + inline bool Util::sendChatReply(const std::string& strChannel, const std::string& strUTF8Text) + { + S32 nChannel; + return LLStringUtil::convertToS32(strChannel, nChannel) ? sendChatReply(nChannel, strUTF8Text) : false; + } + + // ============================================================================ +} diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h new file mode 100644 index 0000000000..e39328fdd6 --- /dev/null +++ b/indra/newview/rlvdefines.h @@ -0,0 +1,194 @@ +/** + * @file rlvdefines.h + * @author Kitty Barnett + * @brief RLVa common defines, constants and enums + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +// ============================================================================ +// Defines +// + +// Defining these makes it easier if we ever need to change our tag +#define RLV_WARNS LL_WARNS("RLV") +#define RLV_INFOS LL_INFOS("RLV") +#define RLV_DEBUGS LL_DEBUGS("RLV") +#define RLV_ENDL LL_ENDL +#define RLV_VERIFY(f) (f) + +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + // Make sure we halt execution on errors + #define RLV_ERRS LL_ERRS("RLV") + // Keep our asserts separate from LL's + #define RLV_ASSERT(f) if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; } + #define RLV_ASSERT_DBG(f) RLV_ASSERT(f) +#else + // Don't halt execution on errors in release + #define RLV_ERRS LL_WARNS("RLV") + // We don't want to check assertions in release builds + #ifdef RLV_DEBUG + #define RLV_ASSERT(f) RLV_VERIFY(f) + #define RLV_ASSERT_DBG(f) + #else + #define RLV_ASSERT(f) + #define RLV_ASSERT_DBG(f) + #endif // RLV_DEBUG +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + +namespace Rlv +{ + // Version of the specification we report + namespace SpecVersion { + constexpr S32 Major = 4; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 Build = 0; + }; + + // RLVa implementation version + namespace ImplVersion { + constexpr S32 Major = 3; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 ImplId = 13; + }; + + namespace Constants + { + constexpr char CmdPrefix = '@'; + constexpr char ConsolePrompt[] = "> "; + constexpr std::string_view OptionSeparator = ";"; + } +} + +// ============================================================================ +// Enumeration declarations +// + +namespace Rlv +{ + enum class EBehaviour { + Version = 0, + VersionNew, + VersionNum, + GetCommand, + + Count, + Unknown, + }; + + enum class EBehaviourOptionType + { + EmptyOrException, // Behaviour takes no parameters + Exception, // Behaviour requires an exception as a parameter + NoneOrException, // Behaviour takes either no parameters or an exception + }; + + enum class EParamType { + Unknown = 0x00, + Add = 0x01, // <param> == "n"|"add" + Remove = 0x02, // <param> == "y"|"rem" + Force = 0x04, // <param> == "force" + Reply = 0x08, // <param> == <number> + Clear = 0x10, + AddRem = Add | Remove + }; + + enum class ECmdRet { + Unknown = 0x0000, // Unknown error (should only be used internally) + Retained, // Command was retained + Succeeded = 0x0100, // Command executed successfully + SuccessUnset, // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) + SuccessDuplicate, // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) + SuccessDeprecated, // Command executed successfully but has been marked as deprecated + SuccessDelayed, // Command parsed valid but will execute at a later time + Failed = 0x0200, // Command failed (general failure) + FailedSyntax, // Command failed (syntax error) + FailedOption, // Command failed (invalid option) + FailedParam, // Command failed (invalid param) + FailedLock, // Command failed (command is locked by another object) + FailedDisabled, // Command failed (command disabled by user) + FailedUnknown, // Command failed (unknown command) + FailedNoSharedRoot, // Command failed (missing #RLV) + FailedDeprecated, // Command failed (deprecated and no longer supported) + FailedNoBehaviour, // Command failed (force modifier on an object with no active restrictions) + FailedUnheldBehaviour, // Command failed (local modifier on an object that doesn't hold the base behaviour) + FailedBlocked, // Command failed (object is blocked) + FailedThrottled, // Command failed (throttled) + FailedNoProcessor // Command doesn't have a template processor define (legacy code) + }; + + enum class EExceptionCheck + { + Permissive, // Exception can be set by any object + Strict, // Exception must be set by all objects holding the restriction + Default, // Permissive or strict will be determined by currently enforced restrictions + }; + + // Replace&remove in c++23 + template <typename E> + constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, std::underlying_type_t<E>> to_underlying(E e) noexcept + { + return static_cast<std::underlying_type_t<E>>(e); + } + + template <typename E> + constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, bool> has_flag(E value, E flag) noexcept + { + return (to_underlying(value) & to_underlying(flag)) != 0; + } + + constexpr bool isReturnCodeSuccess(ECmdRet eRet) + { + return (to_underlying(eRet) & to_underlying(ECmdRet::Succeeded)) == to_underlying(ECmdRet::Succeeded); + } + + constexpr bool isReturnCodeFailed(ECmdRet eRet) + { + return (to_underlying(eRet) & to_underlying(ECmdRet::Failed)) == to_underlying(ECmdRet::Failed); + } +} + +// ============================================================================ +// Settings +// + +namespace Rlv +{ + namespace Settings + { + constexpr char Main[] = "RestrainedLove"; + constexpr char Debug[] = "RestrainedLoveDebug"; + + constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; + constexpr char EnableExperimentalCommands[] = "RLVaExperimentalCommands"; + constexpr char EnableIMQuery[] = "RLVaEnableIMQuery"; + constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments"; + constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; + }; + +}; + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp new file mode 100644 index 0000000000..8a074fd14d --- /dev/null +++ b/indra/newview/rlvfloaters.cpp @@ -0,0 +1,122 @@ +/** + * @file rlvfloaters.cpp + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagentdata.h" +#include "llchatentry.h" +#include "lltexteditor.h" +#include "lltrans.h" +#include "llvoavatarself.h" + +#include "rlvfloaters.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// FloaterConsole +// + +bool FloaterConsole::postBuild() +{ + mInputEdit = getChild<LLChatEntry>("console_input"); + mInputEdit->setCommitCallback(std::bind(&FloaterConsole::onInput, this)); + mInputEdit->setTextExpandedCallback(std::bind(&FloaterConsole::reshapeLayoutPanel, this)); + mInputEdit->setFocus(true); + mInputEdit->setCommitOnFocusLost(false); + + mInputPanel = getChild<LLLayoutPanel>("input_panel"); + mInputEditPad = mInputPanel->getRect().getHeight() - mInputEdit->getRect().getHeight(); + + mOutputText = getChild<LLTextEditor>("console_output"); + mOutputText->appendText(Constants::ConsolePrompt, false); + + if (RlvHandler::isEnabled()) + { + mCommandOutputConn = RlvHandler::instance().setCommandOutputCallback([this](const RlvCommand& rlvCmd, S32, const std::string strText) + { + if (rlvCmd.getObjectID() == gAgentID) + { + mOutputText->appendText(rlvCmd.getBehaviour() + ": ", true); + mOutputText->appendText(strText, false); + } + }); + } + + return true; +} + +void FloaterConsole::onClose(bool fQuitting) +{ + if (RlvHandler::isEnabled()) + { + RlvHandler::instance().processCommand(gAgentID, "clear", true); + } +} + +void FloaterConsole::onInput() +{ + if (!isAgentAvatarValid()) + { + return; + } + + std::string strText = mInputEdit->getText(); + LLStringUtil::trim(strText); + + mOutputText->appendText(strText, false); + mInputEdit->setText(LLStringUtil::null); + + if (!RlvHandler::isEnabled()) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleDisable"), true); + } + else if (strText.length() <= 3 || Constants::CmdPrefix != strText[0]) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleInvalidCmd"), true); + } + else + { + LLChat chat; + chat.mFromID = gAgentID; + chat.mChatType = CHAT_TYPE_OWNER; + + RlvHandler::instance().handleSimulatorChat(strText, chat, gAgentAvatarp); + + mOutputText->appendText(strText, true); + } + + mOutputText->appendText(Constants::ConsolePrompt, true); +} + +void FloaterConsole::reshapeLayoutPanel() +{ + mInputPanel->reshape(mInputPanel->getRect().getWidth(), mInputEdit->getRect().getHeight() + mInputEditPad, false); +} + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h new file mode 100644 index 0000000000..8acfa43f28 --- /dev/null +++ b/indra/newview/rlvfloaters.h @@ -0,0 +1,68 @@ +/** + * @file rlvfloaters.h + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "llfloater.h" + +#include "rlvdefines.h" + +class LLChatEntry; +class LLFloaterReg; +class LLLayoutPanel; +class LLTextEditor; +class RlvCommand; +class RlvHandler; + +namespace Rlv +{ + // ============================================================================ + // FloaterConsole - debug console to allow command execution without the need for a script + // + + class FloaterConsole : public LLFloater + { + friend class ::LLFloaterReg; + FloaterConsole(const LLSD& sdKey) : LLFloater(sdKey) {} + + public: + bool postBuild() override; + void onClose(bool fQuitting) override; + protected: + void onInput(); + void reshapeLayoutPanel(); + + private: + boost::signals2::scoped_connection mCommandOutputConn; + int mInputEditPad = 0; + LLLayoutPanel* mInputPanel = nullptr; + LLChatEntry* mInputEdit = nullptr; + LLTextEditor* mOutputText = nullptr; + }; + + // ============================================================================ +}; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp new file mode 100644 index 0000000000..6c4b439105 --- /dev/null +++ b/indra/newview/rlvhandler.cpp @@ -0,0 +1,225 @@ +/** + * @file rlvhandler.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llstartup.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" + +#include "rlvcommon.h" +#include "rlvhandler.h" +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// Static variable initialization +// + +bool RlvHandler::mIsEnabled = false; + +// ============================================================================ +// Command processing functions +// + +bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj) +{ + // *TODO: There's an edge case for temporary attachments when going from enabled -> disabled with restrictions already in place + static LLCachedControl<bool> enable_temp_attach(gSavedSettings, Settings::EnableTempAttach); + static LLCachedControl<bool> show_debug_output(gSavedSettings, Settings::Debug); + static LLCachedControl<bool> hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); + + if ( message.length() <= 3 || Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || + (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) ) + { + return false; + } + + message.erase(0, 1); + LLStringUtil::toLower(message); + CommandDbgOut cmdDbgOut(message, chatObj->getID() == gAgentID); + + boost_tokenizer tokens(message, boost::char_separator<char>(",", "", boost::drop_empty_tokens)); + for (const std::string& strCmd : tokens) + { + ECmdRet eRet = processCommand(chat.mFromID, strCmd, true); + if ( show_debug_output() && + (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) ) + { + cmdDbgOut.add(strCmd, eRet); + } + } + + message = cmdDbgOut.get(); + return true; +} + +ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj) +{ + const RlvCommand rlvCmd(idObj, strCmd); + return processCommand(std::ref(rlvCmd), fromObj); +} + +ECmdRet RlvHandler::processCommand(std::reference_wrapper<const RlvCommand> rlvCmd, bool fromObj) +{ + { + const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below + + RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; + if (!rlvCmdTmp.isValid()) + { + RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; + return ECmdRet::FailedSyntax; + } + if (rlvCmdTmp.isBlocked()) + { + RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; + return ECmdRet::FailedDisabled; + } + } + + ECmdRet eRet = ECmdRet::Unknown; + switch (rlvCmd.get().getParamType()) + { + case EParamType::Reply: + eRet = rlvCmd.get().processCommand(); + break; + case EParamType::Unknown: + default: + eRet = ECmdRet::FailedParam; + break; + } + RLV_ASSERT(ECmdRet::Unknown != eRet); + + RLV_DEBUGS << "\t--> command " << (isReturnCodeSuccess(eRet) ? "succeeded" : "failed") << RLV_ENDL; + + return eRet; +} + +// ============================================================================ +// Initialization helper functions +// + +bool RlvHandler::canEnable() +{ + return LLStartUp::getStartupState() <= STATE_LOGIN_CLEANUP; +} + +bool RlvHandler::setEnabled(bool enable) +{ + if (mIsEnabled == enable) + return enable; + + if (enable && canEnable()) + { + RLV_INFOS << "Enabling Restrained Love API support - " << Strings::getVersionAbout() << RLV_ENDL; + mIsEnabled = true; + } + + return mIsEnabled; +} + +// ============================================================================ +// Command handlers (RLV_TYPE_REPLY) +// + +ECmdRet CommandHandlerBaseImpl<EParamType::Reply>::processCommand(const RlvCommand& rlvCmd, ReplyHandlerFunc* pHandler) +{ + // Sanity check - <param> should specify a - valid - reply channel + S32 nChannel; + if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgentID)) + return ECmdRet::FailedParam; + + std::string strReply; + ECmdRet eRet = (*pHandler)(rlvCmd, strReply); + + // If we made it this far then: + // - the command was handled successfully so we send off the response + // - the command failed but we still send off an - empty - response to keep the issuing script from blocking + if (nChannel != 0) + { + Util::sendChatReply(nChannel, strReply); + } + RlvHandler::instance().mOnCommandOutput(rlvCmd, nChannel, strReply); + + return eRet; +} + +// Handles: @getcommand[:<behaviour>[;<type>[;<separator>]]]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::GetCommand>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + std::vector<std::string> optionList; + Util::parseStringList(rlvCmd.getOption(), optionList); + + // If a second parameter is present it'll specify the command type + EParamType eType = EParamType::Unknown; + if (optionList.size() >= 2) + { + if (optionList[1] == "any" || optionList[1].empty()) + eType = EParamType::Unknown; + else if (optionList[1] == "add") + eType = EParamType::AddRem; + else if (optionList[1] == "force") + eType = EParamType::Force; + else if (optionList[1] == "reply") + eType = EParamType::Reply; + else + return ECmdRet::FailedOption; + } + + std::list<std::string> cmdList; + if (BehaviourDictionary::instance().getCommands(!optionList.empty() ? optionList[0] : LLStringUtil::null, eType, cmdList)) + strReply = boost::algorithm::join(cmdList, optionList.size() >= 3 ? optionList[2] : Constants::OptionSeparator); + return ECmdRet::Succeeded; +} + +// Handles: @version=<chnannel> and @versionnew=<channel> +template<> template<> +ECmdRet VersionReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + strReply = Strings::getVersion(EBehaviour::Version == rlvCmd.getBehaviourType()); + return ECmdRet::Succeeded; +} + +// Handles: @versionnum[:impl]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::VersionNum>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + if (!rlvCmd.hasOption()) + strReply = Strings::getVersionNum(); + else if ("impl" == rlvCmd.getOption()) + strReply = Strings::getVersionImplNum(); + else + return ECmdRet::FailedOption; + return ECmdRet::Succeeded; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h new file mode 100644 index 0000000000..38612485b1 --- /dev/null +++ b/indra/newview/rlvhandler.h @@ -0,0 +1,80 @@ +/** + * @file rlvhandler.h + * @author Kitty Barnett + * @brief Primary command process and orchestration class + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "llchat.h" +#include "llsingleton.h" + +#include "rlvhelper.h" + +class LLViewerObject; + +// ============================================================================ +// RlvHandler class +// + +class RlvHandler : public LLSingleton<RlvHandler> +{ + template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; + + LLSINGLETON_EMPTY_CTOR(RlvHandler); + + /* + * Command processing + */ +public: + // Command processing helper functions + bool handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj); + Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); +protected: + Rlv::ECmdRet processCommand(std::reference_wrapper<const RlvCommand> rlvCmdRef, bool fromObj); + + /* + * Helper functions + */ +public: + // Initialization (deliberately static so they can safely be called in tight loops) + static bool canEnable(); + static bool isEnabled() { return mIsEnabled; } + static bool setEnabled(bool enable); + + /* + * Event handling + */ +public: + // The command output signal is triggered whenever a command produces channel or debug output + using command_output_signal_t = boost::signals2::signal<void (const RlvCommand&, S32, const std::string&)>; + boost::signals2::connection setCommandOutputCallback(const command_output_signal_t::slot_type& cb) { return mOnCommandOutput.connect(cb); } + +protected: + command_output_signal_t mOnCommandOutput; +private: + static bool mIsEnabled; +}; + +// ============================================================================ diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp new file mode 100644 index 0000000000..1dac297bf1 --- /dev/null +++ b/indra/newview/rlvhelper.cpp @@ -0,0 +1,389 @@ +/** + * @file rlvhelper.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltrans.h" +#include "llviewercontrol.h" + +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// BehaviourDictionary +// + +BehaviourDictionary::BehaviourDictionary() +{ + // + // Restrictions + // + + // + // Reply-only + // + addEntry(new ReplyProcessor<EBehaviour::GetCommand>("getcommand")); + addEntry(new ReplyProcessor<EBehaviour::Version, VersionReplyHandler>("version")); + addEntry(new ReplyProcessor<EBehaviour::VersionNew, VersionReplyHandler>("versionnew")); + addEntry(new ReplyProcessor<EBehaviour::VersionNum>("versionnum")); + + // Populate mString2InfoMap (the tuple <behaviour, type> should be unique) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + mString2InfoMap.insert(std::make_pair(std::make_pair(bhvr_info_p->getBehaviour(), static_cast<EParamType>(bhvr_info_p->getParamTypeMask())), bhvr_info_p)); + } + + // Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(EParamType::AddRem)) && !bhvr_info_p->isSynonym()) + { +#ifdef RLV_DEBUG + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(bhvr_info_p->getBehaviourType()), mBhvr2InfoMap.upper_bound(bhvr_info_p->getBehaviourType()))) + { + RLV_ASSERT((itBhvr.first != bhvr_info_p->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != bhvr_info_p->getBehaviourFlags())); + } +#endif // RLV_DEBUG + mBhvr2InfoMap.insert(std::pair(bhvr_info_p->getBehaviourType(), bhvr_info_p)); + } + } +} + +BehaviourDictionary::~BehaviourDictionary() +{ + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + delete bhvr_info_p; + } + mBhvrInfoList.clear(); +} + +void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) +{ + // Filter experimental commands (if disabled) + static LLCachedControl<bool> sEnableExperimental(gSavedSettings, Settings::EnableExperimentalCommands); + if (!entry_p || (!sEnableExperimental && entry_p->isExperimental())) + { + return; + } + + // Sanity check for duplicate entries +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), + [&entry_p](const BehaviourInfo* bhvr_info_p) { + RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); + }); +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + + mBhvrInfoList.push_back(entry_p); +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const +{ + const BehaviourInfo* bhvr_info_p = nullptr; + for (auto itBhvrLower = mBhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = mBhvr2InfoMap.upper_bound(eBhvr); + std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const auto& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == to_underlying(eParamType); }) != itBhvrUpper; + ++itBhvrLower) + { + if (bhvr_info_p) + return nullptr; + bhvr_info_p = itBhvrLower->second; + } + return bhvr_info_p; +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p) const +{ + size_t idxBhvrLastPart = strBhvr.find_last_of('_'); + std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + + bool isStrict = (strBhvrLastPart.compare("sec") == 0); + if (is_strict_p) + *is_strict_p = isStrict; + + auto itBhvr = mString2InfoMap.find(std::make_pair((!isStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if ((mString2InfoMap.end() == itBhvr) && (!isStrict) && (!strBhvrLastPart.empty()) && (EParamType::Force == eParamType)) + { + // No match found but it could still be a local scope modifier + auto itBhvrMod = mString2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), EParamType::AddRem)); + } + + return ((itBhvr != mString2InfoMap.end()) && ((!isStrict) || (itBhvr->second->hasStrict()))) ? itBhvr->second : nullptr; +} + +EBehaviour BehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* pisStrict) const +{ + const BehaviourInfo* bhvr_info_p = getBehaviourInfo(strBhvr, eParamType, pisStrict); + // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own + return bhvr_info_p->getBehaviourType(); +} + +bool BehaviourDictionary::getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const +{ + cmdList.clear(); + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(eParamType)) || (EParamType::Unknown == eParamType)) + { + std::string strCmd = bhvr_info_p->getBehaviour(); + if ((std::string::npos != strCmd.find(strMatch)) || (strMatch.empty())) + cmdList.push_back(strCmd); + if ((bhvr_info_p->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty()))) + cmdList.push_back(strCmd); + } + } + return !cmdList.empty(); +} + +bool BehaviourDictionary::getHasStrict(EBehaviour eBhvr) const +{ + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(eBhvr), mBhvr2InfoMap.upper_bound(eBhvr))) + { + // Only restrictions can be strict + if (to_underlying(EParamType::AddRem) != itBhvr.second->getParamTypeMask()) + continue; + return itBhvr.second->hasStrict(); + } + RLV_ASSERT(false); + return false; +} + +void BehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable) +{ + auto itBhvr = mString2InfoMap.find(std::make_pair(strBhvr, (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if (mString2InfoMap.end() != itBhvr) + { + const_cast<BehaviourInfo*>(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable); + } +} + +// ============================================================================ +// RlvCommmand +// + +RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCmd) + : mObjId(idObj) +{ + if (parseCommand(strCmd, mBehaviour, mOption, mParam)) + { + if ("n" == mParam || "add" == mParam) + mParamType = EParamType::Add; + else if ("y" == mParam || "rem" == mParam) + mParamType = EParamType::Remove; + else if ("clear" == mBehaviour) // clear is the odd one out so just make it its own type + mParamType = EParamType::Clear; + else if ("force" == mParam) + mParamType = EParamType::Force; + else if (S32 nTemp; LLStringUtil::convertToS32(mParam, nTemp)) // Assume it's a reply command if we can convert <param> to an S32 + mParamType = EParamType::Reply; + } + + mIsValid = mParamType != EParamType::Unknown; + if (!mIsValid) + { + mOption.clear(); + mParam.clear(); + return; + } + + mBhvrInfo = BehaviourDictionary::instance().getBehaviourInfo(mBehaviour, mParamType, &mIsStrict); +} + +RlvCommand::RlvCommand(const RlvCommand& rlvCmd, EParamType eParamType) + : mIsValid(rlvCmd.mIsValid), mObjId(rlvCmd.mObjId), mBehaviour(rlvCmd.mBehaviour), mBhvrInfo(rlvCmd.mBhvrInfo) + , mParamType( (EParamType::Unknown == eParamType) ? rlvCmd.mParamType : eParamType) + , mIsStrict(rlvCmd.mIsStrict), mOption(rlvCmd.mOption), mParam(rlvCmd.mParam), mIsRefCounted(rlvCmd.mIsRefCounted) +{ +} + +bool RlvCommand::parseCommand(const std::string& strCmd, std::string& strBhvr, std::string& strOption, std::string& strParam) +{ + // Format: <behaviour>[:<option>]=<param> + const size_t idxOption = strCmd.find(':'); + const size_t idxParam = strCmd.find('='); + + // If <behaviour> is missing it's always an improperly formatted command + // If there's an option, but it comes after <param> it's also invalid + if ( (idxOption == 0 || idxParam == 0) || + (idxOption != std::string::npos && idxOption >= idxParam) ) + { + return false; + } + + strBhvr = strCmd.substr(0, std::string::npos != idxOption ? idxOption : idxParam); + strOption = strParam = ""; + + // If <param> is missing it's an improperly formatted command + if (idxParam == std::string::npos || idxParam + 1 == strCmd.length()) + { + // Unless "<behaviour> == "clear" AND (idxOption == 0)" + // OR <behaviour> == "clear" AND (idxParam != 0) + if (strBhvr == "clear" && (!idxOption || idxParam)) + return true; + return false; + } + + if (idxOption != std::string::npos && idxOption + 1 != idxParam) + strOption = strCmd.substr(idxOption + 1, idxParam - idxOption - 1); + strParam = strCmd.substr(idxParam + 1); + + return true; +} + +std::string RlvCommand::asString() const +{ + // NOTE: @clear=<param> should be represented as clear:<param> + return mParamType != EParamType::Clear + ? getBehaviour() + (!mOption.empty() ? ":" + mOption : "") + : getBehaviour() + (!mParam.empty() ? ":" + mParam : ""); +} + +// ========================================================================= +// Various helper classes/timers/functors +// + +namespace Rlv +{ + // =========================================================================== + // CommandDbgOut + // + + void CommandDbgOut::add(std::string strCmd, ECmdRet eRet) + { + const std::string strSuffix = getReturnCodeString(eRet); + if (!strSuffix.empty()) + strCmd.append(llformat(" (%s)", strSuffix.c_str())); + else if (mForConsole) + return; // Only show console feedback on successful commands when there's an informational notice + + std::string& strResult = mCommandResults[isReturnCodeSuccess(eRet) ? ECmdRet::Succeeded : (ECmdRet::Retained == eRet ? ECmdRet::Retained : ECmdRet::Failed)]; + if (!strResult.empty()) + strResult.append(", "); + strResult.append(strCmd); + } + + std::string CommandDbgOut::get() const { + std::ostringstream result; + + if (1 == mCommandResults.size() && !mForConsole) + { + auto it = mCommandResults.begin(); + result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second; + } + else if (!mCommandResults.empty()) + { + auto appendResult = [&](ECmdRet eRet, const std::string& name) + { + auto it = mCommandResults.find(eRet); + if (it == mCommandResults.end()) return; + if (!mForConsole) result << "\n - "; + result << LLTrans::getString(name) << ": @" << it->second; + }; + if (!mForConsole) + result << ": @" << mOrigCmd; + appendResult(ECmdRet::Succeeded, !mForConsole ? "RlvDebugExecuted" : "RlvConsoleExecuted"); + appendResult(ECmdRet::Failed, !mForConsole ? "RlvDebugFailed" : "RlvConsoleFailed"); + appendResult(ECmdRet::Retained, !mForConsole ? "RlvDebugRetained" : "RlvConsoleRetained"); + } + + return result.str(); + } + + std::string CommandDbgOut::getDebugVerbFromReturnCode(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::Succeeded: + return LLTrans::getString("RlvDebugExecuted"); + case ECmdRet::Failed: + return LLTrans::getString("RlvDebugFailed"); + case ECmdRet::Retained: + return LLTrans::getString("RlvDebugRetained"); + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + std::string CommandDbgOut::getReturnCodeString(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::SuccessUnset: + return LLTrans::getString("RlvReturnCodeUnset"); + case ECmdRet::SuccessDuplicate: + return LLTrans::getString("RlvReturnCodeDuplicate"); + case ECmdRet::SuccessDelayed: + return LLTrans::getString("RlvReturnCodeDelayed"); + case ECmdRet::SuccessDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecated"); + case ECmdRet::FailedSyntax: + return LLTrans::getString("RlvReturnCodeSyntax"); + case ECmdRet::FailedOption: + return LLTrans::getString("RlvReturnCodeOption"); + case ECmdRet::FailedParam: + return LLTrans::getString("RlvReturnCodeParam"); + case ECmdRet::FailedLock: + return LLTrans::getString("RlvReturnCodeLock"); + case ECmdRet::FailedDisabled: + return LLTrans::getString("RlvReturnCodeDisabled"); + case ECmdRet::FailedUnknown: + return LLTrans::getString("RlvReturnCodeUnknown"); + case ECmdRet::FailedNoSharedRoot: + return LLTrans::getString("RlvReturnCodeNoSharedRoot"); + case ECmdRet::FailedDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecatedAndDisabled"); + case ECmdRet::FailedNoBehaviour: + return LLTrans::getString("RlvReturnCodeNoBehaviour"); + case ECmdRet::FailedUnheldBehaviour: + return LLTrans::getString("RlvReturnCodeUnheldBehaviour"); + case ECmdRet::FailedBlocked: + return LLTrans::getString("RlvReturnCodeBlocked"); + case ECmdRet::FailedThrottled: + return LLTrans::getString("RlvReturnCodeThrottled"); + case ECmdRet::FailedNoProcessor: + return LLTrans::getString("RlvReturnCodeNoProcessor"); + // The following are identified by the chat verb + case ECmdRet::Retained: + case ECmdRet::Succeeded: + case ECmdRet::Failed: + return LLStringUtil::null; + // The following shouldn't occur + case ECmdRet::Unknown: + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + // =========================================================================== +} + +// ============================================================================ diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h new file mode 100644 index 0000000000..f241332594 --- /dev/null +++ b/indra/newview/rlvhelper.h @@ -0,0 +1,299 @@ +/** + * @file rlvhelper.h + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "rlvdefines.h" + +// ============================================================================ +// Forward declarations +// + +class RlvCommand; + +// ============================================================================ + +namespace Rlv +{ + // ============================================================================ + // BehaviourInfo class - Generic behaviour descriptor (used by restrictions, reply and force commands) + // + + class BehaviourInfo + { + public: + enum EBehaviourFlags : uint32_t + { + // General behaviour flags + Strict = 0x0001, // Behaviour has a "_sec" version + Synonym = 0x0002, // Behaviour is a synonym of another + Extended = 0x0004, // Behaviour is part of the RLVa extended command set + Experimental = 0x0008, // Behaviour is part of the RLVa experimental command set + Blocked = 0x0010, // Behaviour is blocked + Deprecated = 0x0020, // Behaviour is deprecated + MaskGeneral = 0x0FFF, + + // Force-wear specific flags + ForceWear_WearReplace = 0x0001 << 16, + ForceWear_WearAdd = 0x0002 << 16, + ForceWear_WearRemove = 0x0004 << 16, + ForceWear_Node = 0x0010 << 16, + ForceWear_Subtree = 0x0020 << 16, + ForceWear_ContextNone = 0x0100 << 16, + ForceWear_ContextObject = 0x0200 << 16, + MaskForceWear = 0xFFFFu << 16 + }; + + BehaviourInfo(const std::string& strBhvr, EBehaviour eBhvr, EParamType maskParamType, std::underlying_type_t<EBehaviourFlags> nBhvrFlags = 0) + : mBhvr(strBhvr), mBhvrType(eBhvr), mBhvrFlags(nBhvrFlags), mMaskParamType(to_underlying(maskParamType)) {} + virtual ~BehaviourInfo() {} + + const std::string& getBehaviour() const { return mBhvr; } + EBehaviour getBehaviourType() const { return mBhvrType; } + std::underlying_type_t<EBehaviourFlags> getBehaviourFlags() const { return mBhvrFlags; } + std::underlying_type_t<EParamType> getParamTypeMask() const { return mMaskParamType; } + bool hasStrict() const { return mBhvrFlags & Strict; } + bool isBlocked() const { return mBhvrFlags & Blocked; } + bool isExperimental() const { return mBhvrFlags & Experimental; } + bool isExtended() const { return mBhvrFlags & Extended; } + bool isSynonym() const { return mBhvrFlags & Synonym; } + void toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); + + virtual ECmdRet processCommand(const RlvCommand& rlvCmd) const { return ECmdRet::FailedNoProcessor; } + + protected: + std::string mBhvr; + EBehaviour mBhvrType; + std::underlying_type_t<EBehaviourFlags> mBhvrFlags; + std::underlying_type_t<EParamType> mMaskParamType; + }; + + inline void BehaviourInfo::toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable) + { + if (fEnable) + mBhvrFlags |= eBhvrFlag; + else + mBhvrFlags &= ~eBhvrFlag; + } + + // ============================================================================ + // BehaviourDictionary and related classes + // + + class BehaviourDictionary : public LLSingleton<BehaviourDictionary> + { + LLSINGLETON(BehaviourDictionary); + protected: + ~BehaviourDictionary() override; + public: + void addEntry(const BehaviourInfo* entry_p); + + /* + * General helper functions + */ + public: + EBehaviour getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; + const BehaviourInfo* getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const; + const BehaviourInfo* getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; + bool getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const; + bool getHasStrict(EBehaviour eBhvr) const; + void toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBvhrFlag, bool fEnable); + + /* + * Member variables + */ + protected: + std::list<const BehaviourInfo*> mBhvrInfoList; + std::map<std::pair<std::string, EParamType>, const BehaviourInfo*> mString2InfoMap; + std::multimap<EBehaviour, const BehaviourInfo*> mBhvr2InfoMap; + }; + + // ============================================================================ + // CommandHandler and related classes + // + + typedef ECmdRet(BhvrHandlerFunc)(const RlvCommand&, bool&); + typedef void(BhvrToggleHandlerFunc)(EBehaviour, bool); + typedef ECmdRet(ForceHandlerFunc)(const RlvCommand&); + typedef ECmdRet(ReplyHandlerFunc)(const RlvCommand&, std::string&); + + // + // CommandHandlerBaseImpl - Base implementation for each command type (the old process(AddRem|Force|Reply)Command functions) + // + template<EParamType paramType> struct CommandHandlerBaseImpl; + template<> struct CommandHandlerBaseImpl<EParamType::AddRem> { static ECmdRet processCommand(const RlvCommand&, BhvrHandlerFunc*, BhvrToggleHandlerFunc* = nullptr); }; + template<> struct CommandHandlerBaseImpl<EParamType::Force> { static ECmdRet processCommand(const RlvCommand&, ForceHandlerFunc*); }; + template<> struct CommandHandlerBaseImpl<EParamType::Reply> { static ECmdRet processCommand(const RlvCommand&, ReplyHandlerFunc*); }; + + // + // CommandHandler - The actual command handler (Note that a handler is more general than a processor; a handler can - for instance - be used by multiple processors) + // + #if LL_WINDOWS + #define RLV_TEMPL_FIX(x) template<x> + #else + #define RLV_TEMPL_FIX(x) template<typename Placeholder = int> + #endif // LL_WINDOWS + + + template <EParamType templParamType, EBehaviour templBhvr> + struct CommandHandler + { + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static ECmdRet onCommand(const RlvCommand&, bool&); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static void onCommandToggle(EBehaviour, bool); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Force>::type) static ECmdRet onCommand(const RlvCommand&); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Reply>::type) static ECmdRet onCommand(const RlvCommand&, std::string&); + }; + + // Aliases to improve readability in definitions + template<EBehaviour templBhvr> using BehaviourHandler = CommandHandler<EParamType::AddRem, templBhvr>; + template<EBehaviour templBhvr> using BehaviourToggleHandler = BehaviourHandler<templBhvr>; + template<EBehaviour templBhvr> using ForceHandler = CommandHandler<EParamType::Force, templBhvr>; + template<EBehaviour templBhvr> using ReplyHandler = CommandHandler<EParamType::Reply, templBhvr>; + + // List of shared handlers + using VersionReplyHandler = ReplyHandler<EBehaviour::Version>; // Shared between @version and @versionnew + + // + // CommandProcessor - Templated glue class that brings BehaviourInfo, CommandHandlerBaseImpl and CommandHandler together + // + template <EParamType templParamType, EBehaviour templBhvr, typename handlerImpl = CommandHandler<templParamType, templBhvr>, typename baseImpl = CommandHandlerBaseImpl<templParamType>> + class CommandProcessor : public BehaviourInfo + { + public: + // Default constructor used by behaviour specializations + RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr != EBehaviour::Unknown>::type) + CommandProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, templBhvr, templParamType, nBhvrFlags) {} + + // Constructor used when we don't want to specialize on behaviour (see BehaviourGenericProcessor) + RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr == EBehaviour::Unknown>::type) + CommandProcessor(const std::string& strBhvr, EBehaviour eBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, templParamType, nBhvrFlags) {} + + ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return baseImpl::processCommand(rlvCmd, &handlerImpl::onCommand); } + }; + + // Aliases to improve readability in definitions + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::AddRem, templBhvr>> using BehaviourProcessor = CommandProcessor<EParamType::AddRem, templBhvr, handlerImpl>; + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Force, templBhvr>> using ForceProcessor = CommandProcessor<EParamType::Force, templBhvr, handlerImpl>; + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Reply, templBhvr>> using ReplyProcessor = CommandProcessor<EParamType::Reply, templBhvr, handlerImpl>; + + // Provides pre-defined generic implementations of basic behaviours (template voodoo - see original commit for something that still made sense) + template<EBehaviourOptionType templOptionType> struct BehaviourGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd, bool& fRefCount); }; + template<EBehaviourOptionType templOptionType> using BehaviourGenericProcessor = BehaviourProcessor<EBehaviour::Unknown, BehaviourGenericHandler<templOptionType>>; + template<EBehaviourOptionType templOptionType> struct ForceGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd); }; + template<EBehaviourOptionType templOptionType> using ForceGenericProcessor = ForceProcessor<EBehaviour::Unknown, ForceGenericHandler<templOptionType>>; + + // ============================================================================ + // BehaviourProcessor and related classes - Handles add/rem comamnds aka "restrictions) + // + + template <EBehaviour eBhvr, typename handlerImpl = BehaviourHandler<eBhvr>, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> + class BehaviourToggleProcessor : public BehaviourInfo + { + public: + BehaviourToggleProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, EParamType::AddRem, nBhvrFlags) {} + ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return CommandHandlerBaseImpl<EParamType::AddRem>::processCommand(rlvCmd, &handlerImpl::onCommand, &toggleHandlerImpl::onCommandToggle); } + }; + template <EBehaviour eBhvr, EBehaviourOptionType optionType, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> using RlvBehaviourGenericToggleProcessor = BehaviourToggleProcessor<eBhvr, BehaviourGenericHandler<optionType>, toggleHandlerImpl>; + + // ============================================================================ + // Various helper classes/timers/functors + // + + struct CommandDbgOut + { + CommandDbgOut(const std::string& orig_cmd, bool for_console) : mOrigCmd(orig_cmd), mForConsole(for_console) {} + void add(std::string strCmd, ECmdRet eRet); + std::string get() const; + static std::string getDebugVerbFromReturnCode(ECmdRet eRet); + static std::string getReturnCodeString(ECmdRet eRet); + private: + std::string mOrigCmd; + std::map<ECmdRet, std::string> mCommandResults; + bool mForConsole = false; + }; +} + +// ============================================================================ +// RlvCommand +// + +class RlvCommand +{ +public: + explicit RlvCommand(const LLUUID& idObj, const std::string& strCmd); + RlvCommand(const RlvCommand& rlvCmd, Rlv::EParamType eParamType = Rlv::EParamType::Unknown); + + /* + * Member functions + */ +public: + std::string asString() const; + const std::string& getBehaviour() const { return mBehaviour; } + const Rlv::BehaviourInfo* getBehaviourInfo() const { return mBhvrInfo; } + Rlv::EBehaviour getBehaviourType() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourType() : Rlv::EBehaviour::Unknown; } + U32 getBehaviourFlags() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourFlags() : 0; } + const LLUUID& getObjectID() const { return mObjId; } + const std::string& getOption() const { return mOption; } + const std::string& getParam() const { return mParam; } + Rlv::EParamType getParamType() const { return mParamType; } + bool hasOption() const { return !mOption.empty(); } + bool isBlocked() const { return (mBhvrInfo) ? mBhvrInfo->isBlocked() : false; } + bool isRefCounted() const { return mIsRefCounted; } + bool isStrict() const { return mIsStrict; } + bool isValid() const { return mIsValid; } + Rlv::ECmdRet processCommand() const { return (mBhvrInfo) ? mBhvrInfo->processCommand(*this) : Rlv::ECmdRet::FailedNoProcessor; } + +protected: + static bool parseCommand(const std::string& strCommand, std::string& strBehaviour, std::string& strOption, std::string& strParam); + bool markRefCounted() const { return mIsRefCounted = true; } + + /* + * Operators + */ +public: + bool operator ==(const RlvCommand&) const; + + /* + * Member variables + */ +protected: + bool mIsValid = false; + LLUUID mObjId; + std::string mBehaviour; + const Rlv::BehaviourInfo* mBhvrInfo = nullptr; + Rlv::EParamType mParamType = Rlv::EParamType::Unknown; + bool mIsStrict = false; + std::string mOption; + std::string mParam; + mutable bool mIsRefCounted = false; + + friend class RlvHandler; + friend class RlvObject; + template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; +}; + +// ============================================================================ diff --git a/indra/newview/scripts/lua/frame_profile.lua b/indra/newview/scripts/lua/frame_profile.lua index 3c6353ff68..4d0874f55e 100644 --- a/indra/newview/scripts/lua/frame_profile.lua +++ b/indra/newview/scripts/lua/frame_profile.lua @@ -4,11 +4,13 @@ LLAgent = require 'LLAgent' startup = require 'startup' Timer = (require 'timers').Timer UI = require 'UI' +teleport_util = require('teleport_util') startup.wait('STATE_STARTED') -- teleport to http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 print(LLAgent.teleport{regionname='Bug Island', x=220, y=224, z=27}) +--teleport_util.wait() Timer(10, 'wait') LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true, focus_pos ={228, 232, 26}, focus_locked=true} diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 4a1132fe7e..6068a916ed 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -1,8 +1,26 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local result_view = require 'result_view' + +local function result(keys) + local result_table = { + result=result_view(keys.result), + -- call result_table:close() to release result sets before garbage + -- collection or script completion + close = function(self) + result_view.close(keys.result[1]) + end + } + -- When the result_table is destroyed, close its result_views. + return LL.setdtor('LLAgent result', result_table, result_table.close) +end local LLAgent = {} +function LLAgent.getID() + return leap.request('LLAgent', {op = 'getID'}).id +end + function LLAgent.getRegionPosition() return leap.request('LLAgent', {op = 'getPosition'}).region end @@ -95,6 +113,33 @@ function LLAgent.requestStand() leap.send('LLAgent', {op = 'requestStand'}) end +-- Get the nearby avatars in a range of provided "dist", +-- if "dist" is not specified, "RenderFarClip" setting is used +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "name", "region_id" +function LLAgent.getNearbyAvatarsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyAvatarsList' + return result(leap.request('LLAgent', args)) +end + +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "region_id" +function LLAgent.getNearbyObjectsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyObjectsList' + return result(leap.request('LLAgent', args)) +end + +-- Get screen position of your own avatar or any other (if "avatar_id" is specified) +-- reply contains "x", "y" coordinates and "onscreen" flag to indicate if it's actually in within the current window +-- avatar render position is used as the point +function LLAgent.getAgentScreenPos(...) + local args = mapargs('avatar_id', ...) + args.op = 'getAgentScreenPos' + return leap.request('LLAgent', args) +end + -- *************************************************************************** -- Autopilot -- *************************************************************************** diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index cf2695917e..34f3fb75eb 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -222,8 +222,10 @@ function UI.hideFloater(floater_name) leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name}) end -function UI.toggleFloater(floater_name) - leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name}) +function UI.toggleFloater(...) + local args = mapargs('name,key', ...) + args.op = 'toggleInstance' + leap.send("LLFloaterReg", args) end function UI.isFloaterVisible(floater_name) diff --git a/indra/newview/scripts/lua/require/teleport_util.lua b/indra/newview/scripts/lua/require/teleport_util.lua new file mode 100644 index 0000000000..8a46768e54 --- /dev/null +++ b/indra/newview/scripts/lua/require/teleport_util.lua @@ -0,0 +1,28 @@ +local leap = require 'leap' + +local teleport_util = {} + +local teleport_pump = 'LLTeleport' +local waitfor = leap.WaitFor(0, teleport_pump) +function waitfor:filter(pump, data) + if pump == self.name then + return data + end +end + +function waitfor:process(data) + teleport_util._success = data.success + leap.WaitFor.process(self, data) +end + +leap.request(leap.cmdpump(), + {op='listen', source=teleport_pump, listener='teleport.lua', tweak=true}) + +function teleport_util.wait() + while teleport_util._success == nil do + local item = waitfor:wait() + end + return teleport_util._success +end + +return teleport_util diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua index 0f269b54e6..fa00b048b2 100644 --- a/indra/newview/scripts/lua/test_LLChatListener.lua +++ b/indra/newview/scripts/lua/test_LLChatListener.lua @@ -1,5 +1,6 @@ -local LLListener = require 'LLListener' +local LLAgent = require 'LLAgent' local LLChat = require 'LLChat' +local LLListener = require 'LLListener' local UI = require 'UI' -- Chat listener script allows to use the following commands in Nearby chat: @@ -23,9 +24,13 @@ function openOrEcho(message) end local listener = LLListener(LLChat.nearbyChatPump) +local agent_id = LLAgent.getID() function listener:handleMessages(event_data) - if string.find(event_data.message, '[LUA]') then + -- ignore messages and commands from other avatars + if event_data.from_id ~= agent_id then + return true + elseif string.find(event_data.message, '[LUA]') then return true elseif event_data.message == 'stop' then LLChat.sendNearby('Closing echo script.') diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua index 37e7254a6c..ae5eabe148 100644 --- a/indra/newview/scripts/lua/test_animation.lua +++ b/indra/newview/scripts/lua/test_animation.lua @@ -7,8 +7,8 @@ animations_id = LLInventory.getBasicFolderID('animatn') anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items local anim_ids = {} -for key in pairs(anims) do - table.insert(anim_ids, key) +for _, key in pairs(anims) do + table.insert(anim_ids, {id = key.id, name = key.name}) end if #anim_ids == 0 then @@ -16,17 +16,17 @@ if #anim_ids == 0 then else -- Start playing a random animation math.randomseed(os.time()) - local random_id = anim_ids[math.random(#anim_ids)] - local anim_info = LLAgent.getAnimationInfo(random_id) + local random_anim = anim_ids[math.random(#anim_ids)] + local anim_info = LLAgent.getAnimationInfo(random_anim.id) - print("Starting animation locally: " .. anims[random_id].name) - print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) - LLAgent.playAnimation{item_id=random_id} + print("Starting animation locally: " .. random_anim.name) + print("Loop: " .. tostring(anim_info.is_loop) .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) + LLAgent.playAnimation{item_id=random_anim.id} -- Stop animation after 3 sec if it's looped or longer than 3 sec - if anim_info.is_loop == 1 or anim_info.duration > 3 then + if anim_info.is_loop or anim_info.duration > 3 then LL.sleep(3) print("Stop animation.") - LLAgent.stopAnimation(random_id) + LLAgent.stopAnimation(random_anim.id) end end diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 2158134511..23310c6176 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -14,7 +14,7 @@ function flt:handleEvents(event_data) end function flt:commit_disable_ctrl(event_data) - self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) + self:post({action="set_enabled", ctrl_name="open_btn", value = not event_data.value}) end function flt:commit_title_cmb(event_data) diff --git a/indra/newview/scripts/lua/test_nearby_avatars.lua b/indra/newview/scripts/lua/test_nearby_avatars.lua new file mode 100644 index 0000000000..c7fa686076 --- /dev/null +++ b/indra/newview/scripts/lua/test_nearby_avatars.lua @@ -0,0 +1,25 @@ +inspect = require 'inspect' +LLAgent = require 'LLAgent' + +-- Get the list of nearby avatars and print the info +local nearby_avatars = LLAgent.getNearbyAvatarsList() +if nearby_avatars.result.length == 0 then + print("No avatars nearby") +else + print("Nearby avatars:") + for _, av in pairs(nearby_avatars.result) do + print(av.name ..' ID: ' .. av.id ..' Global pos:' .. inspect(av.global_pos)) + end +end + +-- Get the list of nearby objects in 3m range and print the info +local nearby_objects = LLAgent.getNearbyObjectsList(3) +if nearby_objects.result.length == 0 then + print("No objects nearby") +else + print("Nearby objects:") + local pos={} + for _, obj in pairs(nearby_objects.result) do + print('ID: ' .. obj.id ..' Global pos:' .. inspect(obj.global_pos)) + end +end diff --git a/indra/newview/scripts/lua/test_screen_position.lua b/indra/newview/scripts/lua/test_screen_position.lua new file mode 100644 index 0000000000..94d57339b1 --- /dev/null +++ b/indra/newview/scripts/lua/test_screen_position.lua @@ -0,0 +1,8 @@ +LLAgent = require 'LLAgent' + +local screen_pos = LLAgent.getAgentScreenPos() +if screen_pos.onscreen then + print("Avatar screen coordinates X: " .. screen_pos.x .. " Y: " .. screen_pos.y) +else + print("Avatar is not on the screen") +end diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua index 7683fca8a3..9cd1043446 100644 --- a/indra/newview/scripts/lua/test_toolbars.lua +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -20,7 +20,7 @@ if response == 'OK' then UI.removeToolbarBtn(BUTTONS[i]) end end - popup:tip('Toolbars were reshuffled') + UI.popup:tip('Toolbars were reshuffled') else - popup:tip('Canceled') + UI.popup:tip('Canceled') end diff --git a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index d52845160b..6a2acfedf7 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -15,6 +15,9 @@ <panel.string name="acquiredDate"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </panel.string> + <panel.string name="acquiredDateAMPM"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </panel.string> <panel.string name="origin_inventory"> (Beholdning) </panel.string> diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index e4f99d14e9..c4275d43f7 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -3723,6 +3723,10 @@ Hvis du bliver ved med at modtage denne besked, kontakt venligst [SUPPORT_SITE]. <string name="conference-title-incoming"> Konference med [AGENT_NAME] </string> + <string name="bot_warning"> +Du chatter med en bot, [NAME]. Del ikke personlige oplysninger. +Læs mere på https://second.life/scripted-agents. + </string> <string name="no_session_message"> (IM session eksisterer ikke) </string> diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml index bb9ab26ef5..9c93ff38ad 100644 --- a/indra/newview/skins/default/xui/de/floater_about_land.xml +++ b/indra/newview/skins/default/xui/de/floater_about_land.xml @@ -25,7 +25,8 @@ <panel.string name="none_text">(keiner)</panel.string> <panel.string name="sale_pending_text">(Wird verkauft)</panel.string> <panel.string name="no_selection_text">Keine Parzelle ausgewählt.</panel.string> - <panel.string name="time_stamp_template">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</panel.string> + <panel.string name="time_stamp_template_ampm">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt]</panel.string> + <panel.string name="time_stamp_template">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</panel.string> <text name="Name:">Name:</text> <text name="Description:">Beschreibung:</text> <text name="LandType">Typ:</text> diff --git a/indra/newview/skins/default/xui/de/floater_inspect.xml b/indra/newview/skins/default/xui/de/floater_inspect.xml index da97ceb2d8..a193e1123e 100644 --- a/indra/newview/skins/default/xui/de/floater_inspect.xml +++ b/indra/newview/skins/default/xui/de/floater_inspect.xml @@ -1,7 +1,10 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater min_width="460" name="inspect" title="OBJEKTE UNTERSUCHEN" width="460"> + <floater.string name="timeStampAMPM"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + </floater.string> <floater.string name="timeStamp"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </floater.string> <scroll_list name="object_list" tool_tip="Wählen Sie ein Objekt aus dieser Liste, um es in der Second Life-Welt zu markieren"> <scroll_list.columns label="Objektname" name="object_name"/> diff --git a/indra/newview/skins/default/xui/de/panel_classified_info.xml b/indra/newview/skins/default/xui/de/panel_classified_info.xml index 007e9d69f0..1b8caf5f78 100644 --- a/indra/newview/skins/default/xui/de/panel_classified_info.xml +++ b/indra/newview/skins/default/xui/de/panel_classified_info.xml @@ -13,7 +13,7 @@ [TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil </panel.string> <panel.string name="date_fmt"> - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] </panel.string> <panel.string name="auto_renew_on"> Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml index 10cf34c170..8726d5e645 100644 --- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml @@ -15,8 +15,11 @@ <string name="server_forbidden_text"> Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. </string> + <string name="acquired_date_ampm"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + </string> <string name="acquired_date"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> diff --git a/indra/newview/skins/default/xui/de/panel_place_profile.xml b/indra/newview/skins/default/xui/de/panel_place_profile.xml index 4077fdab36..a0b5a1e9dc 100644 --- a/indra/newview/skins/default/xui/de/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/de/panel_place_profile.xml @@ -38,8 +38,11 @@ <string name="server_forbidden_text"> Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. </string> + <string name="acquired_date_ampm"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + </string> <string name="acquired_date"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> diff --git a/indra/newview/skins/default/xui/de/panel_profile_classified.xml b/indra/newview/skins/default/xui/de/panel_profile_classified.xml index 5c11a01977..5a4b42870c 100644 --- a/indra/newview/skins/default/xui/de/panel_profile_classified.xml +++ b/indra/newview/skins/default/xui/de/panel_profile_classified.xml @@ -13,7 +13,7 @@ [TELEPORT] Teleportieren, [MAP] Karten, [PROFILE] Profil </panel.string> <panel.string name="date_fmt"> - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] </panel.string> <panel.string name="auto_renew_on"> Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml index baaa58e1d7..437cde99fe 100644 --- a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml +++ b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml @@ -1,5 +1,11 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="Profil" name="panel_profile"> + <string + name="date_format_full" + value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]" /> + <string + name="date_format_short" + value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]." /> <string name="status_online"> Zurzeit online </string> diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml index 0829814220..e017c6dd82 100644 --- a/indra/newview/skins/default/xui/de/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml @@ -10,7 +10,7 @@ [hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt] </panel.string> <panel.string name="timeTooltip"> - [weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt] + [weekday, datetime, slt], [day, datetime, slt]. [month, datetime, slt] [year, datetime, slt] </panel.string> <panel.string name="buycurrencylabel"> [AMT] L$ diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml index 168bb14248..30886d1433 100644 --- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml @@ -19,7 +19,10 @@ Eigentümer kann: </panel.string> <panel.string name="acquiredDate"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] + </panel.string> + <panel.string name="acquiredDateAMPM"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] </panel.string> <panel.string name="origin_inventory"> (Inventar) diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 486d604e9f..dd003e72d9 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -39,7 +39,7 @@ Audiotreiberversion: [AUDIO_DRIVER_VERSION] LibVLC-Version: [LIBVLC_VERSION] Voice-Server-Version: [VOICE_VERSION]</string> <string name="AboutTraffic">Paketverlust: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1] %)</string> - <string name="AboutTime">[month, datetime, slt] [day, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> + <string name="AboutTime">[day, datetime, slt]. [month, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> <string name="ErrorFetchingServerReleaseNotesURL">Fehler beim Abrufen der URL für die Server-Versionshinweise.</string> <string name="BuildConfiguration">Build-Konfiguration</string> <string name="ProgressRestoring">Wird wiederhergestellt...</string> @@ -812,7 +812,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo <string name="Unknown">(unbekannt)</string> <string name="SummaryForTheWeek" value="Zusammenfassung für diese Woche, beginnend am "/> <string name="NextStipendDay" value=". Der nächste Stipendium-Tag ist "/> - <string name="GroupPlanningDate">[mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc]</string> + <string name="GroupPlanningDate">[day,datetime,utc].[mthnum,datetime,utc].[year,datetime,utc]</string> <string name="GroupIndividualShare" value=" Gruppenanteil Einzelanteil"/> <string name="GroupColumn" value="Gruppe"/> <string name="Balance">Kontostand</string> @@ -961,7 +961,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo <string name="GroupMoneyBalance">Kontostand</string> <string name="GroupMoneyCredits">Danksagung</string> <string name="GroupMoneyDebits">Soll</string> - <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string> + <string name="GroupMoneyDate">[weekday,datetime,utc] [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]</string> <string name="AcquiredItems">Erworbene Artikel</string> <string name="Cancel">Abbrechen</string> <string name="UploadingCosts">Das Hochladen von [NAME] kostet [AMOUNT] L$</string> @@ -1613,6 +1613,10 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_ <string name="conference-title-incoming">Konferenz mit [AGENT_NAME]</string> <string name="inventory_item_offered-im">Inventarobjekt „[ITEM_NAME]“ angeboten</string> <string name="inventory_folder_offered-im">Inventarordner „[ITEM_NAME]“ angeboten</string> + <string name="bot_warning"> + Sie chatten mit einem Bot, [NAME]. Geben Sie keine persönlichen Informationen weiter. +Erfahren Sie mehr unter https://second.life/scripted-agents. + </string> <string name="share_alert">Objekte aus dem Inventar hier her ziehen</string> <string name="facebook_post_success">Sie haben auf Facebook gepostet.</string> <string name="flickr_post_success">Sie haben auf Flickr gepostet.</string> @@ -1765,7 +1769,7 @@ Missbrauchsbericht</string> <string name="dance6">Tanzen6</string> <string name="dance7">Tanzen7</string> <string name="dance8">Tanzen8</string> - <string name="AvatarBirthDateFormat">[mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]</string> + <string name="AvatarBirthDateFormat">[day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt]</string> <string name="DefaultMimeType">Keine/Keiner</string> <string name="texture_load_dimensions_error">Bilder, die größer sind als [WIDTH]*[HEIGHT] können nicht geladen werden</string> <string name="outfit_photo_load_dimensions_error">Max. Fotogröße für Outfit ist [WIDTH]*[HEIGHT]. Bitte verkleinern Sie das Bild oder verwenden Sie ein anderes.</string> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 508aba6ae1..c5b42b6dae 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -125,6 +125,9 @@ name="no_selection_text"> No parcel selected. </panel.string> + <panel.string name="time_stamp_template_ampm"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </panel.string> <panel.string name="time_stamp_template"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </panel.string> diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 9403d58441..a083683c23 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -12,6 +12,10 @@ title="INSPECT OBJECTS" width="400"> <floater.string + name="timeStampAMPM"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </floater.string> + <floater.string name="timeStamp"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </floater.string> diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index 6c3214a76d..1aa216d6b4 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -28,6 +28,10 @@ name="acquiredDate"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </floater.string> + <floater.string + name="acquiredDateAMPM"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </floater.string> <icon follows="top|right" height="18" diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml index 597744238c..623f282305 100644 --- a/indra/newview/skins/default/xui/en/floater_joystick.xml +++ b/indra/newview/skins/default/xui/en/floater_joystick.xml @@ -35,7 +35,7 @@ <spinner bottom="56" height="10" - control_name="Axis1" + control_name="JoystickAxis1" decimal_digits="0" increment="1" label="X Axis Mapping" @@ -44,12 +44,12 @@ left="20" max_val="5" min_val="-1" - name="Axis1" + name="JoystickAxis1" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis2" + height="10" + control_name="JoystickAxis2" decimal_digits="0" increment="1" label="Y Axis Mapping" @@ -58,12 +58,12 @@ left="190" max_val="5" min_val="-1" - name="Axis2" + name="JoystickAxis2" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis0" + height="10" + control_name="JoystickAxis0" decimal_digits="0" increment="1" label="Z Axis Mapping" @@ -72,12 +72,12 @@ left="360" max_val="5" min_val="-1" - name="Axis0" + name="JoystickAxis0" width="140" /> <spinner bottom="76" - height="10" - control_name="Axis4" + height="10" + control_name="JoystickAxis4" decimal_digits="0" increment="1" label="Pitch Mapping" @@ -86,12 +86,12 @@ left="20" max_val="5" min_val="-1" - name="Axis4" + name="JoystickAxis4" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis5" + height="10" + control_name="JoystickAxis5" decimal_digits="0" increment="1" label="Yaw Mapping" @@ -100,12 +100,12 @@ left="190" max_val="5" min_val="-1" - name="Axis5" + name="JoystickAxis5" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis3" + height="10" + control_name="JoystickAxis3" decimal_digits="0" increment="1" label="Roll Mapping" @@ -114,12 +114,12 @@ left="360" max_val="5" min_val="-1" - name="Axis3" + name="JoystickAxis3" width="140" /> <spinner bottom="96" - height="10" - control_name="Axis6" + height="10" + control_name="JoystickAxis6" decimal_digits="0" increment="1" label="Zoom Mapping" @@ -128,7 +128,7 @@ left="20" max_val="5" min_val="-1" - name="Axis6" + name="JoystickAxis6" width="140" /> <check_box bottom_delta="18" @@ -141,7 +141,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="Cursor3D" label="3D Cursor" layout="topleft" @@ -150,7 +150,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="AutoLeveling" label="Auto Level" layout="topleft" @@ -173,7 +173,7 @@ </text> <check_box bottom="134" - height="10" + height="10" control_name="JoystickAvatarEnabled" halign="center" label="Avatar" @@ -183,7 +183,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="JoystickBuildEnabled" halign="center" label="Build" @@ -193,7 +193,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="JoystickFlycamEnabled" halign="center" label="Flycam" @@ -213,7 +213,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 0" label_spacing="1" layout="topleft" @@ -225,7 +225,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 1" label_spacing="1" layout="topleft" @@ -234,7 +234,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 2" label_spacing="1" layout="topleft" @@ -243,7 +243,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 3" label_spacing="1" layout="topleft" @@ -252,7 +252,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 4" label_spacing="1" layout="topleft" @@ -262,7 +262,7 @@ bar_max="2" bar_min="-2" label="Axis 5" - show_bar="true" + show_bar="true" label_spacing="1" layout="topleft" name="axis5" @@ -281,7 +281,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale1" decimal_digits="2" label_width="0" @@ -293,7 +293,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale1" decimal_digits="2" label_width="0" @@ -305,7 +305,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale1" decimal_digits="2" label_width="0" @@ -328,7 +328,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale2" decimal_digits="2" label_width="0" @@ -340,7 +340,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale2" decimal_digits="2" label_width="0" @@ -352,7 +352,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale2" decimal_digits="2" label_width="0" @@ -375,7 +375,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale0" decimal_digits="2" label_width="0" @@ -387,7 +387,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale0" decimal_digits="2" label_width="0" @@ -399,7 +399,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale0" decimal_digits="2" label_width="0" @@ -422,7 +422,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale4" decimal_digits="2" label_width="0" @@ -434,7 +434,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale4" decimal_digits="2" label_width="0" @@ -446,7 +446,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale4" decimal_digits="2" label_width="0" @@ -595,7 +595,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisDeadZone2" decimal_digits="2" increment="0.01" diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index 15027f1647..5efe1c958a 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,6 +25,17 @@ width="100"> LUA string: </text> + <check_box + control_name="LuaDebugShowSource" + follows="right|top" + height="15" + label="Show source info" + layout="topleft" + top="10" + right ="-70" + tool_tip="Show source info in Lua Debug Console output" + name="show_script_name" + width="70"/> <line_editor border_style="line" border_thickness="1" diff --git a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml index 6859201650..211cea75d8 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml @@ -22,6 +22,7 @@ follows="all" layout="topleft" sort_column="script_name" + multi_select="true" name="scripts_list" top_pad="10" width="535"> diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 73ea0f548e..e81ff7f343 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -821,7 +821,7 @@ layout="topleft" left="420" min_val="0.5" - max_val="1.5" + max_val="4.0" name="RenderExposure" show_text="true" top_delta="24" diff --git a/indra/newview/skins/default/xui/en/floater_rlv_console.xml b/indra/newview/skins/default/xui/en/floater_rlv_console.xml new file mode 100644 index 0000000000..708055d1b6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_rlv_console.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_resize="true" + height="400" + layout="topleft" + min_height="300" + min_width="300" + name="rlv_console" + title="RLVa console" + width="600" + > + <layout_stack + animate="false" + bottom="-1" + default_tab_group="2" + follows="all" + left="5" + layout="topleft" + mouse_opaque="false" + name="main_stack" + right="-5" + orientation="vertical" + tab_group="1" + top="1" + > + <layout_panel + name="body_panel" + height="235"> + <text_editor + follows="all" + left="1" + right="-1" + top="0" + length="1" + font="Monospace" + bottom="-1" + ignore_tab="false" + layout="topleft" + max_length="65536" + name="console_output" + read_only="true" + track_end="true" + type="string" + word_wrap="true" + > + </text_editor> + </layout_panel> + + <layout_panel + height="26" + auto_resize="false" + name="input_panel"> + <chat_editor + layout="topleft" + expand_lines_count="5" + follows="left|right|bottom" + font="SansSerifSmall" + height="20" + is_expandable="true" + text_tentative_color="TextFgTentativeColor" + name="console_input" + max_length="1023" + spellcheck="true" + tab_group="3" + bottom_delta="20" + left="1" + top="1" + right="-1" + show_emoji_helper="false" + wrap="true" + /> + </layout_panel> + </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 5ab0177de6..6883ec1cd0 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -109,7 +109,7 @@ </panel> <panel follows="right|top" - height="126" + height="150" top_pad="4" width="238" left="1" @@ -285,6 +285,29 @@ by owner </text> + <check_box + name="grid_coords_chk" + control_name="MapShowGridCoords" + layout="topleft" + follows="top|right" + top_pad="2" + left="3" + height="16" + width="22" + /> + <text + name="grid_coords_label" + type="string" + layout="topleft" + follows="top|right" + top_delta="2" + left_pad="3" + height="16" + width="220" + halign="left" + length="1" + >Show grid map coordinates</text> + <button follows="top|right" height="22" @@ -455,7 +478,7 @@ <panel follows="right|top|bottom" - height="330" + height="306" top_pad="0" width="238" name="layout_panel_4"> @@ -576,7 +599,7 @@ draw_stripes="false" bg_writeable_color="MouseGray" follows="all" - height="145" + height="121" layout="topleft" left="28" name="search_results" diff --git a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml index 6074b72dc8..ef3d7ee4f7 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml @@ -22,7 +22,7 @@ halign="left" name="action" tool_tip="Camera move forward/backward" - value="Advance Forward" /> + value="Forward/Backward" /> </rows> <rows name="pan" @@ -33,7 +33,7 @@ halign="left" name="action" tool_tip="Camera pan left/right" - value="Pan Left" /> + value="Pan Left/Right" /> </rows> <rows name="rise" @@ -44,7 +44,7 @@ halign="left" name="action" tool_tip="Camera rise/fall" - value="Rise Up" /> + value="Rise Up/Down" /> </rows> <rows name="pitch" @@ -55,7 +55,7 @@ halign="left" name="action" tool_tip="Camera adjust pitch" - value="Pitch Down" /> + value="Pitch Up/Down" /> </rows> <rows name="yaw" @@ -66,7 +66,7 @@ halign="left" name="action" tool_tip="Camera turn left/right" - value="Yaw CCW" /> + value="Yaw CCW/CW" /> </rows> <rows name="zoom" @@ -77,7 +77,7 @@ halign="left" name="action" tool_tip="Camera zoom in/out" - value="Zoom In" /> + value="Zoom In/Out" /> </rows> <!-- <rows diff --git a/indra/newview/skins/default/xui/en/game_control_table_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_rows.xml index 181bc8592c..cd6f483782 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_rows.xml @@ -21,7 +21,7 @@ font="SansSerif" halign="left" name="action" - value="Walk Forward" /> + value="Forward/Backward" /> </rows> <rows name="slide" @@ -31,7 +31,7 @@ font="SansSerif" halign="left" name="action" - value="Strafe Left" /> + value="Strafe" /> </rows> <rows name="jump" @@ -51,7 +51,7 @@ font="SansSerif" halign="left" name="action" - value="Turn Left" /> + value="Turn" /> </rows> <rows name="look" @@ -61,7 +61,7 @@ font="SansSerif" halign="left" name="action" - value="Look Down" /> + value="Look Up/Down" /> </rows> <rows name="stop" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index da9cc0c98d..53615968e0 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -562,25 +562,6 @@ </menu_item_check> <menu_item_separator/> <menu_item_check - label="Hear Media and Sound from Avatar" - name="Hear Media and Sound from Avatar"> - <menu_item_check.on_check - control="MediaSoundsEarLocation" /> - <menu_item_check.on_click - function="Agent.ToggleHearMediaSoundFromAvatar" /> - </menu_item_check> - <menu_item_check - label="Hear Voice from Avatar" - name="Hear Voice from Avatar"> - <menu_item_check.on_check - control="VoiceEarLocation" /> - <menu_item_check.on_click - function="Agent.ToggleHearVoiceFromAvatar" /> - <menu_item_call.on_enable - control="EnableVoiceChat" /> - </menu_item_check> - <menu_item_separator/> - <menu_item_check label="Gestures..." name="Gestures" shortcut="control|G"> @@ -1838,6 +1819,71 @@ function="World.EnvPreset" </menu> <menu create_jump_keys="true" + label="RLVa" + name="RLVa Main" + tear_off="true" + visible="true"> + <menu + label="Debug" + name="Debug" + tear_off="true"> + <menu_item_check + label="Show Top-level RLVa Menu" + name="Show Top-level RLVa Menu"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaTopLevelMenu" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaTopLevelMenu" /> + </menu_item_check> + <menu_item_separator/> + <menu_item_check + label="Show Debug Messages" + name="Show Debug Messages"> + <menu_item_check.on_check + function="CheckControl" + parameter="RestrainedLoveDebug" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RestrainedLoveDebug" /> + </menu_item_check> + <menu_item_check + label="Hide Unset or Duplicate Messages" + name="Hide Unset or Duplicate Messages"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaDebugHideUnsetDuplicate" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaDebugHideUnsetDuplicate" /> + </menu_item_check> + </menu> + <menu_item_separator/> + <menu_item_check + label="Allow Temporary Attachments" + name="Allow Temporary Attachments"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaEnableTemporaryAttachments" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaEnableTemporaryAttachments" /> + </menu_item_check> + <menu_item_separator /> + <menu_item_check + label="Console..." + name="Console"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="rlv_console" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="rlv_console" /> + </menu_item_check> + </menu> + <menu + create_jump_keys="true" label="Advanced" name="Advanced" tear_off="true" @@ -2343,7 +2389,12 @@ function="World.EnvPreset" function="Advanced.ToggleFeature" parameter="flexible" /> </menu_item_check> - </menu> + </menu> + <menu + label="RLVa" + name="RLVa Embedded" + tear_off="true" + visible="true" /> <menu_item_check label="Use Plugin Read Thread" name="Use Plugin Read Thread"> @@ -3756,6 +3807,13 @@ function="World.EnvPreset" </menu_item_call> <menu_item_call enabled="true" + label="Edit Local Paintmap" + name="Edit Local Paintmap"> + <menu_item_call.on_click + function="Advanced.TerrainEditLocalPaintMap" /> + </menu_item_call> + <menu_item_call + enabled="true" label="Delete Local Paintmap" name="Delete Local Paintmap"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 071f6458c5..cbc0f7ceae 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1662,7 +1662,7 @@ The new skin will appear after you restart [APP_NAME]. icon="alertmodal.tga" name="ChangeLanguage" type="alertmodal"> -Changing language will take effect after you restart [APP_NAME]. +Changing language or time format will take effect after you restart [APP_NAME]. </notification> <notification @@ -6532,6 +6532,22 @@ Do you want to replace it with the selected object? </notification> <notification + icon="alertmodal.tga" + name="CantLinkNotecard" + type="alertmodal"> + You must save the notecard before creating a link to it. + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" + name="CantLinkMaterial" + type="alertmodal"> + You must save the material before creating a link to it. + <tag>fail</tag> + </notification> + + <notification icon="alert.tga" label="Do Not Disturb Mode Warning" name="DoNotDisturbModePay" @@ -12587,4 +12603,34 @@ are wearing now. yestext="OK"/> </notification> + <notification + icon="notify.tga" + name="TextureDiscardBackgrounded" + type="notify"> + <unique> + <context>DELAY</context> + </unique> + To improve system performance, [SECOND_LIFE] has reduced texture memory usage after being in the background for [DELAY] seconds. It may take some time for texture image quality to return to normal. + <usetemplate + ignoretext="Ask me about background texture memory usage and recovery" + name="okcancelignore" + yestext="OK" + notext="Disable"/> + </notification> + + <notification + icon="notify.tga" + name="TextureDiscardMinimized" + type="notify"> + <unique> + <context>DELAY</context> + </unique> + To improve system performance, [SECOND_LIFE] has reduced texture memory usage after being minimized for [DELAY] seconds. It may take some time for texture image quality to return to normal. + <usetemplate + ignoretext="Ask me about minimized texture memory usage and recovery" + name="okcancelignore" + yestext="OK" + notext="Disable"/> + </notification> + </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml index ca6e94397d..53551b2f79 100644 --- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml @@ -156,6 +156,5 @@ mouse_opaque="true" name="speaking_indicator" tool_tip="Voice volume" - visible="true" width="20" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml index 7902588598..e0b7f71321 100644 --- a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml @@ -91,7 +91,6 @@ left_pad="5" mouse_opaque="true" name="speaking_indicator" - visible="false" width="20" /> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/panel_landmark_info.xml b/indra/newview/skins/default/xui/en/panel_landmark_info.xml index af68bd7fee..19599efdc2 100644 --- a/indra/newview/skins/default/xui/en/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/en/panel_landmark_info.xml @@ -40,6 +40,10 @@ Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. </string> <string + name="acquired_date_ampm"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </string> + <string name="acquired_date"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </string> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml index 1a89d07cbb..48f6580d61 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml @@ -34,7 +34,6 @@ mouse_opaque="true" name="chat_zone_indicator" top="6" - visible="true" width="20" /> <button follows="right" diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index 8f5292c531..bb877080b1 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -89,6 +89,10 @@ Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. </string> <string + name="acquired_date_ampm"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </string> + <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml index bd92fe895d..fb6cc8dc14 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml @@ -62,6 +62,7 @@ label="Action" relative_width="0.25" /> <scroll_list.columns + name="channel" label="Channel" relative_width="0.75" /> </scroll_list> @@ -81,6 +82,7 @@ right="-1" bottom="-1">No device</text> <panel + name="device_selection_panel" layout="topleft" follows="top|left|right" top="1" @@ -156,15 +158,19 @@ selection_type="header" fg_disable_color="ScrollUnselectedColor"> <scroll_list.columns + name="axis" label="Axis" relative_width="0.5"/> <scroll_list.columns + name="invert" label="Invert" relative_width="0.1"/> <scroll_list.columns + name="dead_zone" label="Dead Zone" relative_width="0.2"/> <scroll_list.columns + name="offset" label="Offset" relative_width="0.2"/> </scroll_list> @@ -212,6 +218,7 @@ </panel> </tab_container> <panel + name="reset_to_defaults_panel" layout="topleft" follows="top|left" top="405" @@ -237,13 +244,13 @@ name="axis_selector" width="250" height="18"> - <combo_box.item label="AXIS_0 (Left stick: tilt left/right)"/> - <combo_box.item label="AXIS_1 (Left stick: tilt forward/back)"/> - <combo_box.item label="AXIS_2 (Right stick: tilt left/right)"/> - <combo_box.item label="AXIS_3 (Right stick: tilt forward/back)"/> - <combo_box.item label="AXIS_4 (Left trigger: push/release)"/> - <combo_box.item label="AXIS_5 (Right trigger: push/release)"/> - <combo_box.item label="NONE (No mapping)"/> + <combo_box.item name="axis_0" label="AXIS_0 (Left stick: tilt left/right)"/> + <combo_box.item name="axis_1" label="AXIS_1 (Left stick: tilt forward/back)"/> + <combo_box.item name="axis_2" label="AXIS_2 (Right stick: tilt left/right)"/> + <combo_box.item name="axis_3" label="AXIS_3 (Right stick: tilt forward/back)"/> + <combo_box.item name="axis_4" label="AXIS_4 (Left trigger: push/release)"/> + <combo_box.item name="axis_5" label="AXIS_5 (Right trigger: push/release)"/> + <combo_box.item name="axis_none" label="NONE (No mapping)"/> </combo_box> <combo_box name="analog_channel_selector" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 101c506309..ddddb4855f 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -91,6 +91,37 @@ name="Traditional Chinese" value="zh" /> </combo_box> + <text + type="string" + length="1" + follows="left|top" + height="15" + layout="topleft" + left="255" + name="time_format_textbox" + top="10" + width="200"> + Time Format: + </text> + <combo_box + follows="left|top" + height="23" + layout="topleft" + left="255" + max_chars="135" + name="time_format_combobox" + width="70"> + <combo_box.item + enabled="true" + label="1:00 AM" + name="12H" + value="0" /> + <combo_box.item + enabled="true" + label="13:00" + name="24H" + value="1" /> + </combo_box> <text font="SansSerifSmall" type="string" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index cb3e0e4b6a..a64b3eee36 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -371,7 +371,7 @@ layout="topleft" left="30" min_val="0.5" - max_val="1.5" + max_val="4.0" name="RenderExposure" show_text="true" top_pad="14" diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml index 6ef1b6e44a..47e1e669d1 100644 --- a/indra/newview/skins/default/xui/en/panel_region_general.xml +++ b/indra/newview/skins/default/xui/en/panel_region_general.xml @@ -33,6 +33,28 @@ unknown </text> <text + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + right="-100" + name="estate_id_lbl" + width="80"> + Estate ID: + </text> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + name="estate_id" + enabled="false" + right="-10" + initial_value="unknown" + width="85" /> + <text follows="left|top" font="SansSerif" height="20" @@ -55,6 +77,39 @@ unknown </text> <text + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + right="-100" + name="grid_position_lbl" + width="80"> + Grid Position: + </text> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + right="-55" + name="grid_position_x" + enabled="false" + top_delta="0" + default_text="n/a" + width="40" /> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + right="-10" + name="grid_position_y" + enabled="false" + top_delta="0" + default_text="n/a" + width="40" /> + <text follows="left|top" font="SansSerif" height="20" diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index 1cb3eca2eb..40a88d4121 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -32,6 +32,10 @@ Owner can: </panel.string> <panel.string + name="acquiredDateAMPM"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </panel.string> + <panel.string name="acquiredDate"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </panel.string> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index effd19b708..9be4a8f737 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -60,6 +60,7 @@ Disk cache: [DISK_CACHE_INFO] HiDPI display mode: [HIDPI] </string> <string name="AboutLibs"> +RestrainedLove API: [RLV_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] [LIBCEF_VERSION] @@ -3719,6 +3720,10 @@ Please reinstall viewer from https://secondlife.com/support/downloads/ and cont <string name="inventory_folder_offered-im"> Inventory folder '[ITEM_NAME]' offered </string> + <string name="bot_warning"> + You are chatting with a bot, [NAME]. Do not share any personal information. +Learn more at https://second.life/scripted-agents. + </string> <string name="share_alert"> Drag items from inventory here </string> @@ -4381,4 +4386,31 @@ and report the problem. [https://community.secondlife.com/knowledgebase/english/error-messages-r520/#Section__3 Knowledge Base] </string> + <!-- RLVa --> + <string name="RlvConsoleDisable">RLVa is disabled</string> + <string name="RlvConsoleInvalidCmd">Invalid command</string> + <string name="RlvConsoleExecuted">INFO</string> + <string name="RlvConsoleFailed">ERR</string> + <string name="RlvConsoleRetained">RET</string> + <string name="RlvDebugExecuted">executed</string> + <string name="RlvDebugFailed">failed</string> + <string name="RlvDebugRetained">retained</string> + <string name="RlvReturnCodeUnset">unset</string> + <string name="RlvReturnCodeDuplicate">duplicate</string> + <string name="RlvReturnCodeDelayed">delayed</string> + <string name="RlvReturnCodeDeprecated">deprecated</string> + <string name="RlvReturnCodeSyntax">thingy error</string> + <string name="RlvReturnCodeOption">invalid option</string> + <string name="RlvReturnCodeParam">invalid param</string> + <string name="RlvReturnCodeLock">locked command</string> + <string name="RlvReturnCodeDisabled">disabled command</string> + <string name="RlvReturnCodeUnknown">unknown command</string> + <string name="RlvReturnCodeNoSharedRoot">missing #RLV</string> + <string name="RlvReturnCodeDeprecatedAndDisabled">deprecated and disabled</string> + <string name="RlvReturnCodeNoBehaviour">no active behaviours</string> + <string name="RlvReturnCodeUnheldBehaviour">base behaviour not held</string> + <string name="RlvReturnCodeBlocked">blocked object</string> + <string name="RlvReturnCodeThrottled">throttled</string> + <string name="RlvReturnCodeNoProcessor">no command processor found</string> + </strings> diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml index cf995e5833..180071a321 100644 --- a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml @@ -37,6 +37,5 @@ right="-3" mouse_opaque="true" name="speaking_indicator" - visible="true" - width="20" /> + width="20" /> </conversation_view_participant> diff --git a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml index 788c733ca8..10e39fa030 100644 --- a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml +++ b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml @@ -10,4 +10,5 @@ image_level_3="VoicePTT_Lvl3" mouse_opaque="false" name="output_monitor" + visible="false" /> diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml index 82dbdf0dab..e07f340be1 100644 --- a/indra/newview/skins/default/xui/en/widgets/person_view.xml +++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml @@ -111,7 +111,6 @@ right="-3" mouse_opaque="true" name="speaking_indicator" - visible="false" width="20" /> </person_view> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 9fcfc2daa5..97e86e994c 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1584,6 +1584,10 @@ Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conferencia con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Ítem del inventario '[ITEM_NAME]' ofrecido</string> <string name="inventory_folder_offered-im">Carpeta del inventario '[ITEM_NAME]' ofrecida</string> + <string name="bot_warning"> +Estás conversando con un bot, [NAME]. No compartas información personal. +Más información en https://second.life/scripted-agents. + </string> <string name="share_alert">Arrastra los ítems desde el invenbtario hasta aquí</string> <string name="facebook_post_success">Has publicado en Facebook.</string> <string name="flickr_post_success">Has publicado en Flickr.</string> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 55f6209fe1..60916ef92b 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1614,6 +1614,10 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conférence avec [AGENT_NAME]</string> <string name="inventory_item_offered-im">Objet de l’inventaire [ITEM_NAME] offert</string> <string name="inventory_folder_offered-im">Dossier de l’inventaire [ITEM_NAME] offert</string> + <string name="bot_warning"> +Vous discutez avec un bot, [NAME]. Ne partagez pas d’informations personnelles. +En savoir plus sur https://second.life/scripted-agents. + </string> <string name="share_alert">Faire glisser les objets de l'inventaire ici</string> <string name="facebook_post_success">Vous avez publié sur Facebook.</string> <string name="flickr_post_success">Vous avez publié sur Flickr.</string> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f77ab1062a..88708a2b4d 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1586,6 +1586,10 @@ Se il messaggio persiste, contatta [SUPPORT_SITE].</string> <string name="conference-title-incoming">Chiamata in conferenza con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Offerto oggetto di inventario "[ITEM_NAME]"</string> <string name="inventory_folder_offered-im">Offerta cartella di inventario "[ITEM_NAME]"</string> + <string name="bot_warning"> +Stai parlando con un bot, [NAME]. Non condividere informazioni personali. +Scopri di più su https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Hai pubblicato su Facebook.</string> <string name="flickr_post_success">Hai pubblicato su Flickr.</string> <string name="twitter_post_success">Hai pubblicato su Twitter.</string> diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index fa6c329fe7..ff3b1a53a2 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -6150,6 +6150,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ <string name="inventory_folder_offered-im"> フォルダ「[ITEM_NAME]」がインベントリに送られてきました。 </string> + <string name="bot_warning"> +[NAME]とチャットしています。個人情報を共有しないでください。 +詳細は https://second.life/scripted-agents をご覧ください。 + </string> <string name="share_alert"> インベントリからここにアイテムをドラッグします。 </string> diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 8032443020..65b487e1b3 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -4412,6 +4412,10 @@ Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE]. <string name="inventory_folder_offered-im"> Zaoferowano folder: '[ITEM_NAME]' </string> + <string name="bot_warning"> +Rozmawiasz z botem [NAME]. Nie udostępniaj żadnych danych osobowych. +Dowiedz się więcej na https://second.life/scripted-agents. + </string> <string name="share_alert"> Przeciągaj tutaj rzeczy z Szafy </string> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 4ce1e6d2ec..9e66777b5a 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1549,6 +1549,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conversa com [AGENT_NAME]</string> <string name="inventory_item_offered-im">Item do inventário '[ITEM_NAME]' oferecido</string> <string name="inventory_folder_offered-im">Pasta do inventário '[ITEM_NAME]' oferecida</string> + <string name="bot_warning"> +Você está conversando com um bot, [NAME]. Não compartilhe informações pessoais. +Saiba mais em https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Você publicou no Facebook.</string> <string name="flickr_post_success">Você publicou no Flickr.</string> <string name="twitter_post_success">Você publicou no Twitter.</string> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 0079309ba2..174999ea36 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -4576,6 +4576,10 @@ support@secondlife.com. <string name="inventory_folder_offered-im"> Предложена папка инвентаря «[ITEM_NAME]» </string> + <string name="bot_warning"> +Вы общаетесь с ботом [NAME]. Не передавайте личные данные. +Подробнее на https://second.life/scripted-agents. + </string> <string name="share_alert"> Перетаскивайте вещи из инвентаря сюда </string> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index fa2fd3a802..6c1f6506a2 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -4579,6 +4579,10 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. <string name="inventory_folder_offered-im"> "[ITEM_NAME]" envanter klasörü sunuldu </string> + <string name="bot_warning"> +Bir bot ile sohbet ediyorsunuz, [NAME]. Kişisel bilgilerinizi paylaşmayın. +Daha fazla bilgi için: https://second.life/scripted-agents. + </string> <string name="share_alert"> Envanterinizden buraya öğeler sürükleyin </string> diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index bdb16c9bf1..a3a9915dc4 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -4573,6 +4573,10 @@ http://secondlife.com/support 求助解決問題。 <string name="inventory_folder_offered-im"> 收納區資料夾'[ITEM_NAME]'已向人提供 </string> + <string name="bot_warning"> +您正在与人工智能机器人 [NAME] 聊天。请勿分享任何个人信息。 +了解更多:https://second.life/scripted-agents。 + </string> <string name="share_alert"> 將收納區物品拖曳到這裡 </string> diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index f0a1b32eed..9687b68451 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -55,14 +55,14 @@ namespace tut // indra/newview/tests/llluamanager_test.cpp => // indra/newview auto newview{ fsyspath(__FILE__).parent_path().parent_path() }; - auto settings{ newview / "app_settings" / "settings.xml" }; + fsyspath settings{ newview / "app_settings" / "settings.xml" }; // true suppresses implicit declare; implicit declare requires // that every variable in settings.xml has a Comment, which many don't. - gSavedSettings.loadFromFile(settings.u8string(), true); + gSavedSettings.loadFromFile(settings, true); // At test time, since we don't have the app bundle available, // extend LuaRequirePath to include the require directory in the // source tree. - auto require{ (newview / "scripts" / "lua" / "require").u8string() }; + std::string require{ fsyspath(newview / "scripts" / "lua" / "require") }; auto paths{ gSavedSettings.getLLSD("LuaRequirePath") }; bool found = false; for (const auto& path : llsd::inArray(paths)) diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index d5bf189d82..99c98f63ed 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -28,6 +28,7 @@ // Dependencies #include "linden_common.h" #include "llapr.h" +#include "llcontrol.h" // LLControlGroup #include "llsingleton.h" #include "lltrans.h" #include "lluistring.h" @@ -71,6 +72,8 @@ void LLUIString::updateResult() const { } void LLUIString::setArg(const std::string& , const std::string& ) { } void LLUIString::assign(const std::string& ) { } +LLControlGroup gSavedSettings("Global"); // saved at end of session + // End Stubbing // ------------------------------------------------------------------------------------------- @@ -131,6 +134,7 @@ namespace tut // Constructor and destructor of the test wrapper worldmap_test() { + gSavedSettings.declareBOOL("Use24HourClock", true, "", LLControlVariable::PERSIST_NO); mWorld = LLWorldMap::getInstance(); } ~worldmap_test() diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index efc90b8991..18ff6cf7b6 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1032,6 +1032,12 @@ class Darwin_x86_64_Manifest(ViewerManifest): ): dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # SDL2 + for libfile in ( + 'libSDL2-2.0.dylib', + ): + dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # our apps executable_path = {} embedded_apps = [ (os.path.join("llplugin", "slplugin"), "SLPlugin.app") ] |