# Conflicts:
#	indra/newview/llskinningutil.cpp
#	indra/newview/llvoavatar.cpp
#	indra/newview/skins/default/xui/ja/strings.xml
master
Ansariel 2025-05-23 12:22:21 +02:00
commit 6cb5a7425f
883 changed files with 25627 additions and 17180 deletions

View File

@ -922,11 +922,11 @@
<key>archive</key>
<map>
<key>hash</key>
<string>ff3057e8763bdbe87a478a3d77067b5a</string>
<string>19e40aa358c1784b49eccd547e704647</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>file:///opt/firestorm/fmodstudio-2.02.26-darwin64-243641704.tar.bz2</string>
<string>file:///opt/firestorm/fmodstudio-2.03.07-darwin64-251121740.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
@ -936,11 +936,11 @@
<key>archive</key>
<map>
<key>hash</key>
<string>41265f539399e365601a22d108287e91</string>
<string>a459e2967306ff56a835a321dc00718c</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>file:///opt/firestorm/fmodstudio-2.02.26-linux64-243641703.tar.bz2</string>
<string>file:///opt/firestorm/fmodstudio-2.03.07-linux64-251121739.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
@ -950,11 +950,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>595e7aa51f2161b8d11c3afcc88e2197</string>
<key>hash_algorithm</key>
<string>md5</string>
<string>4a0da36b4a31332df62dadf6438f935e</string>
<key>url</key>
<string>file:///c:/cygwin/opt/firestorm/fmodstudio-2.02.26-windows64-243641704.tar.bz2</string>
<string>file:///c:/cygwin/opt/firestorm/fmodstudio-2.03.07-windows64-251121127.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>

View File

@ -140,10 +140,9 @@ public:
LLVector3 mHeadOffset{}; // current head position
LLAvatarJoint* mRoot{ nullptr };
// <FS:ND> This map gets queried a huge amount of time.
//<FS:Ansariel> Joint-lookup improvements
// typedef std::map<std::string, LLJoint*> joint_map_t;
typedef std::unordered_map<U32, LLJoint*> joint_map_t;
// </FS:ND>
typedef std::map<std::string, LLJoint*, std::less<>> joint_map_t;
joint_map_t mJointMap;
@ -157,7 +156,7 @@ public:
public:
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
typedef std::map<std::string, std::string> joint_alias_map_t;
typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
const joint_alias_map_t& getJointAliases();

View File

@ -44,9 +44,9 @@
#include "sound_ids.h"
const U32 EXTRA_SOUND_CHANNELS = 10;
constexpr U32 EXTRA_SOUND_CHANNELS = 10;
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels);
FMOD_RESULT F_CALL windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels);
FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0};
@ -69,13 +69,13 @@ static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
return true;
}
LLUUID FMOD_GUID_to_LLUUID(FMOD_GUID guid)
static LLUUID FMOD_GUID_to_LLUUID(FMOD_GUID guid)
{
return LLUUID(llformat("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]));
}
void set_device(FMOD::System* system, const LLUUID& device_uuid)
static void set_device(FMOD::System* system, const LLUUID& device_uuid)
{
LL_INFOS() << "LLAudioEngine_FMODSTUDIO::setDevice with device_uuid=" << device_uuid << LL_ENDL;
@ -113,7 +113,7 @@ void set_device(FMOD::System* system, const LLUUID& device_uuid)
}
}
FMOD_RESULT F_CALLBACK systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
{
FMOD::System* sys = (FMOD::System*)system;
LLAudioEngine_FMODSTUDIO* audio_engine = (LLAudioEngine_FMODSTUDIO*)userdata;
@ -881,7 +881,7 @@ void LLAudioChannelFMODSTUDIO::set3DMode(bool use3d)
// not the main thread. May have implications for callees or audio
// engine shutdown.
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels)
FMOD_RESULT F_CALL windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels)
{
// inbuffer = fmod's original mixbuffer.
// outbuffer = the buffer passed from the previous DSP unit.

View File

@ -131,7 +131,7 @@ LLQuaternion::Order bvhStringToOrder( char *str )
// LLBVHLoader()
//-----------------------------------------------------------------------------
LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map )
LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map )
{
reset();
errorLine = 0;
@ -156,9 +156,9 @@ LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &error
}
// Recognize all names we've been told are legal.
for (std::map<std::string, std::string>::value_type& alias_pair : joint_alias_map)
for (const auto& [alias, joint] : joint_alias_map)
{
makeTranslation( alias_pair.first , alias_pair.second );
makeTranslation(alias, joint);
}
char error_text[128]; /* Flawfinder: ignore */

View File

