Merge branch 'master' of github.com:Chatterino/chatterino2 into feature/image_uploader_ui

This commit is contained in:
Mm2PL 2024-03-13 03:01:17 +01:00
commit b5da4746f6
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
549 changed files with 16311 additions and 7547 deletions

View file

@ -21,15 +21,12 @@ deb_path="Chatterino-ubuntu-${ubuntu_release}-x86_64.deb"
# Refactor opportunity:
case "$ubuntu_release" in
20.04)
dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.71.0"
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
dependencies="libc6, libstdc++6, libblkid1, libbsd0, libc6, libexpat1, libffi7, libfontconfig1, libfreetype6, libglib2.0-0, libglvnd0, libglx0, libgraphite2-3, libharfbuzz0b, libicu66, libjpeg-turbo8, libmount1, libopengl0, libpcre2-16-0, libpcre3, libpng16-16, libselinux1, libssl1.1, libstdc++6, libuuid1, libx11-xcb1, libxau6, libxcb1, libxcb-cursor0, libxcb-glx0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render0, libxcb-render-util0, libxcb-shape0, libxcb-shm0, libxcb-sync1, libxcb-util1, libxcb-xfixes0, libxcb-xkb1, libxdmcp6, libxkbcommon0, libxkbcommon-x11-0, zlib1g"
;;
22.04)
if [ -n "$Qt6_DIR" ]; then
echo "Qt6_DIR set, assuming Qt6"
dependencies="libc6, libstdc++6, libqt6core6, libqt6widgets6, libqt6network6, libqt6core5compat6, libqt6svg6, qt6-qpa-plugins, qt6-image-formats-plugins"
else
dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.74.0"
fi
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
dependencies="libc6, libstdc++6, libglx0, libopengl0, libpng16-16, libharfbuzz0b, libfreetype6, libfontconfig1, libjpeg-turbo8, libxcb-glx0, libegl1, libx11-6, libxkbcommon0, libx11-xcb1, libxkbcommon-x11-0, libxcb-cursor0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render-util0, libxcb-shm0, libxcb-sync1, libxcb-xfixes0, libxcb-render0, libxcb-shape0, libxcb-xkb1, libxcb1, libbrotli1, libglib2.0-0, zlib1g, libicu70, libpcre2-16-0, libssl3, libgraphite2-3, libexpat1, libuuid1, libxcb-util1, libxau6, libxdmcp6, libbrotli1, libffi8, libmount1, libselinux1, libpcre3, libicu70, libbsd0, libblkid1, libpcre2-8-0, libmd0"
;;
*)
echo "Unsupported Ubuntu release $ubuntu_release"

View file

@ -1,9 +1,9 @@
[Flatpak Ref]
Name=com.chatterino.chatterino
Branch=nightly
Branch=beta
Title=com.chatterino.chatterino from flathub
IsRuntime=false
Url=https://dl.flathub.org/repo/
SuggestRemoteName=flathub
Url=https://dl.flathub.org/beta-repo/
SuggestRemoteName=flathub-beta
GPGKey=mQINBFlD2sABEADsiUZUOYBg1UdDaWkEdJYkTSZD68214m8Q1fbrP5AptaUfCl8KYKFMNoAJRBXn9FbE6q6VBzghHXj/rSnA8WPnkbaEWR7xltOqzB1yHpCQ1l8xSfH5N02DMUBSRtD/rOYsBKbaJcOgW0K21sX+BecMY/AI2yADvCJEjhVKrjR9yfRX+NQEhDcbXUFRGt9ZT+TI5yT4xcwbvvTu7aFUR/dH7+wjrQ7lzoGlZGFFrQXSs2WI0WaYHWDeCwymtohXryF8lcWQkhH8UhfNJVBJFgCY8Q6UHkZG0FxMu8xnIDBMjBmSZKwKQn0nwzwM2afskZEnmNPYDI8nuNsSZBZSAw+ThhkdCZHZZRwzmjzyRuLLVFpOj3XryXwZcSefNMPDkZAuWWzPYjxS80cm2hG1WfqrG0Gl8+iX69cbQchb7gbEb0RtqNskTo9DDmO0bNKNnMbzmIJ3/rTbSahKSwtewklqSP/01o0WKZiy+n/RAkUKOFBprjJtWOZkc8SPXV/rnoS2dWsJWQZhuPPtv3tefdDiEyp7ePrfgfKxuHpZES0IZRiFI4J/nAUP5bix+srcIxOVqAam68CbAlPvWTivRUMRVbKjJiGXIOJ78wAMjqPg3QIC0GQ0EPAWwAOzzpdgbnG7TCQetaVV8rSYCuirlPYN+bJIwBtkOC9SWLoPMVZTwQARAQABtC5GbGF0aHViIFJlcG8gU2lnbmluZyBLZXkgPGZsYXRodWJAZmxhdGh1Yi5vcmc+iQJUBBMBCAA+FiEEblwF2XnHba+TwIE1QYTdTZB6fK4FAllD2sACGwMFCRLMAwAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQQYTdTZB6fK5RJQ/+Ptd4sWxaiAW91FFk7+wmYOkEe1NY2UDNJjEEz34PNP/1RoxveHDt43kYJQ23OWaPJuZAbu+fWtjRYcMBzOsMCaFcRSHFiDIC9aTp4ux/mo+IEeyarYt/oyKb5t5lta6xaAqg7rwt65jW5/aQjnS4h7eFZ+dAKta7Y/fljNrOznUp81/SMcx4QA5G2Pw0hs4Xrxg59oONOTFGBgA6FF8WQghrpR7SnEe0FSEOVsAjwQ13Cfkfa7b70omXSWp7GWfUzgBKyoWxKTqzMN3RQHjjhPJcsQnrqH5enUu4Pcb2LcMFpzimHnUgb9ft72DP5wxfzHGAWOUiUXHbAekfq5iFks8cha/RST6wkxG3Rf44Zn09aOxh1btMcGL+5xb1G0BuCQnA0fP/kDYIPwh9z22EqwRQOspIcvGeLVkFeIfubxpcMdOfQqQnZtHMCabV5Q/Rk9K1ZGc8M2hlg8gHbXMFch2xJ0Wu72eXbA/UY5MskEeBgawTQnQOK/vNm7t0AJMpWK26Qg6178UmRghmeZDj9uNRc3EI1nSbgvmGlpDmCxaAGqaGL1zW4KPW5yN25/qeqXcgCvUjZLI9PNq3Kvizp1lUrbx7heRiSoazCucvHQ1VHUzcPVLUKKTkoTP8okThnRRRsBcZ1+jI4yMWIDLOCT7IW3FePr+3xyuy5eEo9a25Ag0EWUPa7AEQALT/CmSyZ8LWlRYQZKYw417p7Z2hxqd6TjwkwM3IQ1irumkWcTZBZIbBgrSOg6CcXD2oWydCQHWi9qaxhuhEl2bJL5LskmBcMxVdQeD0LLHd8QUnbnnIby8ocvWN1alPfvJFjCUTrmD22U1ycOzRw2lIe4kiQONbOZtdWrVImQQSndjFlisitbmlWHvHm2lOOYy8+GJB7YffVV193hmnBSJffCy4bvkuLxsI+n1DhOzc7MPV3z6HGk4HiEcF0yyt9tCYhpsxHFdBoq2h771HfAcS0s98EVAqYMFnf9em+4cnYpdI6mhIfS1FQiKl6DBAYA8tT3ggla00DurPo0JwX/zN+PaO5h/6O9aCZwV7G6rbkgMuqMergXaf8oP38gr0z+MqWnkfM63Bodq68GP4l4hd02BoFBbDf38TMuGQB14+twJMdfbAxo2MbgluvQgfwHfZ2ca6gyEY+9s/YD1gugLjV+S6CB51WkFNe1z4tAPgJZNxUcKCbeaHNbthl8Hks/pY9RCEseX/EdfzF18epbSjJMPh4DPQXbUoFwmyuYcoBOPmvZHNl9hK7B/1RP8w1ZrXk8qdupC0SNbafX7270B7lMMVImzZetGsM9ypXJ6llhp3FwW09iseNyGJGPsr/dvTMGDXqOPfU/9SAS1LSTY4K9PbRtdrBE318YX8mIk5ABEBAAGJBHIEGAEIACYWIQRuXAXZecdtr5PAgTVBhN1NkHp8rgUCWUPa7AIbAgUJEswDAAJACRBBhN1NkHp8rsF0IAQZAQgAHRYhBFSmzd2JGfsgQgDYrFYnAunj7X7oBQJZQ9rsAAoJEFYnAunj7X7oR6AP/0KYmiAFeqx14Z43/6s2gt3VhxlSd8bmcVV7oJFbMhdHBIeWBp2BvsUf00I0Zl14ZkwCKfLwbbORC2eIxvzJ+QWjGfPhDmS4XUSmhlXxWnYEveSek5Tde+fmu6lqKM8CHg5BNx4GWIX/vdLi1wWJZyhrUwwICAxkuhKxuP2Z1An48930eslTD2GGcjByc27+9cIZjHKa07I/aLffo04V+oMT9/tgzoquzgpVV4jwekADo2MJjhkkPveSNI420bgT+Q7Fi1l0X1aFUniBvQMsaBa27PngWm6xE2ZYvh7nWCdd5g0c0eLIHxWwzV1lZ4Ryx4ITO/VL25ItECcjhTRdYa64sA62MYSaB0x3eR+SihpgP3wSNPFu3MJo6FKTFdi4CBAEmpWHFW7FcRmd+cQXeFrHLN3iNVWryy0HK/CUEJmiZEmpNiXecl4vPIIuyF0zgSCztQtKoMr+injpmQGC/rF/ELBVZTUSLNB350S0Ztvw0FKWDAJSxFmoxt3xycqvvt47rxTrhi78nkk6jATKGyvP55sO+K7Q7Wh0DXA69hvPrYW2eu8jGCdVGxi6HX7L1qcfEd0378S71dZ3g9o6KKl1OsDWWQ6MJ6FGBZedl/ibRfs8p5+sbCX3lQSjEFy3rx6n0rUrXx8U2qb+RCLzJlmC5MNBOTDJwHPcX6gKsUcXZrEQALmRHoo3SrewO41RCr+5nUlqiqV3AohBMhnQbGzyHf2+drutIaoh7Rj80XRh2bkkuPLwlNPf+bTXwNVGse4bej7B3oV6Ae1N7lTNVF4Qh+1OowtGjmfJPWo0z1s6HFJVxoIof9z58Msvgao0zrKGqaMWaNQ6LUeC9g9Aj/9Uqjbo8X54aLiYs8Z1WNc06jKP+gv8AWLtv6CR+l2kLez1YMDucjm7v6iuCMVAmZdmxhg5I/X2+OM3vBsqPDdQpr2TPDLX3rCrSBiS0gOQ6DwN5N5QeTkxmY/7QO8bgLo/Wzu1iilH4vMKW6LBKCaRx5UEJxKpL4wkgITsYKneIt3NTHo5EOuaYk+y2+Dvt6EQFiuMsdbfUjs3seIHsghX/cbPJa4YUqZAL8C4OtVHaijwGo0ymt9MWvS9yNKMyT0JhN2/BdeOVWrHk7wXXJn/ZjpXilicXKPx4udCF76meE+6N2u/T+RYZ7fP1QMEtNZNmYDOfA6sViuPDfQSHLNbauJBo/n1sRYAsL5mcG22UDchJrlKvmK3EOADCQg+myrm8006LltubNB4wWNzHDJ0Ls2JGzQZCd/xGyVmUiidCBUrD537WdknOYE4FD7P0cHaM9brKJ/M8LkEH0zUlo73bY4XagbnCqve6PvQb5G2Z55qhWphd6f4B6DGed86zJEa/RhS
RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo

39
.CI/full-ubuntu-build.sh Executable file
View file

@ -0,0 +1,39 @@
#!/bin/sh
# TODO: Investigate if the -fno-sized-deallocation flag is still necessary
# TODO: Test appimage/deb creation
set -e
env
BUILD_TESTS="On"
BUILD_BENCHMARKS="ON"
ubuntu_version="$(lsb_release -sr)"
if [ "$ubuntu_version" = "20.04" ]; then
BUILD_TESTS="Off"
BUILD_BENCHMARKS="Off"
fi
rm -rf build
mkdir build
cmake \
-B build \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_APP=On \
-DBUILD_TESTS="$BUILD_TESTS" \
-DBUILD_BENCHMARKS="$BUILD_BENCHMARKS" \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
-DCHATTERINO_STATIC_QT_BUILD=On \
-DCMAKE_CXX_FLAGS="-fno-sized-deallocation" \
.
cmake --build build
# sh ./../.CI/CreateAppImage.sh
# sh ./../.CI/CreateUbuntuDeb.sh

View file

@ -29,6 +29,7 @@ Checks: "-*,
-readability-function-cognitive-complexity,
-bugprone-easily-swappable-parameters,
-cert-err58-cpp,
-modernize-avoid-c-arrays
"
CheckOptions:
- key: readability-identifier-naming.ClassCase
@ -44,11 +45,9 @@ CheckOptions:
- key: readability-identifier-naming.MemberCase
value: camelBack
- key: readability-identifier-naming.PrivateMemberIgnoredRegexp
value: .*
- key: readability-identifier-naming.PrivateMemberSuffix
value: _
- key: readability-identifier-naming.ProtectedMemberSuffix
value: _
value: ^.*_$
- key: readability-identifier-naming.ProtectedMemberIgnoredRegexp
value: ^.*_$
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.GlobalConstantCase
@ -64,5 +63,11 @@ CheckOptions:
- key: readability-identifier-naming.LocalPointerIgnoredRegexp
value: ^L$
# Benchmarks
- key: readability-identifier-naming.FunctionIgnoredRegexp
value: ^BM_[^_]+$
- key: readability-identifier-naming.ClassIgnoredRegexp
value: ^BM_[^_]+$
- key: misc-const-correctness.AnalyzeValues
value: false

View file

@ -36,7 +36,7 @@ NOTE: The AppImage from Ubuntu 22.04 is broken. Approach with caution
#### Testing
1. Build a docker image builds the chatterino tests
1. Build a docker image builds the Chatterino tests
`docker buildx build -t chatterino-ubuntu-22.04-test -f .docker/Dockerfile-ubuntu-22.04-test .`
1. Run the tests
`docker run --rm --network=host chatterino-ubuntu-22.04-test`

View file

@ -1,3 +1,5 @@
# Description
<!-- If applicable, please include a summary of what you've changed and what issue is fixed. In the case of a bug fix, please include steps to reproduce the bug so the pull request can be tested -->
<!--
Please include a summary of what you've changed and what issue is fixed.
In the case of a bug fix, please include steps to reproduce the bug so the pull request can be tested.
If this PR fixes an issue on GitHub, mention this here to automatically close it: "Fixes #1234.".
-->

View file

