Merge branch 'master' of https://vcs.firestormviewer.org/viewer-merges/phoenix-firestorm-ll-master
# Conflicts: # .github/workflows/build.yaml # autobuild.xml # indra/llcommon/llerror.cpp # indra/llcommon/tests/workqueue_test.cpp # indra/newview/VIEWER_VERSION.txtmaster
commit
70db255e02
|
|
@ -0,0 +1,18 @@
|
|||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- ignore-for-release
|
||||
authors:
|
||||
- dependabot
|
||||
categories:
|
||||
- title: Breaking Changes 🛠
|
||||
labels:
|
||||
- semver-major
|
||||
- breaking-change
|
||||
- title: New Features 🎉
|
||||
labels:
|
||||
- semver-minor
|
||||
- enhancement
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- '*'
|
||||
|
|
@ -4,7 +4,7 @@ on:
|
|||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, contribute]
|
||||
branches: ["*"]
|
||||
tags: ["*"]
|
||||
|
||||
jobs:
|
||||
|
|
@ -12,47 +12,85 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
runner: [windows-large, macos-12-xl]
|
||||
configuration: [ReleaseOS]
|
||||
addrsize: [64]
|
||||
configuration: [Release, ReleaseOS]
|
||||
python-version: ["3.11"]
|
||||
include:
|
||||
- runner: macos-12-xl
|
||||
developer_dir: "/Applications/Xcode_14.0.1.app/Contents/Developer"
|
||||
exclude:
|
||||
- runner: macos-12-xl
|
||||
configuration: ReleaseOS
|
||||
runs-on: ${{ matrix.runner }}
|
||||
outputs:
|
||||
viewer_channel: ${{ steps.build.outputs.viewer_channel }}
|
||||
viewer_version: ${{ steps.build.outputs.viewer_version }}
|
||||
imagename: ${{ steps.build.outputs.imagename }}
|
||||
env:
|
||||
AUTOBUILD_ADDRSIZE: 64
|
||||
AUTOBUILD_BUILD_ID: ${{ github.run_id }}
|
||||
AUTOBUILD_CONFIGURATION: ${{ matrix.configuration }}
|
||||
AUTOBUILD_ADDRSIZE: ${{ matrix.addrsize }}
|
||||
# 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
|
||||
AUTOBUILD_VSVER: "170" # vs2k22
|
||||
AUTOBUILD_VSVER: "170"
|
||||
DEVELOPER_DIR: ${{ matrix.developer_dir }}
|
||||
LOGFAIL: debug # Show details when tests fail
|
||||
# Ensure that Linden viewer builds engage Bugsplat.
|
||||
BUGSPLAT_DB: ${{ matrix.configuration != 'ReleaseOS' && 'SecondLife_Viewer_2018' || '' }}
|
||||
BUGSPLAT_PASS: ${{ secrets.BUGSPLAT_PASS }}
|
||||
BUGSPLAT_USER: ${{ secrets.BUGSPLAT_USER }}
|
||||
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 }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Checkout build variables
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: secondlife/build-variables
|
||||
ref: viewer
|
||||
path: .build-variables
|
||||
|
||||
- name: Checkout master-message-template
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: secondlife/master-message-template
|
||||
path: .master-message-template
|
||||
|
||||
- name: Install autobuild and python dependencies
|
||||
run: pip3 install autobuild llbase
|
||||
run: pip3 install autobuild llsd
|
||||
|
||||
- name: Cache autobuild packages
|
||||
uses: actions/cache@v3
|
||||
id: cache-installables
|
||||
with:
|
||||
path: .autobuild-installables
|
||||
key: ${{ runner.os }}-${{ matrix.addrsize }}-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }}
|
||||
key: ${{ runner.os }}-64-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.addrsize }}-${{ matrix.configuration }}-
|
||||
${{ runner.os }}-${{ matrix.addrsize }}-
|
||||
${{ runner.os }}-64-${{ matrix.configuration }}-
|
||||
${{ runner.os }}-64-
|
||||
|
||||
- name: Install windows dependencies
|
||||
if: runner.os == 'Windows'
|
||||
|
|
@ -64,31 +102,266 @@ jobs:
|
|||
env:
|
||||
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()
|
||||
{
|
||||
git -C "$1" branch | grep '^* ' | cut -c 3-
|
||||
}
|
||||
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)"
|
||||
# Build with a tag like "Second_Life_Project_Shiny#abcdef0" to get a
|
||||
# viewer channel "Second Life Project Shiny" (ignoring "#hash",
|
||||
# needed to disambiguate tags).
|
||||
if [[ "$GITHUB_REF_TYPE" == "tag" && "${GITHUB_REF_NAME:0:12}" == "Second_Life_" ]]
|
||||
then viewer_channel="${GITHUB_REF_NAME%#*}"
|
||||
export viewer_channel="${viewer_channel//_/ }"
|
||||
else export viewer_channel="Second Life Test"
|
||||
fi
|
||||
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="$(cygpath -m "$(which python)")"
|
||||
export PYTHON="$(native_path "$(which python)")"
|
||||
echo "Python location: $PYTHON"
|
||||
export PYTHON_COMMAND="$PYTHON"
|
||||
else
|
||||
export PYTHON_COMMAND="python3"
|
||||
fi
|
||||
|
||||
autobuild configure -- -DVIEWER_CHANNEL="Second Life Test ${GIT_REF##*/}"
|
||||
autobuild build --no-configure
|
||||
export PYTHON_COMMAND_NATIVE="$(native_path "$PYTHON_COMMAND")"
|
||||
|
||||
# Find artifacts
|
||||
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
||||
installer_path=$(find ./build-*/newview/ | grep '_Setup\.exe')
|
||||
installer_name="$(basename $installer_path)"
|
||||
elif [[ "$RUNNER_OS" == "macOS" ]]; then
|
||||
installer_path=$(find ./build-*/newview/ | grep '\.dmg')
|
||||
installer_name="$(basename $installer_path)"
|
||||
./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
|
||||
|
||||
echo "installer_path=$installer_path" >> $GITHUB_OUTPUT
|
||||
echo "installer_name=$installer_name" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload installer
|
||||
- name: Upload executable
|
||||
if: matrix.configuration != 'ReleaseOS' && steps.build.outputs.viewer_app
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ steps.build.outputs.installer_name }}
|
||||
path: ${{ steps.build.outputs.installer_path }}
|
||||
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: matrix.configuration != 'ReleaseOS'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "${{ steps.build.outputs.artifact }}-symbols"
|
||||
path: |
|
||||
${{ steps.build.outputs.symbolfile }}
|
||||
|
||||
- name: Upload metadata
|
||||
if: matrix.configuration != 'ReleaseOS'
|
||||
uses: actions/upload-artifact@v3
|
||||
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@v3
|
||||
# should only be set for viewer-private
|
||||
if: matrix.configuration != 'ReleaseOS' && 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:
|
||||
needs: build
|
||||
runs-on: windows
|
||||
steps:
|
||||
- name: Sign and package Windows viewer
|
||||
uses: secondlife/viewer-build-util/sign-pkg-windows@main
|
||||
with:
|
||||
vault_uri: "${{ secrets.AZURE_KEY_VAULT_URI }}"
|
||||
cert_name: "${{ secrets.AZURE_CERT_NAME }}"
|
||||
client_id: "${{ secrets.AZURE_CLIENT_ID }}"
|
||||
client_secret: "${{ secrets.AZURE_CLIENT_SECRET }}"
|
||||
tenant_id: "${{ secrets.AZURE_TENANT_ID }}"
|
||||
|
||||
sign-and-package-mac:
|
||||
needs: build
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Unpack Mac notarization credentials
|
||||
id: note-creds
|
||||
shell: bash
|
||||
run: |
|
||||
# In NOTARIZE_CREDS_MACOS we expect to find:
|
||||
# USERNAME="..."
|
||||
# PASSWORD="..."
|
||||
# TEAM_ID="..."
|
||||
eval "${{ secrets.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
|
||||
uses: secondlife/viewer-build-util/sign-pkg-mac@main
|
||||
with:
|
||||
channel: ${{ needs.build.outputs.viewer_channel }}
|
||||
imagename: ${{ needs.build.outputs.imagename }}
|
||||
cert_base64: ${{ secrets.SIGNING_CERT_MACOS }}
|
||||
cert_name: ${{ secrets.SIGNING_CERT_MACOS_IDENTITY }}
|
||||
cert_pass: ${{ secrets.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 }}
|
||||
|
||||
post-windows-symbols:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Post Windows symbols
|
||||
uses: secondlife/viewer-build-util/post-bugsplat-windows@main
|
||||
with:
|
||||
username: ${{ secrets.BUGSPLAT_USER }}
|
||||
password: ${{ secrets.BUGSPLAT_PASS }}
|
||||
database: "SecondLife_Viewer_2018"
|
||||
channel: ${{ needs.build.outputs.viewer_channel }}
|
||||
version: ${{ needs.build.outputs.viewer_version }}
|
||||
|
||||
post-mac-symbols:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Post Mac symbols
|
||||
uses: secondlife/viewer-build-util/post-bugsplat-mac@main
|
||||
with:
|
||||
username: ${{ secrets.BUGSPLAT_USER }}
|
||||
password: ${{ secrets.BUGSPLAT_PASS }}
|
||||
database: "SecondLife_Viewer_2018"
|
||||
channel: ${{ needs.build.outputs.viewer_channel }}
|
||||
version: ${{ needs.build.outputs.viewer_version }}
|
||||
|
||||
release:
|
||||
needs: [sign-and-package-windows, sign-and-package-mac]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life_')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Reshuffle artifact files
|
||||
uses: secondlife/viewer-build-util/release-artifacts@main
|
||||
with:
|
||||
input-path: artifacts
|
||||
output-path: assets
|
||||
# The *-app artifacts are for use only by the signing and
|
||||
# packaging steps. Once we've generated signed installers, we no
|
||||
# longer need them, and we CERTAINLY don't want to publish
|
||||
# thousands of individual files as separate URLs.
|
||||
exclude: |-
|
||||
Windows-app
|
||||
macOS-app
|
||||
# Use just "Windows" or "macOS" prefix because these are the only
|
||||
# artifacts in which we expect files from both platforms with
|
||||
# colliding names (e.g. autobuild-package.xml). release-artifacts
|
||||
# normally resolves collisions by prepending the artifact name, so
|
||||
# when we anticipate collisions, it's good to keep the prefix
|
||||
# short and sweet.
|
||||
prefix: |-
|
||||
Windows-metadata=Windows
|
||||
macOS-metadata=macOS
|
||||
|
||||
# forked from softprops/action-gh-release
|
||||
- uses: secondlife-3p/action-gh-release@v1
|
||||
with:
|
||||
# name the release page for the build number so we can find it
|
||||
# easily (analogous to looking up a codeticket build page)
|
||||
name: "v${{ github.run_id }}"
|
||||
prerelease: true
|
||||
generate_release_notes: true
|
||||
# the only reason we generate a GH release is to post build products
|
||||
fail_on_unmatched_files: true
|
||||
files: "assets/*"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
|
|
|||
2580
autobuild.xml
2580
autobuild.xml
File diff suppressed because it is too large
Load Diff
173
build.sh
173
build.sh
|
|
@ -16,6 +16,8 @@
|
|||
# * The special style in which python is invoked is intentional to permit
|
||||
# use of a native python install on windows - which requires paths in DOS form
|
||||
|
||||
cleanup="true"
|
||||
|
||||
retry_cmd()
|
||||
{
|
||||
max_attempts="$1"; shift
|
||||
|
|
@ -110,6 +112,34 @@ installer_CYGWIN()
|
|||
fi
|
||||
}
|
||||
|
||||
[[ -n "$GITHUB_OUTPUT" ]] || fatal "Need to export GITHUB_OUTPUT"
|
||||
# The following is based on the Warning for GitHub multiline output strings:
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
|
||||
# Build up these arrays as we go
|
||||
metadata=()
|
||||
symbolfile=()
|
||||
physicstpv=()
|
||||
# and dump them to GITHUB_OUTPUT when done
|
||||
cleanup="$cleanup ; \
|
||||
arrayoutput metadata ; \
|
||||
arrayoutput symbolfile ; \
|
||||
arrayoutput physicstpv"
|
||||
trap "$cleanup" EXIT
|
||||
|
||||
arrayoutput()
|
||||
{
|
||||
local outputname="$1"
|
||||
# append "[*]" to the array name so array indirection works
|
||||
local array="$1[*]"
|
||||
local IFS='
|
||||
'
|
||||
echo "$outputname<<$EOF
|
||||
${!array}
|
||||
$EOF" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
|
||||
pre_build()
|
||||
{
|
||||
local variant="$1"
|
||||
|
|
@ -121,7 +151,7 @@ pre_build()
|
|||
RELEASE_CRASH_REPORTING=ON
|
||||
HAVOK=ON
|
||||
SIGNING=()
|
||||
if [ "$arch" == "Darwin" -a "$variant" == "Release" ]
|
||||
if [[ "$arch" == "Darwin" && "$variant" == "Release" ]]
|
||||
then SIGNING=("-DENABLE_SIGNING:BOOL=YES" \
|
||||
"-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.")
|
||||
fi
|
||||
|
|
@ -145,15 +175,27 @@ pre_build()
|
|||
VIEWER_SYMBOL_FILE="$(native_path "$abs_build_dir/newview/$variant/secondlife-symbols-$symplat-${AUTOBUILD_ADDRSIZE}.tar.bz2")"
|
||||
fi
|
||||
|
||||
# don't spew credentials into build log
|
||||
bugsplat_sh="$build_secrets_checkout/bugsplat/bugsplat.sh"
|
||||
set +x
|
||||
if [ -r "$bugsplat_sh" ]
|
||||
then # show that we're doing this, just not the contents
|
||||
echo source "$bugsplat_sh"
|
||||
source "$bugsplat_sh"
|
||||
# expect these variables to be set in the environment from GitHub secrets
|
||||
if [[ -n "$BUGSPLAT_DB" ]]
|
||||
then
|
||||
# don't spew credentials into build log
|
||||
set +x
|
||||
if [[ -z "$BUGSPLAT_USER" || -z "$BUGSPLAT_PASS" ]]
|
||||
then
|
||||
# older mechanism involving build-secrets repo -
|
||||
# if build_secrets_checkout isn't set, report its name
|
||||
bugsplat_sh="${build_secrets_checkout:-\$build_secrets_checkout}/bugsplat/bugsplat.sh"
|
||||
if [ -r "$bugsplat_sh" ]
|
||||
then # show that we're doing this, just not the contents
|
||||
echo source "$bugsplat_sh"
|
||||
source "$bugsplat_sh"
|
||||
else
|
||||
fatal "BUGSPLAT_USER or BUGSPLAT_PASS missing, and no $bugsplat_sh"
|
||||
fi
|
||||
fi
|
||||
set -x
|
||||
export BUGSPLAT_USER BUGSPLAT_PASS
|
||||
fi
|
||||
set -x
|
||||
|
||||
# honor autobuild_configure_parameters same as sling-buildscripts
|
||||
eval_autobuild_configure_parameters=$(eval $(echo echo $autobuild_configure_parameters))
|
||||
|
|
@ -181,13 +223,17 @@ package_llphysicsextensions_tpv()
|
|||
# nat 2016-12-21: without HAVOK, can't build PhysicsExtensions_TPV.
|
||||
if [ "$variant" = "Release" -a "${HAVOK:-}" != "OFF" ]
|
||||
then
|
||||
test -r "$build_dir/packages/llphysicsextensions/autobuild-tpv.xml" || fatal "No llphysicsextensions_tpv autobuild configuration found"
|
||||
tpvconfig=$(native_path "$build_dir/packages/llphysicsextensions/autobuild-tpv.xml")
|
||||
"$autobuild" build --quiet --config-file "$tpvconfig" -c Tpv || fatal "failed to build llphysicsextensions_tpv"
|
||||
tpvconfig="$build_dir/packages/llphysicsextensions/autobuild-tpv.xml"
|
||||
test -r "$tpvconfig" || fatal "No llphysicsextensions_tpv autobuild configuration found"
|
||||
# SL-19942: autobuild ignores -c switch if AUTOBUILD_CONFIGURATION set
|
||||
unset AUTOBUILD_CONFIGURATION
|
||||
"$autobuild" build --quiet --config-file "$(native_path "$tpvconfig")" -c Tpv \
|
||||
|| fatal "failed to build llphysicsextensions_tpv"
|
||||
|
||||
# capture the package file name for use in upload later...
|
||||
PKGTMP=`mktemp -t pgktpv.XXXXXX`
|
||||
trap "rm $PKGTMP* 2>/dev/null" 0
|
||||
cleanup="$cleanup ; rm $PKGTMP* 2>/dev/null"
|
||||
trap "$cleanup" EXIT
|
||||
"$autobuild" package --quiet --config-file "$tpvconfig" --results-file "$(native_path $PKGTMP)" || fatal "failed to package llphysicsextensions_tpv"
|
||||
tpv_status=$?
|
||||
if [ -r "${PKGTMP}" ]
|
||||
|
|
@ -313,12 +359,20 @@ begin_section "coding policy check"
|
|||
# this far. Running coding policy checks on one platform *should* suffice...
|
||||
if [[ "$arch" == "Darwin" ]]
|
||||
then
|
||||
# install the git-hooks dependencies
|
||||
pip install -r "$(native_path "$git_hooks_checkout/requirements.txt")" || \
|
||||
fatal "pip install git-hooks failed"
|
||||
# validate the branch we're about to build
|
||||
python_cmd "$git_hooks_checkout/coding_policy_git.py" --all_files || \
|
||||
fatal "coding policy check failed"
|
||||
git_hooks_reqs="$git_hooks_checkout/requirements.txt"
|
||||
if [[ -r "$(shell_path "$git_hooks_reqs")" ]]
|
||||
then
|
||||
# install the git-hooks dependencies
|
||||
pip install -r "$(native_path "$git_hooks_reqs")" || \
|
||||
fatal "pip install git-hooks failed"
|
||||
fi
|
||||
git_hooks_script="$git_hooks_checkout/coding_policy_git.py"
|
||||
if [[ -r "$(shell_path "$git_hooks_script")" ]]
|
||||
then
|
||||
# validate the branch we're about to build
|
||||
python_cmd "$(native_path "$git_hooks_script")" --all_files || \
|
||||
fatal "coding policy check failed"
|
||||
fi
|
||||
fi
|
||||
end_section "coding policy check"
|
||||
|
||||
|
|
@ -353,6 +407,7 @@ do
|
|||
begin_section "Autobuild metadata"
|
||||
python_cmd "$helpers/codeticket.py" addoutput "Autobuild Metadata" "$build_dir/autobuild-package.xml" --mimetype text/xml \
|
||||
|| fatal "Upload of autobuild metadata failed"
|
||||
metadata+=("$build_dir/autobuild-package.xml")
|
||||
if [ "$arch" != "Linux" ]
|
||||
then
|
||||
record_dependencies_graph "$build_dir/autobuild-package.xml" # defined in buildscripts/hg/bin/build.sh
|
||||
|
|
@ -366,8 +421,11 @@ do
|
|||
if [ -r "$build_dir/newview/viewer_version.txt" ]
|
||||
then
|
||||
begin_section "Viewer Version"
|
||||
python_cmd "$helpers/codeticket.py" addoutput "Viewer Version" "$(<"$build_dir/newview/viewer_version.txt")" --mimetype inline-text \
|
||||
viewer_version="$(<"$build_dir/newview/viewer_version.txt")"
|
||||
python_cmd "$helpers/codeticket.py" addoutput "Viewer Version" "$viewer_version" --mimetype inline-text \
|
||||
|| fatal "Upload of viewer version failed"
|
||||
metadata+=("$build_dir/newview/viewer_version.txt")
|
||||
echo "viewer_version=$viewer_version" >> "$GITHUB_OUTPUT"
|
||||
end_section "Viewer Version"
|
||||
fi
|
||||
;;
|
||||
|
|
@ -376,12 +434,14 @@ do
|
|||
then
|
||||
record_event "Doxygen warnings generated; see doxygen_warnings.log"
|
||||
python_cmd "$helpers/codeticket.py" addoutput "Doxygen Log" "$build_dir/doxygen_warnings.log" --mimetype text/plain ## TBD
|
||||
metadata+=("$build_dir/doxygen_warnings.log")
|
||||
fi
|
||||
if [ -d "$build_dir/doxygen/html" ]
|
||||
then
|
||||
tar -c -f "$build_dir/viewer-doxygen.tar.bz2" --strip-components 3 "$build_dir/doxygen/html"
|
||||
python_cmd "$helpers/codeticket.py" addoutput "Doxygen Tarball" "$build_dir/viewer-doxygen.tar.bz2" \
|
||||
|| fatal "Upload of doxygen tarball failed"
|
||||
metadata+=("$build_dir/viewer-doxygen.tar.bz2")
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
|
|
@ -483,64 +543,29 @@ then
|
|||
if $build_viewer
|
||||
then
|
||||
begin_section "Uploads"
|
||||
# Upload installer
|
||||
package=$(installer_$arch)
|
||||
if [ x"$package" = x ] || test -d "$package"
|
||||
# nat 2016-12-22: without RELEASE_CRASH_REPORTING, we have no symbol file.
|
||||
if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" ]
|
||||
then
|
||||
fatal "No installer found from `pwd`"
|
||||
succeeded=$build_coverity
|
||||
else
|
||||
# Upload base package.
|
||||
retry_cmd 4 30 python_cmd "$helpers/codeticket.py" addoutput Installer "$package" \
|
||||
|| fatal "Upload of installer failed"
|
||||
wait_for_codeticket
|
||||
|
||||
# Upload additional packages.
|
||||
for package_id in $additional_packages
|
||||
do
|
||||
package=$(installer_$arch "$package_id")
|
||||
if [ x"$package" != x ]
|
||||
# BugSplat wants to see xcarchive.zip
|
||||
# e.g. build-darwin-x86_64/newview/Release/Second Life Test.xcarchive.zip
|
||||
symbol_file="${build_dir}/newview/${variant}/${viewer_channel}.xcarchive.zip"
|
||||
if [[ ! -f "$symbol_file" ]]
|
||||
then
|
||||
retry_cmd 4 30 python_cmd "$helpers/codeticket.py" addoutput "Installer $package_id" "$package" \
|
||||
|| fatal "Upload of installer $package_id failed"
|
||||
wait_for_codeticket
|
||||
else
|
||||
record_failure "Failed to find additional package for '$package_id'."
|
||||
# symbol tarball we prep for (e.g.) Breakpad
|
||||
symbol_file="$VIEWER_SYMBOL_FILE"
|
||||
fi
|
||||
done
|
||||
# Upload crash reporter file
|
||||
symbolfile+=("$symbol_file")
|
||||
fi
|
||||
|
||||
if [ "$last_built_variant" = "Release" ]
|
||||
then
|
||||
# nat 2016-12-22: without RELEASE_CRASH_REPORTING, we have no symbol file.
|
||||
if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" ]
|
||||
then
|
||||
# Upload crash reporter file
|
||||
retry_cmd 4 30 python_cmd "$helpers/codeticket.py" addoutput "Symbolfile" "$VIEWER_SYMBOL_FILE" \
|
||||
|| fatal "Upload of symbolfile failed"
|
||||
wait_for_codeticket
|
||||
fi
|
||||
|
||||
# Upload the llphysicsextensions_tpv package, if one was produced
|
||||
# *TODO: Make this an upload-extension
|
||||
if [ -r "$build_dir/llphysicsextensions_package" ]
|
||||
then
|
||||
llphysicsextensions_package=$(cat $build_dir/llphysicsextensions_package)
|
||||
retry_cmd 4 30 python_cmd "$helpers/codeticket.py" addoutput "Physics Extensions Package" "$llphysicsextensions_package" --private \
|
||||
|| fatal "Upload of physics extensions package failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run upload extensions
|
||||
# Ex: bugsplat
|
||||
if [ -d ${build_dir}/packages/upload-extensions ]; then
|
||||
for extension in ${build_dir}/packages/upload-extensions/*.sh; do
|
||||
begin_section "Upload Extension $extension"
|
||||
. $extension
|
||||
[ $? -eq 0 ] || fatal "Upload of extension $extension failed"
|
||||
wait_for_codeticket
|
||||
end_section "Upload Extension $extension"
|
||||
done
|
||||
fi
|
||||
# Upload the llphysicsextensions_tpv package, if one was produced
|
||||
# Only upload this package when building the private repo so the
|
||||
# artifact is private.
|
||||
if [[ "x$GITHUB_REPOSITORY" == "xsecondlife/viewer-private" && \
|
||||
-r "$build_dir/llphysicsextensions_package" ]]
|
||||
then
|
||||
llphysicsextensions_package=$(cat $build_dir/llphysicsextensions_package)
|
||||
physicstpv+=("$llphysicsextensions_package")
|
||||
fi
|
||||
end_section "Uploads"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# standalone functions from sling-buildscripts
|
||||
|
||||
set_build_number_to_revision()
|
||||
{
|
||||
record_event "buildNumber $revision"
|
||||
}
|
||||
|
||||
record_event()
|
||||
{
|
||||
echo "=== $@"
|
||||
}
|
||||
|
||||
begin_section()
|
||||
{
|
||||
record_event "START $*"
|
||||
sections+=("$*")
|
||||
}
|
||||
|
||||
end_section()
|
||||
{
|
||||
# accommodate dumb Mac bash 3, which doesn't understand array[-1]
|
||||
local last=$(( ${#sections[@]} - 1 ))
|
||||
record_event "END ${*:-${sections[$last]}}"
|
||||
unset "sections[$last]"
|
||||
}
|
||||
|
||||
record_success()
|
||||
{
|
||||
record_event "SUCCESS $*"
|
||||
}
|
||||
|
||||
record_failure()
|
||||
{
|
||||
record_event "FAILURE $*" >&2
|
||||
}
|
||||
|
||||
fatal()
|
||||
{
|
||||
record_failure "$@"
|
||||
finalize false
|
||||
exit 1
|
||||
}
|
||||
|
||||
# redefined fail for backward compatibility
|
||||
alias fail=fatal
|
||||
|
||||
pass()
|
||||
{
|
||||
exit 0
|
||||
}
|
||||
|
||||
export -f set_build_number_to_revision
|
||||
export -f record_event
|
||||
export -f begin_section
|
||||
export -f end_section
|
||||
export -f record_success
|
||||
export -f record_failure
|
||||
export -f fatal
|
||||
export -f pass
|
||||
export sections
|
||||
|
|
@ -33,6 +33,11 @@ endif (WINDOWS)
|
|||
|
||||
# Portable compilation flags.
|
||||
add_compile_definitions( ADDRESS_SIZE=${ADDRESS_SIZE})
|
||||
# Because older versions of Boost.Bind dumped placeholders _1, _2 et al. into
|
||||
# the global namespace, Boost now requires either BOOST_BIND_NO_PLACEHOLDERS
|
||||
# to avoid that or BOOST_BIND_GLOBAL_PLACEHOLDERS to state that we require it
|
||||
# -- which we do. Without one or the other, we get a ton of Boost warnings.
|
||||
add_compile_definitions(BOOST_BIND_GLOBAL_PLACEHOLDERS)
|
||||
|
||||
# Configure crash reporting
|
||||
set(RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in release builds")
|
||||
|
|
@ -71,15 +76,6 @@ if (WINDOWS)
|
|||
# http://www.cmake.org/pipermail/cmake/2009-September/032143.html
|
||||
string(REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
|
||||
# Without PreferredToolArchitecture=x64, as of 2020-06-26 the 32-bit
|
||||
# compiler on our TeamCity build hosts has started running out of virtual
|
||||
# memory for the precompiled header file.
|
||||
# CP changed to only append the flag for 32bit builds - on 64bit builds,
|
||||
# locally at least, the build output is spammed with 1000s of 'D9002'
|
||||
# warnings about this switch being ignored.
|
||||
if(ADDRESS_SIZE EQUAL 32 AND DEFINED ENV{"TEAMCITY_PROJECT_NAME"})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64")
|
||||
endif()
|
||||
# zlib has assembly-language object files incompatible with SAFESEH
|
||||
add_link_options(/LARGEADDRESSAWARE
|
||||
/SAFESEH:NO
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ if (WINDOWS)
|
|||
endif (LLCOMMON_LINK_SHARED)
|
||||
target_link_libraries( ll::apr INTERFACE
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}apr-1.lib
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}apriconv-1.lib
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}aprutil-1.lib
|
||||
)
|
||||
elseif (DARWIN)
|
||||
|
|
@ -38,7 +37,6 @@ else (WINDOWS)
|
|||
target_link_libraries( ll::apr INTERFACE
|
||||
apr-1
|
||||
aprutil-1
|
||||
# iconv # <FS:Zi> Doesn't seem to be necessary for Linux
|
||||
uuid
|
||||
rt
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ if(WINDOWS)
|
|||
#openjp2.dll # <FS:Ansariel> Only copy OpenJPEG dll if needed
|
||||
libapr-1.dll
|
||||
libaprutil-1.dll
|
||||
libapriconv-1.dll
|
||||
nghttp2.dll
|
||||
glod.dll # <FS:Beq> restore GLOD
|
||||
libhunspell.dll
|
||||
|
|
@ -205,7 +204,6 @@ elseif(DARWIN)
|
|||
libndofdev.dylib
|
||||
libnghttp2.dylib
|
||||
libnghttp2.14.dylib
|
||||
libnghttp2.14.19.0.dylib
|
||||
liburiparser.dylib
|
||||
liburiparser.1.dylib
|
||||
liburiparser.1.0.27.dylib
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ elseif (WINDOWS)
|
|||
user32
|
||||
ole32
|
||||
dbghelp
|
||||
rpcrt4.lib
|
||||
legacy_stdio_definitions
|
||||
)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import itertools
|
|||
import operator
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -535,15 +536,15 @@ class LLManifest(object, metaclass=LLManifestRegistry):
|
|||
self.cmakedirs(path)
|
||||
return path
|
||||
|
||||
def run_command(self, command):
|
||||
def run_command(self, command, **kwds):
|
||||
"""
|
||||
Runs an external command.
|
||||
Raises ManifestError exception if the command returns a nonzero status.
|
||||
"""
|
||||
print("Running command:", command)
|
||||
print("Running command:", shlex.join(command))
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
subprocess.check_call(command)
|
||||
subprocess.check_call(command, **kwds)
|
||||
except subprocess.CalledProcessError as err:
|
||||
raise ManifestError( "Command %s returned non-zero status (%s)"
|
||||
% (command, err.returncode) )
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
#include "v3math.h"
|
||||
#include "v3dmath.h"
|
||||
|
|
|
|||
|
|
@ -122,11 +122,7 @@ LLCoros::LLCoros():
|
|||
// Previously we used
|
||||
// boost::context::guarded_stack_allocator::default_stacksize();
|
||||
// empirically this is insufficient.
|
||||
#if ADDRESS_SIZE == 64
|
||||
mStackSize(512*1024),
|
||||
#else
|
||||
mStackSize(256*1024),
|
||||
#endif
|
||||
mStackSize(768*1024),
|
||||
// mCurrent does NOT own the current CoroData instance -- it simply
|
||||
// points to it. So initialize it with a no-op deleter.
|
||||
mCurrent{ [](CoroData*){} }
|
||||
|
|
|
|||
|
|
@ -1623,3 +1623,18 @@ namespace LLError
|
|||
|
||||
|
||||
|
||||
void crashdriver(void (*callback)(int*))
|
||||
{
|
||||
// The LLERROR_CRASH macro used to have inline code of the form:
|
||||
//int* make_me_crash = NULL;
|
||||
//*make_me_crash = 0;
|
||||
|
||||
// But compilers are getting smart enough to recognize that, so we must
|
||||
// assign to an address supplied by a separate source file. We could do
|
||||
// the assignment here in crashdriver() -- but then BugSplat would group
|
||||
// all LL_ERRS() crashes as the fault of this one function, instead of
|
||||
// identifying the specific LL_ERRS() source line. So instead, do the
|
||||
// assignment in a lambda in the caller's source. We just provide the
|
||||
// nullptr target.
|
||||
callback(nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -397,11 +397,9 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
|
|||
#define LL_NEWLINE '\n'
|
||||
|
||||
// Use this only in LL_ERRS or in a place that LL_ERRS may not be used
|
||||
#define LLERROR_CRASH \
|
||||
{ \
|
||||
int* make_me_crash = NULL;\
|
||||
*make_me_crash = 0; \
|
||||
exit(*make_me_crash); \
|
||||
#define LLERROR_CRASH \
|
||||
{ \
|
||||
crashdriver([](int* ptr){ *ptr = 0; exit(*ptr); }); \
|
||||
}
|
||||
|
||||
#define LL_ENDL \
|
||||
|
|
@ -483,6 +481,8 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
|
|||
Resist the temptation to add a function like this because it incurs the
|
||||
expense of locking and map-searching every time control reaches it.
|
||||
bool debugLoggingEnabled(const std::string& tag);
|
||||
// used by LLERROR_CRASH
|
||||
void crashdriver(void (*)(int*));
|
||||
|
||||
Instead of:
|
||||
|
||||
|
|
|
|||
|
|
@ -389,6 +389,17 @@ public:
|
|||
// Read all remaining bytes and log.
|
||||
LL_INFOS("LLLeap") << mDesc << ": " << rest << LL_ENDL;
|
||||
}
|
||||
/*--------------------------- diagnostic ---------------------------*/
|
||||
else if (data["eof"].asBoolean())
|
||||
{
|
||||
LL_DEBUGS("LLLeap") << mDesc << " ended, no partial line" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("LLLeap") << mDesc << " (still running, " << childerr.size()
|
||||
<< " bytes pending)" << LL_ENDL;
|
||||
}
|
||||
/*------------------------- end diagnostic -------------------------*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ private:
|
|||
__cpuid(0x1, eax, ebx, ecx, edx);
|
||||
if(feature_infos[0] != (S32)edx)
|
||||
{
|
||||
LL_ERRS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL;
|
||||
LL_WARNS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL;
|
||||
}
|
||||
#endif // LL_RELEASE_FOR_DOWNLOAD
|
||||
|
||||
|
|
|
|||
|
|
@ -58,46 +58,14 @@
|
|||
* to restore uniform distribution.
|
||||
*/
|
||||
|
||||
// *NOTE: The system rand implementation is probably not correct.
|
||||
#define LL_USE_SYSTEM_RAND 0
|
||||
|
||||
#if LL_USE_SYSTEM_RAND
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
#if LL_USE_SYSTEM_RAND
|
||||
class LLSeedRand
|
||||
{
|
||||
public:
|
||||
LLSeedRand()
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
srand(LLUUID::getRandomSeed());
|
||||
#else
|
||||
srand48(LLUUID::getRandomSeed());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
static LLSeedRand sRandomSeeder;
|
||||
inline F64 ll_internal_random_double()
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return (F64)rand() / (F64)RAND_MAX;
|
||||
#else
|
||||
return drand48();
|
||||
#endif
|
||||
}
|
||||
inline F32 ll_internal_random_float()
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return (F32)rand() / (F32)RAND_MAX;
|
||||
#else
|
||||
return (F32)drand48();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed());
|
||||
inline F64 ll_internal_random_double()
|
||||
|
||||
// no default implementation, only specific F64 and F32 specializations
|
||||
template <typename REAL>
|
||||
inline REAL ll_internal_random();
|
||||
|
||||
template <>
|
||||
inline F64 ll_internal_random<F64>()
|
||||
{
|
||||
// *HACK: Through experimentation, we have found that dual core
|
||||
// CPUs (or at least multi-threaded processes) seem to
|
||||
|
|
@ -108,15 +76,35 @@ inline F64 ll_internal_random_double()
|
|||
return rv;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline F32 ll_internal_random<F32>()
|
||||
{
|
||||
return F32(ll_internal_random<F64>());
|
||||
}
|
||||
|
||||
/*------------------------------ F64 aliases -------------------------------*/
|
||||
inline F64 ll_internal_random_double()
|
||||
{
|
||||
return ll_internal_random<F64>();
|
||||
}
|
||||
|
||||
F64 ll_drand()
|
||||
{
|
||||
return ll_internal_random_double();
|
||||
}
|
||||
|
||||
/*------------------------------ F32 aliases -------------------------------*/
|
||||
inline F32 ll_internal_random_float()
|
||||
{
|
||||
// The clamping rules are described above.
|
||||
F32 rv = (F32)gRandomGenerator();
|
||||
if(!((rv >= 0.0f) && (rv < 1.0f))) return fmod(rv, 1.f);
|
||||
return rv;
|
||||
return ll_internal_random<F32>();
|
||||
}
|
||||
#endif
|
||||
|
||||
F32 ll_frand()
|
||||
{
|
||||
return ll_internal_random_float();
|
||||
}
|
||||
|
||||
/*-------------------------- clamped random range --------------------------*/
|
||||
S32 ll_rand()
|
||||
{
|
||||
return ll_rand(RAND_MAX);
|
||||
|
|
@ -130,42 +118,28 @@ S32 ll_rand(S32 val)
|
|||
return rv;
|
||||
}
|
||||
|
||||
F32 ll_frand()
|
||||
template <typename REAL>
|
||||
REAL ll_grand(REAL val)
|
||||
{
|
||||
return ll_internal_random_float();
|
||||
// The clamping rules are described above.
|
||||
REAL rv = ll_internal_random<REAL>() * val;
|
||||
if(val > 0)
|
||||
{
|
||||
if(rv >= val) return REAL();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rv <= val) return REAL();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
F32 ll_frand(F32 val)
|
||||
{
|
||||
// The clamping rules are described above.
|
||||
F32 rv = ll_internal_random_float() * val;
|
||||
if(val > 0)
|
||||
{
|
||||
if(rv >= val) return 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rv <= val) return 0.0f;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
F64 ll_drand()
|
||||
{
|
||||
return ll_internal_random_double();
|
||||
return ll_grand<F32>(val);
|
||||
}
|
||||
|
||||
F64 ll_drand(F64 val)
|
||||
{
|
||||
// The clamping rules are described above.
|
||||
F64 rv = ll_internal_random_double() * val;
|
||||
if(val > 0)
|
||||
{
|
||||
if(rv >= val) return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rv <= val) return 0.0;
|
||||
}
|
||||
return rv;
|
||||
return ll_grand<F64>(val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
// std headers
|
||||
#include <functional>
|
||||
// external library headers
|
||||
#include <boost/assign/list_of.hpp>
|
||||
#include <boost/phoenix/core/argument.hpp>
|
||||
// other Linden headers
|
||||
#include "../test/lltut.h"
|
||||
#include "../test/namedtempfile.h"
|
||||
|
|
@ -30,10 +28,6 @@
|
|||
#include "stringize.h"
|
||||
#include "StringVec.h"
|
||||
|
||||
using boost::assign::list_of;
|
||||
|
||||
StringVec sv(const StringVec& listof) { return listof; }
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
#define sleep(secs) _sleep((secs) * 1000)
|
||||
|
||||
|
|
@ -104,17 +98,12 @@ namespace tut
|
|||
llleap_data():
|
||||
reader(".py",
|
||||
// This logic is adapted from vita.viewerclient.receiveEvent()
|
||||
boost::phoenix::placeholders::arg1 <<
|
||||
[](std::ostream& out){ out <<
|
||||
"import re\n"
|
||||
"import os\n"
|
||||
"import sys\n"
|
||||
"\n"
|
||||
"try:\n"
|
||||
// new freestanding llsd package
|
||||
" import llsd\n"
|
||||
"except ImportError:\n"
|
||||
// older llbase.llsd module
|
||||
" from llbase import llsd\n"
|
||||
"import llsd\n"
|
||||
"\n"
|
||||
"class ProtocolError(Exception):\n"
|
||||
" def __init__(self, msg, data):\n"
|
||||
|
|
@ -193,7 +182,7 @@ namespace tut
|
|||
"def request(pump, data):\n"
|
||||
" # we expect 'data' is a dict\n"
|
||||
" data['reply'] = _reply\n"
|
||||
" send(pump, data)\n"),
|
||||
" send(pump, data)\n";}),
|
||||
// Get the actual pathname of the NamedExtTempFile and trim off
|
||||
// the ".py" extension. (We could cache reader.getName() in a
|
||||
// separate member variable, but I happen to know getName() just
|
||||
|
|
@ -218,14 +207,14 @@ namespace tut
|
|||
void object::test<1>()
|
||||
{
|
||||
set_test_name("multiple LLLeap instances");
|
||||
NamedTempFile script("py",
|
||||
"import time\n"
|
||||
"time.sleep(1)\n");
|
||||
NamedExtTempFile script("py",
|
||||
"import time\n"
|
||||
"time.sleep(1)\n");
|
||||
LLLeapVector instances;
|
||||
instances.push_back(LLLeap::create(get_test_name(),
|
||||
sv(list_of(PYTHON)(script.getName())))->getWeak());
|
||||
StringVec{PYTHON, script.getName()})->getWeak());
|
||||
instances.push_back(LLLeap::create(get_test_name(),
|
||||
sv(list_of(PYTHON)(script.getName())))->getWeak());
|
||||
StringVec{PYTHON, script.getName()})->getWeak());
|
||||
// In this case we're simply establishing that two LLLeap instances
|
||||
// can coexist without throwing exceptions or bombing in any other
|
||||
// way. Wait for them to terminate.
|
||||
|
|
@ -236,10 +225,10 @@ namespace tut
|
|||
void object::test<2>()
|
||||
{
|
||||
set_test_name("stderr to log");
|
||||
NamedTempFile script("py",
|
||||
"import sys\n"
|
||||
"sys.stderr.write('''Hello from Python!\n"
|
||||
"note partial line''')\n");
|
||||
NamedExtTempFile script("py",
|
||||
"import sys\n"
|
||||
"sys.stderr.write('''Hello from Python!\n"
|
||||
"note partial line''')\n");
|
||||
StringVec vcommand{ PYTHON, script.getName() };
|
||||
CaptureLog log(LLError::LEVEL_INFO);
|
||||
waitfor(LLLeap::create(get_test_name(), vcommand));
|
||||
|
|
@ -251,11 +240,11 @@ namespace tut
|
|||
void object::test<3>()
|
||||
{
|
||||
set_test_name("bad stdout protocol");
|
||||
NamedTempFile script("py",
|
||||
"print('Hello from Python!')\n");
|
||||
NamedExtTempFile script("py",
|
||||
"print('Hello from Python!')\n");
|
||||
CaptureLog log(LLError::LEVEL_WARN);
|
||||
waitfor(LLLeap::create(get_test_name(),
|
||||
sv(list_of(PYTHON)(script.getName()))));
|
||||
StringVec{PYTHON, script.getName()}));
|
||||
ensure_contains("error log line",
|
||||
log.messageWith("invalid protocol"), "Hello from Python!");
|
||||
}
|
||||
|
|
@ -264,13 +253,13 @@ namespace tut
|
|||
void object::test<4>()
|
||||
{
|
||||
set_test_name("leftover stdout");
|
||||
NamedTempFile script("py",
|
||||
"import sys\n"
|
||||
// note lack of newline
|
||||
"sys.stdout.write('Hello from Python!')\n");
|
||||
NamedExtTempFile script("py",
|
||||
"import sys\n"
|
||||
// note lack of newline
|
||||
"sys.stdout.write('Hello from Python!')\n");
|
||||
CaptureLog log(LLError::LEVEL_WARN);
|
||||
waitfor(LLLeap::create(get_test_name(),
|
||||
sv(list_of(PYTHON)(script.getName()))));
|
||||
StringVec{PYTHON, script.getName()}));
|
||||
ensure_contains("error log line",
|
||||
log.messageWith("Discarding"), "Hello from Python!");
|
||||
}
|
||||
|
|
@ -279,12 +268,12 @@ namespace tut
|
|||
void object::test<5>()
|
||||
{
|
||||
set_test_name("bad stdout len prefix");
|
||||
NamedTempFile script("py",
|
||||
"import sys\n"
|
||||
"sys.stdout.write('5a2:something')\n");
|
||||
NamedExtTempFile script("py",
|
||||
"import sys\n"
|
||||
"sys.stdout.write('5a2:something')\n");
|
||||
CaptureLog log(LLError::LEVEL_WARN);
|
||||
waitfor(LLLeap::create(get_test_name(),
|
||||
sv(list_of(PYTHON)(script.getName()))));
|
||||
StringVec{PYTHON, script.getName()}));
|
||||
ensure_contains("error log line",
|
||||
log.messageWith("invalid protocol"), "5a2:");
|
||||
}
|
||||
|
|
@ -386,17 +375,18 @@ namespace tut
|
|||
set_test_name("round trip");
|
||||
AckAPI api;
|
||||
Result result;
|
||||
NamedTempFile script("py",
|
||||
boost::phoenix::placeholders::arg1 <<
|
||||
"from " << reader_module << " import *\n"
|
||||
// make a request on our little API
|
||||
"request(pump='" << api.getName() << "', data={})\n"
|
||||
// wait for its response
|
||||
"resp = get()\n"
|
||||
"result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
|
||||
" else 'bad: ' + str(resp)\n"
|
||||
"send(pump='" << result.getName() << "', data=result)\n");
|
||||
waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
|
||||
NamedExtTempFile script("py",
|
||||
[&](std::ostream& out){ out <<
|
||||
"from " << reader_module << " import *\n"
|
||||
// make a request on our little API
|
||||
"request(pump='" << api.getName() << "', data={})\n"
|
||||
// wait for its response
|
||||
"resp = get()\n"
|
||||
"result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
|
||||
" else 'bad: ' + str(resp)\n"
|
||||
"send(pump='" << result.getName() << "', data=result)\n";});
|
||||
waitfor(LLLeap::create(get_test_name(),
|
||||
StringVec{PYTHON, script.getName()}));
|
||||
result.ensure();
|
||||
}
|
||||
|
||||
|
|
@ -424,38 +414,38 @@ namespace tut
|
|||
// iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
|
||||
ReqIDAPI api;
|
||||
Result result;
|
||||
NamedTempFile script("py",
|
||||
boost::phoenix::placeholders::arg1 <<
|
||||
"import sys\n"
|
||||
"from " << reader_module << " import *\n"
|
||||
// Note that since reader imports llsd, this
|
||||
// 'import *' gets us llsd too.
|
||||
"sample = llsd.format_notation(dict(pump='" <<
|
||||
api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
|
||||
// The whole packet has length prefix too: "len:data"
|
||||
"samplen = len(str(len(sample))) + 1 + len(sample)\n"
|
||||
// guess how many messages it will take to
|
||||
// accumulate BUFFERED_LENGTH
|
||||
"count = int(" << BUFFERED_LENGTH << "/samplen)\n"
|
||||
"print('Sending %s requests' % count, file=sys.stderr)\n"
|
||||
"for i in range(count):\n"
|
||||
" request('" << api.getName() << "', dict(reqid=i))\n"
|
||||
// The assumption in this specific test that
|
||||
// replies will arrive in the same order as
|
||||
// requests is ONLY valid because the API we're
|
||||
// invoking sends replies instantly. If the API
|
||||
// had to wait for some external event before
|
||||
// sending its reply, replies could arrive in
|
||||
// arbitrary order, and we'd have to tick them
|
||||
// off from a set.
|
||||
"result = ''\n"
|
||||
"for i in range(count):\n"
|
||||
" resp = get()\n"
|
||||
" if resp['data']['reqid'] != i:\n"
|
||||
" result = 'expected reqid=%s in %s' % (i, resp)\n"
|
||||
" break\n"
|
||||
"send(pump='" << result.getName() << "', data=result)\n");
|
||||
waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
|
||||
NamedExtTempFile script("py",
|
||||
[&](std::ostream& out){ out <<
|
||||
"import sys\n"
|
||||
"from " << reader_module << " import *\n"
|
||||
// Note that since reader imports llsd, this
|
||||
// 'import *' gets us llsd too.
|
||||
"sample = llsd.format_notation(dict(pump='" <<
|
||||
api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
|
||||
// The whole packet has length prefix too: "len:data"
|
||||
"samplen = len(str(len(sample))) + 1 + len(sample)\n"
|
||||
// guess how many messages it will take to
|
||||
// accumulate BUFFERED_LENGTH
|
||||
"count = int(" << BUFFERED_LENGTH << "/samplen)\n"
|
||||
"print('Sending %s requests' % count, file=sys.stderr)\n"
|
||||
"for i in range(count):\n"
|
||||
" request('" << api.getName() << "', dict(reqid=i))\n"
|
||||
// The assumption in this specific test that
|
||||
// replies will arrive in the same order as
|
||||
// requests is ONLY valid because the API we're
|
||||
// invoking sends replies instantly. If the API
|
||||
// had to wait for some external event before
|
||||
// sending its reply, replies could arrive in
|
||||
// arbitrary order, and we'd have to tick them
|
||||
// off from a set.
|
||||
"result = ''\n"
|
||||
"for i in range(count):\n"
|
||||
" resp = get()\n"
|
||||
" if resp['data']['reqid'] != i:\n"
|
||||
" result = 'expected reqid=%s in %s' % (i, resp)\n"
|
||||
" break\n"
|
||||
"send(pump='" << result.getName() << "', data=result)\n";});
|
||||
waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
|
||||
300); // needs more realtime than most tests
|
||||
result.ensure();
|
||||
}
|
||||
|
|
@ -467,65 +457,62 @@ namespace tut
|
|||
{
|
||||
ReqIDAPI api;
|
||||
Result result;
|
||||
NamedTempFile script("py",
|
||||
boost::phoenix::placeholders::arg1 <<
|
||||
"import sys\n"
|
||||
"from " << reader_module << " import *\n"
|
||||
// Generate a very large string value.
|
||||
"desired = int(sys.argv[1])\n"
|
||||
// 7 chars per item: 6 digits, 1 comma
|
||||
"count = int((desired - 50)/7)\n"
|
||||
"large = ''.join('%06d,' % i for i in range(count))\n"
|
||||
// Pass 'large' as reqid because we know the API
|
||||
// will echo reqid, and we want to receive it back.
|
||||
"request('" << api.getName() << "', dict(reqid=large))\n"
|
||||
"try:\n"
|
||||
" resp = get()\n"
|
||||
"except ParseError as e:\n"
|
||||
" # try to find where e.data diverges from expectation\n"
|
||||
// Normally we'd expect a 'pump' key in there,
|
||||
// too, with value replypump(). But Python
|
||||
// serializes keys in a different order than C++,
|
||||
// so incoming data start with 'data'.
|
||||
// Truthfully, though, if we get as far as 'pump'
|
||||
// before we find a difference, something's very
|
||||
// strange.
|
||||
" expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
|
||||
" chunk = 40\n"
|
||||
" for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
|
||||
" if e.data[offset:offset+chunk] != \\\n"
|
||||
" expect[offset:offset+chunk]:\n"
|
||||
" print('Offset %06d: expect %r,\\n'\\\n"
|
||||
" ' get %r' %\\\n"
|
||||
" (offset,\n"
|
||||
" expect[offset:offset+chunk],\n"
|
||||
" e.data[offset:offset+chunk]),\n"
|
||||
" file=sys.stderr)\n"
|
||||
" break\n"
|
||||
" else:\n"
|
||||
" print('incoming data matches expect?!', file=sys.stderr)\n"
|
||||
" send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
|
||||
" sys.exit(1)\n"
|
||||
"\n"
|
||||
"echoed = resp['data']['reqid']\n"
|
||||
"if echoed == large:\n"
|
||||
" send('" << result.getName() << "', '')\n"
|
||||
" sys.exit(0)\n"
|
||||
// Here we know echoed did NOT match; try to find where
|
||||
"for i in range(count):\n"
|
||||
" start = 7*i\n"
|
||||
" end = 7*(i+1)\n"
|
||||
" if end > len(echoed)\\\n"
|
||||
" or echoed[start:end] != large[start:end]:\n"
|
||||
" send('" << result.getName() << "',\n"
|
||||
" 'at offset %s, expected %r but got %r' %\n"
|
||||
" (start, large[start:end], echoed[start:end]))\n"
|
||||
"sys.exit(1)\n");
|
||||
NamedExtTempFile script("py",
|
||||
[&](std::ostream& out){ out <<
|
||||
"import sys\n"
|
||||
"from " << reader_module << " import *\n"
|
||||
// Generate a very large string value.
|
||||
"desired = int(sys.argv[1])\n"
|
||||
// 7 chars per item: 6 digits, 1 comma
|
||||
"count = int((desired - 50)/7)\n"
|
||||
"large = ''.join('%06d,' % i for i in range(count))\n"
|
||||
// Pass 'large' as reqid because we know the API
|
||||
// will echo reqid, and we want to receive it back.
|
||||
"request('" << api.getName() << "', dict(reqid=large))\n"
|
||||
"try:\n"
|
||||
" resp = get()\n"
|
||||
"except ParseError as e:\n"
|
||||
" # try to find where e.data diverges from expectation\n"
|
||||
// Normally we'd expect a 'pump' key in there,
|
||||
// too, with value replypump(). But Python
|
||||
// serializes keys in a different order than C++,
|
||||
// so incoming data start with 'data'.
|
||||
// Truthfully, though, if we get as far as 'pump'
|
||||
// before we find a difference, something's very
|
||||
// strange.
|
||||
" expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
|
||||
" chunk = 40\n"
|
||||
" for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
|
||||
" if e.data[offset:offset+chunk] != \\\n"
|
||||
" expect[offset:offset+chunk]:\n"
|
||||
" print('Offset %06d: expect %r,\\n'\\\n"
|
||||
" ' get %r' %\\\n"
|
||||
" (offset,\n"
|
||||
" expect[offset:offset+chunk],\n"
|
||||
" e.data[offset:offset+chunk]),\n"
|
||||
" file=sys.stderr)\n"
|
||||
" break\n"
|
||||
" else:\n"
|
||||
" print('incoming data matches expect?!', file=sys.stderr)\n"
|
||||
" send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
|
||||
" sys.exit(1)\n"
|
||||
"\n"
|
||||
"echoed = resp['data']['reqid']\n"
|
||||
"if echoed == large:\n"
|
||||
" send('" << result.getName() << "', '')\n"
|
||||
" sys.exit(0)\n"
|
||||
// Here we know echoed did NOT match; try to find where
|
||||
"for i in range(count):\n"
|
||||
" start = 7*i\n"
|
||||
" end = 7*(i+1)\n"
|
||||
" if end > len(echoed)\\\n"
|
||||
" or echoed[start:end] != large[start:end]:\n"
|
||||
" send('" << result.getName() << "',\n"
|
||||
" 'at offset %s, expected %r but got %r' %\n"
|
||||
" (start, large[start:end], echoed[start:end]))\n"
|
||||
"sys.exit(1)\n";});
|
||||
waitfor(LLLeap::create(test_name,
|
||||
sv(list_of
|
||||
(PYTHON)
|
||||
(script.getName())
|
||||
(stringize(size)))),
|
||||
StringVec{PYTHON, script.getName(), stringize(size)}),
|
||||
180); // try a longer timeout
|
||||
result.ensure();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,8 +151,38 @@ struct PythonProcessLauncher
|
|||
/// Launch Python script; verify that it launched
|
||||
void launch()
|
||||
{
|
||||
mPy = LLProcess::create(mParams);
|
||||
tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
|
||||
try
|
||||
{
|
||||
mPy = LLProcess::create(mParams);
|
||||
tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
|
||||
}
|
||||
catch (const tut::failure&)
|
||||
{
|
||||
// On Windows, if APR_LOG is set, our version of APR's
|
||||
// apr_create_proc() logs to the specified file. If this test
|
||||
// failed, try to report that log.
|
||||
const char* APR_LOG = getenv("APR_LOG");
|
||||
if (APR_LOG && *APR_LOG)
|
||||
{
|
||||
std::ifstream inf(APR_LOG);
|
||||
if (! inf.is_open())
|
||||
{
|
||||
LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "==============================" << LL_ENDL;
|
||||
LL_WARNS() << "From '" << APR_LOG << "':" << LL_ENDL;
|
||||
std::string line;
|
||||
while (std::getline(inf, line))
|
||||
{
|
||||
LL_WARNS() << line << LL_ENDL;
|
||||
}
|
||||
LL_WARNS() << "==============================" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Python script and wait for it to complete.
|
||||
|
|
@ -191,7 +221,7 @@ struct PythonProcessLauncher
|
|||
LLProcess::Params mParams;
|
||||
LLProcessPtr mPy;
|
||||
std::string mDesc;
|
||||
NamedTempFile mScript;
|
||||
NamedExtTempFile mScript;
|
||||
};
|
||||
|
||||
/// convenience function for PythonProcessLauncher::run()
|
||||
|
|
@ -214,30 +244,26 @@ static std::string python_out(const std::string& desc, const CONTENT& script)
|
|||
class NamedTempDir: public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
// Use python() function to create a temp directory: I've found
|
||||
// nothing in either Boost.Filesystem or APR quite like Python's
|
||||
// tempfile.mkdtemp().
|
||||
// Special extra bonus: on Mac, mkdtemp() reports a pathname
|
||||
// starting with /var/folders/something, whereas that's really a
|
||||
// symlink to /private/var/folders/something. Have to use
|
||||
// realpath() to compare properly.
|
||||
NamedTempDir():
|
||||
mPath(python_out("mkdtemp()",
|
||||
"from __future__ import with_statement\n"
|
||||
"import os.path, sys, tempfile\n"
|
||||
"with open(sys.argv[1], 'w') as f:\n"
|
||||
" f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
|
||||
{}
|
||||
mPath(NamedTempFile::temp_path()),
|
||||
mCreated(boost::filesystem::create_directories(mPath))
|
||||
{
|
||||
mPath = boost::filesystem::canonical(mPath);
|
||||
}
|
||||
|
||||
~NamedTempDir()
|
||||
{
|
||||
aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
|
||||
if (mCreated)
|
||||
{
|
||||
boost::filesystem::remove_all(mPath);
|
||||
}
|
||||
}
|
||||
|
||||
std::string getName() const { return mPath; }
|
||||
std::string getName() const { return mPath.string(); }
|
||||
|
||||
private:
|
||||
std::string mPath;
|
||||
boost::filesystem::path mPath;
|
||||
bool mCreated;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
@ -355,7 +381,7 @@ namespace tut
|
|||
set_test_name("raw APR nonblocking I/O");
|
||||
|
||||
// Create a script file in a temporary place.
|
||||
NamedTempFile script("py",
|
||||
NamedExtTempFile script("py",
|
||||
"from __future__ import print_function" EOL
|
||||
"import sys" EOL
|
||||
"import time" EOL
|
||||
|
|
@ -565,7 +591,13 @@ namespace tut
|
|||
" f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
|
||||
// Before running, call setWorkingDirectory()
|
||||
py.mParams.cwd = tempdir.getName();
|
||||
ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
|
||||
std::string expected{ tempdir.getName() };
|
||||
#if LL_WINDOWS
|
||||
// SIGH, don't get tripped up by "C:" != "c:" --
|
||||
// but on the Mac, using tolower() fails because "/users" != "/Users"!
|
||||
expected = utf8str_tolower(expected);
|
||||
#endif
|
||||
ensure_equals("os.getcwd()", py.run_read(), expected);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,23 @@
|
|||
#include "../test/lltut.h"
|
||||
|
||||
#include "../llrand.h"
|
||||
#include "stringize.h"
|
||||
|
||||
// In llrand.h, every function is documented to return less than the high end
|
||||
// -- specifically, because you can pass a negative extent, they're documented
|
||||
// never to return a value equal to the extent.
|
||||
// So that we don't need two different versions of ensure_in_range(), when
|
||||
// testing extent < 0, negate the return value and the extent before passing
|
||||
// into ensure_in_range().
|
||||
template <typename NUMBER>
|
||||
void ensure_in_range(const std::string_view& name,
|
||||
NUMBER value, NUMBER low, NUMBER high)
|
||||
{
|
||||
auto failmsg{ stringize(name, " >= ", low, " (", value, ')') };
|
||||
tut::ensure(failmsg, (value >= low));
|
||||
failmsg = stringize(name, " < ", high, " (", value, ')');
|
||||
tut::ensure(failmsg, (value < high));
|
||||
}
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
|
@ -44,84 +60,65 @@ namespace tut
|
|||
template<> template<>
|
||||
void random_object_t::test<1>()
|
||||
{
|
||||
F32 number = 0.0f;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_frand();
|
||||
ensure("frand >= 0", (number >= 0.0f));
|
||||
ensure("frand < 1", (number < 1.0f));
|
||||
ensure_in_range("frand", ll_frand(), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<2>()
|
||||
{
|
||||
F64 number = 0.0f;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_drand();
|
||||
ensure("drand >= 0", (number >= 0.0));
|
||||
ensure("drand < 1", (number < 1.0));
|
||||
ensure_in_range("drand", ll_drand(), 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<3>()
|
||||
{
|
||||
F32 number = 0.0f;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_frand(2.0f) - 1.0f;
|
||||
ensure("frand >= 0", (number >= -1.0f));
|
||||
ensure("frand < 1", (number <= 1.0f));
|
||||
ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<4>()
|
||||
{
|
||||
F32 number = 0.0f;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_frand(-7.0);
|
||||
ensure("drand <= 0", (number <= 0.0));
|
||||
ensure("drand > -7", (number > -7.0));
|
||||
// Negate the result so we don't have to allow a templated low-end
|
||||
// comparison as well.
|
||||
ensure_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<5>()
|
||||
{
|
||||
F64 number = 0.0f;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_drand(-2.0);
|
||||
ensure("drand <= 0", (number <= 0.0));
|
||||
ensure("drand > -2", (number > -2.0));
|
||||
ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<6>()
|
||||
{
|
||||
S32 number = 0;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_rand(100);
|
||||
ensure("rand >= 0", (number >= 0));
|
||||
ensure("rand < 100", (number < 100));
|
||||
ensure_in_range("rand(100)", ll_rand(100), 0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void random_object_t::test<7>()
|
||||
{
|
||||
S32 number = 0;
|
||||
for(S32 ii = 0; ii < 100000; ++ii)
|
||||
{
|
||||
number = ll_rand(-127);
|
||||
ensure("rand <= 0", (number <= 0));
|
||||
ensure("rand > -127", (number > -127));
|
||||
ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,6 @@ typedef U32 uint32_t;
|
|||
#endif
|
||||
|
||||
#include "boost/range.hpp"
|
||||
#include "boost/foreach.hpp"
|
||||
#include "boost/bind.hpp"
|
||||
#include "boost/phoenix/bind/bind_function.hpp"
|
||||
#include "boost/phoenix/core/argument.hpp"
|
||||
using namespace boost::phoenix;
|
||||
|
||||
#include "llsd.h"
|
||||
#include "llsdserialize.h"
|
||||
|
|
@ -57,9 +52,11 @@ using namespace boost::phoenix;
|
|||
#include "llformat.h"
|
||||
#include "llmemorystream.h"
|
||||
|
||||
#include "../test/hexdump.h"
|
||||
#include "../test/lltut.h"
|
||||
#include "../test/namedtempfile.h"
|
||||
#include "stringize.h"
|
||||
#include "StringVec.h"
|
||||
#include <functional>
|
||||
|
||||
typedef std::function<void(const LLSD& data, std::ostream& str)> FormatterFunction;
|
||||
|
|
@ -1796,16 +1793,12 @@ namespace tut
|
|||
// helper for TestPythonCompatible
|
||||
static std::string import_llsd("import os.path\n"
|
||||
"import sys\n"
|
||||
"try:\n"
|
||||
// new freestanding llsd package
|
||||
" import llsd\n"
|
||||
"except ImportError:\n"
|
||||
// older llbase.llsd module
|
||||
" from llbase import llsd\n");
|
||||
"import llsd\n");
|
||||
|
||||
// helper for TestPythonCompatible
|
||||
template <typename CONTENT>
|
||||
void python(const std::string& desc, const CONTENT& script, int expect=0)
|
||||
template <typename CONTENT, typename... ARGS>
|
||||
void python_expect(const std::string& desc, const CONTENT& script, int expect=0,
|
||||
ARGS&&... args)
|
||||
{
|
||||
auto PYTHON(LLStringUtil::getenv("PYTHON"));
|
||||
ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty());
|
||||
|
|
@ -1816,7 +1809,8 @@ namespace tut
|
|||
std::string q("\"");
|
||||
std::string qPYTHON(q + PYTHON + q);
|
||||
std::string qscript(q + scriptfile.getName() + q);
|
||||
int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL);
|
||||
int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(),
|
||||
std::forward<ARGS>(args)..., NULL);
|
||||
if (rc == -1)
|
||||
{
|
||||
char buffer[256];
|
||||
|
|
@ -1832,6 +1826,10 @@ namespace tut
|
|||
LLProcess::Params params;
|
||||
params.executable = PYTHON;
|
||||
params.args.add(scriptfile.getName());
|
||||
for (const std::string& arg : StringVec{ std::forward<ARGS>(args)... })
|
||||
{
|
||||
params.args.add(arg);
|
||||
}
|
||||
LLProcessPtr py(LLProcess::create(params));
|
||||
ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py));
|
||||
// Implementing timeout would mean messing with alarm() and
|
||||
|
|
@ -1866,6 +1864,14 @@ namespace tut
|
|||
#endif
|
||||
}
|
||||
|
||||
// helper for TestPythonCompatible
|
||||
template <typename CONTENT, typename... ARGS>
|
||||
void python(const std::string& desc, const CONTENT& script, ARGS&&... args)
|
||||
{
|
||||
// plain python() expects rc 0
|
||||
python_expect(desc, script, 0, std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
struct TestPythonCompatible
|
||||
{
|
||||
TestPythonCompatible() {}
|
||||
|
|
@ -1880,10 +1886,10 @@ namespace tut
|
|||
void TestPythonCompatibleObject::test<1>()
|
||||
{
|
||||
set_test_name("verify python()");
|
||||
python("hello",
|
||||
"import sys\n"
|
||||
"sys.exit(17)\n",
|
||||
17); // expect nonzero rc
|
||||
python_expect("hello",
|
||||
"import sys\n"
|
||||
"sys.exit(17)\n",
|
||||
17); // expect nonzero rc
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
|
|
@ -1899,7 +1905,7 @@ namespace tut
|
|||
static void writeLLSDArray(const FormatterFunction& serialize,
|
||||
std::ostream& out, const LLSD& array)
|
||||
{
|
||||
for (const LLSD& item : llsd::inArray(array))
|
||||
for (const LLSD& item: llsd::inArray(array))
|
||||
{
|
||||
// It's important to delimit the entries in this file somehow
|
||||
// because, although Python's llsd.parse() can accept a file
|
||||
|
|
@ -1914,7 +1920,14 @@ namespace tut
|
|||
auto buffstr{ buffer.str() };
|
||||
int bufflen{ static_cast<int>(buffstr.length()) };
|
||||
out.write(reinterpret_cast<const char*>(&bufflen), sizeof(bufflen));
|
||||
LL_DEBUGS() << "Wrote length: "
|
||||
<< hexdump(reinterpret_cast<const char*>(&bufflen),
|
||||
sizeof(bufflen))
|
||||
<< LL_ENDL;
|
||||
out.write(buffstr.c_str(), buffstr.length());
|
||||
LL_DEBUGS() << "Wrote data: "
|
||||
<< hexmix(buffstr.c_str(), buffstr.length())
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1943,10 +1956,10 @@ namespace tut
|
|||
" else:\n"
|
||||
" raise AssertionError('Too many data items')\n";
|
||||
|
||||
// Create an llsdXXXXXX file containing 'data' serialized to
|
||||
// notation.
|
||||
// Create an llsdXXXXXX file containing 'data' serialized per
|
||||
// FormatterFunction.
|
||||
NamedTempFile file("llsd",
|
||||
// NamedTempFile's boost::function constructor
|
||||
// NamedTempFile's function constructor
|
||||
// takes a callable. To this callable it passes the
|
||||
// std::ostream with which it's writing the
|
||||
// NamedTempFile.
|
||||
|
|
@ -1954,34 +1967,50 @@ namespace tut
|
|||
(std::ostream& out)
|
||||
{ writeLLSDArray(serialize, out, cdata); });
|
||||
|
||||
python("read C++ " + desc,
|
||||
placeholders::arg1 <<
|
||||
import_llsd <<
|
||||
"from functools import partial\n"
|
||||
"import io\n"
|
||||
"import struct\n"
|
||||
"lenformat = struct.Struct('i')\n"
|
||||
"def parse_each(inf):\n"
|
||||
" for rawlen in iter(partial(inf.read, lenformat.size), b''):\n"
|
||||
" len = lenformat.unpack(rawlen)[0]\n"
|
||||
// Since llsd.parse() has no max_bytes argument, instead of
|
||||
// passing the input stream directly to parse(), read the item
|
||||
// into a distinct bytes object and parse that.
|
||||
" data = inf.read(len)\n"
|
||||
" try:\n"
|
||||
" frombytes = llsd.parse(data)\n"
|
||||
" except llsd.LLSDParseError as err:\n"
|
||||
" print(f'*** {err}')\n"
|
||||
" print(f'Bad content:\\n{data!r}')\n"
|
||||
" raise\n"
|
||||
// Also try parsing from a distinct stream.
|
||||
" stream = io.BytesIO(data)\n"
|
||||
" fromstream = llsd.parse(stream)\n"
|
||||
" assert frombytes == fromstream\n"
|
||||
" yield frombytes\n"
|
||||
<< pydata <<
|
||||
// Don't forget raw-string syntax for Windows pathnames.
|
||||
"verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n");
|
||||
// 'debug' starts empty because it's intended as an output file
|
||||
NamedTempFile debug("debug", "");
|
||||
|
||||
try
|
||||
{
|
||||
python("read C++ " + desc,
|
||||
[&](std::ostream& out){ out <<
|
||||
import_llsd <<
|
||||
"from functools import partial\n"
|
||||
"import io\n"
|
||||
"import struct\n"
|
||||
"lenformat = struct.Struct('i')\n"
|
||||
"def parse_each(inf):\n"
|
||||
" for rawlen in iter(partial(inf.read, lenformat.size), b''):\n"
|
||||
" print('Read length:', ''.join(('%02x' % b) for b in rawlen),\n"
|
||||
" file=debug)\n"
|
||||
" len = lenformat.unpack(rawlen)[0]\n"
|
||||
// Since llsd.parse() has no max_bytes argument, instead of
|
||||
// passing the input stream directly to parse(), read the item
|
||||
// into a distinct bytes object and parse that.
|
||||
" data = inf.read(len)\n"
|
||||
" print('Read data: ', repr(data), file=debug)\n"
|
||||
" try:\n"
|
||||
" frombytes = llsd.parse(data)\n"
|
||||
" except llsd.LLSDParseError as err:\n"
|
||||
" print(f'*** {err}')\n"
|
||||
" print(f'Bad content:\\n{data!r}')\n"
|
||||
" raise\n"
|
||||
// Also try parsing from a distinct stream.
|
||||
" stream = io.BytesIO(data)\n"
|
||||
" fromstream = llsd.parse(stream)\n"
|
||||
" assert frombytes == fromstream\n"
|
||||
" yield frombytes\n"
|
||||
<< pydata <<
|
||||
// Don't forget raw-string syntax for Windows pathnames.
|
||||
"debug = open(r'" << debug.getName() << "', 'w')\n"
|
||||
"verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n";});
|
||||
}
|
||||
catch (const failure&)
|
||||
{
|
||||
LL_DEBUGS() << "Script debug output:" << LL_ENDL;
|
||||
debug.peep_log();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
|
|
@ -2068,7 +2097,7 @@ namespace tut
|
|||
NamedTempFile file("llsd", "");
|
||||
|
||||
python("Python " + pyformatter,
|
||||
placeholders::arg1 <<
|
||||
[&](std::ostream& out){ out <<
|
||||
import_llsd <<
|
||||
"import struct\n"
|
||||
"lenformat = struct.Struct('i')\n"
|
||||
|
|
@ -2086,7 +2115,7 @@ namespace tut
|
|||
" for item in DATA:\n"
|
||||
" serialized = llsd." << pyformatter << "(item)\n"
|
||||
" f.write(lenformat.pack(len(serialized)))\n"
|
||||
" f.write(serialized)\n");
|
||||
" f.write(serialized)\n";});
|
||||
|
||||
std::ifstream inf(file.getName().c_str());
|
||||
LLSD item;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,11 @@ namespace tut
|
|||
// signal the work item that it can quit; consider LLOneShotCond.
|
||||
LLCond<Shared> data;
|
||||
auto start = WorkSchedule::TimePoint::clock::now();
|
||||
auto interval = 100ms;
|
||||
// 2s seems like a long time to wait, since it directly impacts the
|
||||
// duration of this test program. Unfortunately GitHub's Mac runners
|
||||
// are pretty wimpy, and we're getting spurious "too late" errors just
|
||||
// because the thread doesn't wake up as soon as we want.
|
||||
auto interval = 2s;
|
||||
queue.postEvery(
|
||||
interval,
|
||||
[&data, count = 0]
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "llsdserialize.h"
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
//=========================================================================
|
||||
namespace
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "lltrace.h"
|
||||
#include "llfasttimer.h"
|
||||
#include "v3colorutil.h"
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "llfasttimer.h"
|
||||
#include "v3colorutil.h"
|
||||
#include "indra_constants.h"
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
const std::string LLSettingsWater::SETTING_BLUR_MULTIPLIER("blur_multiplier");
|
||||
const std::string LLSettingsWater::SETTING_FOG_COLOR("water_fog_color");
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import os
|
|||
import sys
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
from llsd.fastest_elementtree import parse as xml_parse
|
||||
import llsd
|
||||
from testrunner import freeport, run, debug, VERBOSE
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -14393,7 +14393,7 @@ Change of this parameter will affect the layout of buttons in notification toast
|
|||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string>https://jira.firestormviewer.org/secure/CreateIssueDetails!init.jspa?pid=10003&issuetype=8&priority=3&environment=[ENVIRONMENT]</string>
|
||||
<string>https://phoenixviewer.com/app/file-a-jira/?environment=[ENVIRONMENT]</string>
|
||||
</map>
|
||||
<key>RevokePermsOnStopAnimation</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -3783,8 +3783,10 @@ LLSD LLAppViewer::getViewerInfo() const
|
|||
// LLFloaterAbout.
|
||||
LLSD info;
|
||||
auto& versionInfo(LLVersionInfo::instance());
|
||||
// With GitHub builds, the build number is too big to fit in a 32-bit int,
|
||||
// and LLSD doesn't deal with integers wider than int. Use string.
|
||||
info["VIEWER_VERSION"] = llsd::array(versionInfo.getMajor(), versionInfo.getMinor(),
|
||||
versionInfo.getPatch(), versionInfo.getBuild());
|
||||
versionInfo.getPatch(), stringize(versionInfo.getBuild()));
|
||||
info["VIEWER_VERSION_STR"] = versionInfo.getVersion();
|
||||
info["BUILD_DATE"] = __DATE__;
|
||||
info["BUILD_TIME"] = __TIME__;
|
||||
|
|
@ -4291,7 +4293,7 @@ void LLAppViewer::writeSystemInfo()
|
|||
gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor();
|
||||
gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor();
|
||||
gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch();
|
||||
gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild();
|
||||
gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild());
|
||||
gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize();
|
||||
|
||||
// <FS:ND> Add which flavor of FS generated an error
|
||||
|
|
@ -6433,7 +6435,7 @@ void LLAppViewer::handleLoginComplete()
|
|||
gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor();
|
||||
gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor();
|
||||
gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch();
|
||||
gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild();
|
||||
gDebugInfo["ClientInfo"]["BuildVersion"] = std::to_string(LLVersionInfo::instance().getBuild());
|
||||
|
||||
// <FS:ND> Add which flavor of FS generated an error
|
||||
#ifdef OPENSIM
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "llxmlrpctransaction.h"
|
||||
#include "llviewernetwork.h"
|
||||
#include "llpanel.h"
|
||||
#include "stringize.h"
|
||||
|
||||
|
||||
const F64 CURRENCY_ESTIMATE_FREQUENCY = 2.0;
|
||||
|
|
@ -158,7 +159,7 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
|
|||
mLocalCurrencyEstimated = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
|
||||
keywordArgs.appendString("agentId", gAgent.getID().asString());
|
||||
keywordArgs.appendString(
|
||||
|
|
@ -170,8 +171,10 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
|
|||
keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
|
||||
keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
|
||||
keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
|
||||
keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild());
|
||||
|
||||
// With GitHub builds, the build number is too big to fit in a 32-bit int,
|
||||
// and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
|
||||
keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
|
||||
|
||||
LLXMLRPCValue params = LLXMLRPCValue::createArray();
|
||||
params.append(keywordArgs);
|
||||
|
||||
|
|
@ -245,7 +248,9 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
|
|||
keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
|
||||
keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
|
||||
keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
|
||||
keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild());
|
||||
// With GitHub builds, the build number is too big to fit in a 32-bit int,
|
||||
// and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
|
||||
keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
|
||||
|
||||
LLXMLRPCValue params = LLXMLRPCValue::createArray();
|
||||
params.append(keywordArgs);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class LLViewerCamera;
|
||||
class LLParcel;
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@
|
|||
#include "llgiveinventory.h"
|
||||
#include "llinventoryfunctions.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
const static std::string ADHOC_NAME_SUFFIX(" Conference");
|
||||
|
||||
const static std::string NEARBY_P2P_BY_OTHER("nearby_P2P_by_other");
|
||||
|
|
|
|||
|
|
@ -2426,6 +2426,8 @@ void LLInventoryGallery::startDrag()
|
|||
ids.push_back(selected_id);
|
||||
}
|
||||
}
|
||||
// We must have set this for some reason, but it's causing compile errors
|
||||
(void)src;
|
||||
LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1037,10 +1037,12 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br
|
|||
// params.tool_tip = params.name; // <ND/> Don't bother with tooltips in inventory
|
||||
params.allow_drop = allow_drop;
|
||||
|
||||
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
|
||||
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
|
||||
|
||||
// <FS:Ansariel> Inventory specials
|
||||
//params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
|
||||
//params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
|
||||
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
|
||||
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
|
||||
|
||||
params.for_inventory = true;
|
||||
|
||||
static LLCachedControl<S32> fsFolderViewItemHeight(*LLUI::getInstance()->mSettingGroups["config"], "FSFolderViewItemHeight");
|
||||
|
|
@ -1063,10 +1065,12 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge
|
|||
params.rect = LLRect (0, 0, 0, 0);
|
||||
// params.tool_tip = params.name; // <ND/> Don't bother with tooltips in inventory
|
||||
|
||||
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
|
||||
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
|
||||
|
||||
// <FS:Ansariel> Inventory specials
|
||||
//params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
|
||||
//params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
|
||||
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
|
||||
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
|
||||
|
||||
params.for_inventory = true;
|
||||
|
||||
static LLCachedControl<S32> fsFolderViewItemHeight(*LLUI::getInstance()->mSettingGroups["config"], "FSFolderViewItemHeight");
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/regex/v4/match_results.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#if LL_MSVC
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
#include "lltrans.h"
|
||||
#include "llglheaders.h"
|
||||
#include "llpanelloginlistener.h"
|
||||
#include "stringize.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#pragma warning(disable: 4355) // 'this' used in initializer list
|
||||
|
|
@ -302,10 +303,9 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
|
|||
setDefaultBtn(def_btn);
|
||||
|
||||
std::string channel = LLVersionInfo::instance().getChannel();
|
||||
std::string version = llformat("%s (%d)",
|
||||
LLVersionInfo::instance().getShortVersion().c_str(),
|
||||
LLVersionInfo::instance().getBuild());
|
||||
|
||||
std::string version = stringize(LLVersionInfo::instance().getShortVersion(), " (",
|
||||
LLVersionInfo::instance().getBuild(), ')');
|
||||
|
||||
LLTextBox* forgot_password_text = getChild<LLTextBox>("forgot_password_text");
|
||||
forgot_password_text->setClickedCallback(onClickForgotPassword, NULL);
|
||||
|
||||
|
|
@ -896,9 +896,8 @@ void LLPanelLogin::loadLoginPage()
|
|||
}
|
||||
|
||||
// Channel and Version
|
||||
params["version"] = llformat("%s (%d)",
|
||||
LLVersionInfo::instance().getShortVersion().c_str(),
|
||||
LLVersionInfo::instance().getBuild());
|
||||
params["version"] = stringize(LLVersionInfo::instance().getShortVersion(), " (",
|
||||
LLVersionInfo::instance().getBuild(), ')');
|
||||
params["channel"] = LLVersionInfo::instance().getChannel();
|
||||
|
||||
// Grid
|
||||
|
|
|
|||
|
|
@ -260,7 +260,10 @@ BOOL LLSidepanelInventory::postBuild()
|
|||
//gSavedSettings.getControl("InventoryDisplayInbox")->getCommitSignal()->connect(boost::bind(&handleInventoryDisplayInboxChanged));
|
||||
|
||||
LLFloater *floater = dynamic_cast<LLFloater*>(getParent());
|
||||
if (floater && floater->getKey().isUndefined() && !sLoginCompleted)
|
||||
// <FS:Ansariel> Secondary inventory window
|
||||
//if (floater && floater->getKey().isUndefined() && !sLoginCompleted)
|
||||
if (floater && ((floater->getKey().isUndefined() && !sLoginCompleted) || (floater->getKey().has("is_secondary") && floater->getKey()["is_secondary"].asBoolean())))
|
||||
// </FS:Ansariel>
|
||||
{
|
||||
// Prefill inventory for primary inventory floater
|
||||
// Other floaters should fill on visibility change
|
||||
|
|
@ -702,3 +705,11 @@ void LLSidepanelInventory::onReloadInboxClicked()
|
|||
}
|
||||
}
|
||||
// </FS:Zi>
|
||||
|
||||
// <FS:Ansariel> Secondary inventory window
|
||||
//static
|
||||
LLFloater* LLSidepanelInventory::createSecondaryInventoryWindow(const LLSD& key)
|
||||
{
|
||||
return LLFloaterReg::build<LLFloaterSidePanelContainer>(LLSD().with("is_secondary", true));
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class LLLayoutPanel;
|
|||
class LLPanelMainInventory;
|
||||
class LLSidepanelItemInfo;
|
||||
class LLSidepanelTaskInfo;
|
||||
class LLFloater; // <FS:Ansariel> Secondary inventory window
|
||||
|
||||
class LLSidepanelInventory : public LLPanel
|
||||
{
|
||||
|
|
@ -87,6 +88,9 @@ public:
|
|||
|
||||
static void cleanup();
|
||||
|
||||
// <FS:Ansariel> Secondary inventory window
|
||||
static LLFloater* createSecondaryInventoryWindow(const LLSD& key);
|
||||
|
||||
protected:
|
||||
// Tracks highlighted (selected) item in inventory panel.
|
||||
LLInventoryItem *getSelectedItem();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "json/reader.h"
|
||||
#include "llcorehttputil.h"
|
||||
#include "llurlregistry.h"
|
||||
#include "stringize.h"
|
||||
|
||||
|
||||
static const std::string AZURE_NOTRANSLATE_OPENING_TAG("<div translate=\"no\">");
|
||||
|
|
@ -160,12 +161,12 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, LLSD
|
|||
LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders);
|
||||
|
||||
|
||||
std::string user_agent = llformat("%s %d.%d.%d (%d)",
|
||||
LLVersionInfo::instance().getChannel().c_str(),
|
||||
LLVersionInfo::instance().getMajor(),
|
||||
LLVersionInfo::instance().getMinor(),
|
||||
LLVersionInfo::instance().getPatch(),
|
||||
LLVersionInfo::instance().getBuild());
|
||||
std::string user_agent = stringize(
|
||||
LLVersionInfo::instance().getChannel(), ' ',
|
||||
LLVersionInfo::instance().getMajor(), '.',
|
||||
LLVersionInfo::instance().getMinor(), '.',
|
||||
LLVersionInfo::instance().getPatch(), " (",
|
||||
LLVersionInfo::instance().getBuild(), ')');
|
||||
|
||||
initHttpHeader(httpHeaders, user_agent, key);
|
||||
|
||||
|
|
@ -215,12 +216,12 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s
|
|||
LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders);
|
||||
|
||||
|
||||
std::string user_agent = llformat("%s %d.%d.%d (%d)",
|
||||
LLVersionInfo::instance().getChannel().c_str(),
|
||||
LLVersionInfo::instance().getMajor(),
|
||||
LLVersionInfo::instance().getMinor(),
|
||||
LLVersionInfo::instance().getPatch(),
|
||||
LLVersionInfo::instance().getBuild());
|
||||
std::string user_agent = stringize(
|
||||
LLVersionInfo::instance().getChannel(), ' ',
|
||||
LLVersionInfo::instance().getMajor(), '.',
|
||||
LLVersionInfo::instance().getMinor(), '.',
|
||||
LLVersionInfo::instance().getPatch(), " (",
|
||||
LLVersionInfo::instance().getBuild(), ')');
|
||||
|
||||
initHttpHeader(httpHeaders, user_agent);
|
||||
httpOpts->setSSLVerifyPeer(false);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ void LLVersionInfo::initSingleton()
|
|||
// fully constructed; such calls don't really belong in the constructor.
|
||||
|
||||
// cache the version string
|
||||
version = STRINGIZE(getShortVersion() << "." << getBuild());
|
||||
version = stringize(getShortVersion(), ".", getBuild());
|
||||
}
|
||||
|
||||
LLVersionInfo::~LLVersionInfo()
|
||||
|
|
@ -95,7 +95,7 @@ S32 LLVersionInfo::getPatch()
|
|||
return LL_VIEWER_VERSION_PATCH;
|
||||
}
|
||||
|
||||
S32 LLVersionInfo::getBuild()
|
||||
U64 LLVersionInfo::getBuild()
|
||||
{
|
||||
return LL_VIEWER_VERSION_BUILD;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
S32 getPatch();
|
||||
|
||||
/// return the build number as an integer
|
||||
S32 getBuild();
|
||||
U64 getBuild();
|
||||
|
||||
/// return the full viewer version as a string like "2.0.0.200030"
|
||||
std::string getVersion();
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@
|
|||
#include "llfloaterscriptrecover.h"
|
||||
#include "llfloatersearchreplace.h"
|
||||
#include "llpanelgroup.h"
|
||||
#include "llsidepanelinventory.h"
|
||||
#include "NACLfloaterexploresounds.h"
|
||||
#include "particleeditor.h"
|
||||
#include "quickprefs.h"
|
||||
|
|
@ -632,10 +633,10 @@ void LLViewerFloaterReg::registerFloaters()
|
|||
LLFloaterReg::add("quickprefs", "floater_quickprefs.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FloaterQuickPrefs>);
|
||||
LLFloaterReg::add("region_tracker", "floater_region_tracker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterRegionTracker>);
|
||||
LLFloaterReg::add("search_replace", "floater_search_replace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearchReplace>);
|
||||
LLFloaterReg::add("secondary_inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
|
||||
LLFloaterReg::add("secondary_inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLSidepanelInventory::createSecondaryInventoryWindow);
|
||||
LLFloaterReg::add("script_recover", "floater_script_recover.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptRecover>);
|
||||
LLFloaterReg::add("sound_explorer", "floater_NACL_explore_sounds.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<NACLFloaterExploreSounds>);
|
||||
LLFloaterReg::add("vram_usage", "floater_fs_vram_usage.xml", static_cast<LLFloaterBuildFunc>( &LLFloaterReg::build< FSFloaterVRAMUsage >) );
|
||||
LLFloaterReg::add("vram_usage", "floater_fs_vram_usage.xml", static_cast<LLFloaterBuildFunc>(&LLFloaterReg::build<FSFloaterVRAMUsage>));
|
||||
LLFloaterReg::add("local_mesh_floater", "floater_vj_local_mesh.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLocalMesh>); // local mesh
|
||||
|
||||
LLFloaterReg::registerControlVariables(); // Make sure visibility and rect controls get preserved when saving
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url,
|
|||
substitution["VERSION_MAJOR"] = LLVersionInfo::instance().getMajor();
|
||||
substitution["VERSION_MINOR"] = LLVersionInfo::instance().getMinor();
|
||||
substitution["VERSION_PATCH"] = LLVersionInfo::instance().getPatch();
|
||||
substitution["VERSION_BUILD"] = LLVersionInfo::instance().getBuild();
|
||||
substitution["VERSION_BUILD"] = std::to_string(LLVersionInfo::instance().getBuild());
|
||||
substitution["CHANNEL"] = LLVersionInfo::instance().getChannel();
|
||||
substitution["GRID"] = LLGridManager::getInstance()->getGridId();
|
||||
substitution["GRID_LOWERCASE"] = utf8str_tolower(LLGridManager::getInstance()->getGridId());
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "bufferarray.h"
|
||||
#include "llversioninfo.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "stringize.h"
|
||||
|
||||
// Have to include these last to avoid queue redefinition!
|
||||
|
||||
|
|
@ -405,14 +406,14 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const
|
|||
|
||||
httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML);
|
||||
|
||||
std::string user_agent = llformat("%s %d.%d.%d (%d)",
|
||||
LLVersionInfo::instance().getChannel().c_str(),
|
||||
LLVersionInfo::instance().getMajor(),
|
||||
LLVersionInfo::instance().getMinor(),
|
||||
LLVersionInfo::instance().getPatch(),
|
||||
LLVersionInfo::instance().getBuild());
|
||||
std::string user_agent = stringize(
|
||||
LLVersionInfo::instance().getChannel(), ' ',
|
||||
LLVersionInfo::instance().getMajor(), '.',
|
||||
LLVersionInfo::instance().getMinor(), '.',
|
||||
LLVersionInfo::instance().getPatch(), " (",
|
||||
LLVersionInfo::instance().getBuild(), ')');
|
||||
|
||||
httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent);
|
||||
httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent);
|
||||
|
||||
///* Setting the DNS cache timeout to -1 disables it completely.
|
||||
//This might help with bug #503 */
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<panel
|
||||
default_tab_group="1"
|
||||
follows="all"
|
||||
height="423"
|
||||
label="Things"
|
||||
layout="topleft"
|
||||
min_height="300"
|
||||
min_width="240"
|
||||
name="main inventory panel"
|
||||
width="330">
|
||||
name="main inventory panel">
|
||||
<panel.string
|
||||
name="Itemcount">
|
||||
</panel.string>
|
||||
|
|
@ -24,150 +22,336 @@
|
|||
name="ItemcountUnknown">
|
||||
Fetched [ITEM_COUNT] Elements [FILTER]
|
||||
</panel.string>
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
follows="left|top|right"
|
||||
height="13"
|
||||
layout="topleft"
|
||||
left="12"
|
||||
name="ItemcountText"
|
||||
font="SansSerifMedium"
|
||||
text_color="EmphasisColor"
|
||||
tool_tip="[ITEMS] Items, [CATEGORIES] Folders"
|
||||
use_ellipses="true"
|
||||
top_pad="0"
|
||||
width="300">
|
||||
Elements:
|
||||
</text>
|
||||
<combo_box
|
||||
name="filter_combo_box"
|
||||
width="80"
|
||||
height="21"
|
||||
top="18"
|
||||
left="10"
|
||||
follows="top|left"
|
||||
layout="topleft">
|
||||
|
||||
<combo_box.item value="filter_type_all" label="All Types" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_animations" label="Animations" />
|
||||
<combo_box.item value="filter_type_calling_cards" label="Calling Cards" />
|
||||
<combo_box.item value="filter_type_clothing" label="Clothing / Body Parts" />
|
||||
<combo_box.item value="filter_type_gestures" label="Gestures" />
|
||||
<combo_box.item value="filter_type_landmarks" label="Landmarks" />
|
||||
<combo_box.item value="filter_type_notecards" label="Notecards" />
|
||||
<combo_box.item value="filter_type_objects" label="Objects" />
|
||||
<combo_box.item value="filter_type_scripts" label="Scripts" />
|
||||
<combo_box.item value="filter_type_sounds" label="Sounds" />
|
||||
<combo_box.item value="filter_type_textures" label="Textures" />
|
||||
<combo_box.item value="filter_type_snapshots" label="Snapshots" />
|
||||
<combo_box.item value="filter_type_meshes" label="Meshes" />
|
||||
<combo_box.item value="filter_type_settings" label="Settings" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_custom" label="Custom..." />
|
||||
|
||||
</combo_box>
|
||||
<menu_button
|
||||
follows="top|left"
|
||||
tool_tip="Show search visibility options"
|
||||
height="21"
|
||||
image_overlay="Inv_Toolbar_SearchVisibility"
|
||||
layout="topleft"
|
||||
left_pad="3"
|
||||
name="options_visibility_btn"
|
||||
width="31" />
|
||||
<filter_editor
|
||||
text_pad_left="10"
|
||||
<panel.string name="inventory_title">Inventory</panel.string>
|
||||
<panel.string name="default_mode_btn">Multi_Folder_Mode</panel.string>
|
||||
<panel.string name="single_folder_mode_btn">Single_Folder_Mode</panel.string>
|
||||
|
||||
<layout_stack
|
||||
follows="left|top|right"
|
||||
height="23"
|
||||
label="Filter Inventory"
|
||||
height="25"
|
||||
animate="false"
|
||||
top_pad="4"
|
||||
left="10"
|
||||
name="nav_stack"
|
||||
orientation="horizontal">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
auto_resize="false"
|
||||
height="25"
|
||||
width="62"
|
||||
name="nav_buttons"
|
||||
visible="false">
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Back"
|
||||
image_pressed="Single_Folder_Back"
|
||||
image_unselected="Single_Folder_Back"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
top="2"
|
||||
name="back_btn"
|
||||
tool_tip="Back"
|
||||
width="20" />
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Forward"
|
||||
image_pressed="Single_Folder_Forward"
|
||||
image_unselected="Single_Folder_Forward"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="forward_btn"
|
||||
tool_tip="Forward"
|
||||
width="20" />
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Up"
|
||||
image_pressed="Single_Folder_Up"
|
||||
image_unselected="Single_Folder_Up"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="up_btn"
|
||||
tool_tip="Go up one level"
|
||||
width="20" />
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
height="25"
|
||||
width="324"
|
||||
name="filter_panel"
|
||||
visible="true">
|
||||
<filter_editor
|
||||
text_pad_left="10"
|
||||
follows="left|top|right"
|
||||
height="23"
|
||||
label="Filter Inventory"
|
||||
layout="topleft"
|
||||
left="2"
|
||||
max_length_chars="300"
|
||||
highlight_text_field="true"
|
||||
name="inventory search editor"
|
||||
tool_tip="Type in one or more words to search for, separated by '+' "
|
||||
top="0"
|
||||
width="322" />
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
|
||||
<layout_stack
|
||||
follows="left|top|right"
|
||||
height="16"
|
||||
animate="false"
|
||||
top_pad="4"
|
||||
left="4"
|
||||
name="ctrl_stack"
|
||||
orientation="horizontal">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
auto_resize="false"
|
||||
height="16"
|
||||
width="168"
|
||||
name="collapse_expand_buttons">
|
||||
<button
|
||||
name="collapse_btn"
|
||||
label="Collapse"
|
||||
width="80"
|
||||
height="16"
|
||||
left="0"
|
||||
top="0"
|
||||
follows="top|left"
|
||||
layout="topleft"/>
|
||||
|
||||
<button
|
||||
name="expand_btn"
|
||||
label="Expand"
|
||||
width="80"
|
||||
height="16"
|
||||
left_pad="4"
|
||||
follows="top|left"
|
||||
layout="topleft" />
|
||||
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
height="16"
|
||||
width="164"
|
||||
name="filter_ctrls">
|
||||
|
||||
<text
|
||||
name="filter_label"
|
||||
value="Filter:"
|
||||
width="40"
|
||||
height="16"
|
||||
left="0"
|
||||
top="0"
|
||||
follows="top|left"
|
||||
layout="topleft" />
|
||||
|
||||
<combo_box
|
||||
name="filter_combo_box"
|
||||
right="-35"
|
||||
height="16"
|
||||
left_pad="4"
|
||||
follows="top|left|right"
|
||||
layout="topleft">
|
||||
<combo_box.item value="filter_type_all" label="All Types" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_animations" label="Animations" />
|
||||
<combo_box.item value="filter_type_calling_cards" label="Calling Cards" />
|
||||
<combo_box.item value="filter_type_clothing" label="Clothing / Body Parts" />
|
||||
<combo_box.item value="filter_type_gestures" label="Gestures" />
|
||||
<combo_box.item value="filter_type_landmarks" label="Landmarks" />
|
||||
<combo_box.item value="filter_type_notecards" label="Notecards" />
|
||||
<combo_box.item value="filter_type_objects" label="Objects" />
|
||||
<combo_box.item value="filter_type_scripts" label="Scripts" />
|
||||
<combo_box.item value="filter_type_sounds" label="Sounds" />
|
||||
<combo_box.item value="filter_type_textures" label="Textures" />
|
||||
<combo_box.item value="filter_type_snapshots" label="Snapshots" />
|
||||
<combo_box.item value="filter_type_meshes" label="Meshes" />
|
||||
<combo_box.item value="filter_type_settings" label="Settings" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_custom" label="Custom..." />
|
||||
</combo_box>
|
||||
|
||||
<menu_button
|
||||
follows="top|right"
|
||||
tool_tip="Show search visibility options"
|
||||
height="16"
|
||||
image_overlay="Inv_Toolbar_SearchVisibility"
|
||||
layout="topleft"
|
||||
right="-1"
|
||||
top_delta="0"
|
||||
name="options_visibility_btn"
|
||||
width="31" />
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="338"
|
||||
layout="topleft"
|
||||
left_pad="3"
|
||||
max_length_chars="300"
|
||||
highlight_text_field="true"
|
||||
name="inventory search editor"
|
||||
tool_tip="Type in one or more words to search for, separated by '+' "
|
||||
top="18"
|
||||
width="185" />
|
||||
<tab_container
|
||||
bg_alpha_color="Transparent"
|
||||
bg_opaque_color="Transparent"
|
||||
background_visible="true"
|
||||
background_opaque="false"
|
||||
left="3"
|
||||
right="-3"
|
||||
name="default_inventory_panel"
|
||||
top_pad="4">
|
||||
<tab_container
|
||||
bg_alpha_color="Transparent"
|
||||
bg_opaque_color="Transparent"
|
||||
background_visible="false"
|
||||
background_opaque="false"
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="338"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="inventory filter tabs"
|
||||
open_tabs_on_drag_and_drop="true"
|
||||
tab_position="top"
|
||||
top="0">
|
||||
<inventory_panel
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
label="Inventory"
|
||||
help_topic="my_inventory_tab"
|
||||
layout="topleft"
|
||||
name="All Items"
|
||||
sort_order_setting="InventorySortOrder"
|
||||
show_item_link_overlays="true"
|
||||
preinitialize_views="false"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<folder double_click_override="true"/>
|
||||
</inventory_panel>
|
||||
<recent_inventory_panel
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
label="Recent"
|
||||
sort_order_setting="RecentItemsSortOrder"
|
||||
help_topic="recent_inventory_tab"
|
||||
layout="topleft"
|
||||
name="Recent Items"
|
||||
show_item_link_overlays="true"
|
||||
preinitialize_views="false"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<folder double_click_override="true"/>
|
||||
</recent_inventory_panel>
|
||||
<worn_inventory_panel
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
label="Worn"
|
||||
help_topic="worn_inventory_tab"
|
||||
layout="topleft"
|
||||
name="Worn Items"
|
||||
show_item_link_overlays="true"
|
||||
preinitialize_views="false">
|
||||
<folder double_click_override="true"/>
|
||||
</worn_inventory_panel>
|
||||
</tab_container>
|
||||
</panel>
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="339"
|
||||
layout="topleft"
|
||||
left="7"
|
||||
name="inventory filter tabs"
|
||||
open_tabs_on_drag_and_drop="true"
|
||||
tab_height="23"
|
||||
tab_group="1"
|
||||
tab_position="top"
|
||||
tab_min_width="100"
|
||||
top_pad="10"
|
||||
width="312">
|
||||
<inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
label="Inventory"
|
||||
help_topic="my_inventory_tab"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="All Items"
|
||||
sort_order_setting="InventorySortOrder"
|
||||
show_item_link_overlays="true"
|
||||
top="16"
|
||||
width="288" >
|
||||
<folder double_click_override="true"/>
|
||||
</inventory_panel>
|
||||
<recent_inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
label="Recent"
|
||||
sort_order_setting="RecentItemsSortOrder"
|
||||
help_topic="recent_inventory_tab"
|
||||
layout="topleft"
|
||||
left_delta="0"
|
||||
name="Recent Items"
|
||||
show_item_link_overlays="true"
|
||||
width="290" >
|
||||
<folder double_click_override="true"/>
|
||||
</recent_inventory_panel>
|
||||
<worn_inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
label="Worn"
|
||||
help_topic="worn_inventory_tab"
|
||||
layout="topleft"
|
||||
left_delta="0"
|
||||
name="Worn Items"
|
||||
show_item_link_overlays="true"
|
||||
width="290" >
|
||||
<folder double_click_override="true"/>
|
||||
</worn_inventory_panel>
|
||||
</tab_container>
|
||||
left="4"
|
||||
name="combination_view_inventory"
|
||||
top_delta="0"
|
||||
visible="false">
|
||||
<layout_stack
|
||||
follows="all"
|
||||
height="338"
|
||||
name="combination_view_stack"
|
||||
animate="false"
|
||||
drag_handle_thickness="6"
|
||||
drag_handle_first_indent="18"
|
||||
drag_handle_second_indent="18"
|
||||
drag_handle_color="PanelGray"
|
||||
drag_handle_shift="5"
|
||||
show_drag_handle="true"
|
||||
top="0"
|
||||
left="0"
|
||||
orientation="vertical">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="228"
|
||||
min_width="150"
|
||||
name="comb_gallery_layout">
|
||||
<panel
|
||||
class="inventory_gallery"
|
||||
filename="panel_inventory_gallery.xml"
|
||||
left="0"
|
||||
top_pad="0"
|
||||
height="228"
|
||||
name="comb_gallery_view_inv"
|
||||
background_visible="true"
|
||||
follows="all"
|
||||
layout="topleft">
|
||||
</panel>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="110"
|
||||
name="comb_inventory_layout">
|
||||
<single_folder_inventory_panel
|
||||
name="comb_single_folder_inv"
|
||||
follows="all"
|
||||
left="0"
|
||||
top="0"
|
||||
height="110"
|
||||
layout="topleft"
|
||||
show_item_link_overlays="true"
|
||||
background_visible="true"
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<item
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
<folder
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
</single_folder_inventory_panel>
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
</panel>
|
||||
<panel
|
||||
follows="left|right|bottom"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
top_pad="0"
|
||||
left="10"
|
||||
bottom="-8"
|
||||
left="4"
|
||||
name="bottom_panel"
|
||||
width="307">
|
||||
right="-4">
|
||||
<panel
|
||||
follows="top|left"
|
||||
left="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
left="0"
|
||||
layout="topleft"
|
||||
name="options_gear_btn_panel"
|
||||
width="32">
|
||||
<menu_button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
tool_tip="Show additional options"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Left_Over"
|
||||
|
|
@ -181,14 +365,14 @@
|
|||
width="31" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
left_pad="0"
|
||||
name="add_btn_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_overlay="AddItem_Off"
|
||||
|
|
@ -201,15 +385,15 @@
|
|||
top="0"
|
||||
width="31" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
<panel
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
left_pad="0"
|
||||
name="new_inventory_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_overlay="inventory_18"
|
||||
|
|
@ -227,65 +411,66 @@
|
|||
</button>
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
name="collapse_panel"
|
||||
width="90">
|
||||
<button
|
||||
auto_resize="true"
|
||||
follows="bottom|left|right"
|
||||
left_pad="0"
|
||||
name="view_mode_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
label="Collapse All"
|
||||
image_overlay="Single_Folder_Mode"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="collapse_btn"
|
||||
tool_tip="Collapse Folders"
|
||||
name="view_mode_btn"
|
||||
tool_tip="Switch between views"
|
||||
top="0"
|
||||
width="89" />
|
||||
</panel>
|
||||
width="31"/>
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left|right"
|
||||
left_pad="0"
|
||||
follows="top|left|right"
|
||||
height="25"
|
||||
left_pad="0"
|
||||
right="-33"
|
||||
layout="topleft"
|
||||
name="expand_panel"
|
||||
width="90">
|
||||
<button
|
||||
auto_resize="true"
|
||||
follows="bottom|left|right"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
label="Expand All"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
name="dummy_panel">
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
height="13"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="expand_btn"
|
||||
tool_tip="Expand Folders"
|
||||
top="0"
|
||||
width="89" />
|
||||
<!-- icon
|
||||
left="8"
|
||||
name="ItemcountText"
|
||||
font="SansSerifMedium"
|
||||
top_pad="6"
|
||||
tool_tip="[ITEMS] Items, [CATEGORIES] Folders"
|
||||
use_ellipses="true"
|
||||
follows="bottom|left|right"
|
||||
right="-8">
|
||||
Elements
|
||||
</text>
|
||||
<icon
|
||||
follows="top|left|right"
|
||||
height="25"
|
||||
image_name="Toolbar_Middle_Off"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
left="0"
|
||||
top="0"
|
||||
name="dummy_icon"
|
||||
width="59" / -->
|
||||
right="-1"
|
||||
name="dummy_icon" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|right"
|
||||
left_pad="0"
|
||||
follows="top|right"
|
||||
height="25"
|
||||
left_pad="0"
|
||||
right="-1"
|
||||
layout="topleft"
|
||||
name="trash_btn_panel"
|
||||
width="31">
|
||||
width="32">
|
||||
<dnd_button
|
||||
follows="top|right"
|
||||
height="25"
|
||||
|
|
@ -299,6 +484,6 @@
|
|||
tool_tip="Remove selected item"
|
||||
top="0"
|
||||
width="31"/>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<panel
|
||||
follows="all"
|
||||
height="570"
|
||||
label="Things"
|
||||
layout="topleft"
|
||||
min_height="350"
|
||||
min_width="240"
|
||||
name="objects panel"
|
||||
width="333">
|
||||
<panel
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="sidepanel_inventory_panel"
|
||||
top="0"
|
||||
label=""
|
||||
height="570"
|
||||
visible="true"
|
||||
width="330">
|
||||
<layout_stack
|
||||
follows="left|right|top|bottom"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
top="0"
|
||||
orientation="vertical"
|
||||
name="inventory_layout_stack"
|
||||
height="565"
|
||||
width="330">
|
||||
<layout_panel
|
||||
name="main_inventory_layout_panel"
|
||||
layout="topleft"
|
||||
auto_resize="true"
|
||||
user_resize="true"
|
||||
min_dim="150"
|
||||
width="330"
|
||||
follows="bottom|left|right"
|
||||
height="300">
|
||||
<panel
|
||||
class="panel_main_inventory"
|
||||
filename="panel_main_inventory.xml"
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="panel_main_inventory"
|
||||
top="0"
|
||||
label=""
|
||||
height="300"
|
||||
width="330" />
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
layout="topleft"
|
||||
auto_resize="false"
|
||||
user_resize="true"
|
||||
follows="left|right|top"
|
||||
name="inbox_layout_panel"
|
||||
visible="false"
|
||||
min_dim="35"
|
||||
expanded_min_dim="90"
|
||||
height="235">
|
||||
<panel
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="marketplace_inbox"
|
||||
class="panel_marketplace_inbox"
|
||||
top="0"
|
||||
label=""
|
||||
height="235">
|
||||
<string name="InboxLabelWithArg">Received items ([NUM])</string>
|
||||
<string name="InboxLabelNoArg">Received items</string>
|
||||
<button
|
||||
control_name="InventoryInboxToggleState"
|
||||
label="Received items"
|
||||
font="SansSerifMedium"
|
||||
name="inbox_btn"
|
||||
height="35"
|
||||
image_unselected="MarketplaceBtn_Off"
|
||||
image_selected="MarketplaceBtn_Selected"
|
||||
halign="left"
|
||||
handle_right_mouse="false"
|
||||
follows="top|left|right"
|
||||
is_toggle="true"
|
||||
tab_stop="false"
|
||||
pad_left="35"
|
||||
top="0"
|
||||
left="5"
|
||||
right="-43" />
|
||||
<button
|
||||
name="reload_received_items_btn"
|
||||
height="35"
|
||||
width="35"
|
||||
left_pad="3"
|
||||
follows="top|right"
|
||||
image_overlay="Refresh_Off"
|
||||
tool_tip="Reload the received items list." />
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
follows="right|top"
|
||||
layout="topleft"
|
||||
height="13"
|
||||
top="10"
|
||||
right="-58"
|
||||
name="inbox_fresh_new_count"
|
||||
font="SansSerifMedium"
|
||||
halign="right"
|
||||
top_pad="0">
|
||||
[NUM] new
|
||||
</text>
|
||||
<panel
|
||||
name="inbox_inventory_placeholder_panel"
|
||||
follows="all"
|
||||
left="5"
|
||||
right="-5"
|
||||
top="35"
|
||||
height="200"
|
||||
bg_opaque_color="InventoryBackgroundColor"
|
||||
background_visible="true"
|
||||
background_opaque="true"
|
||||
tool_tip="Drag and drop items to your inventory to use them"
|
||||
>
|
||||
<text name="inbox_inventory_placeholder"
|
||||
type="string"
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
top="0"
|
||||
height="200"
|
||||
wrap="true"
|
||||
halign="center"
|
||||
valign="center">
|
||||
Purchases from the marketplace will be delivered here.
|
||||
</text>
|
||||
</panel>
|
||||
</panel>
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
</panel>
|
||||
</panel>
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<panel
|
||||
default_tab_group="1"
|
||||
follows="all"
|
||||
height="423"
|
||||
label="Things"
|
||||
layout="topleft"
|
||||
min_height="300"
|
||||
min_width="240"
|
||||
name="main inventory panel"
|
||||
bg_opaque_color="Transparent"
|
||||
bg_alpha_color="Transparent"
|
||||
width="330">
|
||||
name="main inventory panel">
|
||||
bg_opaque_color="Transparent"
|
||||
bg_alpha_color="Transparent"
|
||||
<panel.string
|
||||
name="Itemcount">
|
||||
</panel.string>
|
||||
|
|
@ -26,120 +24,234 @@
|
|||
name="ItemcountUnknown">
|
||||
Fetched [ITEM_COUNT] Elements [FILTER]
|
||||
</panel.string>
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
follows="left|top|right"
|
||||
height="13"
|
||||
layout="topleft"
|
||||
left="12"
|
||||
name="ItemcountText"
|
||||
font="SansSerifMedium"
|
||||
text_color="SL-EmphasisColor"
|
||||
tool_tip="[ITEMS] Items, [CATEGORIES] Folders"
|
||||
use_ellipses="true"
|
||||
top_pad="0"
|
||||
width="300">
|
||||
Elements:
|
||||
</text>
|
||||
<combo_box
|
||||
name="filter_combo_box"
|
||||
width="80"
|
||||
height="21"
|
||||
top="18"
|
||||
left="10"
|
||||
follows="top|left"
|
||||
layout="topleft">
|
||||
|
||||
<combo_box.item value="filter_type_all" label="All Types" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_animations" label="Animations" />
|
||||
<combo_box.item value="filter_type_calling_cards" label="Calling Cards" />
|
||||
<combo_box.item value="filter_type_clothing" label="Clothing / Body Parts" />
|
||||
<combo_box.item value="filter_type_gestures" label="Gestures" />
|
||||
<combo_box.item value="filter_type_landmarks" label="Landmarks" />
|
||||
<combo_box.item value="filter_type_notecards" label="Notecards" />
|
||||
<combo_box.item value="filter_type_objects" label="Objects" />
|
||||
<combo_box.item value="filter_type_scripts" label="Scripts" />
|
||||
<combo_box.item value="filter_type_sounds" label="Sounds" />
|
||||
<combo_box.item value="filter_type_textures" label="Textures" />
|
||||
<combo_box.item value="filter_type_snapshots" label="Snapshots" />
|
||||
<combo_box.item value="filter_type_meshes" label="Meshes" />
|
||||
<combo_box.item value="filter_type_settings" label="Settings" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_custom" label="Custom..." />
|
||||
|
||||
</combo_box>
|
||||
<menu_button
|
||||
follows="top|left"
|
||||
tool_tip="Show search visibility options"
|
||||
height="21"
|
||||
image_overlay="Inv_Toolbar_SearchVisibility"
|
||||
layout="topleft"
|
||||
left_pad="3"
|
||||
name="options_visibility_btn"
|
||||
width="31" />
|
||||
<filter_editor
|
||||
text_pad_left="10"
|
||||
<panel.string name="inventory_title">Inventory</panel.string>
|
||||
<panel.string name="default_mode_btn">Multi_Folder_Mode</panel.string>
|
||||
<panel.string name="single_folder_mode_btn">Single_Folder_Mode</panel.string>
|
||||
|
||||
<layout_stack
|
||||
follows="left|top|right"
|
||||
height="23"
|
||||
label="Filter Inventory"
|
||||
height="25"
|
||||
animate="false"
|
||||
top_pad="4"
|
||||
left="10"
|
||||
name="nav_stack"
|
||||
orientation="horizontal">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
auto_resize="false"
|
||||
height="25"
|
||||
width="62"
|
||||
name="nav_buttons"
|
||||
visible="false">
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Back"
|
||||
image_pressed="Single_Folder_Back"
|
||||
image_unselected="Single_Folder_Back"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
top="2"
|
||||
name="back_btn"
|
||||
tool_tip="Back"
|
||||
width="20" />
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Forward"
|
||||
image_pressed="Single_Folder_Forward"
|
||||
image_unselected="Single_Folder_Forward"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="forward_btn"
|
||||
tool_tip="Forward"
|
||||
width="20" />
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
image_selected="Single_Folder_Up"
|
||||
image_pressed="Single_Folder_Up"
|
||||
image_unselected="Single_Folder_Up"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="up_btn"
|
||||
tool_tip="Go up one level"
|
||||
width="20" />
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
height="25"
|
||||
width="324"
|
||||
name="filter_panel"
|
||||
visible="true">
|
||||
<filter_editor
|
||||
text_pad_left="10"
|
||||
follows="left|top|right"
|
||||
height="23"
|
||||
label="Filter Inventory"
|
||||
layout="topleft"
|
||||
left="2"
|
||||
max_length_chars="300"
|
||||
highlight_text_field="true"
|
||||
name="inventory search editor"
|
||||
tool_tip="Type in one or more words to search for, separated by '+' "
|
||||
top="0"
|
||||
width="322" />
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
|
||||
<layout_stack
|
||||
follows="left|top|right"
|
||||
height="16"
|
||||
animate="false"
|
||||
top_pad="4"
|
||||
left="4"
|
||||
name="ctrl_stack"
|
||||
orientation="horizontal">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
auto_resize="false"
|
||||
height="16"
|
||||
width="168"
|
||||
name="collapse_expand_buttons">
|
||||
<button
|
||||
name="collapse_btn"
|
||||
label="Collapse"
|
||||
width="80"
|
||||
height="16"
|
||||
left="0"
|
||||
top="0"
|
||||
follows="top|left"
|
||||
layout="topleft"/>
|
||||
|
||||
<button
|
||||
name="expand_btn"
|
||||
label="Expand"
|
||||
width="80"
|
||||
height="16"
|
||||
left_pad="4"
|
||||
follows="top|left"
|
||||
layout="topleft" />
|
||||
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
height="16"
|
||||
width="164"
|
||||
name="filter_ctrls">
|
||||
|
||||
<text
|
||||
name="filter_label"
|
||||
value="Filter:"
|
||||
width="40"
|
||||
height="16"
|
||||
left="0"
|
||||
top="0"
|
||||
follows="top|left"
|
||||
layout="topleft" />
|
||||
|
||||
<combo_box
|
||||
name="filter_combo_box"
|
||||
right="-35"
|
||||
height="16"
|
||||
left_pad="4"
|
||||
follows="top|left|right"
|
||||
layout="topleft">
|
||||
<combo_box.item value="filter_type_all" label="All Types" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_animations" label="Animations" />
|
||||
<combo_box.item value="filter_type_calling_cards" label="Calling Cards" />
|
||||
<combo_box.item value="filter_type_clothing" label="Clothing / Body Parts" />
|
||||
<combo_box.item value="filter_type_gestures" label="Gestures" />
|
||||
<combo_box.item value="filter_type_landmarks" label="Landmarks" />
|
||||
<combo_box.item value="filter_type_notecards" label="Notecards" />
|
||||
<combo_box.item value="filter_type_objects" label="Objects" />
|
||||
<combo_box.item value="filter_type_scripts" label="Scripts" />
|
||||
<combo_box.item value="filter_type_sounds" label="Sounds" />
|
||||
<combo_box.item value="filter_type_textures" label="Textures" />
|
||||
<combo_box.item value="filter_type_snapshots" label="Snapshots" />
|
||||
<combo_box.item value="filter_type_meshes" label="Meshes" />
|
||||
<combo_box.item value="filter_type_settings" label="Settings" />
|
||||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_custom" label="Custom..." />
|
||||
</combo_box>
|
||||
|
||||
<menu_button
|
||||
follows="top|right"
|
||||
tool_tip="Show search visibility options"
|
||||
height="16"
|
||||
image_overlay="Inv_Toolbar_SearchVisibility"
|
||||
layout="topleft"
|
||||
right="-1"
|
||||
top_delta="0"
|
||||
name="options_visibility_btn"
|
||||
width="31" />
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="338"
|
||||
layout="topleft"
|
||||
left_pad="3"
|
||||
max_length_chars="300"
|
||||
highlight_text_field="true"
|
||||
name="inventory search editor"
|
||||
tool_tip="Type in one or more words to search for, separated by '+' "
|
||||
top="18"
|
||||
width="185" />
|
||||
<tab_container
|
||||
bg_alpha_color="Transparent"
|
||||
bg_opaque_color="Transparent"
|
||||
background_visible="true"
|
||||
background_opaque="false"
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="339"
|
||||
layout="topleft"
|
||||
left="7"
|
||||
name="inventory filter tabs"
|
||||
open_tabs_on_drag_and_drop="true"
|
||||
tab_height="23"
|
||||
tab_group="1"
|
||||
tab_position="top"
|
||||
tab_min_width="100"
|
||||
top_pad="10"
|
||||
width="312">
|
||||
<inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
label="Inventory"
|
||||
help_topic="my_inventory_tab"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="All Items"
|
||||
sort_order_setting="InventorySortOrder"
|
||||
show_item_link_overlays="true"
|
||||
top="16"
|
||||
width="288" >
|
||||
<folder double_click_override="true"/>
|
||||
</inventory_panel>
|
||||
<recent_inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
label="Recent"
|
||||
sort_order_setting="RecentItemsSortOrder"
|
||||
help_topic="recent_inventory_tab"
|
||||
layout="topleft"
|
||||
left_delta="0"
|
||||
name="Recent Items"
|
||||
show_item_link_overlays="true"
|
||||
width="290" >
|
||||
<folder double_click_override="true"/>
|
||||
</recent_inventory_panel>
|
||||
<worn_inventory_panel
|
||||
left="3"
|
||||
right="-3"
|
||||
name="default_inventory_panel"
|
||||
top_pad="4">
|
||||
<tab_container
|
||||
bg_alpha_color="Transparent"
|
||||
bg_opaque_color="Transparent"
|
||||
background_visible="false"
|
||||
background_opaque="false"
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="338"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="inventory filter tabs"
|
||||
open_tabs_on_drag_and_drop="true"
|
||||
tab_position="top"
|
||||
top="0">
|
||||
<inventory_panel
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
label="Inventory"
|
||||
help_topic="my_inventory_tab"
|
||||
layout="topleft"
|
||||
name="All Items"
|
||||
sort_order_setting="InventorySortOrder"
|
||||
show_item_link_overlays="true"
|
||||
preinitialize_views="false"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<folder double_click_override="true"/>
|
||||
</inventory_panel>
|
||||
<recent_inventory_panel
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
label="Recent"
|
||||
sort_order_setting="RecentItemsSortOrder"
|
||||
help_topic="recent_inventory_tab"
|
||||
layout="topleft"
|
||||
name="Recent Items"
|
||||
show_item_link_overlays="true"
|
||||
preinitialize_views="false"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<folder double_click_override="true"/>
|
||||
</recent_inventory_panel>
|
||||
<worn_inventory_panel
|
||||
bevel_style="none"
|
||||
follows="all"
|
||||
height="338"
|
||||
|
|
@ -152,24 +264,97 @@
|
|||
width="290" >
|
||||
<folder double_click_override="true"/>
|
||||
</worn_inventory_panel>
|
||||
</tab_container>
|
||||
</tab_container>
|
||||
</panel>
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="338"
|
||||
layout="topleft"
|
||||
left="4"
|
||||
name="combination_view_inventory"
|
||||
top_delta="0"
|
||||
visible="false">
|
||||
<layout_stack
|
||||
follows="all"
|
||||
height="338"
|
||||
name="combination_view_stack"
|
||||
animate="false"
|
||||
drag_handle_thickness="6"
|
||||
drag_handle_first_indent="18"
|
||||
drag_handle_second_indent="18"
|
||||
drag_handle_color="PanelGray"
|
||||
drag_handle_shift="5"
|
||||
show_drag_handle="true"
|
||||
top="0"
|
||||
left="0"
|
||||
orientation="vertical">
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="228"
|
||||
min_width="150"
|
||||
name="comb_gallery_layout">
|
||||
<panel
|
||||
class="inventory_gallery"
|
||||
filename="panel_inventory_gallery.xml"
|
||||
left="0"
|
||||
top_pad="0"
|
||||
height="228"
|
||||
name="comb_gallery_view_inv"
|
||||
background_visible="true"
|
||||
follows="all"
|
||||
layout="topleft">
|
||||
</panel>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="110"
|
||||
name="comb_inventory_layout">
|
||||
<single_folder_inventory_panel
|
||||
name="comb_single_folder_inv"
|
||||
follows="all"
|
||||
left="0"
|
||||
top="0"
|
||||
height="110"
|
||||
layout="topleft"
|
||||
show_item_link_overlays="true"
|
||||
background_visible="true"
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
<item
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
<folder
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
</single_folder_inventory_panel>
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
</panel>
|
||||
<panel
|
||||
follows="left|right|bottom"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
top_pad="0"
|
||||
left="10"
|
||||
bottom="-8"
|
||||
left="4"
|
||||
name="bottom_panel"
|
||||
width="307">
|
||||
right="-4">
|
||||
<panel
|
||||
follows="top|left"
|
||||
left="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
left="0"
|
||||
layout="topleft"
|
||||
name="options_gear_btn_panel"
|
||||
width="32">
|
||||
<menu_button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
tool_tip="Show additional options"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Left_Over"
|
||||
|
|
@ -183,14 +368,14 @@
|
|||
width="31" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
left_pad="0"
|
||||
name="add_btn_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_overlay="AddItem_Off"
|
||||
|
|
@ -203,15 +388,15 @@
|
|||
top="0"
|
||||
width="31" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
<panel
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
left_pad="0"
|
||||
name="new_inventory_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="bottom|left"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_overlay="inventory_18"
|
||||
|
|
@ -229,65 +414,66 @@
|
|||
</button>
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left"
|
||||
left_pad="0"
|
||||
follows="top|left"
|
||||
height="25"
|
||||
layout="topleft"
|
||||
name="collapse_panel"
|
||||
width="90">
|
||||
<button
|
||||
auto_resize="true"
|
||||
follows="bottom|left|right"
|
||||
left_pad="0"
|
||||
name="view_mode_panel"
|
||||
width="32">
|
||||
<button
|
||||
follows="top|left"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
label="Collapse All"
|
||||
image_overlay="Single_Folder_Mode"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="collapse_btn"
|
||||
tool_tip="Collapse Folders"
|
||||
name="view_mode_btn"
|
||||
tool_tip="Switch between views"
|
||||
top="0"
|
||||
width="89" />
|
||||
</panel>
|
||||
width="31"/>
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|left|right"
|
||||
left_pad="0"
|
||||
follows="top|left|right"
|
||||
height="25"
|
||||
left_pad="0"
|
||||
right="-33"
|
||||
layout="topleft"
|
||||
name="expand_panel"
|
||||
width="90">
|
||||
<button
|
||||
auto_resize="true"
|
||||
follows="bottom|left|right"
|
||||
height="25"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
label="Expand All"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
name="dummy_panel">
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
height="13"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="expand_btn"
|
||||
tool_tip="Expand Folders"
|
||||
top="0"
|
||||
width="89" />
|
||||
<!-- icon
|
||||
left="8"
|
||||
name="ItemcountText"
|
||||
font="SansSerifMedium"
|
||||
top_pad="6"
|
||||
tool_tip="[ITEMS] Items, [CATEGORIES] Folders"
|
||||
use_ellipses="true"
|
||||
follows="bottom|left|right"
|
||||
right="-8">
|
||||
Elements
|
||||
</text>
|
||||
<icon
|
||||
follows="top|left|right"
|
||||
height="25"
|
||||
image_name="Toolbar_Middle_Off"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
left="0"
|
||||
top="0"
|
||||
name="dummy_icon"
|
||||
width="59" / -->
|
||||
right="-1"
|
||||
name="dummy_icon" />
|
||||
</panel>
|
||||
<panel
|
||||
follows="top|right"
|
||||
left_pad="0"
|
||||
follows="top|right"
|
||||
height="25"
|
||||
left_pad="0"
|
||||
right="-1"
|
||||
layout="topleft"
|
||||
name="trash_btn_panel"
|
||||
width="31">
|
||||
width="32">
|
||||
<dnd_button
|
||||
follows="top|right"
|
||||
height="25"
|
||||
|
|
@ -301,6 +487,6 @@
|
|||
tool_tip="Remove selected item"
|
||||
top="0"
|
||||
width="31"/>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
</panel>
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@
|
|||
height="300"
|
||||
width="330" />
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
width="330"
|
||||
<layout_panel
|
||||
layout="topleft"
|
||||
auto_resize="false"
|
||||
user_resize="true"
|
||||
|
|
@ -69,16 +68,15 @@
|
|||
class="panel_marketplace_inbox"
|
||||
top="0"
|
||||
label=""
|
||||
height="235"
|
||||
width="330">
|
||||
height="235">
|
||||
<string name="InboxLabelWithArg">Received items ([NUM])</string>
|
||||
<string name="InboxLabelNoArg">Received items</string>
|
||||
<button
|
||||
control_name="InventoryInboxToggleState"
|
||||
label="Received items"
|
||||
font="SansSerifMedium"
|
||||
font="SansSerifMedium"
|
||||
name="inbox_btn"
|
||||
height="35"
|
||||
width="270"
|
||||
image_unselected="MarketplaceBtn_Off"
|
||||
image_selected="MarketplaceBtn_Selected"
|
||||
halign="left"
|
||||
|
|
@ -88,7 +86,8 @@
|
|||
tab_stop="false"
|
||||
pad_left="35"
|
||||
top="0"
|
||||
left="10" />
|
||||
left="5"
|
||||
right="-43" />
|
||||
<button
|
||||
name="reload_received_items_btn"
|
||||
height="35"
|
||||
|
|
@ -108,35 +107,32 @@
|
|||
name="inbox_fresh_new_count"
|
||||
font="SansSerifMedium"
|
||||
halign="right"
|
||||
top_pad="0"
|
||||
width="300">
|
||||
top_pad="0">
|
||||
[NUM] new
|
||||
</text>
|
||||
<panel
|
||||
name="inbox_inventory_placeholder_panel"
|
||||
follows="all"
|
||||
left="10"
|
||||
bottom="235"
|
||||
width="308"
|
||||
left="5"
|
||||
right="-5"
|
||||
top="35"
|
||||
height="200"
|
||||
bg_opaque_color="InventoryBackgroundColor"
|
||||
background_visible="true"
|
||||
background_opaque="true"
|
||||
tool_tip="Drag and drop items to your inventory to use them"
|
||||
>
|
||||
<text
|
||||
name="inbox_inventory_placeholder"
|
||||
type="string"
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
top="0"
|
||||
left="0"
|
||||
width="308"
|
||||
height="200"
|
||||
wrap="true"
|
||||
halign="center">
|
||||
Purchases from the marketplace will be delivered here.
|
||||
</text>
|
||||
<text name="inbox_inventory_placeholder"
|
||||
type="string"
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
top="0"
|
||||
height="200"
|
||||
wrap="true"
|
||||
halign="center"
|
||||
valign="center">
|
||||
Purchases from the marketplace will be delivered here.
|
||||
</text>
|
||||
</panel>
|
||||
</panel>
|
||||
</layout_panel>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@
|
|||
<panel.string
|
||||
name="ItemcountUnknown">
|
||||
Fetched [ITEM_COUNT] Elements [FILTER]
|
||||
</panel.string>
|
||||
</panel.string>
|
||||
|
||||
<!-- Inventory view modes -->
|
||||
<panel.string name="inventory_title">Inventory</panel.string>
|
||||
<panel.string name="default_mode_btn">Multi_Folder_Mode</panel.string>
|
||||
<panel.string name="single_folder_mode_btn">Single_Folder_Mode</panel.string>
|
||||
|
||||
<menu_bar
|
||||
follows="left|top"
|
||||
|
|
@ -378,6 +383,60 @@
|
|||
name="inventory_search_menu"
|
||||
tear_off="true"
|
||||
top="0">
|
||||
|
||||
<!-- search visibility settings -->
|
||||
<menu_item_check
|
||||
label="Search outfit folders"
|
||||
layout="topleft"
|
||||
name="search_outfits">
|
||||
<on_click
|
||||
function="Inventory.GearDefault.Custom.Action"
|
||||
parameter="toggle_search_outfits" />
|
||||
<on_check
|
||||
function="Inventory.GearDefault.Check"
|
||||
parameter="toggle_search_outfits" />
|
||||
</menu_item_check>
|
||||
<menu_item_check
|
||||
label="Search Trash"
|
||||
layout="topleft"
|
||||
name="search_trash">
|
||||
<on_click
|
||||
function="Inventory.GearDefault.Custom.Action"
|
||||
parameter="toggle_search_trash" />
|
||||
<on_check
|
||||
function="Inventory.GearDefault.Check"
|
||||
parameter="toggle_search_trash" />
|
||||
</menu_item_check>
|
||||
<menu_item_check
|
||||
label="Search Library"
|
||||
layout="topleft"
|
||||
name="search_library">
|
||||
<on_click
|
||||
function="Inventory.GearDefault.Custom.Action"
|
||||
parameter="toggle_search_library" />
|
||||
<on_check
|
||||
function="Inventory.GearDefault.Check"
|
||||
parameter="toggle_search_library" />
|
||||
</menu_item_check>
|
||||
|
||||
<!-- this is already in our menu, further down
|
||||
<menu_item_separator
|
||||
layout="topleft" />
|
||||
<menu_item_check
|
||||
label="Include links"
|
||||
layout="topleft"
|
||||
name="include_links">
|
||||
<on_click
|
||||
function="Inventory.GearDefault.Custom.Action"
|
||||
parameter="include_links" />
|
||||
<on_check
|
||||
function="Inventory.GearDefault.Check"
|
||||
parameter="include_links" />
|
||||
</menu_item_check>
|
||||
-->
|
||||
|
||||
<menu_item_separator />
|
||||
|
||||
<menu_item_check
|
||||
name="inventory_search_by_name"
|
||||
label="By Name">
|
||||
|
|
@ -508,6 +567,78 @@
|
|||
|
||||
</menu_bar>
|
||||
|
||||
<layout_stack
|
||||
follows="left|top|right"
|
||||
height="22"
|
||||
animate="false"
|
||||
top_pad="0"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="nav_stack"
|
||||
width="330"
|
||||
orientation="horizontal">
|
||||
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
auto_resize="false"
|
||||
height="18"
|
||||
width="50"
|
||||
name="nav_buttons"
|
||||
visible="false">
|
||||
|
||||
<button
|
||||
follows="top|left"
|
||||
height="16"
|
||||
image_selected="Single_Folder_Back"
|
||||
image_pressed="Single_Folder_Back"
|
||||
image_unselected="Single_Folder_Back"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
top="2"
|
||||
name="back_btn"
|
||||
tool_tip="Back"
|
||||
width="16" />
|
||||
|
||||
<button
|
||||
follows="top|left"
|
||||
height="16"
|
||||
image_selected="Single_Folder_Forward"
|
||||
image_pressed="Single_Folder_Forward"
|
||||
image_unselected="Single_Folder_Forward"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="forward_btn"
|
||||
tool_tip="Forward"
|
||||
width="16" />
|
||||
|
||||
<button
|
||||
follows="top|left"
|
||||
height="16"
|
||||
image_selected="Single_Folder_Up"
|
||||
image_pressed="Single_Folder_Up"
|
||||
image_unselected="Single_Folder_Up"
|
||||
scale_image="false"
|
||||
layout="topleft"
|
||||
left_pad="1"
|
||||
name="up_btn"
|
||||
tool_tip="Go up one level"
|
||||
width="16" />
|
||||
|
||||
</layout_panel>
|
||||
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="false"
|
||||
height="18"
|
||||
width="330"
|
||||
name="filter_panel"
|
||||
visible="true">
|
||||
|
||||
<filter_editor
|
||||
text_pad_left="10"
|
||||
follows="left|top|right"
|
||||
|
|
@ -518,7 +649,7 @@
|
|||
max_length_chars="300"
|
||||
highlight_text_field="true"
|
||||
name="inventory search editor"
|
||||
right="-1"
|
||||
width="308"
|
||||
tool_tip="Type in one or more words to search for, separated by '+' "
|
||||
top_pad="4">
|
||||
<filter_editor.clear_button
|
||||
|
|
@ -528,6 +659,23 @@
|
|||
/>
|
||||
</filter_editor>
|
||||
|
||||
<button
|
||||
follows="top|right"
|
||||
height="18"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_overlay="Single_Folder_Mode"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
layout="topleft"
|
||||
name="view_mode_btn"
|
||||
tool_tip="Switch between views"
|
||||
left_pad="4"
|
||||
width="18"/>
|
||||
|
||||
</layout_panel>
|
||||
|
||||
</layout_stack>
|
||||
|
||||
<button
|
||||
name="collapse_btn"
|
||||
label="Collapse"
|
||||
|
|
@ -563,7 +711,7 @@
|
|||
left_pad="4"
|
||||
follows="top|left"
|
||||
layout="topleft"
|
||||
right="-34"
|
||||
right="-1"
|
||||
top_delta="-2">
|
||||
|
||||
<combo_box.item value="filter_type_all" label="All Types" />
|
||||
|
|
@ -584,6 +732,8 @@
|
|||
<combo_box.item value="filter_separator" label="------------" enabled="false" />
|
||||
<combo_box.item value="filter_type_custom" label="Custom..." />
|
||||
</combo_box>
|
||||
|
||||
<!-- this should be in the main inventory menu instead of ading another button
|
||||
<menu_button
|
||||
follows="top|left"
|
||||
tool_tip="Show search visibility options"
|
||||
|
|
@ -594,6 +744,18 @@
|
|||
name="options_visibility_btn"
|
||||
width="31"
|
||||
top_delta="0"/>
|
||||
-->
|
||||
|
||||
<!-- inventory display mode: default -->
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="212"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
right="-1"
|
||||
name="default_inventory_panel"
|
||||
top_pad="0">
|
||||
|
||||
<tab_container
|
||||
follows="all"
|
||||
|
|
@ -644,12 +806,101 @@
|
|||
</worn_inventory_panel>
|
||||
</tab_container>
|
||||
|
||||
</panel>
|
||||
|
||||
<!-- inventory display mode: combination view -->
|
||||
|
||||
<panel
|
||||
follows="all"
|
||||
halign="center"
|
||||
height="212"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
right="-1"
|
||||
name="combination_view_inventory"
|
||||
top_delta="0"
|
||||
visible="false">
|
||||
|
||||
<layout_stack
|
||||
follows="all"
|
||||
height="212"
|
||||
name="combination_view_stack"
|
||||
animate="false"
|
||||
drag_handle_thickness="6"
|
||||
drag_handle_first_indent="18"
|
||||
drag_handle_second_indent="18"
|
||||
drag_handle_color="PanelGray"
|
||||
drag_handle_shift="5"
|
||||
layout="topleft"
|
||||
show_drag_handle="true"
|
||||
top="3"
|
||||
left="3"
|
||||
width="323"
|
||||
orientation="vertical">
|
||||
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="110"
|
||||
min_width="150"
|
||||
name="comb_gallery_layout">
|
||||
|
||||
<panel
|
||||
class="inventory_gallery"
|
||||
filename="panel_inventory_gallery.xml"
|
||||
left="0"
|
||||
top_pad="0"
|
||||
height="110"
|
||||
name="comb_gallery_view_inv"
|
||||
follows="all"
|
||||
layout="topleft" />
|
||||
|
||||
</layout_panel>
|
||||
|
||||
<layout_panel
|
||||
border="false"
|
||||
bevel_style="in"
|
||||
user_resize="true"
|
||||
auto_resize="true"
|
||||
height="102"
|
||||
name="comb_inventory_layout">
|
||||
|
||||
<single_folder_inventory_panel
|
||||
name="comb_single_folder_inv"
|
||||
follows="all"
|
||||
left="0"
|
||||
top="0"
|
||||
height="102"
|
||||
layout="topleft"
|
||||
show_item_link_overlays="true"
|
||||
border="false"
|
||||
bevel_style="none"
|
||||
scroll.reserve_scroll_corner="false">
|
||||
|
||||
<item
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
|
||||
<folder
|
||||
single_folder_mode="true"
|
||||
folder_indentation="-8"/>
|
||||
|
||||
</single_folder_inventory_panel>
|
||||
|
||||
</layout_panel>
|
||||
|
||||
</layout_stack>
|
||||
|
||||
</panel>
|
||||
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
height="16"
|
||||
layout="topleft"
|
||||
top_pad="2"
|
||||
top_pad="6"
|
||||
tool_tip="[ITEMS] Items, [CATEGORIES] Folders"
|
||||
left_delta="2"
|
||||
name="ItemcountText"
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ import os.path
|
|||
import plistlib
|
||||
import random
|
||||
import re
|
||||
import secrets
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
sys.dont_write_bytecode = True # <FS:Ansariel> Prevents creating __pycache__ directory
|
||||
|
||||
|
|
@ -494,11 +494,30 @@ class ViewerManifest(LLManifest,FSViewerManifest):
|
|||
|
||||
return os.path.relpath(abspath(path), abspath(base))
|
||||
|
||||
# <FS:Ansariel> Undo Github-Build stuff - I don't think we need this
|
||||
#def set_github_output_path(self, variable, path):
|
||||
# self.set_github_output(variable,
|
||||
# os.path.normpath(os.path.join(self.get_dst_prefix(), path)))
|
||||
|
||||
class WindowsManifest(ViewerManifest):
|
||||
#def set_github_output(self, variable, *values):
|
||||
# GITHUB_OUTPUT = os.getenv('GITHUB_OUTPUT')
|
||||
# if GITHUB_OUTPUT and values:
|
||||
# with open(GITHUB_OUTPUT, 'a') as outf:
|
||||
# if len(values) == 1:
|
||||
# print('='.join((variable, values[0])), file=outf)
|
||||
# else:
|
||||
# delim = secrets.token_hex(8)
|
||||
# print('<<'.join((variable, delim)), file=outf)
|
||||
# for value in values:
|
||||
# print(value, file=outf)
|
||||
# print(delim, file=outf)
|
||||
# </FS:Ansariel>
|
||||
|
||||
class Windows_x86_64_Manifest(ViewerManifest):
|
||||
# We want the platform, per se, for every Windows build to be 'win'. The
|
||||
# VMP will concatenate that with the address_size.
|
||||
build_data_json_platform = 'win'
|
||||
address_size = 64
|
||||
|
||||
def final_exe(self):
|
||||
return self.exec_name()+".exe"
|
||||
|
|
@ -559,7 +578,7 @@ class WindowsManifest(ViewerManifest):
|
|||
print("Doesn't exist:", src)
|
||||
|
||||
def construct(self):
|
||||
super(WindowsManifest, self).construct()
|
||||
super().construct()
|
||||
|
||||
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
|
||||
relpkgdir = os.path.join(pkgdir, "lib", "release")
|
||||
|
|
@ -568,6 +587,32 @@ class WindowsManifest(ViewerManifest):
|
|||
if self.is_packaging_viewer():
|
||||
# Find firestorm-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
|
||||
self.path(src='%s/firestorm-bin.exe' % self.args['configuration'], dst=self.final_exe())
|
||||
# Emit the whole app image as one of the GitHub step outputs. We
|
||||
# want the whole app -- but NOT the extraneous build products that
|
||||
# get tossed into the same directory, such as the installer and
|
||||
# the symbols tarball, so add exclusions. When we feed
|
||||
# upload-artifact multiple absolute pathnames, even just for
|
||||
# exclusion, it ends up creating several extraneous directory
|
||||
# levels within the artifact -- so try using only relative paths.
|
||||
# One problem: as of right now, our current directory os.getcwd()
|
||||
# is not the same as the initial working directory for this job
|
||||
# step, meaning paths relative to our os.getcwd() won't work for
|
||||
# the subsequent upload-artifact step. We're a couple directory
|
||||
# levels down. Try adjusting for those when specifying the base
|
||||
# for self.relpath().
|
||||
# <FS:Ansariel> Undo Github-Build stuff - I don't think we need this
|
||||
#appbase = self.relpath(
|
||||
# self.get_dst_prefix(),
|
||||
# base=os.path.join(os.getcwd(), os.pardir, os.pardir))
|
||||
#self.set_github_output('viewer_app', appbase,
|
||||
# # except for this stuff
|
||||
# *(('!' + os.path.join(appbase, pattern))
|
||||
# for pattern in (
|
||||
# 'secondlife-bin.*',
|
||||
# '*_Setup.exe',
|
||||
# '*.bat',
|
||||
# '*.tar.bz2')))
|
||||
# </FS:Ansariel>
|
||||
|
||||
# <FS:Ansariel> Remove VMP
|
||||
#with self.prefix(src=os.path.join(pkgdir, "VMP")):
|
||||
|
|
@ -631,20 +676,12 @@ class WindowsManifest(ViewerManifest):
|
|||
self.path("SLVoice.exe")
|
||||
|
||||
# Vivox libraries
|
||||
if (self.address_size == 64):
|
||||
self.path("vivoxsdk_x64.dll")
|
||||
self.path("ortp_x64.dll")
|
||||
else:
|
||||
self.path("vivoxsdk.dll")
|
||||
self.path("ortp.dll")
|
||||
self.path("vivoxsdk_x64.dll")
|
||||
self.path("ortp_x64.dll")
|
||||
|
||||
# OpenSSL
|
||||
if (self.address_size == 64):
|
||||
self.path("libcrypto-1_1-x64.dll")
|
||||
self.path("libssl-1_1-x64.dll")
|
||||
else:
|
||||
self.path("libcrypto-1_1.dll")
|
||||
self.path("libssl-1_1.dll")
|
||||
self.path("libcrypto-1_1-x64.dll")
|
||||
self.path("libssl-1_1-x64.dll")
|
||||
|
||||
# HTTP/2
|
||||
self.path("nghttp2.dll")
|
||||
|
|
@ -654,14 +691,9 @@ class WindowsManifest(ViewerManifest):
|
|||
|
||||
# BugSplat
|
||||
if self.args.get('bugsplat'):
|
||||
if(self.address_size == 64):
|
||||
self.path("BsSndRpt64.exe")
|
||||
self.path("BugSplat64.dll")
|
||||
self.path("BugSplatRc64.dll")
|
||||
else:
|
||||
self.path("BsSndRpt.exe")
|
||||
self.path("BugSplat.dll")
|
||||
self.path("BugSplatRc.dll")
|
||||
self.path("BsSndRpt64.exe")
|
||||
self.path("BugSplat64.dll")
|
||||
self.path("BugSplatRc64.dll")
|
||||
|
||||
# Growl
|
||||
self.path("growl.dll")
|
||||
|
|
@ -793,6 +825,48 @@ class WindowsManifest(ViewerManifest):
|
|||
self.fs_copy_windows_manifest( )
|
||||
|
||||
def nsi_file_commands(self, install=True):
|
||||
# <FS:Ansariel> Undo Github-Build stuff - I don't think we need this
|
||||
#def INSTDIR(path):
|
||||
# # Note that '$INSTDIR' is purely textual here: we write
|
||||
# # exactly that into the .nsi file for NSIS to interpret.
|
||||
# # Pass the result through normpath() to handle the case in which
|
||||
# # path is the empty string. On Windows, that produces "$INSTDIR\".
|
||||
# # Unfortunately, if that's the last item on a line, NSIS takes
|
||||
# # that as line continuation and misinterprets the following line.
|
||||
# # Ensure we don't emit a trailing backslash.
|
||||
# return os.path.normpath(os.path.join('$INSTDIR', path))
|
||||
|
||||
#result = []
|
||||
#dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
|
||||
## sort deepest hierarchy first
|
||||
#dest_files.sort(key=lambda f: (f.count(os.path.sep), f), reverse=True)
|
||||
#out_path = None
|
||||
#for pkg_file in dest_files:
|
||||
# pkg_file = os.path.normpath(pkg_file)
|
||||
# rel_file = self.relpath(pkg_file)
|
||||
# installed_dir = INSTDIR(os.path.dirname(rel_file))
|
||||
# if install and installed_dir != out_path:
|
||||
# out_path = installed_dir
|
||||
# # emit SetOutPath every time it changes
|
||||
# result.append('SetOutPath ' + out_path)
|
||||
# if install:
|
||||
# result.append('File ' + rel_file)
|
||||
# else:
|
||||
# result.append('Delete ' + INSTDIR(rel_file))
|
||||
|
||||
## at the end of a delete, just rmdir all the directories
|
||||
#if not install:
|
||||
# deleted_file_dirs = [os.path.dirname(self.relpath(f)) for f in dest_files]
|
||||
# # find all ancestors so that we don't skip any dirs that happened
|
||||
# # to have no non-dir children
|
||||
# deleted_dirs = set(itertools.chain.from_iterable(path_ancestors(d)
|
||||
# for d in deleted_file_dirs))
|
||||
# # sort deepest hierarchy first
|
||||
# for d in sorted(deleted_dirs, key=lambda f: (f.count(os.path.sep), f), reverse=True):
|
||||
# result.append('RMDir ' + INSTDIR(d))
|
||||
|
||||
#return '\n'.join(result)
|
||||
|
||||
def wpath(path):
|
||||
if path.endswith('/') or path.endswith(os.path.sep):
|
||||
path = path[:-1]
|
||||
|
|
@ -833,6 +907,7 @@ class WindowsManifest(ViewerManifest):
|
|||
prev = d
|
||||
|
||||
return result
|
||||
# </FS:Ansariel>
|
||||
|
||||
def package_finish(self):
|
||||
# a standard map of strings for replacing in the templates
|
||||
|
|
@ -840,8 +915,7 @@ class WindowsManifest(ViewerManifest):
|
|||
'version' : '.'.join(self.args['version']),
|
||||
'version_short' : '.'.join(self.args['version'][:-1]),
|
||||
'version_dashes' : '-'.join(self.args['version']),
|
||||
'version_registry' : '%s(%s)' %
|
||||
('.'.join(self.args['version']), self.address_size),
|
||||
'version_registry' : '%s(64)' % '.'.join(self.args['version']),
|
||||
'final_exe' : self.final_exe(),
|
||||
'flags':'',
|
||||
'app_name':self.app_name(),
|
||||
|
|
@ -887,12 +961,18 @@ class WindowsManifest(ViewerManifest):
|
|||
Caption "%(caption)s"
|
||||
"""
|
||||
|
||||
if(self.address_size == 64):
|
||||
engage_registry="SetRegView 64"
|
||||
program_files="!define MULTIUSER_USE_PROGRAMFILES64"
|
||||
else:
|
||||
engage_registry="SetRegView 32"
|
||||
program_files=""
|
||||
engage_registry="SetRegView 64"
|
||||
program_files="!define MULTIUSER_USE_PROGRAMFILES64"
|
||||
|
||||
# Dump the installers/windows directory into the raw app image tree
|
||||
# because NSIS needs those files. But don't use path() because we
|
||||
# don't want them installed with the viewer - they're only for use by
|
||||
# the installer itself.
|
||||
# <FS:Ansariel> Undo Github-Build stuff - I don't think we need this
|
||||
#shutil.copytree(os.path.join(self.get_src_prefix(), 'installers', 'windows'),
|
||||
# os.path.join(self.get_dst_prefix(), 'installers', 'windows'),
|
||||
# dirs_exist_ok=True)
|
||||
# </FS:Ansariel>
|
||||
|
||||
tempfile = "firestorm_setup_tmp.nsi"
|
||||
|
||||
|
|
@ -902,6 +982,10 @@ class WindowsManifest(ViewerManifest):
|
|||
# it also does python-style % substitution
|
||||
self.replace_in("installers/windows/installer_template.nsi", tempfile, {
|
||||
"%%VERSION%%":version_vars,
|
||||
# The template references "%%SOURCE%%\installers\windows\...".
|
||||
# Now that we've copied that directory into the app image
|
||||
# tree, we can just replace %%SOURCE%% with '.'.
|
||||
#"%%SOURCE%%":'.', <FS:Ansariel> Undo Github-Build stuff
|
||||
"%%SOURCE%%":self.get_src_prefix(),
|
||||
"%%INST_VARS%%":inst_vars_template % substitution_strings,
|
||||
"%%INSTALL_FILES%%":self.nsi_file_commands(True),
|
||||
|
|
@ -909,16 +993,7 @@ class WindowsManifest(ViewerManifest):
|
|||
"%%ENGAGEREGISTRY%%":engage_registry,
|
||||
"%%DELETE_FILES%%":self.nsi_file_commands(False)})
|
||||
|
||||
# If we're on a build machine, sign the code using our Authenticode certificate. JC
|
||||
# note that the enclosing setup exe is signed later, after the makensis makes it.
|
||||
# Unlike the viewer binary, the VMP filenames are invariant with respect to version, os, etc.
|
||||
#for exe in (
|
||||
# self.final_exe(),
|
||||
# "SLVersionChecker.exe",
|
||||
# "llplugin/dullahan_host.exe",
|
||||
# ):
|
||||
# self.sign(exe)
|
||||
|
||||
# <FS:Ansariel> Undo Github-Build stuff
|
||||
# Check two paths, one for Program Files, and one for Program Files (x86).
|
||||
# Yay 64bit windows.
|
||||
nsis_path = "makensis.exe"
|
||||
|
|
@ -936,6 +1011,7 @@ class WindowsManifest(ViewerManifest):
|
|||
|
||||
self.created_path(self.dst_path_of(installer_file))
|
||||
self.package_file = installer_file
|
||||
# </FS:Ansariel>
|
||||
|
||||
def sign(self, exe):
|
||||
sign_py = os.environ.get('SIGN', r'C:\buildscripts\code-signing\sign.py')
|
||||
|
|
@ -950,17 +1026,9 @@ class WindowsManifest(ViewerManifest):
|
|||
def escape_slashes(self, path):
|
||||
return path.replace('\\', '\\\\\\\\')
|
||||
|
||||
class Windows_i686_Manifest(WindowsManifest):
|
||||
# Although we aren't literally passed ADDRESS_SIZE, we can infer it from
|
||||
# the passed 'arch', which is used to select the specific subclass.
|
||||
address_size = 32
|
||||
|
||||
class Windows_x86_64_Manifest(WindowsManifest):
|
||||
address_size = 64
|
||||
|
||||
|
||||
class DarwinManifest(ViewerManifest):
|
||||
class Darwin_x86_64_Manifest(ViewerManifest):
|
||||
build_data_json_platform = 'mac'
|
||||
address_size = 64
|
||||
|
||||
def finish_build_data_dict(self, build_data_dict):
|
||||
build_data_dict.update({'Bundle Id':self.args['bundleid']})
|
||||
|
|
@ -1311,8 +1379,9 @@ class DarwinManifest(ViewerManifest):
|
|||
# self.path( "plugins.dat" )
|
||||
|
||||
def construct(self):
|
||||
# copy over the build result (this is a no-op if run within the xcode script)
|
||||
# self.path(os.path.join(self.args['configuration'], self.channel()+".app"), dst="")
|
||||
# copy over the build result (this is a no-op if run within the xcode
|
||||
# script)
|
||||
#self.path(os.path.join(self.args['configuration'], self.channel() + ".app"), dst="")
|
||||
self.path(os.path.join(self.args['configuration'], "Firestorm.app"), dst="")
|
||||
|
||||
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
|
||||
|
|
@ -1358,7 +1427,7 @@ class DarwinManifest(ViewerManifest):
|
|||
|
||||
# most everything goes in the Resources directory
|
||||
with self.prefix(dst="Resources"):
|
||||
super(DarwinManifest, self).construct()
|
||||
super().construct()
|
||||
|
||||
with self.prefix(src_dst="cursors_mac"):
|
||||
self.path("*.tif")
|
||||
|
|
@ -1838,18 +1907,6 @@ class DarwinManifest(ViewerManifest):
|
|||
self.remove(sparsename)
|
||||
self.fs_save_osx_symbols()
|
||||
|
||||
class Darwin_i386_Manifest(DarwinManifest):
|
||||
address_size = 32
|
||||
|
||||
|
||||
class Darwin_i686_Manifest(DarwinManifest):
|
||||
"""alias in case arch is passed as i686 instead of i386"""
|
||||
pass
|
||||
|
||||
|
||||
class Darwin_x86_64_Manifest(DarwinManifest):
|
||||
address_size = 64
|
||||
|
||||
|
||||
class LinuxManifest(ViewerManifest):
|
||||
build_data_json_platform = 'lnx'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* @file hexdump.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2023-09-08
|
||||
* @brief Provide hexdump() and hexmix() ostream formatters
|
||||
*
|
||||
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
|
||||
* Copyright (c) 2023, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_HEXDUMP_H)
|
||||
#define LL_HEXDUMP_H
|
||||
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
// Format a given byte string as 2-digit hex values, no separators
|
||||
// Usage: std::cout << hexdump(somestring) << ...
|
||||
class hexdump
|
||||
{
|
||||
public:
|
||||
hexdump(const std::string_view& data):
|
||||
hexdump(data.data(), data.length())
|
||||
{}
|
||||
|
||||
hexdump(const char* data, size_t len):
|
||||
hexdump(reinterpret_cast<const unsigned char*>(data), len)
|
||||
{}
|
||||
|
||||
hexdump(const unsigned char* data, size_t len):
|
||||
mData(data, data + len)
|
||||
{}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const hexdump& self)
|
||||
{
|
||||
auto oldfmt{ out.flags() };
|
||||
auto oldfill{ out.fill() };
|
||||
out.setf(std::ios_base::hex, std::ios_base::basefield);
|
||||
out.fill('0');
|
||||
for (auto c : self.mData)
|
||||
{
|
||||
out << std::setw(2) << unsigned(c);
|
||||
}
|
||||
out.setf(oldfmt, std::ios_base::basefield);
|
||||
out.fill(oldfill);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<unsigned char> mData;
|
||||
};
|
||||
|
||||
// Format a given byte string as a mix of printable characters and, for each
|
||||
// non-printable character, "\xnn"
|
||||
// Usage: std::cout << hexmix(somestring) << ...
|
||||
class hexmix
|
||||
{
|
||||
public:
|
||||
hexmix(const std::string_view& data):
|
||||
mData(data)
|
||||
{}
|
||||
|
||||
hexmix(const char* data, size_t len):
|
||||
mData(data, len)
|
||||
{}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const hexmix& self)
|
||||
{
|
||||
auto oldfmt{ out.flags() };
|
||||
auto oldfill{ out.fill() };
|
||||
out.setf(std::ios_base::hex, std::ios_base::basefield);
|
||||
out.fill('0');
|
||||
for (auto c : self.mData)
|
||||
{
|
||||
// std::isprint() must be passed an unsigned char!
|
||||
if (std::isprint(static_cast<unsigned char>(c)))
|
||||
{
|
||||
out << c;
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "\\x" << std::setw(2) << unsigned(c);
|
||||
}
|
||||
}
|
||||
out.setf(oldfmt, std::ios_base::basefield);
|
||||
out.fill(oldfill);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mData;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_HEXDUMP_H) */
|
||||
|
|
@ -13,15 +13,16 @@
|
|||
#define LL_NAMEDTEMPFILE_H
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llapr.h"
|
||||
#include "apr_file_io.h"
|
||||
#include "llstring.h"
|
||||
#include "stringize.h"
|
||||
#include <string>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/phoenix/core/argument.hpp>
|
||||
#include <boost/phoenix/operator/bitwise.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* Create a text file with specified content "somewhere in the
|
||||
|
|
@ -31,134 +32,123 @@ class NamedTempFile: public boost::noncopyable
|
|||
{
|
||||
LOG_CLASS(NamedTempFile);
|
||||
public:
|
||||
NamedTempFile(const std::string& pfx, const std::string& content, apr_pool_t* pool=gAPRPoolp):
|
||||
mPool(pool)
|
||||
NamedTempFile(const std::string_view& pfx,
|
||||
const std::string_view& content,
|
||||
const std::string_view& sfx=std::string_view(""))
|
||||
{
|
||||
createFile(pfx, boost::phoenix::placeholders::arg1 << content);
|
||||
createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx);
|
||||
}
|
||||
|
||||
// Disambiguate when passing string literal
|
||||
NamedTempFile(const std::string& pfx, const char* content, apr_pool_t* pool=gAPRPoolp):
|
||||
mPool(pool)
|
||||
// Disambiguate when passing string literal -- unclear why a string
|
||||
// literal should be ambiguous wrt std::string_view and Streamer
|
||||
NamedTempFile(const std::string_view& pfx,
|
||||
const char* content,
|
||||
const std::string_view& sfx=std::string_view(""))
|
||||
{
|
||||
createFile(pfx, boost::phoenix::placeholders::arg1 << content);
|
||||
createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx);
|
||||
}
|
||||
|
||||
// Function that accepts an ostream ref and (presumably) writes stuff to
|
||||
// it, e.g.:
|
||||
// (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n')
|
||||
typedef boost::function<void(std::ostream&)> Streamer;
|
||||
typedef std::function<void(std::ostream&)> Streamer;
|
||||
|
||||
NamedTempFile(const std::string& pfx, const Streamer& func, apr_pool_t* pool=gAPRPoolp):
|
||||
mPool(pool)
|
||||
NamedTempFile(const std::string_view& pfx,
|
||||
const Streamer& func,
|
||||
const std::string_view& sfx=std::string_view(""))
|
||||
{
|
||||
createFile(pfx, func);
|
||||
createFile(pfx, func, sfx);
|
||||
}
|
||||
|
||||
virtual ~NamedTempFile()
|
||||
{
|
||||
ll_apr_assert_status(apr_file_remove(mPath.c_str(), mPool));
|
||||
boost::filesystem::remove(mPath);
|
||||
}
|
||||
|
||||
virtual std::string getName() const { return mPath; }
|
||||
std::string getName() const { return mPath.string(); }
|
||||
|
||||
void peep()
|
||||
template <typename CALLABLE>
|
||||
void peep_via(CALLABLE&& callable) const
|
||||
{
|
||||
std::cout << "File '" << mPath << "' contains:\n";
|
||||
std::ifstream reader(mPath.c_str());
|
||||
std::forward<CALLABLE>(callable)(stringize("File '", mPath, "' contains:"));
|
||||
boost::filesystem::ifstream reader(mPath, std::ios::binary);
|
||||
std::string line;
|
||||
while (std::getline(reader, line))
|
||||
std::cout << line << '\n';
|
||||
std::cout << "---\n";
|
||||
std::forward<CALLABLE>(callable)(line);
|
||||
std::forward<CALLABLE>(callable)("---");
|
||||
}
|
||||
|
||||
void peep_log() const
|
||||
{
|
||||
peep_via([](const std::string& line){ LL_DEBUGS() << line << LL_ENDL; });
|
||||
}
|
||||
|
||||
void peep(std::ostream& out=std::cout) const
|
||||
{
|
||||
peep_via([&out](const std::string& line){ out << line << '\n'; });
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const NamedTempFile& self)
|
||||
{
|
||||
self.peep(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
static boost::filesystem::path temp_path(const std::string_view& pfx="",
|
||||
const std::string_view& sfx="")
|
||||
{
|
||||
// This variable is set by GitHub actions and is the recommended place
|
||||
// to put temp files belonging to an actions job.
|
||||
const char* RUNNER_TEMP = getenv("RUNNER_TEMP");
|
||||
boost::filesystem::path tempdir{
|
||||
// if RUNNER_TEMP is set and not empty
|
||||
(RUNNER_TEMP && *RUNNER_TEMP)?
|
||||
boost::filesystem::path(RUNNER_TEMP) : // use RUNNER_TEMP if available
|
||||
boost::filesystem::temp_directory_path()}; // else canonical temp dir
|
||||
boost::filesystem::path tempname{
|
||||
// use filename template recommended by unique_path() doc, but
|
||||
// with underscores instead of hyphens: some use cases involve
|
||||
// temporary Python scripts
|
||||
tempdir / stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) };
|
||||
return boost::filesystem::unique_path(tempname);
|
||||
}
|
||||
|
||||
protected:
|
||||
void createFile(const std::string& pfx, const Streamer& func)
|
||||
void createFile(const std::string_view& pfx,
|
||||
const Streamer& func,
|
||||
const std::string_view& sfx)
|
||||
{
|
||||
// Create file in a temporary place.
|
||||
const char* tempdir = NULL;
|
||||
ll_apr_assert_status(apr_temp_dir_get(&tempdir, mPool));
|
||||
|
||||
// Construct a temp filename template in that directory.
|
||||
char *tempname = NULL;
|
||||
ll_apr_assert_status(apr_filepath_merge(&tempname,
|
||||
tempdir,
|
||||
(pfx + "XXXXXX").c_str(),
|
||||
0,
|
||||
mPool));
|
||||
|
||||
// Create a temp file from that template.
|
||||
apr_file_t* fp = NULL;
|
||||
ll_apr_assert_status(apr_file_mktemp(&fp,
|
||||
tempname,
|
||||
APR_CREATE | APR_WRITE | APR_EXCL,
|
||||
mPool));
|
||||
// apr_file_mktemp() alters tempname with the actual name. Not until
|
||||
// now is it valid to capture as our mPath.
|
||||
mPath = tempname;
|
||||
|
||||
mPath = temp_path(pfx, sfx);
|
||||
boost::filesystem::ofstream out{ mPath, std::ios::binary };
|
||||
// Write desired content.
|
||||
std::ostringstream out;
|
||||
// Stream stuff to it.
|
||||
func(out);
|
||||
|
||||
std::string data(out.str());
|
||||
apr_size_t writelen(data.length());
|
||||
ll_apr_assert_status(apr_file_write(fp, data.c_str(), &writelen));
|
||||
ll_apr_assert_status(apr_file_close(fp));
|
||||
llassert_always(writelen == data.length());
|
||||
}
|
||||
|
||||
std::string mPath;
|
||||
apr_pool_t* mPool;
|
||||
boost::filesystem::path mPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a NamedTempFile with a specified filename extension. This is useful
|
||||
* when, for instance, you must be able to use the file in a Python import
|
||||
* statement.
|
||||
*
|
||||
* A NamedExtTempFile actually has two different names. We retain the original
|
||||
* no-extension name as a placeholder in the temp directory to ensure
|
||||
* uniqueness; to that we link the name plus the desired extension. Naturally,
|
||||
* both must be removed on destruction.
|
||||
*/
|
||||
class NamedExtTempFile: public NamedTempFile
|
||||
{
|
||||
LOG_CLASS(NamedExtTempFile);
|
||||
public:
|
||||
NamedExtTempFile(const std::string& ext, const std::string& content, apr_pool_t* pool=gAPRPoolp):
|
||||
NamedTempFile(remove_dot(ext), content, pool),
|
||||
mLink(mPath + ensure_dot(ext))
|
||||
{
|
||||
linkto(mLink);
|
||||
}
|
||||
NamedExtTempFile(const std::string& ext, const std::string_view& content):
|
||||
NamedTempFile(remove_dot(ext), content, ensure_dot(ext))
|
||||
{}
|
||||
|
||||
// Disambiguate when passing string literal
|
||||
NamedExtTempFile(const std::string& ext, const char* content, apr_pool_t* pool=gAPRPoolp):
|
||||
NamedTempFile(remove_dot(ext), content, pool),
|
||||
mLink(mPath + ensure_dot(ext))
|
||||
{
|
||||
linkto(mLink);
|
||||
}
|
||||
NamedExtTempFile(const std::string& ext, const char* content):
|
||||
NamedTempFile(remove_dot(ext), content, ensure_dot(ext))
|
||||
{}
|
||||
|
||||
NamedExtTempFile(const std::string& ext, const Streamer& func, apr_pool_t* pool=gAPRPoolp):
|
||||
NamedTempFile(remove_dot(ext), func, pool),
|
||||
mLink(mPath + ensure_dot(ext))
|
||||
{
|
||||
linkto(mLink);
|
||||
}
|
||||
|
||||
virtual ~NamedExtTempFile()
|
||||
{
|
||||
ll_apr_assert_status(apr_file_remove(mLink.c_str(), mPool));
|
||||
}
|
||||
|
||||
// Since the caller has gone to the trouble to create the name with the
|
||||
// extension, that should be the name we return. In this class, mPath is
|
||||
// just a placeholder to ensure that future createFile() calls won't
|
||||
// collide.
|
||||
virtual std::string getName() const { return mLink; }
|
||||
NamedExtTempFile(const std::string& ext, const Streamer& func):
|
||||
NamedTempFile(remove_dot(ext), func, ensure_dot(ext))
|
||||
{}
|
||||
|
||||
static std::string ensure_dot(const std::string& ext)
|
||||
{
|
||||
|
|
@ -175,7 +165,7 @@ public:
|
|||
{
|
||||
return ext;
|
||||
}
|
||||
return std::string(".") + ext;
|
||||
return "." + ext;
|
||||
}
|
||||
|
||||
static std::string remove_dot(const std::string& ext)
|
||||
|
|
@ -187,19 +177,6 @@ public:
|
|||
}
|
||||
return ext.substr(found);
|
||||
}
|
||||
|
||||
private:
|
||||
void linkto(const std::string& path)
|
||||
{
|
||||
// This method assumes that since mPath (without extension) is
|
||||
// guaranteed by apr_file_mktemp() to be unique, then (mPath + any
|
||||
// extension) is also unique. This is likely, though not guaranteed:
|
||||
// files could be created in the same temp directory other than by
|
||||
// this class.
|
||||
//ll_apr_assert_status(apr_file_link(mPath.c_str(), path.c_str()));
|
||||
}
|
||||
|
||||
std::string mLink;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_NAMEDTEMPFILE_H) */
|
||||
|
|
|
|||
|
|
@ -102,10 +102,10 @@ public:
|
|||
class RecordToTempFile : public LLError::Recorder, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
RecordToTempFile(apr_pool_t* pPool)
|
||||
RecordToTempFile()
|
||||
: LLError::Recorder(),
|
||||
boost::noncopyable(),
|
||||
mTempFile("log", "", pPool),
|
||||
mTempFile("log", ""),
|
||||
mFile(mTempFile.getName().c_str())
|
||||
{
|
||||
}
|
||||
|
|
@ -146,11 +146,11 @@ private:
|
|||
class LLReplayLogReal: public LLReplayLog, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool)
|
||||
LLReplayLogReal(LLError::ELevel level)
|
||||
: LLReplayLog(),
|
||||
boost::noncopyable(),
|
||||
mOldSettings(LLError::saveAndResetSettings()),
|
||||
mRecorder(new RecordToTempFile(pool))
|
||||
mRecorder(new RecordToTempFile())
|
||||
{
|
||||
LLError::setFatalFunction(wouldHaveCrashed);
|
||||
LLError::setDefaultLevel(level);
|
||||
|
|
@ -636,7 +636,7 @@ int main(int argc, char **argv)
|
|||
if (LOGFAIL && *LOGFAIL)
|
||||
{
|
||||
LLError::ELevel level = LLError::decodeLevel(LOGFAIL);
|
||||
replayer.reset(new LLReplayLogReal(level, gAPRPoolp));
|
||||
replayer.reset(new LLReplayLogReal(level));
|
||||
}
|
||||
}
|
||||
LLError::setFatalFunction(wouldHaveCrashed);
|
||||
|
|
|
|||
Loading…
Reference in New Issue