@ -227,7 +227,7 @@ class LLBVHLoader
friend class LLKeyframeMotion;
public:
// Constructor
LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map );
LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map );
~LLBVHLoader();
/*

View File

@ -77,7 +77,9 @@ LLCharacter::~LLCharacter()
//-----------------------------------------------------------------------------
// getJoint()
//-----------------------------------------------------------------------------
LLJoint *LLCharacter::getJoint( const std::string &name )
//<FS:Ansariel> Joint-lookup improvements
//LLJoint *LLCharacter::getJoint( const std::string &name )
LLJoint* LLCharacter::getJoint(std::string_view name)
{
LLJoint* joint = NULL;
@ -94,14 +96,6 @@ LLJoint *LLCharacter::getJoint( const std::string &name )
return joint;
}
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// Default fallback is string.
LLJoint *LLCharacter::getJoint( const JointKey &name )
{
return getJoint( name.mName );
}
// </FS:ND>
//-----------------------------------------------------------------------------
// registerMotion()
//-----------------------------------------------------------------------------

View File

@ -76,13 +76,9 @@ public:
// get the specified joint
// default implementation does recursive search,
// subclasses may optimize/cache results.
//<FS:Ansariel> Joint-lookup improvements
// virtual LLJoint *getJoint( const std::string &name );
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
virtual LLJoint *getJoint( const JointKey &name );
// </FS:ND>
LLJoint *getJoint( const std::string &name );
virtual LLJoint* getJoint(std::string_view name);
// get the position of the character
virtual LLVector3 getCharacterPosition() = 0;

View File

@ -34,24 +34,6 @@
#include "llmath.h"
#include <boost/algorithm/string.hpp>
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
#include <unordered_map>
std::unordered_map<std::string, U32> mpStringToKeys;
JointKey JointKey::construct(const std::string& aName)
{
if (const auto itr = mpStringToKeys.find(aName); itr != mpStringToKeys.end())
{
return { aName, itr->second };
}
U32 size = static_cast<U32>(mpStringToKeys.size()) + 1;
mpStringToKeys.try_emplace(aName, size);
return { aName, size };
}
// </FS:ND>
S32 LLJoint::sNumUpdates = 0;
S32 LLJoint::sNumTouches = 0;
@ -260,7 +242,9 @@ LLJoint *LLJoint::getRoot()
//-----------------------------------------------------------------------------
// findJoint()
//-----------------------------------------------------------------------------
LLJoint *LLJoint::findJoint( const std::string &name )
//<FS:Ansariel> Joint-lookup improvements
//LLJoint *LLJoint::findJoint( const std::string &name )
LLJoint *LLJoint::findJoint(std::string_view name)
{
if (name == getName())
return this;

View File

@ -40,31 +40,6 @@
#include "xform.h"
#include "llmatrix4a.h"
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
struct JointKey
{
std::string mName;
U32 mKey;
static JointKey construct(const std::string& aName);
};
inline bool operator==(JointKey const &aLHS, JointKey const &aRHS)
{
return aLHS.mName == aRHS.mName;
}
inline bool operator!=(JointKey const &aLHS, JointKey const &aRHS)
{
return ! (aLHS == aRHS);
}
inline std::ostream& operator<<(std::ostream &aLHS, JointKey const &aRHS)
{
return aLHS << aRHS.mName << " (" << aRHS.mKey << ")";
}
// </FS:ND>
constexpr S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15;
// Need to set this to count of animate-able joints,
// currently = #bones + #collision_volumes + #attachments + 2,
@ -247,7 +222,9 @@ public:
LLJoint *getRoot();
// search for child joints by name
LLJoint *findJoint( const std::string &name );
//<FS:Ansariel> Joint-lookup improvements
//LLJoint *findJoint( const std::string &name );
LLJoint* findJoint(std::string_view name);
// add/remove children
void addChild( LLJoint *joint );

View File

@ -91,7 +91,7 @@ U32 micro_sleep(U64 us, U32 max_yields)
U32 micro_sleep(U64 us, U32 max_yields)
{
LL_PROFILE_ZONE_SCOPED;
// LL_PROFILE_ZONE_SCOPED; // <FS:Beq/> remove pointless profiling
#if 0
LARGE_INTEGER ft;
ft.QuadPart = -static_cast<S64>(us * 10); // '-' using relative time
@ -109,7 +109,7 @@ U32 micro_sleep(U64 us, U32 max_yields)
void ms_sleep(U32 ms)
{
LL_PROFILE_ZONE_SCOPED;
// LL_PROFILE_ZONE_SCOPED; // <FS:Beq/> remove pointless profiling
micro_sleep(ms * 1000, 0);
}

View File

@ -292,7 +292,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread)
ELoopSpeed loop(REQUEST_SLEEP);
while (! mExitRequested)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // <FS:Beq/> remove pointless profiling
try
{
loop = processRequestQueue(loop);

View File

@ -333,11 +333,21 @@ const std::string LLDiskCache::metaDataToFilepath(const LLUUID& id, LLAssetType:
const std::string LLDiskCache::getCacheInfo()
{
LL_PROFILE_ZONE_SCOPED; // <FS:Beq/> add some instrumentation
std::ostringstream cache_info;
F32 max_in_mb = (F32)mMaxSizeBytes / (1024.0f * 1024.0f);
F32 percent_used = ((F32)dirFileSize(sCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
// <FS:Beq> stall prevention. We still need to make sure this initialised when called at startup.
F32 percent_used;
if (mStoredCacheSize > 0)
{
percent_used = ((F32)mStoredCacheSize / (F32)mMaxSizeBytes) * 100.0f;
}
else
{
percent_used = ((F32)dirFileSize(sCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
}
// </FS:Beq>
cache_info << std::fixed;
cache_info << std::setprecision(1);
cache_info << "Max size " << max_in_mb << " MB ";

View File

@ -35,7 +35,7 @@ LLCamera::LLCamera() :
LLCoordFrame(),
mView(DEFAULT_FIELD_OF_VIEW),
mAspect(DEFAULT_ASPECT_RATIO),
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//mInverseAspect(1.0f / DEFAULT_ASPECT_RATIO),
mDrawDistanceMultiplier(1.0f),
// </FS:minerjr> [FIRE-35081]
@ -67,14 +67,14 @@ LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_p
}
mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Store the inverse of the aspect ratio, so we can remove it from texture calculations
//mInverseAspect = 1.0f / mAspect;
// </FS:minerjr> [FIRE-35081]
mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
if(far_plane < 0) far_plane = DEFAULT_FAR_PLANE;
mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Store the draw distance multiplier based upon how much bigger/smaller the far plan is then the default (64.0f)
mDrawDistanceMultiplier = mFarPlane / DEFAULT_FAR_PLANE;
mDrawDistanceMultiplier = mDrawDistanceMultiplier < 1.0f ? 1.0f : mDrawDistanceMultiplier;
@ -141,7 +141,7 @@ void LLCamera::setViewHeightInPixels(S32 height)
void LLCamera::setAspect(F32 aspect_ratio)
{
mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Store the inverse of the aspect ratio, so we can remove it from texture calculations
//mInverseAspect = 1.0f / mAspect;
// </FS:minerjr> [FIRE-35081]
@ -159,7 +159,7 @@ void LLCamera::setNear(F32 near_plane)
void LLCamera::setFar(F32 far_plane)
{
mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Store the draw distance multiplier based upon how much bigger/smaller the far plan is then the default (64.0f)
mDrawDistanceMultiplier = mFarPlane / DEFAULT_FAR_PLANE;
mDrawDistanceMultiplier = mDrawDistanceMultiplier < 1.0f ? 1.0f : mDrawDistanceMultiplier;

View File

@ -127,7 +127,7 @@ private:
F32 mView; // angle between top and bottom frustum planes in radians.
F32 mAspect; // width/height
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Store the inverse of the aspect ratio, for the texture's sizes
//F32 mInverseAspect; // height/width
F32 mDrawDistanceMultiplier; // mFarPlane / DEFAULT_FAR_PLANE
@ -166,7 +166,7 @@ public:
F32 getView() const { return mView; } // vertical FOV in radians
S32 getViewHeightInPixels() const { return mViewHeightInPixels; }
F32 getAspect() const { return mAspect; } // width / height
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//F32 getInverseAspect() const { return mInverseAspect; } // width / height
F32 getDrawDistanceMultiplier() const { return mDrawDistanceMultiplier; } // mFarPlane / DEFAULT_FAR_PLANE (could also include near plane as well)
// </FS:minerjr> [FIRE-35081]

View File

@ -113,10 +113,11 @@ class LLMessageHandlerBridge : public LLHTTPNode
void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response,
const LLSD& context, const LLSD& input) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"];
char* namePtr = LLMessageStringTable::getInstance()->getString(name.c_str());
LL_DEBUGS() << "Setting mLastSender " << input["sender"].asString() << LL_ENDL;
LL_DEBUGS("Messaging") << "Setting mLastSender " << input["sender"].asString() << LL_ENDL;
gMessageSystem->mLastSender = LLHost(input["sender"].asString());
gMessageSystem->mPacketsIn += 1;
gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]);
@ -2050,6 +2051,7 @@ void LLMessageSystem::dispatch(
const std::string& msg_name,
const LLSD& message)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
LLPointer<LLSimpleResponse> responsep = LLSimpleResponse::create();
dispatch(msg_name, message, responsep);
}
@ -2060,6 +2062,7 @@ void LLMessageSystem::dispatch(
const LLSD& message,
LLHTTPNode::ResponsePtr responsep)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if ((gMessageSystem->mMessageTemplates.find
(LLMessageStringTable::getInstance()->getString(msg_name.c_str())) ==
gMessageSystem->mMessageTemplates.end()) &&

View File

@ -917,7 +917,7 @@ LLDAELoader::LLDAELoader(
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
std::map<std::string, std::string>& jointAliasMap,
std::map<std::string, std::string, std::less<>>& jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit,
// <FS:Beq> mesh loader suffix configuration
@ -1467,10 +1467,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
{
name = mJointMap[name];
}
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// model->mSkinInfo.mJointNames.push_back( name );
model->mSkinInfo.mJointNames.push_back( JointKey::construct( name ) );
// </FS:ND>
model->mSkinInfo.mJointNames.push_back(name);
model->mSkinInfo.mJointNums.push_back(-1);
}
}
@ -1488,10 +1485,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
{
name = mJointMap[name];
}
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// model->mSkinInfo.mJointNames.push_back( name );
model->mSkinInfo.mJointNames.push_back( JointKey::construct( name ) );
// </FS:ND>
model->mSkinInfo.mJointNames.push_back(name);
model->mSkinInfo.mJointNums.push_back(-1);
}
}
@ -1533,10 +1527,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
//but does not use the skeleton).
buildJointToNodeMappingFromScene( root );
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames );
critiqueRigForUploadApplicability( toStringVector( model->mSkinInfo.mJointNames ) );
// </FS:ND>
critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames );
if ( !missingSkeletonOrScene )
{
@ -1589,11 +1580,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
//with the skeleton are not stored in the same order as they are in the exported joint buffer.
//This remaps the skeletal joints to be in the same order as the joints stored in the model.
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// std::vector<std::string> ::const_iterator jointIt = model->mSkinInfo.mJointNames.begin();
std::vector< std::string > jointNames = toStringVector( model->mSkinInfo.mJointNames );
std::vector<std::string> ::const_iterator jointIt = jointNames.begin();
// </FS:ND>
std::vector<std::string> ::const_iterator jointIt = model->mSkinInfo.mJointNames.begin();
const int jointCnt = static_cast<int>(model->mSkinInfo.mJointNames.size());
for ( int i=0; i<jointCnt; ++i, ++jointIt )

View File

@ -49,22 +49,22 @@ public:
dae_model_map mModelsMap;
LLDAELoader(
std::string filename,
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
std::map<std::string, std::string>& jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit,
std::string filename,
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
std::map<std::string, std::string, std::less<>>& jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit,
// <FS:Beq> configrable lod suffix support
// bool preprocess);
bool preprocess,
const LODSuffixArray& lod_suffix);
// bool preprocess);
bool preprocess,
const LODSuffixArray& lod_suffix);
// </FS:Beq>
virtual ~LLDAELoader() ;

View File

@ -66,19 +66,19 @@ static const std::string lod_suffix[LLModel::NUM_LODS] =
};
LLGLTFLoader::LLGLTFLoader(std::string filename,
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void * opaque_userdata,
JointTransformMap & jointTransformMap,
JointNameSet & jointsFromNodes,
std::map<std::string, std::string> &jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit) //,
//bool preprocess)
LLGLTFLoader::LLGLTFLoader(std::string filename,
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void * opaque_userdata,
JointTransformMap & jointTransformMap,
JointNameSet & jointsFromNodes,
std::map<std::string, std::string, std::less<>> & jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit) //,
//bool preprocess)
: LLModelLoader( filename,
lod,
load_cb,

View File

@ -121,18 +121,18 @@ class LLGLTFLoader : public LLModelLoader
typedef std::map<std::string, LLImportMaterial> material_map;
LLGLTFLoader(std::string filename,
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void * opaque_userdata,
JointTransformMap & jointTransformMap,
JointNameSet & jointsFromNodes,
std::map<std::string, std::string> &jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit); //,
//bool preprocess );
S32 lod,
LLModelLoader::load_callback_t load_cb,
LLModelLoader::joint_lookup_func_t joint_lookup_func,
LLModelLoader::texture_load_func_t texture_load_func,
LLModelLoader::state_callback_t state_cb,
void * opaque_userdata,
JointTransformMap & jointTransformMap,
JointNameSet & jointsFromNodes,
std::map<std::string, std::string,std::less<>> &jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit); //,
//bool preprocess );
virtual ~LLGLTFLoader();
virtual bool OpenFile(const std::string &filename);

View File

@ -1501,10 +1501,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
{
for (U32 i = 0; i < skin["joint_names"].size(); ++i)
{
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// mJointNames.push_back( skin[ "joint_names" ][ i ] );
mJointNames.push_back( JointKey::construct( skin[ "joint_names" ][ i ] ) );
// </FS>ND>
mJointNames.push_back( skin[ "joint_names" ][ i ] );
mJointNums.push_back(-1);
}
}
@ -1594,10 +1591,7 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
for (U32 i = 0; i < mJointNames.size(); ++i)
{
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// ret[ "joint_names" ][ i ] = mJointNames[ i ];
ret[ "joint_names" ][ i ] = mJointNames[ i ].mName;
// </FS:ND>
ret[ "joint_names" ][ i ] = mJointNames[ i ];
for (U32 j = 0; j < 4; j++)
{
@ -1648,9 +1642,7 @@ void LLMeshSkinInfo::updateHash()
//mJointNames
for (auto& name : mJointNames)
{
// <FS:Ansariel> Joint lookup speedup
//hash.update(name);
hash.update(name.mName);
hash.update(name);
}
//mJointNums
@ -1676,10 +1668,7 @@ U32 LLMeshSkinInfo::sizeBytes() const
res += sizeof(std::vector<std::string>) + sizeof(std::string) * static_cast<U32>(mJointNames.size());
for (U32 i = 0; i < mJointNames.size(); ++i)
{
// <FS> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//res += static_cast<U32>(mJointNames[i].size()); // actual size, not capacity
res += static_cast<U32>(mJointNames[i].mName.size()); // actual size, not capacity
// </FS>
res += static_cast<U32>(mJointNames[i].size()); // actual size, not capacity
}
res += sizeof(std::vector<S32>) + sizeof(S32) * static_cast<U32>(mJointNums.size());

View File

@ -56,10 +56,7 @@ public:
U32 sizeBytes() const;
LLUUID mMeshID;
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// std::vector<std::string> mJointNames;
std::vector< JointKey > mJointNames;
// </FS:ND>
std::vector<std::string> mJointNames;
mutable std::vector<S32> mJointNums;
typedef std::vector<LLMatrix4a> matrix_list_t;
matrix_list_t mInvBindMatrix;

View File

@ -257,10 +257,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename)
if (!loaded_model->mSkinInfo.mJointNames.empty())
{
//check to see if rig is valid
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );
critiqueRigForUploadApplicability( toStringVector( loaded_model->mSkinInfo.mJointNames ) );
// </FS:ND>
critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );
}
else if (mCacheOnlyHitIfRigged)
{

View File

@ -36,7 +36,7 @@ class LLJoint;
typedef std::map<std::string, LLMatrix4> JointTransformMap;
typedef std::map<std::string, LLMatrix4>::iterator JointTransformMapIt;
typedef std::map<std::string, std::string> JointMap;
typedef std::map<std::string, std::string, std::less<>> JointMap;
typedef std::deque<std::string> JointNameSet;
const S32 SLM_SUPPORTED_VERSION = 3;
@ -196,18 +196,6 @@ public:
void clearLog() { mWarningsArray.clear(); }
protected:
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
std::vector< std::string > toStringVector( std::vector< JointKey > const &aIn ) const
{
std::vector< std::string > out;
for( std::vector< JointKey >::const_iterator itr = aIn.begin(); itr != aIn.end(); ++itr )
out.push_back( itr->mName );
return out;
}
// </FS:ND>
LLModelLoader::load_callback_t mLoadCallback;
LLModelLoader::joint_lookup_func_t mJointLookupFunc;
LLModelLoader::texture_load_func_t mTextureLoadFunc;

View File

@ -1074,6 +1074,7 @@ void LLGLManager::initWGL()
// return false if unable (or unwilling due to old drivers) to init GL
bool LLGLManager::initGL()
{
LL_INFOS("RenderInit") << "Initializing OpenGL" << LL_ENDL; // <FS:Beq/> Extra logging to confirm usage on Linux
if (mInited)
{
LL_ERRS("RenderInit") << "Calling init on LLGLManager after already initialized!" << LL_ENDL;
@ -1514,6 +1515,11 @@ void LLGLManager::initExtensions()
mHasATIMemInfo = ExtensionExists("GL_ATI_meminfo", gGLHExts.mSysExts); //Basic AMD method, also see mHasAMDAssociations
LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL;
// FIRE-34655 - VRAM detection failing on Linux. Load all the GL functions we need.
#if LL_LINUX && !LL_MESA_HEADLESS
mHasNVXGpuMemoryInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts);
mHasAMDAssociations = ExtensionExists("WGL_AMD_gpu_association", gGLHExts.mSysExts);
#endif
#if LL_WINDOWS
// </FS:Zi>

View File

@ -1076,8 +1076,8 @@ void LLGLSLShader::bind()
void LLGLSLShader::bind(U8 variant)
{
llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
llassert_always(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
llassert_always(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
mGLTFVariants[variant].bind();
}
@ -1085,7 +1085,7 @@ void LLGLSLShader::bind(bool rigged)
{
if (rigged)
{
llassert(mRiggedVariant);
llassert_always(mRiggedVariant);
mRiggedVariant->bind();
}
else

View File

@ -104,7 +104,7 @@ void LLGLTexture::setBoostLevel(S32 level)
if(mBoostLevel != LLGLTexture::BOOST_NONE
&& mBoostLevel != LLGLTexture::BOOST_ICON
&& mBoostLevel != LLGLTexture::BOOST_THUMBNAIL
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Add the new grass, light and tree boosts
&& mBoostLevel != LLGLTexture::BOOST_GRASS
&& mBoostLevel != LLGLTexture::BOOST_LIGHT

View File

@ -53,7 +53,7 @@ public:
BOOST_AVATAR_BAKED ,
BOOST_TERRAIN , // Needed for minimap generation for now. Lower than BOOST_HIGH so the texture stats don't get forced, i.e. texture stats are manually managed by minimap/terrain instead.
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
BOOST_GRASS , // Grass has a alternative calculation for virtual and face sizes.
BOOST_TREE , // Tree has a alternative calculation for virtual and face sizes.
BOOST_LIGHT , // Light textures has a alternative calculation for virtual and face sizes.

View File

@ -565,7 +565,7 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, L
{
if ( !force_resize )
{
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// if ( mMaxWidth >= 0.0f
//&& mMaxWidth < screen_width)
// If viewer window was made as small as possible with the console enabled, it would cause an assert error

View File

@ -1530,7 +1530,19 @@ void LLTextBase::draw()
bg_rect.intersectWith( text_rect );
gl_rect_2d( text_rect, bg_color, true );
// <FS> Additionally set the font color of highlighted text instead of using LabelTextColor
const LLColor4& font_color = ll::ui::SearchableControl::getHighlightFontColor();
setColor(font_color);
// </FS>
}
// <FS> Set the font color back to LabelTextColor if not highlighted
else
{
const LLColor4& font_color = LLUIColorTable::instance().getColor("LabelTextColor");
setColor(font_color);
}
// </FS>
bool should_clip = mClip || mScroller != NULL;
// <FS:Zi> Fix text bleeding at top edge of scrolling text editors

View File

@ -1249,7 +1249,7 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
//
LLUrlEntryPlace::LLUrlEntryPlace()
{
mPattern = boost::regex("((hop://[-\\w\\.\\:\\@]+/)|((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://)))\\S+(?:/?(-?\\d+/-?\\d+/-?\\d+|-?\\d+/-?\\d+)/?)?", // <AW: hop:// protocol>
mPattern = boost::regex("(((hop://[-\\w\\.\\:\\@]+/)|((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://)))\\S+/?(\\d+/\\d+/-?\\d+|\\d+/-?\\d+)/?)|(hop://[-\\w\\.\\:\\@]+/[^\\s/]+/?(?![^\\s]))", // <AW: hop:// protocol>
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_slurl.xml";
mTooltip = LLTrans::getString("TooltipSLURL");

View File

@ -266,7 +266,15 @@ U32 LLDXHardware::getMBVideoMemoryViaWMI()
//Getting the version of graphics controller driver via WMI
std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
{
std::string mDriverVersion;
// <FS:Beq> Add caching for WMI query results
LL_PROFILE_ZONE_SCOPED;
static auto driver_version = std::string();
if (!driver_version.empty())
{
return driver_version; // Return cached version
}
// </FS:Beq>
HRESULT hres;
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
IWbemLocator *pLoc = NULL;
@ -435,11 +443,11 @@ std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
std::string str = ll_convert_wide_to_string(ws);
LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL;
if (mDriverVersion.empty())
if (driver_version.empty()) // <FS:Beq/> caching version (also make the varname not stupid)
{
mDriverVersion = str;
driver_version = str; // <FS:Beq/> caching version (also make the varname not stupid)
}
else if (mDriverVersion != str)
else if (driver_version != str) // <FS:Beq/> caching version (also make the varname not stupid)
{
if (vendor == GPU_ANY)
{
@ -475,7 +483,7 @@ std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
// supposed to always call CoUninitialize even if init returned false
CoUninitialize();
return mDriverVersion;
return driver_version; // <FS:Beq/> caching version of driver query
}
void get_wstring(IDxDiagContainer* containerp, const WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize)

View File

@ -96,6 +96,7 @@ set(viewer_SOURCE_FILES
dialogstack.cpp
exoflickr.cpp
exoflickrauth.cpp
fsprimfeedauth.cpp
exogroupmutelist.cpp
floatermedialists.cpp
fsareasearch.cpp
@ -164,6 +165,7 @@ set(viewer_SOURCE_FILES
fspose.cpp
fsposeranimator.cpp
fsposingmotion.cpp
fsprimfeedauth.cpp
fsradar.cpp
fsradarentry.cpp
fsradarlistctrl.cpp
@ -182,6 +184,8 @@ set(viewer_SOURCE_FILES
lfsimfeaturehandler.cpp
llflickrconnect.cpp
llfloaterflickr.cpp
fsprimfeedconnect.cpp
fsfloaterprimfeed.cpp
llpanelopenregionsettings.cpp
# <FS:Ansariel> [Legacy Bake]
llagentwearablesfetch.cpp
@ -910,6 +914,7 @@ set(viewer_HEADER_FILES
dialogstack.h
exoflickr.h
exoflickrauth.h
fsprimfeedauth.h
exogroupmutelist.h
floatermedialists.h
fsareasearch.h
@ -980,6 +985,7 @@ set(viewer_HEADER_FILES
fspose.h
fsposeranimator.h
fsposingmotion.h
fsprimfeedauth.h
fsradar.h
fsradarentry.h
fsradarlistctrl.h
@ -999,6 +1005,8 @@ set(viewer_HEADER_FILES
lfsimfeaturehandler.h
llflickrconnect.h
llfloaterflickr.h
fsprimfeedconnect.h
fsfloaterprimfeed.h
# <FS:Ansariel> [Legacy Bake]
llagentwearablesfetch.h
vjlocalmesh.h

View File

@ -871,8 +871,23 @@ void FloaterAO::onAnimationChanged(const LLUUID& animation)
if (mCurrentBoldItem)
{
((LLScrollListIcon*)mCurrentBoldItem->getColumn(0))->setValue("FSAO_Animation_Stopped");
((LLScrollListText*)mCurrentBoldItem->getColumn(1))->setFontStyle(LLFontGL::NORMAL);
// <AS:Chanayane> Safer casts
if (LLScrollListCell* icon_cell = mCurrentBoldItem->getColumn(0))
{
if (LLScrollListIcon* icon = dynamic_cast<LLScrollListIcon*>(icon_cell))
{
icon->setValue("FSAO_Animation_Stopped");
}
}
if (LLScrollListCell* text_cell = mCurrentBoldItem->getColumn(1))
{
if (LLScrollListText* text = dynamic_cast<LLScrollListText*>(text_cell))
{
text->setFontStyle(LLFontGL::NORMAL);
}
}
// </AS:Chanayane>
mCurrentBoldItem = nullptr;
}
@ -882,21 +897,46 @@ void FloaterAO::onAnimationChanged(const LLUUID& animation)
return;
}
// why do we have no LLScrollListCtrl::getItemByUserdata() ? -Zi
for (auto item : mAnimationList->getAllData())
// <AS:Chanayane> Fix potential nullptr
if (!mAnimationList)
{
LLUUID* id = (LLUUID*)item->getUserdata();
LL_WARNS("AO") << "Animation list control is null." << LL_ENDL;
return;
}
// </AS:Chanayane>
if (id == &animation)
// <AS:Chanayane> Safer casts
// why do we have no LLScrollListCtrl::getItemByUserdata() ? -Zi
for (LLScrollListItem* item : mAnimationList->getAllData())
{
LLUUID* id = static_cast<LLUUID*>(item->getUserdata());
// <AS:Chanayane> compares the LLUUID values instead of pointer values
//if (id == &animation)
if (id && *id == animation)
// </AS:Chanayane>
{
mCurrentBoldItem = item;
((LLScrollListIcon*)mCurrentBoldItem->getColumn(0))->setValue("FSAO_Animation_Playing");
((LLScrollListText*)mCurrentBoldItem->getColumn(1))->setFontStyle(LLFontGL::BOLD);
if (LLScrollListCell* icon_cell = mCurrentBoldItem->getColumn(0))
{
if (LLScrollListIcon* icon = dynamic_cast<LLScrollListIcon*>(icon_cell))
{
icon->setValue("FSAO_Animation_Playing");
}
}
if (LLScrollListCell* text_cell = mCurrentBoldItem->getColumn(1))
{
if (LLScrollListText* text = dynamic_cast<LLScrollListText*>(text_cell))
{
text->setFontStyle(LLFontGL::BOLD);
}
}
return;
}
}
// </AS:Chanayane>
}
// virtual

View File

@ -999,6 +999,13 @@ void AOEngine::playAnimation(const LLUUID& animation)
}
LLViewerInventoryItem* item = gInventory.getItem(animation);
if (!item)
{
LL_WARNS("AOEngine") << "Inventory item for animation " << animation << " not found." << LL_ENDL;
return;
}
AOSet::AOAnimation anim;
anim.mName = item->LLInventoryItem::getName();
anim.mInventoryUUID = item->getUUID();
@ -1007,12 +1014,21 @@ void AOEngine::playAnimation(const LLUUID& animation)
// if we can find the original animation already right here, save its asset ID, otherwise this will
// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
LLUUID newAnimation;
if (item->getLinkedItem())
{
anim.mAssetUUID = item->getAssetUUID();
newAnimation = item->getAssetUUID();
//anim.mAssetUUID = item->getAssetUUID();
}
LLUUID newAnimation = anim.mAssetUUID;
if (newAnimation.isNull())
{
LL_WARNS("AOEngine") << "New animation UUID is null for animation " << animation << LL_ENDL;
return;
}
anim.mAssetUUID = newAnimation;
//LLUUID newAnimation = anim.mAssetUUID;
LLUUID oldAnimation = state->mCurrentAnimationID;
// don't do anything if the animation didn't change
@ -1024,7 +1040,7 @@ void AOEngine::playAnimation(const LLUUID& animation)
mAnimationChangedSignal(LLUUID::null);
// Searches for the index of the animation
U32 idx = -1;
S32 idx = -1;
for (U32 i = 0; i < state->mAnimations.size(); i++)
{
if (state->mAnimations[i].mAssetUUID == newAnimation)
@ -1033,18 +1049,22 @@ void AOEngine::playAnimation(const LLUUID& animation)
break;
}
}
if (idx < 0)
if (idx == -1)
{
idx = 0;
LL_WARNS("AOEngine") << "Animation index not found for animation " << animation << LL_ENDL;
return;
}
state->mCurrentAnimation = idx;
state->mCurrentAnimation = static_cast<U32>(idx);
state->mCurrentAnimationID = newAnimation;
if (newAnimation.notNull())
{
LL_DEBUGS("AOEngine") << "requesting animation start for motion " << gAnimLibrary.animationName(mLastMotion) << ": " << newAnimation << LL_ENDL;
gAgent.sendAnimationRequest(newAnimation, ANIM_REQUEST_START);
mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
if (state->mCurrentAnimation < state->mAnimations.size())
{
mAnimationChangedSignal(state->mAnimations[state->mCurrentAnimation].mInventoryUUID);
}
}
else
{

View File

@ -254,6 +254,16 @@
is_running_function="Floater.IsOpen"
is_running_parameters="flickr"
/>
<command name="primfeed"
available_in_toybox="true"
icon="Command_Primfeed_Icon"
label_ref="Command_Primfeed_Label"
tooltip_ref="Command_Primfeed_Tooltip"
execute_function="Floater.Toggle"
execute_parameters="primfeed"
is_running_function="Floater.IsOpen"
is_running_parameters="primfeed"
/>
<command name="speak"
available_in_toybox="true"
icon="Command_Speak_Icon"

View File

@ -153,6 +153,17 @@
<key>Value</key>
<string>http://phoenixviewer.com/app/fsdata/grids.xml</string>
</map>
<key>FSPrimfeedViewerApiKey</key>
<map>
<key>Comment</key>
<string>Viewer key for API login.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>xAcXYt8SBius3Lor4wHle8L96PDHYlAZuWYXIYQUdW4b09mjhQUAwiqmWp5UNYXLpq5GSUtuKHuDYLwaueACPkew93l6MRY8jfBKSH09kv0zyGglpky07X7X7Sp4Rzin</string>
</map>
<key>FSGridBuilderURL</key>
<map>
<key>Comment</key>
@ -18553,6 +18564,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<string>world_map</string>
<string>preferences</string>
<string>flickr</string>
<string>primfeed</string>
</array>
<key>Backup</key>
<integer>0</integer>
@ -24806,6 +24818,39 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>4</integer>
</map>
<key>FSLastSnapshotToPrimfeedHeight</key>
<map>
<key>Comment</key>
<string>The height of the last Primfeed snapshot, in px</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>768</integer>
</map>
<key>FSLastSnapshotToPrimfeedWidth</key>
<map>
<key>Comment</key>
<string>The width of the last Primfeed snapshot, in px</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>1024</integer>
</map>
<key>FSLastSnapshotToPrimfeedResolution</key>
<map>
<key>Comment</key>
<string>At what resolution should snapshots be posted on Primfeed. 0=Current Window, 1=320x240, 2=640x480, 3=800x600, 4=1024x768, 5=1280x1024, 6=1600x1200, 7=Custom</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>4</integer>
</map>
<key>FSLastSnapshotToTwitterHeight</key>
<map>
<key>Comment</key>
@ -25092,6 +25137,19 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>1</integer>
</map>
<key>FSNoVersionPopup</key>
<map>
<key>Comment</key>
<string>Disables version popup on the Firestorm Login page</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
<key>HideFromEditor</key>
<integer>1</integer>
</map>
<key>FSllOwnerSayToScriptDebugWindowRouting</key>
<map>
<key>Comment</key>
@ -26365,5 +26423,27 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSnapshotLocalNamesWithTimestamps</key>
<map>
<key>Comment</key>
<string>include a timestamp in the filename when saving snapshots locally</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>FSRepeatedEnvTogglesShared</key>
<map>
<key>Comment</key>
<string>Whether repeated presses of sky preset shortcuts should revert to shared environment</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
</map>
</llsd>

View File

@ -1358,6 +1358,83 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPrimfeedOAuthToken</key>
<map>
<key>Comment</key>
<string>contains the secure authentication toke to post to your primfeed account (do not share)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>FSPrimfeedProfileLink</key>
<map>
<key>Comment</key>
<string>The profile page for the account associated with the currently linked Primfeed account</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>FSPrimfeedPlan</key>
<map>
<key>Comment</key>
<string>The plan type associated with the currently linked Primfeed account</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>FSPrimfeedUsername</key>
<map>
<key>Comment</key>
<string>The username associated with the currently linked Primfeed account</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>FSPrimfeedCommercialContent</key>
<map>
<key>Comment</key>
<string>Does this post contain commercial content</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPrimfeedAddToPublicGallery</key>
<map>
<key>Comment</key>
<string>Should this post go to the public gallery?</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPrimfeedOpenURLOnPost</key>
<map>
<key>Comment</key>
<string>if true open the URL in a browser when the post completes</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSProtectedFolders</key>
<map>
<key>Comment</key>
@ -1369,5 +1446,49 @@
<key>Value</key>
<array/>
</map>
<key>FSPrimfeedPhotoRating</key>
<map>
<key>Comment</key>
<string>Content rating to be shared with Primfeed.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Integer</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>FSPrimfeedPhotoResolution</key>
<map>
<key>Comment</key>
<string>Last used resolution for Primfeed photos.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<array/>
</map>
<key>FlickrPhotoResolution</key>
<map>
<key>Comment</key>
<string>Last used resolution for Primfeed photos.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<array/>
</map>
<key>FlickrPhotoRating</key>
<map>
<key>Comment</key>
<string>Content rating to be shared with Flickr.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Integer</string>
<key>Value</key>
<integer>1</integer>
</map>
</map>
</llsd>

View File

@ -64,6 +64,7 @@ std::string BugSplatAttributes::to_xml_token(const std::string& input)
bool BugSplatAttributes::writeToFile(const std::string& file_path)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING;
std::lock_guard<std::mutex> lock(mMutex);
// Write to a temporary file first
@ -91,6 +92,7 @@ bool BugSplatAttributes::writeToFile(const std::string& file_path)
}
// Write out all other categories
// BugSplat chaanged the XML format and there is no strict category support now. For now we'll prefix the category to each attribute
for (const auto& cat_pair : mAttributes)
{
const std::string& category = cat_pair.first;
@ -101,14 +103,12 @@ bool BugSplatAttributes::writeToFile(const std::string& file_path)
continue;
}
ofs << " <" << category << ">\n";
for (const auto& kv : cat_pair.second)
{
const std::string& key = kv.first;
const std::string& val = kv.second;
ofs << " <" << key << ">" << val << "</" << key << ">\n";
ofs << " <" << category << "-" << key << ">" << val << "</" << category << "-" << key << ">\n";
}
ofs << " </" << category << ">\n";
}
ofs << "</XmlCrashContext>\n";

View File

@ -333,7 +333,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 0
RenderHeroProbeResolution 1 1024
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1
@ -375,7 +375,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 0
RenderHeroProbeResolution 1 2048
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1

View File

@ -333,7 +333,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 0
RenderHeroProbeResolution 1 1024
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1
@ -375,7 +375,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 0
RenderHeroProbeResolution 1 2048
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1

View File

@ -333,7 +333,7 @@ RenderReflectionProbeLevel 1 2
RenderMirrors 1 0
RenderHeroProbeResolution 1 512
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1
@ -375,7 +375,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 0
RenderHeroProbeResolution 1 1024
RenderHeroProbeDistance 1 16
RenderHeroProbeUpdateRate 1 1
RenderHeroProbeUpdateRate 1 2
RenderHeroProbeConservativeUpdateMultiplier 1 4
RenderCASSharpness 1 0.4
RenderExposure 1 1

View File

@ -156,6 +156,7 @@ FSAreaSearch::FSAreaSearch(const LLSD& key) :
mFilterPhantom(false),
mFilterAttachment(false),
mFilterMoaP(false),
mFilterReflectionProbe(false),
mFilterDistance(false),
mFilterDistanceMin(0),
mFilterDistanceMax(999999),
@ -166,6 +167,7 @@ FSAreaSearch::FSAreaSearch(const LLSD& key) :
mBeacons(false),
mExcludeAttachment(true),
mExcludeTemporary(true),
mExcludeReflectionProbe(false),
mExcludePhysics(true),
mExcludeChildPrims(true),
mExcludeNeighborRegions(true),
@ -545,6 +547,11 @@ bool FSAreaSearch::isSearchableObject(LLViewerObject* objectp, LLViewerRegion* o
return false;
}
if (mExcludeReflectionProbe && objectp->mReflectionProbe.notNull())
{
return false;
}
return true;
}
@ -908,6 +915,11 @@ void FSAreaSearch::matchObject(FSObjectProperties& details, LLViewerObject* obje
return;
}
if (mFilterReflectionProbe && !objectp->mReflectionProbe.notNull())
{
return;
}
//-----------------------------------------------------------------------
// Find text
//-----------------------------------------------------------------------
@ -2217,6 +2229,10 @@ bool FSPanelAreaSearchFilter::postBuild()
mCheckboxExcludetemporary->set(true);
mCheckboxExcludetemporary->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
mCheckboxExcludeReflectionProbes = getChild<LLCheckBoxCtrl>("exclude_reflection_probes");
mCheckboxExcludeReflectionProbes->set(false);
mCheckboxExcludeReflectionProbes->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
mCheckboxExcludeChildPrim = getChild<LLCheckBoxCtrl>("exclude_childprim");
mCheckboxExcludeChildPrim->set(true);
mCheckboxExcludeChildPrim->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
@ -2240,6 +2256,9 @@ bool FSPanelAreaSearchFilter::postBuild()
mCheckboxMoaP = getChild<LLCheckBoxCtrl>("filter_moap");
mCheckboxMoaP->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
mCheckboxReflectionProbe = getChild<LLCheckBoxCtrl>("filter_reflection_probe");
mCheckboxReflectionProbe->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
mCheckboxPermCopy = getChild<LLCheckBoxCtrl>("filter_perm_copy");
mCheckboxPermCopy->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
@ -2262,6 +2281,7 @@ void FSPanelAreaSearchFilter::onCommitCheckbox()
mFSAreaSearch->setFilterForSale(mCheckboxForSale->get());
mFSAreaSearch->setFilterDistance(mCheckboxDistance->get());
mFSAreaSearch->setFilterMoaP(mCheckboxMoaP->get());
mFSAreaSearch->setFilterReflectionProbe(mCheckboxReflectionProbe->get());
if (mCheckboxExcludePhysics->get())
{
@ -2291,6 +2311,19 @@ void FSPanelAreaSearchFilter::onCommitCheckbox()
}
mFSAreaSearch->setFilterTemporary(mCheckboxTemporary->get());
if (mCheckboxExcludeReflectionProbes->get())
{
mFSAreaSearch->setFilterReflectionProbe(false);
mCheckboxReflectionProbe->set(false);
mCheckboxReflectionProbe->setEnabled(false);
mFSAreaSearch->setExcludeReflectionProbe(true);
}
else
{
mCheckboxReflectionProbe->setEnabled(true);
mFSAreaSearch->setExcludeReflectionProbe(false);
}
if (mCheckboxExcludeAttachment->get())
{
mFSAreaSearch->setFilterAttachment(false);

View File

@ -141,12 +141,14 @@ public:
void setFilterPhantom(bool b) { mFilterPhantom = b; }
void setFilterAttachment(bool b) { mFilterAttachment = b; }
void setFilterMoaP(bool b) { mFilterMoaP = b; }
void setFilterReflectionProbe(bool b) { mFilterReflectionProbe = b; }
void setRegexSearch(bool b) { mRegexSearch = b; }
void setBeacons(bool b) { mBeacons = b; }
void setExcludeAttachment(bool b) { mExcludeAttachment = b; }
void setExcludetemporary(bool b) { mExcludeTemporary = b; }
void setExcludeReflectionProbe(bool b) { mExcludeReflectionProbe = b; }
void setExcludePhysics(bool b) { mExcludePhysics = b; }
void setExcludeChildPrims(bool b) { mExcludeChildPrims = b; }
void setExcludeNeighborRegions(bool b) { mExcludeNeighborRegions = b; }
@ -230,6 +232,7 @@ private:
bool mExcludeAttachment;
bool mExcludeTemporary;
bool mExcludeReflectionProbe;
bool mExcludePhysics;
bool mExcludeChildPrims;
bool mExcludeNeighborRegions;
@ -240,6 +243,7 @@ private:
bool mFilterPhantom;
bool mFilterAttachment;
bool mFilterMoaP;
bool mFilterReflectionProbe;
bool mFilterForSale;
S32 mFilterForSaleMin;
@ -382,6 +386,7 @@ private:
LLCheckBoxCtrl* mCheckboxLocked;
LLCheckBoxCtrl* mCheckboxPhantom;
LLCheckBoxCtrl* mCheckboxMoaP;
LLCheckBoxCtrl* mCheckboxReflectionProbe;
LLCheckBoxCtrl* mCheckboxDistance;
LLSpinCtrl* mSpinDistanceMinValue;
LLSpinCtrl* mSpinDistanceMaxValue;
@ -393,6 +398,7 @@ private:
LLCheckBoxCtrl* mCheckboxExcludeAttachment;
LLCheckBoxCtrl* mCheckboxExcludePhysics;
LLCheckBoxCtrl* mCheckboxExcludetemporary;
LLCheckBoxCtrl* mCheckboxExcludeReflectionProbes;
LLCheckBoxCtrl* mCheckboxExcludeChildPrim;
LLCheckBoxCtrl* mCheckboxExcludeNeighborRegions;
LLCheckBoxCtrl* mCheckboxPermCopy;

View File

@ -479,6 +479,10 @@ void FSFloaterIM::sendMsgFromInputEditor(EChatType type)
{
str_version_tag = "Release";
}
else if( viewer_maturity == LLVersionInfo::FSViewerMaturity::STREAMING_VIEWER )
{
str_version_tag = "Streaming";
}
else if( viewer_maturity == LLVersionInfo::FSViewerMaturity::UNOFFICIAL_VIEWER )
{
str_version_tag = "Unofficial";
@ -502,6 +506,10 @@ void FSFloaterIM::sendMsgFromInputEditor(EChatType type)
{
str_version_tag = "Unofficial";
}
if( viewer_maturity == LLVersionInfo::FSViewerMaturity::STREAMING_VIEWER )
{
str_version_tag = "Streaming";
}
else if( viewer_maturity != LLVersionInfo::FSViewerMaturity::RELEASE_VIEWER )
{
str_version_tag = "pre-Release";
@ -1245,6 +1253,22 @@ FSFloaterIM* FSFloaterIM::show(const LLUUID& session_id)
if (!gIMMgr->hasSession(session_id))
return nullptr;
// <AS:chanayane> [FIRE-34494] fixes unable to open an IM with someone who started a group chat
// Prevent showing non-IM sessions in FSFloaterIM::show()
LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
if (!session || (
IM_NOTHING_SPECIAL != session->mType
&& IM_SESSION_P2P_INVITE != session->mType
&& IM_SESSION_INVITE != session->mType
&& IM_SESSION_CONFERENCE_START != session->mType
&& IM_SESSION_GROUP_START != session->mType))
{
LL_WARNS("IMVIEW") << "Attempted to show FSFloaterIM for non-IM session: "
<< (session ? std::to_string(session->mType) : "null") << LL_ENDL;
return nullptr;
}
// </AS:chanayane>
if (!isChatMultiTab())
{
//hide all

View File

@ -33,6 +33,7 @@
#include "llcheckboxctrl.h"
#include "llcommonutils.h"
#include "llcontrolavatar.h"
#include "llnotificationsutil.h"
#include "lldiriterator.h"
#include "llsdserialize.h"
#include "llscrolllistctrl.h"
@ -61,7 +62,10 @@ constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpa
constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed";
constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit";
constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso";
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
} // namespace
/// <summary>
@ -368,19 +372,8 @@ void FSFloaterPoser::onPoseFileSelect()
mLoadPosesBtn->setLabel(getString("LoadPoseLabel"));
}
void FSFloaterPoser::onClickPoseSave()
void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename)
{
std::string filename = mPoseSaveNameEditor->getValue().asString();
if (filename.empty() && hasString("icon_save_failed_button"))
{
mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign());
return;
}
if (confirmFileOverwrite(filename))
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
@ -393,43 +386,60 @@ void FSFloaterPoser::onClickPoseSave()
if (getSavingToBvh())
savePoseToBvh(avatar, filename);
if (hasString("icon_rotation_is_own_work"))
mSavePosesBtn->setImageOverlay(getString("icon_rotation_is_own_work"), mSavePosesBtn->getImageOverlayHAlign());
if (hasString(ICON_SAVE_OK))
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign());
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
}
else
{
if (hasString("icon_save_failed_button"))
mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign());
}
if (hasString(ICON_SAVE_FAILED))
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
}
}
bool FSFloaterPoser::confirmFileOverwrite(std::string fileName)
void FSFloaterPoser::onClickPoseSave()
{
if (fileName.empty())
return false;
std::string filename = mPoseSaveNameEditor->getValue().asString();
if (filename.empty() && hasString(ICON_SAVE_FAILED))
{
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
return;
}
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
// if prompts are disabled or file doesn't exist, do the save immediately:
const bool prompt = gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY);
if (!gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY))
return false;
std::string fullPath = gDirUtilp->getExpandedFilename(
LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, filename + POSE_INTERNAL_FORMAT_FILE_EXT);
const bool exists = gDirUtilp->fileExists(fullPath);
if (!hasString("icon_save_query"))
return false;
if (mSavePosesBtn->getImageOverlay().notNull() && mSavePosesBtn->getImageOverlay()->getName() == getString("icon_save_query"))
return false;
std::string fullSavePath =
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, fileName + POSE_INTERNAL_FORMAT_FILE_EXT);
if (!gDirUtilp->fileExists(fullSavePath))
return false;
mSavePosesBtn->setImageOverlay(getString("icon_save_query"), mSavePosesBtn->getImageOverlayHAlign());
if (hasString("OverWriteLabel"))
mSavePosesBtn->setLabel(getString("OverWriteLabel"));
return true;
if (!prompt || !exists)
{
// new file or no overwrite guard
doPoseSave(avatar, filename);
}
else
{
// show a modal dialog, passing the pose name along
LLSD args;
args["POSE_NAME"] = filename;
LLNotificationsUtil::add("ConfirmPoserOverwrite", args, LLSD(), // no payload
[this, avatar, filename](const LLSD& notification, const LLSD& response)
{
if (LLNotificationsUtil::getSelectedOption(notification, response) == 0)
{
// user clicked “Yes”
doPoseSave(avatar, filename);
}
// else do nothing (cancel)
});
}
}
void FSFloaterPoser::onMouseLeaveSavePoseBtn()
@ -1645,13 +1655,14 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
return joints;
}
void FSFloaterPoser::updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints)
void FSFloaterPoser::updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints) const
{
if (!gAgentAvatarp || gAgentAvatarp.isNull())
auto avatarp = getUiSelectedAvatar();
if (!avatarp)
return;
if (joints.size() >= 1)
FSToolCompPose::getInstance()->setJoint(gAgentAvatarp->getJoint(JointKey::construct(joints[0]->jointName())));
FSToolCompPose::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName()));
else
FSToolCompPose::getInstance()->setJoint(nullptr);
}

View File

@ -86,6 +86,8 @@ public:
void redo() override { onRedoLastChange(); };
bool canRedo() const override { return true; }
private:
// Helper function to encapsualte save logic
void doPoseSave(LLVOAvatar* avatar, const std::string& filename);
bool postBuild() override;
void onOpen(const LLSD& key) override;
void onClose(bool app_quitting) override;
@ -136,7 +138,7 @@ public:
/// Updates the visual with the first selected joint from the supplied collection, if any.
/// </summary>
/// <param name="joints">The collection of selected joints.</param>
static void updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints);
void updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints) const;
/// <summary>
/// Gets a detectable avatar by its UUID.

View File

@ -0,0 +1,912 @@
/**
* @file fsfloaterprimfeed.cpp
* @brief Implementation of primfeed floater
* @author beq@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "fsfloaterprimfeed.h"
#include "fsprimfeedconnect.h"
#include "llagent.h"
#include "llagentui.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llfloaterreg.h"
#include "lliconctrl.h"
#include "llimagefiltersmanager.h"
#include "llresmgr.h" // LLLocale
#include "llsdserialize.h"
#include "llloadingindicator.h"
#include "llslurl.h"
#include "lltrans.h"
#include "llfloatersnapshot.h"
#include "llsnapshotlivepreview.h"
#include "llfloaterbigpreview.h"
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "lltabcontainer.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include <boost/regex.hpp>
#include "llspinctrl.h"
#include "llviewernetwork.h"
#include "llnotificationsutil.h"
#include "fsprimfeedauth.h"
#include "llviewernetwork.h"
static LLPanelInjector<FSPrimfeedPhotoPanel> t_panel_photo("fsprimfeedphotopanel");
static LLPanelInjector<FSPrimfeedAccountPanel> t_panel_account("fsprimfeedaccountpanel");
///////////////////////////
// FSPrimfeedPhotoPanel/////
///////////////////////////
FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
mResolutionComboBox(nullptr),
mRefreshBtn(nullptr),
mWorkingLabel(nullptr),
mThumbnailPlaceholder(nullptr),
mDescriptionTextBox(nullptr),
mLocationCheckbox(nullptr),
mRatingComboBox(nullptr),
mPostButton(nullptr),
mBtnPreview(nullptr),
mBigPreviewFloater(nullptr)
{
mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", [this](LLUICtrl*, const LLSD&) { onSend(); });
mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", [this](LLUICtrl*, const LLSD&) { onClickNewSnapshot(); });
mCommitCallbackRegistrar.add("SocialSharing.BigPreview", [this](LLUICtrl*, const LLSD&) { onClickBigPreview(); });
mCommitCallbackRegistrar.add("Primfeed.Info",
[](LLUICtrl*, const LLSD& param)
{
const std::string url = param.asString();
LL_DEBUGS("primfeed") << "Info button clicked, opening " << url << LL_ENDL;
LLWeb::loadURLExternal(url);
});
}
FSPrimfeedPhotoPanel::~FSPrimfeedPhotoPanel()
{
if (mPreviewHandle.get())
{
mPreviewHandle.get()->die();
}
FSPrimfeedAuth::sPrimfeedAuthPump->stopListening("FSPrimfeedAccountPanel");
gSavedSettings.setS32("FSLastSnapshotToPrimfeedResolution", getChild<LLComboBox>("resolution_combobox")->getCurrentIndex());
gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", getChild<LLSpinCtrl>("custom_snapshot_width")->getValue().asInteger());
gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", getChild<LLSpinCtrl>("custom_snapshot_height")->getValue().asInteger());
}
bool FSPrimfeedPhotoPanel::postBuild()
{
setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
mResolutionComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
mFilterComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
mBtnPreview = getChild<LLButton>("big_preview_btn");
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
mDescriptionTextBox = getChild<LLUICtrl>("photo_description");
mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
mCommercialCheckbox = getChild<LLUICtrl>("primfeed_commercial_content");
mPublicGalleryCheckbox = getChild<LLUICtrl>("primfeed_add_to_public_gallery");
mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
// Update custom resolution controls with lambdas
getChild<LLSpinCtrl>("custom_snapshot_width")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
getChild<LLSpinCtrl>("custom_snapshot_height")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
getChild<LLComboBox>("resolution_combobox")->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToPrimfeedResolution"));
getChild<LLSpinCtrl>("custom_snapshot_width")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedWidth"));
getChild<LLSpinCtrl>("custom_snapshot_height")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedHeight"));
// Update filter list
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
auto* filterbox = static_cast<LLComboBox*>(mFilterComboBox);
for (const std::string& filter : filter_list)
{
filterbox->add(filter);
}
return LLPanel::postBuild();
}
// static
void FSFloaterPrimfeed::update()
{
if (LLFloaterReg::instanceVisible("primfeed"))
{
LLFloaterSnapshotBase::ImplBase::updatePreviewList(true, true);
}
}
// virtual
S32 FSPrimfeedPhotoPanel::notify(const LLSD& info)
{
if (info.has("snapshot-updating"))
{
// Disable the Post button and whatever else while the snapshot is not updated
// updateControls();
return 1;
}
if (info.has("snapshot-updated"))
{
// Enable the send/post/save buttons.
updateControls();
// The refresh button is initially hidden. We show it after the first update,
// i.e. after snapshot is taken
if (LLUICtrl* refresh_button = getRefreshBtn(); !refresh_button->getVisible())
{
refresh_button->setVisible(true);
}
return 1;
}
return 0;
}
void FSPrimfeedPhotoPanel::draw()
{
auto previewp = static_cast<const LLSnapshotLivePreview*>(mPreviewHandle.get());
// Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
auto can_post = !(FSPrimfeedConnect::instance().isTransactionOngoing()) && FSPrimfeedAuth::isAuthorized();
mCancelButton->setEnabled(can_post);
mDescriptionTextBox->setEnabled(can_post);
mRatingComboBox->setEnabled(can_post);
mResolutionComboBox->setEnabled(can_post);
mFilterComboBox->setEnabled(can_post);
mRefreshBtn->setEnabled(can_post);
mBtnPreview->setEnabled(can_post);
mLocationCheckbox->setEnabled(can_post);
mPublicGalleryCheckbox->setEnabled(can_post);
mCommercialCheckbox->setEnabled(can_post);
// Reassign the preview floater if we have the focus and the preview exists
if (hasFocus() && isPreviewVisible())
{
attachPreview();
}
// Toggle the button state as appropriate
bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
mBtnPreview->setToggleState(preview_active);
// Display the preview if one is available
if (previewp && previewp->getThumbnailImage())
{
const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
const S32 thumbnail_w = previewp->getThumbnailWidth();
const S32 thumbnail_h = previewp->getThumbnailHeight();
// calc preview offset within the preview rect
const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2;
const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2;
S32 offset_x = thumbnail_rect.mLeft + local_offset_x;
S32 offset_y = thumbnail_rect.mBottom + local_offset_y;
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
LLColor4 color = LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y, thumbnail_w, thumbnail_h, previewp->getThumbnailImage(), color % alpha);
}
// Update the visibility of the working (computing preview) label
mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
// Enable Post if we have a preview to send and no on going connection being processed
mPostButton->setEnabled(can_post && (previewp && previewp->getSnapshotUpToDate()));
// Draw the rest of the panel on top of it
LLPanel::draw();
}
LLSnapshotLivePreview* FSPrimfeedPhotoPanel::getPreviewView()
{
auto previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
return previewp;
}
void FSPrimfeedPhotoPanel::onVisibilityChange(bool visible)
{
if (visible)
{
if (mPreviewHandle.get())
{
LLSnapshotLivePreview* preview = getPreviewView();
if (preview)
{
LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL;
preview->updateSnapshot(true);
}
}
else
{
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
p.rect(full_screen_rect);
auto previewp = new LLSnapshotLivePreview(p);
mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
previewp->setSnapshotType(LLSnapshotModel::SNAPSHOT_WEB);
previewp->setSnapshotFormat(LLSnapshotModel::SNAPSHOT_FORMAT_PNG);
previewp->setThumbnailSubsampled(true); // We want the preview to reflect the *saved* image
previewp->setAllowRenderUI(false); // We do not want the rendered UI in our snapshots
previewp->setAllowFullScreenPreview(false); // No full screen preview in SL Share mode
previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
updateControls();
}
}
}
void FSPrimfeedPhotoPanel::onClickNewSnapshot()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
previewp->updateSnapshot(true);
}
}
void FSPrimfeedPhotoPanel::onClickBigPreview()
{
// Toggle the preview
if (isPreviewVisible())
{
LLFloaterReg::hideInstance("big_preview");
}
else
{
attachPreview();
LLFloaterReg::showInstance("big_preview");
}
}
bool FSPrimfeedPhotoPanel::isPreviewVisible() const
{
return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
}
void FSPrimfeedPhotoPanel::attachPreview()
{
if (mBigPreviewFloater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
mBigPreviewFloater->setPreview(previewp);
mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
}
}
void FSPrimfeedPhotoPanel::onSend()
{
sendPhoto();
}
bool FSPrimfeedPhotoPanel::onPrimfeedConnectStateChange(const LLSD& /*data*/)
{
if (FSPrimfeedAuth::isAuthorized())
{
sendPhoto();
}
return false;
}
void FSPrimfeedPhotoPanel::sendPhoto()
{
auto ratingToString = [&](int rating)
{
static const std::array<std::string, 4> RATING_NAMES = {
"general", // 1
"moderate", // 2
"adult", // 3
"adult_plus" // 4
};
// clamp into [1,4]
int idx = llclamp(rating, 1, 4) - 1;
return RATING_NAMES[idx];
};
// Get the description (primfeed has no title/tags etc at this point)
std::string description = mDescriptionTextBox->getValue().asString();
// Get the content rating
int content_rating = mRatingComboBox->getValue().asInteger();
bool post_to_public_gallery = mPublicGalleryCheckbox->getValue().asBoolean();
bool commercial_content = mCommercialCheckbox->getValue().asBoolean();
// Get the image
LLSnapshotLivePreview* previewp = getPreviewView();
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTING);
LLSD params;
params["rating"] = ratingToString(content_rating);
params["content"] = description;
params["is_commercial"] = commercial_content;
params["post_to_public_gallery"] = post_to_public_gallery;
// Add the location if required
if (bool add_location = mLocationCheckbox->getValue().asBoolean(); add_location)
{
// Get the SLURL for the location
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
std::string slurl_string = slurl.getSLURLString();
params["location"] = slurl_string;
}
FSPrimfeedConnect::instance().uploadPhoto(params, previewp->getFormattedImage().get(),
[this](bool success, const std::string& url)
{
if (success)
{
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTED);
static LLCachedControl<bool> open_url_on_post(gSavedPerAccountSettings,
"FSPrimfeedOpenURLOnPost", true);
if (open_url_on_post)
{
LLWeb::loadURLExternal(url);
}
LLSD args;
args["PF_POSTURL"] = url;
LLNotificationsUtil::add("FSPrimfeedUploadComplete", args);
}
else
{
mWorkingLabel->setValue("Error posting to Primfeed");
mPostButton->setEnabled(true);
}
});
updateControls();
}
void FSPrimfeedPhotoPanel::clearAndClose()
{
mDescriptionTextBox->setValue("");
if (LLFloater* floater = getParentByType<LLFloater>())
{
floater->closeFloater();
if (mBigPreviewFloater)
{
mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
}
}
}
void FSPrimfeedPhotoPanel::updateControls()
{
// LLSnapshotLivePreview* previewp = getPreviewView();
updateResolution(false);
}
void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
{
auto combobox = static_cast<LLComboBox*>(mResolutionComboBox);
auto filterbox = static_cast<LLComboBox*>(mFilterComboBox);
std::string sdstring = combobox->getSelectedValue();
LLSD sdres;
std::stringstream sstream(sdstring);
LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
S32 width = sdres[0];
S32 height = sdres[1];
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
if (auto previewp = static_cast<LLSnapshotLivePreview*>(mPreviewHandle.get()); previewp && combobox->getCurrentIndex() >= 0)
{
checkAspectRatio(width);
S32 original_width = 0;
S32 original_height = 0;
previewp->getSize(original_width, original_height);
if (width == 0 || height == 0)
{
// take resolution from current window size
LL_DEBUGS() << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x"
<< gViewerWindow->getWindowHeightRaw() << LL_ENDL;
previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
}
else if (width == -1 || height == -1)
{
// take resolution from custom size
LLSpinCtrl* width_spinner = getChild<LLSpinCtrl>("custom_snapshot_width");
LLSpinCtrl* height_spinner = getChild<LLSpinCtrl>("custom_snapshot_height");
S32 custom_width = width_spinner->getValue().asInteger();
S32 custom_height = height_spinner->getValue().asInteger();
if (checkImageSize(previewp, custom_width, custom_height, true, previewp->getMaxImageSize()))
{
width_spinner->set((F32)custom_width);
height_spinner->set((F32)custom_height);
}
LL_DEBUGS() << "Setting preview res from custom: " << custom_width << "x" << custom_height << LL_ENDL;
previewp->setSize(custom_width, custom_height);
}
else
{
// use the resolution from the selected pre-canned drop-down choice
LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL;
previewp->setSize(width, height);
}
previewp->getSize(width, height);
if ((original_width != width) || (original_height != height))
{
previewp->setSize(width, height);
if (do_update)
{
previewp->updateSnapshot(true, true);
updateControls();
}
}
// Get the old filter, compare to the current one "filter_name" and set if changed
std::string original_filter = previewp->getFilter();
if (original_filter != filter_name)
{
previewp->setFilter(filter_name);
if (do_update)
{
previewp->updateSnapshot(false, true);
updateControls();
}
}
}
bool custom_resolution = static_cast<LLComboBox*>(mResolutionComboBox)->getSelectedValue().asString() == "[i-1,i-1]";
getChild<LLSpinCtrl>("custom_snapshot_width")->setEnabled(custom_resolution);
getChild<LLSpinCtrl>("custom_snapshot_height")->setEnabled(custom_resolution);
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setEnabled(custom_resolution);
}
void FSPrimfeedPhotoPanel::checkAspectRatio(S32 index)
{
LLSnapshotLivePreview* previewp = getPreviewView();
bool keep_aspect = false;
if (0 == index) // current window size
{
keep_aspect = true;
}
else if (-1 == index)
{
keep_aspect = getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->get();
}
else // predefined resolution
{
keep_aspect = false;
}
if (previewp)
{
previewp->mKeepAspectRatio = keep_aspect;
}
}
LLUICtrl* FSPrimfeedPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
}
void FSPrimfeedPhotoPanel::onOpen(const LLSD& key)
{
if (!FSPrimfeedAuth::isAuthorized())
{
// Reauthorise if necessary.
FSPrimfeedAuth::initiateAuthRequest();
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
}
}
void FSPrimfeedPhotoPanel::uploadCallback(bool success, const LLSD& response)
{
LLSD args;
if (success && response["stat"].asString() == "ok")
{
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTED);
args["PF_POSTURL"] = response["postUrl"];
LLNotificationsUtil::add("FSPrimfeedUploadComplete", args);
}
else
{
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POST_FAILED);
}
}
void FSPrimfeedPhotoPanel::primfeedAuthResponse(bool success, const LLSD& response)
{
if (!success)
{
if (response.has("status") && response["status"].asString() == "reset")
{
LL_INFOS("Primfeed") << "Primfeed authorization has been reset." << LL_ENDL;
}
else
{
// Complain about failed auth here.
LL_WARNS("Primfeed") << "Primfeed authentication failed." << LL_ENDL;
}
}
onPrimfeedConnectStateChange(response);
}
bool FSPrimfeedPhotoPanel::checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, bool isWidthChanged, S32 max_value)
{
S32 w = width;
S32 h = height;
if (previewp && previewp->mKeepAspectRatio)
{
if (gViewerWindow->getWindowWidthRaw() < 1 || gViewerWindow->getWindowHeightRaw() < 1)
{
return false;
}
// aspect ratio of the current window
F32 aspect_ratio = static_cast<F32>(gViewerWindow->getWindowWidthRaw()) / static_cast<F32>(gViewerWindow->getWindowHeightRaw());
// change another value proportionally
if (isWidthChanged)
{
height = ll_round(static_cast<F32>(width) / aspect_ratio);
}
else
{
width = ll_round(static_cast<F32>(height) * aspect_ratio);
}
// bound w/h by the max_value
if (width > max_value || height > max_value)
{
if (width > height)
{
width = max_value;
height = ll_round(static_cast<F32>(width) / aspect_ratio);
}
else
{
height = max_value;
width = ll_round(static_cast<F32>(height) * aspect_ratio);
}
}
}
return (w != width || h != height);
}
///////////////////////////
// FSPrimfeedAccountPanel///
///////////////////////////
FSPrimfeedAccountPanel::FSPrimfeedAccountPanel() :
mAccountConnectedAsLabel(nullptr),
mAccountNameLink(nullptr),
mAccountPlan(nullptr),
mPanelButtons(nullptr),
mConnectButton(nullptr),
mDisconnectButton(nullptr)
{
mCommitCallbackRegistrar.add("SocialSharing.Connect", [this](LLUICtrl*, const LLSD&) { onConnect(); });
mCommitCallbackRegistrar.add("SocialSharing.Disconnect", [this](LLUICtrl*, const LLSD&) { onDisconnect(); });
FSPrimfeedAuth::sPrimfeedAuthPump->listen("FSPrimfeedAccountPanel",
[this](const LLSD& data)
{
bool success = data["success"].asBoolean();
primfeedAuthResponse(success, data);
return true;
});
setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
}
bool FSPrimfeedAccountPanel::postBuild()
{
mAccountConnectedAsLabel = getChild<LLTextBox>("connected_as_label");
mAccountNameLink = getChild<LLTextBox>("primfeed_account_name");
mAccountPlan = getChild<LLTextBox>("primfeed_account_plan");
mPanelButtons = getChild<LLUICtrl>("panel_buttons");
mConnectButton = getChild<LLUICtrl>("connect_btn");
mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
return LLPanel::postBuild();
}
void FSPrimfeedAccountPanel::draw()
{
FSPrimfeedConnect::EConnectionState connection_state = FSPrimfeedConnect::instance().getConnectionState();
static FSPrimfeedConnect::EConnectionState last_state = FSPrimfeedConnect::PRIMFEED_DISCONNECTED;
// Update the connection state if it has changed
if (connection_state != last_state)
{
onPrimfeedConnectStateChange(LLSD());
last_state = connection_state;
}
LLPanel::draw();
}
void FSPrimfeedAccountPanel::primfeedAuthResponse(bool success, const LLSD& response)
{
if (!success)
{
LL_WARNS("Primfeed") << "Primfeed authentication failed." << LL_ENDL;
LLWeb::loadURLExternal("https://www.primfeed.com/login");
}
onPrimfeedConnectStateChange(response);
}
void FSPrimfeedAccountPanel::onVisibilityChange(bool visible)
{
if (visible)
{
// Connected
if (FSPrimfeedAuth::isAuthorized())
{
showConnectedLayout();
}
else
{
showDisconnectedLayout();
}
}
}
bool FSPrimfeedAccountPanel::onPrimfeedConnectStateChange(const LLSD&)
{
if (FSPrimfeedAuth::isAuthorized() || FSPrimfeedConnect::instance().getConnectionState() == FSPrimfeedConnect::PRIMFEED_CONNECTING)
{
showConnectedLayout();
}
else
{
showDisconnectedLayout();
}
onPrimfeedConnectInfoChange();
return false;
}
bool FSPrimfeedAccountPanel::onPrimfeedConnectInfoChange()
{
std::string clickable_name{ "" };
static LLCachedControl<std::string> primfeed_username(gSavedPerAccountSettings, "FSPrimfeedUsername");
static LLCachedControl<std::string> primfeed_profile_link(gSavedPerAccountSettings, "FSPrimfeedProfileLink");
static LLCachedControl<std::string> primfeed_plan(gSavedPerAccountSettings, "FSPrimfeedPlan");
// Strings of format [http://www.somewebsite.com Click Me] become clickable text
if (!primfeed_username().empty())
{
clickable_name = std::string("[") + std::string(primfeed_profile_link) + " " + std::string(primfeed_username) + "]";
}
mAccountNameLink->setText(clickable_name);
mAccountPlan->setText(primfeed_plan());
return false;
}
void FSPrimfeedAccountPanel::showConnectButton()
{
if (!mConnectButton->getVisible())
{
mConnectButton->setVisible(true);
mDisconnectButton->setVisible(false);
}
}
void FSPrimfeedAccountPanel::hideConnectButton()
{
if (mConnectButton->getVisible())
{
mConnectButton->setVisible(false);
mDisconnectButton->setVisible(true);
}
}
void FSPrimfeedAccountPanel::showDisconnectedLayout()
{
mAccountConnectedAsLabel->setText(getString("primfeed_disconnected"));
mAccountNameLink->setText(std::string(""));
mAccountPlan->setText(getString("primfeed_plan_unknown"));
showConnectButton();
}
void FSPrimfeedAccountPanel::showConnectedLayout()
{
mAccountConnectedAsLabel->setText(getString("primfeed_connected"));
hideConnectButton();
}
void FSPrimfeedAccountPanel::onConnect()
{
FSPrimfeedAuth::initiateAuthRequest();
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
}
void FSPrimfeedAccountPanel::onDisconnect()
{
FSPrimfeedAuth::resetAuthStatus();
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
}
////////////////////////
// FSFloaterPrimfeed/////
////////////////////////
FSFloaterPrimfeed::FSFloaterPrimfeed(const LLSD& key) :
LLFloater(key),
mPrimfeedPhotoPanel(nullptr),
mStatusErrorText(nullptr),
mStatusLoadingText(nullptr),
mStatusLoadingIndicator(nullptr),
mPrimfeedAccountPanel(nullptr)
{
mCommitCallbackRegistrar.add("SocialSharing.Cancel", [this](LLUICtrl*, const LLSD&) { onCancel(); });
}
void FSFloaterPrimfeed::onClose(bool app_quitting)
{
if (auto big_preview_floater = LLFloaterReg::getTypedInstance<LLFloaterBigPreview>("big_preview"))
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
LLFloater::onClose(app_quitting);
}
void FSFloaterPrimfeed::onCancel()
{
if (auto big_preview_floater = LLFloaterReg::getTypedInstance<LLFloaterBigPreview>("big_preview"))
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
closeFloater();
}
bool FSFloaterPrimfeed::postBuild()
{
// Keep tab of the Photo Panel
mPrimfeedPhotoPanel = static_cast<FSPrimfeedPhotoPanel*>(getChild<LLUICtrl>("panel_primfeed_photo"));
mPrimfeedAccountPanel = static_cast<FSPrimfeedAccountPanel*>(getChild<LLUICtrl>("panel_primfeed_account"));
// Connection status widgets
mStatusErrorText = getChild<LLTextBox>("connection_error_text");
mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
return LLFloater::postBuild();
}
void FSFloaterPrimfeed::showPhotoPanel()
{
auto parent = dynamic_cast<LLTabContainer*>(mPrimfeedPhotoPanel->getParent());
if (!parent)
{
LL_WARNS() << "Cannot find panel container" << LL_ENDL;
return;
}
parent->selectTabPanel(mPrimfeedPhotoPanel);
}
void FSFloaterPrimfeed::draw()
{
if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
{
mStatusErrorText->setVisible(false);
mStatusLoadingText->setVisible(false);
mStatusLoadingIndicator->setVisible(false);
FSPrimfeedConnect::EConnectionState connection_state = FSPrimfeedConnect::instance().getConnectionState();
std::string status_text;
if (FSPrimfeedAuth::isAuthorized())
{
switch (connection_state)
{
case FSPrimfeedConnect::PRIMFEED_POSTING:
{
// Posting indicator
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialPrimfeedPosting");
mStatusLoadingText->setValue(status_text);
mStatusLoadingIndicator->setVisible(true);
break;
}
case FSPrimfeedConnect::PRIMFEED_POST_FAILED:
{
// Error posting to the service
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialPrimfeedErrorPosting");
mStatusErrorText->setValue(status_text);
break;
}
default:
{
// LL_WARNS("Prmfeed") << "unexpected state" << connection_state << LL_ENDL;
break;
}
}
}
else if (FSPrimfeedAuth::isPendingAuth())
{
// Show the status text when authorisation is pending
mStatusLoadingText->setVisible(true);
status_text = LLTrans::getString("SocialPrimfeedConnecting");
mStatusLoadingText->setValue(status_text);
}
else
{
// Show the status text when not authorised
mStatusErrorText->setVisible(true);
status_text = LLTrans::getString("SocialPrimfeedNotAuthorized");
mStatusErrorText->setValue(status_text);
}
}
LLFloater::draw();
}
void FSFloaterPrimfeed::onOpen(const LLSD& key)
{
mPrimfeedPhotoPanel->onOpen(key);
}
LLSnapshotLivePreview* FSFloaterPrimfeed::getPreviewView()
{
if (mPrimfeedPhotoPanel)
{
return mPrimfeedPhotoPanel->getPreviewView();
}
return nullptr;
}