@ -24,36 +24,93 @@ env:
CONAN_VERSION: 2.0.2
jobs:
build-ubuntu-docker:
name: "Build Ubuntu in Docker"
runs-on: ubuntu-latest
container: ${{ matrix.container }}
strategy:
matrix:
include:
- os: ubuntu-20.04
container: ghcr.io/chatterino/chatterino2-build-ubuntu-20.04:latest
qt-version: 6.6.1
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
build-appimage: false
build-deb: true
- os: ubuntu-22.04
container: ghcr.io/chatterino/chatterino2-build-ubuntu-22.04:latest
qt-version: 6.6.1
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
build-appimage: true
build-deb: true
env:
C2_ENABLE_LTO: ${{ matrix.force-lto }}
C2_PLUGINS: ${{ matrix.plugins }}
C2_ENABLE_CRASHPAD: ${{ matrix.skip-crashpad == false }}
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Build
run: |
mkdir build
cd build
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_BUILD_TYPE=Release \
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
-DCHATTERINO_STATIC_QT_BUILD=On \
..
make -j"$(nproc)"
- name: Package - AppImage (Ubuntu)
if: matrix.build-appimage
run: |
cd build
sh ./../.CI/CreateAppImage.sh
- name: Upload artifact - AppImage (Ubuntu)
if: matrix.build-appimage
uses: actions/upload-artifact@v4
with:
name: Chatterino-x86_64-Qt-${{ matrix.qt-version }}.AppImage
path: build/Chatterino-x86_64.AppImage
- name: Package - .deb (Ubuntu)
if: matrix.build-deb
run: |
cd build
sh ./../.CI/CreateUbuntuDeb.sh
- name: Upload artifact - .deb (Ubuntu)
if: matrix.build-deb
uses: actions/upload-artifact@v4
with:
name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}.deb
path: build/Chatterino-${{ matrix.os }}-x86_64.deb
build:
name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (LTO:${{ matrix.force-lto }}, crashpad:${{ matrix.skip-crashpad && 'off' || 'on' }})"
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
# Ubuntu 20.04, Qt 5.12
- os: ubuntu-20.04
qt-version: 5.12.12
force-lto: false
plugins: false
skip-artifact: false
skip-crashpad: false
clang-tidy-review: false
# Ubuntu 22.04, Qt 5.15
- os: ubuntu-22.04
qt-version: 5.15.2
force-lto: false
plugins: false
skip-artifact: false
skip-crashpad: false
clang-tidy-review: true
# Ubuntu 22.04, Qt 6.2.4 - tests LTO & plugins
- os: ubuntu-22.04
qt-version: 6.2.4
force-lto: true
plugins: true
skip-artifact: false
skip-crashpad: false
clang-tidy-review: false
# macOS
- os: macos-latest
qt-version: 5.15.2
@ -61,7 +118,6 @@ jobs:
plugins: false
skip-artifact: false
skip-crashpad: false
clang-tidy-review: false
# Windows
- os: windows-latest
qt-version: 6.5.0
@ -69,7 +125,6 @@ jobs:
plugins: false
skip-artifact: false
skip-crashpad: false
clang-tidy-review: false
# Windows 7/8
- os: windows-latest
qt-version: 5.15.2
@ -77,41 +132,15 @@ jobs:
plugins: false
skip-artifact: false
skip-crashpad: true
clang-tidy-review: false
fail-fast: false
env:
C2_ENABLE_LTO: ${{ matrix.force-lto }}
C2_PLUGINS: ${{ matrix.plugins }}
C2_ENABLE_CRASHPAD: ${{ matrix.skip-crashpad == false }}
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
steps:
- name: Force LTO
if: matrix.force-lto
run: |
echo "C2_ENABLE_LTO=ON" >> "$GITHUB_ENV"
shell: bash
- name: Enable plugin support
if: matrix.plugins
run: |
echo "C2_PLUGINS=ON" >> "$GITHUB_ENV"
shell: bash
- name: Set Crashpad
if: matrix.skip-crashpad == false
run: |
echo "C2_ENABLE_CRASHPAD=ON" >> "$GITHUB_ENV"
shell: bash
- name: Set environment variables for windows-latest
if: matrix.os == 'windows-latest'
run: |
echo "vs_version=2022" >> "$GITHUB_ENV"
shell: bash
- name: Set BUILD_WITH_QT6
if: startsWith(matrix.qt-version, '6.')
run: |
echo "C2_BUILD_WITH_QT6=ON" >> "$GITHUB_ENV"
shell: bash
- uses: actions/checkout@v4
with:
submodules: recursive
@ -157,7 +186,7 @@ jobs:
# WINDOWS
- name: Enable Developer Command Prompt (Windows)
if: startsWith(matrix.os, 'windows')
uses: ilammy/msvc-dev-cmd@v1.12.1
uses: ilammy/msvc-dev-cmd@v1.13.0
- name: Setup conan variables (Windows)
if: startsWith(matrix.os, 'windows')
@ -167,8 +196,8 @@ jobs:
shell: powershell
- name: Setup sccache (Windows)
# sccache v0.5.3
uses: nerixyz/ccache-action@9a7e8d00116ede600ee7717350c6594b8af6aaa5
# sccache v0.7.4
uses: hendrikmuhs/ccache-action@v1.2.12
if: startsWith(matrix.os, 'windows')
with:
variant: sccache
@ -180,7 +209,7 @@ jobs:
- name: Cache conan packages (Windows)
if: startsWith(matrix.os, 'windows')
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.py') }}${{ env.C2_CONAN_CACHE_SUFFIX }}
path: ~/.conan2/
@ -236,9 +265,9 @@ jobs:
run: |
cd build
set cl=/MP
nmake /S /NOLOGO crashpad_handler
nmake /S /NOLOGO chatterino-crash-handler
mkdir Chatterino2/crashpad
cp bin/crashpad/crashpad_handler.exe Chatterino2/crashpad/crashpad_handler.exe
cp bin/crashpad/crashpad-handler.exe Chatterino2/crashpad/crashpad-handler.exe
7z a bin/chatterino-Qt-${{ matrix.qt-version }}.pdb.7z bin/chatterino.pdb
- name: Prepare build dir (windows)
@ -263,14 +292,14 @@ jobs:
- name: Upload artifact (Windows - binary)
if: startsWith(matrix.os, 'windows') && !matrix.skip-artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip
path: build/chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip
- name: Upload artifact (Windows - symbols)
if: startsWith(matrix.os, 'windows') && !matrix.skip-artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}-symbols.pdb.7z
path: build/bin/chatterino-Qt-${{ matrix.qt-version }}.pdb.7z
@ -280,115 +309,6 @@ jobs:
run: conan cache clean --source --build --download "*"
shell: bash
# LINUX
- name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get -y install \
cmake \
virtualenv \
rapidjson-dev \
libfuse2 \
libssl-dev \
libboost-dev \
libxcb-randr0-dev \
libboost-system-dev \
libboost-filesystem-dev \
libpulse-dev \
libxkbcommon-x11-0 \
build-essential \
libgl1-mesa-dev \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-xinerama0
- name: Apply Qt patches (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.qt-version, '5.')
run: |
patch "$Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h" .patches/qt5-on-newer-gcc.patch
shell: bash
- name: Build (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
mkdir build
cd build
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_BUILD_TYPE=Release \
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
..
make -j"$(nproc)"
shell: bash
- name: clang-tidy review
if: matrix.clang-tidy-review && github.event_name == 'pull_request'
timeout-minutes: 10
uses: ZedThree/clang-tidy-review@v0.14.0
with:
build_dir: build-clang-tidy
config_file: ".clang-tidy"
split_workflow: true
exclude: "lib/*"
cmake_command: >-
cmake -S. -Bbuild-clang-tidy
-DCMAKE_BUILD_TYPE=Release
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On
-DUSE_PRECOMPILED_HEADERS=OFF
-DCMAKE_EXPORT_COMPILE_COMMANDS=On
-DCHATTERINO_LTO=Off
-DCHATTERINO_PLUGINS=On
-DBUILD_WITH_QT6=Off
-DBUILD_TESTS=On
-DBUILD_BENCHMARKS=On
apt_packages: >-
qttools5-dev, qt5-image-formats-plugins, libqt5svg5-dev,
libsecret-1-dev,
libboost-dev, libboost-system-dev, libboost-filesystem-dev,
libssl-dev,
rapidjson-dev,
libbenchmark-dev
- name: clang-tidy-review upload
if: matrix.clang-tidy-review && github.event_name == 'pull_request'
uses: ZedThree/clang-tidy-review/upload@v0.14.0
- name: Package - AppImage (Ubuntu)
if: startsWith(matrix.os, 'ubuntu-20.04') && !matrix.skip-artifact
run: |
cd build
sh ./../.CI/CreateAppImage.sh
shell: bash
- name: Package - .deb (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') && !matrix.skip-artifact
run: |
cd build
sh ./../.CI/CreateUbuntuDeb.sh
shell: bash
- name: Upload artifact - AppImage (Ubuntu)
if: startsWith(matrix.os, 'ubuntu-20.04') && !matrix.skip-artifact
uses: actions/upload-artifact@v3
with:
name: Chatterino-x86_64-${{ matrix.qt-version }}.AppImage
path: build/Chatterino-x86_64.AppImage
- name: Upload artifact - .deb (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') && !matrix.skip-artifact
uses: actions/upload-artifact@v3
with:
name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}.deb
path: build/Chatterino-${{ matrix.os }}-x86_64.deb
# MACOS
- name: Install dependencies (MacOS)
if: startsWith(matrix.os, 'macos')
@ -428,12 +348,13 @@ jobs:
- name: Upload artifact (MacOS)
if: startsWith(matrix.os, 'macos')
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: chatterino-macos-Qt-${{ matrix.qt-version }}.dmg
path: build/chatterino-macos-Qt-${{ matrix.qt-version }}.dmg
create-release:
needs: build
needs: [build-ubuntu-docker, build]
runs-on: ubuntu-latest
if: (github.event_name == 'push' && github.ref == 'refs/heads/master')
@ -442,52 +363,42 @@ jobs:
with:
fetch-depth: 0 # allows for tags access
- uses: actions/download-artifact@v3
name: Ubuntu 22.04 Qt6.2.4 deb
with:
name: Chatterino-ubuntu-22.04-Qt-6.2.4.deb
path: release-artifacts/
- uses: actions/download-artifact@v3
# Windows
- uses: actions/download-artifact@v4
name: Windows Qt6.5.0
with:
name: chatterino-windows-x86-64-Qt-6.5.0.zip
path: release-artifacts/
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
name: Windows Qt6.5.0 symbols
with:
name: chatterino-windows-x86-64-Qt-6.5.0-symbols.pdb.7z
path: release-artifacts/
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
name: Windows Qt5.15.2
with:
name: chatterino-windows-x86-64-Qt-5.15.2.zip
path: release-artifacts/
- uses: actions/download-artifact@v3
name: Linux Qt5.12.12 AppImage
# Linux
- uses: actions/download-artifact@v4
name: Linux AppImage
with:
name: Chatterino-x86_64-5.12.12.AppImage
name: Chatterino-x86_64-Qt-6.6.1.AppImage
path: release-artifacts/
- uses: actions/download-artifact@v3
name: Ubuntu 20.04 Qt5.12.12 deb
- uses: actions/download-artifact@v4
name: Ubuntu 20.04 deb
with:
name: Chatterino-ubuntu-20.04-Qt-5.12.12.deb
name: Chatterino-ubuntu-20.04-Qt-6.6.1.deb
path: release-artifacts/
- uses: actions/download-artifact@v3
name: Ubuntu 22.04 Qt5.15.2 deb
- uses: actions/download-artifact@v4
name: Ubuntu 22.04 deb
with:
name: Chatterino-ubuntu-22.04-Qt-5.15.2.deb
path: release-artifacts/
- uses: actions/download-artifact@v3
name: macOS x86_64 Qt5.15.2 dmg
with:
name: chatterino-macos-Qt-5.15.2.dmg
name: Chatterino-ubuntu-22.04-Qt-6.6.1.deb
path: release-artifacts/
- name: Copy flatpakref
@ -495,21 +406,26 @@ jobs:
cp .CI/chatterino-nightly.flatpakref release-artifacts/
shell: bash
# macOS
- uses: actions/download-artifact@v4
name: macOS x86_64 Qt5.15.2 dmg
with:
name: chatterino-macos-Qt-5.15.2.dmg
path: release-artifacts/
- name: Rename artifacts
run: |
ls -l
# Rename the macos build to indicate that it's for macOS 10.15 users
mv chatterino-macos-Qt-5.15.2.dmg Chatterino-macOS-10.15.dmg
mv Chatterino-ubuntu-22.04-x86_64.deb EXPERIMENTAL-Chatterino-ubuntu-22.04-Qt-6.2.4.deb
# Mark all Windows Qt5 builds as old
mv chatterino-windows-x86-64-Qt-5.15.2.zip chatterino-windows-old-x86-64-Qt-5.15.2.zip
working-directory: release-artifacts
shell: bash
- name: Create release
uses: ncipollo/release-action@v1.13.0
uses: ncipollo/release-action@v1.14.0
with:
replacesArtifacts: true
allowUpdates: true

View file

@ -33,4 +33,4 @@ jobs:
clangFormatVersion: 16
- name: Check line-endings
run: ./tools/check-line-endings.sh
run: ./scripts/check-line-endings.sh

148
.github/workflows/clang-tidy.yml vendored Normal file
View file

@ -0,0 +1,148 @@
---
name: clang-tidy
on:
pull_request:
concurrency:
group: clang-tidy-${{ github.ref }}
cancel-in-progress: true
env:
CHATTERINO_REQUIRE_CLEAN_GIT: On
C2_BUILD_WITH_QT6: Off
jobs:
build:
name: "clang-tidy ${{ matrix.os }}, Qt ${{ matrix.qt-version }})"
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
# Ubuntu 22.04, Qt 5.15
- os: ubuntu-22.04
qt-version: 5.15.2
plugins: false
fail-fast: false
steps:
- name: Enable plugin support
if: matrix.plugins
run: |
echo "C2_PLUGINS=ON" >> "$GITHUB_ENV"
shell: bash
- name: Set BUILD_WITH_QT6
if: startsWith(matrix.qt-version, '6.')
run: |
echo "C2_BUILD_WITH_QT6=ON" >> "$GITHUB_ENV"
shell: bash
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Install Qt5
if: startsWith(matrix.qt-version, '5.')
uses: jurplel/install-qt-action@v3.3.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
version: ${{ matrix.qt-version }}
- name: Install Qt 6.5.3 imageformats
if: startsWith(matrix.qt-version, '6.')
uses: jurplel/install-qt-action@v3.3.0
with:
cache: false
modules: qtimageformats
set-env: false
version: 6.5.3
extra: --noarchives
- name: Install Qt6
if: startsWith(matrix.qt-version, '6.')
uses: jurplel/install-qt-action@v3.3.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: qt5compat qtimageformats
version: ${{ matrix.qt-version }}
# LINUX
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get -y install \
cmake \
virtualenv \
rapidjson-dev \
libfuse2 \
libssl-dev \
libboost-dev \
libxcb-randr0-dev \
libboost-system-dev \
libboost-filesystem-dev \
libpulse-dev \
libxkbcommon-x11-0 \
build-essential \
libgl1-mesa-dev \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-xinerama0
- name: Apply Qt5 patches
if: startsWith(matrix.qt-version, '5.')
run: |
patch "$Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h" .patches/qt5-on-newer-gcc.patch
shell: bash
- name: Build
run: |
mkdir build
cd build
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_BUILD_TYPE=Release \
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
..
shell: bash
- name: clang-tidy review
timeout-minutes: 20
uses: ZedThree/clang-tidy-review@v0.17.1
with:
build_dir: build-clang-tidy
config_file: ".clang-tidy"
split_workflow: true
exclude: "lib/*,tools/crash-handler/*"
cmake_command: >-
cmake -S. -Bbuild-clang-tidy
-DCMAKE_BUILD_TYPE=Release
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On
-DUSE_PRECOMPILED_HEADERS=OFF
-DCMAKE_EXPORT_COMPILE_COMMANDS=On
-DCHATTERINO_LTO=Off
-DCHATTERINO_PLUGINS=On
-DBUILD_WITH_QT6=Off
-DBUILD_TESTS=On
-DBUILD_BENCHMARKS=On
apt_packages: >-
qttools5-dev, qt5-image-formats-plugins, libqt5svg5-dev,
libsecret-1-dev,
libboost-dev, libboost-system-dev, libboost-filesystem-dev,
libssl-dev,
rapidjson-dev,
libbenchmark-dev
- name: clang-tidy-review upload
uses: ZedThree/clang-tidy-review/upload@v0.17.1

View file

@ -25,7 +25,7 @@ jobs:
fetch-depth: 0 # allows for tags access
- name: Download artifact
uses: dawidd6/action-download-artifact@v2
uses: dawidd6/action-download-artifact@v3
with:
workflow: build.yml
name: chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip
@ -43,7 +43,7 @@ jobs:
run: echo "C:\Program Files (x86)\Inno Setup 6\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Enable Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1.12.1
uses: ilammy/msvc-dev-cmd@v1.13.0
- name: Build installer
id: build-installer
@ -52,7 +52,7 @@ jobs:
shell: powershell
- name: Upload installer
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: build/${{ steps.build-installer.outputs.C2_INSTALLER_BASE_NAME }}.exe
name: ${{ steps.build-installer.outputs.C2_INSTALLER_BASE_NAME }}.exe

View file

@ -3,15 +3,17 @@ name: Post clang-tidy review comments
on:
workflow_run:
workflows: ["Build"]
workflows: ["clang-tidy"]
types:
- completed
jobs:
build:
runs-on: ubuntu-latest
# Only when a build succeeds
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: ZedThree/clang-tidy-review/post@v0.14.0
- uses: ZedThree/clang-tidy-review/post@v0.17.1
with:
lgtm_comment_body: ""

View file

@ -7,8 +7,10 @@ on:
merge_group:
env:
TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:v1.0.6
TWITCH_PUBSUB_SERVER_TAG: v1.0.7
QT_QPA_PLATFORM: minimal
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
concurrency:
group: test-macos-${{ github.ref }}
@ -54,11 +56,7 @@ jobs:
- name: Install dependencies
run: |
brew install boost openssl rapidjson p7zip create-dmg cmake tree docker colima
- name: Setup Colima
run: |
colima start
brew install boost openssl rapidjson p7zip create-dmg cmake
- name: Build
run: |
@ -74,18 +72,27 @@ jobs:
..
make -j"$(sysctl -n hw.logicalcpu)"
- name: Download and extract Twitch PubSub Server Test
run: |
mkdir pubsub-server-test
curl -L -o pubsub-server.tar.gz "https://github.com/Chatterino/twitch-pubsub-server-test/releases/download/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/server-${{ env.TWITCH_PUBSUB_SERVER_TAG }}-darwin-amd64.tar.gz"
tar -xzf pubsub-server.tar.gz -C pubsub-server-test
rm pubsub-server.tar.gz
cd pubsub-server-test
curl -L -o server.crt "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.crt"
curl -L -o server.key "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.key"
cd ..
- name: Cargo Install httpbox
run: |
cargo install --git https://github.com/kevinastone/httpbox --rev 89b971f
- name: Test
timeout-minutes: 30
run: |
docker pull kennethreitz/httpbin
docker pull ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }}
docker run --network=host --detach ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }}
docker run -p 9051:80 --detach kennethreitz/httpbin
httpbox --port 9051 &
cd ../pubsub-server-test
./server 127.0.0.1:9050 &
cd ../build-test
ctest --repeat until-pass:4 --output-on-failure --exclude-regex ClassicEmoteNameFiltering
working-directory: build-test
- name: Post Setup Colima
if: always()
run: |
colima stop
working-directory: build-test

View file

@ -63,7 +63,7 @@ jobs:
version: ${{ matrix.qt-version }}
- name: Enable Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1.12.1
uses: ilammy/msvc-dev-cmd@v1.13.0
- name: Setup conan variables
if: startsWith(matrix.os, 'windows')
@ -72,8 +72,8 @@ jobs:
"C2_CONAN_CACHE_SUFFIX=$(if ($Env:C2_BUILD_WITH_QT6 -eq "on") { "-QT6" } else { "`" })" >> "$Env:GITHUB_ENV"
- name: Setup sccache
# sccache v0.5.3
uses: nerixyz/ccache-action@9a7e8d00116ede600ee7717350c6594b8af6aaa5
# sccache v0.7.4
uses: hendrikmuhs/ccache-action@v1.2.12
with:
variant: sccache
# only save on the default (master) branch
@ -83,7 +83,7 @@ jobs:
sccache-test-${{ matrix.os }}-${{ matrix.qt-version }}
- name: Cache conan packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.py') }}${{ env.C2_CONAN_CACHE_SUFFIX }}
path: ~/.conan2/

View file

@ -1,5 +1,5 @@
---
name: Test
name: Test Ubuntu
on:
pull_request:
@ -7,7 +7,7 @@ on:
merge_group:
env:
TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:v1.0.6
TWITCH_PUBSUB_SERVER_TAG: v1.0.7
QT_QPA_PLATFORM: minimal
concurrency:
@ -16,91 +16,71 @@ concurrency:
jobs:
test:
runs-on: ${{ matrix.os }}
name: "${{ matrix.os }}"
runs-on: ubuntu-latest
container: ${{ matrix.container }}
strategy:
matrix:
include:
- os: "ubuntu-22.04"
qt-version: "5.15.2"
- os: "ubuntu-22.04"
qt-version: "5.12.12"
- os: "ubuntu-22.04"
qt-version: "6.2.4"
container: ghcr.io/chatterino/chatterino2-build-ubuntu-22.04:latest
qt-version: 6.6.1
plugins: true
fail-fast: false
env:
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') && 'ON' || 'OFF' }}
QT_MODULES: ${{ startsWith(matrix.qt-version, '6.') && 'qt5compat qtimageformats' || '' }}
C2_PLUGINS: ${{ matrix.plugins }}
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3.3.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: ${{ env.QT_MODULES }}
version: ${{ matrix.qt-version }}
- name: Apply Qt patches (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.qt-version, '5.')
run: |
patch "$Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h" .patches/qt5-on-newer-gcc.patch
shell: bash
# LINUX
- name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get -y install \
libbenchmark-dev \
cmake \
rapidjson-dev \
libssl-dev \
libboost-dev \
libboost-system-dev \
libboost-filesystem-dev \
libpulse-dev \
libxkbcommon-x11-0 \
libgstreamer-plugins-base1.0-0 \
build-essential \
libgl1-mesa-dev \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-xinerama0
- name: Create build directory (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: mkdir build-test
- name: Install googlebench
run: |
mkdir build-test
shell: bash
sudo apt update
sudo apt -y install \
libbenchmark-dev
- name: Build (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
cmake \
-DBUILD_TESTS=On \
-DBUILD_BENCHMARKS=On \
-DBUILD_APP=OFF \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
-DCHATTERINO_STATIC_QT_BUILD=On \
..
cmake --build .
working-directory: build-test
shell: bash
- name: Test (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
- name: Download and extract Twitch PubSub Server Test
run: |
mkdir pubsub-server-test
curl -L -o pubsub-server.tar.gz "https://github.com/Chatterino/twitch-pubsub-server-test/releases/download/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/server-${{ env.TWITCH_PUBSUB_SERVER_TAG }}-linux-amd64.tar.gz"
tar -xzf pubsub-server.tar.gz -C pubsub-server-test
rm pubsub-server.tar.gz
cd pubsub-server-test
curl -L -o server.crt "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.crt"
curl -L -o server.key "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.key"
cd ..
- uses: dtolnay/rust-toolchain@stable
- name: Cargo Install httpbox
run: |
cargo install --git https://github.com/kevinastone/httpbox --rev 89b971f
- name: Test
timeout-minutes: 30
run: |
docker pull kennethreitz/httpbin
docker pull ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }}
docker run --network=host --detach ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }}
docker run -p 9051:80 --detach kennethreitz/httpbin
ctest --repeat until-pass:4 --output-on-failure
httpbox --port 9051 &
cd ../pubsub-server-test
./server 127.0.0.1:9050 &
cd ../build-test
ctest --repeat until-pass:4 --output-on-failure --exclude-regex ClassicEmoteNameFiltering
working-directory: build-test
shell: bash

6
.gitmodules vendored
View file

@ -38,6 +38,6 @@
[submodule "lib/lua/src"]
path = lib/lua/src
url = https://github.com/lua/lua
[submodule "lib/crashpad"]
path = lib/crashpad
url = https://github.com/getsentry/crashpad
[submodule "tools/crash-handler"]
path = tools/crash-handler
url = https://github.com/Chatterino/crash-handler

View file

@ -1,5 +1,7 @@
# JSON resources should not be prettified...
resources/*.json
benchmarks/resources/*.json
tests/resources/*.json
# ...themes should be prettified for readability.
!resources/themes/*.json
@ -7,6 +9,7 @@ resources/*.json
lib/*/
conan-pkgs/*/
cmake/sanitizers-cmake/
tools/crash-handler
# Build folders
*build-*/

View file

@ -6,13 +6,13 @@ Note on Qt version compatibility: If you are installing Qt from a package manage
### Ubuntu 20.04
_Most likely works the same for other Debian-like distros_
_Most likely works the same for other Debian-like distros._
Install all of the dependencies using `sudo apt install qttools5-dev qt5-image-formats-plugins libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++ libsecret-1-dev`
Install all the dependencies using `sudo apt install qttools5-dev qt5-image-formats-plugins libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++ libsecret-1-dev`
### Arch Linux
Install all of the dependencies using `sudo pacman -S --needed qt5-base qt5-imageformats qt5-svg qt5-tools boost rapidjson pkgconf openssl cmake`
Install all the dependencies using `sudo pacman -S --needed qt5-base qt5-imageformats qt5-svg qt5-tools boost rapidjson pkgconf openssl cmake`
Alternatively you can use the [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) package to build and install Chatterino for you.
@ -20,11 +20,11 @@ Alternatively you can use the [chatterino2-git](https://aur.archlinux.org/packag
_Most likely works the same for other Red Hat-like distros. Substitute `dnf` with `yum`._
Install all of the dependencies using `sudo dnf install qt5-qtbase-devel qt5-qtimageformats qt5-qtsvg-devel qt5-linguist libsecret-devel openssl-devel boost-devel cmake`
Install all the dependencies using `sudo dnf install qt5-qtbase-devel qt5-qtimageformats qt5-qtsvg-devel qt5-linguist libsecret-devel openssl-devel boost-devel cmake`
### NixOS 18.09+
Enter the development environment with all of the dependencies: `nix-shell -p openssl boost qt5.full pkg-config cmake`
Enter the development environment with all the dependencies: `nix-shell -p openssl boost qt5.full pkg-config cmake`
## Compile

View file

@ -20,7 +20,7 @@ Local dev machines for testing are available on Apple Silicon on macOS 13.
1. Go to the project directory where you cloned Chatterino2 & its submodules
1. Create a build directory and go into it:
`mkdir build && cd build`
1. Run cmake:
1. Run CMake:
`cmake -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@5 -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@1.1 ..`
1. Build:
`make`

View file

@ -1,18 +1,18 @@
# Building on Windows
**Note that installing all of the development prerequisites and libraries will require about 40 GB of free disk space. Please ensure this space is available on your `C:` drive before proceeding.**
**Note that installing all the development prerequisites and libraries will require about 12 GB of free disk space. Please ensure this space is available on your `C:` drive before proceeding.**
This guide assumes you are on a 64-bit system. You might need to manually search out alternate download links should you desire to build Chatterino on a 32-bit system.
## Installing prerequisites
## Prerequisites
### Visual Studio
Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/). In the installer, select "Desktop development with C++" and "Universal Windows Platform development".
Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/). In the installer, select "Desktop development with C++".
Notes:
- This installation will take about 21 GB of disk space
- This installation will take about 8 GB of disk space
- You do not need to sign in with a Microsoft account after setup completes. You may simply exit the login dialog.
### Qt
@ -26,7 +26,9 @@ Notes:
- Installing the latest **stable** Qt version is advised for new installations, but if you want to use your existing installation please ensure you are running **Qt 5.12 or later**.
#### When prompted which components to install:
#### Components
When prompted which components to install, do the following:
1. Unfold the tree element that says "Qt"
2. Unfold the top most tree element (latest stable Qt version, e.g. `Qt 6.5.3`)
@ -43,7 +45,7 @@ Once Qt is done installing, make sure you add its bin directory to your `PATH` (
<details>
<summary>How to add Qt to PATH</summary>
1. Type "path" in the Windows start menu and click `Edit the system environment variables`.
2. Click the `Environment Variables...` button bottom right.
3. In the `User variables` (scoped to the current user) or `System variables` (system-wide) section, scroll down until you find `Path` and double click it.
@ -75,19 +77,6 @@ Note: This installation will take about 2.1 GB of disk space.
</details>
<details>
<summary>OpenSSL</summary>
### For our websocket library, we need OpenSSL 1.1
1. Download OpenSSL for windows, version `1.1.1s`: **[Download](https://web.archive.org/web/20221101204129/https://slproweb.com/download/Win64OpenSSL-1_1_1s.exe)**
2. When prompted, install OpenSSL to `C:\local\openssl`
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory".
Note: This installation will take about 200 MB of disk space.
</details>
## Building
### Using CMake
@ -120,57 +109,69 @@ Then in a terminal, configure conan to use `NMake Makefiles` as its generator:
Open up your terminal with the Visual Studio environment variables (e.g. `x64 Native Tools Command Prompt for VS 2022`), cd to the cloned chatterino2 directory and run the following commands:
1. `mkdir build`
1. `cd build`
1. `conan install .. -s build_type=Release -c tools.cmake.cmaketoolchain:generator="NMake Makefiles" --build=missing --output-folder=.`
1. `cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_PREFIX_PATH="C:\Qt\6.5.3\msvc2019_64" ..`
1. `nmake`
```cmd
mkdir build
cd build
conan install .. -s build_type=Release -c tools.cmake.cmaketoolchain:generator="NMake Makefiles" --build=missing --output-folder=.
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_PREFIX_PATH="C:\Qt\6.5.3\msvc2019_64" ..
nmake
```
To build a debug build, you'll also need to add the `-s compiler.runtime_type=Debug` flag to the `conan install` invocation. See [this StackOverflow post](https://stackoverflow.com/questions/59828611/windeployqt-doesnt-deploy-qwindowsd-dll-for-a-debug-application/75607313#75607313)
#### Ensure DLLs are available
#### Deploying Qt libraries
Once Chatterino has finished building, to ensure all .dll's are available you can run this from the build directory:
`windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir bin/`
Can't find windeployqt? You forgot to add your Qt bin directory (e.g. `C:\Qt\6.5.3\msvc2019_64\bin`) to your `PATH`
### Run the build in Qt Creator
### Developing in Qt Creator
1. Open the `CMakeLists.txt` file by double-clicking it, or by opening it via Qt Creator.
2. You will be presented with a screen that is titled "Configure Project". In this screen, you should have at least one option present ready to be configured, like this:
![Qt Create Configure Project screenshot](https://user-images.githubusercontent.com/69117321/169887645-2ae0871a-fe8a-4eb9-98db-7b996dea3a54.png)
3. Select the profile(s) you want to build with and click "Configure Project".
#### How to run and produce builds
#### Building and running
- In the main screen, click the green "play symbol" on the bottom left to run the project directly.
- Click the hammer on the bottom left to generate a build (does not run the build though).
Build results will be placed in a folder at the same level as the "chatterino2" project folder (e.g. if your sources are at `C:\Users\example\src\chatterino2`, then the build will be placed in an automatically generated folder under `C:\Users\example\src`, e.g. `C:\Users\example\src\build-chatterino-Desktop_Qt_6.5.3_MSVC2019_64bit-Release`.)
- Note that if you are building chatterino purely for usage, not for development, it is recommended that you click the "PC" icon above the play icon and select "Release" instead of "Debug".
- Note that if you are building Chatterino purely for usage, not for development, it is recommended that you click the "PC" icon above the play icon and select "Release" instead of "Debug".
- Output and error messages produced by the compiler can be seen under the "4 Compile Output" tab in Qt Creator.
#### Producing standalone builds
If you build chatterino, the result directories will contain a `chatterino.exe` file in the `$OUTPUTDIR\release\` directory. This `.exe` file will not directly run on any given target system, because it will be lacking various Qt runtimes.
If you build Chatterino, the result directories will contain a `chatterino.exe` file in the `$OUTPUTDIR\release\` directory. This `.exe` file will not directly run on any given target system, because it will be lacking various Qt runtimes.
To produce a standalone package, you need to generate all required files using the tool `windeployqt`. This tool can be found in the `bin` directory of your Qt installation, e.g. at `C:\Qt\6.5.3\msvc2019_64\bin\windeployqt.exe`.
To produce all supplement files for a standalone build, follow these steps (adjust paths as required):
1. Navigate to your build output directory with Windows Explorer, e.g. `C:\Users\example\src\build-chatterino-Desktop_Qt_6.5.3_MSVC2019_64bit-Release`
2. Enter the `release` directory
3. Delete all files except the `chatterino.exe` file. You should be left with a directory only containing `chatterino.exe`.
4. Open a command prompt and execute:
1. Navigate to your build output directory with Windows Explorer, e.g. `C:\Users\example\src\build-chatterino-Desktop_Qt_6.5.3_MSVC2019_64bit-Release`
2. Enter the `release` directory
3. Delete all files except the `chatterino.exe` file. You should be left with a directory only containing `chatterino.exe`.
4. Open a command prompt and execute:
```cmd
cd C:\Users\example\src\build-chatterino-Desktop_Qt_6.5.3_MSVC2019_64bit-Release\release
windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir bin/
```
5. The `releases` directory will now be populated with all the required files to make the Chatterino build standalone.
cd C:\Users\example\src\build-chatterino-Desktop_Qt_6.5.3_MSVC2019_64bit-Release\release
C:\Qt\6.5.3\msvc2019_64\bin\windeployqt.exe chatterino.exe
You can now create a zip archive of all the contents in `releases` and distribute the program as is, without requiring any development tools to be present on the target system. (However, the CRT must be present, as usual - see the [README](README.md)).
5. The `releases` directory will now be populated with all the required files to make the chatterino build standalone.
#### Formatting
You can now create a zip archive of all the contents in `releases` and distribute the program as is, without requiring any development tools to be present on the target system. (However, the vcredist package must be present, as usual - see the [README](README.md)).
To automatically format your code, do the following:
1. Download [LLVM 16.0.6](https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.6/LLVM-16.0.6-win64.exe)
2. During the installation, make sure to add it to your path
3. In Qt Creator, Select `Tools` > `Options` > `Beautifier`
4. Under `General` select `Tool: ClangFormat` and enable `Automatic Formatting on File Save`
5. Under `Clang Format` select `Use predefined style: File` and `Fallback style: None`
### Building on MSVC with AddressSanitizer
@ -183,7 +184,7 @@ copy the file found in `<VisualStudio-installation-path>\VC\Tools\MSVC\<version>
To learn more about AddressSanitizer and MSVC, visit the [Microsoft Docs](https://learn.microsoft.com/en-us/cpp/sanitizers/asan).
### Building/Running in CLion
### Developing in CLion
_Note:_ We're using `build` instead of the CLion default `cmake-build-debug` folder.
@ -196,7 +197,7 @@ Clone the repository as described in the readme. Open a terminal in the cloned f
Now open the project in CLion. You will be greeted with the _Open Project Wizard_. Set the _CMake Options_ to
```
```text
-DCMAKE_PREFIX_PATH=C:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
```
@ -227,9 +228,9 @@ Select the `CMake Applications > chatterino` configuration and add a new _Run Ex
</details>
<details>
<summary>Screenshot of chatterino configuration</summary>
<summary>Screenshot of Chatterino configuration</summary>
![Screenshot of chatterino configuration](https://user-images.githubusercontent.com/41973452/160240843-dc0c603c-227f-4f56-98ca-57f03989dfb4.png)
![Screenshot of Chatterino configuration](https://user-images.githubusercontent.com/41973452/160240843-dc0c603c-227f-4f56-98ca-57f03989dfb4.png)
</details>
@ -240,26 +241,24 @@ write `portable` into it.
#### Debugging
To visualize QT types like `QString`, you need to inform CLion and LLDB
To visualize Qt types like `QString`, you need to inform CLion and LLDB
about these types.
1. Set `Enable NatVis renderers for LLDB option`
in `Settings | Build, Execution, Deployment | Debugger | Data Views | C/C++` (should be enabled by default).
2. Use the official NatVis file for QT from [`qt-labs/vstools`](https://github.com/qt-labs/vstools) by saving them to
2. Use the official NatVis file for Qt from [`qt-labs/vstools`](https://github.com/qt-labs/vstools) by saving them to
the project root using PowerShell:
<!--
When switching to QT6 these need to be updated to qt6.natvis.xml.
We need to do the replacement as the QT tools:
https://github.com/qt-labs/vstools/blob/0769d945f8d0040917d654d9731e6b65951e102c/QtVsTools.Package/QtVsToolsPackage.cs#L390-L393
We can't use Invoke-RestMethod here, because it will automatically convert the body to an xml document.
-->
```powershell
(irm "https://github.com/qt-labs/vstools/raw/dev/QtVsTools.Package/qt5.natvis.xml").Replace('##NAMESPACE##::', '') | Out-File qt5.natvis
(iwr "https://github.com/qt-labs/vstools/raw/dev/QtVsTools.Package/qt6.natvis.xml").Content.Replace('##NAMESPACE##::', '') | Out-File qt6.natvis
# [OR] using the permalink
(irm "https://github.com/qt-labs/vstools/raw/0769d945f8d0040917d654d9731e6b65951e102c/QtVsTools.Package/qt5.natvis.xml").Replace('##NAMESPACE##::', '') | Out-File qt5.natvis
(iwr "https://github.com/qt-labs/vstools/raw/1c8ba533bd88d935be3724667e0087fd0796102c/QtVsTools.Package/qt6.natvis.xml").Content.Replace('##NAMESPACE##::', '') | Out-File qt6.natvis
```
Now you can debug the application and see QT types rendered correctly.
Now you can debug the application and see Qt types rendered correctly.
If this didn't work for you, try following
the [tutorial from JetBrains](https://www.jetbrains.com/help/clion/qt-tutorial.html#debug-renderers).

View file

@ -8,12 +8,16 @@ This will require more than 30GB of free space on your hard drive.
1. Install [CMake](https://cmake.org/)
1. Install [git](https://git-scm.com/)
1. Install [vcpkg](https://vcpkg.io/)
- `git clone https://github.com/Microsoft/vcpkg.git`
- `cd .\vcpkg\`
- `.\bootstrap-vcpkg.bat`
- `.\vcpkg integrate install`
- `.\vcpkg integrate powershell`
- `cd ..`
```shell
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg integrate powershell
cd ..
```
1. Configure the environment variables for vcpkg.
Check [this document](https://gist.github.com/mitchmindtree/92c8e37fa80c8dddee5b94fc88d1288b#setting-an-environment-variable-on-windows) for more information for how to set environment variables on Windows.
- Ensure your dependencies are built as 64-bit
@ -31,15 +35,19 @@ This will require more than 30GB of free space on your hard drive.
## Building
1. Clone
- `git clone --recurse-submodules https://github.com/Chatterino/chatterino2.git`
```shell
git clone --recurse-submodules https://github.com/Chatterino/chatterino2.git
```
1. Install dependencies
- `cd .\chatterino2\`
- `vcpkg install`
```powershell
cd .\chatterino2\
vcpkg install
```
1. Build
- `mkdir .\build\`
- `cd .\build\`
- (cmd) `cmake .. -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake`
- (ps1) `cmake .. -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"`
- `cmake --build . --parallel <threads> --config Release`
1. Run
- `.\bin\chatterino2.exe`
```powershell
cmake -B build -DCMAKE_TOOLCHAIN_FILE="$Env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cd build
cmake --build . --parallel <threads> --config Release
```
When using CMD, use `-DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake` to specify the toolchain.
1. Run `.\bin\chatterino2.exe`

View file

@ -3,6 +3,8 @@
## Unversioned
- Major: Allow use of Twitch follower emotes in other channels if subscribed. (#4922)
- Major: Add `/automod` split to track automod caught messages across all open channels the user moderates. (#4986, #5026)
- Major: Show restricted chat messages and suspicious treatment updates. (#5056, #5060)
- Major: Add an Image Uploader tab to the Settings. (#4995)
- Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
- Minor: The account switcher is now styled to match your theme. (#4817)
@ -14,6 +16,38 @@
- Minor: The `/reply` command now replies to the latest message of the user. (#4919)
- Minor: All sound capabilities can now be disabled by setting your "Sound backend" setting to "Null" and restarting Chatterino. (#4978)
- Minor: Add an option to use new experimental smarter emote completion. (#4987)
- Minor: Add `--safe-mode` command line option that can be used for troubleshooting when Chatterino is misbehaving or is misconfigured. It disables hiding the settings button & prevents plugins from loading. (#4985)
- Minor: Added support for FrankerFaceZ channel badges. These can be configured at https://www.frankerfacez.com/channel/mine - right now only supporting bot badges for your chat bots. (#5119)
- Minor: Updated the flatpakref link included with nightly builds to point to up-to-date flathub-beta builds. (#5008)
- Minor: Add a new completion API for experimental plugins feature. (#5000, #5047)
- Minor: Re-enabled _Restart on crash_ option on Windows. (#5012)
- Minor: The chatter list button is now hidden if you don't have moderator privileges. (#5245)
- Minor: The whisper highlight color can now be configured through the settings. (#5053)
- Minor: Added an option to always include the broadcaster in user completions. This is enabled by default. (#5193, #5244)
- Minor: Added missing periods at various moderator messages and commands. (#5061)
- Minor: Improved color selection and display. (#5057)
- Minor: Improved Streamlink documentation in the settings dialog. (#5076)
- Minor: Normalized the input padding between light & dark themes. (#5095)
- Minor: Add `--activate <channel>` (or `-a`) command line option to activate or add a Twitch channel. (#5111)
- Minor: Chatters from recent-messages are now added to autocompletion. (#5116)
- Minor: Added a _System_ theme that updates according to the system's color scheme (requires Qt 6.5). (#5118)
- Minor: Added icons for newer versions of macOS. (#5148)
- Minor: Added the `--incognito/--no-incognito` options to the `/openurl` command, allowing you to override the "Open links in incognito/private mode" setting. (#5149, #5197)
- Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130)
- Minor: Add a new Channel API for experimental plugins feature. (#5141, #5184, #5187)
- Minor: Added the ability to change the top-most status of a window regardless of the _Always on top_ setting (right click the notebook). (#5135)
- Minor: Introduce `c2.later()` function to Lua API. (#5154)
- Minor: Live streams that are marked as reruns now mark a tab as yellow instead of red. (#5176, #5237)
- Minor: Updated to Emoji v15.1. Google emojis are now used as the fallback instead of Twitter emojis. (#5182)
- Minor: Added the ability to show AutoMod caught messages in mentions. (#5215)
- Minor: Added the ability to configure the color of highlighted AutoMod caught messages. (#5215)
- Minor: Allow theming of tab live and rerun indicators. (#5188)
- Minor: Added a fallback theme field to custom themes that will be used in case the custom theme does not contain a color Chatterino needs. If no fallback theme is specified, we'll pull the color from the included Dark or Light theme. (#5198)
- Minor: Image links now reflect the scale of their image instead of an internal label. (#5201)
- Minor: IPC files are now stored in the Chatterino directory instead of system directories on Windows. (#5226)
- Minor: 7TV emotes now have a 4x image rather than a 3x image. (#5209)
- Minor: Add wrappers for Lua `io` library for experimental plugins feature. (#5231)
- Minor: Add permissions to experimental plugins feature. (#5231)
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
- Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848)
- Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834)
@ -25,31 +59,63 @@
- Bugfix: Fixed selection of tabs after closing a tab when using "Live Tabs Only". (#4770)
- Bugfix: Fixed input in reply thread popup losing focus when dragging. (#4815)
- Bugfix: Fixed the Quick Switcher (CTRL+K) from sometimes showing up on the wrong window. (#4819)
- Bugfix: Fixed the font switcher not remembering what font you had previously selected. (#5224)
- Bugfix: Fixed too much text being copied when copying chat messages. (#4812, #4830, #4839)
- Bugfix: Fixed an issue where the setting `Only search for emote autocompletion at the start of emote names` wouldn't disable if it was enabled when the client started. (#4855)
- Bugfix: Fixed empty page being added when showing out of bounds dialog. (#4849)
- Bugfix: Fixed an issue preventing searching a redemption by it's title when the redemption contained text input. (#5117)
- Bugfix: Fixed issue on Windows preventing the title bar from being dragged in the top left corner. (#4873)
- Bugfix: Fixed an issue where reply context didn't render correctly if an emoji was touching text. (#4875, #4977)
- Bugfix: Fixed an issue where reply context didn't render correctly if an emoji was touching text. (#4875, #4977, #5174)
- Bugfix: Fixed the input completion popup from disappearing when clicking on it on Windows and macOS. (#4876)
- Bugfix: Fixed Twitch badges not loading correctly in the badge highlighting setting page. (#5223)
- Bugfix: Fixed double-click text selection moving its position with each new message. (#4898)
- Bugfix: Fixed an issue where notifications on Windows would contain no or an old avatar. (#4899)
- Bugfix: Fixed headers of tables in the settings switching to bold text when selected. (#4913)
- Bugfix: Fixed an issue in the `/live` split that caused some channels to not get grayed-out when they went offline. (#5172)
- Bugfix: Fixed tooltips appearing too large and/or away from the cursor. (#4920)
- Bugfix: Fixed a crash when clicking `More messages below` button in a usercard and closing it quickly. (#4933)
- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923)
- Bugfix: Fixed an occasional crash for channel point redemptions with text input. (#4949)
- Bugfix: Fixed triple click on message also selecting moderation buttons. (#4961)
- Bugfix: Fixed a freeze from a bad regex in _Ignores_. (#4965)
- Bugfix: Fixed some emotes not appearing when using _Ignores_. (#4965)
- Bugfix: Fixed lookahead/-behind not working in _Ignores_. (#4965)
- Bugfix: Fixed a freeze from a bad regex in _Ignores_. (#4965, #5126)
- Bugfix: Fixed badge highlight changes not immediately being reflected. (#5110)
- Bugfix: Fixed emotes being reloaded when pressing "Cancel" in the settings dialog, causing a slowdown. (#5240)
- Bugfix: Fixed some emotes not appearing when using _Ignores_. (#4965, #5126)
- Bugfix: Fixed lookahead/-behind not working in _Ignores_. (#4965, #5126)
- Bugfix: Fixed Image Uploader accidentally deleting images with some hosts when link resolver was enabled. (#4971)
- Bugfix: Fixed rare crash with Image Uploader when closing a split right after starting an upload. (#4971)
- Bugfix: Fixed an issue on macOS where the image uploader would keep prompting the user even after they clicked "Yes, don't ask again". (#5011)
- Bugfix: Hide the Usercard button in the User Info Popup when in special channels. (#4972)
- Bugfix: Fixed support for Windows 11 Snap layouts. (#4994, #5175)
- Bugfix: Fixed some windows appearing between screens. (#4797)
- Bugfix: Fixed a crash that could occur when using certain features in a Usercard after closing the split from which it was created. (#5034, #5051)
- Bugfix: Fixed a crash that could occur when using certain features in a Reply popup after closing the split from which it was created. (#5036, #5051)
- Bugfix: Fixed a bug on Wayland where tooltips would spawn as separate windows instead of behaving like tooltips. (#4998, #5040)
- Bugfix: Fixes to section deletion in text input fields. (#5013)
- Bugfix: Truncated IRC messages to be at most 512 bytes. (#5246)
- Bugfix: Show user text input within watch streak notices. (#5029)
- Bugfix: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052)
- Bugfix: Fixed moderator-only topics being subscribed to for non-moderators. (#5056)
- Bugfix: Fixed a bug where buttons would remain in a hovered state after leaving them. (#5077)
- Bugfix: Fixed an issue where you had to click the `reply` button twice if you already had that users @ in your input box. (#5173)
- Bugfix: Fixed popup windows not persisting between restarts. (#5081)
- Bugfix: Fixed splits not retaining their focus after minimizing. (#5080)
- Bugfix: Fixed _Copy message_ copying the channel name in global search. (#5106)
- Bugfix: Reply contexts now use the color of the replied-to message. (#5145)
- Bugfix: Fixed top-level window getting stuck after opening settings. (#5161, #5166)
- Bugfix: Fixed link info not updating without moving the cursor. (#5178)
- Bugfix: Fixed an upload sometimes failing when copying an image from a browser if it contained extra properties. (#5156)
- Bugfix: Fixed tooltips getting out of bounds when loading images. (#5186)
- Bugfix: Fixed the "Cancel" button in the settings dialog only working after opening the settings dialog twice. (#5229)
- Bugfix: Fixed split header tooltips showing in the wrong position on Windows. (#5230)
- Bugfix: Fixed split header tooltips appearing too tall. (#5232)
- Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978)
- Dev: Change clang-format from v14 to v16. (#4929)
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
- Dev: Tests now run on Ubuntu 22.04 instead of 20.04 to loosen C++ restrictions in tests. (#4774)
- Dev: Do a pretty major refactor of the Settings classes. List settings (e.g. highlights) are most heavily modified, and should have an extra eye kept on them. (#4775)
- Dev: conan: Update Boost to 1.83 & OpenSSL to 3.2.0. (#5007)
- Dev: Remove `boost::noncopyable` use & `boost_random` dependency. (#4776)
- Dev: Fix clang-tidy `cppcoreguidelines-pro-type-member-init` warnings. (#4426)
- Dev: Immediate layout for invisible `ChannelView`s is skipped. (#4811)
@ -66,17 +132,58 @@
- Dev: Replace `boost::optional` with `std::optional`. (#4877)
- Dev: Improve performance of selecting text. (#4889, #4911)
- Dev: Removed direct dependency on Qt 5 compatibility module. (#4906)
- Dev: Added unit test capabilities to SplitInput. (#5179)
- Dev: Refactor `Emoji`'s EmojiMap into a vector. (#4980)
- Dev: Refactor `DebugCount` and add copy button to debug popup. (#4921)
- Dev: Refactor `common/Credentials`. (#4979)
- Dev: Refactor chat logger. (#5058)
- Dev: Refactor Twitch PubSub client. (#5059)
- Dev: Changed lifetime of context menus. (#4924)
- Dev: Renamed `tools` directory to `scripts`. (#5035)
- Dev: Refactor `ChannelView`, removing a bunch of clang-tidy warnings. (#4926)
- Dev: Refactor `IrcMessageHandler`, removing a bunch of clang-tidy warnings & changing its public API. (#4927)
- Dev: Removed almost all raw accesses into Application. (#5104)
- Dev: `Details` file properties tab is now populated on Windows. (#4912)
- Dev: Removed `Outcome` from network requests. (#4959)
- Dev: Added Tests for Windows and MacOS in CI. (#4970)
- Dev: Added Tests for Windows and MacOS in CI. (#4970, #5032)
- Dev: Added "Copy message as JSON" option when shift-right-clicking a message. (#5150)
- Dev: Windows now builds with Qt6 by default. (#5155)
- Dev: Conan now uses OpenSSL 3 by default. (#5159)
- Dev: Move `clang-tidy` checker to its own CI job. (#4996)
- Dev: Refactored the Image Uploader feature. (#4971)
- Dev: Refactored the SplitOverlay code. (#5082)
- Dev: Refactored the Fonts code, making it less of a singleton. (#5228)
- Dev: Refactored the TwitchBadges structure, making it less of a singleton. (#5096, #5144)
- Dev: Refactored emotes out of TwitchIrcServer. (#5120, #5146)
- Dev: Refactored the ChatterinoBadges structure, making it less of a singleton. (#5103)
- Dev: Refactored the ColorProvider class a bit. (#5112)
- Dev: Moved the Network files to their own folder. (#5089)
- Dev: Fixed deadlock and use-after-free in tests. (#4981)
- Dev: Moved all `.clang-format` files to the root directory. (#5037)
- Dev: Load less message history upon reconnects. (#5001, #5018)
- Dev: Removed the `NullablePtr` class. (#5091)
- Dev: BREAKING: Replace custom `import()` with normal Lua `require()`. (#5014, #5108)
- Dev: Fixed most compiler warnings. (#5028, #5137)
- Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747)
- Dev: Refactor Args to be less of a singleton. (#5041)
- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043, #5045)
- Dev: Autogenerate docs/plugin-meta.lua. (#5055)
- Dev: Changed Ubuntu & AppImage builders to statically link Qt. (#5151)
- Dev: Refactor `NetworkPrivate`. (#5063)
- Dev: Refactor `Paths` & `Updates`, focusing on reducing their singletoniability. (#5092, #5102)
- Dev: Removed duplicate scale in settings dialog. (#5069)
- Dev: Fix `NotebookTab` emitting updates for every message. (#5068)
- Dev: Added benchmark for parsing and building recent messages. (#5071)
- Dev: Boost is depended on as a header-only library when using conan. (#5107)
- Dev: Added signal to invalidate paint buffers of channel views without forcing a relayout. (#5123)
- Dev: Specialize `Atomic<std::shared_ptr<T>>` if underlying standard library supports it. (#5133)
- Dev: Added the `developer_name` field to the Linux AppData specification. (#5138)
- Dev: Twitch messages can be sent using Twitch's Helix API instead of IRC (disabled by default). (#5200)
- Dev: Added estimation for image sizes to avoid layout shifts. (#5192)
- Dev: Added the `launachable` entry to Linux AppData. (#5210)
- Dev: Cleaned up and optimized resources. (#5222)
- Dev: Refactor `StreamerMode`. (#5216, #5236)
- Dev: Cleaned up unused code in `MessageElement` and `MessageLayoutElement`. (#5225)
## 2.4.6

View file

@ -18,7 +18,11 @@ option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key
option(USE_SYSTEM_MINIAUDIO "Build Chatterino with your system miniaudio" OFF)
option(BUILD_WITH_CRASHPAD "Build chatterino with crashpad" OFF)
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
if(WIN32)
option(BUILD_WITH_QT6 "Build with Qt6, default on for Windows" On)
else()
option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
endif()
option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF)
# We don't use translations, and we don't want qtkeychain to build translations
option(BUILD_TRANSLATIONS "" OFF)
@ -210,7 +214,7 @@ if (CHATTERINO_PLUGINS)
endif()
if (BUILD_WITH_CRASHPAD)
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/crashpad" EXCLUDE_FROM_ALL)
add_subdirectory("${CMAKE_SOURCE_DIR}/tools/crash-handler")
endif()
# Used to provide a date of build in the About page (for nightly builds). Getting the actual time of

View file

@ -35,7 +35,7 @@ int compare(const QString &a, const QString &b);
```cpp
/*
* Matches a link and returns boost::none if it failed and a
* Matches a link and returns std::nullopt if it failed and a
* QRegularExpressionMatch on success.
* ^^^ This comment just repeats the function signature!!!
*

View file

@ -22,34 +22,29 @@ If you still receive an error about `MSVCR120.dll missing`, then you should inst
To get source code with required submodules run:
```
```shell
git clone --recurse-submodules https://github.com/Chatterino/chatterino2.git
```
or
```
```shell
git clone https://github.com/Chatterino/chatterino2.git
cd chatterino2
git submodule update --init --recursive
```
[Building on Windows](../master/BUILDING_ON_WINDOWS.md)
[Building on Windows with vcpkg](../master/BUILDING_ON_WINDOWS_WITH_VCPKG.md)
[Building on Linux](../master/BUILDING_ON_LINUX.md)
[Building on Mac](../master/BUILDING_ON_MAC.md)
[Building on FreeBSD](../master/BUILDING_ON_FREEBSD.md)
- [Building on Windows](../master/BUILDING_ON_WINDOWS.md)
- [Building on Windows with vcpkg](../master/BUILDING_ON_WINDOWS_WITH_VCPKG.md)
- [Building on Linux](../master/BUILDING_ON_LINUX.md)
- [Building on macOS](../master/BUILDING_ON_MAC.md)
- [Building on FreeBSD](../master/BUILDING_ON_FREEBSD.md)
## Git blame
This project has big commits in the history which for example update all line
endings. To improve the output of git-blame, consider setting:
This project has big commits in the history which touch most files while only doing stylistic changes. To improve the output of git-blame, consider setting:
```
```shell
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
@ -58,19 +53,9 @@ file](./.git-blame-ignore-revs). GitHub does this by default.
## Code style
The code is formatted using clang format in Qt Creator. [.clang-format](src/.clang-format) contains the style file for clang format.
The code is formatted using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). Our configuration is found in the [.clang-format](.clang-format) file in the repository root directory.
### Get it automated with QT Creator + Beautifier + Clang Format
1. Download LLVM: https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.6/LLVM-16.0.6-win64.exe
2. During the installation, make sure to add it to your path
3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin
4. Restart QT Creator
5. Select `Tools` > `Options` > `Beautifier`
6. Under `General` select `Tool: ClangFormat` and enable `Automatic Formatting on File Save`
7. Under `Clang Format` select `Use predefined style: File` and `Fallback style: None`
Qt creator should now format the documents when saving it.
For more contribution guidelines, take a look at [the wiki](https://wiki.chatterino.com/Contributing%20for%20Developers/).
## Doxygen

View file

@ -1,13 +1,16 @@
project(chatterino-benchmark)
set(benchmark_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Emojis.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Highlights.cpp
${CMAKE_CURRENT_LIST_DIR}/src/FormatTime.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Helpers.cpp
${CMAKE_CURRENT_LIST_DIR}/src/LimitedQueue.cpp
${CMAKE_CURRENT_LIST_DIR}/src/LinkParser.cpp
src/main.cpp
resources/bench.qrc
src/Emojis.cpp
src/Highlights.cpp
src/FormatTime.cpp
src/Helpers.cpp
src/LimitedQueue.cpp
src/LinkParser.cpp
src/RecentMessages.cpp
# Add your new file above this line!
)
@ -27,4 +30,12 @@ set_target_properties(${PROJECT_NAME}
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin"
AUTORCC ON
)
if (CHATTERINO_STATIC_QT_BUILD)
qt_import_plugins(${PROJECT_NAME} INCLUDE_BY_TYPE
platforms Qt::QXcbIntegrationPlugin
Qt::QMinimalIntegrationPlugin
)
endif ()

View file

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/bench">
<file>recentmessages-nymn.json</file>
<file>seventvemotes-nymn.json</file>
</qresource>
</RCC>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -145,16 +145,13 @@ static void BM_EmojiParsing(benchmark::State &state)
BENCHMARK(BM_EmojiParsing);
template <class... Args>
static void BM_EmojiParsing2(benchmark::State &state, Args &&...args)
static void BM_EmojiParsing2(benchmark::State &state, const QString &input,
int expectedNumEmojis)
{
Emojis emojis;
emojis.load();
auto argsTuple = std::make_tuple(std::move(args)...);
auto input = std::get<0>(argsTuple);
auto expectedNumEmojis = std::get<1>(argsTuple);
for (auto _ : state)
{
auto output = emojis.parse(input);

View file

@ -4,35 +4,41 @@
using namespace chatterino;
template <class... Args>
void BM_TimeFormatting(benchmark::State &state, Args &&...args)
void BM_TimeFormattingQString(benchmark::State &state, const QString &v)
{
auto args_tuple = std::make_tuple(std::move(args)...);
for (auto _ : state)
{
formatTime(std::get<0>(args_tuple));
formatTime(v);
}
}
BENCHMARK_CAPTURE(BM_TimeFormatting, 0, 0);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs0, "0");
BENCHMARK_CAPTURE(BM_TimeFormatting, 1337, 1337);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs1337, "1337");
BENCHMARK_CAPTURE(BM_TimeFormatting, 623452, 623452);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs623452, "623452");
BENCHMARK_CAPTURE(BM_TimeFormatting, 8345, 8345);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs8345, "8345");
BENCHMARK_CAPTURE(BM_TimeFormatting, 314034, 314034);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs314034, "314034");
BENCHMARK_CAPTURE(BM_TimeFormatting, 27, 27);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs27, "27");
BENCHMARK_CAPTURE(BM_TimeFormatting, 34589, 34589);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs34589, "34589");
BENCHMARK_CAPTURE(BM_TimeFormatting, 3659, 3659);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs3659, "3659");
BENCHMARK_CAPTURE(BM_TimeFormatting, 1045345, 1045345);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs1045345, "1045345");
BENCHMARK_CAPTURE(BM_TimeFormatting, 86432, 86432);
BENCHMARK_CAPTURE(BM_TimeFormatting, qs86432, "86432");
BENCHMARK_CAPTURE(BM_TimeFormatting, qsempty, "");
BENCHMARK_CAPTURE(BM_TimeFormatting, qsinvalid, "asd");
void BM_TimeFormattingInt(benchmark::State &state, int v)
{
for (auto _ : state)
{
formatTime(v);
}
}
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 0, 0);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 1045345, 1045345);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 1337, 1337);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 27, 27);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 314034, 314034);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 34589, 34589);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 3659, 3659);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 623452, 623452);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 8345, 8345);
BENCHMARK_CAPTURE(BM_TimeFormattingInt, 86432, 86432);
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs0, "0");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs1045345, "1045345");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs1337, "1337");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs27, "27");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs314034, "314034");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs34589, "34589");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs3659, "3659");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs623452, "623452");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs8345, "8345");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qs86432, "86432");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qsempty, "");
BENCHMARK_CAPTURE(BM_TimeFormattingQString, qsinvalid, "asd");

View file

@ -0,0 +1,255 @@
#include "common/Literals.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "messages/Emote.hpp"
#include "mocks/DisabledStreamerMode.hpp"
#include "mocks/EmptyApplication.hpp"
#include "mocks/TwitchIrcServer.hpp"
#include "mocks/UserData.hpp"
#include "providers/bttv/BttvEmotes.hpp"
#include "providers/chatterino/ChatterinoBadges.hpp"
#include "providers/ffz/FfzBadges.hpp"
#include "providers/ffz/FfzEmotes.hpp"
#include "providers/recentmessages/Impl.hpp"
#include "providers/seventv/SeventvBadges.hpp"
#include "providers/seventv/SeventvEmotes.hpp"
#include "providers/twitch/TwitchBadges.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Resources.hpp"
#include <benchmark/benchmark.h>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QString>
#include <optional>
using namespace chatterino;
using namespace literals;
namespace {
class MockApplication : mock::EmptyApplication
{
public:
IEmotes *getEmotes() override
{
return &this->emotes;
}
IUserDataController *getUserData() override
{
return &this->userData;
}
AccountController *getAccounts() override
{
return &this->accounts;
}
ITwitchIrcServer *getTwitch() override
{
return &this->twitch;
}
ChatterinoBadges *getChatterinoBadges() override
{
return &this->chatterinoBadges;
}
FfzBadges *getFfzBadges() override
{
return &this->ffzBadges;
}
SeventvBadges *getSeventvBadges() override
{
return &this->seventvBadges;
}
HighlightController *getHighlights() override
{
return &this->highlights;
}
TwitchBadges *getTwitchBadges() override
{
return &this->twitchBadges;
}
BttvEmotes *getBttvEmotes() override
{
return &this->bttvEmotes;
}
FfzEmotes *getFfzEmotes() override
{
return &this->ffzEmotes;
}
SeventvEmotes *getSeventvEmotes() override
{
return &this->seventvEmotes;
}
IStreamerMode *getStreamerMode() override
{
return &this->streamerMode;
}
AccountController accounts;
Emotes emotes;
mock::UserDataController userData;
mock::MockTwitchIrcServer twitch;
ChatterinoBadges chatterinoBadges;
FfzBadges ffzBadges;
SeventvBadges seventvBadges;
HighlightController highlights;
TwitchBadges twitchBadges;
BttvEmotes bttvEmotes;
FfzEmotes ffzEmotes;
SeventvEmotes seventvEmotes;
DisabledStreamerMode streamerMode;
};
std::optional<QJsonDocument> tryReadJsonFile(const QString &path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly))
{
return std::nullopt;
}
QJsonParseError e;
auto doc = QJsonDocument::fromJson(file.readAll(), &e);
if (e.error != QJsonParseError::NoError)
{
return std::nullopt;
}
return doc;
}
QJsonDocument readJsonFile(const QString &path)
{
auto opt = tryReadJsonFile(path);
if (!opt)
{
_exit(1);
}
return *opt;
}
class RecentMessages
{
public:
explicit RecentMessages(const QString &name_)
: name(name_)
, chan(this->name)
{
const auto seventvEmotes =
tryReadJsonFile(u":/bench/seventvemotes-%1.json"_s.arg(this->name));
const auto bttvEmotes =
tryReadJsonFile(u":/bench/bttvemotes-%1.json"_s.arg(this->name));
const auto ffzEmotes =
tryReadJsonFile(u":/bench/ffzemotes-%1.json"_s.arg(this->name));
if (seventvEmotes)
{
this->chan.setSeventvEmotes(
std::make_shared<const EmoteMap>(seventv::detail::parseEmotes(
seventvEmotes->object()["emote_set"_L1]
.toObject()["emotes"_L1]
.toArray(),
false)));
}
if (bttvEmotes)
{
this->chan.setBttvEmotes(std::make_shared<const EmoteMap>(
bttv::detail::parseChannelEmotes(bttvEmotes->object(),
this->name)));
}
if (ffzEmotes)
{
this->chan.setFfzEmotes(std::make_shared<const EmoteMap>(
ffz::detail::parseChannelEmotes(ffzEmotes->object())));
}
this->messages =
readJsonFile(u":/bench/recentmessages-%1.json"_s.arg(this->name));
}
~RecentMessages()
{
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
virtual void run(benchmark::State &state) = 0;
protected:
QString name;
MockApplication app;
TwitchChannel chan;
QJsonDocument messages;
};
class ParseRecentMessages : public RecentMessages
{
public:
explicit ParseRecentMessages(const QString &name_)
: RecentMessages(name_)
{
}
void run(benchmark::State &state)
{
for (auto _ : state)
{
auto parsed = recentmessages::detail::parseRecentMessages(
this->messages.object());
benchmark::DoNotOptimize(parsed);
}
}
};
class BuildRecentMessages : public RecentMessages
{
public:
explicit BuildRecentMessages(const QString &name_)
: RecentMessages(name_)
{
}
void run(benchmark::State &state)
{
auto parsed = recentmessages::detail::parseRecentMessages(
this->messages.object());
for (auto _ : state)
{
auto built = recentmessages::detail::buildRecentMessages(
parsed, &this->chan);
benchmark::DoNotOptimize(built);
}
}
};
void BM_ParseRecentMessages(benchmark::State &state, const QString &name)
{
ParseRecentMessages bench(name);
bench.run(state);
}
void BM_BuildRecentMessages(benchmark::State &state, const QString &name)
{
BuildRecentMessages bench(name);
bench.run(state);
}
} // namespace
BENCHMARK_CAPTURE(BM_ParseRecentMessages, nymn, u"nymn"_s);
BENCHMARK_CAPTURE(BM_BuildRecentMessages, nymn, u"nymn"_s);

View file

@ -1,3 +1,4 @@
#include "singletons/Resources.hpp"
#include "singletons/Settings.hpp"
#include <benchmark/benchmark.h>
@ -11,6 +12,8 @@ int main(int argc, char **argv)
{
QApplication app(argc, argv);
initResources();
::benchmark::Initialize(&argc, argv);
// Ensure settings are initialized before any benchmarks are run

View file

@ -8,12 +8,14 @@ set(
resources_autogenerated.qrc
themes/ChatterinoTheme.schema.json
)
set(RES_IMAGE_EXCLUDE_FILTER ^linuxinstall/)
set(RES_EXCLUDE_FILTER ^raw)
set(RES_IMAGE_EXCLUDE_FILTER "^(buttons/(update|clearSearch)|avatars|icon|settings|raw)")
file(GLOB_RECURSE RES_ALL_FILES RELATIVE "${RES_DIR}" LIST_DIRECTORIES false CONFIGURE_DEPENDS "${RES_DIR}/*")
file(GLOB_RECURSE RES_IMAGE_FILES RELATIVE "${RES_DIR}" LIST_DIRECTORIES false CONFIGURE_DEPENDS "${RES_DIR}/*.png")
list(REMOVE_ITEM RES_ALL_FILES ${RES_IGNORED_FILES})
list(FILTER RES_ALL_FILES EXCLUDE REGEX ${RES_EXCLUDE_FILTER})
list(FILTER RES_IMAGE_FILES EXCLUDE REGEX ${RES_IMAGE_EXCLUDE_FILTER})
###############################

View file

@ -5,12 +5,13 @@ from os import path
class Chatterino(ConanFile):
name = "Chatterino"
requires = "boost/1.81.0"
requires = "boost/1.83.0"
settings = "os", "compiler", "build_type", "arch"
default_options = {
"with_benchmark": False,
"with_openssl3": False,
"with_openssl3": True,
"openssl*:shared": True,
"boost*:header_only": True,
}
options = {
"with_benchmark": [True, False],
@ -24,18 +25,20 @@ class Chatterino(ConanFile):
self.requires("benchmark/1.7.1")
if self.options.get_safe("with_openssl3", False):
self.requires("openssl/3.1.0")
self.requires("openssl/3.2.0")
else:
self.requires("openssl/1.1.1t")
def generate(self):
copy_bin = lambda dep, selector, subdir: copy(
self,
selector,
dep.cpp_info.bindirs[0],
path.join(self.build_folder, subdir),
keep_path=False,
)
def copy_bin(dep, selector, subdir):
src = path.realpath(dep.cpp_info.bindirs[0])
dst = path.realpath(path.join(self.build_folder, subdir))
if src == dst:
return
copy(self, selector, src, dst, keep_path=False)
for dep in self.dependencies.values():
# macOS
copy_bin(dep, "*.dylib", "bin")

View file

@ -339,6 +339,8 @@
"type": "object",
"additionalProperties": false,
"properties": {
"liveIndicator": { "$ref": "#/definitions/qt-color" },
"rerunIndicator": { "$ref": "#/definitions/qt-color" },
"dividerLine": { "$ref": "#/definitions/qt-color" },
"highlighted": {
"$ref": "#/definitions/tab-colors"
@ -388,6 +390,11 @@
"$comment": "Determines which icons to use. 'dark' will use dark icons (best for a light theme). 'light' will use light icons.",
"enum": ["light", "dark"],
"default": "light"
},
"fallbackTheme": {
"$comment": "Determines which built-in Chatterino theme to use as a fallback in case a color isn't configured.",
"enum": ["White", "Light", "Dark", "Black"],
"default": "Dark"
}
},
"required": ["iconTheme"]

83
docs/chatterino.d.ts vendored
View file

@ -9,7 +9,64 @@ declare module c2 {
}
class CommandContext {
words: String[];
channel_name: String;
channel: Channel;
}
enum Platform {
Twitch,
}
enum ChannelType {
None,
Direct,
Twitch,
TwitchWhispers,
TwitchWatching,
TwitchMentions,
TwitchLive,
TwitchAutomod,
Irc,
Misc,
}
interface IWeakResource {
is_valid(): boolean;
}
class RoomModes {
unique_chat: boolean;
subscriber_only: boolean;
emotes_only: boolean;
follower_only: null | number;
slow_mode: null | number;
}
class StreamStatus {
live: boolean;
viewer_count: number;
uptime: number;
title: string;
game_name: string;
game_id: string;
}
class Channel implements IWeakResource {
is_valid(): boolean;
get_name(): string;
get_type(): ChannelType;
get_display_name(): string;
send_message(message: string, execute_commands: boolean): void;
add_system_message(message: string): void;
is_twitch_channel(): boolean;
get_room_modes(): RoomModes;
get_stream_status(): StreamStatus;
get_twitch_id(): string;
is_broadcaster(): boolean;
is_mod(): boolean;
is_vip(): boolean;
static by_name(name: string, platform: Platform): null | Channel;
static by_twitch_id(id: string): null | Channel;
}
function log(level: LogLevel, ...data: any[]): void;
@ -17,6 +74,26 @@ declare module c2 {
name: String,
handler: (ctx: CommandContext) => void
): boolean;
function send_msg(channel: String, text: String): boolean;
function system_msg(channel: String, text: String): boolean;
class CompletionList {
values: String[];
hide_others: boolean;
}
enum EventType {
CompletionRequested = "CompletionRequested",
}
type CbFuncCompletionsRequested = (
query: string,
full_text_content: string,
cursor_position: number,
is_first_word: boolean
) => CompletionList;
type CbFunc<T> = T extends EventType.CompletionRequested
? CbFuncCompletionsRequested
: never;
function register_callback<T>(type: T, func: CbFunc<T>): void;
function later(callback: () => void, msec: number): void;
}

View file

@ -5,7 +5,7 @@
- [ ] Updated version code in `src/common/Version.hpp`
- [ ] Updated version code in `CMakeLists.txt`
This can only be "whole versions", so if you're releasing `2.4.0-beta` you'll need to condense it to `2.4.0`
- [ ] Add a new release at the top of of the `releases` key in `resources/com.chatterino.chatterino.appdata.xml`
- [ ] Add a new release at the top of the `releases` key in `resources/com.chatterino.chatterino.appdata.xml`
This cannot use dash to denote a pre-release identifier, you have to use a tilde instead.
- [ ] Updated version code in `.CI/chatterino-installer.iss`

View file

@ -41,9 +41,22 @@
},
"license": {
"type": "string",
"description": "A small description of your license.",
"description": "SPDX identifier for license of this plugin. See https://spdx.org/licenses/",
"examples": ["MIT", "GPL-2.0-or-later"]
}
},
"permissions": {
"type": "array",
"description": "The permissions the plugin needs to work.",
"items": {
"type": "object",
"properties": {
"type": {
"enum": ["FilesystemRead", "FilesystemWrite"]
}
}
}
},
"$schema": { "type": "string" }
},
"required": ["name", "description", "authors", "version", "license"]
}

193
docs/plugin-meta.lua Normal file
View file

@ -0,0 +1,193 @@
---@meta Chatterino2
-- This file is automatically generated from src/controllers/plugins/LuaAPI.hpp by the scripts/make_luals_meta.py script
-- This file is intended to be used with LuaLS (https://luals.github.io/).
-- Add the folder this file is in to "Lua.workspace.library".
c2 = {}
---@class IWeakResource
--- Returns true if the channel this object points to is valid.
--- If the object expired, returns false
--- If given a non-Channel object, it errors.
---@return boolean
function IWeakResource:is_valid() end
---@alias LogLevel integer
---@type { Debug: LogLevel, Info: LogLevel, Warning: LogLevel, Critical: LogLevel }
c2.LogLevel = {}
---@alias EventType integer
---@type { CompletionRequested: EventType }
c2.EventType = {}
---@class CommandContext
---@field words string[] The words typed when executing the command. For example `/foo bar baz` will result in `{"/foo", "bar", "baz"}`.
---@field channel Channel The channel the command was executed in.
---@class CompletionList
---@field values string[] The completions
---@field hide_others boolean Whether other completions from Chatterino should be hidden/ignored.
-- Now including data from src/common/Channel.hpp.
---@alias ChannelType integer
---@type { None: ChannelType }
ChannelType = {}
-- Back to src/controllers/plugins/LuaAPI.hpp.
-- Now including data from src/controllers/plugins/api/ChannelRef.hpp.
--- This enum describes a platform for the purpose of searching for a channel.
--- Currently only Twitch is supported because identifying IRC channels is tricky.
---@alias Platform integer
---@type { Twitch: Platform }
Platform = {}
---@class Channel: IWeakResource
--- Returns true if the channel this object points to is valid.
--- If the object expired, returns false
--- If given a non-Channel object, it errors.
---
---@return boolean success
function Channel:is_valid() end
--- Gets the channel's name. This is the lowercase login name.
---
---@return string name
function Channel:get_name() end
--- Gets the channel's type
---
---@return ChannelType
function Channel:get_type() end
--- Get the channel owner's display name. This may contain non-lowercase ascii characters.
---
---@return string name
function Channel:get_display_name() end
--- Sends a message to the target channel.
--- Note that this does not execute client-commands.
---
---@param message string
---@param execute_commands boolean Should commands be run on the text?
function Channel:send_message(message, execute_commands) end
--- Adds a system message client-side
---
---@param message string
function Channel:add_system_message(message) end
--- Returns true for twitch channels.
--- Compares the channel Type. Note that enum values aren't guaranteed, just
--- that they are equal to the exposed enum.
---
---@return bool
function Channel:is_twitch_channel() end
--- Twitch Channel specific functions
--- Returns a copy of the channel mode settings (subscriber only, r9k etc.)
---
---@return RoomModes
function Channel:get_room_modes() end
--- Returns a copy of the stream status.
---
---@return StreamStatus
function Channel:get_stream_status() end
--- Returns the Twitch user ID of the owner of the channel.
---
---@return string
function Channel:get_twitch_id() end
--- Returns true if the channel is a Twitch channel and the user owns it
---
---@return boolean
function Channel:is_broadcaster() end
--- Returns true if the channel is a Twitch channel and the user is a moderator in the channel
--- Returns false for broadcaster.
---
---@return boolean
function Channel:is_mod() end
--- Returns true if the channel is a Twitch channel and the user is a VIP in the channel
--- Returns false for broadcaster.
---
---@return boolean
function Channel:is_vip() end
--- Misc
---@return string
function Channel:__tostring() end
--- Static functions
--- Finds a channel by name.
---
--- Misc channels are marked as Twitch:
--- - /whispers
--- - /mentions
--- - /watching
--- - /live
--- - /automod
---
---@param name string Which channel are you looking for?
---@param platform Platform Where to search for the channel?
---@return Channel?
function Channel.by_name(name, platform) end
--- Finds a channel by the Twitch user ID of its owner.
---
---@param string id ID of the owner of the channel.
---@return Channel?
function Channel.by_twitch_id(string) end
---@class RoomModes
---@field unique_chat boolean You might know this as r9kbeta or robot9000.
---@field subscriber_only boolean
---@field emotes_only boolean Whether or not text is allowed in messages.
--- Note that "emotes" here only means Twitch emotes, not Unicode emoji, nor 3rd party text-based emotes
---@field unique_chat number? Time in minutes you need to follow to chat or nil.
---@field slow_mode number? Time in seconds you need to wait before sending messages or nil.
---@class StreamStatus
---@field live boolean
---@field viewer_count number
---@field uptime number Seconds since the stream started.
---@field title string Stream title or last stream title
---@field game_name string
---@field game_id string
-- Back to src/controllers/plugins/LuaAPI.hpp.
--- Registers a new command called `name` which when executed will call `handler`.
---
---@param name string The name of the command.
---@param handler fun(ctx: CommandContext) The handler to be invoked when the command gets executed.
---@return boolean ok Returns `true` if everything went ok, `false` if a command with this name exists.
function c2.register_command(name, handler) end
--- Registers a callback to be invoked when completions for a term are requested.
---
---@param type "CompletionRequested"
---@param func fun(query: string, full_text_content: string, cursor_position: integer, is_first_word: boolean): CompletionList The callback to be invoked.
function c2.register_callback(type, func) end
--- Writes a message to the Chatterino log.
---
---@param level LogLevel The desired level.
---@param ... any Values to log. Should be convertible to a string with `tostring()`.
function c2.log(level, ...) end
--- Calls callback around msec milliseconds later. Does not freeze Chatterino.
---
---@param callback fun() The callback that will be called.
---@param msec number How long to wait.
function c2.later(callback, msec) end

View file

@ -1,6 +1,6 @@
# Test and Benchmark
Chatterino includes a set of unit tests and benchmarks. These can be built using cmake by adding the `-DBUILD_TESTS=On` and `-DBUILD_BENCHMARKS=On` flags respectively.
Chatterino includes a set of unit tests and benchmarks. These can be built using CMake by adding the `-DBUILD_TESTS=On` and `-DBUILD_BENCHMARKS=On` flags respectively.
## Adding your own test

View file

@ -14,10 +14,12 @@ Each plugin should have its own directory.
Chatterino Plugins dir/
└── plugin_name/
├── init.lua
└── info.json
├── info.json
└── data/
└── This is where your data/configs can be dumped
```
`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`import` global function](#importfilename=).
`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`require` global function](#requiremodname).
`info.json` contains metadata about the plugin, like its name, description,
authors, homepage link, tags, version, license name. The version field **must**
@ -35,27 +37,77 @@ Example file:
"homepage": "https://github.com/Chatterino/Chatterino2",
"tags": ["test"],
"version": "0.0.0",
"license": "MIT"
"license": "MIT",
"permissions": []
}
```
An example plugin is available at [https://github.com/Mm2PL/Chatterino-test-plugin](https://github.com/Mm2PL/Chatterino-test-plugin)
## Permissions
Plugins can have permissions associated to them. Unless otherwise noted functions don't require permissions.
These are the valid permissions:
### FilesystemRead
Allows the plugin to read from its data directory.
Example:
```json
{
...,
"permissions": [
{
"type": "FilesystemRead"
},
...
]
}
```
### FilesystemWrite
Allows the plugin to write to files and create files in its data directory.
Example:
```json
{
...,
"permissions": [
{
"type": "FilesystemWrite"
},
...
]
}
```
## Plugins with Typescript
If you prefer, you may use [TypescriptToLua](https://typescripttolua.github.io)
to typecheck your plugins. There is a `chatterino.d.ts` file describing the API
in this directory. However this has several drawbacks like harder debugging at
in this directory. However, this has several drawbacks like harder debugging at
runtime.
## LuaLS type definitions
Type definitions for LuaLS are available in
[the `/plugin-meta.lua` file](./plugin-meta.lua). These are generated from [the C++
headers](../src/controllers/plugins/LuaAPI.hpp) of Chatterino using [a
script](../scripts/make_luals_meta.py).
## API
The following parts of the Lua standard library are loaded:
- `_G` (most globals)
- `table`
- `string`
- `io` - except `stdin`, `stdout`, `stderr`. Some functions require permissions.
- `math`
- `string`
- `table`
- `utf8`
The official manual for them is available [here](https://www.lua.org/manual/5.4/manual.html#6).
@ -97,14 +149,14 @@ command with this name.
Example:
```lua
function cmdWords(ctx)
function cmd_words(ctx)
-- ctx contains:
-- words - table of words supplied to the command including the trigger
-- channel_name - name of the channel the command is being run in
c2.system_msg(ctx.channel_name, "Words are: " .. table.concat(ctx.words, " "))
-- channel - the channel the command is being run in
channel:add_system_message("Words are: " .. table.concat(ctx.words, " "))
end
c2.register_command("/words", cmdWords)
c2.register_command("/words", cmd_words)
```
Limitations/known issues:
@ -113,41 +165,322 @@ Limitations/known issues:
rebuilding the window content caused by reloading another plugin will solve this.
- Spaces in command names aren't handled very well (https://github.com/Chatterino/chatterino2/issues/1517).
#### `send_msg(channel, text)`
#### `register_callback("CompletionRequested", handler)`
Sends a message to `channel` with the specified text. Also executes commands.
Registers a callback (`handler`) to process completions. The callback gets the following parameters:
- `query`: The queried word.
- `full_text_content`: The whole input.
- `cursor_position`: The position of the cursor in the input.
- `is_first_word`: Flag whether `query` is the first word in the input.
Example:
| Input | `query` | `full_text_content` | `cursor_position` | `is_first_word` |
| ---------- | ------- | ------------------- | ----------------- | --------------- |
| `foo│` | `foo` | `foo` | 3 | `true` |
| `fo│o` | `fo` | `foo` | 2 | `true` |
| `foo bar│` | `bar` | `foo bar` | 7 | `false` |
| `foo │bar` | `foo` | `foo bar` | 4 | `false` |
```lua
function string.startswith(s, other)
return string.sub(s, 1, string.len(other)) == other
end
c2.register_callback(
"CompletionRequested",
function(query, full_text_content, cursor_position, is_first_word)
if ("!join"):startswith(query) then
---@type CompletionList
return { hide_others = true, values = { "!join" } }
end
---@type CompletionList
return { hide_others = false, values = {} }
end
)
```
#### `Platform` enum
This table describes platforms that can be accessed. Chatterino supports IRC
however plugins do not yet have explicit access to get IRC channels objects.
The values behind the names may change, do not count on them. It has the
following keys:
- `Twitch`
#### `ChannelType` enum
This table describes channel types Chatterino supports. The values behind the
names may change, do not count on them. It has the following keys:
- `None`
- `Direct`
- `Twitch`
- `TwitchWhispers`
- `TwitchWatching`
- `TwitchMentions`
- `TwitchLive`
- `TwitchAutomod`
- `TwitchEnd`
- `Irc`
- `Misc`
#### `Channel`
This is a type that represents a channel. Existence of this object doesn't
force Chatterino to hold the channel open. Should the user close the last split
holding this channel open, your Channel object will expire. You can check for
this using the `Channel:is_valid()` function. Using any other function on an
expired Channel yields an error. Using any `Channel` member function on a
non-`Channel` table also yields an error.
Some functions make sense only for Twitch channel, these yield an error when
used on non-Twitch channels. Special channels while marked as
`is_twitch_channel() = true` do not have these functions. To check if a channel
is an actual Twitch chatroom use `Channel:get_type()` instead of
`Channel:is_twitch_channel()`.
##### `Channel:by_name(name, platform)`
Finds a channel given by `name` on `platform` (see [`Platform` enum](#Platform-enum)). Returns the channel or `nil` if not open.
Some miscellaneous channels are marked as if they are specifically Twitch channels:
- `/whispers`
- `/mentions`
- `/watching`
- `/live`
- `/automod`
Example:
```lua
function cmdShout(ctx)
local pajladas = c2.Channel.by_name("pajlada", c2.Platform.Twitch)
```
##### `Channel:by_twitch_id(id)`
Finds a channel given by the string representation of the owner's Twitch user ID. Returns the channel or `nil` if not open.
Example:
```lua
local pajladas = c2.Channel.by_twitch_id("11148817")
```
##### `Channel:get_name()`
On Twitch returns the lowercase login name of the channel owner. On IRC returns the normalized channel name.
Example:
```lua
-- Note: if the channel is not open this errors
pajladas:get_name() -- "pajlada"
```
##### `Channel:get_type()`
Returns the channel's type. See [`ChannelType` enum](#ChannelType-enum).
##### `Channel:get_display_name()`
Returns the channel owner's display name. This can contain characters that are not lowercase and even non-ASCII.
Example:
```lua
local saddummys = c2.Channel.by_name("saddummy")
saddummys:get_display_name() -- "서새봄냥"
```
<!-- F Korean Twitch, apparently you were not profitable enough -->
##### `Channel:send_message(message[, execute_commands])`
Sends a message to the channel with the given text. If `execute_commands` is
not present or `false` commands will not be executed client-side, this affects
all user commands and all Twitch commands except `/me`.
Examples:
```lua
-- times out @Mm2PL
pajladas:send_message("/timeout mm2pl 1s test", true)
-- results in a "Unknown command" error from Twitch
pajladas:send_message("/timeout mm2pl 1s test")
-- Given a user command "hello":
-- this will execute it
pajladas:send_message("hello", true)
-- this will send "hello" literally, bypassing commands
pajladas:send_message("hello")
function cmd_shout(ctx)
table.remove(ctx.words, 1)
local output = table.concat(ctx.words, " ")
c2.send_msg(ctx.channel_name, string.upper(output))
ctx.channel:send_message(string.upper(output))
end
c2.register_command("/shout", cmdShout)
c2.register_command("/shout", cmd_shout)
```
Limitations/Known issues:
- It is possible to trigger your own Lua command with this causing a potentially infinite loop.
#### `system_msg(channel, text)`
##### `Channel:add_system_message(message)`
Creates a system message and adds it to the twitch channel specified by
`channel`. Returns `true` if everything went ok, `false` otherwise. It will
throw an error if the number of arguments received doesn't match what it
expects.
Shows a system message in the channel with the given text.
Example:
```lua
local ok = c2.system_msg("pajlada", "test")
if (not ok)
-- channel not found
end
pajladas:add_system_message("Hello, world!")
```
##### `Channel:is_twitch_channel()`
Returns `true` if the channel is a Twitch channel, that is its type name has
the `Twitch` prefix. This returns `true` for special channels like Mentions.
You might want `Channel:get_type() == "Twitch"` if you want to use
Twitch-specific functions.
##### `Channel:get_twitch_id()`
Returns the string form of the channel owner's Twitch user ID.
Example:
```lua
pajladas:get_twitch_id() -- "11148817"
```
##### `Channel:is_broadcaster()`
Returns `true` if the channel is owned by the current user.
##### `Channel:is_mod()`
Returns `true` if the channel can be moderated by the current user.
##### `Channel:is_vip()`
Returns `true` if the current user is a VIP in the channel.
### Input/Output API
These functions are wrappers for Lua's I/O library. Functions on file pointer
objects (`FILE*`) are not modified or replaced. [You can read the documentation
for them here](https://www.lua.org/manual/5.4/manual.html#pdf-file:close).
Chatterino does _not_ give you stdin and stdout as default input and output
respectively. The following objects are missing from the `io` table exposed by
Chatterino compared to Lua's native library: `stdin`, `stdout`, `stderr`.
#### `close([file])`
Closes a file. If not given, `io.output()` is used instead.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.close)
#### `flush()`
Flushes `io.output()`.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.flush)
#### `input([file_or_name])`
When called with no arguments this function returns the default input file.
This variant requires no permissions.
When called with a file object, it will set the default input file to the one
given. This one also requires no permissions.
When called with a filename as a string, it will open that file for reading.
Equivalent to: `io.input(io.open(filename))`. This variant requires
the `FilesystemRead` permission and the given file to be within the plugin's
data directory.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.input)
#### `lines([filename, ...])`
With no arguments this function is equivalent to `io.input():lines("l")`. See
[Lua documentation for file:flush()](https://www.lua.org/manual/5.4/manual.html#pdf-file:flush).
This variant requires no permissions.
With `filename` given it is most like `io.open(filename):lines(...)`. This
variant requires the `FilesystemRead` permission and the given file to be
within the plugin's data directory.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.lines)
#### `open(filename [, mode])`
This functions opens the given file with a mode. It requires `filename` to be
within the plugin's data directory. A call with no mode given is equivalent to
one with `mode="r"`.
Depending on the mode this function has slightly different behavior:
| Mode | Permission | Read? | Write? | Truncate? | Create? |
| ----------- | ----------------- | ----- | ------ | --------- | ------- |
| `r` read | `FilesystemRead` | Yes | No | No | No |
| `w` write | `FilesystemWrite` | No | Yes | Yes | Yes |
| `a` append | `FilesystemWrite` | No | Append | No | Yes |
| `r+` update | `FilesystemWrite` | Yes | Yes | No | No |
| `w+` update | `FilesystemWrite` | Yes | Yes | Yes | Yes |
| `a+` update | `FilesystemWrite` | Yes | Append | No | Yes |
To open a file in binary mode add a `b` at the end of the mode.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.open)
#### `output([file_or_name])`
This is identical to [`io.input()`](#inputfile_or_name) but operates on the
default output and opens the file in write mode instead. Requires
`FilesystemWrite` instead of `FilesystemRead`.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.output)
#### `popen(exe [, mode])`
This function is unavailable in Chatterino. Calling it results in an error
message to let you know that it's not available, no permissions needed.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.popen)
#### `read(...)`
Equivalent to `io.input():read(...)`. See [`io.input()`](#inputfile_or_name)
and [`file:read()`](https://www.lua.org/manual/5.4/manual.html#pdf-file:read).
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.read)
#### `tmpfile()`
This function is unavailable in Chatterino. Calling it results in an error
message to let you know that it's not available, no permissions needed.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.tmpfile)
#### `type(obj)`
This functions allows you to tell if the object is a `file`, a `closed file` or
a different bit of data.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.type)
#### `write(...)`
Equivalent to `io.output():write(...)`. See [`io.output()`](#outputfile_or_name)
and [`file:write()`](https://www.lua.org/manual/5.4/manual.html#pdf-file:write).
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-io.write)
### Changed globals
#### `load(chunk [, chunkname [, mode [, env]]])`
@ -158,18 +491,27 @@ It achieves this by forcing all inputs to be encoded with `UTF-8`.
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-load)
#### `import(filename)`
#### `require(modname)`
This function mimics Lua's `dofile` however relative paths are relative to your plugin's directory.
You are restricted to loading files in your plugin's directory. You cannot load files with bytecode inside.
This is Lua's [`require()`](https://www.lua.org/manual/5.3/manual.html#pdf-require) function.
However, the searcher and load configuration is notably different from the default:
- Lua's built-in dynamic library searcher is removed,
- `package.path` is not used, in its place are two searchers,
- when `require()` is used, first a file relative to the currently executing
file will be checked, then a file relative to the plugin directory,
- binary chunks are never loaded,
- files inside of the plugin `data` directory are never loaded
As in normal Lua, dots are converted to the path separators (`'/'` on Linux and Mac, `'\'` on Windows).
Example:
```lua
import("stuff.lua") -- executes Plugins/name/stuff.lua
import("./stuff.lua") -- executes Plugins/name/stuff.lua
import("../stuff.lua") -- tries to load Plugins/stuff.lua and errors
import("luac.out") -- tried to load Plugins/name/luac.out and errors because it contains non-utf8 data
require("stuff") -- executes Plugins/name/stuff.lua or $(dirname $CURR_FILE)/stuff.lua
require("dir.name") -- executes Plugins/name/dir/name.lua or $(dirname $CURR_FILE)/dir/name.lua
require("binary") -- tried to load Plugins/name/binary.lua and errors because binary is not a text file
require("data.file") -- tried to load Plugins/name/data/file.lua and errors because that is not allowed
```
#### `print(Args...)`

View file

@ -1,3 +1,3 @@
Third party libraries are stored here
Third party libraries are stored here.
Fetched via `git submodule update --init --recursive`

@ -1 +0,0 @@
Subproject commit 3182e3be21a8a753f9f269f0a590370d49c8f3cf

@ -1 +1 @@
Subproject commit 6baee9ef9d5657ab582c8a4b9f885ec58ed502d0
Subproject commit e288c5a91883793d14ed9e9d93464f6ee0b08915

@ -1 +1 @@
Subproject commit bbf0a34260a3e8d6e6c48be57653840ac3fa8c30
Subproject commit 17946d65a41a72b447da37df6e314cded9650c32

@ -1 +1 @@
Subproject commit f92bc7bc4940bf58b7f03cefa81a78ef09752007
Subproject commit ceac9c7e97d2d2b97f40ecd0b421e358d7525cbc

@ -1 +1 @@
Subproject commit ca452a811d684db42f93d6352301406754d0c536
Subproject commit d06770649a7e83db780865d09c313a876bf0f4eb

View file

@ -1,52 +0,0 @@
Language: Cpp
AccessModifierOffset: -4
AlignEscapedNewlinesLeft: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakBeforeMultilineStrings: false
BasedOnStyle: Google
BraceWrapping:
AfterClass: "true"
AfterControlStatement: "true"
AfterFunction: "true"
AfterNamespace: "false"
BeforeCatch: "true"
BeforeElse: "true"
BreakBeforeBraces: Custom
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: false
DerivePointerBinding: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentWidth: 4
IndentWrappedFunctionNames: true
IndentPPDirectives: AfterHash
SortIncludes: CaseInsensitive
IncludeBlocks: Regroup
IncludeCategories:
# Project includes
- Regex: '^"[a-zA-Z\._-]+(/[a-zA-Z0-9\._-]+)*"$'
Priority: 1
# Qt includes
- Regex: '^<Q[a-zA-Z0-9\._\/-]+>$'
Priority: 3
CaseSensitive: true
# LibCommuni includes
- Regex: "^<Irc[a-zA-Z]+>$"
Priority: 3
# Standard library includes
- Regex: "^<[a-zA-Z_]+>$"
Priority: 4
# Third party library includes
- Regex: "^<([a-zA-Z_0-9-]+/)*[a-zA-Z_0-9-]+.h(pp)?>$"
Priority: 3
NamespaceIndentation: Inner
PointerBindsToType: false
SpacesBeforeTrailingComments: 2
Standard: Auto
ReflowComments: false

View file

@ -0,0 +1,17 @@
#pragma once
#include "providers/chatterino/ChatterinoBadges.hpp"
namespace chatterino::mock {
class ChatterinoBadges : public IChatterinoBadges
{
public:
std::optional<EmotePtr> getBadge(const UserId &id) override
{
(void)id;
return std::nullopt;
}
};
} // namespace chatterino::mock

View file

@ -0,0 +1,12 @@
#pragma once
#include "singletons/StreamerMode.hpp"
class DisabledStreamerMode : public chatterino::IStreamerMode
{
public:
bool isEnabled() const override
{
return false;
}
};

View file

@ -1,76 +1,152 @@
#pragma once
#include "Application.hpp"
#include "common/Args.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Updates.hpp"
#include <QTemporaryDir>
namespace chatterino::mock {
class EmptyApplication : public IApplication
{
public:
EmptyApplication()
: updates_(this->paths_)
{
}
virtual ~EmptyApplication() = default;
const Paths &getPaths() override
{
return this->paths_;
}
const Args &getArgs() override
{
return this->args_;
}
Theme *getThemes() override
{
assert(
false &&
"EmptyApplication::getThemes was called without being initialized");
return nullptr;
}
Fonts *getFonts() override
{
assert(
false &&
"EmptyApplication::getFonts was called without being initialized");
return nullptr;
}
IEmotes *getEmotes() override
{
assert(
false &&
"EmptyApplication::getEmotes was called without being initialized");
return nullptr;
}
AccountController *getAccounts() override
{
assert(false && "EmptyApplication::getAccounts was called without "
"being initialized");
return nullptr;
}
HotkeyController *getHotkeys() override
{
assert(false && "EmptyApplication::getHotkeys was called without being "
"initialized");
return nullptr;
}
WindowManager *getWindows() override
{
assert(false && "EmptyApplication::getWindows was called without being "
"initialized");
return nullptr;
}
Toasts *getToasts() override
{
assert(
false &&
"EmptyApplication::getToasts was called without being initialized");
return nullptr;
}
CrashHandler *getCrashHandler() override
{
assert(false && "EmptyApplication::getCrashHandler was called without "
"being initialized");
return nullptr;
}
CommandController *getCommands() override
{
assert(false && "EmptyApplication::getCommands was called without "
"being initialized");
return nullptr;
}
NotificationController *getNotifications() override
{
assert(false && "EmptyApplication::getNotifications was called without "
"being initialized");
return nullptr;
}
HighlightController *getHighlights() override
{
assert(false && "EmptyApplication::getHighlights was called without "
"being initialized");
return nullptr;
}
ITwitchIrcServer *getTwitch() override
{
assert(
false &&
"EmptyApplication::getTwitch was called without being initialized");
return nullptr;
}
ChatterinoBadges *getChatterinoBadges() override
PubSub *getTwitchPubSub() override
{
assert(false && "getTwitchPubSub was called without being initialized");
return nullptr;
}
TwitchBadges *getTwitchBadges() override
{
assert(false && "getTwitchBadges was called without being initialized");
return nullptr;
}
Logging *getChatLogger() override
{
assert(!"getChatLogger was called without being initialized");
return nullptr;
}
IChatterinoBadges *getChatterinoBadges() override
{
assert(false && "EmptyApplication::getChatterinoBadges was called "
"without being initialized");
return nullptr;
}
FfzBadges *getFfzBadges() override
{
assert(false && "EmptyApplication::getFfzBadges was called without "
"being initialized");
return nullptr;
}
@ -82,6 +158,8 @@ public:
IUserDataController *getUserData() override
{
assert(false && "EmptyApplication::getUserData was called without "
"being initialized");
return nullptr;
}
@ -93,11 +171,15 @@ public:
ITwitchLiveController *getTwitchLiveController() override
{
assert(false && "EmptyApplication::getTwitchLiveController was called "
"without being initialized");
return nullptr;
}
ImageUploader *getImageUploader() override
{
assert(false && "EmptyApplication::getImageUploader was called without "
"being initialized");
return nullptr;
}
@ -105,6 +187,61 @@ public:
{
return nullptr;
}
#ifdef CHATTERINO_HAVE_PLUGINS
PluginController *getPlugins() override
{
assert(false && "EmptyApplication::getPlugins was called without "
"being initialized");
return nullptr;
}
#endif
Updates &getUpdates() override
{
return this->updates_;
}
BttvEmotes *getBttvEmotes() override
{
assert(false && "EmptyApplication::getBttvEmotes was called without "
"being initialized");
return nullptr;
}
FfzEmotes *getFfzEmotes() override
{
assert(false && "EmptyApplication::getFfzEmotes was called without "
"being initialized");
return nullptr;
}
SeventvEmotes *getSeventvEmotes() override
{
assert(false && "EmptyApplication::getSeventvEmotes was called without "
"being initialized");
return nullptr;
}
ILinkResolver *getLinkResolver() override
{
assert(false && "EmptyApplication::getLinkResolver was called without "
"being initialized");
return nullptr;
}
IStreamerMode *getStreamerMode() override
{
assert(false && "EmptyApplication::getStreamerMode was called without "
"being initialized");
return nullptr;
}
protected:
QTemporaryDir settingsDir;
Paths paths_;
Args args_;
Updates updates_;
};
} // namespace chatterino::mock

View file

@ -392,6 +392,14 @@ public:
(FailureCallback<HelixSendShoutoutError, QString> failureCallback)),
(override));
// send message
MOCK_METHOD(
void, sendChatMessage,
(HelixSendMessageArgs args,
ResultCallback<HelixSentMessage> successCallback,
(FailureCallback<HelixSendMessageError, QString> failureCallback)),
(override));
MOCK_METHOD(void, update, (QString clientId, QString oauthToken),
(override));

View file

@ -1,6 +1,9 @@
#pragma once
#include "mocks/Channel.hpp"
#include "providers/bttv/BttvEmotes.hpp"
#include "providers/ffz/FfzEmotes.hpp"
#include "providers/seventv/SeventvEmotes.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
namespace chatterino::mock {
@ -16,31 +19,19 @@ public:
{
}
const BttvEmotes &getBttvEmotes() const override
{
return this->bttv;
}
const FfzEmotes &getFfzEmotes() const override
{
return this->ffz;
}
const SeventvEmotes &getSeventvEmotes() const override
{
return this->seventv;
}
const IndirectChannel &getWatchingChannel() const override
{
return this->watchingChannel;
}
BttvEmotes bttv;
FfzEmotes ffz;
SeventvEmotes seventv;
QString getLastUserThatWhisperedMe() const override
{
return this->lastUserThatWhisperedMe;
}
ChannelPtr watchingChannelInner;
IndirectChannel watchingChannel;
QString lastUserThatWhisperedMe{"forsen"};
};
} // namespace chatterino::mock

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
resources/avatars/fraxx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 B

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1 +1 @@
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 0C4.5 0 0 4.5 0 10s4.5 10 10 10 10-4.5 10-10S15.5 0 10 0Zm5 13.6L13.6 15 10 11.4 6.4 15 5 13.6 8.6 10 5 6.4 6.4 5 10 8.6 13.6 5 15 6.4 11.4 10l3.6 3.6Z" fill="#ffffff" fill-rule="evenodd" class="fill-000000"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="#fff" d="M10 0C4.5 0 0 4.5 0 10s4.5 10 10 10 10-4.5 10-10S15.5 0 10 0m5 13.6L13.6 15 10 11.4 6.4 15 5 13.6 8.6 10 5 6.4 6.4 5 10 8.6 13.6 5 15 6.4 11.4 10z"/></svg>

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 236 B

View file

@ -1 +1 @@
<?xml version="1.0" ?><svg height="20px" version="1.1" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><desc/><defs/><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Core" transform="translate(-380.000000, -44.000000)"><g id="cancel" transform="translate(380.000000, 44.000000)"><path d="M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M15,13.6 L13.6,15 L10,11.4 L6.4,15 L5,13.6 L8.6,10 L5,6.4 L6.4,5 L10,8.6 L13.6,5 L15,6.4 L11.4,10 L15,13.6 L15,13.6 Z" id="Shape"/></g></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M10 0C4.5 0 0 4.5 0 10s4.5 10 10 10 10-4.5 10-10S15.5 0 10 0m5 13.6L13.6 15 10 11.4 6.4 15 5 13.6 8.6 10 5 6.4 6.4 5 10 8.6 13.6 5 15 6.4 11.4 10z"/></svg>

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,004 B

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 408 B

View file

@ -1,12 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M50,100c27.7,0,50-22.3,50-50S77.7,0,50,0S0,22.3,0,50S22.3,100,50,100z M33.3,46.2c4.2,0,8.3-3.3,8.3-8.3
s-4.2-8.3-8.3-8.3S25,32.9,25,37.9S29.2,46.2,33.3,46.2z M50,91.7C27,91.7,8.3,73,8.3,50S27,8.3,50,8.3S91.7,27,91.7,50
S73,91.7,50,91.7z M23.3,63.1c16.2,10.3,37.1,10.4,53.2,0.1l-4.3-7c-13.7,8.5-31,8.4-44.5-0.1L23.3,63.1z M67.1,46.2
c4.2,0,8.3-3.3,8.3-8.3s-4.2-8.3-8.3-8.3s-8.3,3.3-8.3,8.3S62.9,46.2,67.1,46.2z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path fill="#fff" d="M50 100c27.7 0 50-22.3 50-50S77.7 0 50 0 0 22.3 0 50s22.3 50 50 50M33.3 46.2c4.2 0 8.3-3.3 8.3-8.3s-4.2-8.3-8.3-8.3-8.3 3.3-8.3 8.3 4.2 8.3 8.3 8.3M50 91.7C27 91.7 8.3 73 8.3 50S27 8.3 50 8.3 91.7 27 91.7 50 73 91.7 50 91.7M23.3 63.1c16.2 10.3 37.1 10.4 53.2.1l-4.3-7c-13.7 8.5-31 8.4-44.5-.1zm43.8-16.9c4.2 0 8.3-3.3 8.3-8.3s-4.2-8.3-8.3-8.3-8.3 3.3-8.3 8.3 4.1 8.3 8.3 8.3"/></svg>

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 466 B

View file

@ -1,12 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#000000;}
</style>
<path class="st0" d="M50,100c27.7,0,50-22.3,50-50S77.7,0,50,0S0,22.3,0,50S22.3,100,50,100z M33.3,46.2c4.2,0,8.3-3.3,8.3-8.3
s-4.2-8.3-8.3-8.3S25,32.9,25,37.9S29.2,46.2,33.3,46.2z M50,91.7C27,91.7,8.3,73,8.3,50S27,8.3,50,8.3S91.7,27,91.7,50
S73,91.7,50,91.7z M23.3,63.1c16.2,10.3,37.1,10.4,53.2,0.1l-4.3-7c-13.7,8.5-31,8.4-44.5-0.1L23.3,63.1z M67.1,46.2
c4.2,0,8.3-3.3,8.3-8.3s-4.2-8.3-8.3-8.3s-8.3,3.3-8.3,8.3S62.9,46.2,67.1,46.2z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M50 100c27.7 0 50-22.3 50-50S77.7 0 50 0 0 22.3 0 50s22.3 50 50 50M33.3 46.2c4.2 0 8.3-3.3 8.3-8.3s-4.2-8.3-8.3-8.3-8.3 3.3-8.3 8.3 4.2 8.3 8.3 8.3M50 91.7C27 91.7 8.3 73 8.3 50S27 8.3 50 8.3 91.7 27 91.7 50 73 91.7 50 91.7M23.3 63.1c16.2 10.3 37.1 10.4 53.2.1l-4.3-7c-13.7 8.5-31 8.4-44.5-.1zm43.8-16.9c4.2 0 8.3-3.3 8.3-8.3s-4.2-8.3-8.3-8.3-8.3 3.3-8.3 8.3 4.1 8.3 8.3 8.3"/></svg>

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 B

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 989 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 313 B

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