diff --git a/.github/workflows/build_viewer.yml b/.github/workflows/build_viewer.yml index ff1cc099bd..04d786d107 100644 --- a/.github/workflows/build_viewer.yml +++ b/.github/workflows/build_viewer.yml @@ -316,11 +316,12 @@ jobs: echo "viewer_build=$viewer_build" >> "$GITHUB_OUTPUT" echo "viewer_channel=${{ env.FS_RELEASE_CHAN }}" >> "$GITHUB_OUTPUT" echo "viewer_release_type=${{ env.FS_RELEASE_TYPE }}" >> "$GITHUB_OUTPUT" - # - name: Install node-dump-syms - # if: runner.os == 'Linux' - # run: | - # npm install -g node-pre-gyp - # npm install -g node-dump-syms + + - name: Unzip xcarchive.zip files + if: runner.os == 'macOS' + run: | + find . -name '*.xcarchive.zip' -exec sh -c 'unzip -o "{}" -d "$(dirname "{}")"' \; + - name: Post Bugsplat Symbols uses: BugSplat-Git/symbol-upload@main @@ -348,7 +349,7 @@ jobs: secrets.BUGSPLAT_DEFAULT_DB }} application: "Firestorm-${{ steps.version.outputs.viewer_channel}}" version: ${{ steps.version.outputs.viewer_version }}.${{ steps.version.outputs.viewer_build }} - files: ${{ runner.os == 'Windows' && '**/Release/*.{pdb,exe,dll}' || runner.os == 'macOS' && '**/*.xcarchive.zip' || '**/{do-not-run-directly-firestorm-bin,*.sym}' }} + files: ${{ runner.os == 'Windows' && '**/Release/*.{pdb,exe,dll}' || runner.os == 'macOS' && '**/Release/{Firestorm,*.dSYM}' || '**/{do-not-run-directly-firestorm-bin,*.sym}' }} directory: "build-*" node-version: "20" dumpSyms: false @@ -432,6 +433,31 @@ jobs: run: ls -R working-directory: ${{steps.download.outputs.download-path}} + - name: Create Build Info artifact + env: + FS_VIEWER_CHANNEL: ${{ needs.build_matrix.outputs.viewer_channel }} + FS_VIEWER_VERSION: + FS_VIEWER_BUILD: + FS_VIEWER_RELEASE_TYPE: + id: create_build_info + run: | + cat < build_info.json + { + "build_run_number": "${{ github.run_number }}", + "release_type": "${{ needs.build_matrix.outputs.viewer_release_type }}", + "viewer_version": "${{ needs.build_matrix.outputs.viewer_version }}", + "viewer_build": "${{ needs.build_matrix.outputs.viewer_build }}" + } + EOF + echo "Build info created: $(cat build_info.json)" + + # Upload Build Info Artifact + - name: Upload Tag Info + uses: actions/upload-artifact@v4 + with: + name: build_info + path: build_info.json + - name: Reorganise artifacts ready for server upload. env: FS_VIEWER_CHANNEL: ${{ needs.build_matrix.outputs.viewer_channel }} diff --git a/.github/workflows/deploy_only.yml b/.github/workflows/deploy_only.yml index 433da18767..1374e51c89 100644 --- a/.github/workflows/deploy_only.yml +++ b/.github/workflows/deploy_only.yml @@ -3,10 +3,9 @@ name: Deploy Viewer on: workflow_dispatch: inputs: - build_run_id: - description: 'Workflow Run ID of the build to deploy' + build_run_number: + description: 'GitHub Run Number (per build_viewer.yml workflow)' required: true - default: '' viewer_channel: description: 'viewer_channel' required: true @@ -50,7 +49,7 @@ jobs: id: download with: workflow: build_viewer.yml - run_number: ${{ github.event.inputs.build_run_id }} + run_number: ${{ github.event.inputs.build_run_number }} path: to_deploy - name: Install discord-webhook library run: pip install discord-webhook diff --git a/.github/workflows/tag-fs-build.yml b/.github/workflows/tag-fs-build.yml new file mode 100644 index 0000000000..9b3ef264d7 --- /dev/null +++ b/.github/workflows/tag-fs-build.yml @@ -0,0 +1,164 @@ +name: Tag FS Build + +on: + workflow_run: + workflows: ["Build Viewer"] # Exact name of the build workflow + types: + - completed + workflow_dispatch: + inputs: + build_run_number: + description: 'GitHub Run Number (per build_viewer.yml workflow)' + required: true + release_type: + description: "Type of build" + required: false + type: choice + default: "undefined" + options: + - "undefined" + - "Manual" + - "Nightly" + - "Beta" + - "Alpha" + - "Release" + viewer_version: + description: 'Viewer version' + required: false + default: "undefined" + viewer_build: + description: 'Viewer build number' + required: false + default: "undefined" + dry_run: + description: 'Execute as dry run (no tag will be created)' + required: false + default: 'true' + +permissions: + contents: write # Grants write access to repository contents + +jobs: + tag-build: + # Run the job only if: + # - The event is workflow_run **and** the conclusion is success + # OR + # - The event is workflow_dispatch (manual trigger) + if: ${{ ( github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' ) || ( github.event_name == 'workflow_dispatch' ) }} + runs-on: ubuntu-latest + + steps: + # Checkout the Repository + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Necessary to fetch all history for tagging + + # Install jq for JSON Parsing + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Get Run Number + id: get_run_number + run: | + if [ ${{ github.event_name == 'workflow_run' }} ]; then + echo "Triggered by workflow_run event." + echo "Using run number of triggering workflow" + echo "build_run_number=${{ github.event.inputs.build_run_number }}" >> "$GITHUB_OUTPUT" + elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "Triggered by workflow_dispatch event." + echo "Using run number of manual dispatch" + echo "build_run_number=${{ github.event.inputs.build_run_number }}" >> "$GITHUB_OUTPUT" + fi + - name: Download Build Artifacts + uses: dawidd6/action-download-artifact@v6 + id: download_build_info + with: + workflow: build_viewer.yml + run_number: ${{ steps.get_run_number.outputs.build_run_number }} + name: build_info + skip_unpack: false + path: build_info + + # workout the inputs based on the trigger type + - name: Query the json and get input overrides if applicable + id: get_inputs + run: | + # Parse the JSON file + RELEASE_TYPE=$(jq -r '.release_type' build_info/build_info.json) + VIEWER_VERSION=$(jq -r '.viewer_version' build_info/build_info.json) + VIEWER_BUILD=$(jq -r '.viewer_build' build_info/build_info.json) + DRY_RUN=false + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "Triggered by workflow_dispatch event." + echo "Allowing manual inputs to override." + if [ "${{ github.event.inputs.release_type }}" != "undefined" ]; then + RELEASE_TYPE=${{ github.event.inputs.release_type }} + fi + if [ "${{ github.event.inputs.viewer_version }}" != "undefined" ]; then + VIEWER_VERSION=${{ github.event.inputs.viewer_version }} + fi + if [ "${{ github.event.inputs.viewer_build }}" != "undefined" ]; then + VIEWER_BUILD=${{ github.event.inputs.viewer_build }} + fi + DRY_RUN=${{ github.event.inputs.dry_run }} + fi + # Set outputs + echo "build_run_number=$BUILD_RUN_NUMBER" >> $GITHUB_OUTPUT + echo "release_type=$RELEASE_TYPE" >> $GITHUB_OUTPUT + echo "viewer_version=$VIEWER_VERSION" >> $GITHUB_OUTPUT + echo "viewer_build=$VIEWER_BUILD" >> $GITHUB_OUTPUT + echo "dry_run=$DRY_RUN" >> $GITHUB_OUTPUT + shell: bash + + # Check if Tag Already Exists + - name: Determine tag + id: get_tag + run: | + TAG_NAME="Firestorm_${{ steps.get_inputs.outputs.release_type }}_${{ steps.get_inputs.outputs.viewer_version }}.${{ steps.get_inputs.outputs.viewer_build }}" + echo "Proposed Tag: $TAG_NAME" + echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT + + - name: Check if tag exists + id: check_tag + run: | + if git rev-parse "${{ steps.get_tag.outputs.tag_name }}" >/dev/null 2>&1; then + echo "Tag ${{ steps.get_tag.outputs.tag_name }} already exists." + echo "tag_exists=true" >> $GITHUB_OUTPUT + else + echo "Tag ${{ steps.get_tag.outputs.tag_name }} does not exist." + echo "tag_exists=false" >> $GITHUB_OUTPUT + fi + shell: bash + + # Create the Tag (Conditional) + - name: Create tag + if: > + ${{ steps.get_inputs.outputs.dry_run != 'true' && + steps.check_tag.outputs.tag_exists == 'false' }} + run: | + echo "Creating tag: ${{ steps.get_tag.outputs.tag_name }}" + git tag ${{ steps.get_tag.outputs.tag_name }} + shell: bash + + # Push the Tag to the Repository (Conditional) + - name: Push tag + if: > + ${{ steps.get_inputs.outputs.dry_run != 'true' && + steps.check_tag.outputs.tag_exists == 'false' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git push origin "${{ steps.get_tag.outputs.tag_name }}" + + # Tagging Confirmation + - name: Tagging Confirmation + run: | + if [ "${{ steps.get_inputs.outputs.dry_run }}" == "true" ]; then + echo "Dry run mode enabled. Tag '$TAG_NAME' was not created or pushed." + elif [ "${{ steps.check_tag.outputs.tag_exists }}" == "true" ]; then + echo "Tag '${{ steps.get_tag.outputs.tag_name }}' already exists. No new tag was created." + else + echo "Tag '${{ steps.get_tag.outputs.tag_name }}' has been created and pushed successfully." + fi + shell: bash diff --git a/fsutils/download_list.py b/fsutils/download_list.py index 81628ed71f..7b89ed314a 100644 --- a/fsutils/download_list.py +++ b/fsutils/download_list.py @@ -343,7 +343,7 @@ def update_fs_version_mgr(build_info, config): sys.exit(1) secret_for_api = generate_secret(secret_key) - build_type = build_info["build_type"] + build_type = build_info["build_type"].lower() version = os.environ.get('FS_VIEWER_VERSION') channel = os.environ.get('FS_VIEWER_CHANNEL') build_number = os.environ.get('FS_VIEWER_BUILD') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f5137fe83e..d085e748e4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -846,6 +846,7 @@ set(viewer_SOURCE_FILES noise.cpp particleeditor.cpp permissionstracker.cpp + pieautohide.cpp piemenu.cpp pieseparator.cpp pieslice.cpp @@ -1647,6 +1648,7 @@ set(viewer_HEADER_FILES noise.h particleeditor.h permissionstracker.h + pieautohide.h piemenu.h pieseparator.h pieslice.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8455b19c7e..17c40631ac 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -6645,7 +6645,7 @@ Type String Value - https://status.secondlifegrid.net/history.rss + https://status.secondlifegrid.net/history.atom GridStatusUpdateDelay diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 1f2d603de3..cf036eea49 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -5680,6 +5680,7 @@ void FSPanelPreferenceBackup:: doRestoreSettings(const LLSD& notification, const // start clean LL_INFOS("SettingsBackup") << "clearing global settings" << LL_ENDL; gSavedSettings.resetToDefaults(); + LLFeatureManager::getInstance()->applyRecommendedSettings(); // run restore on global controls LL_INFOS("SettingsBackup") << "restoring global settings from backup" << LL_ENDL; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 89e3e0bc37..be1cf93823 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -2075,7 +2075,10 @@ bool is_inventorysp_active() } // static -LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(bool auto_open) +// [FIRE-34532] Bugsplat Crash when uploading assets with non-tabbed inventory window active. +// LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(bool auto_open) +LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(bool auto_open, bool ignore_secondary) +// { S32 z_min = S32_MAX; LLInventoryPanel* res = NULL; @@ -2236,7 +2239,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(bool auto_open, const L } // - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open, true);// [FIRE-34532] ignore secondary folder views that are probably single folder if (active_panel) { LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id << LL_ENDL; @@ -2261,15 +2264,18 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(bool auto_open, const L } else if (auto_open) { - // FIRE-22167: Make "Show in Main View" work properly - //LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + // [FIRE-34532] make sure the active panel has a tabbed view and avoid crash if (use_main_panel) { - active_panel->getParentByType()->selectFirstTab(); - active_panel = getActiveInventoryPanel(false); + if (auto* tab_container = active_panel->getParentByType()) + { + tab_container->selectFirstTab(); + active_panel = getActiveInventoryPanel(false, true); + floater_inventory = active_panel->getParentByType(); + } } - LLFloater* floater_inventory = active_panel->getParentByType(); - // + // if (floater_inventory) { floater_inventory->setFocus(true); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 3d93c5c973..9496871856 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -258,8 +258,10 @@ public: // Find whichever inventory panel is active / on top. // "Auto_open" determines if we open an inventory panel if none are open. - static LLInventoryPanel *getActiveInventoryPanel(bool auto_open = true); - + // FIRE-34532: don't try top open snapshots in the secondary windows. + // static LLInventoryPanel *getActiveInventoryPanel(bool auto_open = true); + static LLInventoryPanel* getActiveInventoryPanel(bool auto_open = true, bool ignore_secondary = false); + // static void openInventoryPanelAndSetSelection(bool auto_open, const LLUUID& obj_id, bool use_main_panel = false, diff --git a/indra/newview/llpanelsnapshot.cpp b/indra/newview/llpanelsnapshot.cpp index 7c898b0678..095308fc50 100644 --- a/indra/newview/llpanelsnapshot.cpp +++ b/indra/newview/llpanelsnapshot.cpp @@ -37,6 +37,7 @@ // newview #include "llsidetraypanelcontainer.h" +#include "llsnapshotlivepreview.h" #include "llviewercontrol.h" // gSavedSettings #include "llagentbenefits.h" @@ -108,6 +109,17 @@ void LLPanelSnapshot::onOpen(const LLSD& key) { getParentByType()->notify(LLSD().with("image-format-change", true)); } + + // If resolution is set to "Current Window", force a snapshot update + // each time a snapshot panel is opened to determine the correct + // image size (and upload fee) depending on the snapshot type. + if (mSnapshotFloater && getChild(getImageSizeComboName())->getValue().asString() == "[i0,i0]") + { + if (LLSnapshotLivePreview* preview = mSnapshotFloater->getPreviewView()) + { + preview->mForceUpdateSnapshot = true; + } + } } LLSnapshotModel::ESnapshotFormat LLPanelSnapshot::getImageFormat() const diff --git a/indra/newview/llpanelsnapshotinventory.cpp b/indra/newview/llpanelsnapshotinventory.cpp index 1c05b59568..97ca2666e2 100644 --- a/indra/newview/llpanelsnapshotinventory.cpp +++ b/indra/newview/llpanelsnapshotinventory.cpp @@ -48,78 +48,36 @@ /** * The panel provides UI for saving snapshot as an inventory texture. */ -class LLPanelSnapshotInventoryBase - : public LLPanelSnapshot -{ - LOG_CLASS(LLPanelSnapshotInventoryBase); - -public: - LLPanelSnapshotInventoryBase(); - - /*virtual*/ bool postBuild(); -protected: - void onSend(); - /*virtual*/ LLSnapshotModel::ESnapshotType getSnapshotType(); -}; - class LLPanelSnapshotInventory - : public LLPanelSnapshotInventoryBase + : public LLPanelSnapshot { LOG_CLASS(LLPanelSnapshotInventory); public: LLPanelSnapshotInventory(); - /*virtual*/ ~LLPanelSnapshotInventory(); // Store settings at logout - /*virtual*/ bool postBuild(); - /*virtual*/ void onOpen(const LLSD& key); + ~LLPanelSnapshotInventory() override; // Store settings at logout + bool postBuild() override; + void onOpen(const LLSD& key) override; void onResolutionCommit(LLUICtrl* ctrl); private: - /*virtual*/ std::string getWidthSpinnerName() const { return "inventory_snapshot_width"; } - /*virtual*/ std::string getHeightSpinnerName() const { return "inventory_snapshot_height"; } - /*virtual*/ std::string getAspectRatioCBName() const { return "inventory_keep_aspect_check"; } - /*virtual*/ std::string getImageSizeComboName() const { return "texture_size_combo"; } - /*virtual*/ std::string getImageSizePanelName() const { return LLStringUtil::null; } - /*virtual*/ void updateControls(const LLSD& info); + std::string getWidthSpinnerName() const override { return "inventory_snapshot_width"; } + std::string getHeightSpinnerName() const override { return "inventory_snapshot_height"; } + std::string getAspectRatioCBName() const override { return "inventory_keep_aspect_check"; } + std::string getImageSizeComboName() const override { return "texture_size_combo"; } + std::string getImageSizePanelName() const override { return LLStringUtil::null; } + LLSnapshotModel::ESnapshotType getSnapshotType() override; + void updateControls(const LLSD& info) override; -}; - -class LLPanelOutfitSnapshotInventory - : public LLPanelSnapshotInventoryBase -{ - LOG_CLASS(LLPanelOutfitSnapshotInventory); - -public: - LLPanelOutfitSnapshotInventory(); - /*virtual*/ bool postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - -private: - /*virtual*/ std::string getWidthSpinnerName() const { return ""; } - /*virtual*/ std::string getHeightSpinnerName() const { return ""; } - /*virtual*/ std::string getAspectRatioCBName() const { return ""; } - /*virtual*/ std::string getImageSizeComboName() const { return "texture_size_combo"; } - /*virtual*/ std::string getImageSizePanelName() const { return LLStringUtil::null; } - /*virtual*/ void updateControls(const LLSD& info); - - /*virtual*/ void cancel(); + void onSend(); + void updateUploadCost(); + S32 calculateUploadCost(); }; static LLPanelInjector panel_class1("llpanelsnapshotinventory"); -static LLPanelInjector panel_class2("llpaneloutfitsnapshotinventory"); - -LLPanelSnapshotInventoryBase::LLPanelSnapshotInventoryBase() -{ -} - -bool LLPanelSnapshotInventoryBase::postBuild() -{ - return LLPanelSnapshot::postBuild(); -} - -LLSnapshotModel::ESnapshotType LLPanelSnapshotInventoryBase::getSnapshotType() +LLSnapshotModel::ESnapshotType LLPanelSnapshotInventory::getSnapshotType() { return LLSnapshotModel::SNAPSHOT_TEXTURE; } @@ -130,6 +88,15 @@ LLPanelSnapshotInventory::LLPanelSnapshotInventory() mCommitCallbackRegistrar.add("Inventory.Cancel", boost::bind(&LLPanelSnapshotInventory::cancel, this)); } +// Store settings at logout +LLPanelSnapshotInventory::~LLPanelSnapshotInventory() +{ + gSavedSettings.setS32("LastSnapshotToInventoryResolution", getImageSizeComboBox()->getCurrentIndex()); + gSavedSettings.setS32("LastSnapshotToInventoryWidth", getTypedPreviewWidth()); + gSavedSettings.setS32("LastSnapshotToInventoryHeight", getTypedPreviewHeight()); +} +// + // virtual bool LLPanelSnapshotInventory::postBuild() { @@ -145,15 +112,16 @@ bool LLPanelSnapshotInventory::postBuild() getHeightSpinner()->setValue(gSavedSettings.getS32("LastSnapshotToInventoryHeight")); // - return LLPanelSnapshotInventoryBase::postBuild(); + return LLPanelSnapshot::postBuild(); } // virtual void LLPanelSnapshotInventory::onOpen(const LLSD& key) { + updateUploadCost(); + // FIRE-10537 - Temp texture uploads aren't functional on SSB regions - if (LLAgentBenefitsMgr::current().getTextureUploadCost() == 0 - || gAgent.getRegion()->getCentralBakeVersion() > 0) + if (LLAgentBenefitsMgr::current().getTextureUploadCost() == 0 || gAgent.getRegion()->getCentralBakeVersion() > 0) { gSavedSettings.setBOOL("TemporaryUpload", false); } @@ -167,6 +135,8 @@ void LLPanelSnapshotInventory::updateControls(const LLSD& info) { const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true; getChild("save_btn")->setEnabled(have_snapshot); + + updateUploadCost(); } void LLPanelSnapshotInventory::onResolutionCommit(LLUICtrl* ctrl) @@ -176,30 +146,9 @@ void LLPanelSnapshotInventory::onResolutionCommit(LLUICtrl* ctrl) getChild(getHeightSpinnerName())->setVisible(!current_window_selected); } -// Store settings at logout -LLPanelSnapshotInventory::~LLPanelSnapshotInventory() +void LLPanelSnapshotInventory::onSend() { - gSavedSettings.setS32("LastSnapshotToInventoryResolution", getImageSizeComboBox()->getCurrentIndex()); - gSavedSettings.setS32("LastSnapshotToInventoryWidth", getTypedPreviewWidth()); - gSavedSettings.setS32("LastSnapshotToInventoryHeight", getTypedPreviewHeight()); -} -// - -void LLPanelSnapshotInventoryBase::onSend() -{ - S32 w = 0; - S32 h = 0; - - if( mSnapshotFloater ) - { - LLSnapshotLivePreview* preview = mSnapshotFloater->getPreviewView(); - if( preview ) - { - preview->getSize(w, h); - } - } - - S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(w, h); + S32 expected_upload_cost = calculateUploadCost(); if (can_afford_transaction(expected_upload_cost)) { if (mSnapshotFloater) @@ -220,36 +169,24 @@ void LLPanelSnapshotInventoryBase::onSend() } } -LLPanelOutfitSnapshotInventory::LLPanelOutfitSnapshotInventory() +void LLPanelSnapshotInventory::updateUploadCost() { - mCommitCallbackRegistrar.add("Inventory.SaveOutfitPhoto", boost::bind(&LLPanelOutfitSnapshotInventory::onSend, this)); - mCommitCallbackRegistrar.add("Inventory.SaveOutfitCancel", boost::bind(&LLPanelOutfitSnapshotInventory::cancel, this)); + getChild("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", calculateUploadCost())); } -// virtual -bool LLPanelOutfitSnapshotInventory::postBuild() +S32 LLPanelSnapshotInventory::calculateUploadCost() { - return LLPanelSnapshotInventoryBase::postBuild(); -} + S32 w = 0; + S32 h = 0; -// virtual -void LLPanelOutfitSnapshotInventory::onOpen(const LLSD& key) -{ - getChild("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost())); - LLPanelSnapshot::onOpen(key); -} - -// virtual -void LLPanelOutfitSnapshotInventory::updateControls(const LLSD& info) -{ - const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true; - getChild("save_btn")->setEnabled(have_snapshot); -} - -void LLPanelOutfitSnapshotInventory::cancel() -{ if (mSnapshotFloater) { - mSnapshotFloater->closeFloater(); + if (LLSnapshotLivePreview* preview = mSnapshotFloater->getPreviewView()) + { + w = preview->getEncodedImageWidth(); + h = preview->getEncodedImageHeight(); + } } + + return LLAgentBenefitsMgr::current().getTextureUploadCost(w, h); } diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp index a9f1d0b8ba..bcc0460ceb 100644 --- a/indra/newview/llpanelsnapshotoptions.cpp +++ b/indra/newview/llpanelsnapshotoptions.cpp @@ -30,13 +30,9 @@ #include "llsidetraypanelcontainer.h" #include "llfloatersnapshot.h" // FIXME: create a snapshot model -#include "llsnapshotlivepreview.h" #include "llfloaterreg.h" #include "llfloaterflickr.h" // Share to Flickr -#include "llagentbenefits.h" - - /** * Provides several ways to save a snapshot. */ @@ -47,9 +43,7 @@ class LLPanelSnapshotOptions public: LLPanelSnapshotOptions(); - ~LLPanelSnapshotOptions(); - /*virtual*/ bool postBuild(); - /*virtual*/ void onOpen(const LLSD& key); + bool postBuild() override; private: void updateUploadCost(); @@ -74,10 +68,6 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions() mCommitCallbackRegistrar.add("Snapshot.SendToFlickr", boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this)); // Share to Flickr } -LLPanelSnapshotOptions::~LLPanelSnapshotOptions() -{ -} - // virtual bool LLPanelSnapshotOptions::postBuild() { @@ -85,30 +75,6 @@ bool LLPanelSnapshotOptions::postBuild() return LLPanel::postBuild(); } -// virtual -void LLPanelSnapshotOptions::onOpen(const LLSD& key) -{ - updateUploadCost(); -} - -void LLPanelSnapshotOptions::updateUploadCost() -{ - S32 w = 0; - S32 h = 0; - - if( mSnapshotFloater ) - { - LLSnapshotLivePreview* preview = mSnapshotFloater->getPreviewView(); - if( preview ) - { - preview->getSize(w, h); - } - } - - S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(w, h); - getChild("save_to_inventory_btn")->setLabelArg("[AMOUNT]", llformat("%d", upload_cost)); -} - void LLPanelSnapshotOptions::openPanel(const std::string& panel_name) { LLSideTrayPanelContainer* parent = dynamic_cast(getParent()); diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 424eb214ff..b95073378a 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -988,7 +988,7 @@ void LLViewerAssetUpload::AssetInventoryUploadCoproc(LLCoreHttpUtil::HttpCorouti // Show the preview panel for textures and sounds to let // user know that the image (or snapshot) arrived intact. - LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel(false); + LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel(false, true); // pick the main view not a secondary view. // Use correct inventory floater for showing the upload if (!panel) { diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index ad38c3f3f6..1ab64a2b0c 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1000,13 +1000,13 @@ bool LLViewerTexture::isLargeImage() return (S32)mTexelsPerImage > LLViewerTexture::sMinLargeImageSize; } -bool LLViewerTexture::isInvisiprim() +bool LLViewerTexture::isInvisiprim() const { return isInvisiprim(mID); } //static -bool LLViewerTexture::isInvisiprim(LLUUID id) +bool LLViewerTexture::isInvisiprim(const LLUUID& id) { return (id == sInvisiprimTexture1) || (id == sInvisiprimTexture2); } diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 32111346f7..771d7bbf5e 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -167,8 +167,8 @@ public: const ll_volume_list_t* getVolumeList(U32 channel) const { return &mVolumeList[channel]; } bool isLargeImage() ; - bool isInvisiprim() ; - static bool isInvisiprim(LLUUID id) ; + bool isInvisiprim() const; + static bool isInvisiprim(const LLUUID& id); void setParcelMedia(LLViewerMediaTexture* media) {mParcelMedia = media;} bool hasParcelMedia() const { return mParcelMedia != NULL;} diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index f5f6643fd0..50ed52b48f 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -1179,6 +1179,7 @@ void LLVOTree::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) mDrawable->setPositionGroup(pos); LLFace* facep = mDrawable->getFace(0); + if (!facep) return; // FIRE-34565 Repeatable Bugsplat crash while driving on mainland. facep->mExtents[0] = newMin; facep->mExtents[1] = newMax; } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 54fb270382..8c05fdb9df 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -6568,7 +6568,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace { U8 cur_tex = 0; facep->setTextureIndex(cur_tex); - if (texture_count < MAX_TEXTURE_COUNT) + if (texture_count < MAX_TEXTURE_COUNT && tex) // [FIRE-34534] guard additional cases of tex == null { texture_list[texture_count++] = tex; } @@ -6622,7 +6622,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace // Quick hack test of proper batching logic // if (texture_count < MAX_TEXTURE_COUNT) // only add to the batch if this is a new texture - if (cur_tex == texture_count && texture_count < MAX_TEXTURE_COUNT) + if (cur_tex == texture_count && texture_count < MAX_TEXTURE_COUNT && tex) // [FIRE-34534] guard additional cases of tex == null // { texture_list[texture_count++] = tex; @@ -6999,7 +6999,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace && te->getShiny() && can_be_shiny) { //shiny - if (tex->getPrimaryFormat() == GL_ALPHA) + if (tex && tex->getPrimaryFormat() == GL_ALPHA) // [FIRE-34534] guard additional cases of tex == null { //invisiprim+shiny registerFace(group, facep, LLRenderPass::PASS_INVISI_SHINY); registerFace(group, facep, LLRenderPass::PASS_INVISIBLE); @@ -7036,7 +7036,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } else { //not alpha and not shiny - if (!is_alpha && tex->getPrimaryFormat() == GL_ALPHA) + if (!is_alpha && tex && tex->getPrimaryFormat() == GL_ALPHA) // FIRE-34540 bugsplat crash caused by tex==nullptr. This stops the crash, but should we continue and leave the face unregistered instead of falling through? { //invisiprim registerFace(group, facep, LLRenderPass::PASS_INVISIBLE); } diff --git a/indra/newview/pieautohide.cpp b/indra/newview/pieautohide.cpp new file mode 100644 index 0000000000..c8d64f822d --- /dev/null +++ b/indra/newview/pieautohide.cpp @@ -0,0 +1,45 @@ +/** + * @file pieautohide.cpp + * @brief Pie menu autohide base class + * + * $LicenseInfo:firstyear=2024&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2024, The Phoenix Firestorm Project, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "pieautohide.h" + + +PieAutohide::PieAutohide(bool autohide, bool startAutohide) : mAutohide(autohide), mStartAutohide(startAutohide) {} + +// accessor +bool PieAutohide::getStartAutohide() const +{ + return mStartAutohide; +} + +// accessor +bool PieAutohide::getAutohide() const +{ + return mStartAutohide || mAutohide; +} diff --git a/indra/newview/pieautohide.h b/indra/newview/pieautohide.h new file mode 100644 index 0000000000..041df6d32d --- /dev/null +++ b/indra/newview/pieautohide.h @@ -0,0 +1,48 @@ +/** + * @file pieautohide.h + * @brief Pie menu autohide base class + * + * $LicenseInfo:firstyear=2024&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2024, The Phoenix Firestorm Project, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ + +#ifndef PIEAUTOHIDE_H +#define PIEAUTOHIDE_H + +#include "lluictrl.h" + +// A slice in the pie that supports the auto-hide function. +class PieAutohide +{ +public: + PieAutohide(bool autohide, bool startAutohide); + + // accessor to expose the autohide feature + bool getStartAutohide() const; + bool getAutohide() const; + +protected: + bool mStartAutohide{ false }; + bool mAutohide{ false }; +}; + +#endif // PIEAUTOHIDE_H diff --git a/indra/newview/piemenu.cpp b/indra/newview/piemenu.cpp index a8d62c180d..1147cd13a4 100644 --- a/indra/newview/piemenu.cpp +++ b/indra/newview/piemenu.cpp @@ -56,8 +56,9 @@ constexpr F32 PIE_OUTER_SHADE_FACTOR = 1.09f; // size factor of the outer shad constexpr F32 PIE_SLICE_DIVIDER_WIDTH = 0.04f; // width of a slice divider in radians constexpr F32 PIE_MAX_SLICES_F = F32(PIE_MAX_SLICES); -PieMenu::PieMenu(const LLMenuGL::Params& p) : +PieMenu::PieMenu(const Params& p) : LLMenuGL(p), + PieAutohide(p.autohide, p.start_autohide), mCurrentSegment(-1), mOldSlice(nullptr), mSlice(nullptr), @@ -305,22 +306,21 @@ void PieMenu::draw() gl_washer_2d(PIE_OUTER_SIZE * factor, PIE_INNER_SIZE, steps, bgColor, borderColor); // set up an item list iterator to point at the beginning of the item list - slice_list_t::iterator cur_item_iter; - cur_item_iter = mSlices->begin(); + slice_list_t::iterator cur_item_iter{ mSlices->begin() }; // clear current slice pointer mSlice = nullptr; // current slice number is 0 - S32 num = 0; - bool wasAutohide = false; + S32 num{ 0 }; + bool wasAutohide{ false }; do { // standard item text color LLColor4 itemColor = textColor; // clear the label and set up the starting angle to draw in - std::string label(""); + std::string label{ "" }; F32 segmentStart = F_PI / (PIE_MAX_SLICES_F / 2.f) * (F32)num - F_PI / PIE_MAX_SLICES_F; // iterate through the list of slices @@ -328,16 +328,76 @@ void PieMenu::draw() { // get current slice item LLView* item = (*cur_item_iter); - - // check if this is a submenu or a normal click slice - PieSlice* currentSlice = dynamic_cast(item); - PieMenu* currentSubmenu = dynamic_cast(item); - // advance internally to the next slice item cur_item_iter++; + bool isSliceOrSubmenu{ false }; + + auto checkAutohide = [&](PieAutohide* autohideSlice) + { + // if the current slice is the start of an autohide chain, clear out previous chains + if (autohideSlice->getStartAutohide()) + { + wasAutohide = false; + } + + // check if the current slice is part of an autohide chain + if (autohideSlice->getAutohide()) + { + // if the previous item already won the autohide, skip this item + if (wasAutohide) + { + return true; + } + + // look at the next item in the pie + LLView* lookAhead = (*cur_item_iter); + // check if this is a normal click slice + if (PieSlice* lookSlice = dynamic_cast(lookAhead)) + { + // if the next item is part of the current autohide chain as well ... + if (lookSlice->getAutohide() && !lookSlice->getStartAutohide()) + { + // ... it's visible and it's enabled, skip the current one. + // the first visible and enabled item in autohide chains wins + // this is useful for Sit/Stand toggles + lookSlice->updateEnabled(); + lookSlice->updateVisible(); + if (lookSlice->getVisible() && lookSlice->getEnabled()) + { + return true; + } + + // this item won the autohide contest + wasAutohide = true; + } + } + else if (PieMenu* lookSlice = dynamic_cast(lookAhead)) + { + if (lookSlice->getAutohide() && !lookSlice->getStartAutohide()) + { + if (/*lookSlice->getVisible() &&*/ lookSlice->getEnabled()) // Menu is somehow always set to not visible... + { + return true; + } + + // this item won the autohide contest + wasAutohide = true; + } + } + } + else + { + // reset autohide chain + wasAutohide = false; + } + + return false; + }; // in case it is regular click slice - if (currentSlice) + if (PieSlice* currentSlice = dynamic_cast(item)) { + isSliceOrSubmenu = true; + // get the slice label and tell the slice to check if it's supposed to be visible label = currentSlice->getLabel(); currentSlice->updateVisible(); @@ -350,50 +410,8 @@ void PieMenu::draw() label = ""; } - // if the current slice is the start of an autohide chain, clear out previous chains - if (currentSlice->getStartAutohide()) - { - wasAutohide = false; - } - - // check if the current slice is part of an autohide chain - if (currentSlice->getAutohide()) - { - // if the previous item already won the autohide, skip this item - if (wasAutohide) - { - continue; - } - - // look at the next item in the pie - LLView* lookAhead = (*cur_item_iter); - // check if this is a normal click slice - PieSlice* lookSlice = dynamic_cast(lookAhead); - if (lookSlice) - { - // if the next item is part of the current autohide chain as well ... - if (lookSlice->getAutohide() && !lookSlice->getStartAutohide()) - { - // ... it's visible and it's enabled, skip the current one. - // the first visible and enabled item in autohide chains wins - // this is useful for Sit/Stand toggles - lookSlice->updateEnabled(); - lookSlice->updateVisible(); - if (lookSlice->getVisible() && lookSlice->getEnabled()) - { - continue; - } - - // this item won the autohide contest - wasAutohide = true; - } - } - } - else - { - // reset autohide chain - wasAutohide = false; - } + if (checkAutohide(currentSlice)) + continue; // check if the slice is currently enabled currentSlice->updateEnabled(); @@ -404,9 +422,14 @@ void PieMenu::draw() itemColor %= 0.3f; } } - // if it's a submenu just get the label - else if (currentSubmenu) + // if it's a submenu + else if (PieMenu* currentSubmenu = dynamic_cast(item)) { + isSliceOrSubmenu = true; + + if (checkAutohide(currentSubmenu)) + continue; + label = currentSubmenu->getLabel(); if (sPieMenuOuterRingShade) { @@ -415,7 +438,7 @@ void PieMenu::draw() } // if it's a slice or submenu, the mouse pointer is over the same segment as our counter and the item is enabled - if ((currentSlice || currentSubmenu) && (mCurrentSegment == num) && item->getEnabled()) + if (isSliceOrSubmenu && (mCurrentSegment == num) && item->getEnabled()) { // memorize the currently highlighted slice for later mSlice = item; diff --git a/indra/newview/piemenu.h b/indra/newview/piemenu.h index 40a0f0dbbe..55e2c5f70a 100644 --- a/indra/newview/piemenu.h +++ b/indra/newview/piemenu.h @@ -30,6 +30,7 @@ #include "llmenugl.h" #include "llframetimer.h" +#include "pieautohide.h" constexpr S32 PIE_MAX_SLICES = 8; @@ -39,15 +40,21 @@ struct PieChildRegistry : public LLChildRegistry LLSINGLETON_EMPTY_CTOR(PieChildRegistry); }; -class PieMenu : public LLMenuGL +class PieMenu : public LLMenuGL, public PieAutohide { public: // parameter block for the XUI factory struct Params : public LLInitParam::Block { Optional name; + // autohide feature to hide a disabled pie slice + Optional start_autohide; + // next item in an autohide chain + Optional autohide; - Params() + Params() : + start_autohide("start_autohide", false), + autohide("autohide", false) { visible = false; } @@ -56,7 +63,7 @@ public: // PieChildRegistry contains a list of allowed child types for the XUI definition typedef PieChildRegistry child_registry_t; - PieMenu(const LLMenuGL::Params& p); + PieMenu(const Params& p); /*virtual*/ void setVisible(bool visible); diff --git a/indra/newview/pieslice.cpp b/indra/newview/pieslice.cpp index 77ab4f1b96..83a037aa08 100644 --- a/indra/newview/pieslice.cpp +++ b/indra/newview/pieslice.cpp @@ -43,9 +43,8 @@ PieSlice::Params::Params() : // create a new slice and memorize the XUI parameters PieSlice::PieSlice(const PieSlice::Params& p) : LLUICtrl(p), + PieAutohide(p.autohide, p.start_autohide), mLabel(p.label), - mStartAutohide(p.start_autohide), - mAutohide(p.autohide), mCheckEnableOnce(p.check_enable_once), mDoUpdateEnabled(true) { @@ -140,18 +139,6 @@ void PieSlice::setLabel(std::string_view newLabel) mLabel = newLabel; } -// accessor -bool PieSlice::getStartAutohide() const -{ - return mStartAutohide; -} - -// accessor -bool PieSlice::getAutohide() const -{ - return mStartAutohide || mAutohide; -} - void PieSlice::resetUpdateEnabledCheck() { mDoUpdateEnabled = true; diff --git a/indra/newview/pieslice.h b/indra/newview/pieslice.h index 0643119a53..2f36447d35 100644 --- a/indra/newview/pieslice.h +++ b/indra/newview/pieslice.h @@ -29,10 +29,11 @@ #define PIESLICE_H #include "lluictrl.h" +#include "pieautohide.h" // A slice in the pie. Does nothing by itself, just stores the function and // parameter to be execued when the user clicks on this item -class PieSlice : public LLUICtrl +class PieSlice : public LLUICtrl, public PieAutohide { public: // parameter block for the XUI factory @@ -70,10 +71,6 @@ public: LLSD getValue() const; void setValue(const LLSD& value); - // accessor to expose the autohide feature - bool getStartAutohide() const; - bool getAutohide() const; - // callback connection for the onCommit method to launch the specified function boost::signals2::connection setClickCallback(const commit_signal_t::slot_type& cb) { @@ -91,8 +88,6 @@ public: protected: // accessor store std::string mLabel; - bool mStartAutohide; - bool mAutohide; bool mCheckEnableOnce; bool mDoUpdateEnabled; diff --git a/indra/newview/skins/default/xui/az/menu_pie_object.xml b/indra/newview/skins/default/xui/az/menu_pie_object.xml index cacb9c7d1d..6893e78964 100644 --- a/indra/newview/skins/default/xui/az/menu_pie_object.xml +++ b/indra/newview/skins/default/xui/az/menu_pie_object.xml @@ -6,12 +6,14 @@ - + + + + - diff --git a/indra/newview/skins/default/xui/az/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/az/panel_snapshot_inventory.xml index 1cd9e22138..e80344c307 100644 --- a/indra/newview/skins/default/xui/az/panel_snapshot_inventory.xml +++ b/indra/newview/skins/default/xui/az/panel_snapshot_inventory.xml @@ -7,7 +7,7 @@ Tekstur kimi saxlamaq üçün kvadrat formatlarından birini seçin. - + diff --git a/indra/newview/skins/default/xui/az/panel_snapshot_options.xml b/indra/newview/skins/default/xui/az/panel_snapshot_options.xml index cd850967db..a53d5f7c10 100644 --- a/indra/newview/skins/default/xui/az/panel_snapshot_options.xml +++ b/indra/newview/skins/default/xui/az/panel_snapshot_options.xml @@ -5,7 +5,7 @@