View File

@ -0,0 +1,154 @@
/**
* @file fsfloaterprimfeed.cpp
* @brief Declaration of primfeed floater
* @author beq@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FS_FLOATERPRIMFEED_H
#define FS_FLOATERPRIMFEED_H
#include "llfloater.h"
#include "lltextbox.h"
#include "llviewertexture.h"
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLSnapshotLivePreview;
class LLFloaterBigPreview;
/*
* (TODO) Beq: Refactor this with Flickr
* Primfeed floater is copied heavily from the LLFlaoterFlickr class and deliberately implemetns much of the underlying plumbinng into the connector class.
* Once this is bedded in and any initial issues are addressed, it would be sensible to refactor both the flickr and primfeed classes to share a common base.
* In particular a ref counted test for the livepreview would eliminate the need for the static update method in the app mainloop.
*/
class FSPrimfeedPhotoPanel : public LLPanel
{
public:
FSPrimfeedPhotoPanel();
~FSPrimfeedPhotoPanel();
bool postBuild() override;
S32 notify(const LLSD& info);
void draw() override;
LLSnapshotLivePreview* getPreviewView();
void onVisibilityChange(bool new_visibility);
void onClickNewSnapshot();
void onClickBigPreview();
void onSend();
bool onPrimfeedConnectStateChange(const LLSD& data);
void sendPhoto();
void clearAndClose();
void updateControls();
void updateResolution(bool do_update);
void checkAspectRatio(S32 index);
LLUICtrl* getRefreshBtn();
void onOpen(const LLSD& key) override;
void primfeedAuthResponse(bool success, const LLSD& response);
void uploadCallback(bool success, const LLSD& response);
private:
bool isPreviewVisible() const;
void attachPreview();
bool checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, bool isWidthChanged, S32 max_value);
LLHandle<LLView> mPreviewHandle;
LLUICtrl * mResolutionComboBox;
LLUICtrl * mFilterComboBox;
LLUICtrl * mRefreshBtn;
LLUICtrl * mWorkingLabel;
LLUICtrl * mThumbnailPlaceholder;
LLUICtrl * mDescriptionTextBox;
LLUICtrl * mLocationCheckbox;
LLUICtrl * mCommercialCheckbox;
LLUICtrl * mPublicGalleryCheckbox;
LLUICtrl * mRatingComboBox;
LLUICtrl * mPostButton;
LLUICtrl * mCancelButton;
LLButton * mBtnPreview;
LLFloaterBigPreview * mBigPreviewFloater;
};
class FSPrimfeedAccountPanel : public LLPanel
{
public:
FSPrimfeedAccountPanel();
bool postBuild() override;
void draw() override;
private:
void onVisibilityChange(bool new_visibility);
void primfeedAuthResponse(bool success, const LLSD& response);
bool onPrimfeedConnectStateChange(const LLSD& data);
bool onPrimfeedConnectInfoChange();
void onConnect();
void onDisconnect();
void showConnectButton();
void hideConnectButton();
void showDisconnectedLayout();
void showConnectedLayout();
LLTextBox* mAccountConnectedAsLabel;
LLTextBox* mAccountNameLink;
LLTextBox* mAccountPlan;
LLUICtrl* mPanelButtons;
LLUICtrl* mConnectButton;
LLUICtrl* mDisconnectButton;
};
class FSFloaterPrimfeed : public LLFloater
{
public:
explicit FSFloaterPrimfeed(const LLSD& key);
static void update();
bool postBuild() override;
void draw() override;
void onClose(bool app_quitting) override;
void onCancel();
void showPhotoPanel();
void onOpen(const LLSD& key) override;
LLSnapshotLivePreview* getPreviewView();
private:
FSPrimfeedPhotoPanel* mPrimfeedPhotoPanel;
FSPrimfeedAccountPanel* mPrimfeedAccountPanel;
LLTextBox* mStatusErrorText;
LLTextBox* mStatusLoadingText;
LLUICtrl* mStatusLoadingIndicator;
};
#endif // LL_FSFLOATERPRIMFEED_H

