diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index e8cb1c9dfd..68a369b8c2 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -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::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 */ diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 725f04b1f2..2f96c1ded4 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -333,11 +333,21 @@ const std::string LLDiskCache::metaDataToFilepath(const LLUUID& id, LLAssetType: const std::string LLDiskCache::getCacheInfo() { + LL_PROFILE_ZONE_SCOPED; // 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; - + // 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; + } + // cache_info << std::fixed; cache_info << std::setprecision(1); cache_info << "Max size " << max_in_mb << " MB "; diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 5c37fee62c..4843ed83f1 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -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 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()) && diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 781d0fce43..8f74da47b2 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -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; // 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 // diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index b3f32fdc83..c682b0493a 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -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 diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 68b619f4b4..a3d57f6690 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1530,7 +1530,19 @@ void LLTextBase::draw() bg_rect.intersectWith( text_rect ); gl_rect_2d( text_rect, bg_color, true ); + + // Additionally set the font color of highlighted text instead of using LabelTextColor + const LLColor4& font_color = ll::ui::SearchableControl::getHighlightFontColor(); + setColor(font_color); + // } + // Set the font color back to LabelTextColor if not highlighted + else + { + const LLColor4& font_color = LLUIColorTable::instance().getColor("LabelTextColor"); + setColor(font_color); + } + // bool should_clip = mClip || mScroller != NULL; // Fix text bleeding at top edge of scrolling text editors diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp index a033c72910..f394989f19 100644 --- a/indra/llwindow/lldxhardware.cpp +++ b/indra/llwindow/lldxhardware.cpp @@ -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; + // 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 + } + // 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()) // caching version (also make the varname not stupid) { - mDriverVersion = str; + driver_version = str; // caching version (also make the varname not stupid) } - else if (mDriverVersion != str) + else if (driver_version != str) // 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; // caching version of driver query } void get_wstring(IDxDiagContainer* containerp, const WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8b0555fd0c..cbd633c56c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -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 # [Legacy Bake] llagentwearablesfetch.cpp @@ -909,6 +913,7 @@ set(viewer_HEADER_FILES dialogstack.h exoflickr.h exoflickrauth.h + fsprimfeedauth.h exogroupmutelist.h floatermedialists.h fsareasearch.h @@ -979,6 +984,7 @@ set(viewer_HEADER_FILES fspose.h fsposeranimator.h fsposingmotion.h + fsprimfeedauth.h fsradar.h fsradarentry.h fsradarlistctrl.h @@ -998,6 +1004,8 @@ set(viewer_HEADER_FILES lfsimfeaturehandler.h llflickrconnect.h llfloaterflickr.h + fsprimfeedconnect.h + fsfloaterprimfeed.h # [Legacy Bake] llagentwearablesfetch.h vjlocalmesh.h diff --git a/indra/newview/ao.cpp b/indra/newview/ao.cpp index f0fb99875f..f3b3e390f7 100644 --- a/indra/newview/ao.cpp +++ b/indra/newview/ao.cpp @@ -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); +// Safer casts + if (LLScrollListCell* icon_cell = mCurrentBoldItem->getColumn(0)) + { + if (LLScrollListIcon* icon = dynamic_cast(icon_cell)) + { + icon->setValue("FSAO_Animation_Stopped"); + } + } + + if (LLScrollListCell* text_cell = mCurrentBoldItem->getColumn(1)) + { + if (LLScrollListText* text = dynamic_cast(text_cell)) + { + text->setFontStyle(LLFontGL::NORMAL); + } + } +// 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()) +// Fix potential nullptr + if (!mAnimationList) { - LLUUID* id = (LLUUID*)item->getUserdata(); + LL_WARNS("AO") << "Animation list control is null." << LL_ENDL; + return; + } +// - if (id == &animation) +// Safer casts + // why do we have no LLScrollListCtrl::getItemByUserdata() ? -Zi + for (LLScrollListItem* item : mAnimationList->getAllData()) + { + LLUUID* id = static_cast(item->getUserdata()); + // compares the LLUUID values instead of pointer values + //if (id == &animation) + if (id && *id == animation) + // { 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(icon_cell)) + { + icon->setValue("FSAO_Animation_Playing"); + } + } + + if (LLScrollListCell* text_cell = mCurrentBoldItem->getColumn(1)) + { + if (LLScrollListText* text = dynamic_cast(text_cell)) + { + text->setFontStyle(LLFontGL::BOLD); + } + } return; } } +// } // virtual diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp index a66c8885b8..19b53d3ba1 100644 --- a/indra/newview/aoengine.cpp +++ b/indra/newview/aoengine.cpp @@ -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(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 { diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 7708f9a0c9..7f26b29024 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -254,6 +254,16 @@ is_running_function="Floater.IsOpen" is_running_parameters="flickr" /> + RenderShaderLightingMaxLevel RenderShadowDetail RenderShadowResolutionScale + RenderSkyAmbientScale RenderSkyAutoAdjustAmbientScale RenderSkyAutoAdjustBlueDensityScale RenderSkyAutoAdjustBlueHorizonScale RenderSkyAutoAdjustSunColorScale RenderSkyAutoAdjustHDRScale + RenderSkyAutoAdjustLegacy RenderSkyAutoAdjustProbeAmbiance RenderSkySunlightScale RenderSSAOIrradianceScale diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a32d8c453c..76a3f737aa 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -153,6 +153,17 @@ Value http://phoenixviewer.com/app/fsdata/grids.xml + FSPrimfeedViewerApiKey + + Comment + Viewer key for API login. + Persist + 1 + Type + String + Value + xAcXYt8SBius3Lor4wHle8L96PDHYlAZuWYXIYQUdW4b09mjhQUAwiqmWp5UNYXLpq5GSUtuKHuDYLwaueACPkew93l6MRY8jfBKSH09kv0zyGglpky07X7X7Sp4Rzin + FSGridBuilderURL Comment @@ -18542,6 +18553,7 @@ Change of this parameter will affect the layout of buttons in notification toast world_map preferences flickr + primfeed Backup 0 @@ -24729,6 +24741,39 @@ Change of this parameter will affect the layout of buttons in notification toast Value 4 + FSLastSnapshotToPrimfeedHeight + + Comment + The height of the last Primfeed snapshot, in px + Persist + 1 + Type + S32 + Value + 768 + + FSLastSnapshotToPrimfeedWidth + + Comment + The width of the last Primfeed snapshot, in px + Persist + 1 + Type + S32 + Value + 1024 + + FSLastSnapshotToPrimfeedResolution + + Comment + 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 + Persist + 1 + Type + S32 + Value + 4 + FSLastSnapshotToTwitterHeight Comment @@ -25015,6 +25060,19 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSNoVersionPopup + + Comment + Disables version popup on the Firestorm Login page + Persist + 1 + Type + Boolean + Value + 0 + HideFromEditor + 1 + FSllOwnerSayToScriptDebugWindowRouting Comment @@ -26299,5 +26357,16 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSRepeatedEnvTogglesShared + + Comment + Whether repeated presses of sky preset shortcuts should revert to shared environment + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 95e3c687dd..1b027b4d75 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -1358,6 +1358,83 @@ Value 0 + FSPrimfeedOAuthToken + + Comment + contains the secure authentication toke to post to your primfeed account (do not share) + Persist + 1 + Type + String + Value + + + FSPrimfeedProfileLink + + Comment + The profile page for the account associated with the currently linked Primfeed account + Persist + 1 + Type + String + Value + + + FSPrimfeedPlan + + Comment + The plan type associated with the currently linked Primfeed account + Persist + 1 + Type + String + Value + + + FSPrimfeedUsername + + Comment + The username associated with the currently linked Primfeed account + Persist + 1 + Type + String + Value + + + FSPrimfeedCommercialContent + + Comment + Does this post contain commercial content + Persist + 1 + Type + Boolean + Value + 0 + + FSPrimfeedAddToPublicGallery + + Comment + Should this post go to the public gallery? + Persist + 0 + Type + Boolean + Value + 0 + + FSPrimfeedOpenURLOnPost + + Comment + if true open the URL in a browser when the post completes + Persist + 1 + Type + Boolean + Value + 0 + FSProtectedFolders Comment @@ -1369,5 +1446,49 @@ Value + FSPrimfeedPhotoRating + + Comment + Content rating to be shared with Primfeed. + Persist + 1 + Type + Integer + Value + 1 + + FSPrimfeedPhotoResolution + + Comment + Last used resolution for Primfeed photos. + Persist + 1 + Type + LLSD + Value + + + FlickrPhotoResolution + + Comment + Last used resolution for Primfeed photos. + Persist + 1 + Type + LLSD + Value + + + FlickrPhotoRating + + Comment + Content rating to be shared with Flickr. + Persist + 1 + Type + Integer + Value + 1 + diff --git a/indra/newview/bugsplatattributes.cpp b/indra/newview/bugsplatattributes.cpp index 1852f9de3f..d0a8b32701 100644 --- a/indra/newview/bugsplatattributes.cpp +++ b/indra/newview/bugsplatattributes.cpp @@ -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 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 << "\n"; + ofs << " <" << category << "-" << key << ">" << val << "\n"; } - ofs << " \n"; } ofs << "\n"; diff --git a/indra/newview/fsareasearch.cpp b/indra/newview/fsareasearch.cpp index 4e58c4e28e..ef4efc84fc 100644 --- a/indra/newview/fsareasearch.cpp +++ b/indra/newview/fsareasearch.cpp @@ -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("exclude_reflection_probes"); + mCheckboxExcludeReflectionProbes->set(false); + mCheckboxExcludeReflectionProbes->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + mCheckboxExcludeChildPrim = getChild("exclude_childprim"); mCheckboxExcludeChildPrim->set(true); mCheckboxExcludeChildPrim->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); @@ -2240,6 +2256,9 @@ bool FSPanelAreaSearchFilter::postBuild() mCheckboxMoaP = getChild("filter_moap"); mCheckboxMoaP->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + mCheckboxReflectionProbe = getChild("filter_reflection_probe"); + mCheckboxReflectionProbe->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this)); + mCheckboxPermCopy = getChild("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); diff --git a/indra/newview/fsareasearch.h b/indra/newview/fsareasearch.h index 78d40454dc..a4bb89c2b0 100644 --- a/indra/newview/fsareasearch.h +++ b/indra/newview/fsareasearch.h @@ -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; diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index 136a626252..823552eb10 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -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; + // [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; + } + // + if (!isChatMultiTab()) { //hide all diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index f3d01fd6a9..22ac6d0a94 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -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 /// @@ -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() diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 14c7d90615..7e0e6c42e4 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -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; diff --git a/indra/newview/fsfloaterprimfeed.cpp b/indra/newview/fsfloaterprimfeed.cpp new file mode 100644 index 0000000000..c665d09198 --- /dev/null +++ b/indra/newview/fsfloaterprimfeed.cpp @@ -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 +#include "llspinctrl.h" + +#include "llviewernetwork.h" +#include "llnotificationsutil.h" +#include "fsprimfeedauth.h" +#include "llviewernetwork.h" + +static LLPanelInjector t_panel_photo("fsprimfeedphotopanel"); +static LLPanelInjector 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("resolution_combobox")->getCurrentIndex()); + gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", getChild("custom_snapshot_width")->getValue().asInteger()); + gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", getChild("custom_snapshot_height")->getValue().asInteger()); +} + +bool FSPrimfeedPhotoPanel::postBuild() +{ + setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); }); + + mResolutionComboBox = getChild("resolution_combobox"); + mResolutionComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); }); + mFilterComboBox = getChild("filters_combobox"); + mFilterComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); }); + mRefreshBtn = getChild("new_snapshot_btn"); + mBtnPreview = getChild("big_preview_btn"); + mWorkingLabel = getChild("working_lbl"); + mThumbnailPlaceholder = getChild("thumbnail_placeholder"); + mDescriptionTextBox = getChild("photo_description"); + mLocationCheckbox = getChild("add_location_cb"); + mCommercialCheckbox = getChild("primfeed_commercial_content"); + mPublicGalleryCheckbox = getChild("primfeed_add_to_public_gallery"); + mRatingComboBox = getChild("rating_combobox"); + mPostButton = getChild("post_photo_btn"); + mCancelButton = getChild("cancel_photo_btn"); + mBigPreviewFloater = dynamic_cast(LLFloaterReg::getInstance("big_preview")); + + // Update custom resolution controls with lambdas + getChild("custom_snapshot_width")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); }); + getChild("custom_snapshot_height")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); }); + getChild("keep_aspect_ratio")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); }); + + getChild("resolution_combobox")->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToPrimfeedResolution")); + getChild("custom_snapshot_width")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedWidth")); + getChild("custom_snapshot_height")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedHeight")); + + // Update filter list + std::vector filter_list = LLImageFiltersManager::getInstance()->getFiltersList(); + auto* filterbox = static_cast(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(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())); + 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()); + } +} + +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 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 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()) + { + 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(mResolutionComboBox); + auto filterbox = static_cast(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(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("custom_snapshot_width"); + LLSpinCtrl* height_spinner = getChild("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(mResolutionComboBox)->getSelectedValue().asString() == "[i-1,i-1]"; + getChild("custom_snapshot_width")->setEnabled(custom_resolution); + getChild("custom_snapshot_height")->setEnabled(custom_resolution); + getChild("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("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(gViewerWindow->getWindowWidthRaw()) / static_cast(gViewerWindow->getWindowHeightRaw()); + + // change another value proportionally + if (isWidthChanged) + { + height = ll_round(static_cast(width) / aspect_ratio); + } + else + { + width = ll_round(static_cast(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(width) / aspect_ratio); + } + else + { + height = max_value; + width = ll_round(static_cast(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("connected_as_label"); + mAccountNameLink = getChild("primfeed_account_name"); + mAccountPlan = getChild("primfeed_account_plan"); + mPanelButtons = getChild("panel_buttons"); + mConnectButton = getChild("connect_btn"); + mDisconnectButton = getChild("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 primfeed_username(gSavedPerAccountSettings, "FSPrimfeedUsername"); + static LLCachedControl primfeed_profile_link(gSavedPerAccountSettings, "FSPrimfeedProfileLink"); + static LLCachedControl 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("big_preview")) + { + big_preview_floater->closeOnFloaterOwnerClosing(this); + } + LLFloater::onClose(app_quitting); +} + +void FSFloaterPrimfeed::onCancel() +{ + if (auto big_preview_floater = LLFloaterReg::getTypedInstance("big_preview")) + { + big_preview_floater->closeOnFloaterOwnerClosing(this); + } + closeFloater(); +} + +bool FSFloaterPrimfeed::postBuild() +{ + // Keep tab of the Photo Panel + mPrimfeedPhotoPanel = static_cast(getChild("panel_primfeed_photo")); + mPrimfeedAccountPanel = static_cast(getChild("panel_primfeed_account")); + // Connection status widgets + mStatusErrorText = getChild("connection_error_text"); + mStatusLoadingText = getChild("connection_loading_text"); + mStatusLoadingIndicator = getChild("connection_loading_indicator"); + + return LLFloater::postBuild(); +} + +void FSFloaterPrimfeed::showPhotoPanel() +{ + auto parent = dynamic_cast(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; +} diff --git a/indra/newview/fsfloaterprimfeed.h b/indra/newview/fsfloaterprimfeed.h new file mode 100644 index 0000000000..312f06c130 --- /dev/null +++ b/indra/newview/fsfloaterprimfeed.h @@ -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 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 + diff --git a/indra/newview/fspanellogin.cpp b/indra/newview/fspanellogin.cpp index 1c6d7ddf98..595b47dc47 100644 --- a/indra/newview/fspanellogin.cpp +++ b/indra/newview/fspanellogin.cpp @@ -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, diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 8056ba7785..fd67de8770 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -190,18 +190,18 @@ public: /// /// 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. /// /// /// 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. /// const std::vector 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: diff --git a/indra/newview/fsprimfeedauth.cpp b/indra/newview/fsprimfeedauth.cpp new file mode 100644 index 0000000000..c264c3b8dc --- /dev/null +++ b/indra/newview/fsprimfeedauth.cpp @@ -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 Primfeed’s 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: + * pf-user-uuid: + * Response: + * { "requestId": "<64-char string>" } + * + * 2. Redirect the user to: + * https://www.primfeed.com/oauth/viewer?r=&v= + * + * 3. The user is shown an approval screen. When they click Authorize, + * an in-world message is sent: + * #PRIMFEED_OAUTH: + * 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 + * pf-viewer-api-key: + * pf-viewer-request-id: + * Response: HTTP 204 + * + * 5. Optionally, check user status: + * GET https://api.primfeed.com/pf/viewer/user + * Headers: + * Authorization: Bearer + * pf-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 + +using Callback = FSPrimfeedAuth::authorized_callback_t; + +// private instance variable +std::shared_ptr FSPrimfeedAuth::sPrimfeedAuth; +std::unique_ptr FSPrimfeedAuth::sPrimfeedAuthPump = std::make_unique("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::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(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: " 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); + } +} diff --git a/indra/newview/fsprimfeedauth.h b/indra/newview/fsprimfeedauth.h new file mode 100644 index 0000000000..43fc36a937 --- /dev/null +++ b/indra/newview/fsprimfeedauth.h @@ -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 +#include + +/* +* 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 +{ +public: + // Callback type: first parameter indicates success and the second holds any LLSD response. + using authorized_callback_t = std::function; + static std::shared_ptr create(authorized_callback_t callback); + static std::unique_ptr 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 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 sAuthorisationInProgress; + + static constexpr U32 PRIMFEED_CONNECT_TIMEOUT = 300; // 5 minute timeout should work +}; + +#endif // FSPRIMFEEDAUTH_H \ No newline at end of file diff --git a/indra/newview/fsprimfeedconnect.cpp b/indra/newview/fsprimfeedconnect.cpp new file mode 100644 index 0000000000..f6d91480a2 --- /dev/null +++ b/indra/newview/fsprimfeedconnect.cpp @@ -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); +} \ No newline at end of file diff --git a/indra/newview/fsprimfeedconnect.h b/indra/newview/fsprimfeedconnect.h new file mode 100644 index 0000000000..409a93faed --- /dev/null +++ b/indra/newview/fsprimfeedconnect.h @@ -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 + +// Coro based connector designed to interface with floater designed along the same principles as LLFloaterFlickr.cpp + +class FSPrimfeedConnect : public LLSingleton +{ + 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; + + // 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 \ No newline at end of file diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 38d1cf66a3..909df52dc4 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -643,6 +643,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]; diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsi index e1417c2260..9033ee6c1b 100755 Binary files a/indra/newview/installers/windows/lang_ja.nsi and b/indra/newview/installers/windows/lang_ja.nsi differ diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bc42027bf0..2c7ac76af5 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -234,6 +234,8 @@ #include "llfloaterreg.h" #include "llfloatersimplesnapshot.h" #include "llfloatersnapshot.h" +#include "llfloaterflickr.h" +#include "fsfloaterprimfeed.h" // Primfeed Floater #include "llsidepanelinventory.h" #include "llatmosphere.h" @@ -1749,6 +1751,8 @@ bool LLAppViewer::doFrame() gPipeline.mReflectionMapManager.update(); LLFloaterSnapshot::update(); // take snapshots LLFloaterSimpleSnapshot::update(); + LLFloaterFlickr::update(); // FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon + FSFloaterPrimfeed::update(); // Primfeed support gGLActive = false; } @@ -3793,6 +3797,7 @@ bool LLAppViewer::waitForUpdater() void LLAppViewer::writeDebugInfo(bool isStatic) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // improve instrumentation #if LL_WINDOWS && LL_BUGSPLAT // Improve Bugsplat tracking by using attributes for certain static data items. const LLSD& info = getViewerInfo(); @@ -3821,6 +3826,7 @@ void LLAppViewer::writeDebugInfo(bool isStatic) LLSD LLAppViewer::getViewerInfo() const { + LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // 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 @@ -4133,6 +4139,7 @@ LLSD LLAppViewer::getViewerInfo() const // info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo(); if (auto cache = LLDiskCache::getInstance(); cache) { + LL_PROFILE_ZONE_NAMED("gvi-getCacheInfo"); // improve instrumentation info["DISK_CACHE_INFO"] = cache->getCacheInfo(); } // diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index de560859ec..d219cebf0e 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -644,11 +644,13 @@ int APIENTRY wWinMain(HINSTANCE hInstance, // 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(); diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 959fefd732..f044f61816 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -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. diff --git a/indra/newview/llfloaterflickr.cpp b/indra/newview/llfloaterflickr.cpp index 64d0c9c935..196defda1f 100644 --- a/indra/newview/llfloaterflickr.cpp +++ b/indra/newview/llfloaterflickr.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" +#include "llfloatersnapshot.h" // 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(); } - +// 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 ); + } +} +// LLSnapshotLivePreview* LLFlickrPhotoPanel::getPreviewView() { LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get(); diff --git a/indra/newview/llfloaterflickr.h b/indra/newview/llfloaterflickr.h index e0579ba870..d24f6ec97f 100644 --- a/indra/newview/llfloaterflickr.h +++ b/indra/newview/llfloaterflickr.h @@ -136,6 +136,7 @@ public: void onOpen(const LLSD& key); LLSnapshotLivePreview* getPreviewView(); // Required for snapshot frame rendering + static void update(); // FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon private: LLFlickrPhotoPanel* mFlickrPhotoPanel; LLTextBox* mStatusErrorText; diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp index 4dc711fbe6..7c81fda4ec 100644 --- a/indra/newview/llfloaterimnearbychathandler.cpp +++ b/indra/newview/llfloaterimnearbychathandler.cpp @@ -670,7 +670,14 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg, } } - + // 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; + } + // nearby_chat->addMessage(chat_msg, true, args); if (chat_msg.mSourceType == CHAT_SOURCE_AGENT diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index ac545cfe87..10e9fd3d5e 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -30,6 +30,7 @@ #include "llfloaterreg.h" #include "llfloaterflickr.h" // Share to Flickr +#include "fsfloaterprimfeed.h" // Share to Primfeed #include "llimagefiltersmanager.h" #include "llcheckboxctrl.h" #include "llcombobox.h" @@ -1485,12 +1486,12 @@ bool LLFloaterSnapshot::isWaitingState() // 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) // { // Share to Flickr //if (!initialized) - if (!initialized && !have_flickr) + if (!initialized && !have_socials) // return false; @@ -1509,16 +1510,18 @@ void LLFloaterSnapshotBase::ImplBase::updateLivePreview() { // don't update preview for hidden floater // FIRE-35002 - Post to flickr broken - LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance("flickr"); - auto have_flickr = floater_flickr != nullptr; + bool have_socials = ( + LLFloaterReg::findTypedInstance("flickr") != nullptr || + LLFloaterReg::findTypedInstance("primfeed") != nullptr + ); if ( ((mFloater && mFloater->isInVisibleChain()) || - have_flickr) && - ImplBase::updatePreviewList(true, have_flickr)) + have_socials) && + ImplBase::updatePreviewList(true, have_socials)) // { LL_DEBUGS() << "changed" << LL_ENDL; updateControls(mFloater); - } + } } //static diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h index e6c4025bb3..934893b50b 100644 --- a/indra/newview/llfloatersnapshot.h +++ b/indra/newview/llfloatersnapshot.h @@ -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); // FIRE-35002 - Post to flickr broken, improved solution + static bool updatePreviewList(bool initialized, bool have_socials = false); // FIRE-35002 - Post to flickr broken, improved solution void setAdvanced(bool advanced) { mAdvanced = advanced; } void setSkipReshaping(bool skip) { mSkipReshaping = skip; } diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index c4269ae352..4c102756c2 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -433,10 +433,13 @@ bool LLFloaterWorldMap::postBuild() F32 slider_zoom = mMapView->getZoom(); mZoomSlider->setValue(slider_zoom); + mTrackCtrlsPanel = getChild("layout_panel_4"); + mSearchButton = getChild("DoSearch"); + // Use own expand/collapse function //getChild("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 @@ -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; } } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index d71ae9e88c..f7c9482b23 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -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; }; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 44debf0682..073b23b565 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3912,16 +3912,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()) + // [FIRE-34494] fix unable to open an IM with someone who started a group chat + //ids.size()) + !ids.empty()) + // { session = LLIMModel::getInstance()->findAdHocIMSession(ids); if (session) { - new_session = false; - session_id = session->mSessionID; +// [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); + } +// + } + //Notify observers that a session was added if (new_session) { diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 228aa5bd1e..f29bc9782b 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -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; diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index e5cb14b262..3634dc39bc 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -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 ResponsePtr; void constructAuthParams(LLPointer user_credentials); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0ca6ee2a32..1a0f90bb3a 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -6207,13 +6207,20 @@ bool LLMeshRepository::meshUploadEnabled() bool LLMeshRepository::meshRezEnabled() { static LLCachedControl mesh_enabled(gSavedSettings, "MeshEnabled"); - LLViewerRegion *region = gAgent.getRegion(); - if(mesh_enabled && - region) +// 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 +// + return mesh_enabled; } // Threading: main thread only diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 084d2cc2de..3d286984b6 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -107,6 +107,9 @@ bool LLPanelOutfitsInventory::postBuild() getChild(SAVE_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, false)); getChild(SAVE_AS_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, true)); + // FIRE-17626: Attachment count in appearance floater + mTempAttachmentUpdateTimer.start(); + return true; } @@ -252,6 +255,20 @@ void LLPanelOutfitsInventory::onSave() } // 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()) @@ -264,7 +281,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(obj_items.size()); + U32 attachments = static_cast(obj_items.size()) + mCurrentTempAttachmentCount; LLStringUtil::format_map_t args; args["COUNT"] = llformat("%d", attachments); diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index f834a8d45c..e8b2221874 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -50,6 +50,8 @@ public: /*virtual*/ bool postBuild(); /*virtual*/ void onOpen(const LLSD& key); + void draw(); // FIRE-17626: Attachment count in appearance floater + void onSearchEdit(const std::string& string); void onSave(); void saveOutfit(bool as_new = false); @@ -82,6 +84,10 @@ private: // FIRE-17626: Attachment count in appearance floater LLInventoryCategoriesObserver* mCategoriesObserver; void onCOFChanged(); + + U32 mCurrentTempAttachmentCount{ 0 }; + + LLFrameTimer mTempAttachmentUpdateTimer; // ////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp index 58340a98d9..13aae9c37a 100644 --- a/indra/newview/llpanelsnapshotoptions.cpp +++ b/indra/newview/llpanelsnapshotoptions.cpp @@ -32,6 +32,7 @@ #include "llfloatersnapshot.h" // FIXME: create a snapshot model #include "llfloaterreg.h" #include "llfloaterflickr.h" // Share to Flickr +#include "fsfloaterprimfeed.h" // Share to Primfeed /** * Provides several ways to save a snapshot. @@ -52,6 +53,7 @@ private: void onSaveToInventory(); void onSaveToComputer(); void onSendToFlickr(); // Share to Flickr + void onSendToPrimfeed(); // 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)); // Share to Flickr + mCommitCallbackRegistrar.add("Snapshot.SendToPrimfeed", boost::bind(&LLPanelSnapshotOptions::onSendToPrimfeed, this)); // Share to Primfeed } // virtual @@ -113,11 +116,23 @@ void LLPanelSnapshotOptions::onSendToFlickr() { LLFloaterReg::hideInstance("snapshot"); - LLFloaterFlickr* flickr_floater = dynamic_cast(LLFloaterReg::getInstance("flickr")); - if (flickr_floater) + if (auto flickr_floater = LLFloaterReg::getTypedInstance("flickr")) { flickr_floater->showPhotoPanel(); } LLFloaterReg::showInstance("flickr"); } // + +// Share to Primfeed +void LLPanelSnapshotOptions::onSendToPrimfeed() +{ + LLFloaterReg::hideInstance("snapshot"); + + if (auto primfeed_floater = LLFloaterReg::getTypedInstance("primfeed")) + { + primfeed_floater->showPhotoPanel(); + } + LLFloaterReg::showInstance("primfeed"); +} +// diff --git a/indra/newview/llpresetsmanager.cpp b/indra/newview/llpresetsmanager.cpp index 836869a665..ebc332caa1 100644 --- a/indra/newview/llpresetsmanager.cpp +++ b/indra/newview/llpresetsmanager.cpp @@ -539,21 +539,21 @@ void LLPresetsManager::loadPreset(const std::string& subdirectory, std::string n gSavedSettings.setString("PresetGraphicActive", name); // [FIRE-35390] Old viewer presets have these as true and 0.7, whereas the equivalent on modern viewers is false and 1.0 - gSavedSettings.setBOOL("RenderSkyAutoAdjustLegacy", false); - gSavedSettings.setF32("RenderSkyAmbientScale", 1.0); + if (auto control = gSavedSettings.getControl("RenderSkyAutoAdjustLegacy")) + control->resetToDefault(true); + if (auto control = gSavedSettings.getControl("RenderSkyAmbientScale")) + control->resetToDefault(true); // // Update indirect controls LLAvatarComplexityControls::setIndirectControls(); - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->refreshEnabledGraphics(); } // Graphic preset controls independent from XUI - FloaterQuickPrefs* phototools = LLFloaterReg::findTypedInstance(PHOTOTOOLS_FLOATER); - if (phototools) + if (FloaterQuickPrefs* phototools = LLFloaterReg::findTypedInstance(PHOTOTOOLS_FLOATER)) { phototools->refreshSettings(); } diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 551dd63728..13a8feb9fc 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -7969,19 +7969,20 @@ bool LLSelectMgr::canSelectObject(LLViewerObject* object, bool ignore_select_own // only select my own objects return false; } + + // FIRE-14593: Option to select only copyable objects + if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly")) + { + return false; + } + // + // FIRE-17696: Option to select only locked objects + if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced()) + { + return false; + } + // // Can't select objects that are not owned by you or group } - // FIRE-14593: Option to select only copyable objects - if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly")) - { - return false; - } - // - // FIRE-17696: Option to select only locked objects - if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced()) - { - return false; - } - // // Can't select orphans if (object->isOrphaned()) return false; diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index c9826a9775..80ccde3483 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -36,6 +36,7 @@ #include "llfloaterperms.h" #include "llfloaterreg.h" #include "llfloaterflickr.h" // Share to Flickr +#include "fsfloaterprimfeed.h" // Share to Primfeed #include "llimagefilter.h" #include "llimagefiltersmanager.h" #include "llimagebmp.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 85541d8175..b73a5ef9f6 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1950,9 +1950,9 @@ bool idle_startup() // 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 communication UI] // 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(); // Moved before inventory creation. // request all group information LL_INFOS("Agent_GroupData") << "GROUPDEBUG: Requesting Agent Data during startup" << LL_ENDL; gAgent.sendAgentDataUpdateRequest(); - display_startup(); // + + 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(); // if (gSavedSettings.getBOOL("HelpFloaterOpen")) @@ -3074,7 +3075,7 @@ bool idle_startup() } } #endif // OPENSIM - display_startup(); + do_startup_frame(); // 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); // 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 } // + bool success = false; // JC: gesture loading done below, when we have an asset system // in place. Don't delete/clear gUserCredentials until then. diff --git a/indra/newview/llteleporthistory.cpp b/indra/newview/llteleporthistory.cpp index 3d90734d96..3c20615a24 100644 --- a/indra/newview/llteleporthistory.cpp +++ b/indra/newview/llteleporthistory.cpp @@ -44,6 +44,10 @@ #include "llavatarname.h" #include "llavatarnamecache.h" +#include "llviewernetwork.h" // Access to GridManager +#include "lfsimfeaturehandler.h" // Access to hyperGridURL +#include "llworldmapmessage.h" // 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; } + // [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 + // + // 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] + + // [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 + // } //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; } } + +// [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(); + } +} +// diff --git a/indra/newview/llteleporthistory.h b/indra/newview/llteleporthistory.h index 6962f4afe3..5d705fe90d 100644 --- a/indra/newview/llteleporthistory.h +++ b/indra/newview/llteleporthistory.h @@ -35,6 +35,8 @@ #include #include "llteleporthistorystorage.h" +#include "llslurl.h" // 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) + // [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; // [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); + // [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); + // + /** * Actually, the teleport history. */ diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index b633dc4fed..bbcd832b14 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -2089,12 +2089,14 @@ LLPointer 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; diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index e50758ba2a..3614a6b754 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -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; diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index d399dde814..5d1e90a654 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -119,6 +119,7 @@ public: BETA_VIEWER, NIGHTLY_VIEWER, RELEASE_VIEWER, + STREAMING_VIEWER, }; FSViewerMaturity getFSViewerMaturity() const; // diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4fc57172d6..06f68e9407 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -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); LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("primfeed", "floater_primfeed.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_asset_blacklist", "floater_fs_asset_blacklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_avatar_render_settings", "floater_fs_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_blocklist", "floater_fs_blocklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a85a7e8de2..dbba65666f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2757,6 +2757,28 @@ class LLAdvancedCompressFileTest : public view_listener_t } }; +// 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; + } +}; +// ///////////////////////// // SHOW DEBUG SETTINGS // @@ -11787,6 +11809,19 @@ class LLWorldEnvSettings : public view_listener_t #endif // + // 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"; + // + if (event_name == "sunrise") { // FIRE-29926 - allow manually selected environments to have a user defined transition time. @@ -12809,6 +12844,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"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 3e43434eb2..213ec602a5 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -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); // 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(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 0fdee5ca53..bf2744a8cd 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -120,6 +120,7 @@ namespace void newRegionEntry(LLViewerRegion& region) { + LL_PROFILE_ZONE_SCOPED; // improve instrumentation LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL; gDebugInfo["CurrentRegion"] = region.getName(); LLAppViewer::instance()->writeDebugInfo(); @@ -3982,11 +3983,14 @@ bool LLViewerRegion::bakesOnMeshEnabled() const mSimulatorFeatures["BakesOnMeshEnabled"].asBoolean()); } +// 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 +// bool LLViewerRegion::dynamicPathfindingEnabled() const { diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 07388518f3..119647ecbf 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -355,8 +355,11 @@ public: U8 getCentralBakeVersion() { return mCentralBakeVersion; } void getInfo(LLSD& info); - +// FIRE-35602 etc - Mesh not appearing after TP/login (opensim only) +#ifdef OPENSIM bool meshRezEnabled() const; +#endif // OPENSIM +// bool meshUploadEnabled() const; bool bakesOnMeshEnabled() const; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index aee68093d0..f2de57f14c 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1237,7 +1237,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag vsize = vsize + (vsize * face->mCloseToCamera * texture_camera_boost); // Update the max on screen vsize based upon the on screen vsize close_to_camera += face->mCloseToCamera; - LL_DEBUGS() << face->getViewerObject()->getID() << " TID " << imagep->getID() << " #F " << imagep->getNumFaces(i) << " OS Vsize: " << vsize << " Vsize: " << (vsize * bias) << " CTC: " << face->mCloseToCamera << " Channel " << i << " Face Index " << fi << LL_ENDL; + // LL_DEBUGS() << face->getViewerObject()->getID() << " TID " << imagep->getID() << " #F " << imagep->getNumFaces(i) << " OS Vsize: " << vsize << " Vsize: " << (vsize * bias) << " CTC: " << face->mCloseToCamera << " Channel " << i << " Face Index " << fi << LL_ENDL; max_on_screen_vsize = llmax(max_on_screen_vsize, vsize); max_vsize = llmax(max_vsize, vsize * bias); // [FIRE-35081] diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 77130beae3..e5fe30c167 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -5430,14 +5430,6 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, bool pick_transp pick_transparent = true; } - // Pick from center of screen in mouselook - if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) - { - x = gViewerWindow->getWorldViewRectScaled().getWidth() / 2; - y_from_bot = gViewerWindow->getWorldViewRectScaled().getHeight() / 2; - } - // - // shortcut queueing in mPicks and just update mLastPick in place MASK key_mask = gKeyboard->currentMask(true); mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, pick_rigged, pick_particle, pick_reflection_probe, true, false, NULL); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index ba8fb0438a..1c829e6902 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2664,10 +2664,11 @@ S32 LLVOVolume::setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID) S32 LLVOVolume::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) { S32 res = LLViewerObject::setTEMaterialParams(te, pMaterialParams); - - LL_DEBUGS("MaterialTEs") << "te " << (S32)te << " material " << ((pMaterialParams) ? pMaterialParams->asLLSD() : LLSD("null")) << " res " << res - << ( LLSelectMgr::getInstance()->getSelection()->contains(const_cast(this), te) ? " selected" : " not selected" ) - << LL_ENDL; + // Remove debug logging that is more expensive than the call itself even when disabled + // LL_DEBUGS("MaterialTEs") << "te " << (S32)te << " material " << ((pMaterialParams) ? pMaterialParams->asLLSD() : LLSD("null")) << " res " << res + // << ( LLSelectMgr::getInstance()->getSelection()->contains(const_cast(this), te) ? " selected" : " not selected" ) + // << LL_ENDL; + // setChanged(ALL_CHANGED); if (!mDrawable.isNull()) { diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index f774f22938..fd18971733 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -111,6 +111,7 @@ #include "llfloatertools.h" #include "llfloatersnapshot.h" // for snapshotFrame #include "llfloaterflickr.h" // for snapshotFrame +#include "fsfloaterprimfeed.h" // for snapshotFrame #include "llsnapshotlivepreview.h" // for snapshotFrame // #include "llpanelface.h" // switchable edit texture/materials panel - include not needed #include "llpathfindingpathtool.h" @@ -788,15 +789,9 @@ void LLPipeline::requestResizeShadowTexture() void LLPipeline::resizeShadowTexture() { - // [FIRE-33200] changing shadowres requires reload - original fix by William Weaver (paperwork) - if(mRT->width == 0 || mRT->height == 0) - { - return; - } - // releaseSunShadowTargets(); releaseSpotShadowTargets(); - allocateShadowBuffer(mRT->width, mRT->height); + allocateShadowBuffer(mRT->screen.getWidth(), mRT->screen.getHeight()); // revert and correct previous shadowres fix that leads to FPS drop (FIRE-3200) gResizeShadowTexture = false; } @@ -8055,12 +8050,12 @@ bool LLPipeline::renderSnapshotFrame(LLRenderTarget* src, LLRenderTarget* dst) } const bool simple_snapshot_visible = LLFloaterReg::instanceVisible("simple_snapshot"); const bool flickr_snapshot_visible = LLFloaterReg::instanceVisible("flickr"); + const bool primfeed_snapshot_visible = LLFloaterReg::instanceVisible("primfeed"); // Primfeed integration const bool snapshot_visible = LLFloaterReg::instanceVisible("snapshot"); - const bool any_snapshot_visible = simple_snapshot_visible || flickr_snapshot_visible || snapshot_visible; + const bool any_snapshot_visible = simple_snapshot_visible || flickr_snapshot_visible || primfeed_snapshot_visible || snapshot_visible; // Primfeed integration if (!show_frame || !any_snapshot_visible || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) { return false; - } LLSnapshotLivePreview * previewView = nullptr; if (snapshot_visible) @@ -8074,6 +8069,13 @@ bool LLPipeline::renderSnapshotFrame(LLRenderTarget* src, LLRenderTarget* dst) auto * floater = dynamic_cast(LLFloaterReg::findInstance("flickr")); previewView = floater->getPreviewView(); } + // Primfeed integration + if (primfeed_snapshot_visible && !previewView) + { + auto * floater = dynamic_cast(LLFloaterReg::findInstance("primfeed")); + previewView = floater->getPreviewView(); + } + // if(!previewView) { return false; diff --git a/indra/newview/skins/ansastorm/xui/ja/panel_main_inventory.xml b/indra/newview/skins/ansastorm/xui/ja/panel_main_inventory.xml index c4a73462f9..20b97988a6 100644 --- a/indra/newview/skins/ansastorm/xui/ja/panel_main_inventory.xml +++ b/indra/newview/skins/ansastorm/xui/ja/panel_main_inventory.xml @@ -10,8 +10,6 @@ [ITEM_COUNT]個のアイテムを取得しました。[FILTER] インベントリ - Multi_Folder_Mode - Single_Folder_Mode diff --git a/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml b/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml index 5c14e5e3d4..be7a5f8f57 100644 --- a/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml +++ b/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml @@ -1,7 +1,7 @@ - + diff --git a/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml b/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml new file mode 100644 index 0000000000..22c7d7c3f5 --- /dev/null +++ b/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml @@ -0,0 +1,41 @@ + + + + Girar a câmera em torno do foco + + + Zoom da câmera em direção ao foco + + + Mover a câmera para cima, para baixo, esquerda e direita + + + Visualizar Objeto + + + Use a predefinição + + + + + + + + + [https://docs.primfeed.com Learn more about Primfeed] + + + diff --git a/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml new file mode 100644 index 0000000000..061b206e0d --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + x + + + + + + + Refreshing... + + + + + + + + Description: + + + + + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml index d6f10ff08b..153a63fa6a 100644 --- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml +++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml @@ -9,7 +9,7 @@ animate="false" name="option_buttons" orientation="vertical" - height="152" + height="157" width="208" left="0" top="9" @@ -52,6 +52,7 @@ height="22" image_overlay="Snapshot_Inventory" image_overlay_alignment="left" + image_overlay_width="16" image_top_pad="-1" imgoverlay_label_space="10" label="Save to Inventory" @@ -111,6 +112,30 @@ function="Snapshot.SendToFlickr"/> + + + diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index e5b8a0ee86..0b985ebbfb 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -238,6 +238,10 @@ If you feel this is an error, please contact support@secondlife.com Problem connecting to Flickr Problem posting to Flickr Problem disconnecting from Flickr + Connecting to Primfeed... + Not Authorized... + Posting... + Problem posting to Primfeed Black & White @@ -2771,6 +2775,8 @@ name="Command_360_Capture_Label">360° Snapshot Beacons Poser Pose your avatar and animated objects + Primfeed + Post directly to your Primfeed account. Capture a 360° equirectangular image diff --git a/indra/newview/skins/default/xui/es/floater_about.xml b/indra/newview/skins/default/xui/es/floater_about.xml index 15cf48db0e..03b6daec06 100644 --- a/indra/newview/skins/default/xui/es/floater_about.xml +++ b/indra/newview/skins/default/xui/es/floater_about.xml @@ -1,5 +1,5 @@ - + [APP_NAME] [VIEWER_VERSION_0].[VIEWER_VERSION_1].[VIEWER_VERSION_2] ([VIEWER_VERSION_3]) [BUILD_DATE] [BUILD_TIME] ([CHANNEL]) [BUILD_TYPE] [[VIEWER_RELEASE_NOTES_URL] Notas de la versión] @@ -42,7 +42,7 @@ Calidad de dibujo (Render quality): [RENDERQUALITY] Error obteniendo las notas de la versión de servidor. - + Para obtener la información más reciente acerca de Firestorm, visita @@ -58,10 +58,10 @@ Los Linden son, con contribuciones de código de: - + - + Firestorm es un proyecto de desarrollo de la comunidad para mejorar la experiencia de uso del Visor de SecondLife(tm). Compilamos contribuciones de varios desarrolladores de la comunidad junto con el código de Linden lab y el nuestro propio para brindarte un visor de calidad, enriquecido con nuevas características y respaldado por un amplio equipo de voluntarios para darte soporte. Firestorm llega a ti a través de The Phoenix Firestorm Project, Inc., una organización sin ánimo de lucro. Forman el Equipo de Desarrollo de Firestorm: diff --git a/indra/newview/skins/default/xui/fr/floater_fs_poser.xml b/indra/newview/skins/default/xui/fr/floater_fs_poser.xml index ea7eff527e..2715702cc7 100644 --- a/indra/newview/skins/default/xui/fr/floater_fs_poser.xml +++ b/indra/newview/skins/default/xui/fr/floater_fs_poser.xml @@ -13,11 +13,20 @@ Membres postérieurs Ailes Oreilles/nez + Corps + Corps + Bras + Jambes Tout l'avatar Torse + Colonne vertébrale 1 + Colonne vertébrale 2 + Colonne vertébrale 3 + Colonne vertébrale 4 Poitrine Cou Tête + Crâne Oeil droit Oeil gauche Front, côté gauche @@ -29,13 +38,17 @@ Sourcil, milieu droit Sourcil, intérieur droit Paupière, en haut à gauche + Coin interne gauche de l'œil Paupière, en bas à gauche Paupière, en haut à droite + Coin interne droit de l'œil Paupière en bas à droite Oreille en haut à gauche Oreille en bas à gauche Oreille en haut à droite Oreille en bas à droite + Base du nez + Arête du nez Nez à gauche Nez au milieu Nez à droite @@ -52,16 +65,12 @@ Extrémité de la langue Forme de la mâchoire Milieu du front - Base du nez Dents du haut Lèvre supérieure à gauche Lèvre supérieure à droite Coin gauche de la bouche Coin droit de la bouche Milieu de la lèvre supérieure - Coin interne gauche de l'œil - Coin interne droit de l'œil - Arête du nez Col Bras entier Avant-bras @@ -137,10 +146,31 @@ Droite 2 Droite 3 Droite 4 + Tête + Nuque + Poitrine Fesses Ventre Sein gauche Sein droit + Clavicule gauche + Clavicule droite + Haut du bras gauche + Haut du bras droit + Bas du bras gauche + Bas du bras droit + Main gauche + Main droite + Haut du dos + Taille à gauche + Taille à droite + Bassin + Haut de la jambe gauche + Haut de la jambe droite + Bas de la jambe gauche + Bas de la jambe droite + Pied gauche + Pied droit Charger pose Enr. pose Charger diff diff --git a/indra/newview/skins/default/xui/fr/floater_primfeed.xml b/indra/newview/skins/default/xui/fr/floater_primfeed.xml new file mode 100644 index 0000000000..c8bbeab03e --- /dev/null +++ b/indra/newview/skins/default/xui/fr/floater_primfeed.xml @@ -0,0 +1,17 @@ + + + + + + + + + + Erreur + + + Chargement... + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index 4fd69128f6..00df7d41ce 100644 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -93,6 +93,7 @@ + @@ -616,6 +617,8 @@ + + diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index 71a7609482..93701cf7bc 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -3065,6 +3065,9 @@ Voulez-vous autoriser [APP_NAME] à poster sur votre compte Flickr? Votre photo est visible maintenant [https://www.flickr.com/photos/me/[ID] ici]. + + Votre message Primfeed peut maintenant être consulté [[PF_POSTURL] ici]. + Avis d'événement : @@ -5632,4 +5635,29 @@ Pour obtenir des instructions détaillées sur la manière de mettre [APP_NAME] https://wiki.firestormviewer.org/antivirus_whitelisting + + Remplacer la pose “[POSE_NAME]”? + + + + Demande de connexion refusée par Primfeed. + + + L'autorisation Primfeed a échoué. La séquence d'autorisation n'a pas été achevée. + + + L'autorisation Primfeed est déjà en cours. Veuillez compléter l'autorisation Primfeed dans votre navigateur web avant de réessayer. + + + Autorisation Primfeed terminée. Vous pouvez maintenant poster des images sur Primfeed. + + + La validation de l'utilisateur Primfeed a échoué. Primfeed n'a pas reconnu ce compte ou la connexion a échoué. + + + Vous avez déjà lié ce compte à Primfeed. Utilisez le bouton de réinitialisation si vous souhaitez recommencer. + + + La connexion de l'utilisateur Primfeed a réussi, mais les vérifications d'état ont échoué. Veuillez vérifier que Primfeed fonctionne. + diff --git a/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml b/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml new file mode 100644 index 0000000000..31987c136b --- /dev/null +++ b/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml @@ -0,0 +1,19 @@ + + + + + + + Non connecté(e) à Primfeed. + + + Type de compte : + + + diff --git a/indra/newview/skins/default/xui/ja/menu_mini_map.xml b/indra/newview/skins/default/xui/ja/menu_mini_map.xml index c8fbe97729..a66cbc0228 100644 --- a/indra/newview/skins/default/xui/ja/menu_mini_map.xml +++ b/indra/newview/skins/default/xui/ja/menu_mini_map.xml @@ -37,10 +37,8 @@ - - @@ -55,7 +53,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/menu_pie_avatar_other.xml b/indra/newview/skins/default/xui/ja/menu_pie_avatar_other.xml index ec7637dc5b..eefc54f689 100644 --- a/indra/newview/skins/default/xui/ja/menu_pie_avatar_other.xml +++ b/indra/newview/skins/default/xui/ja/menu_pie_avatar_other.xml @@ -37,7 +37,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/menu_pie_object.xml b/indra/newview/skins/default/xui/ja/menu_pie_object.xml index fb2db89b63..39a8607895 100644 --- a/indra/newview/skins/default/xui/ja/menu_pie_object.xml +++ b/indra/newview/skins/default/xui/ja/menu_pie_object.xml @@ -59,7 +59,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/menu_topinfobar.xml b/indra/newview/skins/default/xui/ja/menu_topinfobar.xml index 5e03dfc7fb..1a67a2a8f7 100644 --- a/indra/newview/skins/default/xui/ja/menu_topinfobar.xml +++ b/indra/newview/skins/default/xui/ja/menu_topinfobar.xml @@ -2,8 +2,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/menu_url_group.xml b/indra/newview/skins/default/xui/ja/menu_url_group.xml index 2afa9ab39a..4d2f77f12b 100644 --- a/indra/newview/skins/default/xui/ja/menu_url_group.xml +++ b/indra/newview/skins/default/xui/ja/menu_url_group.xml @@ -1,13 +1,10 @@ - - - diff --git a/indra/newview/skins/default/xui/ja/menu_viewer.xml b/indra/newview/skins/default/xui/ja/menu_viewer.xml index 1989dceb32..a0c7a496eb 100644 --- a/indra/newview/skins/default/xui/ja/menu_viewer.xml +++ b/indra/newview/skins/default/xui/ja/menu_viewer.xml @@ -671,6 +671,8 @@ + + diff --git a/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml b/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml index 2d24658c72..6724ae58e5 100644 --- a/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml @@ -5,7 +5,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index 0c994c80df..4ef76ff045 100644 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -13,70 +13,23 @@ 閉じる - - - - - - - 現在使用中の[APP_NAME]のバージョンでは、受け取った通知メッセージを表示することができません。 最新のビューアがインストールされている事をご確認ください。 エラー詳細:「[_NAME]」という通知メッセージは、notifications.xmlに定義されていませんでした。 - fail フローターエラー:以下のコントロールが見つかりませんでした: [CONTROLS] - fail 現在利用可能なチュートリアルはありません。 - fail - - [MESSAGE] - [MESSAGE] @@ -94,20 +47,17 @@ [DIAGNOSTIC] インターネット接続が正常かご確認ください。 - fail ビューアがサーバーから不正な形式の応答を受け取りました。インターネット接続が正常に機能していることを確認してから、後でもう一度お試しください。 これが誤りだと思われる場合は、サポートにお問い合わせください。 - fail メッセージテンプレート[PATH]がありませんでした。 - fail 現在の着用物/身体部位の変更を保存しますか? @@ -119,7 +69,6 @@ 販売ボックスの最上位に転送した各アイテムにつき、それぞれ1つの新しいフォルダが作成されました。 - @@ -187,37 +136,30 @@ 出品リストを作成するために、出品リストするコンテンツの階層を固定しました。 - confirm この操作により、この出品リストの有効な内容が変更されます。続けてもよろしいですか? - confirm マーケットプレイスの出品リストウィンドウにドラッグしたアイテムは、元の場所からコピーされるのではなく移動となります。続けてもよろしいですか? - confirm 出品リストフォルダを移動または削除すると、マーケットプレイスの出品リストが削除されます。マーケットプレイスの出品リストを維持する場合は、変更するバージョンフォルダの中身を移動または削除してください。続けてもよろしいですか? - confirm これらのアイテムの1つまたは複数をマーケットプレイスにコピーする権限がありません。移動するか、残したままにすることはできます。 - confirm この操作により、この出品リストが削除されます。続けてもよろしいですか? - confirm この操作により、現在の出品リストのバージョンフォルダが無効になります。続けてもよろしいですか? - confirm @@ -227,48 +169,38 @@ マーケットプレイスの出品リストフォルダにある着用物や身体部位は着用できません。 - fail 出品リストIDが無効です。 - fail この出品リストには複数のバージョンフォルダがあるか、バージョンフォルダがありません。後で、単独でバージョンフォルダを選択して有効にする必要があります。 - confirm 各種の在庫品目を別々の在庫フォルダに分けたため、フォルダは出品リストに表示できるように配置されています。 - confirm 在庫が空のため、出品リストを削除しました。もう一度出品リストを表示するには、在庫フォルダにユニットを追加する必要があります。 - confirm バージョンフォルダが空のため、出品リストを削除しました。もう一度出品リストを表示するには、バージョンフォルダにアイテムを追加する必要があります。 - confirm アニメーションデータの書き込みに問題があります。後でもう一度お試しください。 - fail 以下の理由で、オークションのスナップショットのアップロード時に問題が起こりました:[REASON] - fail 一度に複数のアイテムの中身を表示できません。 アイテムを1つだけ選択して、もう一度お試しください。 - fail 衣装、身体部位に対する変更をすべて保存しますか? - confirm @@ -292,43 +224,32 @@ 他人に修正権限を与えると、権限を与えられた人はあなたが所有するインワールドのオブジェクトを変更することができるようになります。 この権限を与える際には十分に注意してください。選択した住人に修正権限を与えますか? - confirm [NAME]の修正権限を解除しますか? - confirm 選択した住人から変更権限を取り下げますか? - confirm グループ名は[MIN_LEN]~[MAX_LEN]文字である必要があります。 - group - fail グループを作成できません。 [MESSAGE] - group - fail [NEEDS_APPLY_MESSAGE] [WANT_APPLY_MESSAGE] - group - fail グループ通知の送信には、件名の記入が必要です。 - group - fail @@ -370,26 +291,18 @@ [AVATAR_NAME]をグループから追放しようとしています。 - group - confirm [COUNT]名のメンバーをグループから追放しようとしています。 - group - confirm [AVATAR_NAME]をグループからバンしようとしています。 - group - confirm [COUNT]名のメンバーをグループからバンしようとしています。 - group - confirm @@ -403,95 +316,62 @@ このグループに参加するには、L$ [COST]かかります。 このまま続けてもよろしいですか? - confirm - funds - group 「<nolink>[NAME]</nolink>」というグループに入ろうとしています。 このまま続けてもよろしいですか? - group - confirm このグループに加入するには、L$ [COST]必要です。 L$が不足しているので、このグループに参加することができません。 - group - fail - funds このグループ作成にかかる費用:L$ [COST] 一人ではグループにならないので、永久に削除されてしまいます。 48時間以内にメンバーを勧誘し、入会してもらってください。 - group - funds あなたはグループに参加することはできません。 - group_id - success グループ参加のリクエスト時にエラーが発生しました。 - group_id - success 以下のグループに参加できませんでした:[reason] - group_id - success - reason 申し訳ありませんが、トライアルユーザーはグループに参加できません。 - group_id - success 「<nolink>[group_name]</nolink>」に参加できません: 既に[group_count]個のグループのメンバーになっています。参加できるのは最大[max_groups]グループまでです。 - success - group_id - group_name - group_count - max_groups 「<nolink>[group_name]</nolink>」に参加できません: このグループは現在、参加が制限されています。 - group_id - success 会員料金として必要なL$ [membership_fee]を送金することができません。 - group_id - success この区画([PARCEL_NAME])は、L$ [COST]で[TIME]時間入ることができます。入場許可証を購入しますか? - fail - funds - confirm 不特定の人に売却する場合には、売却価格はL$ 0以上に設定する必要があります。 売却価格をL$ 0に設定する場合は、売却する個人を選択してください。 - fail 選択した[LAND_SIZE]㎡の土地は、売り出し中に設定されています。 売却価格L$ [SALE_PRICE]で、[NAME]に売却を認可します。 - confirm @@ -499,7 +379,6 @@ L$が不足しているので、このグループに参加することができ 選択した[LAND_SIZE]㎡の土地は、販売対象に設定されました。 販売価格L$ [SALE_PRICE]で、[NAME]が販売対象者となります。 - confirm @@ -508,20 +387,16 @@ L$が不足しているので、このグループに参加することができ *警告* この操作をすると、グループに寄贈された譲渡禁止のオブジェクトを削除することになります。 オブジェクト:[N] - confirm - group この区画で、住人[NAME]が所有する全てのオブジェクトを本人のインベントリに本当に返却してもよいですか? オブジェクト:[N] - confirm この土地区画内にある、あなたが所有するすべてのオブジェクトを、あなたのインベントリに戻そうとしています。このまま続けてもよろしいですか? オブジェクト:[N] - confirm @@ -532,7 +407,6 @@ L$が不足しているので、このグループに参加することができ これにより、グループに寄贈された「再販・譲渡不可」のオブジェクトは削除されます! オブジェクト:[N] - confirm @@ -543,214 +417,161 @@ L$が不足しているので、このグループに参加することができ これにより、グループに寄贈された「再販・譲渡不可」のオブジェクトは削除されます! オブジェクト:[N] - confirm リストされた全てのオブジェクトを所有者のインベントリに戻しますか? これによりすべてのスクリプト化されたオブジェクトがリージョンに戻ります! - confirm このリージョン内のすべてのオブジェクトを無効にしようとしています。このまま操作を続行してもよろしいですか? - confirm グループ <nolink>[NAME]</nolink>と共有されていない、この区画にあるオブジェクトを前のオーナーに返却しますか? オブジェクト:[N] - confirm スクリプトを無効にできません。 このリージョン全体が「ダメージ有効」に設定されています。 武器を使用するには、スクリプトの実行を許可する必要があります。 - fail 現在複数の面が選択されています。 このまま続けた場合、メディアの別々のインスタンスがオブジェクトの複数の面に設定されます。 メディアを1つの面だけに取り付けるには、「面を選択」を選んでオブジェクトの希望する面をクリック、それから「追加」をクリックしてください。 - confirm ランディング地点を設定するには、この区画の内側に立ってください。 - fail 受信者の有効なメールアドレスを入力してください。 - fail あなたのメールアドレスを入力してください。 - fail デフォルトの件名またはメッセージを付けて、スナップショットを送信しますか? - confirm スナップショットデータの処理エラー - fail スナップショットのエンコードエラー - fail このアイテムをアップロードするには、L$ [COST]が必要です。 - fail インベントリに写真を保存するには、L$ [COST]が必要です。L$を購入するか、代わりに写真をコンピュータに保存できます。 - fail インベントリにテクスチャを保存するには、L$ [COST]が必要です。L$を購入するか、代わりに写真をコンピュータに保存できます。 - fail 以下の理由でスナップショットの送信時に問題が起こりました:[REASON] - fail 以下の理由で、レポートのスクリーンショットのアップロード時に問題が起こりました:[REASON] - fail [CURRENT_GRID]へのログインを続けるには、利用規約、プライバシーポリシー、およびサービス規約に同意する必要があります。 - fail [TITLE] [MESSAGE] - fail アウトフィットを装着できません。 アウトフィットフォルダに衣装、身体部位、装着物がありません。 - fail ごみ箱にある着用物や身体部位は、着用できません。 - fail オブジェクトを装着できませんでした。 最大装着数[MAX_ATTACHMENTS]個を越えています。どれか取り外してからお試しください。 - fail まだ読み込まれていないため、そのアイテムを装着できません。後でやり直してください。 - fail ログインするためにパスワードを入力してください。 - fail アバターのユーザー名を入力してください。 [CURRENT_GRID]に入るにはアカウントが必要です。今すぐアカウントを作成しますか? - [create_account_url] - confirm ユーザー名のフィールドにアバターのユーザー名もしくは氏名を入力してから、再度ログインする必要があります。 - fail 「[GRID]」は、有効なグリッドIDではありません。 - fail ログイン位置で有効なグリッドが指定されませんでした。 - fail クラシファイド広告「[NAME]」を削除してもよろしいですか? 支払い済みの料金は返金されません。 - confirm この顔に紐づけられたメディアの削除を選択しました。 続行してもよろしいですか? - confirm クラシファイド広告「[NAME]」への変更を保存しますか? - confirm クラシファイド広告を出すには、資金が足りません。 - fail クラシファイド広告「<nolink>[CLASSIFIED]</nolink>」を削除してもよろしいですか? - confirm ピック「<nolink>[PICK]</nolink>」を削除してもよろしいですか? - confirm 未公開のクラシファイド広告があります。 ウィンドウを閉じると変更が失われます。 - confirm 保存されていない変更があります。 - confirm - save このアウトフィットを削除してもよろしいですか? - confirm アウトフィット「[NAME]」を削除してもよろしいですか? - confirm [CURRENT_GRID]のイベントWebページに移動しますか? https://secondlife.com/my/community/events/?lang=ja-JP - confirm 表示する提案を選択してください。 - fail 表示する履歴アイテムを選択してください。 - fail - キャッシュは、[APP_NAME]再起動後にクリアされます。 @@ -772,7 +593,6 @@ L$が不足しているので、このグループに参加することができ 新しいスキンは、[APP_NAME]再起動後に反映されます。 変更した色をデフォルトに戻してもよろしいですか? - confirm @@ -780,19 +600,15 @@ L$が不足しているので、このグループに参加することができ [CURRENT_GRID]のWebページに移動し、入札あるいはオークションの詳細を確認しますか? - confirm http://secondlife.com/auctions/auction-detail.php?id=[AUCTION_ID]&lang=ja-JP 変更を保存しますか? - confirm このノートカードを削除してもよろしいですか? - - confirm @@ -803,94 +619,72 @@ L$が不足しているので、このグループに参加することができ ジェスチャーの保存に失敗しました。 ステップ数が多すぎます。 ステップをいくつか削除してから再保存してください - fail ジェスチャーの保存に失敗しました。 少し待ってからもう一度試してください。 - fail ジェスチャーの保存に失敗しました。オブジェクト、または関連するオブジェクトインベントリが見つかりません。 オブジェクトが範囲内に存在しないか、削除された可能性があります。 - fail 以下の理由で、ジェスチャーの保存時に問題が起こりました:[REASON] お手数ですが、後でもう一度試してください。 - fail ノートカードの保存に失敗しました。オブジェクト、または関連するオブジェクトインベントリが見つかりません。 オブジェクトが範囲内に存在しないか、削除された可能性があります。 - fail 以下の理由で、ノートカードの保存時に問題が起こりました:[REASON] お手数ですが、後でもう一度試してください。 - fail あなたのスクリプトのバージョンでは、変更を元に戻すことはできませんでした。 最後にサーバーに保存されたバージョンを読み込みますか? (**警告**:この操作後元に戻すことはできません) - confirm スクリプトの保存に失敗しました。スクリプトが入ったオブジェクトが見つかりません。 オブジェクトは範囲外か、または削除されているかもしれません。 - fail ログイン位置が指定されていません。 ログイン位置の欄にリージョン名を入力するか、「最後にログアウトした場所」か「ホーム」を選択してください。 - fail スクリプトの起動または停止に失敗しました。スクリプトが格納されているオブジェクトが見つかりません。 オブジェクトが範囲内に存在しないか、または削除された可能性があります。 - fail ファイルをダウンロードできません。 - fail ダウンロードをリクエストしたファイルは、[APP_NAME]ではサポートされていません。 - - confirm ファイル[[FILE]]に書き込むことができませんでした。 - fail お使いのコンピュータは、[APP_NAME]の最小システム要件を満たしていません。 このためパフォーマンスが低下する場合があります。恐れ入りますが[SUPPORT_SITE]は、サポートされていないシステム構成に対する技術的サポートを提供できません。 詳細について、[_URL] を確認しますか? - https://wiki.firestormviewer.org/fs_system_requirements - fail グラフィックスチップに最新のドライバがある可能性があります。グラフィックドライバを更新することにより、大幅にパフォーマンスが向上します。 ドライバの更新を確認するために[URL] にアクセスしますか? - - [URL] - - confirm - fail お使いのシステムには、[APP_NAME]が認識できないグラフィックカードが搭載されています。[APP_NAME]でまだテストされていない最新ハードウェアのためだと考えられます。 問題ないとは思いますが、グラフィックの設定を調整する必要があるかもしれません。 (「アバター」>「初期設定」>「グラフィック」) - fail
@@ -900,78 +694,61 @@ L$が不足しているので、このグループに参加することができ ドライバの一般的なエラーを防ぐために、画質が低に設定されます。このため、一部のグラフィック特性に制限が出ます。 お使いのグラフィックカードのドライバをアップデートするようおすすめします。 画質は、「初期設定」>「グラフィック」で設定できます。 - fail
この設定を変更すると、「自動調整」の設定が無効になります。 続けてもよろしいですか? - confirm 高度な光源をオンにするには、品質をレベルを4以上にする必要があります。 - confirm 影を有効にするには、品質をレベルを4以上にする必要があります。 - confirm [REGION]では、地形の変更ができません。 - fail [PARCEL]区画の地形を編集する権限がありません。 - fail 以下のアイテムをコピーする権限がありません: <nolink>[ITEMS]</nolink> 他の人に譲ると、そのアイテムはあなたのインベントリから削除されます。本当にこれらのアイテムを譲ってもよろしいですか? - confirm - fail インベントリのアイテムを渡すことができません。 - fail 取引がキャンセルされました。 - fail 一度に42個以上のアイテムを渡すことはできません。 - fail 選択したアイテムを渡す権限がありません。 - fail あなたは選択した[COUNT]個のアイテムをコピーする権限がありません。 これらのアイテムはあなたのインベントリから失われます。 本当にアイテムを渡してもよろしいですか? - confirm - fail 選択したフォルダを渡す権限がありません。 - fail [AVATAR_NAMES]をフリーズしますか? アバターは一時的に動けなくなり、チャットを含めインワールドで何もできなくなります。 - confirm [AVATAR_NAME]をフリーズしますか? アバターは一時的に動けなくなり、チャットを含めインワールドで何もできなくなります。 - confirm @@ -980,36 +757,30 @@ L$が不足しているので、このグループに参加することができ [RESIDENTS] 彼らは一時的に動けなくなり、チャットを含めインワールドで何もできなくなります。 - confirm あなたの土地から[AVATAR_NAMES]を追放しますか? - confirm あなたの土地からアバターを追放しますか? - confirm [AVATAR_NAMES]をあなたの土地から追放しますか? - confirm 以下の住民らをあなたの土地から追放しますか? [RESIDENTS] - confirm 以下の住民らをあなたの土地から追放しますか? [RESIDENTS] - confirm @@ -1022,36 +793,31 @@ L$が不足しているので、このグループに参加することができ 以下の住民らをこの不動産からキックしますか? [RESIDENTS] - confirm -[AVATAR_NAME]をホームにテレポートさせますか? + [AVATAR_NAME]をホームにテレポートさせますか? [EVIL_USER]のアクセスをこの不動産のみで拒否しますか?それとも[ALL_ESTATES]で拒否しますか? - confirm - 以下の居住者のアクセスをこの不動産のみ拒否しますか?それとも [ALL_ESTATES]で拒否しますか? + 以下の居住者のアクセスをこの不動産のみ拒否しますか?それとも [ALL_ESTATES]で拒否しますか? [RESIDENTS] <nolink>[GROUP_NAME]</nolink>から[AVATAR_NAME]を追放しました。 - group 取得エラー:選択したオブジェクトの数が多すぎます。 - fail 取得エラー:オブジェクトが複数のリージョンにまたがって存在しています。 すべて同じリージョン内に移動させてから取得してください。 - fail [EXTRA] @@ -1062,35 +828,28 @@ L$が不足しているので、このグループに参加することができ [MUTE_LIMIT]エントリの制限に達したため、ブロックリストに新しいエントリを追加できません。 - fail [COUNT]個のオブジェクトをリンクできませんでした。 リンクできるオブジェクトは最大[MAX]個です。 - fail セットが揃っているオブジェクトのみリンクできます。複数のオブジェクトを選択してください。 - fail すべてのオブジェクトの修正許可がないためリンクできません。 どのオブジェクトもロックされておらず、あなたのものであることを確認してください。 - fail リージョンの境界を越えてオブジェクトをリンクできません。 - fail リージョンの境界を越えてオブジェクトをリンクできません。 - fail 所有者が異なるため、オブジェクトをリンクできません。 自分が所有しているオブジェクトだけを選択してください。 - fail ファイル「[FILE]」は、無効な拡張子です。 @@ -1101,99 +860,80 @@ L$が不足しているので、このグループに参加することができ 「[EXTENSION]」は、無効な拡張子です。 有効な拡張子:[VALIDS] - fail 読み込みのためにアップロードされたサウンドファイルを開けません: [FILE] - fail RIFF WAVEファイルとして認識されません: [FILE] - fail PCM WAVEオーディオファイルとして認識されません: [FILE] - fail このファイルのチャンネル数は無効です(モノラルか、ステレオである必要があります): [FILE] - fail このファイルのサンプリングレートはサポートされていません(44.1kである必要があります): [FILE] - fail このファイルのビットサイズはサポートされていません(8bitか、16bitである必要があります): [FILE] - fail WAVヘッダーにデータチャンクが見つかりません: [FILE] - fail WAVファイルのチャンクサイズが間違っています: [FILE] - fail オーディオファイルが長すぎます。(最大[MAX_LENGTH]秒): [FILE] - fail 「[FILE]」に問題があります。 [REASON] - fail 書き込み用の一時圧縮サウンドファイルを開くことができません: [FILE] - fail 不明なVorbisのエンコードに失敗しました: [FILE] - fail 次のファイルのエンコードができません: [FILE] - fail 保存されているログイン認証情報を保存するファイルをデコードできませんでした。この時点で認証情報を保存または削除すると、以前に保存された認証情報はすべて消去されます。 これは、ネットワーク設定を変更した場合に発生する可能性があります。以前のネットワーク設定でビューアを再起動すると、保存されているログイン認証情報を復元できる場合があります。 - fail 破損したリソースファイル: [FILE] - fail 不明なLindenリソースファイルのバージョンです: [FILE] - fail 出力ファイルを作成できません: [FILE] - fail 現在[APP_NAME]では、BVH形式のアニメーションファイルの一括アップロードはサポートされていません。 - fail 受信したフレンドシップリクエストをすべて拒否するモードがオンになっています。 @@ -1210,23 +950,20 @@ L$が不足しているので、このグループに参加することができ [REASON] お手数ですがもう一度やり直してください。 - fail 以下の理由によりスナップショット[FILE]をアップロードできませんでした: [REASON] ファイルが大きすぎる可能性があります。解像度や品質を下げるか、お手数ですがもう一度やり直してください。 - fail スナップショットをアップロードできません。 ファイルが大きすぎる可能性があります。解像度を下げるか、お手数ですがもう一度やり直してください。 - fail -ファイルが開けません。 + ファイルが開けません。 ファイルを開いているときにビューアのメモリが不足しました。ファイルが大きすぎる可能性があります。 @@ -1235,17 +972,13 @@ L$が不足しているので、このグループに参加することができ
この場所のランドマークは、既に持っています。 - fail この土地の所有者が許可していないためランドマークを作成することができませんでした。 - fail - -フォルダ名を入力してください: - confirm + フォルダ名を入力してください: