mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 18:09:59 +03:00
Compare commits
84 commits
develop
...
10.0.0-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ceee0a6e7 | ||
|
|
d33874f24e | ||
|
|
66f7fb909d | ||
|
|
928adcdb4e | ||
|
|
a4db3bfb88 | ||
|
|
73a68659f2 | ||
|
|
35a99bb5e6 | ||
|
|
5e628d9258 | ||
|
|
29f68ba1cf | ||
|
|
6762a1f448 | ||
|
|
c18128dd9f | ||
|
|
52a42d63ba | ||
|
|
a4e86f213f | ||
|
|
13427d77db | ||
|
|
a26954dafa | ||
|
|
94d18c44f3 | ||
|
|
66452b16f8 | ||
|
|
9c80e019cb | ||
|
|
317b8eea9b | ||
|
|
c968bafb0a | ||
|
|
9b7c83ef23 | ||
|
|
5ee0286635 | ||
|
|
10bee70c42 | ||
|
|
1e07305803 | ||
|
|
8d8e9d0390 | ||
|
|
2128e87d92 | ||
|
|
dbfae0599f | ||
|
|
b2d7211254 | ||
|
|
0631359ec4 | ||
|
|
e81ec9c39c | ||
|
|
b30677ef10 | ||
|
|
75215b0d31 | ||
|
|
bf0aa5f980 | ||
|
|
5520dc6aaf | ||
|
|
6d59334777 | ||
|
|
e90965adc1 | ||
|
|
a524d93ada | ||
|
|
7101f15a2c | ||
|
|
131dc0acf3 | ||
|
|
fd4436880a | ||
|
|
2582a90b90 | ||
|
|
31cf378171 | ||
|
|
4173faba7a | ||
|
|
0b3fb6c4ce | ||
|
|
302e10a7d9 | ||
|
|
947a8faa0d | ||
|
|
ad83592834 | ||
|
|
3f9b6ae452 | ||
|
|
f1e382b035 | ||
|
|
a0797d00e3 | ||
|
|
3d805dff29 | ||
|
|
8f314c982a | ||
|
|
9389b9d582 | ||
|
|
df172c0923 | ||
|
|
47be7ae502 | ||
|
|
e1941a5794 | ||
|
|
f85d399928 | ||
|
|
3fbbebe93b | ||
|
|
1274eb7e48 | ||
|
|
68010c6c49 | ||
|
|
49e9671c96 | ||
|
|
819b4e49c8 | ||
|
|
a74b1dd79d | ||
|
|
add5966c52 | ||
|
|
c89150a26e | ||
|
|
c8c6304a15 | ||
|
|
8bfb9b90c1 | ||
|
|
da62b63f52 | ||
|
|
881bb22d45 | ||
|
|
0776291e55 | ||
|
|
c2d324aff3 | ||
|
|
af83cd92c0 | ||
|
|
a639091a39 | ||
|
|
ab71c44ed6 | ||
|
|
9fef9c7bd8 | ||
|
|
811e3de29b | ||
|
|
1ebe081e03 | ||
|
|
79be92ca74 | ||
|
|
8fcfebb321 | ||
|
|
01bb8c81cf | ||
|
|
708222bb80 | ||
|
|
474be07724 | ||
|
|
6b952403c9 | ||
|
|
7711a9aa81 |
54 changed files with 1025 additions and 384 deletions
35
.github/actions/package/linux/action.yml
vendored
35
.github/actions/package/linux/action.yml
vendored
|
|
@ -58,9 +58,7 @@ runs:
|
|||
|
||||
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}
|
||||
|
||||
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
|
||||
echo "$GPG_PRIVATE_KEY" > privkey.asc
|
||||
|
|
@ -70,11 +68,23 @@ runs:
|
|||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop
|
||||
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/
|
||||
# FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged
|
||||
sharun lib4bin \
|
||||
--hard-links \
|
||||
--with-hooks \
|
||||
--dst-dir "$INSTALL_APPIMAGE_DIR" \
|
||||
"$INSTALL_APPIMAGE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
|
||||
|
||||
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/bin/
|
||||
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
|
||||
find "$INSTALL_APPIMAGE_DIR" -name '*gamemode*' -exec rm {} +
|
||||
|
||||
ln -s org.prismlauncher.PrismLauncher.metainfo.xml "$INSTALL_APPIMAGE_DIR"/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||
ln -s share/applications/org.prismlauncher.PrismLauncher.desktop "$INSTALL_APPIMAGE_DIR"
|
||||
ln -s share/icons/hicolor/256x256/apps/org.prismlauncher.PrismLauncher.png "$INSTALL_APPIMAGE_DIR"
|
||||
mv "$INSTALL_APPIMAGE_DIR"/{sharun,AppRun}
|
||||
ls -la "$INSTALL_APPIMAGE_DIR"
|
||||
|
||||
mkappimage \
|
||||
--no-appstream \
|
||||
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
|
||||
"$INSTALL_APPIMAGE_DIR" \
|
||||
"PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
|
||||
|
|
@ -89,9 +99,14 @@ runs:
|
|||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
#the linked cmark .so is of the version that ubuntu uses, so without this it breaks on most updated distros
|
||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
sharun lib4bin \
|
||||
--with-hooks \
|
||||
--hard-links \
|
||||
--dst-dir "$INSTALL_PORTABLE_DIR" \
|
||||
"$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
|
||||
|
||||
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
|
||||
find "$INSTALL_PORTABLE_DIR" -name '*gamemode*' -exec rm {} +
|
||||
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
|
|
|
|||
2
.github/actions/package/windows/action.yml
vendored
2
.github/actions/package/windows/action.yml
vendored
|
|
@ -140,7 +140,7 @@ runs:
|
|||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
||||
- name: Sign installer
|
||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
||||
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
|
||||
uses: azure/trusted-signing-action@v0
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ inputs:
|
|||
qt-version:
|
||||
description: Version of Qt to use
|
||||
required: true
|
||||
default: 6.9.3
|
||||
|
||||
outputs:
|
||||
build-type:
|
||||
|
|
@ -78,6 +77,5 @@ runs:
|
|||
with:
|
||||
aqtversion: "==3.1.*"
|
||||
version: ${{ inputs.qt-version }}
|
||||
arch: ${{ inputs.qt-architecture }}
|
||||
modules: qtimageformats qtnetworkauth
|
||||
cache: ${{ inputs.build-type == 'Debug' }}
|
||||
|
|
|
|||
|
|
@ -12,29 +12,7 @@ runs:
|
|||
dpkg-dev \
|
||||
ninja-build extra-cmake-modules pkg-config scdoc \
|
||||
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
|
||||
libxcb-cursor-dev
|
||||
|
||||
# TODO(@getchoo): Install with the above when all targets use Ubuntu 24.04
|
||||
- name: Install tomlplusplus
|
||||
if: ${{ runner.arch == 'ARM64' }}
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y install libtomlplusplus-dev
|
||||
|
||||
# FIXME(@getchoo): THIS IS HORRIBLE TO DO!
|
||||
# Install tomlplusplus from Ubuntu 24.04, since it never got backported to 22.04
|
||||
# I've done too much to continue keeping this as a submodule....
|
||||
- name: Install tomlplusplus from 24.04
|
||||
if: ${{ runner.arch != 'ARM64' }}
|
||||
shell: bash
|
||||
run: |
|
||||
deb_arch="$(dpkg-architecture -q DEB_HOST_ARCH)"
|
||||
curl -Lo libtomlplusplus-dev.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus-dev_3.4.0+ds-0.2build1_"$deb_arch".deb
|
||||
curl -Lo libtomlplusplus3t64.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus3t64_3.4.0+ds-0.2build1_"$deb_arch".deb
|
||||
sudo dpkg -i libtomlplusplus3t64.deb
|
||||
sudo dpkg -i libtomlplusplus-dev.deb
|
||||
rm *.deb
|
||||
sudo apt-get install -f
|
||||
libxcb-cursor-dev libtomlplusplus-dev
|
||||
|
||||
- name: Setup AppImage tooling
|
||||
shell: bash
|
||||
|
|
@ -56,19 +34,20 @@ runs:
|
|||
;;
|
||||
esac
|
||||
|
||||
gh release download \
|
||||
--repo VHSgunzo/sharun \
|
||||
--pattern "sharun-$APPIMAGE_ARCH-aio" \
|
||||
--output ~/bin/sharun
|
||||
|
||||
# FIXME!: revert this to probonopd/go-appimage once https://github.com/probonopd/go-appimage/pull/377 is merged!
|
||||
gh release download continuous \
|
||||
--repo probonopd/go-appimage \
|
||||
--pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \
|
||||
--output ~/bin/appimagetool
|
||||
gh release download continuous \
|
||||
--repo probonopd/go-appimage \
|
||||
--repo DioEgizio/go-appimage \
|
||||
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
|
||||
--output ~/bin/mkappimage
|
||||
chmod +x ~/bin/appimagetool ~/bin/mkappimage
|
||||
echo "$HOME/bin" >> "$GITHUB_PATH"
|
||||
|
||||
gh release download \
|
||||
--repo AppImageCommunity/AppImageUpdate \
|
||||
--pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
|
||||
--output ~/bin/AppImageUpdate.AppImage
|
||||
chmod +x ~/bin/AppImageUpdate.AppImage
|
||||
chmod +x ~/bin/*
|
||||
echo "$HOME/bin" >> "$GITHUB_PATH"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ runs:
|
|||
shell: bash
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja extra-cmake-modules temurin@17
|
||||
brew install ninja extra-cmake-modules temurin@17 mono
|
||||
|
||||
- name: Set JAVA_HOME
|
||||
shell: bash
|
||||
|
|
|
|||
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
|
|
@ -62,6 +62,9 @@ on:
|
|||
description: Type of build (Debug or Release)
|
||||
type: string
|
||||
default: Debug
|
||||
environment:
|
||||
description: Deployment environment to run under
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build-type:
|
||||
|
|
@ -73,6 +76,8 @@ jobs:
|
|||
build:
|
||||
name: Build (${{ matrix.artifact-name }})
|
||||
|
||||
environment: ${{ inputs.environment || '' }}
|
||||
|
||||
permissions:
|
||||
# Required for Azure Trusted Signing
|
||||
id-token: write
|
||||
|
|
@ -83,17 +88,15 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-24.04
|
||||
artifact-name: Linux
|
||||
cmake-preset: linux
|
||||
qt-version: 6.10.1
|
||||
|
||||
# NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!!
|
||||
#
|
||||
# It's not really documented anywhere AFAICT, but upstream Qt binaries
|
||||
# *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64
|
||||
- os: ubuntu-24.04-arm
|
||||
artifact-name: Linux-aarch64
|
||||
cmake-preset: linux
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: windows-2022
|
||||
artifact-name: Windows-MinGW-w64
|
||||
|
|
@ -112,16 +115,19 @@ jobs:
|
|||
cmake-preset: windows_msvc
|
||||
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
|
||||
vcvars-arch: amd64
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: windows-11-arm
|
||||
artifact-name: Windows-MSVC-arm64
|
||||
cmake-preset: windows_msvc
|
||||
vcvars-arch: arm64
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: macos-14
|
||||
- os: macos-26
|
||||
artifact-name: macOS
|
||||
cmake-preset: macos_universal
|
||||
macosx-deployment-target: 12.0
|
||||
qt-version: 6.9.3
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
|
@ -155,7 +161,7 @@ jobs:
|
|||
artifact-name: ${{ matrix.artifact-name }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
vcvars-arch: ${{ matrix.vcvars-arch }}
|
||||
qt-architecture: ${{ matrix.qt-architecture }}
|
||||
qt-version: ${{ matrix.qt-version }}
|
||||
|
||||
##
|
||||
# BUILD
|
||||
|
|
|
|||
1
.github/workflows/codeql.yml
vendored
1
.github/workflows/codeql.yml
vendored
|
|
@ -79,6 +79,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-dependencies
|
||||
with:
|
||||
build-type: Debug
|
||||
qt-version: 6.10.1
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
|
|
|
|||
3
.github/workflows/flatpak.yml
vendored
3
.github/workflows/flatpak.yml
vendored
|
|
@ -77,9 +77,6 @@ jobs:
|
|||
- os: ubuntu-22.04
|
||||
arch: x86_64
|
||||
|
||||
- os: ubuntu-22.04-arm
|
||||
arch: aarch64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
container:
|
||||
|
|
|
|||
3
.github/workflows/nix.yml
vendored
3
.github/workflows/nix.yml
vendored
|
|
@ -86,9 +86,6 @@ jobs:
|
|||
- os: ubuntu-22.04-arm
|
||||
system: aarch64-linux
|
||||
|
||||
- os: macos-15-intel
|
||||
system: x86_64-darwin
|
||||
|
||||
- os: macos-14
|
||||
system: aarch64-darwin
|
||||
|
||||
|
|
|
|||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
|
|
@ -11,6 +11,7 @@ jobs:
|
|||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build-type: Release
|
||||
environment: Release
|
||||
secrets: inherit
|
||||
|
||||
create_release:
|
||||
|
|
@ -34,6 +35,7 @@ jobs:
|
|||
run: |
|
||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-aarch64-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
|
||||
|
|
@ -88,6 +90,7 @@ jobs:
|
|||
name: Prism Launcher ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
|
|
|
|||
106
CMakeLists.txt
106
CMakeLists.txt
|
|
@ -1,6 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt
|
||||
|
||||
project(Launcher)
|
||||
project(Launcher LANGUAGES C CXX)
|
||||
if(APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
|
|
@ -79,19 +82,6 @@ else()
|
|||
if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
|
||||
# Emit PDBs for WinDbg, etc.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
|
||||
foreach(lang C CXX)
|
||||
set("CMAKE_${lang}_FLAGS" "-gcodeview ${CMAKE_${lang}_FLAGS}")
|
||||
|
||||
# Force-enabling this to use generator expressions like TARGET_PDB_FILE
|
||||
# (and because we can actually emit PDBs)
|
||||
set("CMAKE_${lang}_LINKER_SUPPORTS_PDB" ON)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# -ffunction-sections and -fdata-sections help reduce binary size
|
||||
# -mguard=cf enables Control Flow Guard
|
||||
# TODO: Look into -gc-sections to further reduce binary size
|
||||
|
|
@ -116,30 +106,24 @@ endif()
|
|||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||
|
||||
# If this is a Debug build turn on address sanitiser
|
||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
||||
if (DEBUG_ADDRESS_SANITIZER)
|
||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# using clang with clang-cl front end
|
||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
# AppleClang and Clang
|
||||
message(STATUS "Address Sanitizer available on Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||
endif()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# GCC
|
||||
message(STATUS "Address Sanitizer available on GCC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||
link_libraries("asan")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(STATUS "Address Sanitizer available on MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
|
||||
set(USE_ASAN_COMPILE_OPTIONS $<AND:$<CONFIG:Debug,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>)
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
message(STATUS "Using Address Sanitizer compile options for MSVC frontend")
|
||||
add_compile_options(
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:/Oy->
|
||||
)
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
message(STATUS "Using Address Sanitizer compile options for GCC/Clang")
|
||||
add_compile_options(
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null>
|
||||
)
|
||||
link_libraries("asan" "ubsan")
|
||||
else()
|
||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
|
|
@ -337,7 +321,7 @@ if(NOT LibArchive_FOUND)
|
|||
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
|
||||
endif()
|
||||
|
||||
find_package(tomlplusplus 3.2.0 REQUIRED)
|
||||
find_package(tomlplusplus 3.2.0)
|
||||
# fallback to pkgconfig, important especially as many distros package toml++ built with meson
|
||||
if(NOT tomlplusplus_FOUND)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
|
@ -399,6 +383,52 @@ if(UNIX AND APPLE)
|
|||
# Add the icon
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
|
||||
find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler")
|
||||
if(ACTOOL_EXE)
|
||||
execute_process(
|
||||
COMMAND xcodebuild -version
|
||||
OUTPUT_VARIABLE XCODE_VERSION_OUTPUT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}")
|
||||
if(XCODE_VERSION_MATCH)
|
||||
set(XCODE_VERSION ${CMAKE_MATCH_1})
|
||||
else()
|
||||
set(XCODE_VERSION 0.0)
|
||||
endif()
|
||||
|
||||
if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0)
|
||||
set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info")
|
||||
set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car")
|
||||
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${GENERATED_ASSETS_CAR}"
|
||||
COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}"
|
||||
--compile "${ASSETS_OUT_DIR}"
|
||||
--output-partial-info-plist /dev/null
|
||||
--app-icon PrismLauncher
|
||||
--enable-on-demand-resources NO
|
||||
--target-device mac
|
||||
--minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET}
|
||||
--platform macosx
|
||||
DEPENDS "${ICON_SOURCE}"
|
||||
COMMENT "Compiling asset catalog (${ICON_SOURCE})"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(compile_assets ALL DEPENDS "${GENERATED_ASSETS_CAR}")
|
||||
install(FILES "${GENERATED_ASSETS_CAR}" DESTINATION "${RESOURCES_DEST_DIR}")
|
||||
else()
|
||||
message(WARNING "Xcode ${XCODE_VERSION} is too old. Minimum required version is 26.0. Not compiling liquid glass icons.")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(WARNING "actool not found. Cannot compile macOS app icons.\n"
|
||||
"Install Xcode command line tools: 'xcode-select --install'")
|
||||
endif()
|
||||
|
||||
|
||||
elseif(UNIX)
|
||||
include(KDEInstallDirs)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<string>${Launcher_Name}</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>${Launcher_Name}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
|
|
|
|||
17
flake.lock
generated
17
flake.lock
generated
|
|
@ -18,18 +18,15 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"type": "github"
|
||||
"lastModified": 1766473571,
|
||||
"narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=",
|
||||
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz";
|
||||
|
||||
libnbtplusplus = {
|
||||
url = "github:PrismLauncher/libnbtplusplus";
|
||||
|
|
|
|||
|
|
@ -371,25 +371,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
}
|
||||
|
||||
QString origcwdPath = QDir::currentPath();
|
||||
#if defined(Q_OS_LINUX)
|
||||
const QString binFilePath = applicationFilePath();
|
||||
const bool isAppImage = binFilePath.startsWith("/tmp/.mount_");
|
||||
// Yes, this can technically trigger the logic below if someone makes an AppImage with an actual launcher exe named "ld-linux"
|
||||
// Please don't :)
|
||||
const bool executedFromLinker = QFileInfo(binFilePath).fileName().startsWith("ld-linux");
|
||||
|
||||
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
|
||||
// <root>/lib64
|
||||
// This is not the path to our actual binary, which we want
|
||||
QString binPath;
|
||||
if (isAppImage && executedFromLinker) {
|
||||
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
|
||||
} else {
|
||||
binPath = applicationDirPath();
|
||||
}
|
||||
#else
|
||||
QString binPath = applicationDirPath();
|
||||
#endif
|
||||
|
||||
{
|
||||
// Root path is used for updates and portable data
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ set(CORE_SOURCES
|
|||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ExportToZipTask.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ExportToZipTask.cpp
|
||||
archive/ExportToZipTask.h
|
||||
archive/ExtractZipTask.cpp
|
||||
archive/ExtractZipTask.cpp
|
||||
archive/ExtractZipTask.h
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
|
|
@ -621,10 +621,10 @@ set(PRISMUPDATER_SOURCES
|
|||
# Zip
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
|
||||
# Time
|
||||
MMCTime.h
|
||||
|
|
@ -1300,6 +1300,16 @@ endif()
|
|||
|
||||
include(CompilerWarnings)
|
||||
|
||||
######## Precompiled Headers ###########
|
||||
|
||||
set(PRECOMPILED_HEADERS
|
||||
include/base.pch.hpp
|
||||
include/qtcore.pch.hpp
|
||||
include/qtgui.pch.hpp
|
||||
)
|
||||
|
||||
####### Targets ########
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
set_project_warnings(Launcher_logic
|
||||
|
|
@ -1308,6 +1318,7 @@ set_project_warnings(Launcher_logic
|
|||
"${Launcher_GCC_WARNINGS}")
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_murmur2
|
||||
|
|
@ -1389,6 +1400,7 @@ endif()
|
|||
target_link_libraries(Launcher_logic)
|
||||
|
||||
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
|
||||
target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic)
|
||||
target_link_libraries(${Launcher_Name} Launcher_logic)
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
|
|
@ -1412,14 +1424,15 @@ install(TARGETS ${Launcher_Name}
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
|
||||
if(Launcher_BUILD_UPDATER)
|
||||
# Updater
|
||||
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
target_link_libraries(prism_updater_logic
|
||||
${ZLIB_LIBRARIES}
|
||||
systeminfo
|
||||
|
|
@ -1439,6 +1452,7 @@ if(Launcher_BUILD_UPDATER)
|
|||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||
target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic)
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||
|
|
@ -1455,8 +1469,8 @@ if(Launcher_BUILD_UPDATER)
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -1469,6 +1483,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
"${Launcher_GCC_WARNINGS}")
|
||||
|
||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
|
||||
target_link_libraries(filelink_logic
|
||||
systeminfo
|
||||
BuildConfig
|
||||
|
|
@ -1482,6 +1498,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
||||
|
||||
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||
target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic)
|
||||
|
||||
# HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC
|
||||
# I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo
|
||||
# Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests
|
||||
|
|
@ -1506,8 +1524,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -76,15 +75,6 @@ bool isFlatpak()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool isSelfContained()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFileInfo(QCoreApplication::applicationFilePath()).fileName().startsWith("ld-linux");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSnap()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
|
|||
|
|
@ -37,11 +37,6 @@ bool openUrl(const QUrl& url);
|
|||
*/
|
||||
bool isFlatpak();
|
||||
|
||||
/**
|
||||
* Determine whether the launcher is running in a self-contained Linux bundle
|
||||
*/
|
||||
bool isSelfContained();
|
||||
|
||||
/**
|
||||
* Determine whether the launcher is running in a Snap environment
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -773,34 +772,6 @@ QString ResolveExecutable(QString path)
|
|||
return pathInfo.absoluteFilePath();
|
||||
}
|
||||
|
||||
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments)
|
||||
{
|
||||
qDebug() << "Creating process for" << program;
|
||||
auto proc = std::unique_ptr<QProcess>(new QProcess());
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
if (DesktopServices::isSelfContained()) {
|
||||
const auto linkerPath = QCoreApplication::applicationFilePath();
|
||||
qDebug() << "Wrapping" << program << "with self-contained linker at" << linkerPath;
|
||||
|
||||
QStringList wrappedArguments;
|
||||
wrappedArguments << "--inhibit-cache" << program;
|
||||
wrappedArguments += arguments;
|
||||
|
||||
proc->setProgram(linkerPath);
|
||||
proc->setArguments(wrappedArguments);
|
||||
} else {
|
||||
proc->setProgram(program);
|
||||
proc->setArguments(arguments);
|
||||
}
|
||||
#else
|
||||
proc->setProgram(program);
|
||||
proc->setArguments(arguments);
|
||||
#endif
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize path
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -43,13 +42,11 @@
|
|||
|
||||
#include <system_error>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFlags>
|
||||
#include <QLocalServer>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
|
||||
namespace FS {
|
||||
|
|
@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth);
|
|||
*/
|
||||
QString ResolveExecutable(QString path);
|
||||
|
||||
/**
|
||||
* Create a QProcess instance
|
||||
*
|
||||
* This wrapper is currently only required for wrapping binaries called in
|
||||
* self-contained AppImages (like those created by `go-appimage`)
|
||||
*/
|
||||
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments);
|
||||
|
||||
/**
|
||||
* Normalize path
|
||||
*
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ bool installIcon(QString root, QString instIconKey)
|
|||
if (iconList->iconFileExists(instIconKey)) {
|
||||
iconList->deleteIcon(instIconKey);
|
||||
}
|
||||
iconList->installIcon(importIconPath, instIconKey + ".png");
|
||||
iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
|
|||
return doc.array();
|
||||
}
|
||||
|
||||
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error, QString* garbage)
|
||||
{
|
||||
auto doc = QJsonDocument::fromJson(json, error);
|
||||
if (error->error == QJsonParseError::GarbageAtEnd) {
|
||||
qsizetype offset = error->offset;
|
||||
QByteArray validJson = json.left(offset);
|
||||
doc = QJsonDocument::fromJson(validJson, error);
|
||||
|
||||
if (garbage)
|
||||
*garbage = json.right(json.size() - offset);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
void writeString(QJsonObject& to, const QString& key, const QString& value)
|
||||
{
|
||||
if (!value.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ QJsonArray toJsonArray(const QList<T>& container)
|
|||
|
||||
////////////////// READING ////////////////////
|
||||
|
||||
// Attempt to parse JSON up until garbage is encountered
|
||||
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error = nullptr, QString* garbage = nullptr);
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
||||
|
|
|
|||
|
|
@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
|||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||
|
||||
# Set up env.
|
||||
# Pass our custom variables separately so that the launcher can remove them for child processes
|
||||
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@"
|
||||
export LAUNCHER_LD_PRELOAD=""
|
||||
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
||||
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
||||
# Makes the launcher use portals for file picking
|
||||
export QT_QPA_PLATFORMTHEME=xdgdesktopportal
|
||||
|
||||
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
|
||||
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
|
||||
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
|
||||
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
|
||||
# Just to be sure...
|
||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||
|
||||
# Detect missing dependencies...
|
||||
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
|
||||
if [ "x$DEPS_LIST" = "x" ]; then
|
||||
# We have all our dependencies. Run the launcher.
|
||||
echo "No missing dependencies found."
|
||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||
|
||||
# Just to be sure...
|
||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||
|
||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||
|
||||
if [ -f portable.txt ]; then
|
||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||
fi
|
||||
|
||||
ARGS+=("$@")
|
||||
|
||||
# Run the launcher
|
||||
exec -a "${ARGS[@]}"
|
||||
|
||||
# Run the launcher in valgrind
|
||||
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
||||
|
||||
# Run the launcher with callgrind, delay instrumentation
|
||||
# valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
||||
# use callgrind_control -i on/off to profile actions
|
||||
|
||||
# Exit with launcher's exit code.
|
||||
# exit $?
|
||||
else
|
||||
# apt
|
||||
if which apt-file &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo apt-get install $COMMAND_LIBS"
|
||||
# pacman
|
||||
elif which pkgfile &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo pacman -S $COMMAND_LIBS"
|
||||
# dnf
|
||||
elif which dnf &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do dnf whatprovides -q $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | grep -v 'Repo' | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo dnf install $COMMAND_LIBS"
|
||||
# yum
|
||||
elif which yum &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo yum install $COMMAND_LIBS"
|
||||
# zypper
|
||||
elif which zypper &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo zypper install $COMMAND_LIBS"
|
||||
# emerge
|
||||
elif which pfl &>/dev/null; then
|
||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done`
|
||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
||||
INSTALL_CMD="sudo emerge $COMMAND_LIBS"
|
||||
fi
|
||||
|
||||
MESSAGE="Error: The launcher is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager."
|
||||
MESSAGE="$MESSAGE\n\nHint (please apply common sense): $INSTALL_CMD\n"
|
||||
|
||||
printerror "$MESSAGE"
|
||||
exit 1
|
||||
if [ -f portable.txt ]; then
|
||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||
fi
|
||||
|
||||
ARGS+=("$@")
|
||||
|
||||
# Run the launcher
|
||||
exec -a "${ARGS[@]}"
|
||||
17
launcher/include/base.pch.hpp
Normal file
17
launcher/include/base.pch.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#ifndef PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||
#define PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
#include <FileSystem.h>
|
||||
#include <Json.h>
|
||||
#include <Version.h>
|
||||
#include <sys.h>
|
||||
|
||||
#endif // PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||
59
launcher/include/qtcore.pch.hpp
Normal file
59
launcher/include/qtcore.pch.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
#ifndef PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||
#define PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
// collections
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMimeData>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QProcess>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QtMath>
|
||||
|
||||
#endif // PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||
47
launcher/include/qtgui.pch.hpp
Normal file
47
launcher/include/qtgui.pch.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#ifndef PRISM_PRECOMPILED_QTGUI_HEADERS_H
|
||||
#define PRISM_PRECOMPILED_QTGUI_HEADERS_H
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <QClipboard>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <QAction>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <QStyle>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include <QScrollBar>
|
||||
|
||||
#include <QTabBar>
|
||||
#include <QToolButton>
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QListView>
|
||||
#include <QTreeView>
|
||||
|
||||
#include <QLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QPixmapCache>
|
||||
|
||||
#endif // PRISM_PRECOMPILED_GUI_HEADERS_H
|
||||
|
|
@ -91,6 +91,7 @@
|
|||
#include <QActionGroup>
|
||||
#include <QMainWindow>
|
||||
#include <QScreen>
|
||||
#include <QStandardPaths>
|
||||
#include <QWindow>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
@ -588,6 +589,16 @@ QStringList MinecraftInstance::javaArguments()
|
|||
"minecraft.exe.heapdump");
|
||||
#endif
|
||||
|
||||
// LWJGL2 reads `LWJGL_DISABLE_XRANDR` to force disable xrandr usage and fall back to xf86videomode.
|
||||
// It *SHOULD* check for the executable to exist before trying to use it for queries but it doesnt,
|
||||
// so WE can and force disable xrandr if it is not available.
|
||||
#ifdef Q_OS_LINUX
|
||||
// LWJGL2 is "org.lwjgl" LWJGL3 is "org.lwjgl3"
|
||||
if (m_components->getComponent("org.lwjgl") != nullptr && QStandardPaths::findExecutable("xrandr").isEmpty()) {
|
||||
args << QString("-DLWJGL_DISABLE_XRANDR=true");
|
||||
}
|
||||
#endif
|
||||
|
||||
int min = settings()->get("MinMemAlloc").toInt();
|
||||
int max = settings()->get("MaxMemAlloc").toInt();
|
||||
if (min < max) {
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ void AccountList::beginActivity()
|
|||
void AccountList::endActivity()
|
||||
{
|
||||
if (m_activityCount == 0) {
|
||||
qWarning() << m_name << " - Activity count would become below zero";
|
||||
qWarning() << "Activity count would become below zero";
|
||||
return;
|
||||
}
|
||||
bool deactivating = m_activityCount == 1;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ class AccountList : public QAbstractListModel {
|
|||
void endActivity();
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
uint32_t m_activityCount = 0;
|
||||
signals:
|
||||
void listChanged();
|
||||
|
|
|
|||
|
|
@ -207,7 +207,13 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
|
|||
bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata)
|
||||
{
|
||||
for (auto& resource : m_resources) {
|
||||
if (resource->fileinfo().fileName() == file_name) {
|
||||
auto resourceFileInfo = resource->fileinfo();
|
||||
auto resourceFileName = resource->fileinfo().fileName();
|
||||
if (!resource->enabled() && resourceFileName.endsWith(".disabled")) {
|
||||
resourceFileName.chop(9);
|
||||
}
|
||||
|
||||
if (resourceFileName == file_name) {
|
||||
auto res = resource->destroy(indexDir(), preserve_metadata, false);
|
||||
|
||||
update();
|
||||
|
|
|
|||
|
|
@ -168,10 +168,15 @@ bool processZIP(DataPack* pack, ProcessingLevel level)
|
|||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
auto json_doc = QJsonDocument::fromJson(raw_data);
|
||||
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
||||
QJsonParseError parse_error;
|
||||
auto json_doc = Json::parseUntilGarbage(raw_data, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse JSON:" << parse_error.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
||||
pack->setPackFormat(pack_obj["pack_format"].toInt());
|
||||
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
|
||||
} catch (Json::JsonException& e) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2025 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2025 Rinth, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -22,29 +23,89 @@
|
|||
|
||||
#include "FileSystem.h"
|
||||
|
||||
static void setAlpha(QImage& image, const QRect& region, const int alpha)
|
||||
{
|
||||
for (int y = region.top(); y < region.bottom(); ++y) {
|
||||
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||
for (int x = region.left(); x < region.right(); ++x) {
|
||||
QRgb pixel = line[x];
|
||||
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void doNotchTransparencyHack(QImage& image)
|
||||
{
|
||||
for (int y = 0; y < 32; y++) {
|
||||
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||
for (int x = 32; x < 64; x++) {
|
||||
if (qAlpha(line[x]) < 128) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setAlpha(image, { 32, 0, 32, 32 }, 0);
|
||||
}
|
||||
|
||||
static QImage improveSkin(QImage skin)
|
||||
{
|
||||
int height = skin.height();
|
||||
int width = skin.width();
|
||||
if (width != 64 || (height != 32 && height != 64)) { // this is no minecraft skin
|
||||
return skin;
|
||||
}
|
||||
// It seems some older skins may use this format, which can't be drawn onto
|
||||
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
|
||||
// https://doc.qt.io/qt-6/qpainter.html#begin
|
||||
if (skin.format() == QImage::Format_Indexed8) {
|
||||
skin = skin.convertToFormat(QImage::Format_RGB32);
|
||||
skin = skin.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
if (skin.size() == QSize(64, 32)) { // old format
|
||||
|
||||
auto isLegacy = height == 32; // old format
|
||||
if (isLegacy) {
|
||||
auto newSkin = QImage(QSize(64, 64), skin.format());
|
||||
newSkin.fill(Qt::transparent);
|
||||
QPainter p(&newSkin);
|
||||
p.drawImage(QPoint(0, 0), skin.copy(QRect(0, 0, 64, 32))); // copy head
|
||||
p.drawImage(0, 0, skin);
|
||||
|
||||
auto leg = skin.copy(QRect(0, 16, 16, 16));
|
||||
p.drawImage(QPoint(16, 48), leg); // copy leg
|
||||
auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) {
|
||||
QImage region = newSkin.copy(startX, startY, sizeX, sizeY);
|
||||
region = region.mirrored(true, false);
|
||||
|
||||
auto arm = skin.copy(QRect(40, 16, 16, 16));
|
||||
p.drawImage(QPoint(32, 48), arm); // copy arm
|
||||
return newSkin;
|
||||
p.drawImage(startX + offsetX, startY + offsetY, region);
|
||||
};
|
||||
static const struct {
|
||||
int x;
|
||||
int y;
|
||||
int offsetX;
|
||||
int offsetY;
|
||||
int width;
|
||||
int height;
|
||||
} faces[] = {
|
||||
{ 4, 16, 16, 32, 4, 4 }, { 8, 16, 16, 32, 4, 4 }, { 0, 20, 24, 32, 4, 12 }, { 4, 20, 16, 32, 4, 12 },
|
||||
{ 8, 20, 8, 32, 4, 12 }, { 12, 20, 16, 32, 4, 12 }, { 44, 16, -8, 32, 4, 4 }, { 48, 16, -8, 32, 4, 4 },
|
||||
{ 40, 20, 0, 32, 4, 12 }, { 44, 20, -8, 32, 4, 12 }, { 48, 20, -16, 32, 4, 12 }, { 52, 20, -8, 32, 4, 12 },
|
||||
};
|
||||
|
||||
for (const auto& face : faces) {
|
||||
copyRect(face.x, face.y, face.offsetX, face.offsetY, face.width, face.height);
|
||||
}
|
||||
doNotchTransparencyHack(newSkin);
|
||||
skin = newSkin;
|
||||
}
|
||||
static const QRect opaqueParts[] = {
|
||||
{ 0, 0, 32, 16 },
|
||||
{ 0, 16, 64, 16 },
|
||||
{ 16, 48, 32, 16 },
|
||||
};
|
||||
|
||||
for (const auto& p : opaqueParts) {
|
||||
setAlpha(skin, p, 255);
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
static QImage getSkin(const QString path)
|
||||
{
|
||||
return improveSkin(QImage(path));
|
||||
|
|
@ -66,8 +127,8 @@ static QImage generatePreviews(QImage texture, bool slim)
|
|||
paint.drawImage(4, 22, texture.copy(4, 20, 4, 12));
|
||||
paint.drawImage(4, 22, texture.copy(4, 36, 4, 12));
|
||||
// left leg
|
||||
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
|
||||
paint.drawImage(8, 22, texture.copy(20, 52, 4, 12));
|
||||
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
|
||||
|
||||
auto armWidth = slim ? 3 : 4;
|
||||
auto armPosX = slim ? 1 : 0;
|
||||
|
|
@ -89,8 +150,8 @@ static QImage generatePreviews(QImage texture, bool slim)
|
|||
paint.drawImage(24, 22, texture.copy(12, 20, 4, 12));
|
||||
paint.drawImage(24, 22, texture.copy(12, 36, 4, 12));
|
||||
// left leg
|
||||
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
|
||||
paint.drawImage(28, 22, texture.copy(28, 52, 4, 12));
|
||||
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
|
||||
|
||||
// right arm
|
||||
paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12));
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
|
|||
return {};
|
||||
}
|
||||
for (auto mcVer : versionArray) {
|
||||
file.mcVersion.append(ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()));
|
||||
file.mcVersion.append({ ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()),
|
||||
mcVer.toString() }); // double this so we can check both strings when filtering
|
||||
}
|
||||
auto loaders = Json::requireArray(obj, "loaders");
|
||||
for (auto loader : loaders) {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,12 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
|
||||
if (SkinOpenGLWindow::hasOpenGL()) {
|
||||
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
|
||||
} else {
|
||||
m_skinPreviewLabel = new QLabel(this);
|
||||
m_skinPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
|
|
@ -92,7 +97,9 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
|
||||
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
|
||||
connect(m_ui->elytraCB, &QCheckBox::stateChanged, this, [this]() {
|
||||
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
|
||||
}
|
||||
on_capeCombo_currentIndexChanged(0);
|
||||
});
|
||||
|
||||
|
|
@ -103,13 +110,19 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
||||
|
||||
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
|
||||
if (m_skinPreview) {
|
||||
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
|
||||
} else {
|
||||
m_ui->skinLayout->insertWidget(0, m_skinPreviewLabel);
|
||||
}
|
||||
}
|
||||
|
||||
SkinManageDialog::~SkinManageDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_skinPreview;
|
||||
if (m_skinPreview) {
|
||||
delete m_skinPreview;
|
||||
}
|
||||
}
|
||||
|
||||
void SkinManageDialog::activated(QModelIndex index)
|
||||
|
|
@ -131,7 +144,12 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]
|
|||
if (!skin)
|
||||
return;
|
||||
|
||||
m_skinPreview->updateScene(skin);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
} else {
|
||||
m_skinPreviewLabel->setPixmap(
|
||||
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
m_ui->capeCombo->setCurrentIndex(m_capesIdx.value(skin->getCapeId()));
|
||||
m_ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC);
|
||||
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
|
||||
|
|
@ -165,17 +183,17 @@ void SkinManageDialog::on_fileBtn_clicked()
|
|||
QPixmap previewCape(QImage capeImage, bool elytra = false)
|
||||
{
|
||||
if (elytra) {
|
||||
auto wing = capeImage.copy(34, 0, 12, 22);
|
||||
auto wing = capeImage.copy(34, 2, 12, 20);
|
||||
QImage mirrored = wing.mirrored(true, false);
|
||||
|
||||
QImage combined(wing.width() * 2 - 2, wing.height(), capeImage.format());
|
||||
QImage combined(wing.width() * 2 + 1, wing.height() + 14, capeImage.format());
|
||||
combined.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&combined);
|
||||
painter.drawImage(0, 0, wing);
|
||||
painter.drawImage(wing.width() - 2, 0, mirrored);
|
||||
painter.drawImage(0, 7, wing);
|
||||
painter.drawImage(wing.width() + 1, 7, mirrored);
|
||||
painter.end();
|
||||
return QPixmap::fromImage(combined.scaled(96, 176, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||
return QPixmap::fromImage(combined.scaled(84, 128, Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
return QPixmap::fromImage(capeImage.copy(1, 1, 10, 16).scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
|
|
@ -244,10 +262,17 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
|||
} else {
|
||||
m_ui->capeImage->clear();
|
||||
}
|
||||
m_skinPreview->updateCape(cape);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateCape(cape);
|
||||
}
|
||||
if (auto skin = getSelectedSkin(); skin) {
|
||||
skin->setCapeId(id.toString());
|
||||
m_skinPreview->updateScene(skin);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
} else {
|
||||
m_skinPreviewLabel->setPixmap(
|
||||
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +280,12 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked)
|
|||
{
|
||||
if (auto skin = getSelectedSkin(); skin) {
|
||||
skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM);
|
||||
m_skinPreview->updateScene(skin);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
} else {
|
||||
m_skinPreviewLabel->setPixmap(
|
||||
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -545,6 +575,10 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
|||
} else {
|
||||
m_ui->capeImage->clear();
|
||||
}
|
||||
if (auto skin = getSelectedSkin(); skin && !m_skinPreview) {
|
||||
m_skinPreviewLabel->setPixmap(
|
||||
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
}
|
||||
|
||||
SkinModel* SkinManageDialog::getSelectedSkin()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <QDialog>
|
||||
#include <QItemSelection>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
|
|
@ -68,4 +69,5 @@ class SkinManageDialog : public QDialog, public SkinProvider {
|
|||
QHash<QString, QImage> m_capes;
|
||||
QHash<QString, int> m_capesIdx;
|
||||
SkinOpenGLWindow* m_skinPreview = nullptr;
|
||||
QLabel* m_skinPreviewLabel = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,32 +31,50 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
m_staticComponents = {
|
||||
// head
|
||||
new opengl::BoxGeometry(QVector3D(8, 8, 8), QVector3D(0, 4, 0), QPoint(0, 0), QVector3D(8, 8, 8)),
|
||||
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
|
||||
// body
|
||||
new opengl::BoxGeometry(QVector3D(8, 12, 4), QVector3D(0, -6, 0), QPoint(16, 16), QVector3D(8, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
|
||||
// right leg
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9, -18, -0.1), QPoint(0, 16), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
|
||||
// left leg
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9, -18, -0.1), QPoint(16, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_staticComponentsOverlay = {
|
||||
// head
|
||||
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
|
||||
// body
|
||||
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
|
||||
// right leg
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
|
||||
// left leg
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(1.9, -18, -0.1), QPoint(0, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_normalArms = {
|
||||
// Right Arm
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-6, -6, 0), QPoint(40, 16), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
|
||||
// Left Arm
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(6, -6, 0), QPoint(32, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_normalArmsOverlay = {
|
||||
// Right Arm
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
|
||||
// Left Arm
|
||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(6, -6, 0), QPoint(48, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_slimArms = {
|
||||
// Right Arm
|
||||
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(-5.5, -6, 0), QPoint(40, 16), QVector3D(3, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
|
||||
// Left Arm
|
||||
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(5.5, -6, 0), QPoint(32, 48), QVector3D(3, 12, 4)),
|
||||
};
|
||||
|
||||
m_slimArmsOverlay = {
|
||||
// Right Arm
|
||||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
|
||||
// Left Arm
|
||||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(5.5, -6, 0), QPoint(48, 48), QVector3D(3, 12, 4)),
|
||||
};
|
||||
|
||||
|
|
@ -88,7 +106,8 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
}
|
||||
Scene::~Scene()
|
||||
{
|
||||
for (auto array : { m_staticComponents, m_normalArms, m_slimArms, m_elytra }) {
|
||||
for (auto array :
|
||||
{ m_staticComponents, m_normalArms, m_slimArms, m_elytra, m_staticComponentsOverlay, m_normalArmsOverlay, m_slimArmsOverlay }) {
|
||||
for (auto g : array) {
|
||||
delete g;
|
||||
}
|
||||
|
|
@ -106,7 +125,8 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
|||
{
|
||||
m_skinTexture->bind();
|
||||
program->setUniformValue("texture", 0);
|
||||
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms }) {
|
||||
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms, m_staticComponentsOverlay,
|
||||
m_slim ? m_slimArmsOverlay : m_normalArmsOverlay }) {
|
||||
for (auto g : toDraw) {
|
||||
g->draw(program);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ class Scene : protected QOpenGLFunctions {
|
|||
QList<BoxGeometry*> m_staticComponents;
|
||||
QList<BoxGeometry*> m_normalArms;
|
||||
QList<BoxGeometry*> m_slimArms;
|
||||
QList<BoxGeometry*> m_staticComponentsOverlay;
|
||||
QList<BoxGeometry*> m_normalArmsOverlay;
|
||||
QList<BoxGeometry*> m_slimArmsOverlay;
|
||||
BoxGeometry* m_cape = nullptr;
|
||||
QList<BoxGeometry*> m_elytra;
|
||||
QOpenGLTexture* m_skinTexture = nullptr;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QtMath>
|
||||
#include <functional>
|
||||
|
||||
#include "minecraft/skins/SkinModel.h"
|
||||
#include "rainbow.h"
|
||||
|
|
@ -270,11 +271,12 @@ void SkinOpenGLWindow::updateCape(const QImage& cape)
|
|||
|
||||
QColor calculateContrastingColor(const QColor& color)
|
||||
{
|
||||
constexpr float contrast = 0.2;
|
||||
auto luma = Rainbow::luma(color);
|
||||
if (luma < 0.5) {
|
||||
constexpr float contrast = 0.05;
|
||||
return Rainbow::lighten(color, contrast);
|
||||
} else {
|
||||
constexpr float contrast = 0.2;
|
||||
return Rainbow::darken(color, contrast);
|
||||
}
|
||||
}
|
||||
|
|
@ -282,11 +284,14 @@ QColor calculateContrastingColor(const QColor& color)
|
|||
QImage generateChessboardImage(int width, int height, int tileSize, QColor baseColor)
|
||||
{
|
||||
QImage image(width, height, QImage::Format_RGB888);
|
||||
auto white = baseColor;
|
||||
auto black = calculateContrastingColor(baseColor);
|
||||
bool isDarkBase = Rainbow::luma(baseColor) < 0.5;
|
||||
float contrast = isDarkBase ? 0.05 : 0.45;
|
||||
auto contrastFunc = std::bind(isDarkBase ? Rainbow::lighten : Rainbow::darken, std::placeholders::_1, contrast, 1.0);
|
||||
auto white = contrastFunc(baseColor);
|
||||
auto black = contrastFunc(calculateContrastingColor(baseColor));
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
bool isWhite = ((x / tileSize) % 2) == ((y / tileSize) % 2);
|
||||
bool isWhite = ((x / tileSize) + (y / tileSize)) % 2 == 0;
|
||||
image.setPixelColor(x, y, isWhite ? white : black);
|
||||
}
|
||||
}
|
||||
|
|
@ -325,3 +330,9 @@ void SkinOpenGLWindow::setElytraVisible(bool visible)
|
|||
if (m_scene)
|
||||
m_scene->setElytraVisible(visible);
|
||||
}
|
||||
|
||||
bool SkinOpenGLWindow::hasOpenGL()
|
||||
{
|
||||
QOpenGLContext ctx;
|
||||
return ctx.create();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
|
|||
void updateCape(const QImage& cape);
|
||||
void setElytraVisible(bool visible);
|
||||
|
||||
static bool hasOpenGL();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
void mouseReleaseEvent(QMouseEvent* e) override;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<height>620</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
|||
m_model->loadColumns(ui->treeView);
|
||||
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
|
||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
ExternalResourcesPage::~ExternalResourcesPage()
|
||||
|
|
|
|||
|
|
@ -80,7 +80,13 @@ void McClient::parseResponse()
|
|||
Q_UNUSED(readVarInt(m_resp)); // json length
|
||||
|
||||
// 'resp' should now be the JSON string
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_resp);
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument doc = Json::parseUntilGarbage(m_resp, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qDebug() << "Failed to parse JSON:" << parseError.errorString();
|
||||
emitFail(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
emitSucceed(doc.object());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
#include <QFutureWatcher>
|
||||
|
||||
#include <Json.h>
|
||||
#include "Exception.h"
|
||||
#include "McClient.h"
|
||||
#include "McResolver.h"
|
||||
#include "ServerPingTask.h"
|
||||
|
||||
unsigned getOnlinePlayers(QJsonObject data)
|
||||
{
|
||||
return Json::requireInteger(Json::requireObject(data, "players"), "online");
|
||||
try {
|
||||
return Json::requireInteger(Json::requireObject(data, "players"), "online");
|
||||
} catch (Exception& e) {
|
||||
qWarning() << "server ping failed to parse response" << e.what();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerPingTask::executeTask()
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Note: If your FTB instances are not in the default location, select it using the button next to search.</string>
|
||||
<string>Note: Many recent FTB modpacks are also available from CurseForge! Also, if your FTB instances are not in the default location, select it using the button next to search.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -47,8 +47,7 @@
|
|||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="viewfolder">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="viewfolder"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
|
|
@ -82,7 +81,7 @@
|
|||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
++it;
|
||||
#endif
|
||||
for (const auto& version : m_current->versions) {
|
||||
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.addonId));
|
||||
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.fileId));
|
||||
}
|
||||
|
||||
QVariant current_updated;
|
||||
|
|
@ -227,9 +227,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
for (auto version : m_current->versions) {
|
||||
if (!version.version.contains(version.version))
|
||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
||||
QVariant(version.addonId));
|
||||
QVariant(version.fileId));
|
||||
else
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.addonId));
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.fileId));
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
|
|
@ -312,7 +312,7 @@ void ModrinthPage::suggestCurrent()
|
|||
}
|
||||
|
||||
for (auto& ver : m_current->versions) {
|
||||
if (ver.addonId == m_selectedVersion) {
|
||||
if (ver.fileId == m_selectedVersion) {
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", m_current->addonId.toString());
|
||||
extra_info.insert("pack_version_id", ver.fileId.toString());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QProgressDialog>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
|
|
@ -34,7 +35,6 @@
|
|||
#include "StringUtils.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "ui/dialogs/UpdateAvailableDialog.h"
|
||||
|
||||
|
|
@ -97,9 +97,14 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
|||
progress.show();
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QProcess proc;
|
||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
#if defined Q_OS_WIN32
|
||||
exe_name.append(".exe");
|
||||
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc.setProcessEnvironment(env);
|
||||
#else
|
||||
exe_name = QString("bin/%1").arg(exe_name);
|
||||
#endif
|
||||
|
|
@ -108,21 +113,15 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
|||
if (priv->allowBeta)
|
||||
args.append("--pre-release");
|
||||
|
||||
auto proc = FS::createProcess(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
#if defined Q_OS_WIN32
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc->setProcessEnvironment(env);
|
||||
#endif
|
||||
proc->start(proc->program(), proc->arguments());
|
||||
auto result_start = proc->waitForStarted(5000);
|
||||
proc.start(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
auto result_start = proc.waitForStarted(5000);
|
||||
if (!result_start) {
|
||||
auto err = proc->error();
|
||||
auto err = proc.error();
|
||||
qDebug() << "Failed to start updater after 5 seconds."
|
||||
<< "reason:" << err << proc->errorString();
|
||||
<< "reason:" << err << proc.errorString();
|
||||
auto msgBox =
|
||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
|
||||
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
|
|
@ -134,16 +133,16 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
|||
}
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto result_finished = proc->waitForFinished(60000);
|
||||
auto result_finished = proc.waitForFinished(60000);
|
||||
if (!result_finished) {
|
||||
proc->kill();
|
||||
auto err = proc->error();
|
||||
auto output = proc->readAll();
|
||||
proc.kill();
|
||||
auto err = proc.error();
|
||||
auto output = proc.readAll();
|
||||
qDebug() << "Updater failed to close after 60 seconds."
|
||||
<< "reason:" << err << proc->errorString();
|
||||
<< "reason:" << err << proc.errorString();
|
||||
auto msgBox =
|
||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
|
||||
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||
msgBox.setDetailedText(output);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
|
|
@ -155,10 +154,10 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
|||
return;
|
||||
}
|
||||
|
||||
auto exit_code = proc->exitCode();
|
||||
auto exit_code = proc.exitCode();
|
||||
|
||||
auto std_output = proc->readAllStandardOutput();
|
||||
auto std_error = proc->readAllStandardError();
|
||||
auto std_output = proc.readAllStandardOutput();
|
||||
auto std_error = proc.readAllStandardError();
|
||||
|
||||
progress.hide();
|
||||
QCoreApplication::processEvents();
|
||||
|
|
@ -336,9 +335,14 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin
|
|||
|
||||
void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
||||
{
|
||||
QProcess proc;
|
||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
#if defined Q_OS_WIN32
|
||||
exe_name.append(".exe");
|
||||
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc.setProcessEnvironment(env);
|
||||
#else
|
||||
exe_name = QString("bin/%1").arg(exe_name);
|
||||
#endif
|
||||
|
|
@ -347,16 +351,9 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
|||
if (priv->allowBeta)
|
||||
args.append("--pre-release");
|
||||
|
||||
auto proc = FS::createProcess(exe_name, args);
|
||||
#if defined Q_OS_WIN32
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc->setProcessEnvironment(env);
|
||||
#endif
|
||||
|
||||
auto result = proc->startDetached(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
if (!result) {
|
||||
qDebug() << "Failed to start updater:" << proc->error() << proc->errorString();
|
||||
qDebug() << "Failed to start updater:" << proc.error() << proc.errorString();
|
||||
}
|
||||
QCoreApplication::exit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,19 +124,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||
logToConsole = parser.isSet("debug");
|
||||
|
||||
QString origCwdPath = QDir::currentPath();
|
||||
#if defined(Q_OS_LINUX)
|
||||
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
|
||||
// <root>/lib64
|
||||
// This is not the path to our actual binary, which we want
|
||||
QString binPath;
|
||||
if (DesktopServices::isSelfContained()) {
|
||||
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
|
||||
} else {
|
||||
binPath = applicationDirPath();
|
||||
}
|
||||
#else
|
||||
QString binPath = applicationDirPath();
|
||||
#endif
|
||||
|
||||
{ // find data director
|
||||
// Root path is used for updates and portable data
|
||||
|
|
@ -819,16 +807,13 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
|
|||
bool PrismUpdaterApp::callAppImageUpdate()
|
||||
{
|
||||
auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||
QProcess proc = QProcess();
|
||||
qDebug() << "Calling: AppImageUpdate" << appimage_path;
|
||||
const auto program = FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage");
|
||||
auto proc = FS::createProcess(program, { appimage_path });
|
||||
if (!proc) {
|
||||
qCritical() << "Unable to create process:" << program;
|
||||
return false;
|
||||
}
|
||||
auto result = proc->startDetached();
|
||||
proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage"));
|
||||
proc.setArguments({ appimage_path });
|
||||
auto result = proc.startDetached();
|
||||
if (!result)
|
||||
qDebug() << "Failed to start AppImageUpdate reason:" << proc->errorString();
|
||||
qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ symlinkJoin {
|
|||
|
||||
buildInputs = [
|
||||
kdePackages.qtbase
|
||||
kdePackages.qtimageformats
|
||||
kdePackages.qtsvg
|
||||
]
|
||||
++ lib.optional (
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ set(Launcher_MetaInfo "program_info/${Launcher_AppID}.metainfo.xml" PARENT_SCOPE
|
|||
set(Launcher_PNG_256 "program_info/${Launcher_AppID}_256.png" PARENT_SCOPE)
|
||||
set(Launcher_SVG "program_info/${Launcher_SVGFileName}" PARENT_SCOPE)
|
||||
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
|
||||
set(Launcher_Branding_MAC_ICON "program_info/PrismLauncher.icon" PARENT_SCOPE)
|
||||
set(Launcher_Branding_ICO "program_info/prismlauncher.ico")
|
||||
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
|
||||
set(Launcher_Branding_WindowsRC "program_info/prismlauncher.rc" PARENT_SCOPE)
|
||||
|
|
|
|||
91
program_info/PrismLauncher.icon/Assets/block.svg
Normal file
91
program_info/PrismLauncher.icon/Assets/block.svg
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
version="1.1"
|
||||
viewBox="0 0 12.7 12.7"
|
||||
id="svg_combined"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<title>Prism Launcher Logo</title>
|
||||
<g
|
||||
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
|
||||
id="g32_top"
|
||||
style="display:inline">
|
||||
<g
|
||||
transform="translate(13.26,2.2776)"
|
||||
id="g28"
|
||||
style="display:inline">
|
||||
<path
|
||||
transform="matrix(0.96975,0,0,0.96975,0.1921,0.1921)"
|
||||
d="m 6.3498,2.9393 c -0.34105,0 -2.7827,1.4099 -2.9532,1.7052 L 6.3498,9.7602 9.3036,4.6445 C 9.13308,4.34915 6.6909,2.9393 6.3498,2.9393 Z"
|
||||
fill="#ffffff"
|
||||
stroke-width="0.26458"
|
||||
id="path26"
|
||||
style="display:inline" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
|
||||
id="g32"
|
||||
style="display:inline">
|
||||
<path
|
||||
d="m 16.746,6.9737 2.8639,4.9609 c 0.33073,0 2.6991,-1.3672 2.8644,-1.6536 0.16536,-0.28642 0.16536,-3.0209 0,-3.3073 l -2.8644,1.6536 z"
|
||||
fill="#dfdfdf"
|
||||
stroke-width="0.26458"
|
||||
id="path30"
|
||||
style="display:inline" />
|
||||
</g>
|
||||
<path
|
||||
d="m 3.8299,4.8948 c -0.14551,0.25205 -0.14553,2.6584 0,2.9104 0.14553,0.25204 2.2292,1.4552 2.5203,1.4552 V 6.35 Z"
|
||||
fill="#d6d2d2"
|
||||
stroke-width="0.26458"
|
||||
id="path34"
|
||||
style="display:inline" />
|
||||
<metadata
|
||||
id="metadata64">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3 KiB |
95
program_info/PrismLauncher.icon/Assets/rainbow.svg
Normal file
95
program_info/PrismLauncher.icon/Assets/rainbow.svg
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
version="1.1"
|
||||
viewBox="0 0 12.7 12.7"
|
||||
id="svg_rainbow"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<title>Prism Launcher Logo</title>
|
||||
<g stroke-width="0.26458">
|
||||
<path
|
||||
d="M 6.35,0.52917 3.8292,4.89477 6.35,6.34997 8.8703,4.89477 8.97985,1.79517 C 7.82875,1.13058 6.64105,0.52907 6.35005,0.52907 Z"
|
||||
fill="#df6277"
|
||||
id="path6" />
|
||||
<path
|
||||
d="M 8.9798,1.7952 6.35,6.35 8.8703,7.8052 11.3911,3.4396 C 11.24558,3.18755 10.131,2.45985 8.9799,1.7953 Z"
|
||||
fill="#fb9168"
|
||||
id="path8" />
|
||||
<path
|
||||
d="M 11.391,3.4396 6.35,6.35 8.8703,7.8052 11.6092,6.35 c 0,-1.3292 -0.07255,-2.6584 -0.21808,-2.9104 z"
|
||||
fill="#f3db6c"
|
||||
id="path10" />
|
||||
<path
|
||||
d="m 6.35,6.35 v 2.9104 h 5.041 C 11.53652,9.00835 11.60907,7.6792 11.60908,6.35 Z"
|
||||
fill="#7ab392"
|
||||
id="path12" />
|
||||
<path
|
||||
d="m 6.35,6.35 v 2.9104 l 2.6298,1.6443 c 1.1511,-0.66459 2.2657,-1.3923 2.4112,-1.6443 z"
|
||||
fill="#4b7cbc"
|
||||
id="path14" />
|
||||
<path
|
||||
d="M 6.35,6.35 3.8292,7.8052 6.35,12.1708 c 0.29104,0 1.4787,-0.60148 2.6298,-1.2661 z"
|
||||
fill="#6f488c"
|
||||
id="path16" />
|
||||
<path
|
||||
d="M 3.8292,4.8948 1.3089,9.2604 c 0.29104,0.5041 4.459,2.9104 5.041,2.9104 V 6.35 Z"
|
||||
fill="#4d3f33"
|
||||
id="path18" />
|
||||
<path
|
||||
d="m 1.309,3.4396 c -0.29104,0.5041 -0.29104,5.3167 0,5.8208 L 6.35,6.35 V 3.4396 Z"
|
||||
fill="#7a573b"
|
||||
id="path20" />
|
||||
<path
|
||||
d="m 6.35,0.52917 c -0.58208,-2e-8 -4.75,2.4063 -5.041,2.9104 l 5.041,2.9104 z"
|
||||
fill="#99cd61"
|
||||
id="path22" />
|
||||
</g>
|
||||
<metadata
|
||||
id="metadata64">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
72
program_info/PrismLauncher.icon/icon.json
Normal file
72
program_info/PrismLauncher.icon/icon.json
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"color-space-for-untagged-svg-colors" : "display-p3",
|
||||
"fill" : {
|
||||
"solid" : "extended-gray:1.00000,1.00000"
|
||||
},
|
||||
"groups" : [
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"image-name" : "block.svg",
|
||||
"name" : "block",
|
||||
"position" : {
|
||||
"scale" : 19.28,
|
||||
"translation-in-points" : [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"shadow" : {
|
||||
"kind" : "neutral",
|
||||
"opacity" : 0.5
|
||||
},
|
||||
"translucency" : {
|
||||
"enabled" : true,
|
||||
"value" : 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"blur-material-specializations" : [
|
||||
{
|
||||
"value" : 0.5
|
||||
},
|
||||
{
|
||||
"appearance" : "dark",
|
||||
"value" : null
|
||||
}
|
||||
],
|
||||
"layers" : [
|
||||
{
|
||||
"blend-mode" : "normal",
|
||||
"fill" : "automatic",
|
||||
"glass" : true,
|
||||
"hidden" : false,
|
||||
"image-name" : "rainbow.svg",
|
||||
"name" : "rainbow",
|
||||
"position" : {
|
||||
"scale" : 19.28,
|
||||
"translation-in-points" : [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"shadow" : {
|
||||
"kind" : "neutral",
|
||||
"opacity" : 0.5
|
||||
},
|
||||
"translucency" : {
|
||||
"enabled" : false,
|
||||
"value" : 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"supported-platforms" : {
|
||||
"squares" : [
|
||||
"macOS"
|
||||
]
|
||||
}
|
||||
}
|
||||
191
tools/ninjatracing.py
Normal file
191
tools/ninjatracing.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright 2018 Nico Weber
|
||||
# Patched to work with v7 logs
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Converts one (or several) .ninja_log files into chrome's about:tracing format.
|
||||
|
||||
If clang -ftime-trace .json files are found adjacent to generated files they
|
||||
are embedded.
|
||||
|
||||
Usage:
|
||||
ninja -C $BUILDDIR
|
||||
ninjatracing $BUILDDIR/.ninja_log > trace.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import optparse
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
class Target:
|
||||
"""Represents a single line read for a .ninja_log file. Start and end times
|
||||
are milliseconds."""
|
||||
def __init__(self, start, end):
|
||||
self.start = int(start)
|
||||
self.end = int(end)
|
||||
self.targets = []
|
||||
|
||||
|
||||
def read_targets(log, show_all):
|
||||
"""Reads all targets from .ninja_log file |log_file|, sorted by start
|
||||
time"""
|
||||
header = log.readline()
|
||||
m = re.search(r'^# ninja log v(\d+)\n$', header)
|
||||
assert m, "unrecognized ninja log version %r" % header
|
||||
version = int(m.group(1))
|
||||
assert 5 <= version <= 7, "unsupported ninja log version %d" % version
|
||||
if version >= 6:
|
||||
# Skip header line
|
||||
next(log)
|
||||
|
||||
targets = {}
|
||||
last_end_seen = 0
|
||||
for line in log:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
start, end, _, name, cmdhash = line.strip().split('\t') # Ignore restat.
|
||||
if not show_all and int(end) < last_end_seen:
|
||||
# An earlier time stamp means that this step is the first in a new
|
||||
# build, possibly an incremental build. Throw away the previous data
|
||||
# so that this new build will be displayed independently.
|
||||
targets = {}
|
||||
last_end_seen = int(end)
|
||||
targets.setdefault(cmdhash, Target(start, end)).targets.append(name)
|
||||
return sorted(targets.values(), key=lambda job: job.end, reverse=True)
|
||||
|
||||
|
||||
class Threads:
|
||||
"""Tries to reconstruct the parallelism from a .ninja_log"""
|
||||
def __init__(self):
|
||||
self.workers = [] # Maps thread id to time that thread is occupied for.
|
||||
|
||||
def alloc(self, target):
|
||||
"""Places target in an available thread, or adds a new thread."""
|
||||
for worker in range(len(self.workers)):
|
||||
if self.workers[worker] >= target.end:
|
||||
self.workers[worker] = target.start
|
||||
return worker
|
||||
self.workers.append(target.start)
|
||||
return len(self.workers) - 1
|
||||
|
||||
|
||||
def read_events(trace, options):
|
||||
"""Reads all events from time-trace json file |trace|."""
|
||||
trace_data = json.load(trace)
|
||||
|
||||
def include_event(event, options):
|
||||
"""Only include events if they are complete events, are longer than
|
||||
granularity, and are not totals."""
|
||||
return ((event['ph'] == 'X') and
|
||||
(event['dur'] >= options['granularity']) and
|
||||
(not event['name'].startswith('Total')))
|
||||
|
||||
return [x for x in trace_data['traceEvents'] if include_event(x, options)]
|
||||
|
||||
|
||||
def trace_to_dicts(target, trace, options, pid, tid):
|
||||
"""Read a file-like object |trace| containing -ftime-trace data and yields
|
||||
about:tracing dict per eligible event in that log."""
|
||||
ninja_time = (target.end - target.start) * 1000
|
||||
for event in read_events(trace, options):
|
||||
# Check if any event duration is greater than the duration from ninja.
|
||||
if event['dur'] > ninja_time:
|
||||
print("Inconsistent timing found (clang time > ninja time). Please"
|
||||
" ensure that timings are from consistent builds.")
|
||||
sys.exit(1)
|
||||
|
||||
# Set tid and pid from ninja log.
|
||||
event['pid'] = pid
|
||||
event['tid'] = tid
|
||||
|
||||
# Offset trace time stamp by ninja start time.
|
||||
event['ts'] += (target.start * 1000)
|
||||
|
||||
yield event
|
||||
|
||||
|
||||
def embed_time_trace(ninja_log_dir, target, pid, tid, options):
|
||||
"""Produce time trace output for the specified ninja target. Expects
|
||||
time-trace file to be in .json file named based on .o file."""
|
||||
for t in target.targets:
|
||||
o_path = os.path.join(ninja_log_dir, t)
|
||||
json_trace_path = os.path.splitext(o_path)[0] + '.json'
|
||||
try:
|
||||
with open(json_trace_path, 'r') as trace:
|
||||
for time_trace_event in trace_to_dicts(target, trace, options,
|
||||
pid, tid):
|
||||
yield time_trace_event
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def log_to_dicts(log, pid, options):
|
||||
"""Reads a file-like object |log| containing a .ninja_log, and yields one
|
||||
about:tracing dict per command found in the log."""
|
||||
threads = Threads()
|
||||
for target in read_targets(log, options['showall']):
|
||||
tid = threads.alloc(target)
|
||||
|
||||
yield {
|
||||
'name': '%0s' % ', '.join(target.targets), 'cat': 'targets',
|
||||
'ph': 'X', 'ts': (target.start * 1000),
|
||||
'dur': ((target.end - target.start) * 1000),
|
||||
'pid': pid, 'tid': tid, 'args': {},
|
||||
}
|
||||
if options.get('embed_time_trace', False):
|
||||
# Add time-trace information into the ninja trace.
|
||||
try:
|
||||
ninja_log_dir = os.path.dirname(log.name)
|
||||
except AttributeError:
|
||||
continue
|
||||
for time_trace in embed_time_trace(ninja_log_dir, target, pid,
|
||||
tid, options):
|
||||
yield time_trace
|
||||
|
||||
|
||||
def main(argv):
|
||||
usage = __doc__
|
||||
parser = optparse.OptionParser(usage)
|
||||
parser.add_option('-a', '--showall', action='store_true', dest='showall',
|
||||
default=False,
|
||||
help='report on last build step for all outputs. Default '
|
||||
'is to report just on the last (possibly incremental) '
|
||||
'build')
|
||||
parser.add_option('-g', '--granularity', type='int', default=50000,
|
||||
dest='granularity',
|
||||
help='minimum length time-trace event to embed in '
|
||||
'microseconds. Default: %default')
|
||||
parser.add_option('-e', '--embed-time-trace', action='store_true',
|
||||
default=False, dest='embed_time_trace',
|
||||
help='embed clang -ftime-trace json file found adjacent '
|
||||
'to a target file')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) == 0:
|
||||
print('Must specify at least one .ninja_log file')
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
entries = []
|
||||
for pid, log_file in enumerate(args):
|
||||
with open(log_file, 'r') as log:
|
||||
entries += list(log_to_dicts(log, pid, vars(options)))
|
||||
json.dump(entries, sys.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
Loading…
Add table
Add a link
Reference in a new issue