Compare commits

..

No commits in common. "master" and "snapshot-356" have entirely different histories.

291 changed files with 9962 additions and 10423 deletions

108
.github/build.sh vendored
View File

@ -44,39 +44,12 @@ wasm32-emscripten-emscripten-static) ;;
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
esac
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-31
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
>&2 echo "JAVA_HOME_8_X64 not set"
exit 1
fi
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
>&2 echo "ANDROID_SDK_ROOT not set"
exit 1
fi
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
exit 1
fi
fi
if [[ -z ${BSH_NO_PACKAGES-} ]]; then
case $BSH_HOST_PLATFORM in
android)
(
export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/tools/bin:$PATH
sdkmanager "platforms;$android_platform"
)
;;
windows)
if [[ $BSH_BUILD_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-gcc
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{cmake,7zip,jq} patch
else
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{pkgconf,bzip2,luajit,jsoncpp,curl,SDL2,libpng,meson,fftw,jq}
fi
export PKG_CONFIG=$(which pkg-config.exe)
if [[ $BSH_BUILD_PLATFORM == linux ]] && [[ $BSH_HOST_LIBC == mingw ]]; then
sudo apt update
sudo apt install g++-mingw-w64-x86-64
fi
;;
linux)
@ -114,6 +87,22 @@ function inplace_sed() {
fi
}
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-30
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
>&2 echo "JAVA_HOME_8_X64 not set"
exit 1
fi
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
>&2 echo "ANDROID_SDK_ROOT not set"
exit 1
fi
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
exit 1
fi
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]]; then
case $BSH_HOST_ARCH in
x86_64) vs_env_arch=x64;;
@ -199,7 +188,7 @@ meson_configure+=$'\t'-Dapp_exe=$APP_EXE
meson_configure+=$'\t'-Dapp_id=$APP_ID
meson_configure+=$'\t'-Dapp_data=$APP_DATA
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
meson_configure+=$'\t'-Dstrip=false
meson_configure+=$'\t'-Db_strip=false
meson_configure+=$'\t'-Db_staticpic=false
meson_configure+=$'\t'-Dmod_id=$MOD_ID
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
@ -219,9 +208,23 @@ if [[ $PACKAGE_MODE == nolua ]]; then
fi
if [[ $PACKAGE_MODE == backendvs ]]; then
meson_configure+=$'\t'-Dbackend=vs
# meson 1.2.3 configures vs projects that bring their own manifest, which conflicts with ours
# TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release that we can use
meson_configure+=$'\t'-Dwindows_utf8cp=false
echo "NOTE: patching CREATEPROCESS_MANIFEST_RESOURCE_ID out of powder-res.template.rc"
echo "TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release"
echo "TODO: also remove the relevant note from the building guide"
git apply <<PATCH
diff --git a/resources/powder-res.template.rc b/resources/powder-res.template.rc
index 1dc26c78..2094049f 100644
--- a/resources/powder-res.template.rc
+++ b/resources/powder-res.template.rc
@@ -7,7 +7,6 @@
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
PATCH
fi
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
meson_configure+=$'\t'-Dstatic=prebuilt
@ -238,11 +241,6 @@ if [[ $BSH_STATIC_DYNAMIC == static ]]; then
c_link_args+=\'-static-libstdc++\',
fi
else
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC $BSH_BUILD_PLATFORM" == "windows-mingw windows" ]]; then
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_include_dir=/ucrt64/include
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_lib_dir=/ucrt64/lib
fi
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
fi
@ -290,21 +288,18 @@ if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
fi
if [[ $RELEASE_TYPE == snapshot ]] || [[ $MOD_ID != 0 ]]; then
meson_configure+=$'\t'-Dupdate_server=starcatcher.us/TPT
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
meson_configure+=$'\t'-Dserver=tptserv.starcatcher.us
meson_configure+=$'\t'-Dstatic_server=tptserv.starcatcher.us/Static
fi
fi
if [[ $RELEASE_TYPE != dev ]]; then
meson_configure+=$'\t'-Dignore_updates=false
fi
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC" == "windows-mingw" ]]; then
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
# there is some mingw bug that only ever manifests on ghactions which makes MakeIco.exe use tons of memory and fail
# TODO: remove this hack once we figure out how to fix that
meson_configure+=$'\t'-Dwindows_icons=false
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
fi
fi
if [[ $BSH_DEBUG_RELEASE-$BSH_STATIC_DYNAMIC == release-static ]]; then
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC != windows-mingw ]] && [[ $BSH_STATIC_DYNAMIC == static ]]; then
# LTO simply doesn't work with MinGW. I have no idea why and I also don't care.
# It also has a tendency to not play well with dynamic libraries.
meson_configure+=$'\t'-Db_lto=true
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
@ -313,7 +308,7 @@ fi
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
meson_configure+=$'\t'--cross-file=.github/emscripten-ghactions.ini
fi
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
if [[ -z ${TPTLIBSREMOTE-} ]]; then
if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
>&2 echo "GITHUB_REPOSITORY_OWNER not set"
@ -323,6 +318,11 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC ==
else
tptlibsremote=$TPTLIBSREMOTE
fi
if [[ "$BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "x86_64-windows-mingw-dynamic linux" ]]; then
>&2 echo "this configuration is not supported in tptlibsdev mode"
touch $ASSET_PATH
exit 0
fi
tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH
if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
rm -rf build-tpt-libs
@ -350,11 +350,7 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC ==
meson_configure+=$'\t'-Dtpt_libs_vtag=$tpt_libs_vtag
fi
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform_jar=$ANDROID_SDK_ROOT/platforms/$android_platform/android.jar
if ! [[ -f $android_platform_jar ]]; then
>&2 echo "$android_platform_jar not found"
exit 1
fi
android_platform=android-30
meson_configure+=$'\t'--cross-file=android/cross/$BSH_HOST_ARCH.ini
cat << ANDROID_INI > .github/android-ghactions.ini
[constants]
@ -365,7 +361,7 @@ andriod_sdk_build_tools = '$ANDROID_SDK_ROOT/build-tools/32.0.0'
# android_ndk_toolchain_prefix comes from the correct cross-file in ./android/cross
android_ndk_toolchain_prefix = android_ndk_toolchain_prefix
android_platform = '$android_platform'
android_platform_jar = '$android_platform_jar'
android_platform_jar = '$ANDROID_SDK_ROOT/platforms/' + android_platform + '/android.jar'
java_runtime_jar = '$JAVA_HOME_8_X64/jre/lib/rt.jar'
[binaries]

View File

@ -1,4 +0,0 @@
set -euo pipefail
IFS=$'\t\n'
gh release create --draft --verify-tag --title $RELEASE_NAME $GITHUB_REF_NAME

View File

@ -2,8 +2,8 @@
prefix = 'x86_64-w64-mingw32'
[binaries]
c = prefix + '-gcc'
cpp = prefix + '-g++'
c = prefix + '-gcc-posix'
cpp = prefix + '-g++-posix'
strip = prefix + '-strip'
windres = prefix + '-windres'

35
.github/prepare.py vendored
View File

@ -17,7 +17,6 @@ match_stable = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)', ref)
match_beta = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)b', ref)
match_snapshot = re.fullmatch(r'refs/tags/snapshot-([0-9]+)', ref)
match_tptlibsdev = re.fullmatch(r'refs/heads/tptlibsdev-(.*)', ref)
match_alljobs = re.fullmatch(r'refs/heads/(.*)-alljobs', ref)
do_release = False
do_priority = 10
if event_name == 'pull_request':
@ -44,8 +43,6 @@ elif match_tptlibsdev:
else:
release_type = 'dev'
release_name = 'dev'
if match_alljobs:
do_priority = 0
do_publish = publish_hostport and do_release
set_output('release_type', release_type)
@ -103,10 +100,10 @@ for arch, platform, libc, statdyn, bplatform, runso
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug', 10 ),
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug', 10 ),
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ),
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ),
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ),
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, True, '.dbg', None, None, 'release', 10 ),
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
@ -114,20 +111,20 @@ for arch, platform, libc, statdyn, bplatform, runso
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release', 10 ),
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
# ( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release', 10 ),
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release', 10 ), # I have no idea how to separate debug info on macos
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release', 10 ), # I have no idea how to separate debug info on macos
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release', 10 ),
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
@ -173,10 +170,6 @@ for arch, platform, libc, statdyn, bplatform, runso
debug_asset_path = f'{app_name_slug}-{arch}.AppImage.dbg'
debug_asset_name = f'{app_name_slug}-{arch}.AppImage.dbg'
starcatcher_name = f'powder-{release_name}-{starcatcher}{suffix}'
msys2_bash = (bplatform == 'windows' and libc == 'mingw')
shell = 'bash'
if msys2_bash:
shell = 'msys2 {0}'
build_matrix.append({
'bsh_build_platform': bplatform, # part of the unique portion of the matrix
'bsh_host_arch': arch, # part of the unique portion of the matrix
@ -185,7 +178,6 @@ for arch, platform, libc, statdyn, bplatform, runso
'bsh_static_dynamic': statdyn, # part of the unique portion of the matrix
'bsh_debug_release': dbgrel, # part of the unique portion of the matrix
'runs_on': runson,
'force_msys2_bash': msys2_bash and 'yes' or 'no',
'package_suffix': suffix,
'package_mode': mode,
'publish': publish and 'yes' or 'no',
@ -196,7 +188,6 @@ for arch, platform, libc, statdyn, bplatform, runso
'debug_asset_path': debug_asset_path,
'debug_asset_name': debug_asset_name,
'job_name': job_name,
'shell': shell,
})
if publish:
publish_matrix.append({

View File

@ -13,8 +13,6 @@ chmod 660 ~/.netrc
mountpoint=ftpmnt
mkdir $mountpoint
curlftpfs "$PUBLISH_HOSTPORT" $mountpoint -o ssl,ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256'
if [[ -z ${PUBLISH_ACCESSCHECK-} ]]; then
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
fi
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
fusermount -u $mountpoint
rmdir $mountpoint

View File

@ -1,12 +0,0 @@
set -euo pipefail
IFS=$'\t\n'
temp=.temp
mkdir $temp
cp $ASSET_PATH $temp/$ASSET_NAME
(
cd $temp
gh release upload $GITHUB_REF_NAME $ASSET_NAME
)
rm -r $temp
echo browser_download_url=https://github.com/$GITHUB_REPOSITORY/releases/download/$GITHUB_REF_NAME/$ASSET_NAME >> $GITHUB_OUTPUT

3
.github/vs-env.sh vendored Executable file → Normal file
View File

@ -24,8 +24,5 @@ IFS=$'\t\n'
for i in $(MSYS_NO_PATHCONV=1 cmd /c "$vs_install_dir\\VC\\Auxiliary\\Build\\vcvarsall.bat" $VS_ENV_PARAMS \& env \& exit /b); do
set +e
export "$i" 2>/dev/null
echo $i | grep ERROR
set -e
done
cl

View File

@ -16,6 +16,7 @@ jobs:
prepare:
runs-on: ubuntu-latest
outputs:
release_upload_url: ${{ steps.create_release.outputs.upload_url }}
do_release: ${{ steps.prepare.outputs.do_release }}
build_matrix: ${{ steps.prepare.outputs.build_matrix }}
publish_matrix: ${{ steps.prepare.outputs.publish_matrix }}
@ -31,8 +32,8 @@ jobs:
do_publish: ${{ steps.prepare.outputs.do_publish }}
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
@ -41,19 +42,16 @@ jobs:
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
GITHUB_REF: ${{ github.ref }}
- if: steps.prepare.outputs.do_publish == 'yes'
run: sudo apt update && sudo apt install curlftpfs && bash -c './.github/starcatcher-publish.sh'
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
PUBLISH_PASSWORD: ${{ secrets.STARCATCHER_PUBLISH_PASSWORD }}
PUBLISH_ACCESSCHECK: yes
- if: steps.prepare.outputs.do_release == 'yes'
id: create_release
uses: LBPHacker/create-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_NAME: ${{ steps.prepare.outputs.release_name }}
run: bash -c './.github/create-release.sh'
with:
tag_name: ${{ github.ref }}
release_name: ${{ steps.prepare.outputs.release_name }}
draft: true
prerelease: false
build:
runs-on: ${{ matrix.runs_on }}
name: ${{ matrix.job_name }}
@ -61,46 +59,13 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- if: matrix.force_msys2_bash == 'yes'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
path-type: strict
cache: true
# this list doesn't have to mirror the one in build.sh perfectly
# but the packages listed here get cached properly and take less time to install
install: >-
git
curl
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-bzip2
mingw-w64-ucrt-x86_64-luajit
mingw-w64-ucrt-x86_64-jsoncpp
mingw-w64-ucrt-x86_64-curl
mingw-w64-ucrt-x86_64-SDL2
mingw-w64-ucrt-x86_64-libpng
mingw-w64-ucrt-x86_64-meson
mingw-w64-ucrt-x86_64-python
mingw-w64-ucrt-x86_64-python-pip
mingw-w64-ucrt-x86_64-fftw
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-7zip
mingw-w64-ucrt-x86_64-jq
patch
- run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- if: matrix.force_msys2_bash != 'yes'
uses: actions/setup-python@v5
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- if: matrix.force_msys2_bash != 'yes'
run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- if: matrix.bsh_build_platform == 'darwin'
run: brew install bash coreutils
- run: bash -c './.github/build.sh'
@ -124,24 +89,30 @@ jobs:
PACKAGE_MODE: ${{ matrix.package_mode }}
ASSET_PATH: ${{ matrix.asset_path }}
DEBUG_ASSET_PATH: ${{ matrix.debug_asset_path }}
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' # TODO-NTL: ship licenses
- uses: LBPHacker/upload-release-asset@v2 # TODO-NTL: ship licenses
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ASSET_PATH: build/${{ matrix.asset_path }}
ASSET_NAME: ${{ matrix.asset_name }}
run: bash -c './.github/upload-release-asset.sh'
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
with:
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
asset_path: build/${{ matrix.asset_path }}
asset_name: ${{ matrix.asset_name }}
asset_content_type: application/zip
- uses: LBPHacker/upload-release-asset@v2
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ASSET_PATH: build/${{ matrix.debug_asset_path }}
ASSET_NAME: ${{ matrix.debug_asset_name }}
run: bash -c './.github/upload-release-asset.sh'
- uses: actions/upload-artifact@v4
with:
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
asset_path: build/${{ matrix.debug_asset_path }}
asset_name: ${{ matrix.debug_asset_name }}
asset_content_type: application/zip
- uses: actions/upload-artifact@v3
if: matrix.artifact == 'yes'
with:
path: build/${{ matrix.asset_path }}
name: ${{ matrix.asset_name }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
with:
path: build/${{ matrix.debug_asset_path }}
@ -155,8 +126,8 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: ${{ matrix.asset_name }}
- run: mv ${{ matrix.asset_path }} ${{ matrix.starcatcher_name }}
@ -173,7 +144,7 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- run: ./.github/starcatcher-release.sh
env:
RELEASE_NAME: ${{ needs.prepare.outputs.release_name }}

View File

@ -1,4 +1,4 @@
The Powder Toy - April 2024
The Powder Toy - January 2023
==========================
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
@ -108,7 +108,6 @@ Controls
| Shift + R | Horizontal mirror for selected area when pasting stamps |
| Ctrl + Shift + R | Vertical mirror for selected area when pasting stamps |
| R | Rotate selected area counterclockwise when pasting stamps |
| F11 | Toggle fullscreen |
Command Line
---------------------------------------------------------------------------

View File

@ -1,272 +0,0 @@
#!/usr/bin/env bash
# mainly based on https://www.sh-zam.com/2019/05/debugging-krita-on-android.html
set -euo pipefail
IFS=$'\n\t'
function get_buildoption() {
jq -r '.[] | select(.name == "'$1'") | .value' < meson-info/intro-buildoptions.json
}
function get_cpp_compiler() {
jq -r '.[] | select(.name == "'$1'") | .target_sources.[] | select(.language == "cpp") | .compiler.[]' < meson-info/intro-targets.json
}
# customize
default_app_id=uk.co.powdertoy.tpt
default_app_exe=powder
default_lldb_server=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/17/lib/linux/aarch64/lldb-server
default_lldb_client=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/lldb.sh
in_build_site=no
if which jq >/dev/null && [[ -f meson-info/intro-buildoptions.json ]]; then
>&2 echo "[+] pwd is a build site, auto-detecting parameters"
in_build_site=yes
default_app_id=$(get_buildoption app_id)
default_app_exe=$(get_buildoption app_exe)
found_lldb=no
compiler_path=$(get_cpp_compiler $default_app_exe)
for line in $compiler_path; do
# iterate over the command array (might be for example [ "ccache", "aarch64-linux-android21-clang++" ])
if (echo $line | grep toolchains && basename $line | grep android | grep clang++) >/dev/null; then
arch=$(basename $line | cut -d '-' -f 1)
default_lldb_server=$(realpath $(dirname $line)/../lib/clang/*/lib/linux/$arch/lldb-server)
default_lldb_client=$(realpath $(dirname $line)/lldb.sh)
found_lldb=yes
fi
done
if [[ $found_lldb != yes ]]; then
>&2 echo "[-] cannot determine LLDB paths from compiler command array:"
for line in $compiler_path; do
>&2 echo "[-] - $line"
done
exit 1
fi
>&2 echo "[+] APP_ID: $default_app_id"
>&2 echo "[+] APP_EXE: $default_app_exe"
>&2 echo "[+] LLDB_SERVER: $default_lldb_server"
>&2 echo "[+] LLDB_CLIENT: $default_lldb_client"
else
>&2 echo "[+] pwd is not a build site, not auto-detecting parameters"
fi
app_id=${APP_ID:-$default_app_id}
app_exe=${APP_EXE:-$default_app_exe}
lldb_server=${LLDB_SERVER:-$default_lldb_server}
lldb_server_port=${LLDB_SERVER_PORT:-9998}
jdb_port=${JDB_PORT:-13456}
lldb_client=${LLDB_CLIENT:-$default_lldb_client}
meson=${MESON:-meson}
adb=${ADB:-adb}
jdb=${JDB:-jdb}
# don't customize unless necessary
app_activity=${APP_ACTIVITY:-PowderActivity}
lldb_server_staging=${LLDB_SERVER_STAGING:-/data/local/tmp/lldb-server}
lldb_server_remote=${LLDB_SERVER_REMOTE:-lldb-server}
pidof_retry_count=${PIDOF_RETRY_COUNT:-20}
pidof_retry_delay=${PIDOF_RETRY_DELAY:-0.1}
function check_which() {
if ! which $1 >/dev/null; then
>&2 echo "[-] can't run $1"
return 1
fi
}
function check_env() {
if ! [[ -f $lldb_server ]]; then
>&2 echo "[-] $lldb_server doesn't exist"
return 1
fi
check_which $lldb_client
check_which $adb
check_which $jdb
}
function check_adb() {
$adb shell whoami >/dev/null
}
function maybe_install_app() {
if [[ $in_build_site != yes ]]; then
return 0
fi
android_keystore=$(get_buildoption android_keystore)
android_keyalias=$(get_buildoption android_keyalias)
if [[ -z ${ANDROID_KEYSTORE_PASS-} ]]; then
>&2 echo "[-] ANDROID_KEYSTORE_PASS not set"
>&2 echo
>&2 cat << HELP
The current directory seems to be a build site, but ANDROID_KEYSTORE_PASS is not set, so android/$app_exe.apk cannot be invoked. If you don't have a keystore yet, create one with:
ANDROID_KEYSTORE_PASS=bagelsbagels keytool -genkey \\
-keystore $android_keystore \\
-alias $android_keyalias \\
-storepass:env ANDROID_KEYSTORE_PASS \\
-keypass:env ANDROID_KEYSTORE_PASS \\
-dname CN=bagels
Then try again with:
ANDROID_KEYSTORE_PASS=bagelsbagels $0
Naturally, replace bagelsbagels with an appropriate password.
HELP
exit 1
fi
>&2 echo "[+] meson compiling android/$app_exe.apk"
if ! $meson compile sign-apk; then
>&2 echo "[-] failed"
return 1
fi
>&2 echo "[+] adb installing android/$app_exe.apk"
if ! $adb install android/$app_exe.apk; then
>&2 echo "[-] failed"
return 1
fi
}
function check_debuggable() {
$adb shell run-as $app_id whoami >/dev/null
}
function find_lldb_server() {
$adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null
}
function kill_lldb_server() {
if ! $adb shell run-as $app_id pkill $lldb_server_remote; then
>&2 echo "[-] failed"
return 1
fi
}
function adb_forward() {
>&2 echo "[+] adb forwarding tcp:$jdb_port jdwp:$pid"
if ! ($adb forward tcp:$jdb_port jdwp:$pid | grep $jdb_port >/dev/null); then
>&2 echo "[+] failed"
return 1
fi
}
function adb_unforward() {
$adb forward --remove tcp:$jdb_port
}
function undo_current_adb_forward() {
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
adb_unforward
}
function maybe_undo_previous_adb_forward() {
if $adb forward --list | grep tcp:$jdb_port; then
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
adb_unforward
fi
}
function maybe_kill_previous_lldb_server() {
if find_lldb_server; then
>&2 echo "[+] killing orphaned $lldb_server_remote"
kill_lldb_server
fi
}
function kill_current_lldb_server() {
>&2 echo "[+] killing $lldb_server_remote"
kill_lldb_server
}
function start_app() {
>&2 echo "[+] starting $app_id/.$app_activity"
set +e
$adb shell am start -D -n "$app_id/.$app_activity" >/dev/null
set -e
local i
local maybe_pid
local pidof_result
for ((i = 0; i <= $pidof_retry_count; i++)); do
set +e
maybe_pid=$($adb shell pidof $app_id)
pidof_result=$?
set -e
if [[ $pidof_result == 0 ]]; then
pid=$maybe_pid
break
fi
sleep $pidof_retry_delay
done
if [[ -z ${pid-} ]]; then
>&2 echo "[-] failed"
return 1
fi
echo $pid
}
function jdb_attach() {
>&2 echo "[+] attaching jdb in the background"
$jdb -attach localhost:$jdb_port >/dev/null 2>/dev/null &
disown $!
# at some point jdb exits because it doesn't have an stdin... fine by me
}
function maybe_deploy_lldb_server() {
if ! $adb shell [[ -f $lldb_server_staging ]]; then
>&2 echo "[+] $lldb_server_remote not present on host, deploying"
if ! ($adb push $lldb_server $lldb_server_staging && $adb shell chmod +x $lldb_server_staging); then
>&2 echo "[-] failed"
fi
fi
}
function start_lldb_server() {
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
>&2 echo "[+] $lldb_server_remote not running on host, starting"
$adb shell run-as $app_id cp $lldb_server_staging /data/data/$app_id/$lldb_server_remote
$adb shell run-as $app_id ./$lldb_server_remote platform --server --listen "*:$lldb_server_port" >/dev/null 2>/dev/null &
disown $!
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
>&2 echo "[-] failed"
return 1
fi
fi
}
function start_lldb() {
local pid=$1
>&2 echo "[+] starting $lldb_client"
local lldb_init=$(mktemp)
cat - << LLDB_INIT > $lldb_init
platform select remote-android
platform connect connect://localhost:$lldb_server_port
attach $pid
continue
LLDB_INIT
local lldb_status
set +e
$lldb_client --source $lldb_init
lldb_status=$?
set -e
>&2 echo "[+] $lldb_client exited with status $lldb_status"
rm $lldb_init
}
check_env
check_adb
maybe_install_app
check_debuggable
maybe_kill_previous_lldb_server
maybe_undo_previous_adb_forward
if [[ ${1-} == clean ]]; then
>&2 echo "[+] done"
exit 0
fi
maybe_deploy_lldb_server
start_lldb_server
pid=$(start_app)
adb_forward
jdb_attach
start_lldb $pid
kill_current_lldb_server
undo_current_adb_forward
>&2 echo "[+] done"

View File

@ -23,3 +23,6 @@ if subprocess.run([
apk_path,
]).returncode:
sys.exit(1)
with open(phony_path, 'w') as _:
pass

View File

@ -44,6 +44,9 @@ if c_compiler.get_id() in [ 'msvc' ]
host_libc = 'msvc'
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
host_libc = 'mingw'
if get_option('b_lto')
warning('mingw does not like static + lto')
endif
elif host_platform in [ 'darwin' ]
host_libc = 'macos'
elif host_platform in [ 'emscripten' ]
@ -66,8 +69,8 @@ if static_variant != 'prebuilt' and host_platform == 'android'
warning('only prebuilt libs are supported for android')
static_variant = 'prebuilt'
endif
if static_variant == 'system' and host_platform == 'windows' and host_libc == 'msvc'
warning('no way to find system libs for msvc on windows')
if static_variant == 'system' and host_platform == 'windows'
warning('no way to find system libs on windows')
static_variant = 'prebuilt'
endif
@ -79,19 +82,20 @@ tpt_libs_static = 'none'
if static_variant == 'prebuilt'
tpt_libs_static = 'static'
endif
if static_variant == 'none' and host_platform == 'windows' and host_libc == 'msvc'
if static_variant == 'none' and host_platform == 'windows'
tpt_libs_static = 'dynamic'
endif
tpt_libs_debug = is_debug ? 'debug' : 'release'
tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static)
tpt_libs_vtag = get_option('tpt_libs_vtag')
if tpt_libs_vtag == ''
tpt_libs_vtag = 'v20240112165024'
tpt_libs_vtag = 'v20231003175140'
endif
if tpt_libs_static != 'none'
if tpt_libs_variant not in [
'x86_64-linux-gnu-static',
'x86_64-windows-mingw-static',
'x86_64-windows-mingw-dynamic',
'x86_64-windows-msvc-static',
'x86_64-windows-msvc-dynamic',
'x86-windows-msvc-static',
@ -325,8 +329,7 @@ if host_platform == 'windows'
if host_arch == 'x86_64'
args_ccomp_win += [ '-DZLIB_WINAPI' ]
endif
endif
if tpt_libs_static == 'dynamic'
else
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
dll_input = input_output_condition[0]
dll_output = input_output_condition[1]
@ -358,12 +361,9 @@ else
ident_platform = 'UNKNOWN'
endif
project_deps = []
data_files = []
powder_deps = []
project_export_dynamic = false
subdir('src')
subdir('resources')
@ -376,25 +376,9 @@ if host_platform == 'emscripten'
'-o', app_exe + '.js', # so we get a .wasm, and a .js
]
endif
if get_option('export_lua_symbols')
if is_static and lua_variant != 'none' and not project_export_dynamic
if host_platform == 'windows'
error('Lua symbols are currently impossible to export correctly on Windows')
elif c_compiler.has_link_argument('-Wl,--export-dynamic-symbol')
project_link_args += [
'-Wl,--export-dynamic-symbol=lua_*',
'-Wl,--export-dynamic-symbol=luaL_*',
'-Wl,--export-dynamic-symbol=luaopen_*',
]
else
warning('your linker does not support -Wl,--export-dynamic-symbol so Meson will be instructed to export all symbols in order to enable loading Lua shared modules, which may blow up the size of the resulting binary')
project_export_dynamic = true
endif
endif
endif
if get_option('build_powder')
powder_deps += project_deps + [
powder_deps += [
threads_dep,
zlib_dep,
png_dep,
@ -426,7 +410,6 @@ if get_option('build_powder')
win_subsystem: is_debug ? 'console' : 'windows',
link_args: project_link_args,
dependencies: powder_deps,
export_dynamic: project_export_dynamic,
install: true,
)
endif
@ -436,7 +419,7 @@ if get_option('build_render')
if host_platform == 'emscripten'
error('render does not target emscripten')
endif
render_deps = project_deps + [
render_deps = [
threads_dep,
zlib_dep,
bzip2_dep,
@ -455,7 +438,6 @@ if get_option('build_render')
cpp_args: project_cpp_args,
link_args: render_link_args,
dependencies: render_deps,
export_dynamic: project_export_dynamic,
)
endif
@ -463,7 +445,7 @@ if get_option('build_font')
if host_platform == 'emscripten'
error('font does not target emscripten')
endif
font_deps = project_deps + [
font_deps = [
threads_dep,
zlib_dep,
png_dep,
@ -479,6 +461,5 @@ if get_option('build_font')
cpp_args: project_cpp_args,
link_args: project_link_args,
dependencies: font_deps,
export_dynamic: project_export_dynamic,
)
endif

View File

@ -40,42 +40,42 @@ option(
'display_version_major',
type: 'integer',
min: 0,
value: 98,
value: 97,
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
)
option(
'display_version_minor',
type: 'integer',
min: 0,
value: 2,
value: 0,
description: 'Minor component of the display version, should more or less map to the PATCH version in semantic versioning'
)
option(
'build_num',
type: 'integer',
min: 0,
value: 365,
value: 356,
description: 'Build number, should be strictly monotonously increasing across public releases'
)
option(
'upstream_version_major',
type: 'integer',
min: 0,
value: 98,
value: 97,
description: 'Major component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_version_minor',
type: 'integer',
min: 0,
value: 2,
value: 0,
description: 'Minor component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_build_num',
type: 'integer',
min: 0,
value: 365,
value: 356,
description: 'Upstream build number, mod owners should not change this but merge upstream changes to it'
)
option(
@ -286,28 +286,3 @@ option(
value: true,
description: 'Enable platform clipboard, allows copying simulation data between different windows'
)
option(
'use_bluescreen',
type: 'combo',
choices: [ 'no', 'yes', 'auto' ],
value: 'auto',
description: 'Show blue error screen upon unhandled signals and exceptions'
)
option(
'windows_icons',
type: 'boolean',
value: true,
description: 'Add icon resources to the executable on Windows'
)
option(
'windows_utf8cp',
type: 'boolean',
value: true,
description: 'Ask Windows nicely for UTF-8 as the codepage'
)
option(
'export_lua_symbols',
type: 'boolean',
value: false,
description: 'Export Lua symbols to enable loading of Lua shared modules'
)

View File

@ -1,137 +1,67 @@
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
static void writeU32LE(uint8_t *dest, uint32_t value)
{
dest[0] = uint8_t( value & 0xFF);
dest[1] = uint8_t((value >> 8) & 0xFF);
dest[2] = uint8_t((value >> 16) & 0xFF);
dest[3] = uint8_t((value >> 24) & 0xFF);
}
static uint32_t readU32BE(const uint8_t *src)
{
return uint32_t(src[3]) |
(uint32_t(src[2]) << 8) |
(uint32_t(src[1]) << 16) |
(uint32_t(src[0]) << 24);
}
#include <cstdint>
int main(int argc, char *argv[])
{
if (argc < 3)
{
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
exit(1);
return 1;
}
auto *outputIcoPath = argv[1];
std::ofstream outputIco(outputIcoPath, std::ios::binary);
if (!outputIco)
{
return 2;
}
auto images = argc - 2;
if (images > 255)
std::vector<char> header(22 + images * 16);
auto *incondir = &header[0];
*reinterpret_cast<uint16_t *>(&incondir[0]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondir[2]) = 1; // icon
*reinterpret_cast<uint16_t *>(&incondir[4]) = uint16_t(images);
std::vector<char> allData;
for (auto i = 0; i < images; ++i)
{
std::cerr << "too many images specified" << std::endl;
exit(1);
auto *inputAnyPath = argv[i + 2];
std::ifstream inputAny(inputAnyPath, std::ios::binary);
std::vector<char> data;
while (true)
{
char ch;
inputAny.read(&ch, 1);
if (inputAny.eof())
{
break;
}
if (!inputAny)
{
return 3;
}
data.push_back(ch);
}
if (*reinterpret_cast<uint64_t *>(&data[0]) != UINT64_C(0x0A1A0A0D474E5089)) // png magic
{
return 5;
}
auto width = uint8_t(data[19]);
auto height = uint8_t(data[23]);
auto *incondirentry = &header[6 + i * 16];
*reinterpret_cast<uint8_t *>(&incondirentry[0]) = width;
*reinterpret_cast<uint8_t *>(&incondirentry[1]) = height;
*reinterpret_cast<uint8_t *>(&incondirentry[2]) = 0; // no color palette
*reinterpret_cast<uint8_t *>(&incondirentry[3]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondirentry[4]) = 1; // 1 color plane
*reinterpret_cast<uint16_t *>(&incondirentry[6]) = 32; // 32 bits per pixel
*reinterpret_cast<uint32_t *>(&incondirentry[8]) = uint32_t(data.size()); // data size
*reinterpret_cast<uint32_t *>(&incondirentry[12]) = uint32_t(header.size() + allData.size()); // data offset
allData.insert(allData.end(), data.begin(), data.end());
}
std::string outputPath = argv[1];
std::ofstream output(outputPath, std::ios::binary);
auto outputFailure = [&outputPath](std::string action) {
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
if (!output)
outputIco.write(&header[0], header.size());
outputIco.write(&allData[0], allData.size());
if (!outputIco)
{
outputFailure("open");
return 4;
}
std::vector<char> header(6 + images * 16, 0);
auto writeHeader = [&header, &output, &outputFailure]() {
output.seekp(0, std::ios_base::beg);
output.write(&header[0], header.size());
if (!output)
{
outputFailure("write");
}
};
writeHeader(); // make space for header
auto *headerU8 = reinterpret_cast<uint8_t *>(&header[0]);
headerU8[2] = 1;
headerU8[4] = images;
for (auto image = 0; image < images; ++image)
{
std::string inputPath = argv[2 + image];
std::ifstream input(inputPath, std::ios::binary);
auto inputFailure = [&inputPath](std::string action) {
std::cerr << "failed to " << action << " " << inputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
auto imageFailure = [&inputPath](std::string failure) {
std::cerr << "failed to process " << inputPath << ": " << failure << std::endl;
exit(1);
};
if (!input)
{
inputFailure("open");
}
std::vector<char> buf;
input.seekg(0, std::ios_base::end);
buf.resize(input.tellg());
input.seekg(0, std::ios_base::beg);
input.read(&buf[0], buf.size());
if (!input)
{
inputFailure("read");
}
auto *bufU8 = reinterpret_cast<uint8_t *>(&buf[0]);
if (buf.size() < 0x21 ||
readU32BE(&bufU8[0]) != UINT32_C(0x89504E47) ||
readU32BE(&bufU8[4]) != UINT32_C(0x0D0A1A0A) ||
bufU8[0x18] != 8 ||
bufU8[0x19] != 6)
{
imageFailure("not a 32bpp RGBA PNG");
}
auto writeOffset = output.tellp();
output.write(&buf[0], buf.size());
if (!output)
{
outputFailure("write");
}
auto width = readU32BE(&bufU8[0x10]);
auto height = readU32BE(&bufU8[0x14]);
if (width == 256)
{
width = 0;
}
if (width > 255)
{
imageFailure("width exceeds U8 limit");
}
if (height == 256)
{
height = 0;
}
if (height > 255)
{
imageFailure("height exceeds U8 limit");
}
auto *entryU8 = headerU8 + 6 + image * 16;
entryU8[0] = width;
entryU8[1] = height;
entryU8[4] = 1;
entryU8[6] = 32;
if (buf.size() > UINT32_MAX)
{
imageFailure("data size exceeds U32 limit");
}
writeU32LE(&entryU8[8], uint32_t(buf.size()));
if (writeOffset > UINT32_MAX)
{
std::cerr << "output data size exceeds U32 limit" << std::endl;
exit(1);
}
writeU32LE(&entryU8[12], uint32_t(writeOffset));
}
writeHeader(); // actually write it out
return 0;
}

Binary file not shown.

View File

@ -32,65 +32,46 @@ else
endif
if host_platform == 'windows'
windows_icons = get_option('windows_icons')
windows_utf8cp = get_option('windows_utf8cp')
rc_conf_depends = []
rc_conf_depend_files = [
'resource.h',
]
icon_exe_ico_path = ''
icon_cps_ico_path = ''
winutf8_xml_path = ''
if windows_icons
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
endforeach
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
endforeach
rc_conf_depends += [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
]
icon_exe_ico_path = join_paths(meson.current_build_dir(), 'icon_exe.ico')
icon_cps_ico_path = join_paths(meson.current_build_dir(), 'icon_cps.ico')
endif
if windows_utf8cp
rc_conf_depend_files += [
'winutf8.xml',
]
winutf8_xml_path = join_paths(meson.current_source_dir(), 'winutf8.xml')
endif
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
endforeach
rc_conf_data = configuration_data()
rc_conf_data.merge_from(conf_data)
rc_conf_data.set('HAVE_ICONS', windows_icons ? 1 : 0)
rc_conf_data.set('HAVE_UTF8CP', windows_utf8cp ? 1 : 0)
rc_conf_data.set('RESOUCE_H', join_paths(meson.current_source_dir(), 'resource.h'))
rc_conf_data.set('WINUTF8_XML', winutf8_xml_path)
rc_conf_data.set('ICON_EXE_ICO', icon_exe_ico_path)
rc_conf_data.set('ICON_CPS_ICO', icon_cps_ico_path)
rc_conf_data.set('WINUTF8_XML', join_paths(meson.current_source_dir(), 'winutf8.xml'))
rc_conf_data.set('ICON_EXE_ICO', join_paths(meson.current_build_dir(), 'icon_exe.ico'))
rc_conf_data.set('ICON_CPS_ICO', join_paths(meson.current_build_dir(), 'icon_cps.ico'))
powder_files += windows_mod.compile_resources(
configure_file(
input: 'powder-res.template.rc',
output: 'powder-res.rc',
configuration: rc_conf_data,
),
depends: rc_conf_depends,
depend_files: rc_conf_depend_files,
depends: [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
],
depend_files: [
'resource.h',
'winutf8.xml',
],
)
elif host_platform == 'darwin'
configure_file(

View File

@ -5,15 +5,9 @@
#include <winver.h>
#include <ntdef.h>
#define HAVE_ICONS @HAVE_ICONS@
#define HAVE_UTF8CP @HAVE_UTF8CP@
#if HAVE_ICONS
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
#endif
#if HAVE_UTF8CP
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@

View File

@ -18,7 +18,6 @@ constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@;
constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@;
constexpr bool PLATFORM_CLIPBOARD = @PLATFORM_CLIPBOARD@;
constexpr bool USE_SYSTEM_CERT_PROVIDER = @USE_SYSTEM_CERT_PROVIDER@;
constexpr bool FFTW_PLAN_MEASURE = @FFTW_PLAN_MEASURE@;
constexpr bool ALLOW_QUIT = @ALLOW_QUIT@;

View File

@ -10,19 +10,12 @@
#include "Format.h"
#include "graphics/Graphics.h"
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat, bool local)
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat)
{
struct tm * timeData;
char buffer[128];
if (local)
{
timeData = localtime(&unixtime);
}
else
{
timeData = gmtime(&unixtime);
}
timeData = localtime(&unixtime);
strftime(buffer, 128, dateFormat.c_str(), timeData);
return ByteString(buffer);

View File

@ -11,7 +11,7 @@ namespace format
{
ByteString URLEncode(ByteString value);
ByteString URLDecode(ByteString value);
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"), bool local = true);
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"));
ByteString UnixtimeToDateMini(time_t unixtime);
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
std::vector<char> PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &);

View File

@ -51,11 +51,6 @@ inline int isign(float i)
return 0;
}
inline int iabs(int i)
{
return i * isign(i);
}
inline unsigned clamp_flt(float f, float min, float max)
{
if (f<min)

View File

@ -22,7 +22,6 @@
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/ErrorMessage.h"
#include "gui/interface/Engine.h"
#include "gui/interface/TextWrapper.h"
#include "Config.h"
#include "SimulationConfig.h"
#include <optional>
@ -30,8 +29,6 @@
#include <iostream>
#include <csignal>
#include <SDL.h>
#include <exception>
#include <cstdlib>
void LoadWindowPosition()
{
@ -100,70 +97,38 @@ void TickClient()
Client::Ref().Tick();
}
static void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
void BlueScreen(String detailMessage)
{
auto &engine = ui::Engine::Ref();
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
auto crashPrevLogPath = ByteString("crash.prev.log");
auto crashLogPath = ByteString("crash.log");
Platform::RenameFile(crashLogPath, crashPrevLogPath, true);
String errorTitle = "ERROR";
String errorDetails = "Details: " + detailMessage;
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
auto versionInfo = ByteString::Build("Version: ", VersionInfo(), "\nTag: ", VCS_TAG).FromUtf8();
StringBuilder crashInfo;
crashInfo << "ERROR - Details: " << detailMessage << "\n";
crashInfo << "An unrecoverable fault has occurred, please report it by visiting the website below\n\n " << SCHEME << SERVER << "\n\n";
crashInfo << "An attempt will be made to save all of this information to " << crashLogPath.FromUtf8() << " in your data folder.\n";
crashInfo << "Please attach this file to your report.\n\n";
crashInfo << "Version: " << VersionInfo().FromUtf8() << "\n";
crashInfo << "Tag: " << VCS_TAG << "\n";
crashInfo << "Date: " << format::UnixtimeToDate(time(NULL), "%Y-%m-%dT%H:%M:%SZ", false).FromUtf8() << "\n";
if (stackTrace)
{
crashInfo << "Stack trace:\n";
for (auto &item : *stackTrace)
{
crashInfo << " - " << item << "\n";
}
}
else
{
crashInfo << "Stack trace not available\n";
}
String errorText = crashInfo.Build();
constexpr auto width = 440;
ui::TextWrapper tw;
tw.Update(errorText, true, width);
engine.g->BlendText(ui::Point((engine.g->Size().X - width) / 2, 80), tw.WrappedText(), 0xFFFFFF_rgb .WithAlpha(0xFF));
auto crashLogData = errorText.ToUtf8();
std::cerr << crashLogData << std::endl;
Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath);
// We use the width of errorHelp to center, but heights of the individual texts for vertical spacing
auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100);
engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorTitle).Y;
engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorDetails).Y;
engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorHelp).Y;
engine.g->BlendText(pos, versionInfo, 0xFFFFFF_rgb .WithAlpha(0xFF));
//Death loop
SDL_Event event;
auto running = true;
while (running)
while(true)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
running = false;
}
}
if(event.type == SDL_QUIT)
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
blit(engine.g->Data());
}
// Don't use Platform::Exit, we're practically zombies at this point anyway.
#if defined(__MINGW32__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
// Come on...
exit(-1);
#else
quick_exit(-1);
#endif
}
static struct
struct
{
int sig;
const char *message;
@ -175,7 +140,7 @@ static struct
{ 0, nullptr },
};
static void SigHandler(int signal)
void SigHandler(int signal)
{
const char *message = "Unknown signal";
for (auto *msg = signalMessages; msg->message; ++msg)
@ -186,29 +151,7 @@ static void SigHandler(int signal)
break;
}
}
BlueScreen(ByteString(message).FromUtf8(), Platform::StackTrace());
}
static void TerminateHandler()
{
ByteString err = "std::terminate called without a current exception";
auto eptr = std::current_exception();
try
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
catch (const std::exception &e)
{
err = "unhandled exception: " + ByteString(e.what());
}
catch (...)
{
err = "unhandled exception not derived from std::exception, cannot determine reason";
}
BlueScreen(err.FromUtf8(), Platform::StackTrace());
BlueScreen(ByteString(message).FromUtf8());
}
constexpr int SCALE_MAXIMUM = 10;
@ -445,20 +388,19 @@ int Main(int argc, char *argv[])
engine.Begin();
engine.SetFastQuit(prefs.Get("FastQuit", true));
engine.TouchUI = prefs.Get("TouchUI", DEFAULT_TOUCH_UI);
engine.windowFrameOps = windowFrameOps;
SDLOpen();
if (Client::Ref().IsFirstRun() && FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsNone)
{
auto guessed = GuessBestScale();
if (engine.windowFrameOps.scale != guessed)
if (windowFrameOps.scale != guessed)
{
engine.windowFrameOps.scale = guessed;
windowFrameOps.scale = guessed;
prefs.Set("Scale", windowFrameOps.scale);
showLargeScreenDialog = true;
}
}
engine.windowFrameOps = windowFrameOps;
SDLOpen();
bool enableBluescreen = USE_BLUESCREEN && !true_arg(arguments["disable-bluescreen"]);
if (enableBluescreen)
@ -468,7 +410,6 @@ int Main(int argc, char *argv[])
{
signal(msg->sig, SigHandler);
}
std::set_terminate(TerminateHandler);
}
if constexpr (X86)
@ -476,97 +417,114 @@ int Main(int argc, char *argv[])
X86KillDenormals();
}
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
auto wrapWithBluescreen = [&]() {
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
auto openArg = arguments["open"];
if (openArg.has_value())
{
if constexpr (DEBUG)
auto openArg = arguments["open"];
if (openArg.has_value())
{
std::cout << "Loading " << openArg.value() << std::endl;
if constexpr (DEBUG)
{
std::cout << "Loading " << openArg.value() << std::endl;
}
if (Platform::FileExists(openArg.value()))
{
try
{
std::vector<char> gameSaveData;
if (!Platform::ReadFile(gameSaveData, openArg.value()))
{
new ErrorMessage("Error", "Could not read file");
}
else
{
auto newFile = std::make_unique<SaveFile>(openArg.value());
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
newFile->SetGameSave(std::move(newSave));
gameController->LoadSaveFile(std::move(newFile));
}
}
catch (std::exception & e)
{
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
}
}
else
{
new ErrorMessage("Error", "Could not open file");
}
}
if (Platform::FileExists(openArg.value()))
auto ptsaveArg = arguments["ptsave"];
if (ptsaveArg.has_value())
{
engine.g->Clear();
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
String loadingText = "Loading save...";
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
blit(engine.g->Data());
try
{
std::vector<char> gameSaveData;
if (!Platform::ReadFile(gameSaveData, openArg.value()))
ByteString saveIdPart;
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
{
new ErrorMessage("Error", "Could not read file");
if (split.Before() != "ptsave")
throw std::runtime_error("Not a ptsave link");
saveIdPart = split.After().SplitBy('#').Before();
}
else
{
auto newFile = std::make_unique<SaveFile>(openArg.value());
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
newFile->SetGameSave(std::move(newSave));
gameController->LoadSaveFile(std::move(newFile));
}
throw std::runtime_error("Invalid save link");
if (!saveIdPart.size())
throw std::runtime_error("No Save ID");
if constexpr (DEBUG)
{
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
}
ByteString saveHistoryPart = "0";
if (auto split = saveIdPart.SplitBy('@'))
{
saveHistoryPart = split.After();
saveIdPart = split.Before();
}
int saveId = saveIdPart.ToNumber<int>();
int saveHistory = saveHistoryPart.ToNumber<int>();
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
}
catch (std::exception & e)
{
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
Platform::MarkPresentable();
}
}
else
{
new ErrorMessage("Error", "Could not open file");
Platform::MarkPresentable();
}
}
auto ptsaveArg = arguments["ptsave"];
if (ptsaveArg.has_value())
MainLoop();
};
if (enableBluescreen)
{
engine.g->Clear();
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
String loadingText = "Loading save...";
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
blit(engine.g->Data());
try
{
ByteString saveIdPart;
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
{
if (split.Before() != "ptsave")
throw std::runtime_error("Not a ptsave link");
saveIdPart = split.After().SplitBy('#').Before();
}
else
throw std::runtime_error("Invalid save link");
if (!saveIdPart.size())
throw std::runtime_error("No Save ID");
if constexpr (DEBUG)
{
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
}
ByteString saveHistoryPart = "0";
if (auto split = saveIdPart.SplitBy('@'))
{
saveHistoryPart = split.After();
saveIdPart = split.Before();
}
int saveId = saveIdPart.ToNumber<int>();
int saveHistory = saveHistoryPart.ToNumber<int>();
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
wrapWithBluescreen();
}
catch (std::exception & e)
catch (const std::exception &e)
{
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
Platform::MarkPresentable();
BlueScreen(ByteString(e.what()).FromUtf8());
}
}
else
{
Platform::MarkPresentable();
wrapWithBluescreen();
}
MainLoop();
Platform::Exit(0);
return 0;
}

View File

@ -75,7 +75,7 @@ int main(int argc, char * argv[])
}
else
{
std::cerr << "path to font.bz2 not supplied" << std::endl;
std::cerr << "path to font.cpp not supplied" << std::endl;
Platform::Exit(1);
}

View File

@ -22,8 +22,6 @@ int main(int argc, char *argv[])
auto inputFilename = ByteString(argv[1]);
auto outputFilename = ByteString(argv[2]) + ".png";
auto simulationData = std::make_unique<SimulationData>();
std::vector<char> fileData;
if (!Platform::ReadFile(fileData, inputFilename))
{
@ -42,6 +40,7 @@ int main(int argc, char *argv[])
throw e;
}
auto simulationData = std::make_unique<SimulationData>();
Simulation * sim = new Simulation();
Renderer * ren = new Renderer(sim);
@ -64,7 +63,6 @@ int main(int argc, char *argv[])
}
else
{
ren->clearScreen();
int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));

View File

@ -349,7 +349,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseDown(mousex, mousey, mouseButton);
engine.onMouseClick(mousex, mousey, mouseButton);
mouseDown = true;
if constexpr (!DEBUG)
@ -365,7 +365,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseUp(mousex, mousey, mouseButton);
engine.onMouseUnclick(mousex, mousey, mouseButton);
mouseDown = false;
if constexpr (!DEBUG)

View File

@ -42,13 +42,10 @@ constexpr float AIR_PLOSS = 0.9999f;
constexpr int NGOL = 24;
enum DefaultBrushes
{
BRUSH_CIRCLE,
BRUSH_SQUARE,
BRUSH_TRIANGLE,
NUM_DEFAULTBRUSHES,
};
constexpr int CIRCLE_BRUSH = 0;
constexpr int SQUARE_BRUSH = 1;
constexpr int TRI_BRUSH = 2;
constexpr int BRUSH_NUM = 3;
//Photon constants
constexpr int SURF_RANGE = 10;

View File

@ -3,6 +3,7 @@
#include "client/http/StartupRequest.h"
#include "ClientListener.h"
#include "Format.h"
#include "MD5.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "client/SaveInfo.h"
@ -10,7 +11,6 @@
#include "common/platform/Platform.h"
#include "common/String.h"
#include "graphics/Graphics.h"
#include "gui/dialogues/ErrorMessage.h"
#include "prefs/Prefs.h"
#include "lua/CommandInterface.h"
#include "Config.h"
@ -117,7 +117,7 @@ void Client::Tick()
{
if (versionCheckRequest->StatusCode() == 618)
{
AddServerNotification({ "Failed to load SSL certificates", ByteString::Build(SCHEME, SERVER, "/FAQ.html") });
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
}
try
{
@ -130,7 +130,7 @@ void Client::Tick()
{
updateInfo = info.updateInfo;
applyUpdateInfo = true;
SetMessageOfTheDay(info.messageOfTheDay);
messageOfTheDay = info.messageOfTheDay;
}
for (auto &notification : info.notifications)
{
@ -141,7 +141,7 @@ void Client::Tick()
{
if (!usingAltUpdateServer)
{
SetMessageOfTheDay(ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8());
messageOfTheDay = ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8();
}
}
versionCheckRequest.reset();
@ -153,7 +153,7 @@ void Client::Tick()
auto info = alternateVersionCheckRequest->Finish();
updateInfo = info.updateInfo;
applyUpdateInfo = true;
SetMessageOfTheDay(info.messageOfTheDay);
messageOfTheDay = info.messageOfTheDay;
for (auto &notification : info.notifications)
{
AddServerNotification(notification);
@ -161,7 +161,7 @@ void Client::Tick()
}
catch (const http::RequestError &ex)
{
SetMessageOfTheDay(ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8());
messageOfTheDay = ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8();
}
alternateVersionCheckRequest.reset();
}
@ -287,27 +287,6 @@ void Client::DeleteStamp(ByteString stampID)
}
}
void Client::RenameStamp(ByteString stampID, ByteString newName)
{
auto oldPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm");
auto newPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, newName, ".stm");
if (Platform::FileExists(newPath))
{
new ErrorMessage("Error renaming stamp", "A stamp with this name already exists.");
return;
}
if (!Platform::RenameFile(oldPath, newPath, false))
{
new ErrorMessage("Error renaming stamp", "Could not rename the stamp.");
return;
}
std::replace(stampIDs.begin(), stampIDs.end(), stampID, newName);
WriteStamps();
}
ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
{
auto now = (uint64_t)time(NULL);
@ -377,6 +356,7 @@ void Client::RescanStamps()
newStampIDs.push_back(stampID);
}
}
auto oldCount = newStampIDs.size();
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
for (auto &stampID : stampFilesSet)
{
@ -388,6 +368,8 @@ void Client::RescanStamps()
}
if (changed)
{
// Move newly discovered stamps to front.
std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end());
stampIDs = newStampIDs;
WriteStamps();
}
@ -441,6 +423,7 @@ std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
{
file->SetLoadingError(err.FromUtf8());
}
commandInterface->SetLastError(err.FromUtf8());
}
return file;
}

View File

@ -91,7 +91,6 @@ public:
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
void DeleteStamp(ByteString stampID);
void RenameStamp(ByteString stampID, ByteString newName);
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
void RescanStamps();
const std::vector<ByteString> &GetStamps() const;

View File

@ -51,18 +51,9 @@ GameSave::GameSave(const std::vector<char> &data, bool newWantAuthors)
void GameSave::MapPalette()
{
int partMap[PT_NUM];
bool ignoreMissingErrors[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = i;
ignoreMissingErrors[i] = false;
}
if (version <= Version(98, 2))
{
ignoreMissingErrors[PT_ICEI] = true;
ignoreMissingErrors[PT_SNOW] = true;
ignoreMissingErrors[PT_RSST] = true;
ignoreMissingErrors[PT_RSSS] = true;
}
auto &sd = SimulationData::CRef();
@ -99,14 +90,12 @@ void GameSave::MapPalette()
}
}
}
auto paletteLookup = [this, &partMap](int type, bool ignoreMissingErrors) {
auto paletteLookup = [this, &partMap](int type) {
if (type > 0 && type < PT_NUM)
{
auto carriedType = partMap[type];
if (!carriedType) // type is not 0 so this shouldn't be 0 either
{
if (ignoreMissingErrors)
return type;
missingElements.ids.insert(type);
}
type = carriedType;
@ -124,7 +113,7 @@ void GameSave::MapPalette()
{
continue;
}
tempPart.type = paletteLookup(tempPart.type, false);
tempPart.type = paletteLookup(tempPart.type);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
@ -132,7 +121,7 @@ void GameSave::MapPalette()
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(&tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> pmapbits;
carriedType = paletteLookup(carriedType, ignoreMissingErrors[tempPart.type]);
carriedType = paletteLookup(carriedType);
*prop = PMAP(extra, carriedType);
}
}
@ -1864,26 +1853,26 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
return;
auto signCount = data[p++];
for (auto i = 0; i < signCount; i++)
for (auto i=0; i<signCount; i++)
{
if (p+6 > dataLength)
throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
{
int x = data[p++];
auto x = data[p++];
x |= ((unsigned)data[p++])<<8;
tempSign.x = x+partP.X;
}
{
int y = data[p++];
auto y = data[p++];
y |= ((unsigned)data[p++])<<8;
tempSign.y = y+partP.Y;
}
{
int ju = data[p++];
auto ju = data[p++];
tempSign.ju = (sign::Justification)ju;
}
{
int l = data[p++];
auto l = data[p++];
if (p+l > dataLength)
throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
if(l>254)
@ -2347,14 +2336,6 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{
RESTRICTVERSION(97, 0);
}
if (particles[i].type == PT_RSST || particles[i].type == PT_RSSS)
{
RESTRICTVERSION(98, 0);
}
if (particles[i].type == PT_ETRD && (particles[i].tmp || particles[i].tmp2))
{
RESTRICTVERSION(98, 0);
}
//Get the pmap entry for the next particle in the same position
i = partsPosLink[i];

231
src/client/MD5.cpp Normal file
View File

@ -0,0 +1,231 @@
// based on public-domain code from Colin Plumb (1993)
#include "MD5.h"
#include <cstring>
static unsigned getu32(const unsigned char *addr)
{
return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0];
}
static void putu32(unsigned data, unsigned char *addr)
{
addr[0] = (unsigned char)data;
addr[1] = (unsigned char)(data >> 8);
addr[2] = (unsigned char)(data >> 16);
addr[3] = (unsigned char)(data >> 24);
}
void md5_init(struct md5_context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
void md5_update(struct md5_context *ctx, unsigned char const *buf, unsigned len)
{
unsigned t;
// update bit count
t = ctx->bits[0];
if ((ctx->bits[0] = (t + ((unsigned)len << 3)) & 0xffffffff) < t)
ctx->bits[1]++; // carry
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
// use leading data to top up the buffer
if (t)
{
unsigned char *p = ctx->in + t;
t = 64-t;
if (len < t)
{
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
md5_transform(ctx->buf, ctx->in);
buf += t;
len -= t;
}
// following 64-byte chunks
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
md5_transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
// save rest of bytes for later
memcpy(ctx->in, buf, len);
}
void md5_final(unsigned char digest[16], struct md5_context *ctx)
{
unsigned count;
unsigned char *p;
// #bytes mod64
count = (ctx->bits[0] >> 3) & 0x3F;
// first char of padding = 0x80
p = ctx->in + count;
*p++ = 0x80;
// calculate # of bytes to pad
count = 64 - 1 - count;
// Pad out to 56 mod 64
if (count < 8)
{
// we need to finish a whole block before padding
memset(p, 0, count);
md5_transform(ctx->buf, ctx->in);
memset(ctx->in, 0, 56);
}
else
{
// just pad to 56 bytes
memset(p, 0, count-8);
}
// append length & final transform
putu32(ctx->bits[0], ctx->in + 56);
putu32(ctx->bits[1], ctx->in + 60);
md5_transform(ctx->buf, ctx->in);
putu32(ctx->buf[0], digest);
putu32(ctx->buf[1], digest + 4);
putu32(ctx->buf[2], digest + 8);
putu32(ctx->buf[3], digest + 12);
memset(&ctx, 0, sizeof(ctx));
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
void md5_transform(unsigned buf[4], const unsigned char inraw[64])
{
unsigned a, b, c, d;
unsigned in[16];
int i;
for (i = 0; i < 16; ++i)
in[i] = getu32 (inraw + 4 * i);
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
static char hexChars[] = "0123456789abcdef";
void md5_ascii(char *result, unsigned char const *buf, unsigned len)
{
struct md5_context md5;
unsigned char hash[16];
int i;
if (len==0)
len = strlen((char *)buf);
md5_init(&md5);
md5_update(&md5, buf, len);
md5_final(hash, &md5);
for (i=0; i<16; i++)
{
result[i*2] = hexChars[(hash[i]>>4)&0xF];
result[i*2+1] = hexChars[hash[i]&0x0F];
}
result[32] = 0;
}

15
src/client/MD5.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
struct md5_context
{
unsigned buf[4];
unsigned bits[2];
unsigned char in[64];
};
void md5_init(struct md5_context *context);
void md5_update(struct md5_context *context, unsigned char const *buf, unsigned len);
void md5_final(unsigned char digest[16], struct md5_context *context);
void md5_transform(unsigned buf[4], const unsigned char in[64]);
void md5_ascii(char *result, unsigned char const *buf, unsigned len);

View File

@ -14,13 +14,4 @@ namespace http
sortByVotes,
sortByDate,
};
enum Period
{
allSaves,
todaySaves,
weekSaves,
monthSaves,
yearSaves,
};
}

View File

@ -5,7 +5,7 @@
namespace http
{
LogoutRequest::LogoutRequest() :
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, true)
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, false)
{
}

View File

@ -1,4 +1,3 @@
#include <ctime>
#include "SearchSavesRequest.h"
#include "Config.h"
#include "client/Client.h"
@ -7,7 +6,7 @@
namespace http
{
static ByteString Url(int start, int count, ByteString query, Period period, Sort sort, Category category)
static ByteString Url(int start, int count, ByteString query, Sort sort, Category category)
{
ByteStringBuilder builder;
builder << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
@ -18,38 +17,6 @@ namespace http
}
query += str;
};
time_t currentTime = time(NULL);
if(period)
{
switch (period)
{
case todaySaves:
currentTime -= 60*60*24; // One day
break;
case weekSaves:
currentTime -= 60*60*24*7; // One week
break;
case monthSaves:
currentTime -= 60*60*24*31; // One month
break;
case yearSaves:
currentTime -= 60*60*24*365; // One year
break;
default:
break;
}
struct tm currentTimeData = *localtime(&currentTime);
ByteStringBuilder afterQuery;
afterQuery << "after:" << currentTimeData.tm_year+1900 << "-" <<
(currentTimeData.tm_mon < 9 ? "0" : "") << currentTimeData.tm_mon+1 << "-" <<
(currentTimeData.tm_mday < 10 ? "0" : "") << currentTimeData.tm_mday;
appendToQuery(afterQuery.Build());
}
switch (sort)
{
case sortByDate:
@ -81,7 +48,7 @@ namespace http
return builder.Build();
}
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category) : APIRequest(Url(start, count, query, period, sort, category), authUse, false)
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category) : APIRequest(Url(start, count, query, sort, category), authUse, false)
{
}

View File

@ -8,7 +8,7 @@ namespace http
class SearchSavesRequest : public APIRequest
{
public:
SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category);
SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category);
std::pair<int, std::vector<std::unique_ptr<SaveInfo>>> Finish();
};

View File

@ -52,10 +52,6 @@ namespace http
return;
}
auto &info = versions[key];
if (info.isNull())
{
return;
}
auto getOr = [&info](ByteString key, int defaultValue) -> int {
if (!info.isMember(key))
{
@ -63,7 +59,7 @@ namespace http
}
return info[key].asInt();
};
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", 0);
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", -1);
if (!updateAvailableFunc(build))
{
return;
@ -72,8 +68,8 @@ namespace http
channel,
ByteString::Build(SCHEME, alternate ? UPDATESERVER : SERVER, info["File"].asString()),
ByteString(info["Changelog"].asString()).FromUtf8(),
getOr("Major", 0),
getOr("Minor", 0),
getOr("Major", -1),
getOr("Minor", -1),
build,
};
};

View File

@ -296,8 +296,13 @@ namespace http
{
{
std::lock_guard lk(sharedStateMx);
// Register new handles first. This always succeeds even if the handle is "failed early" so that
// a single MarkDone call could be issued on all handles further down in this block.
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
for (auto &requestHandle : requestHandlesToRegister)
{
// Must not be present
@ -306,17 +311,6 @@ namespace http
RegisterRequestHandle(requestHandle);
}
requestHandlesToRegister.clear();
// Then unregister done handles. As explained above, registering a new handle may also immediately mark
// it done and we won't be coming back here until Wait() returns, so this has to come second.
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
// Actually unregister handles queued to be unregistered. They can be queued just above, or from another thread.
// Thus, it's ok for them to be in the queue multiple times, but it's not ok to try to unregister them multiple times.
for (auto &requestHandle : requestHandlesToUnregister)
{
auto eraseFrom = std::remove(requestHandles.begin(), requestHandles.end(), requestHandle);

View File

@ -12,7 +12,6 @@ else
if host_platform == 'windows'
use_system_cert_provider = true
client_files += files('WindowsCertProvider.cpp')
powder_deps += c_compiler.find_library('crypt32')
endif
if host_platform == 'android'
use_system_cert_provider = true

View File

@ -1,4 +1,5 @@
client_files = files(
'MD5.cpp',
'SaveFile.cpp',
'SaveInfo.cpp',
'ThumbnailRendererTask.cpp',

View File

@ -1,23 +0,0 @@
#pragma once
#include <functional>
class Defer
{
std::function<void ()> func;
public:
Defer(std::function<void ()> newFunc) : func(newFunc)
{
}
Defer(const Defer &other) = delete;
Defer &operator =(const Defer &other) = delete;
~Defer()
{
if (func)
{
func();
}
}
};

View File

@ -4,6 +4,7 @@
#include <limits>
#include <stdexcept>
#include "common/tpt-thread-local.h"
#include "String.h"
ByteString ConversionError::formatError(ByteString::value_type const *at, ByteString::value_type const *upto)
@ -375,7 +376,7 @@ struct LocaleImpl
static LocaleImpl *getLocaleImpl()
{
thread_local LocaleImpl li;
static THREAD_LOCAL(LocaleImpl, li);
return &li;
}

View File

@ -112,14 +112,6 @@ struct Vec2
);
}
Vec2<T> Min(Vec2<T> other) const
{
return Vec2<T>(
std::min(X, other.X),
std::min(Y, other.Y)
);
}
// Return a rectangle starting at origin, whose dimensions match this vector
template<typename S = T, typename = std::enable_if_t<std::is_integral_v<S>>>
constexpr inline Rect<T> OriginRect() const

View File

@ -1,6 +1,5 @@
#pragma once
#include <memory>
#include <optional>
#include "common/String.h"
class GameSave;
@ -11,8 +10,5 @@ namespace Clipboard
void SetClipboardData(std::unique_ptr<GameSave> data);
const GameSave *GetClipboardData();
void Init();
bool GetEnabled();
void SetEnabled(bool newEnabled);
void RecreateWindow();
std::optional<String> Explanation();
}

View File

@ -62,11 +62,6 @@ namespace Clipboard
}
return gdc;
}
std::optional<String> Explanation() final override
{
return std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> CocoaClipboardFactory()

View File

@ -1,5 +1,4 @@
#include "Dynamic.h"
#include "Clipboard.h"
#include "client/GameSave.h"
#include "prefs/GlobalPrefs.h"
#include "PowderToySDL.h"
@ -24,7 +23,7 @@ namespace Clipboard
};
std::unique_ptr<GameSave> clipboardData;
static std::unique_ptr<ClipboardImpl> clipboard;
std::unique_ptr<ClipboardImpl> clipboard;
void InvokeClipboardSetClipboardData()
{
@ -91,21 +90,8 @@ namespace Clipboard
return clipboardData.get();
}
static bool enabled = false;
void Init()
{
enabled = GlobalPrefs::Ref().Get("NativeClipboard.Enabled", false);
}
bool GetEnabled()
{
return enabled;
}
void SetEnabled(bool newEnabled)
{
enabled = newEnabled;
RecreateWindow();
}
int currentSubsystem;
@ -118,7 +104,7 @@ namespace Clipboard
SDL_GetWindowWMInfo(sdl_window, &info);
clipboard.reset();
currentSubsystem = info.subsystem;
if (enabled)
if (GlobalPrefs::Ref().Get<bool>("NativeClipboard.Enabled", true))
{
for (auto *impl = clipboardImpls; impl->factory; ++impl)
{
@ -131,9 +117,4 @@ namespace Clipboard
}
InvokeClipboardSetClipboardData();
}
std::optional<String> Explanation()
{
return clipboard ? clipboard->Explanation() : std::nullopt;
}
}

View File

@ -3,7 +3,6 @@
#include <vector>
#include <variant>
#include <memory>
#include <optional>
class GameSave;
@ -36,8 +35,6 @@ namespace Clipboard
GetClipboardDataUnknown
>;
virtual GetClipboardDataResult GetClipboardData() = 0;
virtual std::optional<String> Explanation() = 0;
};
extern std::unique_ptr<GameSave> clipboardData;

View File

@ -18,7 +18,6 @@ namespace Clipboard
ByteString inCommand;
ByteString formatsCommand;
ByteString outCommand;
std::optional<String> explanation;
std::optional<int> defaultForSubsystem;
};
std::map<ByteString, Preset> builtInPresets = {
@ -26,14 +25,12 @@ namespace Clipboard
"xclip -selection clipboard -target %s",
"xclip -out -selection clipboard -target TARGETS",
"xclip -out -selection clipboard -target %s",
"Requires the xclip utility to be installed",
SDL_SYSWM_X11,
} },
{ "wl-clipboard", {
"wl-copy --type %s",
"wl-paste --list-types",
"wl-paste --type %s",
"Requires the wl-clipboard utility to be installed",
SDL_SYSWM_WAYLAND,
} },
};
@ -101,7 +98,6 @@ namespace Clipboard
SubstFormat(it->second.inCommand),
SubstFormat(it->second.formatsCommand),
SubstFormat(it->second.outCommand),
it->second.explanation,
};
}
@ -244,12 +240,6 @@ namespace Clipboard
}
return GetClipboardDataChanged{ std::move(*saveDataOpt) };
}
std::optional<String> Explanation() final override
{
auto preset = GetPreset();
return preset ? preset->explanation : std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> ExternalClipboardFactory()

View File

@ -16,21 +16,7 @@ namespace Clipboard
{
}
bool GetEnabled()
{
return false;
}
void SetEnabled(bool)
{
}
void RecreateWindow()
{
}
std::optional<String> Explanation()
{
return std::nullopt;
}
}

View File

@ -217,11 +217,6 @@ namespace Clipboard
auto base = reinterpret_cast<const char *>(data.get());
return GetClipboardDataChanged{ std::vector<char>(base, base + size) };
}
std::optional<String> Explanation() final override
{
return std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> WindowsClipboardFactory()

View File

@ -1,4 +1,4 @@
if platform_clipboard
if get_option('platform_clipboard')
clipboard_impl_factories = []
if host_platform == 'windows'
powder_files += files('Windows.cpp')

View File

@ -1,6 +1,7 @@
common_files += files(
'String.cpp',
'tpt-rand.cpp',
'tpt-thread-local.cpp',
)
subdir('clipboard')

View File

@ -58,7 +58,7 @@ bool ReadFile(std::vector<char> &fileData, ByteString filename)
if (f) f.seekg(0, std::ios::end);
if (f) fileData.resize(f.tellg());
if (f) f.seekg(0);
if (f && fileData.size()) f.read(&fileData[0], fileData.size());
if (f) f.read(&fileData[0], fileData.size());
if (!f)
{
std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;

View File

@ -6,9 +6,6 @@
#include "Config.h"
#include <cstring>
#include <ctime>
#ifdef __FreeBSD__
# include <sys/sysctl.h>
#endif
namespace Platform
{
@ -29,26 +26,7 @@ long unsigned int GetTime()
ByteString ExecutableNameFirstApprox()
{
if (Stat("/proc/self/exe"))
{
return "/proc/self/exe";
}
#ifdef __FreeBSD__
{
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
std::array<char, 1000> buf;
size_t cb = buf.size();
if (!sysctl(mib, 4, &buf[0], &cb, NULL, 0))
{
return ByteString(&buf[0], &buf[0] + cb);
}
}
#endif
return "";
return "/proc/self/exe";
}
bool CanUpdate()

View File

@ -1,9 +1,6 @@
#pragma once
#include "common/String.h"
#include <cstdint>
#include <string>
#include <vector>
#include <optional>
namespace Platform
{
@ -70,8 +67,6 @@ namespace Platform
int InvokeMain(int argc, char *argv[]);
std::optional<std::vector<String>> StackTrace();
void MarkPresentable();
}

View File

@ -2,11 +2,7 @@ common_files += files(
'Common.cpp',
)
if get_option('use_bluescreen') == 'auto'
use_bluescreen = not is_debug
else
use_bluescreen = get_option('use_bluescreen') == 'yes'
endif
use_bluescreen = not is_debug
can_install_enforce_no = false
set_window_icon = false
path_sep_char = '/'
@ -77,19 +73,6 @@ else
'DdirCommon.cpp',
)
endif
bluescreen_export_symbols = false
subdir('stacktrace')
if use_bluescreen
common_files += stacktrace_files
if bluescreen_export_symbols and is_debug
project_export_dynamic = true
endif
else
common_files += files('stacktrace/Null.cpp')
endif
conf_data.set('SET_WINDOW_ICON', set_window_icon.to_string())
conf_data.set('PATH_SEP_CHAR', path_sep_char)
conf_data.set('USE_BLUESCREEN', use_bluescreen.to_string())

View File

@ -1,53 +0,0 @@
#include "common/platform/Platform.h"
#include "common/Defer.h"
#include <execinfo.h>
#include <cstdint>
#include <array>
#include <cxxabi.h>
#include <iostream>
namespace Platform
{
std::optional<std::vector<String>> StackTrace()
{
std::array<void *, 100> buf;
auto used = backtrace(&buf[0], buf.size());
auto *strs = backtrace_symbols(&buf[0], used);
Defer freeStrs([strs]() {
free(strs);
});
std::vector<String> res;
for (auto i = 0; i < used; ++i)
{
if (strs)
{
auto line = ByteString(strs[i]);
if (auto beginSymbolName = line.SplitBy('('))
{
auto afterBeginSymbolName = beginSymbolName.After();
if (auto endSymbolName = afterBeginSymbolName.SplitBy('+'))
{
auto beforeSymbolName = beginSymbolName.Before();
auto symbolName = endSymbolName.Before();
auto afterSymbolName = endSymbolName.After();
int status;
char *demangled = abi::__cxa_demangle(symbolName.c_str(), NULL, NULL, &status);
Defer freeDemangled([demangled]() {
free(demangled);
});
if (!status)
{
line = ByteString::Build(beforeSymbolName, "(", demangled, "+", afterSymbolName);
}
}
}
res.push_back(line.FromUtf8());
}
else
{
res.push_back(String::Build("0x", Format::Hex(), uintptr_t(buf[i])));
}
}
return res;
}
}

View File

@ -1,9 +0,0 @@
#include "common/platform/Platform.h"
namespace Platform
{
std::optional<std::vector<String>> StackTrace()
{
return std::nullopt;
}
}

View File

@ -1,137 +0,0 @@
#include "common/platform/Platform.h"
#include "common/Defer.h"
#include <windows.h>
#pragma pack(push, 8)
#include <dbghelp.h>
#pragma pack(pop)
#include <psapi.h>
#include <array>
#include <mutex>
#include <cstdint>
namespace Platform
{
struct SymbolInfo
{
String name;
uintptr_t displacement;
};
static std::optional<SymbolInfo> GetSymbolInfo(HANDLE process, uintptr_t offset)
{
DWORD64 displacement;
std::array<char, sizeof(SYMBOL_INFOW) + 1000> symbolData{};
auto &symbol = *reinterpret_cast<SYMBOL_INFOW *>(&symbolData[0]);
symbol.SizeOfStruct = sizeof(symbol);
symbol.MaxNameLen = symbolData.size() - sizeof(symbol);
if (SymFromAddrW(process, offset, &displacement, &symbol))
{
return SymbolInfo{ WinNarrow(&symbol.Name[0]).FromUtf8(), uintptr_t(displacement) };
}
return std::nullopt;
}
struct ModuleInfo
{
String name;
uintptr_t displacement;
};
static std::optional<ModuleInfo> GetModuleInfo(HANDLE process, uintptr_t offset)
{
IMAGEHLP_MODULEW64 module{};
module.SizeOfStruct = sizeof(module);
if (SymGetModuleInfoW64(process, offset, &module))
{
auto displacement = offset - uintptr_t(module.BaseOfImage);
return ModuleInfo{ WinNarrow(&module.LoadedImageName[0]).FromUtf8(), displacement };
}
return std::nullopt;
}
std::optional<std::vector<String>> StackTrace()
{
static std::mutex mx;
std::unique_lock lk(mx);
auto process = GetCurrentProcess();
auto thread = GetCurrentThread();
Defer symCleanup([process]() {
SymCleanup(process);
});
SymInitialize(process, NULL, TRUE);
CONTEXT context{};
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
STACKFRAME64 frame{};
DWORD machine;
#if defined(_M_IX86)
machine = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_X64)
machine = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_IA64)
machine = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_ARM64)
machine = IMAGE_FILE_MACHINE_ARM64;
frame.AddrPC.Offset = context.Pc;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Fp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Sp;
frame.AddrStack.Mode = AddrModeFlat;
#else
return std::nullopt;
#endif
std::vector<String> res;
for (auto i = 0; i < 100; ++i)
{
if (!StackWalk64(machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
break;
}
auto offset = uintptr_t(frame.AddrPC.Offset);
StringBuilder addr;
addr << Format::Hex();
if (auto moduleInfo = GetModuleInfo(process, offset))
{
addr << moduleInfo->name << "(";
if (auto symbolInfo = GetSymbolInfo(process, offset))
{
addr << symbolInfo->name << "+0x" << symbolInfo->displacement;
}
else
{
addr << "+0x" << moduleInfo->displacement;
}
addr << ") [0x" << offset << "]";
}
else
{
addr << "0x" << offset;
}
res.push_back(addr.Build());
}
return res;
}
}

View File

@ -1,26 +0,0 @@
if host_platform == 'windows'
if use_bluescreen
project_deps += [
c_compiler.find_library('dbghelp'),
c_compiler.find_library('psapi'),
]
endif
stacktrace_files = files('Windows.cpp')
elif host_platform == 'darwin'
# TODO: good impl; current one is only slightly better than nothing
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
bluescreen_export_symbols = true
elif host_platform == 'linux'
# TODO: again, this is more like "posix" than "linux"
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see above
bluescreen_export_symbols = true
if host_machine.system() in [ 'freebsd' ]
project_deps += [
c_compiler.find_library('execinfo'),
]
endif
else
stacktrace_files = files('Null.cpp')
endif

View File

@ -0,0 +1,61 @@
#include "tpt-thread-local.h"
#ifdef __MINGW32__
# include <pthread.h>
# include <cstdlib>
# include <cassert>
void *ThreadLocalCommon::Get() const
{
// https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
extern ThreadLocalCommon __start_tpt_tls;
extern ThreadLocalCommon __stop_tpt_tls;
static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t key;
struct ThreadLocalEntry
{
void *ptr;
};
auto *staticsBegin = &__start_tpt_tls;
auto *staticsEnd = &__stop_tpt_tls;
pthread_once(&once, []() -> void {
assert(!pthread_key_create(&key, [](void *opaque) -> void {
auto *staticsBegin = &__start_tpt_tls;
auto *staticsEnd = &__stop_tpt_tls;
auto staticsCount = staticsEnd - staticsBegin;
auto *liveObjects = reinterpret_cast<ThreadLocalEntry *>(opaque);
if (liveObjects)
{
for (auto i = 0; i < staticsCount; ++i)
{
if (liveObjects[i].ptr)
{
staticsBegin[i].dtor(liveObjects[i].ptr);
free(liveObjects[i].ptr);
}
}
free(liveObjects);
}
}));
});
auto *liveObjects = reinterpret_cast<ThreadLocalEntry *>(pthread_getspecific(key));
if (!liveObjects)
{
auto staticsCount = staticsEnd - staticsBegin;
liveObjects = reinterpret_cast<ThreadLocalEntry *>(calloc(staticsCount, sizeof(ThreadLocalEntry)));
assert(liveObjects);
assert(!pthread_setspecific(key, reinterpret_cast<void *>(liveObjects)));
}
auto idx = this - staticsBegin;
auto &entry = liveObjects[idx];
if (!entry.ptr)
{
entry.ptr = malloc(staticsBegin[idx].size);
assert(entry.ptr);
staticsBegin[idx].ctor(entry.ptr);
}
return entry.ptr;
}
#endif

View File

@ -0,0 +1,63 @@
#pragma once
#ifdef __MINGW32__
# include <cstddef>
class ThreadLocalCommon
{
ThreadLocalCommon(const ThreadLocalCommon &other) = delete;
ThreadLocalCommon &operator =(const ThreadLocalCommon &other) = delete;
protected:
size_t size;
void (*ctor)(void *);
void (*dtor)(void *);
size_t padding;
void *Get() const;
public:
ThreadLocalCommon() = default;
static constexpr size_t Alignment = 0x20;
};
// * If this fails, add or remove padding fields, possibly change Alignment to a larger power of 2.
static_assert(sizeof(ThreadLocalCommon) == ThreadLocalCommon::Alignment, "fix me");
template<class Type>
class ThreadLocal : public ThreadLocalCommon
{
static void Ctor(void *type)
{
new(type) Type();
}
static void Dtor(void *type)
{
reinterpret_cast<Type *>(type)->~Type();
}
public:
ThreadLocal()
{
// * If this fails, you're out of luck.
static_assert(sizeof(ThreadLocal<Type>) == sizeof(ThreadLocalCommon), "fix me");
size = sizeof(Type);
ctor = Ctor;
dtor = Dtor;
}
Type *operator &() const
{
return reinterpret_cast<Type *>(Get());
}
operator Type &() const
{
return *(this->operator &());
}
};
# define THREAD_LOCAL(Type, tl) const ThreadLocal<Type> tl __attribute__((section("tpt_tls"), aligned(ThreadLocalCommon::Alignment)))
#else
# define THREAD_LOCAL(Type, tl) thread_local Type tl
#endif

View File

@ -183,8 +183,6 @@ void Renderer::render_parts()
gfctx.pipeSubcallTpart = nullptr;
int deca, decr, decg, decb, cola, colr, colg, colb, firea, firer, fireg, fireb, pixel_mode, q, i, t, nx, ny, x, y;
int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0};
int drawing_budget = 1000000; //Serves as an upper bound for costly effects such as SPARK, FLARE and LFLARE
if(!sim)
return;
auto *parts = sim->parts;
@ -635,7 +633,7 @@ void Renderer::render_parts()
{
auto flicker = float(gfctx.rng()%20);
auto gradv = 4*sim->parts[i].life + flicker;
for (x = 0; (gradv>0.5) && (drawing_budget > 0); x++) {
for (x = 0; gradv>0.5; x++) {
auto col = RGBA<uint8_t>(
std::min(0xFF, colr * int(gradv) / 255),
std::min(0xFF, colg * int(gradv) / 255),
@ -646,7 +644,6 @@ void Renderer::render_parts()
AddPixel({ nx, ny+x }, col);
AddPixel({ nx, ny-x }, col);
gradv = gradv/1.5f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_FLARE)
@ -663,13 +660,12 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
for (x = 1; gradv>0.5; x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.2f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_LFLARE)
@ -686,13 +682,12 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
for (x = 1; gradv>0.5; x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.01f;
drawing_budget--;
}
}
if (pixel_mode & EFFECT_GRAVIN)

View File

@ -41,8 +41,6 @@ class Renderer: public RasterDrawMethods<Renderer>
friend struct RasterDrawMethods<Renderer>;
float fireIntensity = 1;
public:
Vec2<int> Size() const
{
@ -99,10 +97,6 @@ public:
void DrawSigns();
void render_gravlensing(const Video &source);
void render_fire();
float GetFireIntensity() const
{
return fireIntensity;
}
void prepare_alpha(int size, float intensity);
void render_parts();
void draw_grav_zones();

View File

@ -11,6 +11,7 @@ constexpr auto VIDYRES = WINDOWH;
void Renderer::RenderBegin()
{
draw_air();
draw_grav();
DrawWalls();
render_parts();
@ -141,10 +142,9 @@ float glow_alphaf[11][11];
float blur_alphaf[7][7];
void Renderer::prepare_alpha(int size, float intensity)
{
fireIntensity = intensity;
//TODO: implement size
int x,y,i,j;
float multiplier = 255.0f*fireIntensity;
float multiplier = 255.0f*intensity;
memset(temp, 0, sizeof(temp));
for (x=0; x<CELL; x++)

View File

@ -6,7 +6,6 @@
#include <SDL.h>
#include "gui/interface/Textbox.h"
#include "gui/interface/ScrollPanel.h"
#include "gui/interface/Label.h"
#include "gui/game/Tool.h"
#include "gui/game/Menu.h"
@ -53,9 +52,6 @@ ElementSearchActivity::ElementSearchActivity(GameController * gameController, st
AddComponent(okButton);
AddComponent(closeButton);
scrollPanel = new ui::ScrollPanel(searchField->Position + Vec2{ 1, searchField->Size.Y+9 }, { searchField->Size.X - 2, Size.Y-(searchField->Position.Y+searchField->Size.Y+6)-23 });
AddComponent(scrollPanel);
searchTools("");
}
@ -63,12 +59,12 @@ void ElementSearchActivity::searchTools(String query)
{
firstResult = NULL;
for (auto &toolButton : toolButtons) {
scrollPanel->RemoveChild(toolButton);
RemoveComponent(toolButton);
delete toolButton;
}
toolButtons.clear();
ui::Point viewPosition = { 1, 1 };
ui::Point viewPosition = searchField->Position + ui::Point(2+0, searchField->Size.Y+2+8);
ui::Point current = ui::Point(0, 0);
String queryLower = query.ToLower();
@ -182,7 +178,7 @@ void ElementSearchActivity::searchTools(String query)
}
toolButtons.push_back(tempButton);
scrollPanel->AddChild(tempButton);
AddComponent(tempButton);
current.X += 31;
@ -190,13 +186,10 @@ void ElementSearchActivity::searchTools(String query)
current.X = 0;
current.Y += 19;
}
}
if (current.X == 0)
{
current.Y -= 19;
if(current.Y + viewPosition.Y + 18 > Size.Y-23)
break;
}
scrollPanel->InnerSize = ui::Point(scrollPanel->Size.X, current.Y + 20);
}
void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool)
@ -223,7 +216,8 @@ void ElementSearchActivity::OnDraw()
g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb);
g->BlendRect(
RectSized(Position + scrollPanel->Position - Vec2{ 1, 1 }, scrollPanel->Size + Vec2{ 2, 2 }),
RectSized(Position + searchField->Position + Vec2{ 0, searchField->Size.Y+8 },
{ searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23 }),
0xFFFFFF_rgb .WithAlpha(180));
if (toolTipPresence && toolTip.length())
{

View File

@ -10,7 +10,6 @@ class GameController;
namespace ui
{
class ScrollPanel;
class Textbox;
}
@ -21,7 +20,6 @@ class ElementSearchActivity: public WindowActivity
std::vector<Tool*> tools;
ui::Textbox * searchField;
std::vector<ToolButton*> toolButtons;
ui::ScrollPanel *scrollPanel = nullptr;
String toolTip;
int toolTipPresence;
bool shiftPressed;

View File

@ -39,7 +39,7 @@ class LoadFilesTask: public Task
bool doWork() override
{
std::vector<ByteString> files = Platform::DirectorySearch(directory, search, { ".cps" });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() > b.ToLower(); });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() < b.ToLower(); });
notifyProgress(-1);
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)

View File

@ -52,14 +52,6 @@ void Brush::InitOutline()
void Brush::SetRadius(ui::Point newRadius)
{
if (newRadius.X < 0)
newRadius.X = 0;
if (newRadius.Y < 0)
newRadius.Y = 0;
if (newRadius.X > 200)
newRadius.X = 200;
if (newRadius.Y > 200)
newRadius.Y = 200;
radius = newRadius;
InitOutline();
}
@ -75,6 +67,14 @@ void Brush::AdjustSize(int delta, bool logarithmic, bool keepX, bool keepY)
newSize = oldSize + ui::Point(delta * std::max(oldSize.X / 5, 1), delta * std::max(oldSize.Y / 5, 1));
else
newSize = oldSize + ui::Point(delta, delta);
if (newSize.X < 0)
newSize.X = 0;
if (newSize.Y < 0)
newSize.Y = 0;
if (newSize.X > 200)
newSize.X = 200;
if (newSize.Y > 200)
newSize.Y = 200;
if (keepY)
SetRadius(ui::Point(newSize.X, oldSize.Y));

View File

@ -90,7 +90,7 @@ GameController::GameController():
gameView->SetDebugHUD(GlobalPrefs::Ref().Get("Renderer.DebugMode", false));
commandInterface = CommandInterface::Create(this, gameModel);
CommandInterface::Create(this, gameModel);
Client::Ref().AddListener(this);
@ -146,7 +146,7 @@ GameController::~GameController()
{
delete *iter;
}
commandInterface.reset();
delete commandInterface;
delete gameModel;
if (gameView->CloseActiveWindow())
{
@ -396,10 +396,6 @@ void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point n
}
activeTool->Strength = gameModel->GetToolStrength();
// This is a joke, the game mvc has to go >_>
activeTool->shiftBehaviour = gameView->ShiftBehaviour();
activeTool->ctrlBehaviour = gameView->CtrlBehaviour();
activeTool->altBehaviour = gameView->AltBehaviour();
if (!held)
activeTool->Draw(sim, cBrush, newPos);
else
@ -448,12 +444,7 @@ static Rect<int> SaneSaveRect(Vec2<int> point1, Vec2<int> point2)
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
{
return StampRegion(point1, point2, gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
}
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2, bool includePressure)
{
auto newSave = gameModel->GetSimulation()->Save(includePressure, SaneSaveRect(point1, point2));
auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), SaneSaveRect(point1, point2));
if(newSave)
{
newSave->paused = gameModel->GetPaused();
@ -552,7 +543,7 @@ bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason
}
break;
case sign::Type::Thread:
Platform::OpenURI(ByteString::Build(SCHEME, SERVER, "/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
Platform::OpenURI(ByteString::Build(SCHEME, "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
break;
case sign::Type::Search:
OpenSearch(str.Substr(3, si.first - 3));
@ -785,7 +776,7 @@ void GameController::ResetSpark()
void GameController::SwitchGravity()
{
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAVMODES;
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAV_MODES;
switch (gameModel->GetSimulation()->gravityMode)
{
@ -806,23 +797,23 @@ void GameController::SwitchGravity()
void GameController::SwitchAir()
{
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIRMODES;
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIR_MODES;
switch (gameModel->GetSimulation()->air->airMode)
{
case AIR_ON:
gameModel->SetInfoTip("Air: On");
break;
case AIR_PRESSUREOFF:
case AIR_PRESSURE_OFF:
gameModel->SetInfoTip("Air: Pressure Off");
break;
case AIR_VELOCITYOFF:
case AIR_VELOCITY_OFF:
gameModel->SetInfoTip("Air: Velocity Off");
break;
case AIR_OFF:
gameModel->SetInfoTip("Air: Off");
break;
case AIR_NOUPDATE:
case AIR_NO_UPDATE:
gameModel->SetInfoTip("Air: No Update");
break;
}
@ -1031,7 +1022,7 @@ int GameController::GetEdgeMode()
void GameController::SetEdgeMode(int edgeMode)
{
if (edgeMode < 0 || edgeMode >= NUM_EDGEMODES)
if (edgeMode < 0 || edgeMode >= NUM_EDGE_MODES)
edgeMode = 0;
gameModel->SetEdgeMode(edgeMode);
@ -1111,16 +1102,11 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool)
gameModel->SetLastTool(tool);
for(int i = 0; i < 3; i++)
{
auto *activeTool = gameModel->GetActiveTool(i);
if (activeTool && activeTool->Identifier == "DEFAULT_WL_GRVTY")
{
if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV))
gameModel->GetRenderer()->gravityZonesEnabled = true;
}
}
if (tool->Identifier == "DEFAULT_UI_PROPERTY")
{
static_cast<PropertyTool *>(tool)->OpenWindow(gameModel->GetSimulation(), nullptr);
}
if(tool->Identifier == "DEFAULT_UI_PROPERTY")
((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation());
if(tool->Identifier == "DEFAULT_UI_ADDLIFE")
{
((GOLTool *)tool)->OpenWindow(gameModel->GetSimulation(), toolSelection);
@ -1140,11 +1126,6 @@ void GameController::SetLastTool(Tool * tool)
gameModel->SetLastTool(tool);
}
Tool *GameController::GetLastTool()
{
return gameModel->GetLastTool();
}
int GameController::GetReplaceModeFlags()
{
return gameModel->GetSimulation()->replaceModeFlags;
@ -1389,7 +1370,7 @@ void GameController::OpenOptions()
void GameController::ShowConsole()
{
if (!console)
console = new ConsoleController(NULL, commandInterface.get());
console = new ConsoleController(NULL, commandInterface);
if (console->GetView() != ui::Engine::Ref().GetWindow())
ui::Engine::Ref().ShowWindow(console->GetView());
}
@ -1716,13 +1697,3 @@ void GameController::RemoveCustomGOLType(const ByteString &identifier)
{
gameModel->RemoveCustomGOLType(identifier);
}
void GameController::BeforeSimDraw()
{
commandInterface->HandleEvent(BeforeSimDrawEvent{});
}
void GameController::AfterSimDraw()
{
commandInterface->HandleEvent(AfterSimDrawEvent{});
}

View File

@ -1,5 +1,4 @@
#pragma once
#include "lua/CommandInterfacePtr.h"
#include "client/ClientListener.h"
#include "client/StartupInfo.h"
#include "gui/interface/Point.h"
@ -29,6 +28,7 @@ class LocalBrowserController;
class SearchController;
class PreviewController;
class RenderController;
class CommandInterface;
class VideoBuffer;
class Tool;
class Menu;
@ -39,8 +39,6 @@ class TagsController;
class ConsoleController;
class GameController: public ClientListener
{
CommandInterfacePtr commandInterface;
private:
bool firstTick;
int foundSignID;
@ -108,7 +106,6 @@ public:
void DrawLine(int toolSelection, ui::Point point1, ui::Point point2);
void DrawFill(int toolSelection, ui::Point point);
ByteString StampRegion(ui::Point point1, ui::Point point2);
ByteString StampRegion(ui::Point point1, ui::Point point2, bool includePressure);
void CopyRegion(ui::Point point1, ui::Point point2);
void CutRegion(ui::Point point1, ui::Point point2);
void Update();
@ -128,7 +125,6 @@ public:
int GetEdgeMode();
void SetEdgeMode(int edgeMode);
void SetDebugFlags(unsigned int flags) { debugFlags = flags; }
unsigned int GetDebugFlags() const { return debugFlags; }
void SetActiveMenu(int menuID);
std::vector<Menu*> GetMenuList();
int GetNumMenus(bool onlyEnabled);
@ -137,7 +133,6 @@ public:
void SetActiveTool(int toolSelection, Tool * tool);
void SetActiveTool(int toolSelection, ByteString identifier);
void SetLastTool(Tool * tool);
Tool *GetLastTool();
int GetReplaceModeFlags();
void SetReplaceModeFlags(int flags);
void SetActiveColourPreset(int preset);
@ -201,7 +196,4 @@ public:
bool GetMouseClickRequired();
void RemoveCustomGOLType(const ByteString &identifier);
void BeforeSimDraw();
void AfterSimDraw();
};

View File

@ -1,30 +1,22 @@
#pragma once
#include "common/String.h"
#include <variant>
#include <cstdint>
enum EventTraits : uint32_t
{
eventTraitNone = UINT32_C(0x00000000),
eventTraitSimRng = UINT32_C(0x00000001),
eventTraitSimGraphics = UINT32_C(0x00000002),
};
struct TextInputEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
String text;
};
struct TextEditingEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
String text;
};
struct KeyEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
int key;
int scan;
bool repeat;
@ -35,17 +27,17 @@ struct KeyEvent
struct KeyPressEvent : public KeyEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
};
struct KeyReleaseEvent : public KeyEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
};
struct MouseDownEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
int x;
int y;
unsigned int button;
@ -53,7 +45,7 @@ struct MouseDownEvent
struct MouseUpEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
int x;
int y;
unsigned int button;
@ -62,7 +54,7 @@ struct MouseUpEvent
struct MouseMoveEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
int x;
int y;
int dx;
@ -71,7 +63,7 @@ struct MouseMoveEvent
struct MouseWheelEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
int x;
int y;
int d;
@ -79,37 +71,27 @@ struct MouseWheelEvent
struct TickEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
};
struct BlurEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
};
struct CloseEvent
{
static constexpr EventTraits traits = eventTraitNone;
static constexpr bool simEvent = false;
};
struct BeforeSimEvent
{
static constexpr EventTraits traits = eventTraitSimRng;
static constexpr bool simEvent = true;
};
struct AfterSimEvent
{
static constexpr EventTraits traits = eventTraitSimRng;
};
struct BeforeSimDrawEvent
{
static constexpr EventTraits traits = eventTraitSimGraphics;
};
struct AfterSimDrawEvent
{
static constexpr EventTraits traits = eventTraitSimGraphics;
static constexpr bool simEvent = true;
};
using GameControllerEvent = std::variant<
@ -125,7 +107,5 @@ using GameControllerEvent = std::variant<
BlurEvent,
CloseEvent,
BeforeSimEvent,
AfterSimEvent,
BeforeSimDrawEvent,
AfterSimDrawEvent
AfterSimEvent
>;

View File

@ -54,16 +54,16 @@ GameModel::GameModel():
colour(255, 0, 0, 255),
edgeMode(EDGE_VOID),
ambientAirTemp(R_TEMP + 273.15f),
decoSpace(DECOSPACE_SRGB)
decoSpace(0)
{
sim = new Simulation();
sim->useLuaCallbacks = true;
ren = new Renderer(sim);
activeTools = &regularToolset[0];
activeTools = regularToolset;
std::fill(decoToolset.begin(), decoToolset.end(), nullptr);
std::fill(regularToolset.begin(), regularToolset.end(), nullptr);
std::fill(decoToolset, decoToolset+4, (Tool*)NULL);
std::fill(regularToolset, regularToolset+4, (Tool*)NULL);
//Default render prefs
ren->SetRenderMode({
@ -93,7 +93,7 @@ GameModel::GameModel():
ren->decorations_enable = prefs.Get("Renderer.Decorations", true);
//Load config into simulation
edgeMode = prefs.Get("Simulation.EdgeMode", NUM_EDGEMODES, EDGE_VOID);
edgeMode = prefs.Get("Simulation.EdgeMode", (int)EDGE_VOID);
sim->SetEdgeMode(edgeMode);
ambientAirTemp = float(R_TEMP) + 273.15f;
{
@ -104,9 +104,9 @@ GameModel::GameModel():
}
}
sim->air->ambientAirTemp = ambientAirTemp;
decoSpace = prefs.Get("Simulation.DecoSpace", NUM_DECOSPACES, DECOSPACE_SRGB);
decoSpace = prefs.Get("Simulation.DecoSpace", 0); // TODO: DecoSpace enum
sim->SetDecoSpace(decoSpace);
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", NUM_GRAVMODES, GRAV_VERTICAL);
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", 0); // TODO: NewtonianGravity enum
if (ngrav_enable)
sim->grav->start_grav_async();
sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum
@ -228,7 +228,7 @@ void GameModel::BuildMenus()
if(activeMenu != -1)
lastMenu = activeMenu;
std::array<ByteString, NUM_TOOLINDICES> activeToolIdentifiers;
ByteString activeToolIdentifiers[4];
if(regularToolset[0])
activeToolIdentifiers[0] = regularToolset[0]->Identifier;
if(regularToolset[1])
@ -881,17 +881,17 @@ void GameModel::SetActiveMenu(int menuID)
if(menuID == SC_DECO)
{
if(activeTools != &decoToolset[0])
if(activeTools != decoToolset)
{
activeTools = &decoToolset[0];
activeTools = decoToolset;
notifyActiveToolsChanged();
}
}
else
{
if(activeTools != &regularToolset[0])
if(activeTools != regularToolset)
{
activeTools = &regularToolset[0];
activeTools = regularToolset;
notifyActiveToolsChanged();
}
}
@ -1712,7 +1712,7 @@ void GameModel::BeforeSim()
{
if (!sim->sys_pause || sim->framerender)
{
CommandInterface::Ref().HandleEvent(BeforeSimEvent{});
commandInterface->HandleEvent(BeforeSimEvent{});
}
sim->BeforeSim();
}
@ -1720,5 +1720,5 @@ void GameModel::BeforeSim()
void GameModel::AfterSim()
{
sim->AfterSim();
CommandInterface::Ref().HandleEvent(AfterSimEvent{});
commandInterface->HandleEvent(AfterSimEvent{});
}

View File

@ -6,9 +6,6 @@
#include <deque>
#include <memory>
#include <optional>
#include <array>
constexpr auto NUM_TOOLINDICES = 4;
class Menu;
class Tool;
@ -75,8 +72,8 @@ private:
std::unique_ptr<SaveFile> currentFile;
Tool * lastTool;
Tool ** activeTools;
std::array<Tool *, NUM_TOOLINDICES> decoToolset;
std::array<Tool *, NUM_TOOLINDICES> regularToolset;
Tool * decoToolset[4];
Tool * regularToolset[4];
User currentUser;
float toolStrength;
std::deque<HistoryEntry> history;
@ -195,10 +192,6 @@ public:
Brush &GetBrush();
Brush *GetBrushByID(int i);
int GetBrushID();
int BrushListSize() const
{
return int(brushList.size());
}
void SetBrushID(int i);
void SetVote(int direction);

View File

@ -87,7 +87,7 @@ public:
}
}
}
void OnMouseClick(int x, int y, unsigned int button) override
void OnMouseUnclick(int x, int y, unsigned int button) override
{
if(isButtonDown)
{
@ -96,7 +96,7 @@ public:
else if(rightDown)
DoRightAction();
}
ui::Button::OnMouseClick(x, y, button);
ui::Button::OnMouseUnclick(x, y, button);
}
void OnMouseHover(int x, int y) override
@ -120,18 +120,15 @@ public:
toolTip = newToolTip1;
toolTip2 = newToolTip2;
}
void OnMouseDown(int x, int y, unsigned int button) override
void OnMouseClick(int x, int y, unsigned int button) override
{
ui::Button::OnMouseDown(x, y, button);
if (MouseDownInside)
{
rightDown = false;
leftDown = false;
if(x - Position.X >= splitPosition)
rightDown = true;
else if(x - Position.X < splitPosition)
leftDown = true;
}
ui::Button::OnMouseClick(x, y, button);
rightDown = false;
leftDown = false;
if(x >= splitPosition)
rightDown = true;
else if(x < splitPosition)
leftDown = true;
}
void DoRightAction()
{
@ -1055,17 +1052,10 @@ void GameView::updateToolButtonScroll()
{
for (auto *button : toolButtons)
{
auto inside = button->Position.X < x && button->Position.X + button->Size.X > x;
if (inside && !button->MouseInside)
{
button->MouseInside = true;
if (button->Position.X < x && button->Position.X + button->Size.X > x)
button->OnMouseEnter(x, y);
}
if (!inside && button->MouseInside)
{
button->MouseInside = false;
else
button->OnMouseLeave(x, y);
}
}
}
}
@ -2096,11 +2086,6 @@ void GameView::UpdateDrawMode()
drawMode = DrawLine;
else
drawMode = DrawPoints;
// TODO: have tools decide on draw mode
if (c->GetLastTool() && c->GetLastTool()->Identifier == "DEFAULT_UI_SAMPLE")
{
drawMode = DrawPoints;
}
}
void GameView::UpdateToolStrength()
@ -2134,8 +2119,6 @@ void GameView::OnDraw()
auto &sd = SimulationData::Ref();
std::unique_lock lk(sd.elementGraphicsMx);
ren->clearScreen();
ren->draw_air();
c->BeforeSimDraw();
ren->RenderBegin();
ren->SetSample(c->PointTranslate(currentMouse));
if (showBrush && selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES)))
@ -2241,7 +2224,6 @@ void GameView::OnDraw()
}
}
c->AfterSimDraw();
ren->RenderEnd();
std::copy_n(ren->Data(), ren->Size().X * ren->Size().Y, g->Data());
@ -2381,7 +2363,7 @@ void GameView::OnDraw()
if (type == PT_CRAY || type == PT_DRAY || type == PT_EXOT || type == PT_LIGH || type == PT_SOAP || type == PT_TRON
|| type == PT_VIBR || type == PT_VIRS || type == PT_WARP || type == PT_LCRY || type == PT_CBNW || type == PT_TSNS
|| type == PT_DTEC || type == PT_LSNS || type == PT_PSTN || type == PT_LDTC || type == PT_VSNS || type == PT_LITH
|| type == PT_CONV || type == PT_ETRD)
|| type == PT_CONV)
sampleInfo << ", Tmp2: " << sample.particle.tmp2;
sampleInfo << ", Pressure: " << sample.AirPressure;

View File

@ -76,7 +76,7 @@ inline ByteString IntroText()
}
else
{
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://" << SERVER << "/Register.html\n";
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://powdertoy.co.uk/Register.html\n";
}
sb << "\n\bt" << VersionInfo();
return sb.Build();

View File

@ -28,7 +28,6 @@
class PropertyWindow: public ui::Window
{
void HandlePropertyChange();
std::optional<std::pair<int, String>> TakePropertyFrom(const Particle *takePropertyFrom) const;
public:
ui::DropDown * property;
@ -37,7 +36,7 @@ public:
Simulation *sim;
std::vector<StructProperty> properties;
std::optional<PropertyTool::Configuration> configuration;
PropertyWindow(PropertyTool *tool_, Simulation *sim, const Particle *takePropertyFrom);
PropertyWindow(PropertyTool *tool_, Simulation *sim);
void SetProperty();
void CheckProperty();
void Update();
@ -47,7 +46,7 @@ public:
virtual ~PropertyWindow() {}
};
PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation *sim_, const Particle *takePropertyFrom):
PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation *sim_):
ui::Window(ui::Point(-1, -1), ui::Point(200, 87)),
tool(tool_),
sim(sim_)
@ -82,67 +81,23 @@ sim(sim_)
property->AddOption(std::pair<String, int>(properties[i].Name.FromAscii(), i));
}
auto &prefs = GlobalPrefs::Ref();
property->SetOption(prefs.Get("Prop.Type", 0));
textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]");
textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
textField->SetText(prefs.Get("Prop.Value", String("")));
textField->SetActionCallback({ [this]() {
Update();
} });
AddComponent(textField);
{
auto &prefs = GlobalPrefs::Ref();
auto propertyIndex = prefs.Get("Prop.Type", 0);
auto valueString = prefs.Get("Prop.Value", String(""));
auto taken = TakePropertyFrom(takePropertyFrom);
if (taken)
{
std::tie(propertyIndex, valueString) = *taken;
}
property->SetOption(propertyIndex);
textField->SetText(valueString);
}
FocusComponent(textField);
Update();
MakeActiveWindow();
}
std::optional<std::pair<int, String>> PropertyWindow::TakePropertyFrom(const Particle *takePropertyFrom) const
{
auto toolConfiguration = tool->GetConfiguration();
if (!toolConfiguration || !takePropertyFrom)
{
return {};
}
auto *prop = reinterpret_cast<const char *>(takePropertyFrom) + toolConfiguration->prop.Offset;
auto takeValue = [this, toolConfiguration](auto &value) -> std::optional<std::pair<int, String>> {
auto it = std::find(properties.begin(), properties.end(), toolConfiguration->prop);
if (it != properties.end())
{
return std::pair{ int(it - properties.begin()), String::Build(value) };
}
return {};
};
switch (toolConfiguration->prop.Type)
{
case StructProperty::Float:
return takeValue(*reinterpret_cast<const float *>(prop));
case StructProperty::ParticleType:
case StructProperty::Integer:
return takeValue(*reinterpret_cast<const int *>(prop));
case StructProperty::UInteger:
return takeValue(*reinterpret_cast<const unsigned int *>(prop));
default:
break;
}
return {};
}
void PropertyWindow::HandlePropertyChange()
{
FocusComponent(textField);
@ -332,9 +287,9 @@ void PropertyWindow::OnKeyPress(int key, int scan, bool repeat, bool shift, bool
}
}
void PropertyTool::OpenWindow(Simulation *sim, const Particle *takePropertyFrom)
void PropertyTool::OpenWindow(Simulation *sim)
{
new PropertyWindow(this, sim, takePropertyFrom);
new PropertyWindow(this, sim);
}
void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
@ -370,6 +325,39 @@ void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
}
}
void PropertyTool::UpdateConfigurationFromParticle(const Particle &part)
{
auto configuration = GetConfiguration();
switch (configuration->prop.Type)
{
case StructProperty::Float:
{
auto value = *((float*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
case StructProperty::ParticleType:
case StructProperty::Integer:
{
auto value = *((int*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
case StructProperty::UInteger:
{
auto value = *((unsigned int*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
default:
break;
}
SetConfiguration(configuration);
}
void PropertyTool::Draw(Simulation *sim, Brush const &cBrush, ui::Point position)
{
for (ui::Point off : cBrush)

View File

@ -40,11 +40,11 @@ void SampleTool::Draw(Simulation * sim, Brush const &brush, ui::Point position)
}
if (part)
{
if (shiftBehaviour)
auto *propTool = static_cast<PropertyTool *>(gameModel.GetToolFromIdentifier("DEFAULT_UI_PROPERTY"));
if (gameModel.GetActiveTool(0) == propTool && propTool->GetConfiguration())
{
auto *propTool = static_cast<PropertyTool *>(gameModel.GetToolFromIdentifier("DEFAULT_UI_PROPERTY"));
gameModel.SetActiveTool(0, propTool);
propTool->OpenWindow(gameModel.GetSimulation(), part);
propTool->UpdateConfigurationFromParticle(*part);
gameModel.SetActiveTool(0, propTool); // trigger change so Renderer::findingElement is updated
}
else if (part->type == PT_LIFE)
{

View File

@ -25,9 +25,6 @@ public:
RGB<uint8_t> const Colour;
bool const Blocky;
float Strength = 1.0f;
bool shiftBehaviour = false;
bool ctrlBehaviour = false;
bool altBehaviour = false;
Tool(int id, String name, String description,
RGB<uint8_t> colour, ByteString identifier, std::unique_ptr<VideoBuffer> (*textureGen)(int, Vec2<int>) = NULL, bool blocky = false
@ -113,9 +110,6 @@ public:
};
private:
void SetProperty(Simulation *sim, ui::Point position);
void SetConfiguration(std::optional<Configuration> newConfiguration);
GameModel &gameModel;
std::optional<Configuration> configuration;
@ -132,13 +126,17 @@ public:
virtual ~PropertyTool()
{}
void OpenWindow(Simulation *sim, const Particle *takePropertyFrom);
void OpenWindow(Simulation *sim);
void SetProperty(Simulation *sim, ui::Point position);
void UpdateConfigurationFromParticle(const Particle &part);
void Click(Simulation * sim, Brush const &brush, ui::Point position) override { }
void Draw(Simulation *sim, Brush const &brush, ui::Point position) override;
void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override;
void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override;
void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override;
void SetConfiguration(std::optional<Configuration> newConfiguration);
std::optional<Configuration> GetConfiguration() const
{
return configuration;

View File

@ -16,15 +16,12 @@ ToolButton::ToolButton(ui::Point position, ui::Point size, String text, ByteStri
Component::TextPosition(buttonDisplayText);
}
void ToolButton::OnMouseDown(int x, int y, unsigned int button)
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
{
if (MouseDownInside)
{
isButtonDown = true;
}
isButtonDown = true;
}
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
void ToolButton::OnMouseUnclick(int x, int y, unsigned int button)
{
if(isButtonDown)
{

View File

@ -9,7 +9,7 @@ class ToolButton: public ui::Button
ByteString toolIdentifier;
public:
ToolButton(ui::Point position, ui::Point size, String text, ByteString toolIdentifier, String toolTip = String());
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void Draw(const ui::Point& screenPos) override;

View File

@ -51,7 +51,7 @@ void AvatarButton::Draw(const Point& screenPos)
}
}
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
void AvatarButton::OnMouseUnclick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -70,19 +70,16 @@ void AvatarButton::OnContextMenuAction(int item)
//Do nothing
}
void AvatarButton::OnMouseDown(int x, int y, unsigned int button)
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
{
if (MouseDownInside)
if(button == SDL_BUTTON_RIGHT)
{
if(button == SDL_BUTTON_RIGHT)
{
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
}
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
}
}

View File

@ -30,7 +30,7 @@ public:
virtual ~AvatarButton() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;

View File

@ -86,7 +86,7 @@ void Button::Draw(const Point& screenPos)
if (Enabled)
{
if ((isButtonDown && MouseDownInside) || (isTogglable && toggle))
if (isButtonDown || (isTogglable && toggle))
{
textColour = Appearance.TextActive;
borderColour = Appearance.BorderActive;
@ -140,7 +140,7 @@ void Button::Draw(const Point& screenPos)
}
}
void Button::OnMouseClick(int x, int y, unsigned int button)
void Button::OnMouseUnclick(int x, int y, unsigned int button)
{
if(button == 1)
{
@ -171,20 +171,17 @@ void Button::OnMouseUp(int x, int y, unsigned int button)
isAltButtonDown = false;
}
void Button::OnMouseDown(int x, int y, unsigned int button)
void Button::OnMouseClick(int x, int y, unsigned int button)
{
if (MouseDownInside)
if(!Enabled)
return;
if(button == 1)
{
if(!Enabled)
return;
if(button == 1)
{
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
}
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
}
}

View File

@ -18,7 +18,7 @@ public:
virtual ~Button() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;

View File

@ -124,21 +124,17 @@ void Component::SetParent(Panel* new_parent)
this->_parent = new_parent;
}
Point Component::GetContainerPos()
Point Component::GetScreenPos()
{
Point newPos(0,0);
if(GetParentWindow())
newPos += GetParentWindow()->Position;
if(GetParent())
newPos += GetParent()->Position + GetParent()->ViewportPosition;
newPos += Position;
return newPos;
}
Point Component::GetScreenPos()
{
return GetContainerPos() + Position;
}
Graphics * Component::GetGraphics()
{
return parentstate_->GetGraphics();
@ -189,7 +185,11 @@ void Component::OnMouseHover(int localx, int localy)
{
}
void Component::OnMouseMoved(int localx, int localy)
void Component::OnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy)
{
}
@ -201,6 +201,10 @@ void Component::OnMouseLeave(int localx, int localy)
{
}
void Component::OnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Component::OnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -43,8 +43,6 @@ namespace ui
bool Enabled;
bool Visible;
bool DoesTextInput;
bool MouseInside;
bool MouseDownInside;
ui::Appearance Appearance;
//virtual void SetAppearance(ui::Appearance);
@ -53,7 +51,6 @@ namespace ui
void Refresh();
Point GetContainerPos();
Point GetScreenPos();
/* See the parent of this component.
@ -97,8 +94,20 @@ namespace ui
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMoved(int localx, int localy);
virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
///
// Called: When the mouse moves.
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMovedInside(int localx, int localy, int dx, int dy);
///
// Called: When the mouse moves on top of the item.
@ -137,7 +146,7 @@ namespace ui
virtual void OnMouseUp(int x, int y, unsigned button);
///
// Called: When a mouse button is pressed and then released on top of the item.
// Called: When a mouse button is pressed on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
@ -145,6 +154,15 @@ namespace ui
///
virtual void OnMouseClick(int localx, int localy, unsigned button);
///
// Called: When a mouse button is released on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
// button: The button that is being released.
///
virtual void OnMouseUnclick(int localx, int localy, unsigned button);
///
// Called: When the mouse wheel moves/changes.
// Params:

View File

@ -133,7 +133,7 @@ void DirectionSelector::Draw(const ui::Point& screenPos)
g->BlendEllipse(center + value.offset, { handleRadius, handleRadius }, borderColor);
}
void DirectionSelector::OnMouseMoved(int x, int y)
void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
{
if (mouseDown)
{
@ -142,14 +142,11 @@ void DirectionSelector::OnMouseMoved(int x, int y)
CheckHovering(x, y);
}
void DirectionSelector::OnMouseDown(int x, int y, unsigned button)
void DirectionSelector::OnMouseClick(int x, int y, unsigned button)
{
if (MouseDownInside)
{
mouseDown = true;
SetPositionAbs({ x - Position.X, y - Position.Y });
CheckHovering(x - Position.X, y - Position.Y);
}
mouseDown = true;
SetPositionAbs({ x, y });
CheckHovering(x, y);
}
void DirectionSelector::OnMouseUp(int x, int y, unsigned button)

View File

@ -78,8 +78,8 @@ public:
void SetValues(float x, float y);
void Draw(const ui::Point& screenPos) override;
void OnMouseMoved(int x, int y) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseMoved(int x, int y, int dx, int dy) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned button) override;
inline void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }
inline void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }

View File

@ -15,8 +15,7 @@ class DropDownWindow : public ui::Window
public:
DropDownWindow(DropDown * dropDown):
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - (dropDown->optionIndex*16 < dropDown->GetScreenPos().Y ? dropDown->optionIndex*16 : 0)),
ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - dropDown->optionIndex * 16), ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
dropDown(dropDown),
appearance(dropDown->Appearance)
{

View File

@ -83,8 +83,6 @@ void Engine::ShowWindow(Window * window)
{
window->Position.Y = (g->Size().Y - window->Size.Y) / 2;
}
window->Size = window->Size.Min(g->Size());
window->Position = window->Position.Clamp(RectBetween<int>({0, 0}, g->Size()));
/*if(window->Position.Y > 0)
{
windowTargetPosition = window->Position;
@ -270,14 +268,14 @@ void Engine::onTextEditing(String text, int start)
}
}
void Engine::onMouseDown(int x, int y, unsigned button)
void Engine::onMouseClick(int x, int y, unsigned button)
{
mouseb_ |= button;
if (state_ && !ignoreEvents)
state_->DoMouseDown(x, y, button);
}
void Engine::onMouseUp(int x, int y, unsigned button)
void Engine::onMouseUnclick(int x, int y, unsigned button)
{
mouseb_ &= ~button;
if (state_ && !ignoreEvents)

View File

@ -30,8 +30,8 @@ namespace ui
void initialMouse(int x, int y);
void onMouseMove(int x, int y);
void onMouseDown(int x, int y, unsigned button);
void onMouseUp(int x, int y, unsigned button);
void onMouseClick(int x, int y, unsigned button);
void onMouseUnclick(int x, int y, unsigned button);
void onMouseWheel(int x, int y, int delta);
void onKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);

View File

@ -88,27 +88,24 @@ void Label::OnContextMenuAction(int item)
}
}
void Label::OnMouseDown(int x, int y, unsigned button)
void Label::OnMouseClick(int x, int y, unsigned button)
{
if (MouseDownInside)
if(button == SDL_BUTTON_RIGHT)
{
if(button == SDL_BUTTON_RIGHT)
if (menu)
{
if (menu)
{
menu->Show(GetContainerPos() + ui::Point(x, y));
}
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - Position.X - tp.X, y - Position.Y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
}
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - tp.X, y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
updateSelection();
}
updateSelection();
}
}
@ -146,7 +143,7 @@ void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bo
}
}
void Label::OnMouseMoved(int localx, int localy)
void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
{
if (selecting)
{

View File

@ -58,9 +58,9 @@ namespace ui
void SetTextColour(Colour textColour) { this->textColour = textColour; }
void OnContextMenuAction(int item) override;
virtual void OnMouseDown(int x, int y, unsigned button) override;
virtual void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseMoved(int localx, int localy) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
void Draw(const Point& screenPos) override;
void Tick(float dt) override;

View File

@ -10,7 +10,8 @@ using namespace ui;
Panel::Panel(Point position, Point size):
Component(position, size),
InnerSize(size),
ViewportPosition(0, 0)
ViewportPosition(0, 0),
mouseInside(false)
{
}
@ -26,8 +27,6 @@ void Panel::AddChild(Component* c)
{
c->SetParent(this);
c->SetParentWindow(this->GetParentWindow());
c->MouseInside = false;
c->MouseDownInside = false;
}
int Panel::GetChildCount()
@ -109,40 +108,43 @@ void Panel::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl,
void Panel::OnMouseClick(int localx, int localy, unsigned button)
{
XOnMouseClick(localx, localy, button);
bool childclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
childclicked = true;
GetParentWindow()->FocusComponent(children[i]);
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
//if a child wasn't clicked, send click to ourself
if(!childclicked)
{
XOnMouseClick(localx, localy, button);
GetParentWindow()->FocusComponent(this);
}
}
void Panel::OnMouseDown(int x, int y, unsigned button)
{
if (MouseDownInside)
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
auto localx = x - Position.X;
auto localy = y - Position.Y;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
GetParentWindow()->FocusComponent(children[i]);
children[i]->MouseDownInside = true;
break;
}
}
}
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseDown(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
}
if(children[i]->Enabled)
children[i]->OnMouseDown(x, y, button);
}
}
@ -153,14 +155,12 @@ void Panel::OnMouseHover(int localx, int localy)
{
if (children[i]->Enabled)
{
auto px = children[i]->Position.X + ViewportPosition.X;
auto py = children[i]->Position.Y + ViewportPosition.Y;
if( localx >= px &&
localy >= py &&
localx < px + children[i]->Size.X &&
localy < py + children[i]->Size.Y )
if( localx >= children[i]->Position.X &&
localy >= children[i]->Position.Y &&
localx < children[i]->Position.X + children[i]->Size.X &&
localy < children[i]->Position.Y + children[i]->Size.Y )
{
children[i]->OnMouseHover(localx - px, localy - py);
children[i]->OnMouseHover(localx - children[i]->Position.X, localy - children[i]->Position.Y);
break;
}
}
@ -170,26 +170,25 @@ void Panel::OnMouseHover(int localx, int localy)
XOnMouseHover(localx, localy);
}
void Panel::OnMouseMoved(int localx, int localy)
void Panel::OnMouseMoved(int localx, int localy, int dx, int dy)
{
PropagateMouseMove();
XOnMouseMoved(localx, localy);
XOnMouseMoved(localx, localy, dx, dy);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
}
}
void Panel::PropagateMouseMove()
void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
{
auto localx = ui::Engine::Ref().GetMouseX() - GetScreenPos().X;
auto localy = ui::Engine::Ref().GetMouseY() - GetScreenPos().Y;
mouseInside = true;
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
{
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y)
, prevlocal (local.X - dx, local.Y - dy);
// mouse currently inside?
if( local.X >= 0 &&
@ -197,12 +196,14 @@ void Panel::PropagateMouseMove()
local.X < children[i]->Size.X &&
local.Y < children[i]->Size.Y )
{
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
children[i]->OnMouseMovedInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
// was the mouse outside?
if (!children[i]->MouseInside)
if(!(prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y ) )
{
children[i]->MouseInside = true;
children[i]->OnMouseEnter(local.X, local.Y);
}
}
@ -210,59 +211,71 @@ void Panel::PropagateMouseMove()
else
{
// was the mouse inside?
if (children[i]->MouseInside)
if( prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y )
{
children[i]->MouseInside = false;
children[i]->OnMouseLeave(local.X, local.Y);
}
}
}
}
// always allow hover on parent (?)
XOnMouseMovedInside(localx, localy, dx, dy);
}
void Panel::OnMouseEnter(int localx, int localy)
{
mouseInside = true;
XOnMouseEnter(localx, localy);
}
void Panel::OnMouseLeave(int localx, int localy)
{
mouseInside = false;
XOnMouseLeave(localx, localy);
}
void Panel::OnMouseUp(int x, int y, unsigned button)
void Panel::OnMouseUnclick(int localx, int localy, unsigned button)
{
auto localx = x - Position.X;
auto localy = y - Position.Y;
bool childunclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
//child must be unlocked
if(children[i]->Enabled)
{
//is mouse inside?
if( children[i]->MouseDownInside &&
localx >= children[i]->Position.X + ViewportPosition.X &&
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
childunclicked = true;
children[i]->OnMouseUnclick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
for (auto *child : children)
{
child->MouseDownInside = false;
}
//if a child wasn't clicked, send click to ourself
if (!childunclicked)
{
XOnMouseUnclick(localx, localy, button);
}
}
void Panel::OnMouseUp(int x, int y, unsigned button)
{
XOnMouseUp(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
children[i]->OnMouseUp(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
children[i]->OnMouseUp(x, y, button);
}
}
@ -329,7 +342,11 @@ void Panel::XOnMouseHover(int localx, int localy)
{
}
void Panel::XOnMouseMoved(int localx, int localy)
void Panel::XOnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Panel::XOnMouseMovedInside(int localx, int localy, int dx, int dy)
{
}
@ -341,6 +358,10 @@ void Panel::XOnMouseLeave(int localx, int localy)
{
}
void Panel::XOnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Panel::XOnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -51,12 +51,14 @@ namespace ui
void Draw(const Point& screenPos) override;
void OnMouseHover(int localx, int localy) override;
void OnMouseMoved(int localx, int localy) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMovedInside(int localx, int localy, int dx, int dy) override;
void OnMouseEnter(int localx, int localy) override;
void OnMouseLeave(int localx, int localy) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseClick(int localx, int localy, unsigned button) override;
void OnMouseUnclick(int localx, int localy, unsigned button) override;
void OnMouseWheel(int localx, int localy, int d) override;
void OnMouseWheelInside(int localx, int localy, int d) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
@ -65,6 +67,7 @@ namespace ui
protected:
// child components
std::vector<ui::Component*> children;
bool mouseInside;
// Overridable. Called by XComponent::Tick()
virtual void XTick(float dt);
@ -77,7 +80,10 @@ namespace ui
virtual void XOnMouseHover(int localx, int localy);
// Overridable. Called by XComponent::OnMouseMoved()
virtual void XOnMouseMoved(int localx, int localy);
virtual void XOnMouseMoved(int localx, int localy, int dx, int dy);
// Overridable. Called by XComponent::OnMouseMovedInside()
virtual void XOnMouseMovedInside(int localx, int localy, int dx, int dy);
// Overridable. Called by XComponent::OnMouseEnter()
virtual void XOnMouseEnter(int localx, int localy);
@ -94,6 +100,9 @@ namespace ui
// Overridable. Called by XComponent::OnMouseClick()
virtual void XOnMouseClick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseUnclick()
virtual void XOnMouseUnclick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseWheel()
virtual void XOnMouseWheel(int localx, int localy, int d);
@ -105,8 +114,6 @@ namespace ui
// Overridable. Called by XComponent::OnKeyRelease()
virtual void XOnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void PropagateMouseMove();
};
}

View File

@ -64,22 +64,19 @@ void RichLabel::SetText(String newText)
regions = newRegions;
}
void RichLabel::OnMouseDown(int x, int y, unsigned button)
void RichLabel::OnMouseClick(int x, int y, unsigned button)
{
if (MouseDownInside)
int cursorPosition = displayTextWrapper.Point2Index(x - textPosition.X, y - textPosition.Y).raw_index;
for (auto const &region : regions)
{
int cursorPosition = displayTextWrapper.Point2Index(x - Position.X - textPosition.X, y - Position.Y - textPosition.Y).raw_index;
for (auto const &region : regions)
if (region.begin <= cursorPosition && region.end > cursorPosition)
{
if (region.begin <= cursorPosition && region.end > cursorPosition)
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
{
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
{
Platform::OpenURI(linkAction->uri);
return;
}
Platform::OpenURI(linkAction->uri);
return;
}
}
}
Label::OnMouseDown(x, y, button);
Label::OnMouseClick(x, y, button);
}

View File

@ -24,6 +24,6 @@ namespace ui
RichLabel(Point position, Point size, String text);
void SetText(String newText) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseClick(int x, int y, unsigned button) override;
};
}

View File

@ -262,7 +262,7 @@ void SaveButton::Draw(const Point& screenPos)
}
}
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
void SaveButton::OnMouseUnclick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -333,40 +333,36 @@ void SaveButton::OnContextMenuAction(int item)
}
}
void SaveButton::OnMouseDown(int x, int y, unsigned int button)
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
{
if (MouseDownInside)
if(button == SDL_BUTTON_RIGHT)
{
if(button == SDL_BUTTON_RIGHT)
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
{
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
selected = !selected;
DoSelection();
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
{
selected = !selected;
DoSelection();
}
}
}
}
void SaveButton::OnMouseMoved(int x, int y)
void SaveButton::OnMouseMovedInside(int x, int y, int dx, int dy)
{
isMouseInsideAuthor = false;
isMouseInsideHistory = false;
if (MouseInside)
{
if (y > Size.Y-11)
isMouseInsideAuthor = true;
if(y > Size.Y-11)
isMouseInsideAuthor = true;
else
isMouseInsideAuthor = false;
if (y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
}
if(showVotes && y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
else
isMouseInsideHistory = false;
}
void SaveButton::OnMouseEnter(int x, int y)

View File

@ -48,12 +48,12 @@ public:
virtual ~SaveButton();
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;
void OnMouseMoved(int x, int y) override;
void OnMouseMovedInside(int x, int y, int dx, int dy) override;
void AddContextMenu(int menuType);
void OnContextMenuAction(int item) override;

View File

@ -1,9 +1,8 @@
#include "ScrollPanel.h"
#include "Engine.h"
#include "graphics/Graphics.h"
#include "Misc.h"
#include "PowderToySDL.h"
#include "Window.h"
#include <algorithm>
using namespace ui;
@ -71,56 +70,28 @@ void ScrollPanel::Draw(const Point& screenPos)
}
}
void ScrollPanel::XOnMouseDown(int x, int y, unsigned int button)
void ScrollPanel::XOnMouseClick(int x, int y, unsigned int button)
{
if (MouseDownInside)
if (isMouseInsideScrollbar)
{
CancelPanning();
if (isMouseInsideScrollbar)
{
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
}
initialOffsetY = offsetY;
scrollbarInitialYClick = y - Position.Y;
scrollbarClickLocation = 100;
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
}
}
void ScrollPanel::CancelPanning()
{
panning = false;
panHistory = {};
yScrollVel = 0;
scrollbarInitialYClick = y;
scrollbarClickLocation = 100;
}
void ScrollPanel::XOnMouseUp(int x, int y, unsigned int button)
{
scrollbarSelected = false;
auto oldPanHistory = panHistory;
CancelPanning();
{
auto it = oldPanHistory.end();
while (it != oldPanHistory.begin() && *(it - 1))
{
--it;
}
if (it < oldPanHistory.end())
{
auto offsetYDiff = oldPanHistory.back()->offsetY - (*it)->offsetY;
auto tickDiff = oldPanHistory.back()->ticks - (*it)->ticks;
yScrollVel += offsetYDiff / tickDiff * (1000.f / Engine::Ref().GetFps());
}
}
isMouseInsideScrollbarArea = false;
scrollbarClickLocation = 0;
}
void ScrollPanel::XOnMouseMoved(int x, int y)
void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
{
if(maxOffset.Y>0 && InnerSize.Y>0)
{
auto oldViewportPositionY = ViewportPosition.Y;
float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y));
float scrollPos = 0;
if (-ViewportPosition.Y>0)
@ -142,19 +113,6 @@ void ScrollPanel::XOnMouseMoved(int x, int y)
offsetY = float(scrollbarInitialYOffset);
}
}
else if (MouseDownInside)
{
Vec2<int> mouseAt{ x, y };
if (Engine::Ref().TouchUI && iabs(scrollbarInitialYClick - mouseAt.Y) > PanOffsetThreshold)
{
panning = true;
for (auto *child : children)
{
child->MouseDownInside = false;
}
GetParentWindow()->FocusComponent(NULL);
}
}
if (x > (Size.X-scrollBarWidth) && x < (Size.X-scrollBarWidth)+scrollBarWidth)
{
@ -164,31 +122,11 @@ void ScrollPanel::XOnMouseMoved(int x, int y)
}
else
isMouseInsideScrollbar = false;
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}
}
void ScrollPanel::XTick(float dt)
{
auto oldViewportPositionY = ViewportPosition.Y;
if (panning)
{
auto scrollY = initialOffsetY + scrollbarInitialYClick - (Engine::Ref().GetMouseY() - GetScreenPos().Y);
ViewportPosition.Y = -scrollY;
offsetY = float(scrollY);
PanPoint p{ offsetY, GetTicks() };
if (!(panHistory.back() && panHistory.back()->ticks == p.ticks))
{
std::copy(panHistory.begin() + 1, panHistory.end(), panHistory.begin());
panHistory.back() = p;
}
}
if (xScrollVel > 7.0f) xScrollVel = 7.0f;
if (xScrollVel < -7.0f) xScrollVel = -7.0f;
if (xScrollVel > -0.5f && xScrollVel < 0.5)
@ -245,9 +183,9 @@ void ScrollPanel::XTick(float dt)
}
}
if (MouseInside && scrollBarWidth < 6)
if (mouseInside && scrollBarWidth < 6)
scrollBarWidth++;
else if (!MouseInside && scrollBarWidth > 0 && !scrollbarSelected)
else if (!mouseInside && scrollBarWidth > 0 && !scrollbarSelected)
scrollBarWidth--;
if (isMouseInsideScrollbarArea && scrollbarClickLocation && !scrollbarSelected)
@ -267,9 +205,4 @@ void ScrollPanel::XTick(float dt)
offsetY += scrollbarClickLocation*scrollHeight/10;
ViewportPosition.Y -= int(scrollbarClickLocation*scrollHeight/10);
}
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}

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