summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/parameters/setup-macports.yaml25
-rw-r--r--.github/workflows/build-macos.yml123
-rw-r--r--.github/workflows/build-windows.yml225
-rw-r--r--.github/workflows/build.yaml530
-rw-r--r--.github/workflows/check-pr.yaml21
-rw-r--r--.github/workflows/cla.yaml26
-rw-r--r--.github/workflows/label.yaml15
-rw-r--r--.github/workflows/pre-commit.yaml18
-rw-r--r--.github/workflows/qatest.yaml610
-rw-r--r--.github/workflows/stale.yaml25
-rw-r--r--.github/workflows/tag-release.yaml72
-rw-r--r--README.md12
-rw-r--r--autobuild.xml2
-rw-r--r--indra/cmake/00-Common.cmake4
-rw-r--r--indra/cmake/Boost.cmake18
-rw-r--r--indra/cmake/CEFPlugin.cmake178
-rw-r--r--indra/cmake/Discord.cmake9
-rw-r--r--indra/cmake/LLPrimitive.cmake15
-rw-r--r--indra/cmake/UnixInstall.cmake4
-rw-r--r--indra/llwebrtc/CMakeLists.txt6
-rw-r--r--indra/media_plugins/cef/CMakeLists.txt72
-rw-r--r--indra/media_plugins/cef/media_plugin_cef.cpp16
-rw-r--r--indra/media_plugins/libvlc/CMakeLists.txt6
-rw-r--r--indra/newview/CMakeLists.txt12
-rw-r--r--indra/newview/FixBundle.cmake.in14
-rw-r--r--indra/newview/FixPackage.cmake.in6
-rw-r--r--indra/newview/PKGBUILD.in2
-rw-r--r--indra/newview/ViewerInstall.cmake33
-rw-r--r--indra/newview/app_settings/settings.xml89
-rw-r--r--indra/newview/llagent.cpp18
-rw-r--r--indra/newview/llagentcamera.cpp86
-rw-r--r--indra/newview/llagentcamera.h9
-rw-r--r--indra/newview/llappviewer.cpp10
-rw-r--r--indra/newview/llappviewerlinux.cpp4
-rw-r--r--indra/newview/llavataractions.cpp85
-rw-r--r--indra/newview/llavataractions.h6
-rw-r--r--indra/newview/llnetmap.cpp35
-rw-r--r--indra/newview/llpanelpeoplemenus.cpp16
-rw-r--r--indra/newview/llpanelpeoplemenus.h1
-rw-r--r--indra/newview/llquickprefs.cpp23
-rw-r--r--indra/newview/llquickprefs.h5
-rw-r--r--indra/newview/llviewerdisplay.cpp6
-rw-r--r--indra/newview/llviewermedia.cpp6
-rw-r--r--indra/newview/llviewermenu.cpp65
-rw-r--r--indra/newview/llviewermessage.cpp11
-rw-r--r--indra/newview/llvoavatar.h1
-rw-r--r--indra/newview/skins/default/xui/en/floater_quick_prefs.xml126
-rw-r--r--indra/newview/skins/default/xui/en/menu_avatar_other.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_nearby.xml16
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml12
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_move.xml755
51 files changed, 1730 insertions, 1770 deletions
diff --git a/.github/parameters/setup-macports.yaml b/.github/parameters/setup-macports.yaml
new file mode 100644
index 0000000000..ed8cd40acd
--- /dev/null
+++ b/.github/parameters/setup-macports.yaml
@@ -0,0 +1,25 @@
+# Parameters for melusina-org/setup-macports.
+# Listing ports here (rather than in a manual `port install` step) means the
+# action installs them and they fall under its built-in installation cache,
+# which is keyed on the macOS version + this file. Edit this list and the
+# cache invalidates automatically.
+#
+# NOTE: no +universal variants. MacPorts' own guidance is that universal
+# builds are poorly supported on Apple Silicon (apr-util fails to build that
+# way). The runner is arm64 and we build the viewer arm64-only, so arm64
+# ports are correct and faster.
+version: '2.11.5'
+ports:
+ - name: 'cmake'
+ - name: 'pkgconfig'
+ - name: 'freealut'
+ - name: 'apr-util'
+ - name: 'boost188'
+ - name: 'glm'
+ - name: 'hunspell'
+ - name: 'freetype'
+ - name: 'minizip'
+ - name: 'nghttp2'
+ - name: 'openjpeg'
+ - name: 'libvorbis'
+ - name: 'xxhashlib'
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
new file mode 100644
index 0000000000..d1efde8ce9
--- /dev/null
+++ b/.github/workflows/build-macos.yml
@@ -0,0 +1,123 @@
+name: macOS Build
+
+on:
+ pull_request:
+ branches: [ main ]
+ push:
+ tags:
+ - 'test/*'
+ - 'build/*'
+ - 'v*'
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ build-macos:
+ name: macOS (arm64)
+ runs-on: macos-15
+
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@v5
+
+ - name: Set up MacPorts (installs cached deps from parameter file)
+ uses: melusina-org/setup-macports@v1
+ with:
+ parameters: .github/parameters/setup-macports.yaml
+
+ - name: Dump MacPorts logs on failure
+ if: failure()
+ shell: bash
+ run: |
+ echo "=== searching for MacPorts main.log files ==="
+ LOGDIR="/opt/local/var/macports/logs"
+ find "$LOGDIR" -name "main.log" 2>/dev/null | while read -r log; do
+ echo ""
+ echo "######################################################"
+ echo "### $log"
+ echo "######################################################"
+ tail -n 80 "$log"
+ done
+
+ - name: Configure
+ shell: bash
+ run: |
+ SDKPATH="$(xcrun --show-sdk-path)"
+ export LL_BUILD="-O3 -gdwarf-2 -stdlib=libc++ -mmacosx-version-min=12 -iwithsysroot $SDKPATH -std=c++20 -fPIC -DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -DNDEBUG -DPIC -DLL_DARWIN=1"
+
+ mkdir build-macos && cd build-macos
+ cmake \
+ -DCMAKE_BUILD_TYPE:STRING=Release \
+ -DADDRESS_SIZE:STRING=64 \
+ -DUSE_OPENAL:BOOL=ON \
+ -DUSE_FMODSTUDIO:BOOL=OFF \
+ -DENABLE_MEDIA_PLUGINS:BOOL=ON \
+ -DLL_TESTS:BOOL=OFF \
+ -DNDOF:BOOL=OFF \
+ -DROOT_PROJECT_NAME:STRING=Megapahit \
+ -DVIEWER_CHANNEL:STRING=Megapahit \
+ -DVIEWER_BINARY_NAME:STRING=megapahit \
+ -DBUILD_SHARED_LIBS:BOOL=OFF \
+ -DINSTALL:BOOL=ON \
+ -DPACKAGE:BOOL=OFF \
+ -DCMAKE_INSTALL_PREFIX:PATH=newview/Megapahit.app/Contents/Resources \
+ -DCMAKE_OSX_ARCHITECTURES:STRING=$(uname -m) \
+ -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=12 \
+ -DENABLE_SIGNING:BOOL=ON \
+ -DSIGNING_IDENTITY:STRING=- \
+ ../indra
+
+ - name: Build
+ shell: bash
+ run: |
+ cd build-macos
+ make -j$(sysctl -n hw.ncpu)
+
+ - name: Install into app bundle
+ shell: bash
+ run: |
+ cd build-macos
+ make install
+
+ - name: Read version
+ id: version
+ shell: bash
+ run: echo "version=$(cat indra/newview/viewer_version.txt)" >> "$GITHUB_OUTPUT"
+
+ - name: Package .app into a .zip
+ shell: bash
+ run: |
+ cd build-macos/newview
+ ditto -c -k --keepParent "Megapahit.app" \
+ "Megapahit-${{ steps.version.outputs.version }}-macos-$(uname -m).zip"
+
+ - name: Verify signature survived packaging
+ shell: bash
+ run: |
+ cd build-macos/newview
+ mkdir -p verify-tmp
+ ditto -x -k Megapahit-*-macos-*.zip verify-tmp/
+ codesign --verify --deep --strict --verbose=2 verify-tmp/Megapahit.app || {
+ echo "Signature verification FAILED"
+ rm -rf verify-tmp
+ exit 1
+ }
+ rm -rf verify-tmp
+ echo "Signature verified intact."
+
+ - name: Upload app artifact
+ uses: actions/upload-artifact@v6
+ with:
+ name: megapahit-macos-${{ steps.version.outputs.version }}
+ path: build-macos/newview/Megapahit-*-macos-*.zip
+ if-no-files-found: error
+
+ - name: Upload to release
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: softprops/action-gh-release@v3
+ with:
+ name: ${{ github.ref_name }}
+ files: build-macos/newview/Megapahit-*-macos-*.zip
+ prerelease: ${{ startsWith(github.ref_name, 'test/') || startsWith(github.ref_name, 'build/') }}
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
new file mode 100644
index 0000000000..8be2d33802
--- /dev/null
+++ b/.github/workflows/build-windows.yml
@@ -0,0 +1,225 @@
+name: Windows Build
+
+on:
+ pull_request:
+ branches: [ main ]
+ push:
+ tags:
+ - 'test/*'
+ - 'build/*'
+ - 'v*'
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ build-windows-x64:
+ name: Windows x64
+ runs-on: windows-2025-vs2026
+
+ steps:
+ - name: Set VCPKG_ROOT
+ shell: bash
+ run: echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
+
+ - name: Add MSBuild to PATH
+ uses: microsoft/setup-msbuild@v3
+
+ - name: Checkout source
+ uses: actions/checkout@v5
+
+ - name: Install vcpkg dependencies (x64)
+ shell: bash
+ run: |
+ vcpkg install \
+ python3:x64-windows \
+ freealut:x64-windows \
+ apr-util:x64-windows \
+ boost:x64-windows \
+ freetype:x64-windows \
+ glm:x64-windows \
+ hunspell:x64-windows \
+ libjpeg-turbo:x64-windows \
+ meshoptimizer:x64-windows \
+ minizip:x64-windows \
+ nanosvg:x64-windows \
+ nghttp2:x64-windows \
+ openjpeg:x64-windows \
+ libvorbis:x64-windows \
+ "libxml2[tools]:x64-windows" \
+ xxhash:x64-windows
+
+ - name: Configure (x64)
+ shell: bash
+ env:
+ LL_BUILD: "/MD /O2 /Ob2 /std:c++20 /Zc:wchar_t- /Zi /GR /DLL_RELEASE=1 /DLL_RELEASE_FOR_DOWNLOAD=1 /DNDEBUG /D_SECURE_STL=0 /D_HAS_ITERATOR_DEBUGGING=0 /DWIN32 /D_WINDOWS /DLL_WINDOWS=1 /DUNICODE /D_UNICODE /DWINVER=0x0602 /D_WIN32_WINNT=0x0602"
+ run: |
+ export PYTHON="$VCPKG_ROOT/installed/x64-windows/tools/python3/python.exe"
+ CMAKE_BIN=$(find "$VCPKG_ROOT/downloads/tools" -name "cmake.exe" -path "*/x86_64/*" | head -1)
+ if [ -z "$CMAKE_BIN" ]; then
+ CMAKE_BIN=cmake
+ fi
+ mkdir build-windows-x86_64
+ cd build-windows-x86_64
+ "$CMAKE_BIN" \
+ -DCMAKE_BUILD_TYPE:STRING=Release \
+ -DADDRESS_SIZE:STRING=64 \
+ -DUSE_OPENAL:BOOL=ON \
+ -DUSE_FMODSTUDIO:BOOL=OFF \
+ -DENABLE_MEDIA_PLUGINS:BOOL=ON \
+ -DLL_TESTS:BOOL=OFF \
+ -DNDOF:BOOL=OFF \
+ -DROOT_PROJECT_NAME:STRING=Megapahit \
+ -DVIEWER_CHANNEL:STRING=Megapahit \
+ -DVIEWER_BINARY_NAME:STRING=Megapahit \
+ -DBUILD_SHARED_LIBS:BOOL=OFF \
+ -DINSTALL:BOOL=ON \
+ -DPACKAGE:BOOL=ON \
+ "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
+ -DVS_DISABLE_FATAL_WARNINGS:BOOL=ON \
+ ../indra
+
+ - name: Build (x64)
+ shell: bash
+ run: |
+ cd build-windows-x86_64
+ MSBuild.exe Megapahit.slnx -p:Configuration=Release -m
+
+ - name: Install NSIS (x64)
+ shell: pwsh
+ run: |
+ choco install nsis --no-progress -y
+ "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Append
+
+ - name: Package (x64)
+ shell: bash
+ run: |
+ cd build-windows-x86_64
+ cpack -G NSIS
+
+ - name: Read version
+ id: version
+ shell: bash
+ run: echo "version=$(cat indra/newview/viewer_version.txt)" >> "$GITHUB_OUTPUT"
+
+ - name: Upload installer artifact (x64)
+ uses: actions/upload-artifact@v6
+ with:
+ name: megapahit-windows-x64-${{ steps.version.outputs.version }}
+ path: build-windows-x86_64/Megapahit-*-win64.exe
+ if-no-files-found: error
+
+ - name: Upload to release (x64)
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: softprops/action-gh-release@v3
+ with:
+ name: ${{ github.ref_name }}
+ files: build-windows-x86_64/Megapahit-*-win64.exe
+ prerelease: ${{ startsWith(github.ref_name, 'test/') || startsWith(github.ref_name, 'build/') }}
+
+ build-windows-arm64:
+ name: Windows arm64
+ if: false
+ runs-on: windows-2022
+
+ steps:
+ - name: Set VCPKG_ROOT
+ shell: bash
+ run: echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
+
+ - name: Add MSBuild to PATH
+ uses: microsoft/setup-msbuild@v3
+
+ - name: Checkout source
+ uses: actions/checkout@v5
+
+ - name: Install vcpkg dependencies (arm64)
+ shell: bash
+ run: |
+ vcpkg install \
+ python3:arm64-windows \
+ freealut:arm64-windows \
+ apr-util:arm64-windows \
+ boost:arm64-windows \
+ curl:arm64-windows \
+ freetype:arm64-windows \
+ glm:arm64-windows \
+ hunspell:arm64-windows \
+ libjpeg-turbo:arm64-windows \
+ meshoptimizer:arm64-windows \
+ minizip:arm64-windows \
+ nanosvg:arm64-windows \
+ nghttp2:arm64-windows \
+ openjpeg:arm64-windows \
+ sse2neon:arm64-windows \
+ libvorbis:arm64-windows \
+ "libxml2[tools]:arm64-windows" \
+ xxhash:arm64-windows
+ vcpkg install boost-fiber:arm64-windows --allow-unsupported || true
+
+ - name: Configure (arm64)
+ shell: bash
+ env:
+ LL_BUILD: "/MD /O2 /Ob2 /std:c++20 /Zc:wchar_t- /Zi /GR /DLL_RELEASE=1 /DLL_RELEASE_FOR_DOWNLOAD=1 /DNDEBUG /D_SECURE_STL=0 /D_HAS_ITERATOR_DEBUGGING=0 /DWIN32 /D_WINDOWS /DLL_WINDOWS=1 /DUNICODE /D_UNICODE /DWINVER=0x0602 /D_WIN32_WINNT=0x0602 /Zc:preprocessor"
+ run: |
+ export PYTHON="$VCPKG_ROOT/installed/arm64-windows/tools/python3/python.exe"
+ CMAKE_BIN=$(find "$VCPKG_ROOT/downloads/tools" -name "cmake.exe" -path "*/arm64/*" | head -1)
+ if [ -z "$CMAKE_BIN" ]; then
+ CMAKE_BIN=$(find "$VCPKG_ROOT/downloads/tools" -name "cmake.exe" | head -1)
+ fi
+ if [ -z "$CMAKE_BIN" ]; then
+ CMAKE_BIN=cmake
+ fi
+ mkdir build-windows-aarch64
+ cd build-windows-aarch64
+ "$CMAKE_BIN" \
+ -DCMAKE_BUILD_TYPE:STRING=Release \
+ -DADDRESS_SIZE:STRING=64 \
+ -DUSE_OPENAL:BOOL=ON \
+ -DUSE_FMODSTUDIO:BOOL=OFF \
+ -DENABLE_MEDIA_PLUGINS:BOOL=OFF \
+ -DLL_TESTS:BOOL=OFF \
+ -DNDOF:BOOL=OFF \
+ -DROOT_PROJECT_NAME:STRING=Megapahit \
+ -DVIEWER_CHANNEL:STRING=Megapahit \
+ -DVIEWER_BINARY_NAME:STRING=Megapahit \
+ -DBUILD_SHARED_LIBS:BOOL=OFF \
+ -DINSTALL:BOOL=ON \
+ -DPACKAGE:BOOL=ON \
+ "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
+ -DVCPKG_TARGET_TRIPLET:STRING=arm64-windows \
+ -DVS_DISABLE_FATAL_WARNINGS:BOOL=ON \
+ ../indra
+
+ - name: Build (arm64)
+ shell: bash
+ run: |
+ cd build-windows-aarch64
+ MSBuild.exe Megapahit.slnx -p:Configuration=Release -m
+
+ - name: Package (arm64)
+ shell: bash
+ run: |
+ cd build-windows-aarch64
+ cpack -G NSIS
+
+ - name: Read version
+ id: version
+ shell: bash
+ run: echo "version=$(cat indra/newview/viewer_version.txt)" >> "$GITHUB_OUTPUT"
+
+ - name: Upload installer artifact (arm64)
+ uses: actions/upload-artifact@v6
+ with:
+ name: megapahit-windows-arm64-${{ steps.version.outputs.version }}
+ path: build-windows-aarch64/Megapahit-*-win64.exe
+ if-no-files-found: error
+
+ - name: Upload to release (arm64)
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: softprops/action-gh-release@v3
+ with:
+ name: ${{ github.ref_name }}
+ files: build-windows-aarch64/Megapahit-*-win64.exe
+ prerelease: ${{ startsWith(github.ref_name, 'test/') || startsWith(github.ref_name, 'build/') }}
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
deleted file mode 100644
index 84c05dacdf..0000000000
--- a/.github/workflows/build.yaml
+++ /dev/null
@@ -1,530 +0,0 @@
-name: Build
-
-on:
- workflow_dispatch:
- inputs:
- installer_type:
- description: 'Windows installer type'
- type: choice
- options:
- - velopack
- - nsis
- default: 'velopack'
- pull_request:
- push:
- branches: ["main", "release/*", "project/*"]
- tags: ["Second_Life*"]
-
-jobs:
- # The whole point of the setup job is that we want to set variables once
- # that will be consumed by multiple subsequent jobs.
- setup:
- runs-on: ubuntu-latest
- outputs:
- release_run: ${{ steps.setvar.outputs.release_run }}
- configurations: ${{ steps.setvar.outputs.configurations }}
- bugsplat_db: ${{ steps.setvar.outputs.bugsplat_db }}
- env:
- # Build with a tag like "Second_Life#abcdef0" to generate a release page
- # (used for builds we are planning to deploy).
- # When you want to use a string variable as a workflow YAML boolean, it's
- # important to ensure it's the empty string when false. If you omit || '',
- # its value when false is "false", which is interpreted as true.
- RELEASE_RUN: ${{ (github.event.inputs.release_run || github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life')) && 'Y' || '' }}
- FROM_FORK: ${{ github.event.pull_request.head.repo.fork }}
- steps:
- - name: Set Variables
- id: setvar
- shell: bash
- run: |
- echo "release_run=$RELEASE_RUN" >> "$GITHUB_OUTPUT"
-
- if [[ "$FROM_FORK" == "true" ]]; then
- # PR from fork; don't build with Bugsplat, proprietary libs
- echo 'configurations=["ReleaseOS"]' >> $GITHUB_OUTPUT
- echo "bugsplat_db=" >> $GITHUB_OUTPUT
- else
- echo 'configurations=["Release"]' >> $GITHUB_OUTPUT
- echo "bugsplat_db=SecondLife_Viewer_2018" >> $GITHUB_OUTPUT
- fi
- build:
- needs: setup
- strategy:
- matrix:
- runner: ${{ fromJson((github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/Second_Life')) && '["windows-large","macos-15-xlarge"]' || '["windows-2022","macos-15-xlarge"]') }}
- configuration: ${{ fromJson(needs.setup.outputs.configurations) }}
- runs-on: ${{ matrix.runner }}
- outputs:
- viewer_channel: ${{ steps.build.outputs.viewer_channel }}
- viewer_version: ${{ steps.build.outputs.viewer_version }}
- viewer_branch: ${{ steps.which-branch.outputs.branch }}
- relnotes: ${{ steps.which-branch.outputs.relnotes }}
- imagename: ${{ steps.build.outputs.imagename }}
- configuration: ${{ matrix.configuration }}
- # Windows Velopack outputs (passed to sign-pkg-windows)
- velopack_pack_id: ${{ steps.build.outputs.velopack_pack_id }}
- velopack_pack_version: ${{ steps.build.outputs.velopack_pack_version }}
- velopack_pack_title: ${{ steps.build.outputs.velopack_pack_title }}
- velopack_main_exe: ${{ steps.build.outputs.velopack_main_exe }}
- velopack_exclude: ${{ steps.build.outputs.velopack_exclude }}
- velopack_icon: ${{ steps.build.outputs.velopack_icon }}
- velopack_installer_base: ${{ steps.build.outputs.velopack_installer_base }}
- # macOS Velopack outputs (passed to sign-pkg-mac)
- velopack_mac_pack_id: ${{ steps.build.outputs.velopack_mac_pack_id }}
- velopack_mac_pack_version: ${{ steps.build.outputs.velopack_mac_pack_version }}
- velopack_mac_pack_title: ${{ steps.build.outputs.velopack_mac_pack_title }}
- velopack_mac_main_exe: ${{ steps.build.outputs.velopack_mac_main_exe }}
- velopack_mac_bundle_id: ${{ steps.build.outputs.velopack_mac_bundle_id }}
- env:
- AUTOBUILD_ADDRSIZE: 64
- AUTOBUILD_BUILD_ID: ${{ github.run_id }}
- AUTOBUILD_CONFIGURATION: ${{ matrix.configuration }}
- # authorizes fetching private constituent packages
- AUTOBUILD_GITHUB_TOKEN: ${{ secrets.SHARED_AUTOBUILD_GITHUB_TOKEN }}
- AUTOBUILD_INSTALLABLE_CACHE: ${{ github.workspace }}/.autobuild-installables
- AUTOBUILD_VARIABLES_FILE: ${{ github.workspace }}/.build-variables/variables
- # Direct autobuild to store vcs_url, vcs_branch and vcs_revision in
- # autobuild-package.xml.
- AUTOBUILD_VCS_INFO: "true"
- AUTOBUILD_VSVER: "170"
- DEVELOPER_DIR: "/Applications/Xcode_16.4.app/Contents/Developer"
- # Ensure that Linden viewer builds engage Bugsplat.
- BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }}
- build_coverity: false
- build_log_dir: ${{ github.workspace }}/.logs
- build_viewer: true
- BUILDSCRIPTS_SHARED: ${{ github.workspace }}/.shared
- # extracted and committed to viewer repo
- BUILDSCRIPTS_SUPPORT_FUNCTIONS: ${{ github.workspace }}/buildscripts_support_functions
- GIT_REF: ${{ github.head_ref || github.ref }}
- LL_SKIP_REQUIRE_SYSROOT: 1
- # Setting this variable directs Linden's TUT test driver code to capture
- # test-program log output at the specified level, but to display it only if
- # the individual test fails.
- LOGFAIL: DEBUG
- master_message_template_checkout: ${{ github.workspace }}/.master-message-template
- # Only set variants to the one configuration: don't let build.sh loop
- # over variants, let GitHub distribute variants over multiple hosts.
- variants: ${{ matrix.configuration }}
- # Pass USE_VELOPACK to CMake when using Velopack installer (default) - Windows and macOS
- autobuild_configure_parameters: ${{ (contains(matrix.runner, 'windows') || contains(matrix.runner, 'macos')) && (github.event.inputs.installer_type || 'velopack') == 'velopack' && '-- -DUSE_VELOPACK:BOOL=ON' || '' }}
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
- with:
- ref: ${{ github.event.pull_request.head.sha || github.sha }}
-
- - name: Setup python
- uses: actions/setup-python@v6
- with:
- python-version: "3.11"
- - name: Checkout build variables
- uses: actions/checkout@v6
- with:
- repository: secondlife/build-variables
- ref: master
- path: .build-variables
-
- - name: Checkout master-message-template
- uses: actions/checkout@v6
- with:
- repository: secondlife/master-message-template
- path: .master-message-template
-
- - name: Install autobuild and python dependencies
- run: pip3 install autobuild llsd
-
- - name: Cache autobuild packages
- id: cache-installables
- uses: actions/cache@v5
- with:
- path: .autobuild-installables
- key: ${{ runner.os }}-64-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }}
- restore-keys: |
- ${{ runner.os }}-64-${{ matrix.configuration }}-
- ${{ runner.os }}-64-
-
- - name: Determine source branch
- id: which-branch
- uses: secondlife/viewer-build-util/which-branch@v2
- with:
- token: ${{ github.token }}
-
- - name: Setup .NET for Velopack
- if: (runner.os == 'Windows' || runner.os == 'macOS') && (github.event.inputs.installer_type || 'velopack') == 'velopack'
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: '9.0.x'
-
- - name: Install Velopack CLI
- if: (runner.os == 'Windows' || runner.os == 'macOS') && (github.event.inputs.installer_type || 'velopack') == 'velopack'
- shell: bash
- run: dotnet tool install -g vpk
-
- - name: Build
- id: build
- shell: bash
- env:
- AUTOBUILD_VCS_BRANCH: ${{ steps.which-branch.outputs.branch }}
- RUNNER_OS: ${{ runner.os }}
- run: |
- # set up things the viewer's build.sh script expects
- set -x
- mkdir -p "$build_log_dir"
- mkdir -p "$BUILDSCRIPTS_SHARED/packages/lib/python"
- source "$BUILDSCRIPTS_SUPPORT_FUNCTIONS"
- if [[ "$OSTYPE" =~ cygwin|msys ]]
- then
- native_path() { cygpath --windows "$1"; }
- shell_path() { cygpath --unix "$1"; }
- else
- native_path() { echo "$1"; }
- shell_path() { echo "$1"; }
- fi
- finalize()
- {
- case "$1" in
- true|0)
- record_success "Build Succeeded"
- ;;
- *)
- record_failure "Build Failed with $1"
- ;;
- esac
- }
- initialize_build()
- {
- echo "initialize_build"
- }
- initialize_version()
- {
- export revision="$AUTOBUILD_BUILD_ID"
- }
- python_cmd()
- {
- if [[ "x${1:0:1}" == "x-" ]] # -m, -c, etc.
- then # if $1 is a switch, don't try to twiddle paths
- "$(shell_path "$PYTHON_COMMAND")" "$@"
- elif [[ "$(basename "$1")" == "codeticket.py" ]]
- then # ignore any attempt to contact codeticket
- echo "## $@"
- else # running a script at an explicit path: fix path for Python
- local script="$1"
- shift
- "$(shell_path "$PYTHON_COMMAND")" "$(native_path "$script")" "$@"
- fi
- }
- repo_branch()
- {
- echo "$AUTOBUILD_VCS_BRANCH"
- }
- record_dependencies_graph()
- {
- echo "TODO: generate and post dependency graph"
- }
- # Since we're not uploading to codeticket, DO NOT sleep for minutes.
- sleep()
- {
- echo "Not sleeping for $1 seconds"
- }
- export -f native_path shell_path finalize initialize_build initialize_version
- export -f python_cmd repo_branch record_dependencies_graph sleep
- ## Useful for diagnosing Windows LLProcess/LLLeap test failures
- ##export APR_LOG="${RUNNER_TEMP}/apr.log"
- export arch=$(uname | cut -b-6)
- # Surprise! GH Windows runner's MINGW6 is a $arch value we've never
- # seen before, so numerous tests don't know about it.
- [[ "$arch" == "MINGW6" ]] && arch=CYGWIN
- export AUTOBUILD="$(which autobuild)"
-
- # determine the viewer channel from the branch or tag name
- # trigger an EDU build by including "edu" in the tag
- edu=${{ github.ref_type == 'tag' && contains(github.ref_name, 'edu') }}
- echo "ref_type=${{ github.ref_type }}, ref_name=${{ github.ref_name }}, edu='$edu'"
- branch=$AUTOBUILD_VCS_BRANCH
- if [[ "$edu" == "true" ]]
- then
- export viewer_channel="Second Life Release edu"
- elif [[ "$branch" == "develop" ]];
- then
- export viewer_channel="Second Life Develop"
- else
- IFS='/' read -ra ba <<< "$branch"
- prefix=${ba[0]}
- if [ "$prefix" == "project" ]; then
- IFS='_' read -ra prj <<< "${ba[1]}"
- prj_str="${prj[*]}"
- # uppercase first letter of each word
- capitalized=$(echo "$prj_str" | awk '{for (i=1; i<=NF; i++) $i = toupper(substr($i,1,1)) substr($i,2); print}')
- export viewer_channel="Second Life Project $capitalized"
- elif [[ "$prefix" == "release" || "$prefix" == "main" ]];
- then
- export viewer_channel="Second Life Release"
- else
- export viewer_channel="Second Life Test"
- fi
- fi
- echo "viewer_channel=$viewer_channel"
- echo "viewer_channel=$viewer_channel" >> "$GITHUB_OUTPUT"
- # On windows we need to point the build to the correct python
- # as neither CMake's FindPython nor our custom Python.cmake module
- # will resolve the correct interpreter location.
- if [[ "$RUNNER_OS" == "Windows" ]]; then
- export PYTHON="$(native_path "$(which python)")"
- echo "Python location: $PYTHON"
- export PYTHON_COMMAND="$PYTHON"
- else
- export PYTHON_COMMAND="python3"
- fi
- export PYTHON_COMMAND_NATIVE="$(native_path "$PYTHON_COMMAND")"
-
- ./build.sh
-
- # Each artifact is downloaded as a distinct .zip file. Multiple jobs
- # (per the matrix above) writing the same filepath to the same
- # artifact name will *overwrite* that file. Moreover, they can
- # interfere with each other, causing the upload to fail.
- # https://github.com/actions/upload-artifact#uploading-to-the-same-artifact
- # Given the size of our installers, and the fact that we typically
- # only want to download just one instead of a single zip containing
- # several, generate a distinct artifact name for each installer.
- # If the matrix above can run multiple builds on the same
- # platform, we must disambiguate on more than the platform name.
- # e.g. if we were still running Windows 32-bit builds, we'd need to
- # qualify the artifact with bit width.
- if [[ "$AUTOBUILD_CONFIGURATION" == "ReleaseOS" ]]
- then cfg_suffix='OS'
- else cfg_suffix=''
- fi
- echo "artifact=$RUNNER_OS$cfg_suffix" >> $GITHUB_OUTPUT
-
- - name: Upload executable
- if: steps.build.outputs.viewer_app
- uses: actions/upload-artifact@v6
- with:
- name: "${{ steps.build.outputs.artifact }}-app"
- path: |
- ${{ steps.build.outputs.viewer_app }}
-
- # The other upload of nontrivial size is the symbol file. Use a distinct
- # artifact for that too.
- - name: Upload symbol file
- if: steps.build.outputs.symbolfile
- uses: actions/upload-artifact@v6
- with:
- name: "${{ steps.build.outputs.artifact }}-symbols"
- path: ${{ steps.build.outputs.symbolfile }}
-
- - name: Upload metadata
- uses: actions/upload-artifact@v6
- with:
- name: "${{ steps.build.outputs.artifact }}-metadata"
- # emitted by build.sh, possibly multiple lines
- path: |
- ${{ steps.build.outputs.metadata }}
-
- - name: Upload physics package
- uses: actions/upload-artifact@v6
- # should only be set for viewer-private
- if: matrix.configuration == 'Release' && steps.build.outputs.physicstpv
- with:
- name: "${{ steps.build.outputs.artifact }}-physics"
- # emitted by build.sh, zero or one lines
- path: |
- ${{ steps.build.outputs.physicstpv }}
-
- sign-and-package-windows:
- env:
- AZURE_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }}
- AZURE_CERT_NAME: ${{ secrets.AZURE_CERT_NAME }}
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- needs: build
- runs-on: windows-2022
- steps:
- - name: Sign and package Windows viewer
- if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
- uses: secondlife/viewer-build-util/sign-pkg-windows@v2.1.0
- with:
- vault_uri: "${{ env.AZURE_KEY_VAULT_URI }}"
- cert_name: "${{ env.AZURE_CERT_NAME }}"
- client_id: "${{ env.AZURE_CLIENT_ID }}"
- client_secret: "${{ env.AZURE_CLIENT_SECRET }}"
- tenant_id: "${{ env.AZURE_TENANT_ID }}"
- installer_type: "${{ github.event.inputs.installer_type || 'velopack' }}"
- velopack_pack_id: "${{ needs.build.outputs.velopack_pack_id }}"
- velopack_pack_version: "${{ needs.build.outputs.velopack_pack_version }}"
- velopack_pack_title: "${{ needs.build.outputs.velopack_pack_title }}"
- velopack_main_exe: "${{ needs.build.outputs.velopack_main_exe }}"
- velopack_exclude: "${{ needs.build.outputs.velopack_exclude }}"
- velopack_icon: "${{ needs.build.outputs.velopack_icon }}"
- velopack_installer_base: "${{ needs.build.outputs.velopack_installer_base }}"
-
- sign-and-package-mac:
- env:
- NOTARIZE_CREDS_MACOS: ${{ secrets.NOTARIZE_CREDS_MACOS }}
- SIGNING_CERT_MACOS: ${{ secrets.SIGNING_CERT_MACOS }}
- SIGNING_CERT_MACOS_IDENTITY: ${{ secrets.SIGNING_CERT_MACOS_IDENTITY }}
- SIGNING_CERT_MACOS_PASSWORD: ${{ secrets.SIGNING_CERT_MACOS_PASSWORD }}
- needs: build
- runs-on: macos-latest
- steps:
- - name: Unpack Mac notarization credentials
- if: env.NOTARIZE_CREDS_MACOS
- id: note-creds
- shell: bash
- run: |
- # In NOTARIZE_CREDS_MACOS we expect to find:
- # USERNAME="..."
- # PASSWORD="..."
- # TEAM_ID="..."
- eval "${{ env.NOTARIZE_CREDS_MACOS }}"
- echo "::add-mask::$USERNAME"
- echo "::add-mask::$PASSWORD"
- echo "::add-mask::$TEAM_ID"
- echo "note_user=$USERNAME" >> "$GITHUB_OUTPUT"
- echo "note_pass=$PASSWORD" >> "$GITHUB_OUTPUT"
- echo "note_team=$TEAM_ID" >> "$GITHUB_OUTPUT"
- # If we didn't manage to retrieve all of these credentials, better
- # find out sooner than later.
- [[ -n "$USERNAME" && -n "$PASSWORD" && -n "$TEAM_ID" ]]
-
- - name: Sign and package Mac viewer
- if: env.SIGNING_CERT_MACOS && env.SIGNING_CERT_MACOS_IDENTITY && env.SIGNING_CERT_MACOS_PASSWORD && steps.note-creds.outputs.note_user && steps.note-creds.outputs.note_pass && steps.note-creds.outputs.note_team
- uses: secondlife/viewer-build-util/sign-pkg-mac@v2.1.0
- with:
- channel: ${{ needs.build.outputs.viewer_channel }}
- imagename: ${{ needs.build.outputs.imagename }}
- cert_base64: ${{ env.SIGNING_CERT_MACOS }}
- cert_name: ${{ env.SIGNING_CERT_MACOS_IDENTITY }}
- cert_pass: ${{ env.SIGNING_CERT_MACOS_PASSWORD }}
- note_user: ${{ steps.note-creds.outputs.note_user }}
- note_pass: ${{ steps.note-creds.outputs.note_pass }}
- note_team: ${{ steps.note-creds.outputs.note_team }}
- velopack_pack_id: "${{ needs.build.outputs.velopack_mac_pack_id }}"
- velopack_pack_version: "${{ needs.build.outputs.velopack_mac_pack_version }}"
- velopack_pack_title: "${{ needs.build.outputs.velopack_mac_pack_title }}"
- velopack_main_exe: "${{ needs.build.outputs.velopack_mac_main_exe }}"
- velopack_bundle_id: "${{ needs.build.outputs.velopack_mac_bundle_id }}"
-
- post-windows-symbols:
- env:
- BUGSPLAT_DATABASE: "${{ secrets.BUGSPLAT_DATABASE }}"
- SYMBOL_UPLOAD_CLIENT_ID: "${{ secrets.BUGSPLAT_SYMBOL_UPLOAD_CLIENT_ID }}"
- SYMBOL_UPLOAD_CLIENT_SECRET: "${{ secrets.BUGSPLAT_SYMBOL_UPLOAD_CLIENT_SECRET }}"
- needs: build
- if: needs.build.outputs.configuration == 'Release'
- runs-on: ubuntu-latest
- steps:
- - name: Download viewer exe
- uses: actions/download-artifact@v7
- with:
- name: Windows-app
- path: _artifacts
- - name: Download Windows Symbols
- if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
- uses: actions/download-artifact@v7
- with:
- name: Windows-symbols
- - name: Extract viewer pdb
- if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
- shell: bash
- run: |
- tar -xJf "${{ needs.build.outputs.viewer_channel }}.sym.tar.xz" -C _artifacts
- - name: Post Windows symbols
- if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
- uses: BugSplat-Git/symbol-upload@095d163ae9ceb006d286a731dcd35cf6a1b458c8
- with:
- clientId: "${{ env.SYMBOL_UPLOAD_CLIENT_ID }}"
- clientSecret: "${{ env.SYMBOL_UPLOAD_CLIENT_SECRET }}"
- database: "${{ env.BUGSPLAT_DATABASE }}"
- application: ${{ needs.build.outputs.viewer_channel }}
- version: ${{ needs.build.outputs.viewer_version }}
- directory: _artifacts
- files: "**/{SecondLifeViewer.exe,llwebrtc.dll,*.pdb}"
- node-version: "22"
- dumpSyms: false
-
- post-mac-symbols:
- env:
- BUGSPLAT_DATABASE: "${{ secrets.BUGSPLAT_DATABASE }}"
- SYMBOL_UPLOAD_CLIENT_ID: "${{ secrets.BUGSPLAT_SYMBOL_UPLOAD_CLIENT_ID }}"
- SYMBOL_UPLOAD_CLIENT_SECRET: "${{ secrets.BUGSPLAT_SYMBOL_UPLOAD_CLIENT_SECRET }}"
- needs: build
- if: needs.build.outputs.configuration == 'Release'
- runs-on: ubuntu-latest
- steps:
- - name: Download Mac Symbols
- if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
- uses: actions/download-artifact@v7
- with:
- name: macOS-symbols
- - name: Post Mac symbols
- if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
- uses: BugSplat-Git/symbol-upload@095d163ae9ceb006d286a731dcd35cf6a1b458c8
- with:
- clientId: "${{ env.SYMBOL_UPLOAD_CLIENT_ID }}"
- clientSecret: "${{ env.SYMBOL_UPLOAD_CLIENT_SECRET }}"
- database: "${{ env.BUGSPLAT_DATABASE }}"
- application: ${{ needs.build.outputs.viewer_channel }}
- version: ${{ needs.build.outputs.viewer_version }} (${{ needs.build.outputs.viewer_version }})
- directory: .
- files: "**/*.xcarchive.zip"
- node-version: "22"
- dumpSyms: false
-
- release:
- needs: [setup, build, sign-and-package-windows, sign-and-package-mac]
- runs-on: ubuntu-latest
- if: needs.setup.outputs.release_run
- steps:
- - uses: actions/download-artifact@v7
- with:
- pattern: "*-installer"
-
- - uses: actions/download-artifact@v7
- with:
- pattern: "*-metadata"
-
- - uses: actions/download-artifact@v4
- with:
- pattern: "*-releases"
-
- - name: Rename metadata
- run: |
- cp Windows-metadata/autobuild-package.xml Windows-autobuild-package.xml
- cp Windows-metadata/newview/viewer_version.txt Windows-viewer_version.txt
- cp macOS-metadata/autobuild-package.xml macOS-autobuild-package.xml
- cp macOS-metadata/newview/viewer_version.txt macOS-viewer_version.txt
-
- # forked from softprops/action-gh-release
- - name: Create GitHub release
- id: release
- uses: secondlife-3p/action-gh-release@v1
- with:
- # name the release page for the branch
- name: "${{ needs.build.outputs.viewer_branch }}"
- # SL-20546: want the channel and version to be visible on the
- # release page
- body: |
- Build ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- ${{ needs.build.outputs.viewer_channel }}
- ${{ needs.build.outputs.viewer_version }}
- ${{ needs.build.outputs.relnotes }}
- prerelease: true
- generate_release_notes: true
- target_commitish: ${{ github.sha }}
- append_body: true
- fail_on_unmatched_files: false
- files: |
- macOS-installer/*.dmg
- Windows-installer/*.exe
- *-autobuild-package.xml
- *-viewer_version.txt
- Windows-releases/*
- macOS-releases/*
-
- - name: post release URL
- run: |
- echo "::notice::Release ${{ steps.release.outputs.url }}"
diff --git a/.github/workflows/check-pr.yaml b/.github/workflows/check-pr.yaml
deleted file mode 100644
index 08e907e83f..0000000000
--- a/.github/workflows/check-pr.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-name: Check PR
-
-on:
- pull_request:
- types: [opened, edited, reopened, synchronize]
-
-permissions:
- contents: read
-
-jobs:
- check-description:
- runs-on: ubuntu-latest
- steps:
- - name: Check PR description
- uses: actions/github-script@v8
- with:
- script: |
- const description = context.payload.pull_request.body || '';
- if (description.trim().length < 20) {
- core.setFailed("❌ PR description is too short. Please provide at least 20 characters.");
- }
diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml
deleted file mode 100644
index 800f3c42d1..0000000000
--- a/.github/workflows/cla.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Check CLA
-
-on:
- issue_comment:
- types: [created]
- pull_request_target:
- types: [opened, closed, synchronize]
-
-jobs:
- cla:
- name: Check CLA
- runs-on: ubuntu-latest
- steps:
- - name: CLA Assistant
- if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
- uses: secondlife-3p/contributor-assistant@v2.6.1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- PERSONAL_ACCESS_TOKEN: ${{ secrets.SHARED_CLA_TOKEN }}
- with:
- branch: main
- path-to-document: https://github.com/secondlife/cla/blob/main/CLA.md
- path-to-signatures: signatures.json
- remote-organization-name: secondlife
- remote-repository-name: cla-signatures
- allowlist: callum@mbp.localdomain,rye@lindenlab.com,rye,signal@lindenlab.com,dependabot*,bot*
diff --git a/.github/workflows/label.yaml b/.github/workflows/label.yaml
deleted file mode 100644
index 218327ef47..0000000000
--- a/.github/workflows/label.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Pull Request Labeler
-on:
- - pull_request_target
-
-jobs:
- triage:
- permissions:
- contents: read
- pull-requests: write
- runs-on: ubuntu-latest
- steps:
- - uses: actions/labeler@v6
- with:
- configuration-path: .github/labeler.yaml
- repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml
deleted file mode 100644
index 93bcafdea8..0000000000
--- a/.github/workflows/pre-commit.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: pre-commit
-
-on:
- pull_request:
- push:
- branches: [main, contribute]
- tags: [v*]
-
-
-jobs:
- pre-commit:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
- - uses: actions/setup-python@v6
- with:
- python-version: 3.x
- - uses: pre-commit/action@v3.0.1
diff --git a/.github/workflows/qatest.yaml b/.github/workflows/qatest.yaml
deleted file mode 100644
index b6883d88d4..0000000000
--- a/.github/workflows/qatest.yaml
+++ /dev/null
@@ -1,610 +0,0 @@
-name: Run QA Test # Runs automated tests on self-hosted QA machines
-
-permissions:
- contents: read
-
-on:
- workflow_run:
- workflows: ["Build"]
- types:
- - completed
- workflow_dispatch:
- inputs:
- build_id:
- description: 'Build workflow run ID (e.g. For github.com/secondlife/viewer/actions/runs/1234567890 the ID is 1234567890)'
- required: true
- default: '14806728332'
-
-jobs:
- debug-workflow:
- runs-on: ubuntu-latest
- steps:
- - name: Debug Workflow Variables
- run: |
- echo "Workflow Conclusion: ${{ github.event.workflow_run.conclusion }}"
- echo "Workflow Head Branch: ${{ github.event.workflow_run.head_branch }}"
- echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
- echo "Head Commit Message: ${{ github.event.workflow_run.head_commit.message }}"
- echo "GitHub Ref: ${{ github.ref }}"
- echo "GitHub Ref Name: ${{ github.ref_name }}"
- echo "GitHub Event Name: ${{ github.event_name }}"
- echo "GitHub Workflow Name: ${{ github.workflow }}"
-
- install-viewer-and-run-tests:
- concurrency:
- group: ${{ github.workflow }}-${{ matrix.runner }}
- cancel-in-progress: false # Prevents cancellation of in-progress jobs
-
- strategy:
- matrix:
- include:
- - os: windows
- runner: qa-windows-atlas
- artifact: Windows-installer
- install-path: 'C:\viewer-automation-main'
- - os: windows
- runner: qa-windows-asus-dan
- artifact: Windows-installer
- install-path: 'C:\viewer-automation-main'
- - os: windows
- runner: qa-windows-kurt
- artifact: Windows-installer
- install-path: 'C:\viewer-automation-main'
- - os: mac
- runner: qa-mac-dan
- artifact: macOS-installer
- install-path: '$HOME/Documents/viewer-automation'
- - os: mac
- runner: qa-mac-atlas
- artifact: macOS-installer
- install-path: '$HOME/Documents/viewer-automation'
- - os: mac
- runner: qa-mac-caleb
- artifact: macOS-installer
- install-path: '$HOME/Documents/viewer-automation'
- fail-fast: false
-
- runs-on: [self-hosted, "${{ matrix.runner }}"]
- # Run test only on successful builds of Second_Life_X branches or on manual dispatch
- if: >
- (github.event_name == 'workflow_run' &&
- github.event.workflow_run.conclusion == 'success' &&
- startsWith(github.event.workflow_run.head_branch, 'Second_Life')) ||
- github.event_name == 'workflow_dispatch'
-
- steps:
- # Windows-specific steps
- - name: Set Build ID
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- if ("${{ github.event_name }}" -eq "workflow_dispatch") {
- echo "BUILD_ID=${{ github.event.inputs.build_id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
- echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
- } else {
- echo "BUILD_ID=${{ github.event.workflow_run.id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
- echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
- }
-
- - name: Temporarily Allow PowerShell Scripts (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- Set-ExecutionPolicy RemoteSigned -Scope Process -Force
-
- - name: Verify viewer-automation-main Exists (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- if (-Not (Test-Path -Path '${{ matrix.install-path }}')) {
- Write-Host '❌ Error: viewer-automation folder not found on runner!'
- exit 1
- }
- Write-Host '✅ viewer-automation folder is provided.'
-
- - name: Verify viewer-automation-main is Up-To-Date (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- continue-on-error: true
- run: |
- cd ${{ matrix.install-path }}
- Write-Host "Checking for repository updates..."
-
- # Check if .git directory exists
- if (Test-Path -Path ".git") {
- try {
- # Save local changes instead of discarding them
- git stash push -m "Automated stash before update $(Get-Date)"
- Write-Host "Local changes saved (if any)"
-
- # Update the repository
- git pull
- Write-Host "✅ Repository updated successfully"
-
- # Try to restore local changes if any were stashed
- $stashList = git stash list
- if ($stashList -match "Automated stash before update") {
- try {
- git stash pop
- Write-Host "✅ Local changes restored successfully"
- } catch {
- Write-Host "⚠️ Conflict when restoring local changes"
- # Save the conflicted state in a new branch for later review
- $branchName = "conflict-recovery-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
- git checkout -b $branchName
- Write-Host "✅ Created branch '$branchName' with conflicted state"
-
- # For test execution, revert to a clean state
- git reset --hard HEAD
- Write-Host "✅ Reset to clean state for test execution"
- }
- }
- } catch {
- Write-Host "⚠️ Could not update repository: $_"
- Write-Host "Continuing with existing files..."
- }
- } else {
- Write-Host "⚠️ Not a Git repository, using existing files"
- }
-
- - name: Verify Python Installation (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- try {
- $pythonVersion = (python --version)
- Write-Host "✅ Python found: $pythonVersion"
- } catch {
- Write-Host "❌ Error: Python not found in PATH. Please install Python on this runner."
- exit 1
- }
-
- - name: Setup Python Virtual Environment (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
- cd ${{ matrix.install-path }}
-
- if (-Not (Test-Path -Path ".venv")) {
- Write-Host "Creating virtual environment..."
- python -m venv .venv
- } else {
- Write-Host "Using existing virtual environment"
- }
-
- # Direct environment activation to avoid script execution issues
- $env:VIRTUAL_ENV = "$PWD\.venv"
- $env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
-
- # Install dependencies
- if (Test-Path -Path "requirements.txt") {
- Write-Host "Installing dependencies from requirements.txt..."
- pip install -r requirements.txt
-
- # Install Playwright browsers - add this line
- Write-Host "Installing Playwright browsers..."
- python -m playwright install
- } else {
- pip install outleap requests behave playwright
- # Install Playwright browsers - add this line
- Write-Host "Installing Playwright browsers..."
- python -m playwright install
- }
-
- - name: Fetch & Download Installer Artifact (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- $BUILD_ID = "${{ env.BUILD_ID }}"
- $ARTIFACTS_URL = "${{ env.ARTIFACTS_URL }}"
-
- # Fetch the correct artifact URL
- $response = Invoke-RestMethod -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}" } -Uri $ARTIFACTS_URL
- $ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "${{ matrix.artifact }}" }).archive_download_url
-
- if (-Not $ARTIFACT_NAME) {
- Write-Host "❌ Error: ${{ matrix.artifact }} artifact not found!"
- exit 1
- }
-
- Write-Host "✅ Artifact found: $ARTIFACT_NAME"
-
- # Secure download path
- $DownloadPath = "$env:TEMP\secondlife-build-$BUILD_ID"
- New-Item -ItemType Directory -Path $DownloadPath -Force | Out-Null
- $InstallerPath = "$DownloadPath\installer.zip"
-
- # Download the ZIP
- Invoke-WebRequest -Uri $ARTIFACT_NAME -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}"} -OutFile $InstallerPath
-
- # Ensure download succeeded
- if (-Not (Test-Path $InstallerPath)) {
- Write-Host "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
- exit 1
- }
-
- # Set the path for other steps
- echo "DOWNLOAD_PATH=$DownloadPath" | Out-File -FilePath $env:GITHUB_ENV -Append
-
- - name: Extract Installer & Locate Executable (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- $BUILD_ID = "${{ env.BUILD_ID }}"
- $ExtractPath = "${{ env.DOWNLOAD_PATH }}"
- $InstallerZip = "$ExtractPath\installer.zip"
-
- # Print paths for debugging
- Write-Host "Extract Path: $ExtractPath"
- Write-Host "Installer ZIP Path: $InstallerZip"
-
- # Verify ZIP exists before extracting
- if (-Not (Test-Path $InstallerZip)) {
- Write-Host "❌ Error: ZIP file not found at $InstallerZip!"
- exit 1
- }
-
- Write-Host "✅ ZIP file exists and is valid. Extracting..."
-
- Expand-Archive -Path $InstallerZip -DestinationPath $ExtractPath -Force
-
- # Find installer executable
- $INSTALLER_PATH = (Get-ChildItem -Path $ExtractPath -Filter '*.exe' -Recurse | Select-Object -First 1).FullName
-
- if (-Not $INSTALLER_PATH -or $INSTALLER_PATH -eq "") {
- Write-Host "❌ Error: No installer executable found in the extracted files!"
- Write-Host "📂 Extracted Files:"
- Get-ChildItem -Path $ExtractPath -Recurse | Format-Table -AutoSize
- exit 1
- }
-
- Write-Host "✅ Installer found: $INSTALLER_PATH"
- echo "INSTALLER_PATH=$INSTALLER_PATH" | Out-File -FilePath $env:GITHUB_ENV -Append
-
- - name: Install Second Life (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- # Windows - Use Task Scheduler to bypass UAC
- $action = New-ScheduledTaskAction -Execute "${{ env.INSTALLER_PATH }}" -Argument "/S"
- $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
- $task = New-ScheduledTask -Action $action -Principal $principal
- Register-ScheduledTask -TaskName "SilentSLInstaller" -InputObject $task -Force
- Start-ScheduledTask -TaskName "SilentSLInstaller"
-
- - name: Wait for Installation to Complete (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- Write-Host "Waiting for the Second Life installer to finish..."
- do {
- Start-Sleep -Seconds 5
- $installerProcess = Get-Process | Where-Object { $_.Path -eq "${{ env.INSTALLER_PATH }}" }
- } while ($installerProcess)
-
- Write-Host "✅ Installation completed!"
-
- - name: Cleanup After Installation (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- # Cleanup Task Scheduler Entry
- Unregister-ScheduledTask -TaskName "SilentSLInstaller" -Confirm:$false
- Write-Host "✅ Task Scheduler entry removed."
-
- # Delete Installer ZIP
- $DeletePath = "${{ env.DOWNLOAD_PATH }}\installer.zip"
-
- Write-Host "Checking if installer ZIP exists: $DeletePath"
-
- # Ensure the ZIP file exists before trying to delete it
- if (Test-Path $DeletePath) {
- Remove-Item -Path $DeletePath -Force
- Write-Host "✅ Successfully deleted: $DeletePath"
- } else {
- Write-Host "⚠️ Warning: ZIP file does not exist, skipping deletion."
- }
-
- - name: Run QA Test Script (Windows)
- if: matrix.os == 'windows'
- shell: pwsh
- run: |
- Write-Host "Running QA Test script on Windows runner: ${{ matrix.runner }}..."
- cd ${{ matrix.install-path }}
-
- # Activate virtual environment
- Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
- $env:VIRTUAL_ENV = "$PWD\.venv"
- $env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
-
- # Set runner name as environment variable
- $env:RUNNER_NAME = "${{ matrix.runner }}"
-
- # Run the test script
- python runTests.py
-
- # Mac-specific steps
- - name: Set Build ID (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
- echo "BUILD_ID=${{ github.event.inputs.build_id }}" >> $GITHUB_ENV
- echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" >> $GITHUB_ENV
- else
- echo "BUILD_ID=${{ github.event.workflow_run.id }}" >> $GITHUB_ENV
- echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" >> $GITHUB_ENV
- fi
-
- - name: Verify viewer-automation-main Exists (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- if [ ! -d "${{ matrix.install-path }}" ]; then
- echo "❌ Error: viewer-automation folder not found on runner!"
- exit 1
- fi
- echo "✅ viewer-automation is provided."
-
- - name: Verify viewer-automation-main is Up-To-Date (Mac)
- if: matrix.os == 'mac'
- shell: bash
- continue-on-error: true
- run: |
- cd ${{ matrix.install-path }}
- echo "Checking for repository updates..."
-
- # Check if .git directory exists
- if [ -d ".git" ]; then
- # Save local changes instead of discarding them
- git stash push -m "Automated stash before update $(date)"
- echo "Local changes saved (if any)"
-
- # Update the repository
- git pull || echo "⚠️ Could not update repository"
- echo "✅ Repository updated (or attempted update)"
-
- # Try to restore local changes if any were stashed
- if git stash list | grep -q "Automated stash before update"; then
- # Try to pop the stash, but be prepared for conflicts
- if ! git stash pop; then
- echo "⚠️ Conflict when restoring local changes"
- # Save the conflicted state in a new branch for later review
- branch_name="conflict-recovery-$(date +%Y%m%d-%H%M%S)"
- git checkout -b "$branch_name"
- echo "✅ Created branch '$branch_name' with conflicted state"
-
- # For test execution, revert to a clean state
- git reset --hard HEAD
- echo "✅ Reset to clean state for test execution"
- else
- echo "✅ Local changes restored successfully"
- fi
- fi
- else
- echo "⚠️ Not a Git repository, using existing files"
- fi
-
- - name: Verify Python Installation (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- if command -v python3 &> /dev/null; then
- PYTHON_VERSION=$(python3 --version)
- echo "✅ Python found: $PYTHON_VERSION"
- else
- echo "❌ Error: Python3 not found in PATH. Please install Python on this runner."
- exit 1
- fi
-
- - name: Setup Python Virtual Environment (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- cd ${{ matrix.install-path }}
-
- # Create virtual environment if it doesn't exist
- if [ ! -d ".venv" ]; then
- echo "Creating virtual environment..."
- python3 -m venv .venv
- else
- echo "Using existing virtual environment"
- fi
-
- # Activate virtual environment
- source .venv/bin/activate
-
- # Install dependencies
- if [ -f "requirements.txt" ]; then
- pip install -r requirements.txt
- echo "✅ Installed dependencies from requirements.txt"
-
- # Install Playwright browsers - add this line
- echo "Installing Playwright browsers..."
- python -m playwright install
- else
- pip install outleap requests behave playwright
- echo "⚠️ requirements.txt not found, installed basic dependencies"
-
- # Install Playwright browsers - add this line
- echo "Installing Playwright browsers..."
- python -m playwright install
- fi
-
- - name: Fetch & Download Installer Artifact (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- # Mac-specific Bash commands
- response=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s ${{ env.ARTIFACTS_URL }})
- ARTIFACT_NAME=$(echo $response | jq -r '.artifacts[] | select(.name=="${{ matrix.artifact }}") | .archive_download_url')
-
- if [ -z "$ARTIFACT_NAME" ]; then
- echo "❌ Error: ${{ matrix.artifact }} artifact not found!"
- exit 1
- fi
-
- echo "✅ Artifact found: $ARTIFACT_NAME"
-
- # Secure download path
- DOWNLOAD_PATH="/tmp/secondlife-build-${{ env.BUILD_ID }}"
- mkdir -p $DOWNLOAD_PATH
- INSTALLER_PATH="$DOWNLOAD_PATH/installer.zip"
-
- # Download the ZIP
- curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -L $ARTIFACT_NAME -o $INSTALLER_PATH
-
- # Ensure download succeeded
- if [ ! -f "$INSTALLER_PATH" ]; then
- echo "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
- exit 1
- fi
-
- # Set the path for other steps
- echo "DOWNLOAD_PATH=$DOWNLOAD_PATH" >> $GITHUB_ENV
-
- - name: Extract Installer & Locate Executable (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- EXTRACT_PATH="${{ env.DOWNLOAD_PATH }}"
- INSTALLER_ZIP="$EXTRACT_PATH/installer.zip"
-
- # Debug output
- echo "Extract Path: $EXTRACT_PATH"
- echo "Installer ZIP Path: $INSTALLER_ZIP"
-
- # Verify ZIP exists
- if [ ! -f "$INSTALLER_ZIP" ]; then
- echo "❌ Error: ZIP file not found at $INSTALLER_ZIP!"
- exit 1
- fi
-
- echo "✅ ZIP file exists and is valid. Extracting..."
-
- # Extract the ZIP
- unzip -o "$INSTALLER_ZIP" -d "$EXTRACT_PATH"
-
- # Find DMG file
- INSTALLER_PATH=$(find "$EXTRACT_PATH" -name "*.dmg" -type f | head -1)
-
- if [ -z "$INSTALLER_PATH" ]; then
- echo "❌ Error: No installer DMG found in the extracted files!"
- echo "📂 Extracted Files:"
- ls -la "$EXTRACT_PATH"
- exit 1
- fi
-
- echo "✅ Installer found: $INSTALLER_PATH"
- echo "INSTALLER_PATH=$INSTALLER_PATH" >> $GITHUB_ENV
-
- - name: Install Second Life (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- # Mac installation
- echo "Mounting DMG installer..."
- MOUNT_POINT="/tmp/secondlife-dmg"
- mkdir -p "$MOUNT_POINT"
-
- # Mount the DMG
- hdiutil attach "$INSTALLER_PATH" -mountpoint "$MOUNT_POINT" -nobrowse
-
- echo "✅ DMG mounted at $MOUNT_POINT"
-
- echo "Installing application to default location from DMG..."
-
- # Find the .app bundle in the DMG
- APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1)
-
- if [ -z "$APP_PATH" ]; then
- echo "❌ Error: No .app bundle found in the mounted DMG!"
- exit 1
- fi
-
- APP_NAME=$(basename "$APP_PATH")
- DEST_PATH="/Applications/$APP_NAME"
-
- # Handle existing installation
- if [ -d "$DEST_PATH" ]; then
- echo "Found existing installation at: $DEST_PATH"
- echo "Moving existing installation to trash..."
-
- # Move to trash instead of force removing
- TRASH_PATH="$HOME/.Trash/$(date +%Y%m%d_%H%M%S)_$APP_NAME"
- mv "$DEST_PATH" "$TRASH_PATH" || {
- echo "⚠️ Could not move to trash, trying direct removal..."
- rm -rf "$DEST_PATH" || {
- echo "❌ Could not remove existing installation"
- echo "Please manually remove: $DEST_PATH"
- exit 1
- }
- }
-
- echo "✅ Existing installation handled successfully"
- fi
-
- # Copy the .app to /Applications
- echo "Copying app from: $APP_PATH"
- echo "To destination: /Applications/"
- cp -R "$APP_PATH" /Applications/
-
- # Verify the app was copied successfully
- if [ ! -d "$DEST_PATH" ]; then
- echo "❌ Error: Failed to install application to /Applications!"
- exit 1
- fi
-
- echo "✅ Application installed successfully to /Applications"
-
- # Save mount point for cleanup
- echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV
-
- - name: Wait for Installation to Complete (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- echo "Waiting for installation to complete..."
- # Sleep to allow installation to finish (adjust as needed)
- sleep 30
- echo "✅ Installation completed"
-
- - name: Cleanup After Installation (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- # Mac cleanup
- # Unmount the DMG
- echo "Unmounting DMG..."
- hdiutil detach "${{ env.MOUNT_POINT }}" -force
-
- # Clean up temporary files
- echo "Cleaning up temporary files..."
- rm -rf "${{ env.DOWNLOAD_PATH }}"
- rm -rf "${{ env.MOUNT_POINT }}"
-
- echo "✅ Cleanup completed"
-
- - name: Run QA Test Script (Mac)
- if: matrix.os == 'mac'
- shell: bash
- run: |
- echo "Running QA Test script on Mac runner: ${{ matrix.runner }}..."
- cd ${{ matrix.install-path }}
-
- # Activate virtual environment
- source .venv/bin/activate
-
- # Set runner name as environment variable
- export RUNNER_NAME="${{ matrix.runner }}"
-
- # Run the test script
- python runTests.py
-
- # - name: Upload Test Results
- # if: always()
- # uses: actions/upload-artifact@v4
- # with:
- # name: test-results-${{ matrix.runner }}
- # path: ${{ matrix.install-path }}/regressionTest/test_results.html
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
deleted file mode 100644
index edfe71b693..0000000000
--- a/.github/workflows/stale.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Stale PRs
-on:
- workflow_dispatch:
- schedule:
- - cron: 0 0 * * *
-
-permissions:
- issues: write
- pull-requests: write
-
-jobs:
- stale:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/stale@v10
- id: stale
- with:
- stale-pr-message: This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or it will be closed in 7 days
- days-before-stale: 30
- days-before-close: 7
- days-before-issue-close: -1
- exempt-pr-labels: blocked,must,should,keep
- stale-pr-label: stale
- - name: Print outputs
- run: echo ${{ join(steps.stale.outputs.*, ',') }}
diff --git a/.github/workflows/tag-release.yaml b/.github/workflows/tag-release.yaml
deleted file mode 100644
index 0f826222a0..0000000000
--- a/.github/workflows/tag-release.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-name: Tag a Build
-
-on:
- # schedule event triggers always run on the default branch
- # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
- schedule:
- # run "nightly" builds on default branch every mon/wed/fri
- - cron: "21 2 * * 2,4,6" # 2:21am UTC tues/thurs/sat == 7:21pm PDT mon/wed/fri -- see https://crontab.guru/#21_01_*_*_2,4,6
- workflow_dispatch:
- inputs:
- channel:
- description: "Channel to configure the build"
- required: true
- type: choice
- default: "Test"
- options:
- - "Test"
- - "Develop"
- - "Project"
- - "Release"
- project:
- description: "Project Name (used for channel name in project builds, and tag name for all builds)"
- default: "hippo"
- tag_override:
- description: "Override the tag name (optional). If the tag already exists, a numeric suffix is appended."
- required: false
-
-jobs:
- tag-release:
- runs-on: ubuntu-latest
- steps:
- - name: Setup Env Vars
- run: |
- CHANNEL="${{ inputs.channel }}"
- echo VIEWER_CHANNEL="Second_Life_${CHANNEL:-Develop}" >> ${GITHUB_ENV}
- NIGHTLY_DATE=$(date --rfc-3339=date)
- echo NIGHTLY_DATE=${NIGHTLY_DATE} >> ${GITHUB_ENV}
- echo TAG_ID="$(echo ${{ github.sha }} | cut -c1-8)-${{ inputs.project || '${NIGHTLY_DATE}' }}" >> ${GITHUB_ENV}
- - name: Create Tag
- uses: actions/github-script@v8
- with:
- # use a real access token instead of GITHUB_TOKEN default.
- # required so that the results of this tag creation can trigger the build workflow
- # https://stackoverflow.com/a/71372524
- # https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
- # this token will need to be renewed anually in January
- github-token: ${{ secrets.LL_TAG_RELEASE_TOKEN }}
- script: |
- const override = `${{ inputs.tag_override }}`.trim();
- const baseTag = override || `${{ env.VIEWER_CHANNEL }}#${{ env.TAG_ID }}`;
-
- // Try the base tag first, then append -2, -3, etc. if it already exists
- let tag = baseTag;
- for (let attempt = 1; ; attempt++) {
- try {
- await github.rest.git.createRef({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: `refs/tags/${tag}`,
- sha: context.sha
- });
- core.info(`Created tag: ${tag}`);
- break;
- } catch (e) {
- if (e.status === 422 && attempt < 10) {
- core.info(`Tag '${tag}' already exists, trying next suffix...`);
- tag = `${baseTag}-${attempt + 1}`;
- } else {
- throw e;
- }
- }
- }
diff --git a/README.md b/README.md
index d7df20e1a2..edb25b4bba 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ $ cd build-`uname -s|tr '[:upper:]' '[:lower:]'`-`uname -m`
### Arch
```
-$ sudo pacman -S cmake base-devel python freealut apr-util boost fltk glm glu hunspell minizip nanosvg libnghttp2 openjpeg2 libpipewire sdl2 vlc libvorbis xxhash
+$ sudo pacman -S cmake base-devel patchelf python freealut apr-util boost cef fltk glm glu hunspell minizip nanosvg libnghttp2 openjpeg2 libpipewire sdl2 vlc libvorbis xxhash
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
$ cmake -DCMAKE_BUILD_TYPE:STRING=Release -DADDRESS_SIZE:STRING=64 -DUSE_OPENAL:BOOL=ON -DUSE_FMODSTUDIO:BOOL=OFF -DENABLE_MEDIA_PLUGINS:BOOL=ON -DLL_TESTS:BOOL=OFF -DNDOF:BOOL=ON -DROOT_PROJECT_NAME:STRING=Megapahit -DVIEWER_CHANNEL:STRING=Megapahit -DVIEWER_BINARY_NAME:STRING=megapahit -DBUILD_SHARED_LIBS:BOOL=OFF -DINSTALL:BOOL=ON -DPACKAGE:BOOL=ON -DCMAKE_INSTALL_PREFIX=/usr ../indra
$ make -j`nproc`
@@ -31,7 +31,7 @@ $ megapahit
### Debian amd64
```
-$ sudo apt install cmake pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
+$ sudo apt install cmake patchelf pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
$ cmake -DCMAKE_BUILD_TYPE:STRING=Release -DADDRESS_SIZE:STRING=64 -DUSE_OPENAL:BOOL=ON -DUSE_FMODSTUDIO:BOOL=OFF -DENABLE_MEDIA_PLUGINS:BOOL=ON -DLL_TESTS:BOOL=OFF -DNDOF:BOOL=ON -DROOT_PROJECT_NAME:STRING=Megapahit -DVIEWER_CHANNEL:STRING=Megapahit -DVIEWER_BINARY_NAME:STRING=megapahit -DBUILD_SHARED_LIBS:BOOL=OFF -DINSTALL:BOOL=ON -DPACKAGE:BOOL=ON ../indra
$ make -j`nproc`
@@ -43,7 +43,7 @@ $ megapahit
### Debian arm64
```
-$ sudo apt install cmake pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
+$ sudo apt install cmake patchelf pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
$ cmake -DCMAKE_BUILD_TYPE:STRING=Release -DADDRESS_SIZE:STRING=64 -DUSE_OPENAL:BOOL=ON -DUSE_FMODSTUDIO:BOOL=OFF -DENABLE_MEDIA_PLUGINS:BOOL=ON -DLL_TESTS:BOOL=OFF -DNDOF:BOOL=ON -DROOT_PROJECT_NAME:STRING=Megapahit -DVIEWER_CHANNEL:STRING=Megapahit -DVIEWER_BINARY_NAME:STRING=megapahit -DBUILD_SHARED_LIBS:BOOL=OFF -DINSTALL:BOOL=ON -DPACKAGE:BOOL=ON -DOPENGL_glu_LIBRARY:FILEPATH=/usr/lib/aarch64-linux-gnu/libGLU.so -DOPENGL_glx_LIBRARY:FILEPATH=/usr/lib/aarch64-linux-gnu/libGLX.so -DOPENGL_opengl_LIBRARY:FILEPATH=/usr/lib/aarch64-linux-gnu/libOpenGL.so ../indra
$ make -j`nproc`
@@ -55,7 +55,7 @@ $ megapahit
### Fedora
```
-$ sudo dnf install cmake gcc-c++ patch patchelf rpm-build perl-FindBin freealut-devel apr-util-devel boost-devel boost-url expat-devel fltk-devel glm-devel mesa-libGLU-devel hunspell-devel minizip-ng-compat-devel libnghttp2-devel nanosvg-devel openjpeg-devel pipewire-devel pulseaudio-libs-devel sdl2-compat-devel v-hacd-devel vlc-devel libvorbis-devel libXcursor-devel libXfixes-devel libXinerama-devel xxhash-devel
+$ sudo dnf install cmake gcc-c++ patch patchelf rpm-build perl-FindBin freealut-devel apr-util-devel boost-devel cef-devel expat-devel fltk-devel glm-devel mesa-libGLU-devel hunspell-devel minizip-ng-compat-devel libnghttp2-devel nanosvg-devel openjpeg-devel pipewire-devel pulseaudio-libs-devel sdl2-compat-devel v-hacd-devel vlc-devel libvorbis-devel libXcursor-devel libXfixes-devel libXinerama-devel xxhash-devel
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
$ cmake -DCMAKE_BUILD_TYPE:STRING=Release -DADDRESS_SIZE:STRING=64 -DUSE_OPENAL:BOOL=ON -DUSE_FMODSTUDIO:BOOL=OFF -DENABLE_MEDIA_PLUGINS:BOOL=ON -DLL_TESTS:BOOL=OFF -DNDOF:BOOL=ON -DROOT_PROJECT_NAME:STRING=Megapahit -DVIEWER_CHANNEL:STRING=Megapahit -DVIEWER_BINARY_NAME:STRING=megapahit -DBUILD_SHARED_LIBS:BOOL=OFF -DINSTALL:BOOL=ON -DPACKAGE:BOOL=ON ../indra
$ make -j`nproc`
@@ -110,7 +110,7 @@ $ open newview/Megapahit.app
### openSUSE Tumbleweed
```
-$ sudo zypper install cmake gcc-c++ patch patchelf apr-util-devel boost-devel libboost_program_options-devel libboost_url1_91_0 libboost_url1_91_0-devel libboost_context-devel libboost_fiber-devel libboost_filesystem-devel libboost_regex-devel libboost_thread-devel libpng16-devel libxml++-devel libexpat-devel fltk-devel glm glu-devel hunspell-devel minizip-devel nanosvg-devel libnghttp2-devel openjpeg2-devel pipewire-devel libpulse-devel libSDL2_gfx-1_0-0 libSDL2_gfx-devel sdl2-compat-devel vlc-devel libvorbis-devel xxhash-devel zlib-ng-devel libXrender-devel libXcursor-devel libXfixes-devel libXext-devel libXft-devel libXinerama-devel freetype2-devel fontconfig-devel libjpeg8-devel libjpeg8-devel freealut-devel rpm-build
+$ sudo zypper install cmake gcc-c++ patch patchelf apr-util-devel boost-devel libboost_program_options-devel libboost_url1_91_0 libboost_url1_91_0-devel libboost_context-devel libboost_fiber-devel libboost_filesystem-devel libboost_regex-devel libboost_thread-devel libpng16-devel libxml++-devel libexpat-devel fltk-devel glm-devel glu-devel hunspell-devel minizip-devel nanosvg-devel libnghttp2-devel openjpeg2-devel pipewire-devel libpulse-devel libSDL2_gfx-1_0-0 libSDL2_gfx-devel sdl2-compat-devel vlc-devel libvorbis-devel xxhash-devel zlib-ng-devel libXrender-devel libXcursor-devel libXfixes-devel libXext-devel libXft-devel libXinerama-devel freetype2-devel fontconfig-devel libjpeg8-devel libjpeg8-devel freealut-devel rpm-build
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
@@ -126,7 +126,7 @@ $ megapahit
### Ubuntu
```
-$ sudo apt install cmake pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglm-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
+$ sudo apt install cmake patchelf pkg-config libxml2-utils libalut-dev libaprutil1-dev libboost-fiber-dev libboost-json-dev libboost-program-options-dev libboost-regex-dev libboost-url-dev libexpat1-dev libfltk1.4-dev libfontconfig-dev libfreetype-dev libglm-dev libglu1-mesa-dev libhunspell-dev libjpeg-dev libmeshoptimizer-dev libminizip-dev libnanosvg-dev libnghttp2-dev libopenjp2-7-dev libpipewire-0.3-dev libpng-dev libsdl2-dev libvlc-dev libvlccore-dev libvorbis-dev libxft-dev libxml2-dev libxxhash-dev
$ export LL_BUILD="-O3 -std=c++20 -fPIC -DLL_LINUX=1"
$ cmake -DCMAKE_BUILD_TYPE:STRING=Release -DADDRESS_SIZE:STRING=64 -DUSE_OPENAL:BOOL=ON -DUSE_FMODSTUDIO:BOOL=OFF -DENABLE_MEDIA_PLUGINS:BOOL=ON -DLL_TESTS:BOOL=OFF -DNDOF:BOOL=ON -DROOT_PROJECT_NAME:STRING=Megapahit -DVIEWER_CHANNEL:STRING=Megapahit -DVIEWER_BINARY_NAME:STRING=megapahit -DBUILD_SHARED_LIBS:BOOL=OFF -DINSTALL:BOOL=ON -DPACKAGE:BOOL=ON ../indra
$ make -j`nproc`
diff --git a/autobuild.xml b/autobuild.xml
index 357ec9f952..571da61367 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -500,7 +500,7 @@
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/dullahan/releases/download/v1.14.0-r3/dullahan-1.14.0.202408091637_118.4.1_g3dd6078_chromium-118.0.5993.54-linux64-10322607516.tar.zst</string>
+ <string>https://github.com/secondlife/dullahan/releases/download/v1.25.0-CEF_139.0.40/dullahan-1.25.0.202510152245_139.0.40_g465474a_chromium-139.0.7258.139-linux64-18544680875.tar.zst</string>
</map>
<key>name</key>
<string>linux64</string>
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 42e211c84d..90ca9866bb 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -16,6 +16,7 @@ include_guard()
include(Variables)
include(Linker)
+include(UnixInstall)
# We go to some trouble to set LL_BUILD to the set of relevant compiler flags.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{LL_BUILD}")
@@ -127,7 +128,7 @@ endif (NOT CMAKE_CXX_COMPILER_ID MATCHES GNU AND WINDOWS)
if (LINUX OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE )
- set( CMAKE_INSTALL_RPATH $ORIGIN $ORIGIN/../lib )
+ set( CMAKE_INSTALL_RPATH ${INSTALL_LIBRARY_DIR} )
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--exclude-libs,ALL")
find_program(CCACHE_EXE ccache)
@@ -190,6 +191,7 @@ if (LINUX OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(CLANG_WARNINGS
${GCC_CLANG_COMPATIBLE_WARNINGS}
# Put clang specific warning configuration here
+ -Wno-inconsistent-missing-override
)
set(GCC_WARNINGS
diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake
index 25d673e49a..3f9134cb8f 100644
--- a/indra/cmake/Boost.cmake
+++ b/indra/cmake/Boost.cmake
@@ -12,10 +12,21 @@ if (DARWIN)
elseif (WINDOWS)
target_include_directories( ll::boost SYSTEM INTERFACE ${prefix_result}/../include)
target_link_directories( ll::boost INTERFACE ${prefix_result})
- if ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
- set(sfx -vc143-mt-a64-1_91)
+ # Detect the actual toolset/version suffix from whatever vcpkg installed.
+ # Glob for boost_context-*.lib and strip the known prefix to get the suffix.
+ file(GLOB _boost_context_libs "${prefix_result}/boost_context-*.lib")
+ if (_boost_context_libs)
+ list(GET _boost_context_libs 0 _boost_context_lib)
+ get_filename_component(_boost_context_name "${_boost_context_lib}" NAME_WE)
+ string(REPLACE "boost_context" "" sfx "${_boost_context_name}")
else ()
- set(sfx -vc143-mt-x64-1_91)
+ if ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
+ set(sfx -vc143-mt-a64-1_91)
+ else ()
+ set(sfx -vc143-mt-x64-1_91)
+ endif ()
+ message(WARNING "Could not detect Boost suffix via glob; using fallback '${sfx}'. "
+ "Check that vcpkg installed boost into ${prefix_result}.")
endif ()
else ()
find_package( Boost )
@@ -152,4 +163,3 @@ target_link_libraries(ll::boost INTERFACE
if (LINUX)
target_link_libraries(ll::boost INTERFACE rt)
endif (LINUX)
-
diff --git a/indra/cmake/CEFPlugin.cmake b/indra/cmake/CEFPlugin.cmake
index 31ed86213a..117c83353e 100644
--- a/indra/cmake/CEFPlugin.cmake
+++ b/indra/cmake/CEFPlugin.cmake
@@ -1,54 +1,187 @@
# -*- cmake -*-
include(Linking)
include(Prebuilt)
+include(UnixInstall)
include_guard()
add_library( ll::cef INTERFACE IMPORTED )
-if (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64)
+if (${LINUX_DISTRO} MATCHES arch)
if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0)
- if (NOT EXISTS ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz)
+ file(
+ COPY /usr/src/cef/libcef_dll
+ DESTINATION ${CMAKE_BINARY_DIR}
+ )
+ execute_process(
+ COMMAND sed -i "s/macro(L/cmake_minimum_required(VERSION 3.28)\\nmacro(SET_LIBRARY_TARGET_PROPERTIES)\\nendmacro()\\nmacro(L/" CMakeLists.txt
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/libcef_dll
+ )
+ try_compile(LIBCEF_DLL_RESULT
+ PROJECT libcef_dll
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/libcef_dll
+ BINARY_DIR ${CMAKE_BINARY_DIR}/libcef_dll
+ CMAKE_FLAGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef -fPIC"
+ )
+ if (${LIBCEF_DLL_RESULT})
+ file(
+ COPY ${CMAKE_BINARY_DIR}/libcef_dll/libcef_dll_wrapper.a
+ DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE}
+ )
+ endif ()
+ if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz)
file(DOWNLOAD
- https://github.com/secondlife/dullahan/archive/refs/tags/v1.14.0-r3.tar.gz
- ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz
+ https://github.com/secondlife/dullahan/archive/refs/tags/v1.30.0-CEF_147.0.10.tar.gz
+ ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz
+ )
+ endif ()
+ file(ARCHIVE_EXTRACT
+ INPUT ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz
+ DESTINATION ${CMAKE_BINARY_DIR}
+ )
+ try_compile(DULLAHAN_RESULT
+ PROJECT dullahan
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10
+ BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10
+ CMAKE_FLAGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR}
+ -DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE}
+ -DCEF_WRAPPER_DIR:PATH=/usr/include/cef
+ -DCEF_WRAPPER_BUILD_DIR:PATH=${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10
+ -DCEF_LIBRARY_RELEASE:FILEPATH=${INSTALL_PREFIX}/${_LIB}/cef/libcef.so
+ -DCEF_DLL_LIBRARY_RELEASE:FILEPATH=${ARCH_PREBUILT_DIRS_RELEASE}/libcef_dll_wrapper.a
+ "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef -DWRAPPING_CEF_SHARED"
+ )
+ if (${DULLAHAN_RESULT})
+ file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/bin/release)
+ file(
+ COPY ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/dullahan_host
+ DESTINATION ${LIBS_PREBUILT_DIR}/bin/release
+ )
+ file(
+ COPY ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/libdullahan.a
+ DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE}
+ )
+ file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef)
+ file(
+ COPY
+ ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/src/dullahan.h
+ ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/src/dullahan_version.h
+ DESTINATION ${LIBS_PREBUILT_DIR}/include/cef
+ )
+ file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "0")
+ endif ()
+ endif ()
+elseif (${LINUX_DISTRO} MATCHES fedora)
+ if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0)
+ file(
+ COPY /usr/src/cef-146.0.11/libcef_dll
+ DESTINATION ${CMAKE_BINARY_DIR}
+ )
+ execute_process(
+ COMMAND sed -i "s/macro(L/cmake_minimum_required(VERSION 3.28)\\nmacro(SET_LIBRARY_TARGET_PROPERTIES)\\nendmacro()\\nmacro(L/" CMakeLists.txt
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/libcef_dll
+ )
+ try_compile(LIBCEF_DLL_RESULT
+ PROJECT libcef_dll
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/libcef_dll
+ BINARY_DIR ${CMAKE_BINARY_DIR}/libcef_dll
+ CMAKE_FLAGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef-146.0.11 -fPIC"
+ )
+ if (${LIBCEF_DLL_RESULT})
+ file(
+ COPY ${CMAKE_BINARY_DIR}/libcef_dll/libcef_dll_wrapper.a
+ DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE}
+ )
+ endif ()
+ if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz)
+ file(DOWNLOAD
+ https://github.com/secondlife/dullahan/archive/refs/tags/v1.29.0-CEF_146.0.12.tar.gz
+ ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz
+ )
+ endif ()
+ file(ARCHIVE_EXTRACT
+ INPUT ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz
+ DESTINATION ${CMAKE_BINARY_DIR}
+ )
+ try_compile(DULLAHAN_RESULT
+ PROJECT dullahan
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12
+ BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12
+ CMAKE_FLAGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR}
+ -DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE}
+ -DCEF_WRAPPER_DIR:PATH=/usr/include/cef
+ -DCEF_WRAPPER_BUILD_DIR:PATH=${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12
+ -DCEF_LIBRARY_RELEASE:FILEPATH=${INSTALL_PREFIX}/${_LIB}/cef/libcef.so
+ -DCEF_DLL_LIBRARY_RELEASE:FILEPATH=${ARCH_PREBUILT_DIRS_RELEASE}/libcef_dll_wrapper.a
+ "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef-146.0.11 -DWRAPPING_CEF_SHARED"
+ )
+ if (${DULLAHAN_RESULT})
+ file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/bin/release)
+ file(
+ COPY ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/dullahan_host
+ DESTINATION ${LIBS_PREBUILT_DIR}/bin/release
+ )
+ file(
+ COPY ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/libdullahan.a
+ DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE}
+ )
+ file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef)
+ file(
+ COPY
+ ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/src/dullahan.h
+ ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/src/dullahan_version.h
+ DESTINATION ${LIBS_PREBUILT_DIR}/include/cef
+ )
+ file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "0")
+ endif ()
+ endif ()
+elseif (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64)
+ if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0)
+ if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz)
+ file(DOWNLOAD
+ https://github.com/secondlife/dullahan/archive/refs/tags/v1.24.0-CEF_139.0.40.tar.gz
+ ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz
)
endif ()
file(ARCHIVE_EXTRACT
- INPUT ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz
+ INPUT ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz
DESTINATION ${CMAKE_BINARY_DIR}
)
execute_process(
COMMAND sed -i "/#include <vector>/a #include <cstdint>" dullahan.h
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src
)
file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef)
try_compile(DULLAHAN_RESULT
PROJECT dullahan
- SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3
- BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40
+ BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40
CMAKE_FLAGS
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
- -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}
- -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET}
-DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR}
-DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE}
-DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON
- -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS}
-DUSE_SPOTIFY_CEF:BOOL=ON
- -DSPOTIFY_CEF_URL:STRING=https://cef-builds.spotifycdn.com/cef_binary_118.4.1%2Bg3dd6078%2Bchromium-118.0.5993.54_linuxarm64_beta_minimal.tar.bz2
+ -DSPOTIFY_CEF_URL:STRING=https://cef-builds.spotifycdn.com/cef_binary_139.0.40%2Bg465474a%2Bchromium-139.0.7258.139_linuxarm64_minimal.tar.bz2
-DPROJECT_ARCH:STRING=${CMAKE_SYSTEM_PROCESSOR}
- -DENABLE_CXX11_ABI:BOOL=ON
)
if (${DULLAHAN_RESULT})
execute_process(
COMMAND ${CMAKE_MAKE_PROGRAM} install
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40
OUTPUT_VARIABLE dullahan_installed
)
file(
COPY
- ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src/dullahan.h
- ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src/dullahan_version.h
+ ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src/dullahan.h
+ ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src/dullahan_version.h
DESTINATION ${LIBS_PREBUILT_DIR}/include/cef
)
file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "${dullahan_installed}")
@@ -59,9 +192,17 @@ use_prebuilt_binary(dullahan)
endif ()
execute_process(
- COMMAND patchelf --remove-rpath bin/release/dullahan_host
+ COMMAND patchelf --set-rpath ${INSTALL_LIBRARY_DIR} bin/release/dullahan_host
WORKING_DIRECTORY ${LIBS_PREBUILT_DIR}
+)
+
+if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora))
+ target_include_directories( ll::cef SYSTEM INTERFACE /usr/include/cef/include)
+ execute_process(
+ COMMAND patchelf --add-rpath ${INSTALL_PREFIX}/${_LIB}/cef bin/release/dullahan_host
+ WORKING_DIRECTORY ${LIBS_PREBUILT_DIR}
)
+endif ()
target_include_directories( ll::cef SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/cef)
@@ -129,6 +270,9 @@ elseif (DARWIN)
)
elseif (LINUX)
+ if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora))
+ target_link_directories( ll::cef INTERFACE ${INSTALL_PREFIX}/${_LIB}/cef )
+ endif ()
target_link_libraries( ll::cef INTERFACE
libdullahan.a
cef
diff --git a/indra/cmake/Discord.cmake b/indra/cmake/Discord.cmake
index f474457b03..68e6f59ecb 100644
--- a/indra/cmake/Discord.cmake
+++ b/indra/cmake/Discord.cmake
@@ -9,7 +9,7 @@ target_compile_definitions(ll::discord_sdk INTERFACE LL_DISCORD=1)
if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/discord_sdk_installed OR NOT ${discord_sdk_installed} EQUAL 0)
file(ARCHIVE_EXTRACT
- INPUT $ENV{HOME}/Downloads/DiscordSocialSdk-1.8.13395.zip
+ INPUT $ENV{HOME}/Downloads/DiscordSocialSdk-1.9.15780.zip
DESTINATION ${CMAKE_BINARY_DIR}
)
file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/discord_sdk)
@@ -19,9 +19,12 @@ if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKIN
${CMAKE_BINARY_DIR}/discord_social_sdk/include/discordpp.h
DESTINATION ${LIBS_PREBUILT_DIR}/include/discord_sdk
)
+ if ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
+ set(DISCORD_PLATFORM /arm64)
+ endif ()
if (WINDOWS)
file(
- COPY ${CMAKE_BINARY_DIR}/discord_social_sdk/bin/release/discord_partner_sdk.dll
+ COPY ${CMAKE_BINARY_DIR}/discord_social_sdk/bin/release${DISCORD_PLATFORM}/discord_partner_sdk.dll
DESTINATION ${LIBS_PREBUILT_DIR}/bin/release
)
set(LIBRARY_EXTENSION lib)
@@ -39,7 +42,7 @@ if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKIN
)
else ()
file(
- COPY ${CMAKE_BINARY_DIR}/discord_social_sdk/lib/release/${LIBRARY_PREFIX}discord_partner_sdk.${LIBRARY_EXTENSION}
+ COPY ${CMAKE_BINARY_DIR}/discord_social_sdk/lib/release${DISCORD_PLATFORM}/${LIBRARY_PREFIX}discord_partner_sdk.${LIBRARY_EXTENSION}
DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE}
)
endif ()
diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake
index df8d96109f..4708834653 100644
--- a/indra/cmake/LLPrimitive.cmake
+++ b/indra/cmake/LLPrimitive.cmake
@@ -69,10 +69,19 @@ if (TRUE)
elseif (WINDOWS)
set(BOOST_CFLAGS -I${prefix_result}/../include)
set(BOOST_LIBS -L${prefix_result})
- if ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
- set(BOOST_LIBRARY_SUFFIX -vc143-mt-a64-1_91)
+ # Detect actual toolset/version suffix from vcpkg-installed libs.
+ file(GLOB _boost_context_libs "${prefix_result}/boost_context-*.lib")
+ if (_boost_context_libs)
+ list(GET _boost_context_libs 0 _boost_context_lib)
+ get_filename_component(_boost_context_name "${_boost_context_lib}" NAME_WE)
+ string(REPLACE "boost_context" "" BOOST_LIBRARY_SUFFIX "${_boost_context_name}")
else ()
- set(BOOST_LIBRARY_SUFFIX -vc143-mt-x64-1_91)
+ if ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
+ set(BOOST_LIBRARY_SUFFIX -vc143-mt-a64-1_91)
+ else ()
+ set(BOOST_LIBRARY_SUFFIX -vc143-mt-x64-1_91)
+ endif ()
+ message(WARNING "Could not detect Boost suffix via glob in LLPrimitive; using fallback.")
endif ()
elseif (CMAKE_SYSTEM_NAME MATCHES FreeBSD)
set(BOOST_CFLAGS -I/usr/local/include)
diff --git a/indra/cmake/UnixInstall.cmake b/indra/cmake/UnixInstall.cmake
index 1fd17a0142..b82cbbcc2f 100644
--- a/indra/cmake/UnixInstall.cmake
+++ b/indra/cmake/UnixInstall.cmake
@@ -22,8 +22,8 @@ if (INSTALL)
set(_LIB lib)
endif ()
- set(INSTALL_LIBRARY_DIR ${INSTALL_PREFIX}/${_LIB} CACHE PATH
- "Installation directory for read-only shared files.")
+ set(INSTALL_LIBRARY_DIR ${INSTALL_PREFIX}/${_LIB}/${VIEWER_BINARY_NAME} CACHE PATH
+ "Installation directory for dynamic library files and their resources.")
set(INSTALL_SHARE_DIR ${INSTALL_PREFIX}/share CACHE PATH
"Installation directory for read-only shared files.")
diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt
index a01d9fc632..1c53b0263c 100644
--- a/indra/llwebrtc/CMakeLists.txt
+++ b/indra/llwebrtc/CMakeLists.txt
@@ -77,11 +77,11 @@ if (INSTALL)
if (DARWIN)
set(_LIB ../Frameworks)
elseif (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu))
- set(_LIB lib/${ARCH}-linux-gnu)
+ set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME})
elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo))
- set(_LIB lib${ADDRESS_SIZE})
+ set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME})
else ()
- set(_LIB lib)
+ set(_LIB lib/${VIEWER_BINARY_NAME})
endif ()
if (WINDOWS)
diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt
index 236e117aa7..4736eef420 100644
--- a/indra/media_plugins/cef/CMakeLists.txt
+++ b/indra/media_plugins/cef/CMakeLists.txt
@@ -50,7 +50,10 @@ if (LINUX)
)
list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER})
- set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'")
+ set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id")
+ if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora))
+ set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH};${INSTALL_PREFIX}/${_LIB}/cef)
+ endif ()
list(APPEND media_plugin_cef_LINK_LIBRARIES llwindow )
elseif (DARWIN)
list(APPEND media_plugin_cef_SOURCE_FILES volume_catcher_null.cpp)
@@ -145,45 +148,62 @@ if (INSTALL)
)
elseif (LINUX)
if (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu))
- set(_LIB lib/${ARCH}-linux-gnu)
+ set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME})
elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo))
- set(_LIB lib${ADDRESS_SIZE})
+ set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME})
else ()
- set(_LIB lib)
+ set(_LIB lib/${VIEWER_BINARY_NAME})
endif ()
if (${LINUX_DISTRO} MATCHES arch)
install(
- PROGRAMS
- ${AUTOBUILD_INSTALL_DIR}/bin/release/chrome-sandbox
- ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host
+ PROGRAMS ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host
DESTINATION lib/${VIEWER_BINARY_NAME}
- #PERMISSIONS SETUID OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
- else (${LINUX_DISTRO} MATCHES arch)
+ elseif (${LINUX_DISTRO} MATCHES fedora)
+ install(
+ PROGRAMS ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host
+ DESTINATION libexec/${VIEWER_BINARY_NAME}
+ )
+ else ()
install(
PROGRAMS
${AUTOBUILD_INSTALL_DIR}/bin/release/chrome-sandbox
${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host
DESTINATION libexec/${VIEWER_BINARY_NAME}
- #PERMISSIONS SETUID OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
- endif (${LINUX_DISTRO} MATCHES arch)
- install(
- FILES
- ${ARCH_PREBUILT_DIRS_RELEASE}/libcef.so
- ${ARCH_PREBUILT_DIRS_RELEASE}/libvk_swiftshader.so
- ${AUTOBUILD_INSTALL_DIR}/bin/release/snapshot_blob.bin
- ${AUTOBUILD_INSTALL_DIR}/bin/release/v8_context_snapshot.bin
- ${AUTOBUILD_INSTALL_DIR}/resources/chrome_100_percent.pak
- ${AUTOBUILD_INSTALL_DIR}/resources/chrome_200_percent.pak
- ${AUTOBUILD_INSTALL_DIR}/resources/icudtl.dat
- ${AUTOBUILD_INSTALL_DIR}/resources/resources.pak
- DESTINATION ${_LIB}
+ endif ()
+ if (NOT (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora)))
+ file(MAKE_DIRECTORY ${AUTOBUILD_INSTALL_DIR}/lib/release/${VIEWER_BINARY_NAME})
+ file(CREATE_LINK
+ "../libGLESv2.so"
+ "${AUTOBUILD_INSTALL_DIR}/lib/release/${VIEWER_BINARY_NAME}/libGLESv2.so"
+ SYMBOLIC
)
- install(
- DIRECTORY ${AUTOBUILD_INSTALL_DIR}/resources/locales
- DESTINATION ${_LIB}
+ file(CREATE_LINK
+ "../libvulkan.so.1"
+ "${AUTOBUILD_INSTALL_DIR}/lib/release/${VIEWER_BINARY_NAME}/libvulkan.so.1"
+ SYMBOLIC
+ )
+ install(
+ FILES
+ ${ARCH_PREBUILT_DIRS_RELEASE}/libcef.so
+ ${ARCH_PREBUILT_DIRS_RELEASE}/libvk_swiftshader.so
+ ${ARCH_PREBUILT_DIRS_RELEASE}/libEGL.so
+ ${ARCH_PREBUILT_DIRS_RELEASE}/v8_context_snapshot.bin
+ ${ARCH_PREBUILT_DIRS_RELEASE}/vk_swiftshader_icd.json
+ ${ARCH_PREBUILT_DIRS_RELEASE}/${VIEWER_BINARY_NAME}/libGLESv2.so
+ ${ARCH_PREBUILT_DIRS_RELEASE}/${VIEWER_BINARY_NAME}/libvulkan.so.1
+ ${AUTOBUILD_INSTALL_DIR}/resources/chrome_100_percent.pak
+ ${AUTOBUILD_INSTALL_DIR}/resources/chrome_200_percent.pak
+ ${AUTOBUILD_INSTALL_DIR}/resources/icudtl.dat
+ ${AUTOBUILD_INSTALL_DIR}/resources/resources.pak
+ DESTINATION ${_LIB}
)
+ install(
+ DIRECTORY ${AUTOBUILD_INSTALL_DIR}/resources/locales
+ DESTINATION ${_LIB}
+ )
+ endif ()
elseif (WINDOWS)
set(_LIB llplugin)
install(
@@ -213,7 +233,7 @@ if (INSTALL)
DESTINATION llplugin
)
else ()
- set(_LIB lib)
+ set(_LIB lib/${VIEWER_BINARY_NAME})
endif ()
if (NOT WINDOWS)
install(TARGETS ${PROJECT_NAME} DESTINATION ${_LIB})
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp
index a2b664c755..d5ec3dbb20 100644
--- a/indra/media_plugins/cef/media_plugin_cef.cpp
+++ b/indra/media_plugins/cef/media_plugin_cef.cpp
@@ -957,7 +957,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
authResponse(message_in);
}
-#if !LL_LINUX
if (message_name == "edit_undo")
{
mCEFLib->editUndo();
@@ -966,7 +965,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mCEFLib->editRedo();
}
-#endif
if (message_name == "edit_cut")
{
mCEFLib->editCut();
@@ -979,7 +977,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mCEFLib->editPaste();
}
-#if !LL_LINUX
if (message_name == "edit_delete")
{
mCEFLib->editDelete();
@@ -992,7 +989,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mCEFLib->viewSource();
}
-#endif
}
else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
{
@@ -1176,28 +1172,19 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD
//
void MediaPluginCEF::checkEditState()
{
-#if !LL_LINUX
bool can_undo = mCEFLib->editCanUndo();
bool can_redo = mCEFLib->editCanRedo();
-#endif
bool can_cut = mCEFLib->editCanCut();
bool can_copy = mCEFLib->editCanCopy();
bool can_paste = mCEFLib->editCanPaste();
-#if !LL_LINUX
bool can_delete = mCEFLib->editCanDelete();
bool can_select_all = mCEFLib->editCanSelectAll();
-#endif
-#if LL_LINUX
- if ((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste))
-#else
if ((can_undo != mCanUndo) || (can_redo != mCanRedo) || (can_cut != mCanCut) || (can_copy != mCanCopy)
|| (can_paste != mCanPaste) || (can_delete != mCanDelete) || (can_select_all != mCanSelectAll))
-#endif
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state");
-#if !LL_LINUX
if (can_undo != mCanUndo)
{
mCanUndo = can_undo;
@@ -1209,7 +1196,6 @@ void MediaPluginCEF::checkEditState()
mCanRedo = can_redo;
message.setValueBoolean("redo", can_redo);
}
-#endif
if (can_cut != mCanCut)
{
@@ -1229,7 +1215,6 @@ void MediaPluginCEF::checkEditState()
message.setValueBoolean("paste", can_paste);
}
-#if !LL_LINUX
if (can_delete != mCanDelete)
{
mCanDelete = can_delete;
@@ -1241,7 +1226,6 @@ void MediaPluginCEF::checkEditState()
mCanSelectAll = can_select_all;
message.setValueBoolean("select_all", can_select_all);
}
-#endif
sendMessage(message);
}
diff --git a/indra/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt
index b8b3bd68f3..96790a8037 100644
--- a/indra/media_plugins/libvlc/CMakeLists.txt
+++ b/indra/media_plugins/libvlc/CMakeLists.txt
@@ -88,11 +88,11 @@ if (INSTALL)
DESTINATION ${_LIB}/plugins
)
elseif (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu))
- set(_LIB lib/${ARCH}-linux-gnu)
+ set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME})
elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo))
- set(_LIB lib${ADDRESS_SIZE})
+ set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME})
else ()
- set(_LIB lib)
+ set(_LIB lib/${VIEWER_BINARY_NAME})
endif ()
if (WINDOWS)
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${PROJECT_NAME}.dll DESTINATION llplugin)
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7812735867..e070fb3da3 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -50,9 +50,6 @@ include(VisualLeakDetector)
#include(VulkanGltf)
include(ZLIBNG)
include(LLPrimitive)
-if (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64)
- include(UnixInstall)
-endif ()
if (ENABLE_MEDIA_PLUGINS)
include(LibVLCPlugin)
@@ -1534,7 +1531,6 @@ if (NOT (DARWIN OR WINDOWS))
llappviewerlinux.cpp
PROPERTIES
COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}"
- COMPILE_FLAGS "-DAPP_PLUGIN_DIR=\\\"${INSTALL_LIBRARY_DIR}\\\""
)
#LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp)
if (NOT CMAKE_CXX_COMPILER_ID MATCHES AppleClang)
@@ -2206,8 +2202,14 @@ endif ()
if (ENABLE_MEDIA_PLUGINS)
target_link_libraries(${VIEWER_BINARY_NAME} ll::libvlc )
+ # Tell the viewer source which media-library version headers are
+ # actually available in this build, so version reporting in
+ # llappviewer.cpp is gated on the build configuration rather than on
+ # a CPU/compiler macro. Mirrors the link availability above exactly.
+ target_compile_definitions(${VIEWER_BINARY_NAME} PRIVATE LL_VLC=1)
if (DARWIN OR LINUX)
target_link_libraries(${VIEWER_BINARY_NAME} ll::cef )
+ target_compile_definitions(${VIEWER_BINARY_NAME} PRIVATE LL_CEF=1)
endif ()
endif ()
@@ -2399,7 +2401,7 @@ if (LINUX)
set(CPACK_RPM_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_DESCRIPTION}
CACHE STRING "RPM package description.")
if (${LINUX_DISTRO} MATCHES fedora)
- set(CPACK_RPM_PACKAGE_REQUIRES "freealut, apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, expat, fltk, mesa-libGLU, hunspell, libnghttp2, openjpeg, sdl2-compat, vlc-libs, vlc-plugins-base, libvorbis"
+ set(CPACK_RPM_PACKAGE_REQUIRES "freealut, apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, cef, expat, fltk, mesa-libGLU, hunspell, minizip-ng-compat, libnghttp2, openjpeg, sdl2-compat, vlc-libs, vlc-plugins-base, libvorbis"
CACHE STRING "RPM package requirements.")
else ()
set(CPACK_RPM_PACKAGE_REQUIRES "libalut0, libapr-util1-0, libboost_fiber1_91_0, libboost_program_options1_91_0, libboost_regex1_91_0, libboost_thread1_91_0, libboost_url1_91_0, libboost_url1_91_0-x86-64-v3, libpng16-16 expat, libfltk1_3, libGLU1, libhunspell-1_7-0, libnghttp2-14, openjpeg2, libSDL2-2_0-0, libvlc5, libvorbis0"
diff --git a/indra/newview/FixBundle.cmake.in b/indra/newview/FixBundle.cmake.in
index 77b4683c88..d915cac35b 100644
--- a/indra/newview/FixBundle.cmake.in
+++ b/indra/newview/FixBundle.cmake.in
@@ -94,14 +94,17 @@ file(CREATE_LINK
"${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/libicuuc.78.dylib"
SYMBOLIC
)
+if(@ENABLE_MEDIA_PLUGINS@)
file(CREATE_LINK
"../../../../Frameworks/Chromium Embedded Framework.framework"
"${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework"
SYMBOLIC
)
+endif()
fixup_bundle(${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/MacOS/${VIEWER_CHANNEL} "" "${dirs}")
+if(@ENABLE_MEDIA_PLUGINS@)
file(CHMOD
"${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/DullahanHelper.app/Contents/MacOS/DullahanHelper"
"${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/DullahanHelper (Alerts).app/Contents/MacOS/DullahanHelper (Alerts)"
@@ -110,7 +113,9 @@ file(CHMOD
"${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/DullahanHelper (Renderer).app/Contents/MacOS/DullahanHelper (Renderer)"
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
+endif()
+if(@ENABLE_MEDIA_PLUGINS@)
execute_process(
COMMAND hdiutil detach /Volumes/VLC\ media\ player
COMMAND lipo libalut.0.dylib
@@ -158,9 +163,9 @@ execute_process(
COMMAND lipo libbz2.1.0.8.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
-output libbz2.1.0.8.dylib
- COMMAND lipo libexpat.1.11.3.dylib
+ COMMAND lipo libexpat.1.12.1.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
- -output libexpat.1.11.3.dylib
+ -output libexpat.1.12.1.dylib
COMMAND lipo libfreetype.6.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
-output libfreetype.6.dylib
@@ -233,6 +238,7 @@ execute_process(
WORKING_DIRECTORY ${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Frameworks
ERROR_QUIET
)
+endif()
message("By default, the situation is assumed to be the strictest, an Apple Silicon Mac with the default security settings. Running a native self-built viewer on it without correct codesigning would lead to a crash. Also, codesigning requires administrative access. If you believe you're not in such a situation, you can remove the sudos in this file.")
execute_process(
@@ -258,7 +264,7 @@ execute_process(
Frameworks/libbrotlidec.1.2.0.dylib
Frameworks/libbz2.1.0.8.dylib
Frameworks/libdiscord_partner_sdk.dylib
- Frameworks/libexpat.1.11.3.dylib
+ Frameworks/libexpat.1.12.1.dylib
Frameworks/libfreetype.6.dylib
Frameworks/libhunspell-1.7.0.dylib
Frameworks/libiconv.2.dylib
@@ -637,6 +643,7 @@ execute_process(
Resources/SLPlugin.app/Contents/Frameworks/DullahanHelper\ \(Plugin\).app/Contents/MacOS/DullahanHelper\ \(Plugin\)
WORKING_DIRECTORY ${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents
)
+if(@ENABLE_MEDIA_PLUGINS@)
execute_process(
COMMAND sudo codesign -f -s ${SIGNING_IDENTITY} --timestamp -o runtime --runtime-version ${CMAKE_OSX_DEPLOYMENT_TARGET}
DullahanHelper.app
@@ -657,6 +664,7 @@ execute_process(
DullahanHelper\ \(Renderer\).app
WORKING_DIRECTORY ${viewer_BINARY_DIR}/${VIEWER_CHANNEL}.app/Contents/Resources/SLPlugin.app/Contents/Frameworks
)
+endif()
execute_process(
COMMAND sudo codesign -f -s ${SIGNING_IDENTITY} --timestamp -o runtime --runtime-version ${CMAKE_OSX_DEPLOYMENT_TARGET}
--entitlements ${CMAKE_SOURCE_DIR}/newview/slplugin.entitlements
diff --git a/indra/newview/FixPackage.cmake.in b/indra/newview/FixPackage.cmake.in
index e67026aabb..4ae777c2f4 100644
--- a/indra/newview/FixPackage.cmake.in
+++ b/indra/newview/FixPackage.cmake.in
@@ -158,9 +158,9 @@ execute_process(
COMMAND lipo libbz2.1.0.8.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
-output libbz2.1.0.8.dylib
- COMMAND lipo libexpat.1.11.3.dylib
+ COMMAND lipo libexpat.1.12.1.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
- -output libexpat.1.11.3.dylib
+ -output libexpat.1.12.1.dylib
COMMAND lipo libfreetype.6.dylib
-thin ${CMAKE_OSX_ARCHITECTURES}
-output libfreetype.6.dylib
@@ -258,7 +258,7 @@ execute_process(
Frameworks/libbrotlidec.1.2.0.dylib
Frameworks/libbz2.1.0.8.dylib
Frameworks/libdiscord_partner_sdk.dylib
- Frameworks/libexpat.1.11.3.dylib
+ Frameworks/libexpat.1.12.1.dylib
Frameworks/libfreetype.6.dylib
Frameworks/libhunspell-1.7.0.dylib
Frameworks/libiconv.2.dylib
diff --git a/indra/newview/PKGBUILD.in b/indra/newview/PKGBUILD.in
index 3de9558000..b2830ed145 100644
--- a/indra/newview/PKGBUILD.in
+++ b/indra/newview/PKGBUILD.in
@@ -6,7 +6,7 @@ pkgdesc="${VIEWER_PACKAGE_COMMENT}"
arch=('${CMAKE_SYSTEM_PROCESSOR}')
url="https://${VIEWER_PACKAGE_DOMAIN_NAME}"
license=('LGPL-2.1')
-depends=(freealut apr-util at-spi2-core boost-libs fltk glu hunspell libnghttp2 openjpeg2 sdl2 vlc libvorbis)
+depends=(freealut apr-util at-spi2-core boost-libs cef fltk glu hunspell libnghttp2 openjpeg2 sdl2 vlc libvorbis)
package() {
cd "$startdir"
diff --git a/indra/newview/ViewerInstall.cmake b/indra/newview/ViewerInstall.cmake
index 56a3394943..7717605f1f 100644
--- a/indra/newview/ViewerInstall.cmake
+++ b/indra/newview/ViewerInstall.cmake
@@ -130,18 +130,29 @@ elseif (WINDOWS)
set(BOOST_PLATFORM x${ADDRESS_SIZE})
endif ()
+ # Detect the actual Boost DLL suffix from vcpkg-installed binaries.
+ file(GLOB _boost_context_dlls "${prefix_result}/../bin/boost_context-*.dll")
+ if (_boost_context_dlls)
+ list(GET _boost_context_dlls 0 _boost_context_dll)
+ get_filename_component(_boost_context_dll_name "${_boost_context_dll}" NAME_WE)
+ string(REPLACE "boost_context" "" BOOST_DLL_SFX "${_boost_context_dll_name}")
+ else ()
+ set(BOOST_DLL_SFX -vc143-mt-${BOOST_PLATFORM}-1_91)
+ message(WARNING "Could not detect Boost DLL suffix via glob; using fallback '${BOOST_DLL_SFX}'.")
+ endif ()
+
install(
PROGRAMS
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${VIEWER_BINARY_NAME}.exe
${prefix_result}/../bin/OpenAL32.dll
${prefix_result}/../bin/alut.dll
- ${prefix_result}/../bin/boost_context-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_fiber-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_filesystem-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_json-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_program_options-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_thread-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_url-vc143-mt-${BOOST_PLATFORM}-1_91.dll
+ ${prefix_result}/../bin/boost_context${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_fiber${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_filesystem${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_json${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_program_options${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_thread${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_url${BOOST_DLL_SFX}.dll
${prefix_result}/../bin/brotlicommon.dll
${prefix_result}/../bin/brotlidec.dll
${prefix_result}/../bin/bz2.dll
@@ -176,8 +187,8 @@ elseif (WINDOWS)
install(
PROGRAMS
- ${prefix_result}/../bin/boost_context-vc143-mt-${BOOST_PLATFORM}-1_91.dll
- ${prefix_result}/../bin/boost_fiber-vc143-mt-${BOOST_PLATFORM}-1_91.dll
+ ${prefix_result}/../bin/boost_context${BOOST_DLL_SFX}.dll
+ ${prefix_result}/../bin/boost_fiber${BOOST_DLL_SFX}.dll
${prefix_result}/../bin/libapr-1.dll
${prefix_result}/../bin/libaprutil-1.dll
${prefix_result}/../bin/libexpat.dll
@@ -201,7 +212,7 @@ if (LINUX)
if (USE_DISCORD)
install(
FILES ${ARCH_PREBUILT_DIRS_RELEASE}/libdiscord_partner_sdk.so
- DESTINATION ${_LIB}
+ DESTINATION ${_LIB}/${VIEWER_BINARY_NAME}
)
endif ()
if (USE_FMODSTUDIO)
@@ -209,7 +220,7 @@ if (LINUX)
${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so
${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so.13
${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so.13.34
- DESTINATION ${_LIB})
+ DESTINATION ${_LIB}/${VIEWER_BINARY_NAME})
endif (USE_FMODSTUDIO)
endif (LINUX)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 481cafafd1..8b6c5e2e1a 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1365,6 +1365,63 @@
<integer>1</integer>
</map>
<!-- End NaCl/Firestorm port -->
+ <!-- OTS over-the-shoulder aim settings -->
+ <key>OTSEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>When true, M key enters OTS shoulder cam instead of first-person mouselook</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>OTSCameraDistance</key>
+ <map>
+ <key>Comment</key>
+ <string>OTS camera distance behind avatar (meters)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>3.0</real>
+ </map>
+ <key>OTSCameraSide</key>
+ <map>
+ <key>Comment</key>
+ <string>OTS camera side offset (negative = right of avatar)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>-0.5</real>
+ </map>
+ <key>OTSCameraHeight</key>
+ <map>
+ <key>Comment</key>
+ <string>OTS camera height above avatar root (meters)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.5</real>
+ </map>
+ <key>OTSFocusDistance</key>
+ <map>
+ <key>Comment</key>
+ <string>OTS focus point distance in front of avatar (meters)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>10.0</real>
+ </map>
+ <!-- End OTS settings -->
<key>CameraOffset</key>
<map>
<key>Comment</key>
@@ -1402,8 +1459,8 @@
<key>Value</key>
<array>
<real>-3.0</real>
- <real>0.5</real>
- <real>0.2</real>
+ <real>0.0</real>
+ <real>0.75</real>
</array>
</map>
<key>CameraOffsetScale</key>
@@ -3272,9 +3329,9 @@
<string>Vector3D</string>
<key>Value</key>
<array>
- <real>0.9</real>
- <real>0.5</real>
- <real>0.2</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
</array>
</map>
<key>AvatarSitRotation</key>
@@ -4684,6 +4741,28 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>KeepCameraOnLocalTeleport</key>
+ <map>
+ <key>Comment</key>
+ <string>Do not reset the camera position or mode when teleporting within the same region.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>DisableTeleportScreens</key>
+ <map>
+ <key>Comment</key>
+ <string>Do not show the fullscreen teleport progress/black screen during teleports.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>MiniMapAutoCenter</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 23647487b0..75a9ef58fc 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -2123,6 +2123,13 @@ std::ostream& operator<<(std::ostream &s, const LLAgent &agent)
//-----------------------------------------------------------------------------
bool LLAgent::needsRenderAvatar()
{
+ // OTS mode: always render avatar — we are in third-person even though
+ // mouselook input is active.
+ if (gAgentCamera.cameraOTS())
+ {
+ return mShowAvatar && mOutfitChosen;
+ }
+
if (gAgentCamera.cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson)
{
return false;
@@ -2134,6 +2141,11 @@ bool LLAgent::needsRenderAvatar()
// true if we need to render your own avatar's head.
bool LLAgent::needsRenderHead()
{
+ // OTS mode: always render head — avatar is fully visible.
+ if (gAgentCamera.cameraOTS())
+ {
+ return mShowAvatar;
+ }
return (LLVOAvatar::sVisibleInFirstPerson && LLPipeline::sReflectionRender) || (mShowAvatar && !gAgentCamera.cameraMouselook());
}
@@ -2247,7 +2259,8 @@ void LLAgent::endAnimationUpdateUI()
}
// clean up UI from mode we're leaving
- if (gAgentCamera.getLastCameraMode() == CAMERA_MODE_MOUSELOOK )
+ if (gAgentCamera.getLastCameraMode() == CAMERA_MODE_MOUSELOOK
+ || gAgentCamera.getLastCameraMode() == CAMERA_MODE_OTS)
{
gToolBarView->setToolBarsVisible(true);
// show mouse cursor
@@ -2369,7 +2382,8 @@ void LLAgent::endAnimationUpdateUI()
//---------------------------------------------------------------------
// Set up UI for mode we're entering
//---------------------------------------------------------------------
- if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK
+ || gAgentCamera.getCameraMode() == CAMERA_MODE_OTS)
{
// clean up UI
// first show anything hidden by UI toggle
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 369a6d3697..6f9f8bfa2a 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -1176,7 +1176,7 @@ void LLAgentCamera::updateLookAt(const S32 mouse_x, const S32 mouse_y)
LLVector3 headLookAxis;
LLCoordFrame frameCamera = *((LLCoordFrame*)LLViewerCamera::getInstance());
- if (cameraMouselook())
+ if (cameraMouselook() || cameraOTS())
{
lookAtType = LOOKAT_TARGET_MOUSELOOK;
}
@@ -1409,7 +1409,7 @@ void LLAgentCamera::updateCamera()
gAgent.setShowAvatar(true);
}
- if (isAgentAvatarValid() && (mCameraMode != CAMERA_MODE_MOUSELOOK))
+ if (isAgentAvatarValid() && mCameraMode != CAMERA_MODE_MOUSELOOK)
{
gAgentAvatarp->updateAttachmentVisibility(mCameraMode);
}
@@ -1497,7 +1497,8 @@ void LLAgentCamera::updateCamera()
}
gAgent.setLastPositionGlobal(global_pos);
- if (LLVOAvatar::sVisibleInFirstPerson && isAgentAvatarValid() && !gAgentAvatarp->isSitting() && cameraMouselook())
+ // Exclude OTS — shoulder camera position must not be overridden by head-tracking.
+ if (LLVOAvatar::sVisibleInFirstPerson && isAgentAvatarValid() && !gAgentAvatarp->isSitting() && cameraMouselook() && !cameraOTS())
{
LLVector3 head_pos = gAgentAvatarp->mHeadp->getWorldPosition() +
LLVector3(0.08f, 0.f, 0.05f) * gAgentAvatarp->mHeadp->getWorldRotation() +
@@ -1598,6 +1599,19 @@ LLVector3d LLAgentCamera::calcFocusPositionTargetGlobal()
mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedFocus());
return mFocusTargetGlobal;
}
+ else if (mCameraMode == CAMERA_MODE_OTS)
+ {
+ // Focus in front of avatar at aim height
+ static LLCachedControl<F32> ots_focus_dist(gSavedSettings, "OTSFocusDistance", 10.0f);
+ static LLCachedControl<F32> ots_height(gSavedSettings, "OTSCameraHeight", 0.5f);
+ static LLCachedControl<F32> ots_side(gSavedSettings, "OTSCameraSide", -0.5f);
+ LLVector3 focus_local((F32)ots_focus_dist, (F32)ots_side * 0.3f, (F32)ots_height * 0.5f);
+ LLQuaternion agent_rot = gAgent.getFrameAgent().getQuaternion();
+ LLVector3 focus_world = focus_local * agent_rot;
+ LLVector3d avatar_pos = gAgent.getPosGlobalFromAgent(getAvatarRootPosition());
+ mFocusTargetGlobal = avatar_pos + LLVector3d(focus_world);
+ return mFocusTargetGlobal;
+ }
else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
LLVector3d at_axis(1.0, 0.0, 0.0);
@@ -1776,6 +1790,18 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit)
{
camera_position_global = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedPosition());
}
+ else if (mCameraMode == CAMERA_MODE_OTS)
+ {
+ // Shoulder offset camera — avatar-local space: X=forward, Y=left, Z=up
+ static LLCachedControl<F32> ots_dist(gSavedSettings, "OTSCameraDistance", 3.0f);
+ static LLCachedControl<F32> ots_side(gSavedSettings, "OTSCameraSide", -0.5f);
+ static LLCachedControl<F32> ots_height(gSavedSettings, "OTSCameraHeight", 0.5f);
+ LLVector3 local_offset(-(F32)ots_dist, (F32)ots_side, (F32)ots_height);
+ LLQuaternion agent_rot = gAgent.getFrameAgent().getQuaternion();
+ LLVector3 world_offset = local_offset * agent_rot;
+ LLVector3d avatar_pos = gAgent.getPosGlobalFromAgent(getAvatarRootPosition());
+ camera_position_global = avatar_pos + LLVector3d(world_offset);
+ }
else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
{
if (!isAgentAvatarValid() || gAgentAvatarp->mDrawable.isNull())
@@ -2244,6 +2270,58 @@ void LLAgentCamera::changeCameraToDefault()
//-----------------------------------------------------------------------------
+// changeCameraToOTS()
+// Over-the-shoulder aim mode.
+// Calls changeCameraToMouselook() to inherit ALL of its input setup
+// (cursor hiding, mouse capture, control flags, focus management),
+// then immediately overrides mCameraMode to CAMERA_MODE_OTS so that
+// calcCameraPositionTargetGlobal places the camera at the shoulder offset
+// instead of the avatar's eye position.
+// Avatar rendering is handled explicitly in needsRenderAvatar() and needsRenderHead().
+//-----------------------------------------------------------------------------
+void LLAgentCamera::changeCameraToOTS()
+{
+ if (mCameraMode != CAMERA_MODE_OTS)
+ {
+ // Inherit everything from mouselook: cursor lock, mouse capture,
+ // AGENT_CONTROL_MOUSELOOK flag, keyboard focus clear, etc.
+ changeCameraToMouselook(false);
+
+ // Override mCameraMode to OTS so position/focus calculations
+ // use the shoulder offset instead of the eye position.
+ mCameraMode = CAMERA_MODE_OTS;
+
+ // changeCameraToMouselook hid attachments via updateAttachmentVisibility
+ // with CAMERA_MODE_MOUSELOOK. Restore full visibility for OTS mode.
+ if (isAgentAvatarValid())
+ {
+ gAgentAvatarp->updateAttachmentVisibility(CAMERA_MODE_THIRD_PERSON);
+ }
+
+ // Start the camera animation LAST, after mCameraMode is OTS and after
+ // changeCameraToMouselook(false) has cleared mCameraAnimating via its
+ // animate=false branch. The rendered camera is still at the old
+ // (third-person) position this frame, so startCameraAnimation snapshots
+ // that as the start point and updateCamera lerps to the OTS shoulder
+ // target over ZoomTime seconds — matching the mouselook transition feel.
+ startCameraAnimation();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraFromOTS()
+//-----------------------------------------------------------------------------
+void LLAgentCamera::changeCameraFromOTS()
+{
+ if (mCameraMode == CAMERA_MODE_OTS)
+ {
+ // changeCameraToDefault handles clearing AGENT_CONTROL_MOUSELOOK,
+ // showing the cursor, and restoring the normal camera mode.
+ changeCameraToDefault();
+ }
+}
+
+//-----------------------------------------------------------------------------
// changeCameraToFollow()
//-----------------------------------------------------------------------------
void LLAgentCamera::changeCameraToFollow(bool animate)
@@ -2336,7 +2414,7 @@ void LLAgentCamera::changeCameraToThirdPerson(bool animate)
}
mCameraLag.clearVec();
- if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK || mCameraMode == CAMERA_MODE_OTS)
{
mCurrentCameraDistance = MIN_CAMERA_DISTANCE;
mTargetCameraDistance = MIN_CAMERA_DISTANCE;
diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h
index d277fd6158..ac1a36a6c2 100644
--- a/indra/newview/llagentcamera.h
+++ b/indra/newview/llagentcamera.h
@@ -43,7 +43,8 @@ enum ECameraMode
CAMERA_MODE_THIRD_PERSON,
CAMERA_MODE_MOUSELOOK,
CAMERA_MODE_CUSTOMIZE_AVATAR,
- CAMERA_MODE_FOLLOW
+ CAMERA_MODE_FOLLOW,
+ CAMERA_MODE_OTS // Over-the-shoulder: mouselook input + third-person camera
};
/** Camera Presets for CAMERA_MODE_THIRD_PERSON */
@@ -93,10 +94,14 @@ public:
void changeCameraToThirdPerson(bool animate = true);
void changeCameraToCustomizeAvatar(); // Trigger transition animation
void changeCameraToFollow(bool animate = true); // Ventrella
+ void changeCameraToOTS(); // Over-the-shoulder aim mode
+ void changeCameraFromOTS(); // Exit OTS back to third person
bool cameraThirdPerson() const { return (mCameraMode == CAMERA_MODE_THIRD_PERSON && mLastCameraMode == CAMERA_MODE_THIRD_PERSON); }
- bool cameraMouselook() const { return (mCameraMode == CAMERA_MODE_MOUSELOOK && mLastCameraMode == CAMERA_MODE_MOUSELOOK); }
+ // Also true for OTS — reuses mouselook input and UI behaviour; camera position handled separately.
+ bool cameraMouselook() const { return (mCameraMode == CAMERA_MODE_MOUSELOOK && mLastCameraMode == CAMERA_MODE_MOUSELOOK) || mCameraMode == CAMERA_MODE_OTS; }
bool cameraCustomizeAvatar() const { return (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR /*&& !mCameraAnimating*/); }
bool cameraFollow() const { return (mCameraMode == CAMERA_MODE_FOLLOW && mLastCameraMode == CAMERA_MODE_FOLLOW); }
+ bool cameraOTS() const { return mCameraMode == CAMERA_MODE_OTS; }
ECameraMode getCameraMode() const { return mCameraMode; }
ECameraMode getLastCameraMode() const { return mLastCameraMode; }
void updateCamera(); // Call once per frame to update camera location/orientation
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index c74c0db08c..719447b920 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -136,10 +136,12 @@
#include "stringize.h"
#include "llcoros.h"
#include "llexception.h"
-#if !_M_ARM64 // !LL_LINUX
+#if defined(LL_CEF)
#include "cef/dullahan_version.h"
+#endif
+#if defined(LL_VLC)
#include "vlc/libvlc_version.h"
-#endif // LL_LINUX
+#endif
#if LL_DARWIN
#if LL_SDL
@@ -3517,7 +3519,7 @@ LLSD LLAppViewer::getViewerInfo() const
info["VOICE_VERSION"] = LLTrans::getString("NotConnected");
}
-#if !_M_ARM64 // !LL_LINUX
+#if defined(LL_CEF)
std::ostringstream cef_ver_codec;
cef_ver_codec << "Dullahan: ";
cef_ver_codec << DULLAHAN_VERSION_MAJOR;
@@ -3547,7 +3549,7 @@ LLSD LLAppViewer::getViewerInfo() const
info["LIBCEF_VERSION"] = "Undefined";
#endif
-#if !_M_ARM64 // !LL_LINUX
+#if defined(LL_VLC)
std::ostringstream vlc_ver_codec;
vlc_ver_codec << LIBVLC_VERSION_MAJOR;
vlc_ver_codec << ".";
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index 5bbaed750c..728a8ef81e 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -131,16 +131,12 @@ int main( int argc, char **argv )
// install unexpected exception handler
gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);
-#ifdef __aarch64__
- setenv("LD_PRELOAD", APP_PLUGIN_DIR"/libcef.so", 1);
-#else
# if LL_LINUX
setenv("LD_PRELOAD", "libpthread.so.0 libGL.so.1", 1);
# else
setenv("LD_PRELOAD", "libpthread.so libGL.so.1", 1);
# endif
setenv("__GL_THREADED_OPTIMIZATIONS", "1", 0);
-#endif
bool ok = viewer_app_ptr->init();
if(!ok)
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index fb1426a235..c6b17fcb4e 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -34,6 +34,7 @@
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "roles_constants.h" // for GP_MEMBER_INVITE
+#include "llregionflags.h" // for ESTATE_ACCESS_BANNED_AGENT_ADD
#include "llagent.h"
#include "llappviewer.h" // for gLastVersionChannel
@@ -625,6 +626,34 @@ void LLAvatarActions::ejectAvatar(const LLUUID& id, bool ban_enabled)
}
// static
+void LLAvatarActions::estateKickAvatar(const LLUUID& id)
+{
+ LLAvatarName av_name;
+ LLSD payload;
+ payload["avatar_id"] = id;
+ LLSD args;
+ if (LLAvatarNameCache::get(id, &av_name))
+ {
+ args["EVIL_USER"] = av_name.getCompleteName();
+ }
+ LLNotificationsUtil::add("EstateKickUser", args, payload, handleEstateKickAvatar);
+}
+
+// static
+void LLAvatarActions::estateBanAvatar(const LLUUID& id)
+{
+ LLAvatarName av_name;
+ LLSD payload;
+ payload["avatar_id"] = id;
+ LLSD args;
+ if (LLAvatarNameCache::get(id, &av_name))
+ {
+ args["AVATAR_NAME"] = av_name.getCompleteName();
+ }
+ LLNotificationsUtil::add("EstateBanUser", args, payload, handleEstateBanAvatar);
+}
+
+// static
void LLAvatarActions::freeze(const LLUUID& id)
{
LLSD payload;
@@ -1425,6 +1454,62 @@ bool LLAvatarActions::handleEjectAvatar(const LLSD& notification, const LLSD& re
return false;
}
+bool LLAvatarActions::handleEstateKickAvatar(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0)
+ {
+ LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null);
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "kickestate");
+ msg->addUUID("Invoice", LLUUID::generateNewID());
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", avatar_id.asString());
+ gAgent.sendReliableMessage();
+ }
+ return false;
+}
+
+bool LLAvatarActions::handleEstateBanAvatar(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0)
+ {
+ LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null);
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estateaccessdelta");
+ msg->addUUID("Invoice", LLUUID::generateNewID());
+
+ std::string buf;
+ gAgent.getID().toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ buf = llformat("%u", (U32)ESTATE_ACCESS_BANNED_AGENT_ADD);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ avatar_id.toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ gAgent.sendReliableMessage();
+ }
+ return false;
+}
+
bool LLAvatarActions::handleFreeze(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h
index 1f5a42ed22..bc4eb6fa60 100644
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -192,6 +192,10 @@ public:
static void freezeAvatar(const LLUUID& id);
static void ejectAvatar(const LLUUID& id, bool ban_enabled = false);
+
+ static void estateKickAvatar(const LLUUID& id);
+
+ static void estateBanAvatar(const LLUUID& id);
/**
* Kick avatar off grid
*/
@@ -263,6 +267,8 @@ private:
static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id);
static bool handleFreezeAvatar(const LLSD& notification, const LLSD& response);
static bool handleEjectAvatar(const LLSD& notification, const LLSD& response);
+ static bool handleEstateKickAvatar(const LLSD& notification, const LLSD& response);
+ static bool handleEstateBanAvatar(const LLSD& notification, const LLSD& response);
static bool handleKick(const LLSD& notification, const LLSD& response);
static bool handleFreeze(const LLSD& notification, const LLSD& response);
static bool handleUnfreeze(const LLSD& notification, const LLSD& response);
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 63ec43458b..a2a0731256 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -48,6 +48,8 @@
#include "llagentcamera.h"
#include "llappviewer.h" // for gDisconnected
#include "llavataractions.h"
+#include "llgroupcolormap.h" // group-based dot tinting
+#include "llvoavatar.h" // group-based dot tinting
#include "llcallingcard.h" // LLAvatarTracker
#include "llfloaterland.h"
#include "llfloaterworldmap.h"
@@ -432,6 +434,24 @@ void LLNetMap::draw()
LLColor4 color = LLAvatarActions::isFriend(uuid) ? map_avatar_friend_color : map_avatar_color;
+ // Group-based dot tinting: override with group color if one is set.
+ // Look up the avatar's active group UUID from the LLVOAvatar object.
+ if (LLViewerObject* obj = gObjectList.findObject(uuid))
+ {
+ if (LLVOAvatar* av = dynamic_cast<LLVOAvatar*>(obj))
+ {
+ LLUUID active_group = av->getActiveGroupID();
+ if (active_group.notNull())
+ {
+ LLColor4 group_color = LLGroupColorMap::getInstance()->getGroupColor(active_group);
+ if (group_color.mV[VW] >= 0.01f)
+ {
+ color = group_color;
+ }
+ }
+ }
+ }
+
unknown_relative_z = sorted_positions[i].mdV[VZ] >= COARSEUPDATE_MAX_Z &&
camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z;
@@ -505,10 +525,23 @@ void LLNetMap::draw()
LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
if (you)
{
+ // Group-based dot tinting for self: use gAgent.getGroupID() directly
+ LLColor4 self_color = UI_VERTEX_COLOR;
+ LLUUID self_group = gAgent.getGroupID();
+ if (self_group.notNull())
+ {
+ LLColor4 group_color = LLGroupColorMap::getInstance()->getGroupColor(self_group);
+ if (group_color.mV[VW] >= 0.01f)
+ {
+ self_color = group_color;
+ }
+ }
+
you->draw(ll_round(pos_map.mV[VX] - mDotRadius),
ll_round(pos_map.mV[VY] - mDotRadius),
dot_width,
- dot_width);
+ dot_width,
+ self_color);
F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
LLVector2((F32)local_mouse_x, (F32)local_mouse_y));
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 794ae4ad44..537ea28a4d 100644
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -84,11 +84,14 @@ LLContextMenu* PeopleContextMenu::createMenu()
registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id));
registrar.add("Avatar.Freeze", boost::bind(&LLAvatarActions::freezeAvatar, id));
registrar.add("Avatar.Eject", boost::bind(&PeopleContextMenu::eject, this));
+ registrar.add("Avatar.EstateKick", boost::bind(&LLAvatarActions::estateKickAvatar, id));
+ registrar.add("Avatar.EstateBan", boost::bind(&LLAvatarActions::estateBanAvatar, id));
enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2));
enable_registrar.add("Avatar.CheckItem", boost::bind(&PeopleContextMenu::checkContextMenuItem, this, _2));
enable_registrar.add("Avatar.EnableFreezeEject", boost::bind(&PeopleContextMenu::enableFreezeEject, this, _2));
+ enable_registrar.add("Avatar.EnableEstateEjectBan", boost::bind(&PeopleContextMenu::enableEstateEjectBan, this, _2));
// create the context menu from the XUI
menu = createFromFile("menu_people_nearby.xml");
@@ -318,6 +321,17 @@ bool PeopleContextMenu::enableFreezeEject(const LLSD& userdata)
return new_value;
}
+bool PeopleContextMenu::enableEstateEjectBan(const LLSD& userdata)
+{
+ if (gAgent.getID() == mUUIDs.front() || mUUIDs.size() != 1)
+ return false;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return false;
+ if (gAgent.isGodlike()) return true;
+ return region->getOwner() == gAgent.getID() || region->isEstateManager();
+}
+
void PeopleContextMenu::requestTeleport()
{
// boost::bind cannot recognize overloaded method LLAvatarActions::teleportRequest(),
@@ -419,6 +433,8 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
items.push_back(std::string("block_unblock"));
items.push_back(std::string("freeze"));
items.push_back(std::string("eject"));
+ items.push_back(std::string("estate_eject"));
+ items.push_back(std::string("estate_ban"));
}
hide_context_entries(menu, items, disabled_items);
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index ad38cebc31..ad6a26c645 100644
--- a/indra/newview/llpanelpeoplemenus.h
+++ b/indra/newview/llpanelpeoplemenus.h
@@ -47,6 +47,7 @@ private:
bool enableContextMenuItem(const LLSD& userdata);
bool checkContextMenuItem(const LLSD& userdata);
bool enableFreezeEject(const LLSD& userdata);
+ bool enableEstateEjectBan(const LLSD& userdata);
void offerTeleport();
void eject();
void startConference();
diff --git a/indra/newview/llquickprefs.cpp b/indra/newview/llquickprefs.cpp
index 800aa7abac..9e83857a15 100644
--- a/indra/newview/llquickprefs.cpp
+++ b/indra/newview/llquickprefs.cpp
@@ -26,6 +26,8 @@
#include "llquickprefs.h"
#include "llagent.h"
+#include "llagentcamera.h"
+#include "llcheckboxctrl.h"
#include "llsliderctrl.h"
#include "lltextbox.h"
#include "llviewercontrol.h"
@@ -97,6 +99,14 @@ bool LLFloaterQuickPrefs::postBuild()
}
onRegionChanged(); // evaluate current region immediately
+ // OTS aim mode checkbox
+ mOTSEnabledCheck = getChild<LLCheckBoxCtrl>("ots_enabled");
+ if (mOTSEnabledCheck)
+ {
+ mOTSEnabledCheck->setCommitCallback(
+ boost::bind(&LLFloaterQuickPrefs::onOTSEnabledChanged, this));
+ }
+
return true;
}
@@ -203,3 +213,16 @@ void LLFloaterQuickPrefs::syncAvatarZOffsetFromPreferenceSetting()
F32 value = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ");
mAvatarZOffsetSlider->setValue(value, false); // false = no commit signal
}
+
+void LLFloaterQuickPrefs::onOTSEnabledChanged()
+{
+ if (mOTSEnabledCheck)
+ {
+ gSavedSettings.setBOOL("OTSEnabled", mOTSEnabledCheck->get());
+ // If currently in OTS mode and checkbox was unchecked, exit OTS
+ if (!mOTSEnabledCheck->get() && gAgentCamera.cameraOTS())
+ {
+ gAgentCamera.changeCameraFromOTS();
+ }
+ }
+}
diff --git a/indra/newview/llquickprefs.h b/indra/newview/llquickprefs.h
index 0ef56a4299..b90333831c 100644
--- a/indra/newview/llquickprefs.h
+++ b/indra/newview/llquickprefs.h
@@ -27,6 +27,7 @@
#include "llfloater.h"
class LLSliderCtrl;
+class LLCheckBoxCtrl;
class LLTextBox;
/**
@@ -76,6 +77,10 @@ private:
void syncAvatarZOffsetFromPreferenceSetting();
boost::signals2::connection mRegionChangedSlot;
+
+ // OTS over-the-shoulder aim
+ LLCheckBoxCtrl* mOTSEnabledCheck { nullptr };
+ void onOTSEnabledChanged();
};
#endif // LL_LLQUICKPREFS_H
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 7d97151e9d..59dc4319e1 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -280,6 +280,12 @@ static void update_tp_display(bool minimized)
F32 teleport_save_time = TELEPORT_EXPIRY + TELEPORT_EXPIRY_PER_ATTACHMENT * attach_count;
F32 teleport_elapsed = gTeleportDisplayTimer.getElapsedTimeF32();
F32 teleport_percent = teleport_elapsed * (100.f / teleport_save_time);
+
+ // Reuse the minimized-window path to suppress the teleport progress screen.
+ if (gSavedSettings.getBOOL("DisableTeleportScreens"))
+ {
+ minimized = true;
+ }
if (gAgent.getTeleportState() != LLAgent::TELEPORT_START && teleport_percent > 100.f)
{
// Give up. Don't keep the UI locked forever.
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 6448dd6ba5..66ca722b88 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1826,7 +1826,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
if (plugin_basename == "media_plugin_cef")
{
launcher_name = "/compat/linux/usr/libexec/megapahit/SLPlugin";
- plugin_name = "/compat/linux/usr/lib/x86_64-linux-gnu/libmedia_plugin_cef.so";
+ plugin_name = "/compat/linux/usr/lib/x86_64-linux-gnu/megapahit/libmedia_plugin_cef.so";
}
#endif
@@ -1891,8 +1891,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
std::string plugin_dir = gDirUtilp->getLLPluginDir();
if (plugin_basename == "media_plugin_cef")
{
- plugin_dir = "/compat/linux/usr/lib/x86_64-linux-gnu";
- plugin_name = "/usr/lib/x86_64-linux-gnu/libmedia_plugin_cef.so";
+ plugin_dir = "/compat/linux/usr/lib/x86_64-linux-gnu/megapahit";
+ plugin_name = "/usr/lib/x86_64-linux-gnu/megapahit/libmedia_plugin_cef.so";
}
#else
const std::string plugin_dir = gDirUtilp->getLLPluginDir();
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 4632800e2d..1b1d703f38 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -4065,6 +4065,44 @@ void handle_avatar_eject(const LLSD& avatar_id)
}
}
+void handle_avatar_estate_kick(const LLSD& avatar_id)
+{
+ LLUUID id = avatar_id.asUUID();
+ if (id.isNull())
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(
+ LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
+ if (avatar) id = avatar->getID();
+ }
+ if (id.notNull())
+ {
+ LLAvatarActions::estateKickAvatar(id);
+ }
+}
+
+void handle_avatar_estate_ban(const LLSD& avatar_id)
+{
+ LLUUID id = avatar_id.asUUID();
+ if (id.isNull())
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(
+ LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
+ if (avatar) id = avatar->getID();
+ }
+ if (id.notNull())
+ {
+ LLAvatarActions::estateBanAvatar(id);
+ }
+}
+
+bool enable_estate_eject_ban(const LLSD& avatar_id)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return false;
+ if (gAgent.isGodlike()) return true;
+ return region->getOwner() == gAgent.getID() || region->isEstateManager();
+}
+
bool my_profile_visible()
{
LLFloater* floaterp = LLAvatarActions::getProfileFloater(gAgentID);
@@ -4778,25 +4816,37 @@ class LLViewMouselook : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
+ // When OTS is enabled M toggles OTS mode (shoulder cam).
+ // When disabled M toggles normal first-person mouselook.
+ if (gSavedSettings.getBOOL("OTSEnabled"))
+ {
+ if (!gAgentCamera.cameraOTS())
+ {
+ gAgentCamera.changeCameraToOTS();
+ }
+ else
+ {
+ gAgentCamera.changeCameraFromOTS();
+ }
+ return true;
+ }
+
if (!gAgentCamera.cameraMouselook())
{
gAgentCamera.changeCameraToMouselook();
}
else
{
- // NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm).
- // If we were zoomed when the user toggles out of mouselook, restore the
- // normal (pre-zoom) FOV before switching back to the default camera.
+ // NaCl: restore FOV on mouselook exit
LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues");
F32 cameraAngle = gSavedSettings.getF32("CameraAngle");
if (mlFovValues.mV[VZ] > 0.0f)
{
- mlFovValues.mV[VY] = cameraAngle; // preserve last zoomed FOV
+ mlFovValues.mV[VY] = cameraAngle;
mlFovValues.mV[VZ] = 0.0f;
gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues);
- gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VX]); // restore normal FOV
+ gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VX]);
}
- // NaCl End
gAgentCamera.changeCameraToDefault();
}
return true;
@@ -10346,6 +10396,9 @@ void initialize_menus()
view_listener_t::addMenu(new LLAvatarVisibleDebug(), "Avatar.VisibleDebug");
view_listener_t::addMenu(new LLAvatarInviteToGroup(), "Avatar.InviteToGroup");
commit.add("Avatar.Eject", boost::bind(&handle_avatar_eject, LLSD()));
+ commit.add("Avatar.EstateKick", boost::bind(&handle_avatar_estate_kick, _2));
+ commit.add("Avatar.EstateBan", boost::bind(&handle_avatar_estate_ban, _2));
+ enable.add("Avatar.EnableEstateEjectBan", boost::bind(&enable_estate_eject_ban, _2));
commit.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector));
view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM");
view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index b8515fd92b..1c6544f3f9 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -6120,11 +6120,18 @@ void process_teleport_local(LLMessageSystem *msg,void**)
}
gAgent.setPositionAgent(pos);
- gAgentCamera.slamLookAt(look_at);
+
+ bool keep_camera_local_tp = gSavedSettings.getBOOL("KeepCameraOnLocalTeleport");
+
+ if (!keep_camera_local_tp)
+ {
+ gAgentCamera.slamLookAt(look_at);
+ }
if ( !(gAgent.getTeleportKeepsLookAt() && LLViewerJoystick::getInstance()->getOverrideCamera()) )
{
- gAgentCamera.resetView(true, true);
+ // resetView still runs (cleanup); the false args just leave the camera alone.
+ gAgentCamera.resetView(!keep_camera_local_tp, !keep_camera_local_tp);
}
// send camera update to new region
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 70e31363ff..0c3b82c886 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -298,6 +298,7 @@ public:
// LLAvatarPropertiesObserver: receives APT_GROUPS reply for group-tint lookup
/*virtual*/ void processProperties(void* data, EAvatarProcessorType type) override;
void sendAvatarGroupsRequest();
+ const LLUUID& getActiveGroupID() const { return mActiveGroupID; }
static void invalidateNameTag(const LLUUID& agent_id);
// force all name tags to rebuild, useful when display names turned on/off
static void invalidateNameTags();
diff --git a/indra/newview/skins/default/xui/en/floater_quick_prefs.xml b/indra/newview/skins/default/xui/en/floater_quick_prefs.xml
index 8b20fda6f5..f5bdcf2156 100644
--- a/indra/newview/skins/default/xui/en/floater_quick_prefs.xml
+++ b/indra/newview/skins/default/xui/en/floater_quick_prefs.xml
@@ -9,7 +9,7 @@
can_minimize="true"
can_close="true"
can_resize="false"
- height="96"
+ height="240"
width="320"
layout="topleft"
name="quick_prefs"
@@ -82,4 +82,128 @@
name="max_bandwidth"
tool_tip="Network bandwidth in Kbps (50 – 3000)" />
+ <view_border
+ follows="left|right"
+ layout="topleft"
+ left="2"
+ right="-2"
+ top_pad="10"
+ height="2"
+ name="ots_divider" />
+
+ <text
+ type="string"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="5"
+ top_pad="6"
+ width="290"
+ text_color="EmphasisColor"
+ name="OTSLabel">
+ Over-the-Shoulder Cam:
+ </text>
+
+ <check_box
+ control_name="OTSEnabled"
+ follows="left|top"
+ height="16"
+ label="Use Over-The-Shoulder Cam"
+ layout="topleft"
+ left="10"
+ top_pad="4"
+ width="290"
+ name="ots_enabled"
+ tool_tip="When checked, M enters shoulder cam instead of first-person mouselook" />
+
+ <text
+ type="string"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ top_pad="6"
+ width="100"
+ name="OTSDistLabel">
+ Distance:
+ </text>
+
+ <slider
+ control_name="OTSCameraDistance"
+ decimal_digits="1"
+ can_edit_text="true"
+ follows="left|right|top"
+ height="16"
+ increment="0.1"
+ initial_value="3.0"
+ max_val="10.0"
+ min_val="1.0"
+ label_width="0"
+ layout="topleft"
+ left="110"
+ right="-10"
+ top_delta="-2"
+ name="ots_distance"
+ tool_tip="Camera distance behind avatar (1 – 10 m)" />
+
+ <text
+ type="string"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ top_pad="4"
+ width="100"
+ name="OTSSideLabel">
+ Side offset:
+ </text>
+
+ <slider
+ control_name="OTSCameraSide"
+ decimal_digits="2"
+ can_edit_text="true"
+ follows="left|right|top"
+ height="16"
+ increment="0.05"
+ initial_value="-0.5"
+ max_val="1.0"
+ min_val="-1.0"
+ label_width="0"
+ layout="topleft"
+ left="110"
+ right="-10"
+ top_delta="-2"
+ name="ots_side"
+ tool_tip="Side offset: negative = right shoulder, positive = left shoulder" />
+
+ <text
+ type="string"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ top_pad="4"
+ width="100"
+ name="OTSHeightLabel">
+ Height:
+ </text>
+
+ <slider
+ control_name="OTSCameraHeight"
+ decimal_digits="2"
+ can_edit_text="true"
+ follows="left|right|top"
+ height="16"
+ increment="0.05"
+ initial_value="0.5"
+ max_val="2.0"
+ min_val="0.0"
+ label_width="0"
+ layout="topleft"
+ left="110"
+ right="-10"
+ top_delta="-2"
+ name="ots_height"
+ tool_tip="Camera height above avatar root (0 – 2 m)" />
+
</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_avatar_other.xml b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
index fc4ffde947..f86e9160ed 100644
--- a/indra/newview/skins/default/xui/en/menu_avatar_other.xml
+++ b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
@@ -90,6 +90,22 @@
function="Avatar.EnableFreezeEject"/>
</menu_item_call>
<menu_item_call
+ label="Estate Eject"
+ name="EstateEject...">
+ <menu_item_call.on_click
+ function="Avatar.EstateKick" />
+ <menu_item_call.on_visible
+ function="Avatar.EnableEstateEjectBan"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Estate Ban"
+ name="EstateBan...">
+ <menu_item_call.on_click
+ function="Avatar.EstateBan" />
+ <menu_item_call.on_visible
+ function="Avatar.EnableEstateEjectBan"/>
+ </menu_item_call>
+ <menu_item_call
label="Debug Textures"
name="Debug...">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
index b7a296bf31..8d73eee60a 100644
--- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
@@ -169,4 +169,20 @@
<menu_item_call.on_visible
function="Avatar.EnableFreezeEject"/>
</menu_item_call>
+ <menu_item_call
+ label="Estate Eject"
+ name="estate_eject">
+ <menu_item_call.on_click
+ function="Avatar.EstateKick" />
+ <menu_item_call.on_visible
+ function="Avatar.EnableEstateEjectBan"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Estate Ban"
+ name="estate_ban">
+ <menu_item_call.on_click
+ function="Avatar.EstateBan" />
+ <menu_item_call.on_visible
+ function="Avatar.EnableEstateEjectBan"/>
+ </menu_item_call>
</context_menu>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index facf9b7e46..65f26d50e7 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -5404,6 +5404,18 @@ Kick [EVIL_USER] from this estate?
<notification
icon="alertmodal.tga"
+ name="EstateBanUser"
+ type="alertmodal">
+Ban [AVATAR_NAME] from this estate?
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelbuttons"
+ notext="Cancel"
+ yestext="Ban"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="EstateChangeCovenant"
type="alertmodal">
Are you sure you want to change the Estate Covenant?
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_move.xml b/indra/newview/skins/default/xui/en/panel_preferences_move.xml
index eee55bd7bc..ad24451e24 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_move.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_move.xml
@@ -9,317 +9,450 @@
name="move_panel"
top="1"
width="517">
- <icon
- follows="left|top"
- height="18"
- image_name="Cam_FreeCam_Off"
- layout="topleft"
- name="camera_icon"
- mouse_opaque="false"
- visible="true"
- width="18"
- left="30"
- top="10"/>
- <slider
- can_edit_text="true"
- control_name="CameraAngle"
- decimal_digits="2"
- follows="left|top"
- height="16"
- increment="0.025"
- initial_value="1.57"
- layout="topleft"
- label_width="100"
- label="View angle"
- left_pad="30"
- max_val="2.97"
- min_val="0.17"
- name="camera_fov"
- show_text="false"
- width="240" />
- <slider
- can_edit_text="true"
- control_name="CameraOffsetScale"
- decimal_digits="2"
- follows="left|top"
- height="16"
- increment="0.025"
- initial_value="1"
- layout="topleft"
- label="Distance"
+
+ <tab_container
+ name="move_view_tab_container"
+ enabled="true"
+ top_pad="0"
+ follows="left|top|right|bottom"
+ width="517"
+ height="408"
left_delta="0"
- label_width="100"
- max_val="3"
- min_val="0.5"
- name="camera_offset_scale"
- show_text="false"
- width="240"
- top_pad="5"/>
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- left="80"
- name="heading2"
- width="270"
- top_pad="5">
- Automatic position for:
- </text>
- <check_box
- control_name="EditCameraMovement"
- height="20"
- follows="left|top"
- label="Build/Edit"
+ tab_position="top"
+ tab_stop="false">
+
+ <!-- ── Move & View tab (existing content) ─────────────────────────── -->
+ <panel
+ label="Move &amp; View"
+ name="move_view_panel"
layout="topleft"
- left_delta="30"
- name="edit_camera_movement"
- tool_tip="Use automatic camera positioning when entering and exiting edit mode"
- width="280"
- top_pad="5" />
- <check_box
- control_name="AppearanceCameraMovement"
- follows="left|top"
- height="16"
- label="Appearance"
- layout="topleft"
- name="appearance_camera_movement"
- tool_tip="Use automatic camera positioning while in edit mode"
- width="242" />
- <icon
- follows="left|top"
- height="18"
- image_name="Move_Walk_Off"
- layout="topleft"
- name="avatar_icon"
- mouse_opaque="false"
- visible="true"
- width="18"
- top_pad="10"
- left="30" />
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- layout="topleft"
- left="78"
- name="keyboard_lbl"
- width="270"
- top_delta="2">
- Keyboard:
- </text>
- <check_box
- control_name="ArrowKeysAlwaysMove"
- follows="left|top"
- height="20"
- label="Arrow keys always move me while in chat"
- layout="topleft"
- left_delta="5"
- name="arrow_keys_move_avatar_check"
- width="237"
- top_pad="5"/>
- <check_box
- control_name="AllowTapTapHoldRun"
- follows="left|top"
- height="20"
- label="Tap-tap-hold to run"
- layout="topleft"
- left_delta="0"
- name="tap_tap_hold_to_run"
- width="237"
- top_pad="0"/>
- <check_box
- control_name="AutomaticFly"
- follows="left|top"
- height="20"
- label="Hold jump or crouch key to start or stop flying"
- layout="topleft"
- left_delta="0"
- name="automatic_fly"
- width="237"
- top_pad="0"/>
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- layout="topleft"
- left="78"
- name="mouse_lbl"
- width="270"
- top_pad="15">
- Mouse:
- </text>
- <check_box
- control_name="FirstPersonAvatarVisible"
- follows="left|top"
- height="20"
- label="Show me in Mouselook"
- layout="topleft"
- left_delta="5"
- name="first_person_avatar_visible"
- top_pad="5"
- width="256" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="15"
- layout="topleft"
- left_delta="3"
- name=" Mouse Sensitivity"
- top_pad="10"
- width="160"
- wrap="true">
- Mouselook mouse sensitivity:
- </text>
- <slider
- control_name="MouseSensitivity"
- follows="left|top"
- height="15"
- initial_value="2"
- layout="topleft"
- show_text="false"
- left_pad="0"
- max_val="15"
- name="mouse_sensitivity"
- top_delta="-1"
- width="115" />
- <check_box
- control_name="InvertMouse"
- height="16"
- label="Invert"
- layout="topleft"
- left_pad="2"
- name="invert_mouse"
- top_delta="0"
- width="128" />
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- layout="topleft"
- left="86"
- name="mouse_warp_lbl"
- width="150"
- top_pad="20">
- Mouse Warp:
- </text>
- <combo_box
- control_name="MouseWarpMode"
- height="23"
- layout="topleft"
- left_pad="10"
- top_delta="-6"
- name="mouse_warp_combo"
- tool_tip="Controls warping of the mouse to the center of the screen during alt-zoom and mouse look."
- width="200">
- <combo_box.item
- label="Automatic"
- name="0"
- value="0"/>
- <combo_box.item
- label="On"
- name="1"
- value="1"/>
- <combo_box.item
- label="Off"
- name="2"
- value="2"/>
- </combo_box>
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- layout="topleft"
- left="86"
- name="single_click_action_lbl"
- width="150"
- top_pad="12">
- Single click on land:
- </text>
- <combo_box
- height="23"
- layout="topleft"
- left_pad="10"
- top_delta="-6"
- name="single_click_action_combo"
- width="200">
- <combo_box.item
- label="No action"
- name="0"
- value="0"/>
- <combo_box.item
- label="Move to clicked point"
- name="1"
- value="1"/>
- <combo_box.commit_callback
- function="Pref.ClickActionChange"/>
- </combo_box>
- <text
- follows="left|top"
- type="string"
- length="1"
- height="10"
- layout="topleft"
- left="86"
- name="double_click_action_lbl"
- width="150"
- top_pad="12">
- Double click on land:
- </text>
- <combo_box
- height="23"
- layout="topleft"
- left_pad="10"
- top_delta="-6"
- name="double_click_action_combo"
- width="200">
- <combo_box.item
- label="No action"
- name="0"
- value="0"/>
- <combo_box.item
- label="Move to clicked point"
- name="1"
- value="1"/>
- <combo_box.item
- label="Teleport to clicked point"
- name="2"
- value="2"/>
- <combo_box.commit_callback
- function="Pref.ClickActionChange"/>
- </combo_box>
- <check_box
- control_name="EnableCollisionSounds"
- height="20"
- label="Play sound on collisions"
- layout="topleft"
- left="83"
- name="sound_on_collisions"
- top_pad="0"
- width="200" />
- <check_box
- control_name="DoubleClickZoomIn"
- height="20"
- label="Double click on nearby list to zoom in on avatar"
- layout="topleft"
- left="83"
- name="double_click_zoom_in"
- top_pad="0"
- width="200" />
- <button
- height="23"
- label="Other Devices"
- left="30"
- name="joystick_setup_button"
- top="30"
- width="155">
- <button.commit_callback
- function="Floater.Show"
- parameter="pref_joystick" />
- </button>
+ follows="top|left">
+
+ <icon
+ follows="left|top"
+ height="18"
+ image_name="Cam_FreeCam_Off"
+ layout="topleft"
+ name="camera_icon"
+ mouse_opaque="false"
+ visible="true"
+ width="18"
+ left="30"
+ top="10"/>
+ <slider
+ can_edit_text="true"
+ control_name="CameraAngle"
+ decimal_digits="2"
+ follows="left|top"
+ height="16"
+ increment="0.025"
+ initial_value="1.57"
+ layout="topleft"
+ label_width="100"
+ label="View angle"
+ left_pad="30"
+ max_val="2.97"
+ min_val="0.17"
+ name="camera_fov"
+ show_text="false"
+ width="240" />
+ <slider
+ can_edit_text="true"
+ control_name="CameraOffsetScale"
+ decimal_digits="2"
+ follows="left|top"
+ height="16"
+ increment="0.025"
+ initial_value="1"
+ layout="topleft"
+ label="Distance"
+ left_delta="0"
+ label_width="100"
+ max_val="3"
+ min_val="0.5"
+ name="camera_offset_scale"
+ show_text="false"
+ width="240"
+ top_pad="5"/>
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ left="80"
+ name="heading2"
+ width="270"
+ top_pad="5">
+ Automatic position for:
+ </text>
+ <check_box
+ control_name="EditCameraMovement"
+ height="20"
+ follows="left|top"
+ label="Build/Edit"
+ layout="topleft"
+ left_delta="30"
+ name="edit_camera_movement"
+ tool_tip="Use automatic camera positioning when entering and exiting edit mode"
+ width="280"
+ top_pad="5" />
+ <check_box
+ control_name="AppearanceCameraMovement"
+ follows="left|top"
+ height="16"
+ label="Appearance"
+ layout="topleft"
+ name="appearance_camera_movement"
+ tool_tip="Use automatic camera positioning while in edit mode"
+ width="242" />
+ <icon
+ follows="left|top"
+ height="18"
+ image_name="Move_Walk_Off"
+ layout="topleft"
+ name="avatar_icon"
+ mouse_opaque="false"
+ visible="true"
+ width="18"
+ top_pad="10"
+ left="30" />
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ layout="topleft"
+ left="78"
+ name="keyboard_lbl"
+ width="270"
+ top_delta="2">
+ Keyboard:
+ </text>
+ <check_box
+ control_name="ArrowKeysAlwaysMove"
+ follows="left|top"
+ height="20"
+ label="Arrow keys always move me while in chat"
+ layout="topleft"
+ left_delta="5"
+ name="arrow_keys_move_avatar_check"
+ width="237"
+ top_pad="5"/>
+ <check_box
+ control_name="AllowTapTapHoldRun"
+ follows="left|top"
+ height="20"
+ label="Tap-tap-hold to run"
+ layout="topleft"
+ left_delta="0"
+ name="tap_tap_hold_to_run"
+ width="237"
+ top_pad="0"/>
+ <check_box
+ control_name="AutomaticFly"
+ follows="left|top"
+ height="20"
+ label="Hold jump or crouch key to start or stop flying"
+ layout="topleft"
+ left_delta="0"
+ name="automatic_fly"
+ width="237"
+ top_pad="0"/>
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ layout="topleft"
+ left="78"
+ name="mouse_lbl"
+ width="270"
+ top_pad="15">
+ Mouse:
+ </text>
+ <check_box
+ control_name="FirstPersonAvatarVisible"
+ follows="left|top"
+ height="20"
+ label="Show me in Mouselook"
+ layout="topleft"
+ left_delta="5"
+ name="first_person_avatar_visible"
+ top_pad="5"
+ width="256" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="15"
+ layout="topleft"
+ left_delta="3"
+ name=" Mouse Sensitivity"
+ top_pad="10"
+ width="160"
+ wrap="true">
+ Mouselook mouse sensitivity:
+ </text>
+ <slider
+ control_name="MouseSensitivity"
+ follows="left|top"
+ height="15"
+ initial_value="2"
+ layout="topleft"
+ show_text="false"
+ left_pad="0"
+ max_val="15"
+ name="mouse_sensitivity"
+ top_delta="-1"
+ width="115" />
+ <check_box
+ control_name="InvertMouse"
+ height="16"
+ label="Invert"
+ layout="topleft"
+ left_pad="2"
+ name="invert_mouse"
+ top_delta="0"
+ width="128" />
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ layout="topleft"
+ left="86"
+ name="mouse_warp_lbl"
+ width="150"
+ top_pad="20">
+ Mouse Warp:
+ </text>
+ <combo_box
+ control_name="MouseWarpMode"
+ height="23"
+ layout="topleft"
+ left_pad="10"
+ top_delta="-6"
+ name="mouse_warp_combo"
+ tool_tip="Controls warping of the mouse to the center of the screen during alt-zoom and mouse look."
+ width="200">
+ <combo_box.item label="Automatic" name="0" value="0"/>
+ <combo_box.item label="On" name="1" value="1"/>
+ <combo_box.item label="Off" name="2" value="2"/>
+ </combo_box>
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ layout="topleft"
+ left="86"
+ name="single_click_action_lbl"
+ width="150"
+ top_pad="12">
+ Single click on land:
+ </text>
+ <combo_box
+ height="23"
+ layout="topleft"
+ left_pad="10"
+ top_delta="-6"
+ name="single_click_action_combo"
+ width="200">
+ <combo_box.item label="No action" name="0" value="0"/>
+ <combo_box.item label="Move to clicked point" name="1" value="1"/>
+ <combo_box.commit_callback function="Pref.ClickActionChange"/>
+ </combo_box>
+ <text
+ follows="left|top"
+ type="string"
+ length="1"
+ height="10"
+ layout="topleft"
+ left="86"
+ name="double_click_action_lbl"
+ width="150"
+ top_pad="12">
+ Double click on land:
+ </text>
+ <combo_box
+ height="23"
+ layout="topleft"
+ left_pad="10"
+ top_delta="-6"
+ name="double_click_action_combo"
+ width="200">
+ <combo_box.item label="No action" name="0" value="0"/>
+ <combo_box.item label="Move to clicked point" name="1" value="1"/>
+ <combo_box.item label="Teleport to clicked point" name="2" value="2"/>
+ <combo_box.commit_callback function="Pref.ClickActionChange"/>
+ </combo_box>
+ <check_box
+ control_name="EnableCollisionSounds"
+ height="20"
+ label="Play sound on collisions"
+ layout="topleft"
+ left="83"
+ name="sound_on_collisions"
+ top_pad="0"
+ width="200" />
+ <check_box
+ control_name="DoubleClickZoomIn"
+ height="20"
+ label="Double click on nearby list to zoom in on avatar"
+ layout="topleft"
+ left="83"
+ name="double_click_zoom_in"
+ top_pad="0"
+ width="200" />
+ <button
+ height="23"
+ label="Other Devices"
+ left="30"
+ name="joystick_setup_button"
+ top="30"
+ width="155">
+ <button.commit_callback function="Floater.Show" parameter="pref_joystick" />
+ </button>
+
+ </panel>
+
+ <!-- ── Over-The-Shoulder tab ──────────────────────────────────────── -->
+ <panel
+ label="Over-The-Shoulder"
+ name="ots_panel"
+ layout="topleft"
+ follows="top|left">
+
+ <text
+ type="string"
+ follows="left|top"
+ font="SansSerifSmallBold"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="ots_enable_header"
+ top="10"
+ width="490">
+ Over-the-Shoulder Camera
+ </text>
+
+ <check_box
+ control_name="OTSEnabled"
+ follows="left|top"
+ height="20"
+ label="Use Over-The-Shoulder Cam"
+ layout="topleft"
+ left="15"
+ name="ots_enabled_pref"
+ top_pad="5"
+ tool_tip="When checked, pressing M enters over-the-shoulder cam instead of first-person mouselook"
+ width="350" />
+
+ <text
+ type="string"
+ follows="left|top"
+ font="SansSerifSmallBold"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="ots_pos_header"
+ top_pad="12"
+ width="490">
+ Camera Position
+ </text>
+
+ <text type="string" follows="left|top" height="16" layout="topleft"
+ left="15" top_pad="6" width="130" name="dist_label">Distance:</text>
+ <slider
+ control_name="OTSCameraDistance"
+ decimal_digits="1" can_edit_text="true"
+ follows="left|right|top" height="16"
+ increment="0.1" initial_value="3.0" max_val="10.0" min_val="1.0"
+ label_width="0" layout="topleft" left="150" right="-10" top_delta="-2"
+ name="ots_distance"
+ tool_tip="Camera distance behind avatar (1 – 10 m)" />
+
+ <text type="string" follows="left|top" height="16" layout="topleft"
+ left="15" top_pad="6" width="130" name="side_label">Side offset:</text>
+ <slider
+ control_name="OTSCameraSide"
+ decimal_digits="2" can_edit_text="true"
+ follows="left|right|top" height="16"
+ increment="0.05" initial_value="-0.5" max_val="1.0" min_val="-1.0"
+ label_width="0" layout="topleft" left="150" right="-10" top_delta="-2"
+ name="ots_side"
+ tool_tip="Side offset: negative = right shoulder, positive = left shoulder" />
+
+ <text type="string" follows="left|top" height="16" layout="topleft"
+ left="15" top_pad="6" width="130" name="height_label">Height:</text>
+ <slider
+ control_name="OTSCameraHeight"
+ decimal_digits="2" can_edit_text="true"
+ follows="left|right|top" height="16"
+ increment="0.05" initial_value="0.5" max_val="2.0" min_val="0.0"
+ label_width="0" layout="topleft" left="150" right="-10" top_delta="-2"
+ name="ots_height"
+ tool_tip="Camera height above avatar root (0 – 2 m)" />
+
+ <text type="string" follows="left|top" height="16" layout="topleft"
+ left="15" top_pad="6" width="130" name="focus_label">Focus distance:</text>
+ <slider
+ control_name="OTSFocusDistance"
+ decimal_digits="1" can_edit_text="true"
+ follows="left|right|top" height="16"
+ increment="0.5" initial_value="10.0" max_val="30.0" min_val="2.0"
+ label_width="0" layout="topleft" left="150" right="-10" top_delta="-2"
+ name="ots_focus_dist"
+ tool_tip="Focus point distance along camera forward axis (2 – 30 m)" />
+
+ </panel>
+
+ <!-- ── Teleports tab ──────────────────────────────────────────────── -->
+ <panel
+ label="Teleports"
+ name="teleports_panel"
+ layout="topleft"
+ follows="top|left">
+
+ <text
+ type="string"
+ follows="left|top"
+ font="SansSerifSmallBold"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="teleport_header"
+ top="10"
+ width="490">
+ Teleport Options
+ </text>
+
+ <check_box
+ control_name="KeepCameraOnLocalTeleport"
+ follows="left|top"
+ height="20"
+ label="Keep camera position on local teleport"
+ layout="topleft"
+ left="15"
+ name="keep_camera_on_local_teleport"
+ top_pad="8"
+ tool_tip="When teleporting within the same region, do not reset the camera position or mode (stays in mouselook/over-the-shoulder)"
+ width="450" />
+
+ <check_box
+ control_name="DisableTeleportScreens"
+ follows="left|top"
+ height="20"
+ label="Disable teleport progress screen"
+ layout="topleft"
+ left="15"
+ name="disable_teleport_screens"
+ top_pad="6"
+ tool_tip="Do not show the fullscreen progress/black screen during teleports"
+ width="450" />
+
+ </panel>
+
+ </tab_container>
+
</panel>