View File

@ -923,6 +923,12 @@ void FSPanelLogin::loadLoginPage()
// login page (web) content version
params["login_content_version"] = gSavedSettings.getString("LoginContentVersion");
// No version popup
if (gSavedSettings.getBOOL("FSNoVersionPopup"))
{
params["noversionpopup"] = "true";
}
// Make an LLURI with this augmented info
std::string url = login_page.scheme().empty()? login_page.authority() : login_page.scheme() + "://" + login_page.authority();
LLURI login_uri(LLURI::buildHTTP(url,

View File

@ -190,18 +190,18 @@ public:
/// <summary>
/// An ordered list of poser joints, clustered by body-area.
/// Order is based on ease-of-use.
/// Not necessarily exhaustive, just the joints we care to edit without adding UI clutter.
/// </summary>
/// <remarks>
/// For an implementation of something other than LLJoints, different name(s) may be required.
/// A bvhEndSiteValue is only required if the bone has no descendants.
/// </remarks>
const std::vector<FSPoserJoint> PoserJoints{
// head, torso, legs
{ "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot" }, "0.000 0.076 0.000" },
{ "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot", "mSkull" }, "0.000 0.076 0.000" },
{ "mNeck", "", BODY, { "mHead" }, "0.000 0.251 -0.010" },
{ "mPelvis", "", WHOLEAVATAR, { "mTorso", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot" }, "0.000000 0.000000 0.000000" },
{ "mPelvis", "", WHOLEAVATAR, { "mSpine1", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot" }, "0.000000 0.000000 0.000000" },
{ "mChest", "", BODY, { "mNeck", "mCollarLeft", "mCollarRight", "mWingsRoot" }, "0.000 0.205 -0.015" },
{ "mTorso", "", BODY, { "mChest" }, "0.000 0.084 0.000" },
{ "mTorso", "", BODY, { "mSpine3" }, "0.000 0.084 0.000" },
{ "mCollarLeft", "mCollarRight", BODY, { "mShoulderLeft" }, "0.085 0.165 -0.021" },
{ "mShoulderLeft", "mShoulderRight", BODY, { "mElbowLeft" }, "0.079 0.000 0.000" },
{ "mElbowLeft", "mElbowRight", BODY, { "mWristLeft" }, "0.248 0.000 0.000" },
@ -212,10 +212,12 @@ public:
{ "mWristRight", "mWristLeft", BODY, { "mHandThumb1Right", "mHandIndex1Right", "mHandMiddle1Right", "mHandRing1Right", "mHandPinky1Right" }, "-0.205 0.000 0.000", "", true },
{ "mHipLeft", "mHipRight", BODY, { "mKneeLeft" }, "0.127 -0.041 0.034" },
{ "mKneeLeft", "mKneeRight", BODY, { "mAnkleLeft" }, "-0.046 -0.491 -0.001" },
{ "mAnkleLeft", "mAnkleRight", BODY, {}, "0.001 -0.468 -0.029", "0.000 -0.061 0.112" },
{ "mHipRight", "mHipLeft", BODY, { "mKneeRight" }, "-0.129 -0.041 0.034", "0.000 -0.061 0.112", true },
{ "mAnkleLeft", "mAnkleRight", BODY, { "mToeLeft" }, "0.001 -0.468 -0.029" },
{ "mToeLeft", "mToeRight", BODY, {}, "0.000 0.109 0.000", "0.000 0.020 0.000" },
{ "mHipRight", "mHipLeft", BODY, { "mKneeRight" }, "-0.129 -0.041 0.034", "", true },
{ "mKneeRight", "mKneeLeft", BODY, { "mAnkleRight" }, "0.049 -0.491 -0.001", "", true },
{ "mAnkleRight", "mAnkleLeft", BODY, {}, "0.000 -0.468 -0.029", "0.000 -0.061 0.112", true },
{ "mAnkleRight", "mAnkleLeft", BODY, { "mToeRight" }, "0.000 -0.468 -0.029", "", true },
{ "mToeRight", "mToeLeft", BODY, {}, "0.000 0.109 0.000", "0.000 0.020 0.000", true },
// face
{ "mFaceRoot",
@ -225,10 +227,10 @@ public:
"mFaceForeheadLeft", "mFaceForeheadCenter", "mFaceForeheadRight",
"mFaceEyebrowOuterLeft", "mFaceEyebrowCenterLeft", "mFaceEyebrowInnerLeft",
"mFaceEyebrowOuterRight", "mFaceEyebrowCenterRight", "mFaceEyebrowInnerRight",
"mFaceEyeLidUpperLeft", "mFaceEyeLidLowerLeft",
"mFaceEyeLidUpperRight", "mFaceEyeLidLowerRight",
"mFaceEyeLidUpperLeft", "mFaceEyeLidLowerLeft", "mFaceEyecornerInnerLeft",
"mFaceEyeLidUpperRight", "mFaceEyeLidLowerRight", "mFaceEyecornerInnerRight",
"mFaceEar1Left", "mFaceEar1Right",
"mFaceNoseLeft", "mFaceNoseCenter", "mFaceNoseRight",
"mFaceNoseBase", "mFaceNoseBridge", "mFaceNoseLeft", "mFaceNoseCenter", "mFaceNoseRight",
"mFaceCheekUpperLeft", "mFaceCheekLowerLeft",
"mFaceCheekUpperRight", "mFaceCheekLowerRight",
"mFaceJaw", "mFaceTeethUpper"
@ -247,14 +249,18 @@ public:
{ "mEyeLeft", "mEyeRight", FACE, {}, "-0.036 0.079 0.098", "0.000 0.000 0.025" },
{ "mEyeRight", "mEyeLeft", FACE, {}, "0.036 0.079 0.098", "0.000 0.000 0.025", true },
{ "mFaceEyeLidUpperLeft", "mFaceEyeLidUpperRight", FACE, {}, "0.036 0.034 0.073", "0.000 0.005 0.027" },
{ "mFaceEyecornerInnerLeft", "mFaceEyecornerInnerRight", FACE, {}, "0.032 0.075 0.017", "0.000 0.016 0.000" },
{ "mFaceEyeLidLowerLeft", "mFaceEyeLidLowerRight", FACE, {}, "0.036 0.034 0.073", "0.000 -0.007 0.024" },
{ "mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE, {}, "-0.036 0.034 0.073", "0.000 0.005 0.027", true },
{ "mFaceEyecornerInnerRight", "mFaceEyecornerInnerLeft", FACE, {}, "0.032 0.075 -0.017", "0.000 0.016 0.000", true },
{ "mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE, {}, "-0.036 0.034 0.073", "0.000 -0.007 0.024", true },
{ "mFaceEar1Left", "mFaceEar1Right", FACE, { "mFaceEar2Left" }, "0.080 0.002 0.000", "" },
{ "mFaceEar2Left", "mFaceEar2Right", FACE, {}, "0.018 0.025 -0.019", "0.000 0.033 0.000" },
{ "mFaceEar1Right", "mFaceEar1Left", FACE, { "mFaceEar2Right" }, "-0.080 0.002 0.000", "", true },
{ "mFaceEar2Right", "mFaceEar2Left", FACE, {}, "-0.018 0.025 -0.019", "0.000 0.033 0.000", true },
{ "mFaceNoseBase", "", FACE, {}, "-0.016 0.094 0.000", "0.000 0.014 0.000" },
{ "mFaceNoseBridge", "", FACE, {}, "0.020 0.091 0.000", "0.008 0.015 0.000" },
{ "mFaceNoseLeft", "mFaceNoseRight", FACE, {}, "0.015 -0.004 0.086", "0.004 0.000 0.015" },
{ "mFaceNoseCenter", "", FACE, {}, "0.000 0.000 0.102", "0.000 0.000 0.025" },
{ "mFaceNoseRight", "mFaceNoseLeft", FACE, {}, "-0.015 -0.004 0.086", "-0.004 0.000 0.015", true },
@ -343,11 +349,39 @@ public:
{ "mWing4Right", "mWing4Left", MISC, {}, "-0.173 0.000 -0.171", "-0.132 0.000 -0.146", true },
{ "mWing4FanRight", "mWing4FanLeft", MISC, {}, "-0.173 0.000 -0.171", "-0.062 -0.159 -0.068", true },
// Misc body parts
{ "mSkull", "", MISC, {}, "0.079 0.000 0.000", "0.033 0.000 0.000" },
{ "mSpine1", "", MISC, { "mSpine2" }, "0.084 0.000 0.000" },
{ "mSpine2", "", MISC, { "mTorso", }, "-0.084 0.000 0.000" },
{ "mSpine3", "", MISC, { "mSpine4" }, "0.205 -0.015 0.000" },
{ "mSpine4", "", MISC, { "mChest", }, "-0.205 0.015 0.000" },
// Collision Volumes
{ "HEAD", "", COL_VOLUMES },
{ "NECK", "", COL_VOLUMES },
{ "L_CLAVICLE", "R_CLAVICLE", COL_VOLUMES },
{ "R_CLAVICLE", "L_CLAVICLE", COL_VOLUMES, {}, "", "", true },
{ "CHEST", "", COL_VOLUMES },
{ "LEFT_PEC", "RIGHT_PEC", COL_VOLUMES },
{ "RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, {}, "", "", true },
{ "UPPER_BACK", "", COL_VOLUMES },
{ "LEFT_HANDLE", "RIGHT_HANDLE", COL_VOLUMES },
{ "RIGHT_HANDLE", "LEFT_HANDLE", COL_VOLUMES, {}, "", "", true },
{ "BELLY", "", COL_VOLUMES },
{ "PELVIS", "", COL_VOLUMES },
{ "BUTT", "", COL_VOLUMES },
{ "L_UPPER_ARM", "R_UPPER_ARM", COL_VOLUMES },
{ "R_UPPER_ARM", "L_UPPER_ARM", COL_VOLUMES, {}, "", "", true },
{ "L_LOWER_ARM", "R_LOWER_ARM", COL_VOLUMES },
{ "R_LOWER_ARM", "L_LOWER_ARM", COL_VOLUMES, {}, "", "", true },
{ "L_HAND", "R_HAND", COL_VOLUMES },
{ "R_HAND", "L_HAND", COL_VOLUMES, {}, "", "", true },
{ "L_UPPER_LEG", "R_UPPER_LEG", COL_VOLUMES },
{ "R_UPPER_LEG", "L_UPPER_LEG", COL_VOLUMES, {}, "", "", true },
{ "L_LOWER_LEG", "R_LOWER_LEG", COL_VOLUMES },
{ "R_LOWER_LEG", "L_LOWER_LEG", COL_VOLUMES, {}, "", "", true },
{ "L_FOOT", "R_FOOT", COL_VOLUMES },
{ "R_FOOT", "L_FOOT", COL_VOLUMES, {}, "", "", true },
};
public:

View File

@ -0,0 +1,450 @@
/**
* @file fsprimfeedauth.cpp
* @file fsprimfeedauth.h
* @brief Primfeed Authorisation workflow class
* @author beq@firestorm
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
/*
* Handles Primfeed authentication and authorisation through a multi-factor OAuth flow.
*
* This module integrates with Primfeeds Third Party Viewers API.
* The authentication flow is as follows:
* 1. Initiate a login request:
* POST https://api.primfeed.com/pf/viewer/create-login-request
* Headers:
* pf-viewer-api-key: <viewer_api_key>
* pf-user-uuid: <avatar_uuid>
* Response:
* { "requestId": "<64-char string>" }
*
* 2. Redirect the user to:
* https://www.primfeed.com/oauth/viewer?r=<requestId>&v=<viewer_api_key>
*
* 3. The user is shown an approval screen. When they click Authorize,
* an in-world message is sent:
* #PRIMFEED_OAUTH: <oauth_token>
* We intercept this code through an onChat handle then call onOauthTokenReceived().
*
* 4. Validate the login request:
* POST https://api.primfeed.com/pf/viewer/validate-request
* Headers:
* Authorization: Bearer <oauth_token>
* pf-viewer-api-key: <viewer_api_key>
* pf-viewer-request-id: <requestId>
* Response: HTTP 204
*
* 5. Optionally, check user status:
* GET https://api.primfeed.com/pf/viewer/user
* Headers:
* Authorization: Bearer <oauth_token>
* pf-viewer-api-key: <viewer_api_key>
* Response: { "plan": "free" } (or "pro")
*/
#include "llviewerprecompiledheaders.h"
#include "fsprimfeedauth.h"
#include "fsprimfeedconnect.h"
#include "llimview.h"
#include "llnotificationsutil.h"
#include "llfloaterimnearbychathandler.h"
#include "llnotificationmanager.h"
#include "llagent.h"
#include "llevents.h"
#include "fscorehttputil.h"
#include "llwindow.h"
#include "llviewerwindow.h"
#include "lluri.h"
#include "llsdjson.h"
#include <string_view>
using Callback = FSPrimfeedAuth::authorized_callback_t;
// private instance variable
std::shared_ptr<FSPrimfeedAuth> FSPrimfeedAuth::sPrimfeedAuth;
std::unique_ptr<LLEventPump> FSPrimfeedAuth::sPrimfeedAuthPump = std::make_unique<LLEventStream>("PrimfeedAuthResponse");
// Helper callback that unpacks HTTP POST response data.
void FSPrimfeedAuthResponse(LLSD const &aData, Callback callback)
{
LLSD header = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS][LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(
aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]);
const LLSD::Binary &rawData = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary();
std::string result;
result.assign(rawData.begin(), rawData.end());
// Assume JSON response.
LLSD resultLLSD;
if(!result.empty())
{
resultLLSD = LlsdFromJson(boost::json::parse(result));
}
callback((status.getType() == HTTP_OK ||
status.getType() == HTTP_NO_CONTENT), resultLLSD);
}
void FSPrimfeedAuth::initiateAuthRequest()
{
// This function is called to initiate the authentication request.
// It should be called when the user clicks the "Authenticate" button.
// Also triggered on opening the floater.
// The actual implementation is in the create() method.
if (!isAuthorized())
{
if (sPrimfeedAuth)
{
LLNotificationsUtil::add("PrimfeedAuthorizationAlreadyInProgress");
return;
}
// If no token stored, begin the login request; otherwise check user status.
sPrimfeedAuth = FSPrimfeedAuth::create(
[](bool success, const LLSD &response)
{
LLSD event_data = response;
event_data["success"] = success;
sPrimfeedAuthPump->post(event_data);
// Now that auth is complete, clear the static pointer.
sPrimfeedAuth.reset();
}
);
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
}
else
{
LLNotificationsUtil::add("PrimfeedAlreadyAuthorized");
}
}
void FSPrimfeedAuth::resetAuthStatus()
{
sPrimfeedAuth.reset();
gSavedPerAccountSettings.setString("FSPrimfeedOAuthToken", "");
gSavedPerAccountSettings.setString("FSPrimfeedProfileLink", "");
gSavedPerAccountSettings.setString("FSPrimfeedPlan", "");
gSavedPerAccountSettings.setString("FSPrimfeedUsername", "");
LLSD event_data;
event_data["status"] = "reset";
event_data["success"] = "false";
sPrimfeedAuthPump->post(event_data);
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
}
FSPrimfeedAuth::FSPrimfeedAuth(authorized_callback_t callback)
: mCallback(callback)
{
mChatMessageConnection = LLNotificationsUI::LLNotificationManager::instance().getChatHandler()->addNewChatCallback(
[this](const LLSD &message) {
LL_DEBUGS("FSPrimfeedAuth") << "Received chat message: " << message["message"].asString() << LL_ENDL;
this->onChatMessage(message);
});
}
FSPrimfeedAuth::~FSPrimfeedAuth()
{
if (mChatMessageConnection.connected())
{
try
{
mChatMessageConnection.disconnect();
}
catch (const std::exception& e)
{
LL_WARNS("FSPrimfeedAuth") << "Exception during chat connection disconnect: " << e.what() << LL_ENDL;
}
catch (...)
{
LL_WARNS("FSPrimfeedAuth") << "Unknown exception during chat connection disconnect." << LL_ENDL;
}
}
}
// Factory method to create a shared pointer to FSPrimfeedAuth.
std::shared_ptr<FSPrimfeedAuth> FSPrimfeedAuth::create(authorized_callback_t callback)
{
// Ensure only one authentication attempt is in progress.
if (sPrimfeedAuth)
{
// Already in progress; return the existing instance.
return sPrimfeedAuth;
}
auto auth = std::shared_ptr<FSPrimfeedAuth>(new FSPrimfeedAuth(callback));
if(!auth)
{
return nullptr;
}
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
// If no token stored, begin the login request; otherwise check user status.
if (gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken").empty())
{
auth->beginLoginRequest();
}
else
{
auth->checkUserStatus();
}
return auth;
}
void FSPrimfeedAuth::beginLoginRequest()
{
// Get our API key and user UUID.
std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
std::string user_uuid = gAgent.getID().asString();
std::string url = "https://api.primfeed.com/pf/viewer/create-login-request";
std::string post_data = ""; // No body parameters required.
// Create the headers object.
LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
pHeader->append("pf-viewer-api-key", viewer_api_key);
pHeader->append("pf-user-uuid", user_uuid);
// Set up HTTP options
options->setWantHeaders(true);
options->setRetries(0);
options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
// Capture shared_ptr to self
auto self = shared_from_this();
const auto end(pHeader->end());
for (auto it(pHeader->begin()); end != it; ++it)
{
LL_DEBUGS("Primfeed") << "Header: " << it->first << " = " << it->second << LL_ENDL;
}
// Pass both success and failure callbacks
FSCoreHttpUtil::callbackHttpPostRaw(
url,
post_data,
[self](LLSD const &aData) {
LL_DEBUGS("FSPrimfeedAuth") << "Login request response(OK): " << aData << LL_ENDL;
FSPrimfeedAuthResponse(aData,
[self](bool success, const LLSD &response) {
self->gotRequestId(success, response);
}
);
},
[self](LLSD const &aData) {
LL_DEBUGS("FSPrimfeedAuth") << "Login request response(FAIL): " << aData << LL_ENDL;
FSPrimfeedAuthResponse(aData,
[self](bool success, const LLSD &response) {
self->gotRequestId(success, response);
}
);
},
pHeader,
options
);
}
void FSPrimfeedAuth::gotRequestId(bool success, const LLSD &response)
{
if (!success)
{
LLNotificationsUtil::add("PrimfeedLoginRequestFailed");
mCallback(false, LLSD());
return;
}
mRequestId = response["requestId"].asString();
if (mRequestId.empty())
{
LLNotificationsUtil::add("PrimfeedLoginRequestFailed");
mCallback(false, LLSD());
return;
}
// Open the browser for user approval.
std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
std::string auth_url = "https://www.primfeed.com/oauth/viewer?r=" + mRequestId + "&v=" + viewer_api_key;
gViewerWindow->getWindow()->spawnWebBrowser(auth_url, true);
}
/// This function is called by the chat interceptor when the message
/// "#PRIMFEED_OAUTH: <oauth_token>" is intercepted.
void FSPrimfeedAuth::onOauthTokenReceived(const std::string_view& oauth_token)
{
if (oauth_token.empty())
{
mCallback(false, LLSD());
return;
}
mOauthToken = oauth_token;
validateRequest();
}
void FSPrimfeedAuth::onChatMessage(const LLSD& message)
{
constexpr std::string_view oauth_msg_prefix = "#PRIMFEED_OAUTH: ";
const std::string msg = message["message"].asString();
if (msg.find(std::string(oauth_msg_prefix)) == 0)
{
std::string_view oauth_token(msg.data() + oauth_msg_prefix.size(), msg.size() - oauth_msg_prefix.size());
LL_DEBUGS("Primfeed") << "Received OAuth token: " << msg << "extracted:<" << oauth_token << ">" << LL_ENDL;
onOauthTokenReceived(oauth_token);
}
}
void FSPrimfeedAuth::validateRequest()
{
// No POST body needed.
std::string post_data = "";
std::string url = "https://api.primfeed.com/pf/viewer/validate-request";
// Retrieve the viewer API key.
std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
// Create and populate the headers.
LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
pHeader->append("Authorization", "Bearer " + mOauthToken);
pHeader->append("pf-viewer-api-key", viewer_api_key);
pHeader->append("pf-viewer-request-id", mRequestId);
// Set HTTP options
LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
options->setWantHeaders(true);
options->setRetries(0);
options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
// print out pHeader for debuging using iterating over pHeader and using LL_DEBUGS
const auto end(pHeader->end());
for (auto it(pHeader->begin()); end != it; ++it)
{
LL_DEBUGS("Primfeed") << "Header: " << it->first << " = " << it->second << LL_ENDL;
}
auto self = shared_from_this();
try
{
FSCoreHttpUtil::callbackHttpPostRaw(
url,
post_data,
[self](LLSD const &aData) {
LL_DEBUGS("FSPrimfeedAuth") << "Validation-request response(OK): " << aData << LL_ENDL;
FSPrimfeedAuthResponse(aData,
[self](bool success, const LLSD &response) {
self->gotValidateResponse(success, response);
}
);
},
[self](LLSD const &aData) {
LL_INFOS("FSPrimfeedAuth") << "Validation-request response(FAIL): " << aData << LL_ENDL;
FSPrimfeedAuthResponse(aData,
[self](bool success, const LLSD &response) {
self->gotValidateResponse(success, response);
}
);
},
pHeader,
options
);
}
catch(const std::exception& e)
{
LL_WARNS("Primfeed") << "Primfeed validation failed " << e.what() << LL_ENDL;
}
}
void FSPrimfeedAuth::gotValidateResponse(bool success, const LLSD &response)
{
if (!success)
{
LLNotificationsUtil::add("PrimfeedValidateFailed");
mCallback(false, response);
return;
}
checkUserStatus();
}
void FSPrimfeedAuth::checkUserStatus()
{
std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
// Build the base URL without query parameters.
std::string url = "https://api.primfeed.com/pf/viewer/user";
LL_DEBUGS("Primfeed") << "URL: " << url << LL_ENDL;
// Create and populate the headers.
LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
pHeader->append("Authorization", "Bearer " + mOauthToken);
pHeader->append("pf-viewer-api-key", viewer_api_key);
// Set HTTP options.
LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
options->setWantHeaders(true);
options->setRetries(0);
options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
// Make the HTTP GET request, passing in the headers and options.
FSCoreHttpUtil::callbackHttpGetRaw(
url,
[this](LLSD const &aData) {
LL_DEBUGS("FSPrimfeedAuth") << "Check-user-status response: " << aData << LL_ENDL;
FSPrimfeedAuthResponse(aData, [this](bool success, const LLSD &response) {
this->gotUserStatus(success, response);
});
},
[this](LLSD const &aData) {
LL_INFOS("FSPrimfeedAuth") << "Check-user-status response (failure): " << aData << LL_ENDL;
// Optionally, call the same processing for failure or handle separately.
FSPrimfeedAuthResponse(aData, [this](bool success, const LLSD &response){
this->gotUserStatus(success, response);
});
},
pHeader,
options
);
}
void FSPrimfeedAuth::gotUserStatus(bool success, const LLSD &response)
{
LL_INFOS("Primfeed") << "User status: " << response << "(" << success << ")" << LL_ENDL;
if (success && response.has("plan"))
{
gSavedPerAccountSettings.setString("FSPrimfeedOAuthToken", mOauthToken);
gSavedPerAccountSettings.setString("FSPrimfeedPlan", response["plan"].asString());
gSavedPerAccountSettings.setString("FSPrimfeedProfileLink", response["link"].asString());
gSavedPerAccountSettings.setString("FSPrimfeedUsername", response["username"].asString());
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTED);
mCallback(true, response);
}
else
{
LLNotificationsUtil::add("PrimfeedUserStatusFailed");
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
mCallback(false, response);
}
}

View File

@ -0,0 +1,92 @@
/**
* @file fsprimfeedauth.h
* @brief Primfeed Authorisation workflow class
* @author beq@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FSPRIMFEEDAUTH_H
#define FSPRIMFEEDAUTH_H
#include "llsd.h"
#include "llviewercontrol.h"
#include <string>
#include <functional>
/*
* Primfeed authentication workflow class.
*
* This class handles the Primfeed OAuth login flow and provides methods to
* check the user status and receive a callback when the authentication
* process is complete.
* based on the workflow documented at https://docs.primfeed.com/api/third-party-viewers
*/
class FSPrimfeedAuth : public std::enable_shared_from_this<FSPrimfeedAuth>
{
public:
// Callback type: first parameter indicates success and the second holds any LLSD response.
using authorized_callback_t = std::function<void(bool, const LLSD&)>;
static std::shared_ptr<FSPrimfeedAuth> create(authorized_callback_t callback);
static std::unique_ptr<LLEventPump> sPrimfeedAuthPump;
~FSPrimfeedAuth();
// Should be called by the chat interceptor when an oauth token is received.
void onOauthTokenReceived(const std::string_view& oauth_token);
void onInstantMessage(const LLSD& message);
void onChatMessage(const LLSD& message);
// Begin the login request flow.
void beginLoginRequest();
// Check the user status.
void checkUserStatus();
static bool isPendingAuth(){ return (sPrimfeedAuth != nullptr); }
static bool isAuthorized(){ return (!gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken").empty()); }
static void initiateAuthRequest();
static void resetAuthStatus();
private:
static std::shared_ptr<FSPrimfeedAuth> sPrimfeedAuth;
explicit FSPrimfeedAuth(authorized_callback_t callback);
authorized_callback_t mCallback;
std::string mOauthToken;
std::string mRequestId;
// Callback when a login request response is received.
void gotRequestId(bool success, const LLSD &response);
// Validate the login request.
void validateRequest();
// Callback when the validate response is received.
void gotValidateResponse(bool success, const LLSD &response);
// Callback when the user status response is received.
void gotUserStatus(bool success, const LLSD &response);
boost::signals2::connection mInstantMessageConnection;
boost::signals2::connection mChatMessageConnection;
// Static flag to prevent duplicate authentication attempts.
static std::atomic<bool> sAuthorisationInProgress;
static constexpr U32 PRIMFEED_CONNECT_TIMEOUT = 300; // 5 minute timeout should work
};
#endif // FSPRIMFEEDAUTH_H

View File

@ -0,0 +1,189 @@
/**
* @file fsprimfeedconnect.cpp
* @brief Primfeed connector class
* @author beq@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "fsprimfeedconnect.h"
#include "fsprimfeedauth.h"
#include "llviewercontrol.h"
#include "llcoros.h"
#include "llsdjson.h"
// The connector workflow for Primfeed is realtively simple and mostly just builds on top of the established Auth workflow
// and the posting endpoint documented at https://docs.primfeed.com/api/third-party-viewers#creating-a-post
FSPrimfeedConnect::FSPrimfeedConnect() = default;
void FSPrimfeedConnect::uploadPhoto(const LLSD& params, LLImageFormatted* image, post_callback_t callback)
{
LL_DEBUGS("primfeed") << "uploadPhoto() called" << LL_ENDL;
if (!FSPrimfeedAuth::isAuthorized())
{
LL_WARNS("primfeed") << "Authorization failed, aborting.\n" << LL_ENDL;
callback(false, "");
return;
}
LL_DEBUGS("primfeed") << "Authorization successful" << LL_ENDL;
mPostCallback = callback;
LL_DEBUGS("primfeed") << "Launching upload coroutine" << LL_ENDL;
LLCoros::instance().launch(
"FSPrimfeedConnect::uploadPhotoCoro",
[this, params, image]() { uploadPhotoCoro(params, image); }
);
}
void FSPrimfeedConnect::uploadPhotoCoro(const LLSD& params, LLImageFormatted* image)
{
LL_DEBUGS("primfeed") << "Entered uploadPhotoCoro" << LL_ENDL;
setConnectionState(PRIMFEED_POSTING);
LL_DEBUGS("primfeed") << "Connection state set to PRIMFEED_POSTING" << LL_ENDL;
const std::string fmt = (image->getCodec() == EImageCodec::IMG_CODEC_JPEG) ? "jpg" : "png";
LL_DEBUGS("primfeed") << "Image format: " << fmt << LL_ENDL;
const std::string boundary = "----------------------------0123456789abcdef";
const std::string sep = "\n";
const std::string dash = "--" + boundary;
LL_DEBUGS("primfeed") << "Building multipart body" << LL_ENDL;
LLCore::BufferArray::ptr_t raw(new LLCore::BufferArray());
LLCore::BufferArrayStream body(raw.get());
auto addPart = [&](const std::string& name, const std::string& val)
{
LL_DEBUGS("primfeed") << "Adding part: " << name << "=" << val << LL_ENDL;
body << dash << sep
<< "Content-Disposition: form-data; name=\"" << name << "\"" << sep << sep
<< val << sep;
};
addPart("commercial", params["commercial"].asBoolean() ? "true" : "false");
addPart("rating", params["rating"].asString());
addPart("content", params["content"].asString());
addPart("publicGallery", params["post_to_public_gallery"].asBoolean()? "true" : "false");
if (params.has("location") && !params["location"].asString().empty())
{
addPart("location", params["location"].asString());
}
LL_DEBUGS("primfeed") << "Adding image file header" << LL_ENDL;
body << dash << sep
<< "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << fmt << "\"" << sep
<< "Content-Type: image/" << fmt << sep << sep;
U8* data = image->getData();
S32 size = image->getDataSize();
LL_DEBUGS("primfeed") << "Appending image data, size=" << size << LL_ENDL;
// yep this seems inefficient, but all other occurrences in the codebase do it this way.
for (S32 i = 0; i < size; ++i)
{
body << data[i];
}
body << sep;
body << dash << "--" << sep;
LL_DEBUGS("primfeed") << "Multipart body ready" << LL_ENDL;
// Setup HTTP
LL_DEBUGS("primfeed") << "Preparing HTTP request" << LL_ENDL;
LLCore::HttpRequest::policy_t policy = LLCore::HttpRequest::DEFAULT_POLICY_ID;
LLCoreHttpUtil::HttpCoroutineAdapter adapter("PrimfeedUpload", policy);
LLCore::HttpRequest::ptr_t request(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
options->setWantHeaders(true);
LL_DEBUGS("primfeed") << "Setting HTTP headers" << LL_ENDL;
LLCore::HttpHeaders::ptr_t headers(new LLCore::HttpHeaders);
std::string token = gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken");
std::string apiKey = gSavedSettings.getString("FSPrimfeedViewerApiKey");
headers->append("Authorization", "Bearer " + token);
headers->append("pf-viewer-api-key", apiKey);
headers->append("Content-Type", "multipart/form-data; boundary=" + boundary);
LL_DEBUGS("primfeed") << "Dumping HTTP headers for POST:" << LL_ENDL;
for (auto it = headers->begin(); it != headers->end(); ++it)
{
LL_DEBUGS("primfeed") << it->first << ": " << it->second << LL_ENDL;
}
LL_DEBUGS("primfeed") << "Headers set" << LL_ENDL;
LL_DEBUGS("primfeed") << "Starting HTTP POST" << LL_ENDL;
LLSD result = adapter.postRawAndSuspend(request,
"https://api.primfeed.com/pf/viewer/post",
raw,
options,
headers);
LL_DEBUGS("primfeed") << "HTTP POST complete" << LL_ENDL;
const LLSD::Binary &rawData = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary();
std::string response_raw;
response_raw.assign(rawData.begin(), rawData.end());
LLSD result_LLSD;
if(!response_raw.empty())
{
result_LLSD = LlsdFromJson(boost::json::parse(response_raw));
}
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]);
bool success = (status.getType() == HTTP_OK);
LL_DEBUGS("primfeed") << "HTTP status =" << (success?"OK":"FAIL") << " "<< status.getMessage() << LL_ENDL;
std::string url;
if (success)
{
url = result_LLSD["url"].asString();
LL_DEBUGS("primfeed") << "Received URL=" << url << LL_ENDL;
}
LL_DEBUGS("primfeed") << "Invoking callback" << LL_ENDL;
mPostCallback(success, url);
setConnectionState(success ? PRIMFEED_POSTED : PRIMFEED_POST_FAILED);
LL_DEBUGS("primfeed") << "Final state set" << LL_ENDL;
}
// Handle connection state transitions
void FSPrimfeedConnect::setConnectionState(EConnectionState state)
{
LL_DEBUGS("primfeed") << "setConnectionState(" << state << ")" << LL_ENDL;
mConnectionState = state;
}
FSPrimfeedConnect::EConnectionState FSPrimfeedConnect::getConnectionState() const
{
return mConnectionState;
}
bool FSPrimfeedConnect::isTransactionOngoing() const
{
return (mConnectionState == PRIMFEED_CONNECTING ||
mConnectionState == PRIMFEED_POSTING ||
mConnectionState == PRIMFEED_DISCONNECTING);
}
void FSPrimfeedConnect::loadPrimfeedInfo()
{
LL_DEBUGS("primfeed") << "loadPrimfeedInfo() called" << LL_ENDL;
// Nothing to do here for Primfeed
setConnectionState(PRIMFEED_CONNECTED);
}

View File

@ -0,0 +1,82 @@
/**
* @file fsprimfeedconect.h
* @brief Primfeed connector class
* @author beq@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Beq Janus
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FS_PRIMFEEDCONNECT_H
#define FS_PRIMFEEDCONNECT_H
#include "llsingleton.h"
#include "llsd.h"
#include "llimage.h"
#include "fsprimfeedauth.h"
#include "llcorehttputil.h"
#include "bufferarray.h"
#include "llcoros.h"
#include "llviewercontrol.h" // for gSavedSettings/gSavedPerAccountSettings
#include <functional>
// Coro based connector designed to interface with floater designed along the same principles as LLFloaterFlickr.cpp
class FSPrimfeedConnect : public LLSingleton<FSPrimfeedConnect>
{
LLSINGLETON(FSPrimfeedConnect);
public:
// Connection states for Primfeed operations
enum EConnectionState
{
PRIMFEED_DISCONNECTED = 0,
PRIMFEED_CONNECTING,
PRIMFEED_CONNECTED,
PRIMFEED_POSTING,
PRIMFEED_POSTED,
PRIMFEED_POST_FAILED,
PRIMFEED_DISCONNECTING
};
// Callback invoked on post completion: success flag and URL (empty on failure)
using post_callback_t = std::function<void(bool success, const std::string& url)>;
// Posts a snapshot to Primfeed; requires FSPrimfeedAuth::isAuthorized()
void uploadPhoto(const LLSD& params, LLImageFormatted* image, post_callback_t callback);
// Retrieve and update account info from Primfeed (not used kept for compatibility)
void loadPrimfeedInfo();
void setConnectionState(EConnectionState state);
EConnectionState getConnectionState() const;
bool isTransactionOngoing() const;
private:
// Internal coroutine entry-point for uploads
void uploadPhotoCoro(const LLSD& params, LLImageFormatted* image);
// Cached callback until coroutine completes
post_callback_t mPostCallback;
// Current connection/post state
EConnectionState mConnectionState = PRIMFEED_DISCONNECTED;
};
#endif // FS_PRIMFEEDCONNECT_H

View File

@ -646,6 +646,12 @@ void GLTFSceneManager::render(Asset& asset, U8 variant)
return;
}
if (gGLTFPBRMetallicRoughnessProgram.mGLTFVariants.size() <= variant)
{
llassert(false); // mGLTFVariants should have been initialized
return;
}
for (U32 ds = 0; ds < 2; ++ds)
{
RenderData& rd = asset.mRenderData[ds];

View File

@ -234,6 +234,8 @@
#include "llfloaterreg.h"
#include "llfloatersimplesnapshot.h"
#include "llfloatersnapshot.h"
#include "llfloaterflickr.h"
#include "fsfloaterprimfeed.h" // <FS:Beq/> Primfeed Floater
#include "llsidepanelinventory.h"
#include "llatmosphere.h"
@ -1764,6 +1766,8 @@ bool LLAppViewer::doFrame()
gPipeline.mReflectionMapManager.update();
LLFloaterSnapshot::update(); // take snapshots
LLFloaterSimpleSnapshot::update();
LLFloaterFlickr::update(); // <FS:Beq/> FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon
FSFloaterPrimfeed::update(); // <FS:Beq/> Primfeed support
gGLActive = false;
}
@ -3808,6 +3812,7 @@ bool LLAppViewer::waitForUpdater()
void LLAppViewer::writeDebugInfo(bool isStatic)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // <FS:Beq/> improve instrumentation
#if LL_WINDOWS && LL_BUGSPLAT
// <FS:Beq> Improve Bugsplat tracking by using attributes for certain static data items.
const LLSD& info = getViewerInfo();
@ -3836,6 +3841,7 @@ void LLAppViewer::writeDebugInfo(bool isStatic)
LLSD LLAppViewer::getViewerInfo() const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // <FS:Beq/> improve instrumentation
// The point of having one method build an LLSD info block and the other
// construct the user-visible About string is to ensure that the same info
// is available to a getInfo() caller as to the user opening
@ -4148,6 +4154,7 @@ LLSD LLAppViewer::getViewerInfo() const
// info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo();
if (auto cache = LLDiskCache::getInstance(); cache)
{
LL_PROFILE_ZONE_NAMED("gvi-getCacheInfo"); // <FS:Beq/> improve instrumentation
info["DISK_CACHE_INFO"] = cache->getCacheInfo();
}
// </FS:Beq>
@ -6003,7 +6010,7 @@ void LLAppViewer::idle()
// objects and camera should be in sync, do LOD calculations now
{
LL_RECORD_BLOCK_TIME(FTM_LOD_UPDATE);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Added a max time limit to the object list updates as these updates do affect the texture system
//gObjectList.updateApparentAngles(gAgent);
F32 max_update_apparent_angles = 0.025f * gFrameIntervalSeconds.value(); // 20 ms/second decode time

View File

@ -644,11 +644,13 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// <FS:Beq> Use the Attributes API on Windows to enhance crash metadata
void LLAppViewerWin32::bugsplatAddStaticAttributes(const LLSD& info)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING;
#ifdef LL_BUGSPLAT
auto& bugSplatMap = BugSplatAttributes::instance();
static bool write_once_after_startup = false;
if (!write_once_after_startup )
{
LL_PROFILE_ZONE_NAMED("bs-st-att-once")
// Only write the attributes that are fixed once after we've started.
// note we might update them more than once and some/many may be empty during startup as we want to catch early crashes
// once we're started we can assume they don't change for this run.
@ -689,7 +691,7 @@ void LLAppViewerWin32::bugsplatAddStaticAttributes(const LLSD& info)
#if LL_DARWIN
bugSplatMap.setAttribute("HiDPI", info["HIDPI"].asBoolean() ? "Enabled" : "Disabled");
#endif
bugSplatMap.setAttribute("Max Texture Size", gSavedSettings.getString("RenderMaxTextureResolution"));
bugSplatMap.setAttribute("Max Texture Size", gSavedSettings.getU32("RenderMaxTextureResolution"));
}
// These attributes are potentially dynamic
@ -1034,6 +1036,29 @@ bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
return false;
}
bool LLAppViewerWin32::initWindow()
{
// This is a workaround/hotfix for a change in Windows 11 24H2 (and possibly later)
// Where the window width and height need to correctly reflect an available FullScreen size
if (gSavedSettings.getBOOL("FullScreen"))
{
DEVMODE dev_mode;
::ZeroMemory(&dev_mode, sizeof(DEVMODE));
dev_mode.dmSize = sizeof(DEVMODE);
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
{
gSavedSettings.setU32("WindowWidth", dev_mode.dmPelsWidth);
gSavedSettings.setU32("WindowHeight", dev_mode.dmPelsHeight);
}
else
{
LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL;
}
}
return LLAppViewer::initWindow();
}
void LLAppViewerWin32::initLoggingAndGetLastDuration()
{
LLAppViewer::initLoggingAndGetLastDuration();

View File

@ -46,6 +46,7 @@ public:
bool reportCrashToBugsplat(void* pExcepInfo) override;
protected:
bool initWindow() override; // Override to initialize the viewer's window.
void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info.
void initConsole() override; // Initialize OS level debugging console.
bool initHardwareTest() override; // Win32 uses DX9 to test hardware.

View File

@ -516,7 +516,7 @@ void LLAvatarTracker::idleNotifyObservers()
void LLAvatarTracker::notifyObservers()
{
if (mIsNotifyObservers || (LLStartUp::getStartupState() <= STATE_INVENTORY_CALLBACKS))
if (mIsNotifyObservers || (LLStartUp::getStartupState() <= STATE_INVENTORY_SEND2))
{
// Don't allow multiple calls.
// new masks and ids will be processed later from idle.
@ -835,7 +835,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
mModifyMask |= LLFriendObserver::ONLINE;
instance().notifyObservers();
// Skip if we had received the friends list before the inventory callbacks were properly initialized
if (LLStartUp::getStartupState() > STATE_INVENTORY_CALLBACKS)
if (LLStartUp::getStartupState() > STATE_INVENTORY_SEND2)
{
gInventory.notifyObservers();
}

View File

@ -75,7 +75,7 @@ static LLStaticHashedString sColorIn("color_in");
bool LLFace::sSafeRenderSelect = true; // false
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Moved to allow more code to access these values
const S8 FACE_IMPORTANCE_LEVEL = 4 ;
const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL][2] = //{distance, importance_weight}
@ -180,7 +180,7 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
mFaceColor = LLColor4(1,0,0,1);
mImportanceToCamera = 1.f ;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
mCloseToCamera = 1.0f;
// </FS:minerjr> [FIRE-35081]
mBoundingSphereRadius = 0.0f ;
@ -1683,7 +1683,7 @@ bool LLFace::getGeometryVolume(const LLVolume& volume,
xforms = XFORM_NONE;
}
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Removed check for turning off animations
//if (getVirtualSize() >= MIN_TEX_ANIM_SIZE) // || isState(LLFace::RIGGED))
// </FS:minerjr> [FIRE-35081]
@ -2287,7 +2287,7 @@ F32 LLFace::getTextureVirtualSize()
F32 radius;
F32 cos_angle_to_view_dir;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//bool in_frustum = calcPixelArea(cos_angle_to_view_dir, radius);
// The mInFrustum value is now updated in calcPixelArea, so no longer need to accss the value
calcPixelArea(cos_angle_to_view_dir, radius);
@ -2325,7 +2325,7 @@ F32 LLFace::getTextureVirtualSize()
}
face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Remove the face area being affected by being partial off screen as close to screen textures can then become scaled down along with
// animated textures.
/*
@ -2423,7 +2423,7 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
// no rigged extents, zero out bounding box and skip update
mRiggedExtents[0] = mRiggedExtents[1] = LLVector4a(0.f, 0.f, 0.f);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Set the face to be out of the frustum as the object is invalid
mInFrustum = false;
// </FS:minerjr> [FIRE-35081]
@ -2473,7 +2473,7 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
LLVector4a x_axis;
x_axis.load3(camera->getXAxis().mV);
cos_angle_to_view_dir = lookAt.dot3(x_axis).getF32();
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Added close to camera (based upon the mImportanceToCamera) where any object that is within the FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE (16.1f)
// gets an extra texture scaling up.
// Use positive distance to the camera and apply the multiplier based upon the texture scaled for increase in the default draw distance
@ -2488,7 +2488,7 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
if(!camera->AABBInFrustum(center, size))
{
mImportanceToCamera = 0.f ;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Added real in frustum check value. Previous was only false for media textures off screen and invalid rig objects
mInFrustum = false;
// </FS:minerjr> [FIRE-35081]
@ -2514,7 +2514,7 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
{
cos_angle_to_view_dir = 1.0f ;
mImportanceToCamera = 1.0f ;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
mInFrustum = true; // If the face is important to the camera, it is in the frustum
mCloseToCamera = 1.0f;
// </FS:minerjr> [FIRE-35081]
@ -2561,7 +2561,7 @@ F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE;
F32 importance = 0.f ;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Move camera out to use for the inital check for the distance to the face importance with the multiplier
LLViewerCamera* camera = LLViewerCamera::getInstance();
@ -2581,7 +2581,7 @@ F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
}
S32 i = 0 ;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Added draw distance multiplier to the distance
for(i = 0; i < FACE_IMPORTANCE_LEVEL && dist > FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][0] * camera->getDrawDistanceMultiplier(); ++i);
// </FS:minerjr> [FIRE-35081]

View File

@ -52,7 +52,7 @@ class LLDrawInfo;
class LLMeshSkinInfo;
const F32 MIN_ALPHA_SIZE = 1024.f;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//const F32 MIN_TEX_ANIM_SIZE = 512.f;
// Change the min size to
const F32 MIN_TEX_ANIM_SIZE = 10.f;

View File

@ -217,7 +217,7 @@ void LLFloaterBvhPreview::setAnimCallbacks()
getChild<LLUICtrl>("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1));
}
std::map <std::string, std::string> LLFloaterBvhPreview::getJointAliases()
std::map <std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()
{
LLPointer<LLVOAvatar> av = (LLVOAvatar*)mAnimPreview->getDummyAvatar();
return av->getJointAliases();
@ -337,7 +337,7 @@ bool LLFloaterBvhPreview::loadBVH()
ELoadStatus load_status = E_ST_OK;
S32 line_number = 0;
std::map<std::string, std::string> joint_alias_map = getJointAliases();
auto joint_alias_map = getJointAliases();
loaderp = new LLBVHLoader(file_buffer, load_status, line_number, joint_alias_map);
std::string status = getString(STATUS[load_status]);

View File

@ -123,7 +123,7 @@ public:
// </FS:Sei>
private:
void setAnimCallbacks() ;
std::map <std::string, std::string> getJointAliases();
std::map <std::string, std::string, std::less<>> getJointAliases();
// <FS> Reload animation from disk
bool loadBVH();

View File

@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llfloatersnapshot.h" // <FS:Beq/> Fix share to flickr preview again
#include "llfloaterflickr.h"
#include "llagent.h"
@ -243,7 +244,16 @@ void LLFlickrPhotoPanel::draw()
// Draw the rest of the panel on top of it
LLPanel::draw();
}
// <FS:Beq> FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon
//static
void LLFloaterFlickr::update()
{
if (LLFloaterReg::instanceVisible("flickr"))
{
LLFloaterSnapshotBase::ImplBase::updatePreviewList( true, true );
}
}
// </FS:Beq>
LLSnapshotLivePreview* LLFlickrPhotoPanel::getPreviewView()
{
LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();

View File

@ -136,6 +136,7 @@ public:
void onOpen(const LLSD& key);
LLSnapshotLivePreview* getPreviewView(); // <FS:Beq/> Required for snapshot frame rendering
static void update(); // <FS:Beq/> FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon
private:
LLFlickrPhotoPanel* mFlickrPhotoPanel;
LLTextBox* mStatusErrorText;

View File

@ -96,6 +96,13 @@ public:
{
ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime, this));
}
// <FS:darl> [FIRE-35039 > FIRE-35294] Add flag to show/hide the on-screen console
ctrl = gSavedSettings.getControl("FSShowOnscreenConsole").get();
if (ctrl)
{
ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::removeToastsFromChannel, this));
}
// </FS:darl> [FIRE-35039 > FIRE-35294] Add flag to show/hide the on-screen console
}
void addChat (LLSD& chat);
@ -663,7 +670,14 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
}
}
// <FS:Beq> Hide Primfeed OAuth message from chat to prevent accidental leak of secret.
const std::string primfeed_oauth = "#PRIMFEED_OAUTH: ";
if( chat_msg.mText.compare(0, primfeed_oauth.length(), primfeed_oauth) == 0 && chat_msg.mChatType == CHAT_TYPE_IM && chat_msg.mSourceType == CHAT_SOURCE_OBJECT )
{
// Don't show the message in chat.
return;
}
// </FS:Beq>
nearby_chat->addMessage(chat_msg, true, args);
if (chat_msg.mSourceType == CHAT_SOURCE_AGENT
@ -694,6 +708,14 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
}
// </FS:Ansariel>
// <FS:darl> [FIRE-35039 > FIRE-35294] Add flag to show/hide the on-screen console
static LLUICachedControl<bool> showOnscreenConsole("FSShowOnscreenConsole");
if (!showOnscreenConsole)
{
return;
}
// </FS:darl> [FIRE-35039 > FIRE-35294]
static LLCachedControl<bool> useChatBubbles(gSavedSettings, "UseChatBubbles");
static LLCachedControl<bool> fsBubblesHideConsoleAndToasts(gSavedSettings, "FSBubblesHideConsoleAndToasts");
// <FS:Ansariel> [FS communication UI]

View File

@ -1625,14 +1625,9 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
for (U32 j = 0; j < joint_count; ++j)
{
const LLVector3& joint_pos = LLVector3(skin->mAlternateBindMatrix[j].getTranslation());
// <FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
//LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j], mModelPreview);
LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j].mName];
LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j].mName, mModelPreview);
// <FS:ND>
LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j], mModelPreview);
if (pJoint)
{
// see how voavatar uses aboveJointPosThreshold
@ -1661,9 +1656,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
{
for (U32 j = 0; j < joint_count; ++j)
{
// <FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j].mName];
LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]];
data.mModelsNoOverrides.insert(model->getName());
}
}
@ -1678,7 +1671,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
{
// Populate table
std::map<std::string, std::string> joint_alias_map;
std::map<std::string, std::string, std::less<>> joint_alias_map;
mModelPreview->getJointAliases(joint_alias_map);
S32 conflicts = 0;

View File

@ -45,10 +45,27 @@ bool LLFloaterNewFeatureNotification::postBuild()
const std::string title_txt = "title_txt";
const std::string dsc_txt = "description_txt";
std::string feature = "_" + getKey().asString();
getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature));
getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature));
// <FS:Beq> FIRE-35393 stop crashing just cos whirly does something daft and blames Atlas for it
std::string feature = getKey().asString();
if(feature.empty() )
{
LL_WARNS("FloaterNewFeature") << "Unexpected failure - No feature name NewFeatureNotification." << LL_ENDL;
return false;
}
if (!hasString( title_txt + "_" + feature ) )
{
LL_WARNS("FloaterNewFeature") << "No string for " << title_txt + "_" + feature << LL_ENDL;
return false;
}
if (!hasString( dsc_txt + "_" + feature ) )
{
LL_WARNS("FloaterNewFeature") << "No string for " << dsc_txt + "_" + feature << LL_ENDL;
return false;
}
// </FS:Beq>
getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + "_" + feature));
getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + "_" + feature));
if (getKey().asString() == "gltf")
{

View File

@ -30,6 +30,7 @@
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // <FS:Ansariel> Share to Flickr
#include "fsfloaterprimfeed.h" // <FS:Beq> Share to Primfeed
#include "llimagefiltersmanager.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
@ -1485,12 +1486,12 @@ bool LLFloaterSnapshot::isWaitingState()
// <FS:Beq> FIRE-35002 - Post to flickr broken, improved solution
// bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized)
bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized, bool have_flickr)
bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized, bool have_socials)
// </FS:Beq>
{
// <FS:Ansariel> Share to Flickr
//if (!initialized)
if (!initialized && !have_flickr)
if (!initialized && !have_socials)
// </FS:Ansariel>
return false;
@ -1509,16 +1510,18 @@ void LLFloaterSnapshotBase::ImplBase::updateLivePreview()
{
// don't update preview for hidden floater
// <FS:Beq> FIRE-35002 - Post to flickr broken
LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
auto have_flickr = floater_flickr != nullptr;
bool have_socials = (
LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr") != nullptr ||
LLFloaterReg::findTypedInstance<FSFloaterPrimfeed>("primfeed") != nullptr
);
if ( ((mFloater && mFloater->isInVisibleChain()) ||
have_flickr) &&
ImplBase::updatePreviewList(true, have_flickr))
have_socials) &&
ImplBase::updatePreviewList(true, have_socials))
// </FS:Beq>
{
LL_DEBUGS() << "changed" << LL_ENDL;
updateControls(mFloater);
}
}
}
//static

View File

@ -123,7 +123,7 @@ public:
virtual EStatus getStatus() const { return mStatus; }
virtual void setNeedRefresh(bool need);
static bool updatePreviewList(bool initialized, bool have_flickr = false); // <FS:Beq/> FIRE-35002 - Post to flickr broken, improved solution
static bool updatePreviewList(bool initialized, bool have_socials = false); // <FS:Beq/> FIRE-35002 - Post to flickr broken, improved solution
void setAdvanced(bool advanced) { mAdvanced = advanced; }
void setSkipReshaping(bool skip) { mSkipReshaping = skip; }

View File

@ -433,10 +433,13 @@ bool LLFloaterWorldMap::postBuild()
F32 slider_zoom = mMapView->getZoom();
mZoomSlider->setValue(slider_zoom);
mTrackCtrlsPanel = getChild<LLPanel>("layout_panel_4");
mSearchButton = getChild<LLButton>("DoSearch");
// <FS:Ansariel> Use own expand/collapse function
//getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));
setDefaultBtn(NULL);
mTrackCtrlsPanel->setDefaultBtn(nullptr);
onChangeMaturity();
@ -784,7 +787,7 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string&
{
LLTracker::stopTracking(false);
}
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
@ -829,7 +832,7 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
{
LLTracker::stopTracking(false);
}
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
@ -838,7 +841,7 @@ void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
mShowParcelInfo = false;
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT);
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
@ -846,7 +849,7 @@ void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
mShowParcelInfo = false;
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM);
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
@ -860,7 +863,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
S32 world_x = S32(pos_global.mdV[0] / 256);
S32 world_y = S32(pos_global.mdV[1] / 256);
LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
setDefaultBtn("");
mTrackCtrlsPanel->setDefaultBtn(nullptr);
// clicked on a non-region - turn off coord display
enableTeleportCoordsDisplay( false );
@ -874,7 +877,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
LLTracker::stopTracking(false);
LLWorldMap::getInstance()->setTracking(pos_global);
LLWorldMap::getInstance()->setTrackingInvalid();
setDefaultBtn("");
mTrackCtrlsPanel->setDefaultBtn(nullptr);
// clicked on a down region - turn off coord display
enableTeleportCoordsDisplay( false );
@ -925,7 +928,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
// we have a valid region - turn on coord display
enableTeleportCoordsDisplay( true );
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
// enable/disable teleport destination coordinates
@ -1085,9 +1088,9 @@ void LLFloaterWorldMap::updateLocation()
// mSLURL = LLSLURL(sim_name, pos_global);
// <FS> [FIRE-35268] OpenSim support for when on other grids
if (LLGridManager::getInstance()->isInSecondLife())
mSLURL = LLSLURL(sim_info->getName(), gAgent.getPositionAgent());
mSLURL = LLSLURL(sim_info->getName(), sim_info->getGlobalOrigin(), pos_global);
else
mSLURL = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), sim_info->getName(), gAgent.getPositionAgent());
mSLURL = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), sim_info->getName(), sim_info->getGlobalOrigin(), pos_global);
// </FS>
}
// </FS:Beq pp Oren>
@ -1131,7 +1134,7 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3
local_pos.mV[VZ] = (F32)z_coord;
LLVector3d global_pos = sim_info->getGlobalPos(local_pos);
trackLocation(global_pos);
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
else
{
@ -1569,11 +1572,11 @@ void LLFloaterWorldMap::updateSearchEnabled()
if (childHasKeyboardFocus("location") &&
mLocationEditor->getValue().asString().length() > 0)
{
setDefaultBtn("DoSearch");
mTrackCtrlsPanel->setDefaultBtn(mSearchButton);
}
else
{
setDefaultBtn(NULL);
mTrackCtrlsPanel->setDefaultBtn(nullptr);
}
}
@ -2033,7 +2036,7 @@ void LLFloaterWorldMap::onCommitSearchResult()
mLocationEditor->setValue(sim_name);
trackLocation(pos_global);
setDefaultBtn("Teleport");
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
break;
}
}

View File

@ -232,6 +232,7 @@ private:
LLButton* mShowDestinationButton = nullptr;
LLButton* mCopySlurlButton = nullptr;
LLButton* mGoHomeButton = nullptr;
LLButton* mSearchButton = nullptr;
LLCheckBoxCtrl* mPeopleCheck = nullptr;
LLCheckBoxCtrl* mInfohubCheck = nullptr;
@ -258,6 +259,8 @@ private:
LLSliderCtrl* mZoomSlider = nullptr;
LLPanel* mTrackCtrlsPanel = nullptr;
boost::signals2::connection mTeleportFinishConnection;
};

View File

@ -3914,16 +3914,55 @@ LLUUID LLIMMgr::addSession(
//works only for outgoing ad-hoc sessions
if (new_session &&
((IM_NOTHING_SPECIAL == dialog) || (IM_SESSION_P2P_INVITE == dialog) || (IM_SESSION_CONFERENCE_START == dialog)) &&
ids.size())
// <AS:chanayane> [FIRE-34494] fix unable to open an IM with someone who started a group chat
//ids.size())
!ids.empty())
// </AS:chanayane>
{
session = LLIMModel::getInstance()->findAdHocIMSession(ids);
if (session)
{
new_session = false;
session_id = session->mSessionID;
// <AS:chanayane> [FIRE-34494] fix unable to open an IM with someone who started a group chat
// new_session = false;
// session_id = session->mSessionID;
// Protect against wrong session type reuse (e.g., conference reused for IM)
if (session->mType != dialog)
{
LL_WARNS("IMVIEW") << "Discarding mismatched session type reuse: expected "
<< dialog << " but found " << session->mType
<< " for session " << session->mSessionID
<< ". This may indicate improper reuse of a session object." << LL_ENDL;
session = nullptr;
new_session = true;
session_id = computeSessionID(dialog, other_participant_id);
}
else
{
new_session = false;
session_id = session->mSessionID;
}
}
}
if (session && session->mType != dialog)
{
// Prevent reusing a session of the wrong type
session = nullptr;
new_session = true;
// Recompute session ID depending on dialog type
if (dialog == IM_SESSION_CONFERENCE_START)
{
session_id.generate();
}
else
{
session_id = computeSessionID(dialog, other_participant_id);
}
// </AS:chanayane>
}
//Notify observers that a session was added
if (new_session)
{

View File

@ -346,6 +346,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
LL_DEBUGS("LLLogin") << "reason " << reason_response
<< " message " << message_response
<< LL_ENDL;
if (response.has("mfa_hash"))
{
mRequestData["params"]["mfa_hash"] = response["mfa_hash"];
mRequestData["params"]["token"] = "";
saveMFAHash(response);
}
// For the cases of critical message or TOS agreement,
// start the TOS dialog. The dialog response will be handled
// by the LLLoginInstance::handleTOSResponse() callback.
@ -609,6 +618,24 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon
return true;
}
void LLLoginInstance::saveMFAHash(LLSD const& response)
{
std::string grid(LLGridManager::getInstance()->getGridId());
std::string user_id(LLStartUp::getUserId());
// Only save mfa_hash for future logins if the user wants their info remembered.
if (response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && LLLoginInstance::getInstance()->saveMFA())
{
gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, response["mfa_hash"]);
}
else if (!LLLoginInstance::getInstance()->saveMFA())
{
gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
}
// TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
gSecAPIHandler->syncProtectedMap();
}
std::string construct_start_string()
{
std::string start;

View File

@ -73,6 +73,8 @@ public:
void setNotificationsInterface(LLNotificationsInterface* ni) { mNotifications = ni; }
LLNotificationsInterface& getNotificationsInterface() const { return *mNotifications; }
void saveMFAHash(LLSD const& response);
private:
typedef std::shared_ptr<LLEventAPI::Response> ResponsePtr;
void constructAuthParams(LLPointer<LLCredential> user_credentials);

View File

@ -1909,42 +1909,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
//static
void LLMeshRepoThread::incActiveLODRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
++LLMeshRepoThread::sActiveLODRequests;
}
//static
void LLMeshRepoThread::decActiveLODRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
--LLMeshRepoThread::sActiveLODRequests;
}
//static
void LLMeshRepoThread::incActiveHeaderRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
++LLMeshRepoThread::sActiveHeaderRequests;
}
//static
void LLMeshRepoThread::decActiveHeaderRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
--LLMeshRepoThread::sActiveHeaderRequests;
}
//static
void LLMeshRepoThread::incActiveSkinRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
++LLMeshRepoThread::sActiveSkinRequests;
}
//static
void LLMeshRepoThread::decActiveSkinRequests()
{
LLMutexLock lock(gMeshRepo.mThread->mMutex);
--LLMeshRepoThread::sActiveSkinRequests;
}
@ -4665,13 +4659,20 @@ void LLMeshRepository::notifyLoadedMeshes()
{
LLMutexTrylock lock1(mMeshMutex);
LLMutexTrylock lock2(mThread->mMutex);
LLMutexTrylock lock3(mThread->mHeaderMutex);
LLMutexTrylock lock4(mThread->mPendingMutex);
static U32 hold_offs(0);
if (! lock1.isLocked() || ! lock2.isLocked())
if (! lock1.isLocked() || ! lock2.isLocked() || ! lock3.isLocked() || ! lock4.isLocked())
{
// If we can't get the locks, skip and pick this up later.
// Eventually thread queue will be free enough
++hold_offs;
sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs);
if (hold_offs > 4)
{
LL_WARNS_ONCE() << "High mesh thread holdoff" << LL_ENDL;
}
return;
}
hold_offs = 0;
@ -4732,6 +4733,7 @@ void LLMeshRepository::notifyLoadedMeshes()
if (mPendingRequests.size() > push_count)
{
LL_PROFILE_ZONE_NAMED("Mesh score_map");
// More requests than the high-water limit allows so
// sort and forward the most important.
@ -4782,8 +4784,6 @@ void LLMeshRepository::notifyLoadedMeshes()
std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,
mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());
}
LLMutexTrylock lock3(mThread->mHeaderMutex);
LLMutexTrylock lock4(mThread->mPendingMutex);
while (!mPendingRequests.empty() && push_count > 0)
{
std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
@ -6238,13 +6238,20 @@ bool LLMeshRepository::meshUploadEnabled()
bool LLMeshRepository::meshRezEnabled()
{
static LLCachedControl<bool> mesh_enabled(gSavedSettings, "MeshEnabled");
LLViewerRegion *region = gAgent.getRegion();
if(mesh_enabled &&
region)
// <FS:Beq> FIRE-35602 etc - Mesh not appearing after TP/login (opensim only)
// For OpenSim there is still an outside chance that mesh rezzing is disabled on the sim/region
// restore the old behaviour but keep the bias to mesh_enabled == true in the underlying checks.
#ifdef OPENSIM
if (LLGridManager::instance().isInOpenSim())
{
return region->meshRezEnabled();
if (LLViewerRegion* region = gAgent.getRegion(); mesh_enabled && region)
{
return region->meshRezEnabled();
}
}
return false;
#endif // OPENSIM
// </FS:Beq>
return mesh_enabled;
}
// Threading: main thread only

View File

@ -1020,7 +1020,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
clearGLODGroup();
}
// </FS:Beq>
std::map<std::string, std::string> joint_alias_map;
std::map<std::string, std::string, std::less<>> joint_alias_map;
getJointAliases(joint_alias_map);
LLHandle<LLModelPreview> preview_handle = getHandle();
@ -4185,10 +4185,7 @@ LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque)
LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque);
if (pPreview)
{
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// return pPreview->getPreviewAvatar()->getJoint(str);
return pPreview->getPreviewAvatar()->getJoint( JointKey::construct( str ) );
// <FS:ND>
return pPreview->getPreviewAvatar()->getJoint(str);
}
return NULL;
}

View File

@ -131,10 +131,7 @@ void LLMorphView::updateCamera()
{
if (!mCameraTargetJoint)
{
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
// setCameraTargetJoint( gAgentAvatarp->getJoint( "mHead" ) );
setCameraTargetJoint( gAgentAvatarp->getJoint( JointKey::construct( "mHead" ) ) );
// </FS:ND>
setCameraTargetJoint( gAgentAvatarp->getJoint( "mHead" ) );
}
if (!isAgentAvatarValid()) return;

View File

@ -1316,15 +1316,11 @@ void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, bool show, bo
value_map_t sorted_params;
getSortedParams(sorted_params, edit_group);
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//LLJoint* jointp = gAgentAvatarp->getJoint( subpart_entry->mTargetJoint );
LLJoint* jointp = gAgentAvatarp->getJoint( JointKey::construct( subpart_entry->mTargetJoint ) );
LLJoint* jointp = gAgentAvatarp->getJoint( subpart_entry->mTargetJoint );
if (!jointp)
{
//jointp = gAgentAvatarp->getJoint( "mHead" );
jointp = gAgentAvatarp->getJoint( JointKey::construct( "mHead" ) );
jointp = gAgentAvatarp->getJoint( "mHead" );
}
// </FS:ND>
buildParamList(panel_list, sorted_params, tab, jointp);
@ -1440,11 +1436,7 @@ void LLPanelEditWearable::changeCamera(U8 subpart)
}
// Update the camera
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//gMorphView->setCameraTargetJoint( gAgentAvatarp->getJoint( subpart_entry->mTargetJoint ) );
gMorphView->setCameraTargetJoint( gAgentAvatarp->getJoint( JointKey::construct( subpart_entry->mTargetJoint ) ) );
// </FS>ND>
gMorphView->setCameraTargetJoint( gAgentAvatarp->getJoint( subpart_entry->mTargetJoint ) );
gMorphView->setCameraTargetOffset( subpart_entry->mTargetOffset );
gMorphView->setCameraOffset( subpart_entry->mCameraOffset );
if (gSavedSettings.getBOOL("AppearanceCameraMovement"))

View File

@ -5540,6 +5540,11 @@ void LLPanelFace::changePrecision(S32 decimal_precision)
mBumpyRotate->setPrecision(decimal_precision);
mShinyRotate->setPrecision(decimal_precision);
mTexRepeat->setPrecision(decimal_precision);
mPBRScaleU->setPrecision(decimal_precision);
mPBRScaleV->setPrecision(decimal_precision);
mPBRRotate->setPrecision(decimal_precision);
mPBROffsetU->setPrecision(decimal_precision);
mPBROffsetV->setPrecision(decimal_precision);
}
// </FS:CR>

View File

@ -116,6 +116,9 @@ bool LLPanelOutfitsInventory::postBuild()
getChild<LLButton>(SAVE_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, false));
getChild<LLButton>(SAVE_AS_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, true));
// <FS:Ansariel> FIRE-17626: Attachment count in appearance floater
mTempAttachmentUpdateTimer.start();
return true;
}
@ -261,6 +264,20 @@ void LLPanelOutfitsInventory::onSave()
}
// <FS:Ansariel> FIRE-17626: Attachment count in appearance floater
void LLPanelOutfitsInventory::draw()
{
if (mTempAttachmentUpdateTimer.checkExpirationAndReset(1.f))
{
if (U32 tempAttachmentCount = (U32)LLAgentWearables::getTempAttachments().size(); tempAttachmentCount != mCurrentTempAttachmentCount)
{
mCurrentTempAttachmentCount = tempAttachmentCount;
onCOFChanged();
}
}
LLPanel::draw();
}
void LLPanelOutfitsInventory::onCOFChanged()
{
if (!isAgentAvatarValid())
@ -273,7 +290,7 @@ void LLPanelOutfitsInventory::onCOFChanged()
LLInventoryModel::cat_array_t cats;
LLIsType is_of_type(LLAssetType::AT_OBJECT);
gInventory.collectDescendentsIf(cof, cats, obj_items, LLInventoryModel::EXCLUDE_TRASH, is_of_type);
U32 attachments = static_cast<U32>(obj_items.size());
U32 attachments = static_cast<U32>(obj_items.size()) + mCurrentTempAttachmentCount;
LLStringUtil::format_map_t args;
args["COUNT"] = llformat("%d", attachments);

View File

@ -52,6 +52,8 @@ public:
/*virtual*/ bool postBuild();
/*virtual*/ void onOpen(const LLSD& key);
void draw(); // <FS:Ansariel> FIRE-17626: Attachment count in appearance floater
void onSearchEdit(const std::string& string);
void onSave();
void saveOutfit(bool as_new = false);
@ -91,6 +93,10 @@ private:
// <FS:Ansariel> FIRE-17626: Attachment count in appearance floater
LLInventoryCategoriesObserver* mCategoriesObserver;
void onCOFChanged();
U32 mCurrentTempAttachmentCount{ 0 };
LLFrameTimer mTempAttachmentUpdateTimer;
// </FS:Ansariel>
//////////////////////////////////////////////////////////////////////////////////

View File

@ -32,6 +32,7 @@
#include "llfloatersnapshot.h" // FIXME: create a snapshot model
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // <FS:Ansariel> Share to Flickr
#include "fsfloaterprimfeed.h" // <FS:Beq> Share to Primfeed
/**
* Provides several ways to save a snapshot.
@ -52,6 +53,7 @@ private:
void onSaveToInventory();
void onSaveToComputer();
void onSendToFlickr(); // <FS:Ansariel> Share to Flickr
void onSendToPrimfeed(); // <FS:Beq/> Share to Primfeed
LLFloaterSnapshotBase* mSnapshotFloater;
};
@ -65,6 +67,7 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions()
mCommitCallbackRegistrar.add("Snapshot.SaveToInventory", boost::bind(&LLPanelSnapshotOptions::onSaveToInventory, this));
mCommitCallbackRegistrar.add("Snapshot.SaveToComputer", boost::bind(&LLPanelSnapshotOptions::onSaveToComputer, this));
mCommitCallbackRegistrar.add("Snapshot.SendToFlickr", boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this)); // <FS:Ansariel> Share to Flickr
mCommitCallbackRegistrar.add("Snapshot.SendToPrimfeed", boost::bind(&LLPanelSnapshotOptions::onSendToPrimfeed, this)); // <FS:Beq/> Share to Primfeed
}
// virtual
@ -113,11 +116,23 @@ void LLPanelSnapshotOptions::onSendToFlickr()
{
LLFloaterReg::hideInstance("snapshot");
LLFloaterFlickr* flickr_floater = dynamic_cast<LLFloaterFlickr*>(LLFloaterReg::getInstance("flickr"));
if (flickr_floater)
if (auto flickr_floater = LLFloaterReg::getTypedInstance<LLFloaterFlickr>("flickr"))
{
flickr_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("flickr");
}
// </FS:Ansariel>
// <FS:Beq> Share to Primfeed
void LLPanelSnapshotOptions::onSendToPrimfeed()
{
LLFloaterReg::hideInstance("snapshot");
if (auto primfeed_floater = LLFloaterReg::getTypedInstance<FSFloaterPrimfeed>("primfeed"))
{
primfeed_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("primfeed");
}
// </FS:Beq>

View File

@ -538,17 +538,22 @@ void LLPresetsManager::loadPreset(const std::string& subdirectory, std::string n
{
gSavedSettings.setString("PresetGraphicActive", name);
// <FS> [FIRE-35390] Old viewer presets have these as true and 0.7, whereas the equivalent on modern viewers is false and 1.0
if (auto control = gSavedSettings.getControl("RenderSkyAutoAdjustLegacy"))
control->resetToDefault(true);
if (auto control = gSavedSettings.getControl("RenderSkyAmbientScale"))
control->resetToDefault(true);
// </FS>
// <FS:Ansariel> Update indirect controls
LLAvatarComplexityControls::setIndirectControls();
LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences");
if (instance)
if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"))
{
instance->refreshEnabledGraphics();
}
// <FS:Ansariel> Graphic preset controls independent from XUI
FloaterQuickPrefs* phototools = LLFloaterReg::findTypedInstance<FloaterQuickPrefs>(PHOTOTOOLS_FLOATER);
if (phototools)
if (FloaterQuickPrefs* phototools = LLFloaterReg::findTypedInstance<FloaterQuickPrefs>(PHOTOTOOLS_FLOATER))
{
phototools->refreshSettings();
}

View File

@ -7970,19 +7970,20 @@ bool LLSelectMgr::canSelectObject(LLViewerObject* object, bool ignore_select_own
// only select my own objects
return false;
}
// <FS:Ansariel> FIRE-14593: Option to select only copyable objects
if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly"))
{
return false;
}
// </FS:Ansariel>
// <FS:Ansariel> FIRE-17696: Option to select only locked objects
if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced())
{
return false;
}
// </FS:Ansariel>// Can't select objects that are not owned by you or group
}
// <FS:Ansariel> FIRE-14593: Option to select only copyable objects
if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly"))
{
return false;
}
// </FS:Ansariel>
// <FS:Ansariel> FIRE-17696: Option to select only locked objects
if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced())
{
return false;
}
// </FS:Ansariel>
// Can't select orphans
if (object->isOrphaned()) return false;

View File

@ -54,10 +54,7 @@ void dump_avatar_and_skin_state(const std::string& reason, LLVOAvatar *avatar, c
{
LL_WARNS("Avatar") << "skin joint idx " << j << " name [" << skin->mJointNames[j]
<< "] num " << skin->mJointNums[j] << LL_ENDL;
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//const std::string& name = skin->mJointNames[j];
const std::string& name = skin->mJointNames[j].mName;
// </FS:ND>
const std::string& name = skin->mJointNames[j];
S32 joint_num = skin->mJointNums[j];
LLJoint *name_joint = avatar->getJoint(name);
@ -119,14 +116,9 @@ void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin
// needed for handling of any legacy bad data.
if (!avatar->getJoint(skin->mJointNames[j]))
{
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
//LL_DEBUGS("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL;
//LL_WARNS_ONCE("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL;
//skin->mJointNames[j] = "mPelvis";
LL_DEBUGS("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint " << skin->mJointNames[j].mName << LL_ENDL;
LL_WARNS_ONCE("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint" << skin->mJointNames[j].mName << LL_ENDL;
skin->mJointNames[j] = JointKey::construct("mPelvis");
//</FS:ND>
LL_DEBUGS("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL;
LL_WARNS_ONCE("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL;
skin->mJointNames[j] = "mPelvis";
skin->mJointNumsInitialized = false; // force update after names change.
}
}

View File

@ -36,6 +36,7 @@
#include "llfloaterperms.h"
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // <FS:Ansariel> Share to Flickr
#include "fsfloaterprimfeed.h" // <FS:Beq> Share to Primfeed
#include "llimagefilter.h"
#include "llimagefiltersmanager.h"
#include "llimagebmp.h"

View File

@ -1950,9 +1950,9 @@ bool idle_startup()
// <FS:Ansariel> Wait for notification confirmation
if (STATE_LOGIN_CONFIRM_NOTIFICATON == LLStartUp::getStartupState())
{
display_startup();
do_startup_frame();
gViewerWindow->getProgressView()->setVisible(false);
display_startup();
do_startup_frame();
ms_sleep(1);
return false;
}
@ -2298,10 +2298,10 @@ bool idle_startup()
//so I just moved nearby history loading a few states further
if (gSavedPerAccountSettings.getBOOL("LogShowHistory"))
{
FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance();
if (nearby_chat) nearby_chat->loadHistory();
if (FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance())
nearby_chat->loadHistory();
}
display_startup();
do_startup_frame();
// </FS:Ansariel> [FS communication UI]
// <FS:KC> FIRE-18250: Option to disable default eye movement
@ -2549,13 +2549,14 @@ bool idle_startup()
LL_INFOS() << "Requesting Money Balance" << LL_ENDL;
LLStatusBar::sendMoneyBalanceRequest();
do_startup_frame();
// <FS:Ansariel> Moved before inventory creation.
// request all group information
LL_INFOS("Agent_GroupData") << "GROUPDEBUG: Requesting Agent Data during startup" << LL_ENDL;
gAgent.sendAgentDataUpdateRequest();
display_startup();
// </FS:Ansariel>
do_startup_frame();
// Inform simulator of our language preference
LLAgentLanguage::update();
@ -2808,7 +2809,7 @@ bool idle_startup()
// Create the inventory views
LL_INFOS() << "Creating Inventory Views" << LL_ENDL;
LLFloaterReg::getInstance("inventory");
//do_startup_frame();
do_startup_frame();
// [RLVa:KB] - Checked: RLVa-1.1.0
if (RlvHandler::isEnabled())
@ -2898,7 +2899,7 @@ bool idle_startup()
//ok, we're done, set it back to false.
gSavedSettings.setBOOL("FSFirstRunAfterSettingsRestore", false);
}
display_startup();
do_startup_frame();
// </FS:CR>
if (gSavedSettings.getBOOL("HelpFloaterOpen"))
@ -3074,7 +3075,7 @@ bool idle_startup()
}
}
#endif // OPENSIM
display_startup();
do_startup_frame();
// </FS:CR>
LLStartUp::setStartupState( STATE_PRECACHE );
@ -4972,24 +4973,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y
LLViewerMedia::getInstance()->openIDSetup(openid_url, openid_token);
}
// Only save mfa_hash for future logins if the user wants their info remembered.
if(response.has("mfa_hash")
&& gSavedSettings.getBOOL("RememberUser")
&& LLLoginInstance::getInstance()->saveMFA())
{
std::string grid(LLGridManager::getInstance()->getGridId());
std::string user_id(gUserCredential->userID());
gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, response["mfa_hash"]);
// TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
gSecAPIHandler->syncProtectedMap();
}
else if (!LLLoginInstance::getInstance()->saveMFA())
{
std::string grid(LLGridManager::getInstance()->getGridId());
std::string user_id(gUserCredential->userID());
gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
gSecAPIHandler->syncProtectedMap();
}
LLLoginInstance::getInstance()->saveMFAHash(response);
// <FS:Ansariel> OpenSim legacy economy support
#ifdef OPENSIM
@ -5092,6 +5076,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y
}
// </FS:Techwolf Lupindo>
bool success = false;
// JC: gesture loading done below, when we have an asset system
// in place. Don't delete/clear gUserCredentials until then.

View File

@ -44,6 +44,10 @@
#include "llavatarname.h"
#include "llavatarnamecache.h"
#include "llviewernetwork.h" // <FS/> Access to GridManager
#include "lfsimfeaturehandler.h" // <FS/> Access to hyperGridURL
#include "llworldmapmessage.h" // <FS/> Access to sendNamedRegionRequest
// [RLVa:KB] - Checked: 2010-09-03 (RLVa-1.2.1b)
#include "rlvhandler.h"
// [/RLVa:KB]
@ -98,6 +102,46 @@ void LLTeleportHistory::goToItem(int idx)
return;
}
// <FS> [FIRE-35355] OpenSim global position is dependent on the Grid you are on
#ifdef OPENSIM
if (LLGridManager::getInstance()->isInOpenSim())
{
if (mItems[mCurrentItem].mRegionID != mItems[idx].mRegionID)
{
LLSLURL slurl = mItems[idx].mSLURL;
std::string grid = slurl.getGrid();
std::string current_grid = LFSimFeatureHandler::instance().hyperGridURL();
std::string gatekeeper = LLGridManager::getInstance()->getGatekeeper(grid);
// Requesting region information from the server is only required when changing grid
if (slurl.isValid() && grid != current_grid)
{
if (!gatekeeper.empty())
{
slurl = LLSLURL(gatekeeper + ":" + slurl.getRegion(), slurl.getPosition(), true);
}
if (mRequestedItem != -1)
{
return; // We already have a request in progress and don't want to spam the server
}
mRequestedItem = idx;
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(
slurl.getRegion(),
boost::bind(&LLTeleportHistory::regionNameCallback, this, idx, _1, _2, _3, _4),
slurl.getSLURLString(),
true
);
return; // The teleport will occur in the callback with the correct global position
}
}
}
#endif
// </FS>
// Attempt to teleport to the requested item.
gAgent.teleportViaLocation(mItems[idx].mGlobalPos);
mRequestedItem = idx;
@ -210,6 +254,22 @@ void LLTeleportHistory::updateCurrentLocation(const LLVector3d& new_pos)
mItems[mCurrentItem] = LLTeleportHistoryItem(RlvStrings::getString(RlvStringKeys::Hidden::Parcel), LLVector3d::zero);
}
// [/RLVa:KB]
// <FS> [FIRE-35355] OpenSim global position is dependent on the Grid you are on,
// so we need to store the slurl so we can request the global position later
#ifdef OPENSIM
if (LLGridManager::getInstance()->isInOpenSim())
{
auto regionp = gAgent.getRegion();
if (regionp)
{
LLVector3 new_pos_local = gAgent.getPosAgentFromGlobal(new_pos);
LLSLURL slurl = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), regionp->getName(), new_pos_local);
mItems[mCurrentItem].mSLURL = slurl;
}
}
#endif
// </FS>
}
//dump(); // LO - removing the dump from happening every time we TP.
@ -287,3 +347,35 @@ void LLTeleportHistory::dump() const
LL_INFOS() << line.str() << LL_ENDL;
}
}
// <FS> [FIRE-35355] Callback for OpenSim so we can teleport to the correct global position on another grid
void LLTeleportHistory::regionNameCallback(int idx, U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)
{
if (region_handle)
{
// Sanity checks again just in case since time has passed since the request was made
if (idx < 0 || idx >= (int)mItems.size())
{
LL_WARNS() << "Invalid teleport history index (" << idx << ") specified" << LL_ENDL;
return;
}
if (idx == mCurrentItem)
{
LL_WARNS() << "Will not teleport to the same location." << LL_ENDL;
return;
}
LLVector3d origin_pos = from_region_handle(region_handle);
LLVector3d global_pos(origin_pos + LLVector3d(slurl.getPosition()));
// Attempt to teleport to the target grids region
gAgent.teleportViaLocation(global_pos);
}
else
{
LL_WARNS() << "Invalid teleport history region handle" << LL_ENDL;
onTeleportFailed();
}
}
// </FS>

View File

@ -35,6 +35,8 @@
#include <boost/signals2.hpp>
#include "llteleporthistorystorage.h"
#include "llslurl.h" // <FS/> Access to LLSLURL
/**
* An item of the teleport history.
@ -47,8 +49,11 @@ public:
LLTeleportHistoryItem()
{}
LLTeleportHistoryItem(std::string title, LLVector3d global_pos)
: mTitle(title), mGlobalPos(global_pos)
// <FS> [FIRE-35355] OpenSim requires knowing the grid to teleport correctly if changing grids
//LLTeleportHistoryItem(std::string title, LLVector3d global_pos)
// : mTitle(title), mGlobalPos(global_pos)
LLTeleportHistoryItem(std::string title, LLVector3d global_pos, const LLSLURL& slurl = LLSLURL())
: mTitle(title), mGlobalPos(global_pos), mSLURL(slurl)
{}
/**
@ -61,6 +66,7 @@ public:
std::string mFullTitle; // human-readable location title including coordinates
LLVector3d mGlobalPos; // global position
LLUUID mRegionID; // region ID for getting the region info
LLSLURL mSLURL; // <FS/> [FIRE-35355] slurl for the location required for OpenSim
};
/**
@ -180,6 +186,10 @@ private:
*/
static std::string getCurrentLocationTitle(bool full, const LLVector3& local_pos_override);
// <FS> [FIRE-35355] Callback for OpenSim so we can teleport to the correct global position on another grid
void regionNameCallback(int idx, U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport);
// </FS>
/**
* Actually, the teleport history.
*/

View File

@ -2089,12 +2089,14 @@ LLPointer<LLImageRaw> LLTextureCache::readFromFastCache(const LLUUID& id, S32& d
// So a 1024x1024 texture with a dicard of 6 will become 32x32 and a 2048x2048 texture with a discard of 7 will become a 64x64 texture.
if (discardlevel > MAX_DISCARD_LEVEL)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("FixBadDiscardLevel");
S32 w = head[0]; // Get the current width from the header (16)
S32 h = head[1]; // Get the current height from the header (16)
// Expand the width and height by teh difference between the discard and MAX_DISCARD_LEVEL bit shifted to the left. (Expand power of 2 textures)
w <<= MAX_DISCARD_LEVEL - discardlevel;
h <<= MAX_DISCARD_LEVEL - discardlevel;
w <<= discardlevel - MAX_DISCARD_LEVEL;
h <<= discardlevel - MAX_DISCARD_LEVEL;
// Set the discard level to the MAX_DISCARD_LEVEL
discardlevel = MAX_DISCARD_LEVEL;

View File

@ -224,6 +224,7 @@ LLVersionInfo::FSViewerMaturity LLVersionInfo::getFSViewerMaturity() const
static const boost::regex is_alpha_channel("\\bAlpha(x64)?\\b");
static const boost::regex is_release_channel("\\bRelease(x64)?\\b");
static const boost::regex is_nightly_channel("\\bNightly(x64)?\\b");
static const boost::regex is_streaming_channel("\\bStreaming\\b");
if (ll_regex_search(channel, is_release_channel))
{
@ -245,6 +246,10 @@ LLVersionInfo::FSViewerMaturity LLVersionInfo::getFSViewerMaturity() const
{
maturity = FSViewerMaturity::NIGHTLY_VIEWER;
}
else if (ll_regex_search(channel, is_streaming_channel))
{
maturity = FSViewerMaturity::STREAMING_VIEWER;
}
else
{
maturity = FSViewerMaturity::UNOFFICIAL_VIEWER;

View File

@ -119,6 +119,7 @@ public:
BETA_VIEWER,
NIGHTLY_VIEWER,
RELEASE_VIEWER,
STREAMING_VIEWER,
};
FSViewerMaturity getFSViewerMaturity() const;
// </FS:Beq>

View File

@ -225,6 +225,7 @@
#include "lggbeamcolormapfloater.h"
#include "lggbeammapfloater.h"
#include "llfloaterdisplayname.h"
#include "fsfloaterprimfeed.h"
#include "llfloaterflickr.h"
#include "llfloaterscriptrecover.h"
#include "llfloatersearchreplace.h"
@ -630,6 +631,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("export_collada", "floater_export_collada.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ColladaExportFloater>);
LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeleteQueue>);
LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFlickr>);
LLFloaterReg::add("primfeed", "floater_primfeed.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterPrimfeed>);
LLFloaterReg::add("fs_asset_blacklist", "floater_fs_asset_blacklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterAssetBlacklist>);
LLFloaterReg::add("fs_avatar_render_settings", "floater_fs_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterAvatarRenderSettings>);
LLFloaterReg::add("fs_blocklist", "floater_fs_blocklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterBlocklist>);

View File

@ -2757,6 +2757,28 @@ class LLAdvancedCompressFileTest : public view_listener_t
}
};
// <FS:Beq> Primfeed integration test functions (can be removed when the feature is stable)
///////////////////
// PRIMFEED AUTH //
///////////////////
#include "fsprimfeedauth.h"
class LLAdvancedPrimfeedAuth : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
FSPrimfeedAuth::initiateAuthRequest();
return true;
}
};
class LLAdvancedPrimfeedAuthReset : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
FSPrimfeedAuth::resetAuthStatus();
return true;
}
};
// </FS:Beq>
/////////////////////////
// SHOW DEBUG SETTINGS //
@ -11882,6 +11904,19 @@ class LLWorldEnvSettings : public view_listener_t
#endif
// </FS:Beq>
// <FS:Darl> Redundant environment toggles revert to shared environment
LLSettingsSky::ptr_t sky = LLEnvironment::instance().getEnvironmentFixedSky(LLEnvironment::ENV_LOCAL);
LLUUID skyid = (sky) ? sky->getAssetId() : LLUUID::null;
bool repeatedEnvTogglesShared = gSavedSettings.getBOOL("FSRepeatedEnvTogglesShared");
if(repeatedEnvTogglesShared && ((skyid == LLEnvironment::KNOWN_SKY_SUNRISE && event_name == "sunrise") ||
(skyid == LLEnvironment::KNOWN_SKY_MIDDAY && event_name == "noon") ||
(skyid == LLEnvironment::KNOWN_SKY_LEGACY_MIDDAY && event_name == "legacy noon") ||
(skyid == LLEnvironment::KNOWN_SKY_SUNSET && event_name == "sunset") ||
(skyid == LLEnvironment::KNOWN_SKY_MIDNIGHT && event_name == "midnight")))
event_name = "region";
// </FS:Darl>
if (event_name == "sunrise")
{
// <FS:Beq> FIRE-29926 - allow manually selected environments to have a user defined transition time.
@ -12906,6 +12941,8 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates");
view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage");
view_listener_t::addMenu(new LLAdvancedCompressFileTest(), "Advanced.CompressFileTest");
view_listener_t::addMenu(new LLAdvancedPrimfeedAuth(), "Advanced.PrimfeedAuth");
view_listener_t::addMenu(new LLAdvancedPrimfeedAuthReset(), "Advanced.PrimfeedAuthReset");
view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings");
view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions");
view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions");

View File

@ -6687,6 +6687,7 @@ void process_alert_core(const std::string& message, bool modal)
if (text.substr(0, restart_cancelled.length()) == restart_cancelled)
{
LLFloaterRegionRestarting::close();
fs_report_region_restart_to_channel(-1); // <FS:Darl> Announce region restart to a defined chat channel
}
std::string new_msg =LLNotifications::instance().getGlobalString(text);
@ -8755,7 +8756,14 @@ void fs_report_region_restart_to_channel(S32 seconds)
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_ChatData);
msg->addStringFast(_PREHASH_Message, "region_restart_in:" + llformat("%d", seconds));
if(seconds >= 0)
{
msg->addStringFast(_PREHASH_Message, "region_restart_in:" + llformat("%d", seconds));
}
else // Input is a negative number
{
msg->addStringFast(_PREHASH_Message, "region_restart_cancelled");
}
msg->addU8Fast(_PREHASH_Type, CHAT_TYPE_WHISPER);
msg->addS32("Channel", channel);
gAgent.sendReliableMessage();

View File

@ -835,7 +835,7 @@ void LLViewerObjectList::setAllObjectDefaultTextures(U32 nChannel, bool fShowDef
}
}
// [/SL:KB]
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
// Added time limit on processing of objects as they affect the texture system (They also calcuate mMaxVirtualSize and mPixelArea)
void LLViewerObjectList::updateApparentAngles(LLAgent &agent, F32 max_time)
@ -845,7 +845,7 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent, F32 max_time)
LLViewerObject *objectp;
S32 num_updates, max_value;
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
// Remove the old code as it worked on fixed number of updates (Total # of Object / 128) per frame
// and some objects had nothing to do while others were avatars or volumes and could t
/*

View File

@ -89,7 +89,7 @@ public:
void processObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type, bool compressed=false);
void processCompressedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
void processCachedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings, not happening with SL Viewer
// <FS:minerjr> [FIRE-35081] Blurry prims not changing with graphics settings
//void updateApparentAngles(LLAgent &agent);
// Added time limit on processing of objects as they affect the texture system
void updateApparentAngles(LLAgent &agent, F32 max_time);

View File

@ -121,6 +121,7 @@ namespace
void newRegionEntry(LLViewerRegion& region)
{
LL_PROFILE_ZONE_SCOPED; // <FS:Beq/> improve instrumentation
LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL;
gDebugInfo["CurrentRegion"] = region.getName();
LLAppViewer::instance()->writeDebugInfo();
@ -3983,11 +3984,14 @@ bool LLViewerRegion::bakesOnMeshEnabled() const
mSimulatorFeatures["BakesOnMeshEnabled"].asBoolean());
}
// <FS:Beq> FIRE-35602 etc - Mesh not appearing after TP/login (opensim only)
#ifdef OPENSIM
bool LLViewerRegion::meshRezEnabled() const
{
return (mSimulatorFeatures.has("MeshRezEnabled") &&
mSimulatorFeatures["MeshRezEnabled"].asBoolean());
return (mSimulatorFeatures.has("MeshRezEnabled") && mSimulatorFeatures["MeshRezEnabled"].asBoolean());
}
#endif
// </FS:Beq>
bool LLViewerRegion::dynamicPathfindingEnabled() const
{

Some files were not shown because too many files have changed in this diff Show More